Merge branch 'master' of git://android.git.kernel.org/platform/system/core into merge_korg_master

* 'master' of git://android.git.kernel.org/platform/system/core:
  adb: Use correct language ID when retrieving USB serial number.
  Fix typo in adb commandline help
  Fix bug where ECONNABORTED would have always occured on asocket_write.
  Helper to perform abortable blocking operations on a socket:
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b25c15b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/Android.mk b/Android.mk
index 8b79ceb..fa2f6f8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,6 +21,7 @@
   include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
 	      adb \
 	      libcutils \
+	      libsysutils \
 	      liblog \
 	      libnetutils \
 	      libpixelflinger \
diff --git a/adb/Android.mk b/adb/Android.mk
index 2296610..9725478 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -7,7 +7,6 @@
 
 # adb host tool
 # =========================================================
-ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean (also unused with the sim)
 include $(CLEAR_VARS)
 
 # Default to a virtual (sockets) usb interface
@@ -54,10 +53,13 @@
 	$(USB_SRCS) \
 	shlist.c \
 	utils.c \
+	usb_vendors.c \
 
 
 ifneq ($(USE_SYSDEPS_WIN32),)
   LOCAL_SRC_FILES += sysdeps_win32.c
+else
+  LOCAL_SRC_FILES += fdevent.c
 endif
 
 LOCAL_CFLAGS += -O2 -g -DADB_HOST=1  -Wall -Wno-unused-parameter
@@ -77,7 +79,6 @@
 $(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
 endif
 
-endif
 
 # adbd device daemon
 # =========================================================
@@ -100,6 +101,7 @@
 
 LOCAL_SRC_FILES := \
 	adb.c \
+	fdevent.c \
 	transport.c \
 	transport_local.c \
 	transport_usb.c \
diff --git a/adb/adb.c b/adb/adb.c
index 12a36f5..956df54 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -23,12 +23,15 @@
 #include <errno.h>
 #include <string.h>
 #include <time.h>
+#include <sys/time.h>
 
 #include "sysdeps.h"
 #include "adb.h"
 
 #if !ADB_HOST
 #include <private/android_filesystem_config.h>
+#else
+#include "usb_vendors.h"
 #endif
 
 
@@ -655,10 +658,25 @@
 void start_device_log(void)
 {
     int fd;
-    char    path[100];
+    char    path[PATH_MAX];
+    struct tm now;
+    time_t t;
+    char value[PROPERTY_VALUE_MAX];
 
-    snprintf(path, sizeof path, "/data/adb_%ld.txt", (long)time(NULL));
-    fd = unix_open(path, O_WRONLY | O_CREAT | O_APPEND, 0640);
+    // read the trace mask from persistent property persist.adb.trace_mask
+    // give up if the property is not set or cannot be parsed
+    property_get("persist.adb.trace_mask", value, "");
+    if (sscanf(value, "%x", &adb_trace_mask) != 1)
+        return;
+
+    adb_mkdir("/data/adb", 0775);
+    tzset();
+    time(&t);
+    localtime_r(&t, &now);
+    strftime(path, sizeof(path),
+                "/data/adb/adb-%Y-%m-%d-%H-%M-%S.txt",
+                &now);
+    fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0640);
     if (fd < 0)
         return;
 
@@ -669,11 +687,6 @@
 
     fd = unix_open("/dev/null", O_RDONLY);
     dup2(fd, 0);
-
-    // log everything
-    adb_trace_mask = ~0;
-    // except TRACE_RWX is a bit too verbose
-    adb_trace_mask &= ~TRACE_RWX;
 }
 #endif
 
@@ -818,19 +831,6 @@
 #if !ADB_HOST
     int secure = 0;
     char value[PROPERTY_VALUE_MAX];
-
-    // prevent the OOM killer from killing us
-    char text[64];
-    snprintf(text, sizeof text, "/proc/%d/oom_adj", (int)getpid());
-    int fd = adb_open(text, O_WRONLY);
-    if (fd >= 0) {
-        // -17 should make us immune to OOM
-        snprintf(text, sizeof text, "%d", -17);
-        adb_write(fd, text, strlen(text));
-        adb_close(fd);
-    } else {
-       D("adb: unable to open %s\n", text);
-    }
 #endif
 
     atexit(adb_cleanup);
@@ -846,6 +846,7 @@
 
 #if ADB_HOST
     HOST = 1;
+    usb_vendors_init();
     usb_init();
     local_init();
 
@@ -885,9 +886,10 @@
         ** AID_INET to diagnose network issues (netcfg, ping)
         ** 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
         */
         gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS,
-                           AID_NET_BT, AID_NET_BT_ADMIN };
+                           AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_RW };
         setgroups(sizeof(groups)/sizeof(groups[0]), groups);
 
         /* then switch user and group to "shell" */
@@ -1088,9 +1090,8 @@
         adb_device_banner = "recovery";
         recovery_mode = 1;
     }
-#if ADB_DEVICE_LOG
+
     start_device_log();
-#endif
     return adb_main(0);
 #endif
 }
diff --git a/adb/adb.h b/adb/adb.h
index a17c8dd..b9ed556 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -345,11 +345,6 @@
 #endif
 
 
-/* set this to log to /data/adb/adb_<time>.txt on the device.
- * has no effect if the /data/adb/ directory does not exist.
- */
-#define ADB_DEVICE_LOG 0
-
 #if !TRACE_PACKETS
 #define print_packet(tag,p) do {} while (0)
 #endif
@@ -357,18 +352,10 @@
 #define ADB_PORT 5037
 #define ADB_LOCAL_TRANSPORT_PORT 5555
 
-// Google's USB Vendor ID
-#define VENDOR_ID_GOOGLE        0x18d1
-// HTC's USB Vendor ID
-#define VENDOR_ID_HTC           0x0bb4
+#define ADB_CLASS              0xff
+#define ADB_SUBCLASS           0x42
+#define ADB_PROTOCOL           0x1
 
-// products for VENDOR_ID_GOOGLE
-#define PRODUCT_ID_SOONER       0xd00d  // Sooner bootloader
-#define PRODUCT_ID_SOONER_COMP  0xdeed  // Sooner composite device
-
-// products for VENDOR_ID_HTC
-#define PRODUCT_ID_DREAM        0x0c01  // Dream bootloader
-#define PRODUCT_ID_DREAM_COMP   0x0c02  // Dream composite device
 
 void local_init();
 int  local_connect(int  port);
@@ -382,7 +369,9 @@
 void usb_kick(usb_handle *h);
 
 /* used for USB device detection */
+#if ADB_HOST
 int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
+#endif
 
 unsigned host_to_le32(unsigned n);
 int adb_commandline(int argc, char **argv);
diff --git a/adb/adb_client.c b/adb/adb_client.c
index 5868744..243f0fa 100644
--- a/adb/adb_client.c
+++ b/adb/adb_client.c
@@ -213,7 +213,7 @@
             fprintf(stdout,"* daemon started successfully *\n");
         }
         /* give the server some time to start properly and detect devices */
-        adb_sleep_ms(2000);
+        adb_sleep_ms(3000);
         // fall through to _adb_connect
     } else {
         // if server was running, check its version to make sure it is not out of date
diff --git a/adb/commandline.c b/adb/commandline.c
index 548d362..ad1021c 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -96,7 +96,8 @@
         " -e                            - directs command to the only running emulator.\n"
         "                                 returns an error if more than one emulator is running.\n"
         " -s <serial number>            - directs command to the USB device or emulator with\n"
-        "                                 the given serial number\n"
+        "                                 the given serial number. Overrides ANDROID_SERIAL\n"
+        "                                 envivornment variable.\n"
         " -p <product name or path>     - simple product name like 'sooner', or\n"
         "                                 a relative/absolute path to a product\n"
         "                                 out directory like 'out/target/product/sooner'.\n"
@@ -766,6 +767,8 @@
     }
     // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
 
+    serial = getenv("ANDROID_SERIAL");
+
         /* modifiers and flags */
     while(argc > 0) {
         if(!strcmp(argv[0],"nodaemon")) {
diff --git a/libcutils/fdevent.c b/adb/fdevent.c
similarity index 98%
rename from libcutils/fdevent.c
rename to adb/fdevent.c
index 4cf46fa..c179b20 100644
--- a/libcutils/fdevent.c
+++ b/adb/fdevent.c
@@ -26,7 +26,7 @@
 #include <stdarg.h>
 #include <stddef.h>
 
-#include <cutils/fdevent.h>
+#include "fdevent.h"
 
 #define TRACE(x...) fprintf(stderr,x)
 
@@ -87,7 +87,7 @@
 {
         /* XXX: what's a good size for the passed in hint? */
     epoll_fd = epoll_create(256);
-    
+
     if(epoll_fd < 0) {
         perror("epoll_create() failed");
         exit(1);
@@ -105,7 +105,7 @@
     ev.events = 0;
     ev.data.ptr = fde;
 
-#if 0    
+#if 0
     if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
         perror("epoll_ctl() failed\n");
         exit(1);
@@ -116,7 +116,7 @@
 static void fdevent_disconnect(fdevent *fde)
 {
     struct epoll_event ev;
-    
+
     memset(&ev, 0, sizeof(ev));
     ev.events = 0;
     ev.data.ptr = fde;
@@ -133,9 +133,9 @@
 {
     struct epoll_event ev;
     int active;
-    
+
     active = (fde->state & FDE_EVENTMASK) != 0;
-    
+
     memset(&ev, 0, sizeof(ev));
     ev.events = 0;
     ev.data.ptr = fde;
@@ -241,7 +241,7 @@
 static void fdevent_disconnect(fdevent *fde)
 {
     int i, n;
-    
+
     FD_CLR(fde->fd, &read_fds);
     FD_CLR(fde->fd, &write_fds);
     FD_CLR(fde->fd, &error_fds);
@@ -283,9 +283,9 @@
     memcpy(&rfd, &read_fds, sizeof(fd_set));
     memcpy(&wfd, &write_fds, sizeof(fd_set));
     memcpy(&efd, &error_fds, sizeof(fd_set));
-    
+
     n = select(select_n, &rfd, &wfd, &efd, 0);
-    
+
     if(n < 0) {
         if(errno == EINTR) return;
         perror("select");
@@ -300,12 +300,12 @@
 
         if(events) {
             n--;
-            
+
             fde = fd_table[i];
             if(fde == 0) FATAL("missing fde for fd %d\n", i);
 
             fde->events |= events;
-            
+
             if(fde->state & FDE_PENDING) continue;
             fde->state |= FDE_PENDING;
             fdevent_plist_enqueue(fde);
@@ -320,7 +320,7 @@
     if(fde->fd < 0) {
         FATAL("bogus negative fd (%d)\n", fde->fd);
     }
-    
+
     if(fde->fd >= fd_table_max) {
         int oldmax = fd_table_max;
         if(fde->fd > 32000) {
@@ -383,9 +383,9 @@
 {
     fdevent *list = &list_pending;
     fdevent *node = list->next;
-    
+
     if(node == list) return 0;
-    
+
     list->next = node->next;
     list->next->prev = list;
     node->next = 0;
@@ -449,9 +449,9 @@
 void fdevent_set(fdevent *fde, unsigned events)
 {
     events &= FDE_EVENTMASK;
-    
+
     if((fde->state & FDE_EVENTMASK) == events) return;
-    
+
     if(fde->state & FDE_ACTIVE) {
         fdevent_update(fde, events);
         dump_fde(fde, "update");
@@ -487,13 +487,13 @@
 void fdevent_loop()
 {
     fdevent *fde;
-    
+
     for(;;) {
 #if DEBUG
         fprintf(stderr,"--- ---- waiting for events\n");
 #endif
         fdevent_process();
-        
+
         while((fde = fdevent_plist_dequeue())) {
             unsigned events = fde->events;
             fde->events = 0;
diff --git a/include/cutils/fdevent.h b/adb/fdevent.h
similarity index 89%
rename from include/cutils/fdevent.h
rename to adb/fdevent.h
index 7a442d4..6b7e7ec 100644
--- a/include/cutils/fdevent.h
+++ b/adb/fdevent.h
@@ -17,10 +17,13 @@
 #ifndef __FDEVENT_H
 #define __FDEVENT_H
 
+#include <stdint.h>  /* for int64_t */
+
 /* events that may be observed */
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
 #define FDE_ERROR             0x0004
+#define FDE_TIMEOUT           0x0008
 
 /* features that may be set (via the events set/add/del interface) */
 #define FDE_DONT_CLOSE        0x0080
@@ -30,6 +33,8 @@
 typedef void (*fd_func)(int fd, unsigned events, void *userdata);
 
 /* Allocate and initialize a new fdevent object
+ * Note: use FD_TIMER as 'fd' to create a fd-less object
+ * (used to implement timers).
 */
 fdevent *fdevent_create(int fd, fd_func func, void *arg);
 
@@ -53,6 +58,8 @@
 void fdevent_add(fdevent *fde, unsigned events);
 void fdevent_del(fdevent *fde, unsigned events);
 
+void fdevent_set_timeout(fdevent *fde, int64_t  timeout_ms);
+
 /* loop forever, handling events.
 */
 void fdevent_loop();
@@ -65,7 +72,7 @@
     int fd;
     unsigned short state;
     unsigned short events;
-    
+
     fd_func func;
     void *arg;
 };
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
index 4e6d385..0ebfe73 100644
--- a/adb/file_sync_client.c
+++ b/adb/file_sync_client.c
@@ -165,7 +165,7 @@
 }
 
 static int sync_finish_readtime(int fd, unsigned int *timestamp,
-				unsigned int *mode, unsigned int *size)
+                                unsigned int *mode, unsigned int *size)
 {
     syncmsg msg;
 
@@ -908,12 +908,12 @@
             unsigned int timestamp, mode, size;
             if (sync_finish_readtime(fd, &timestamp, &mode, &size))
                 return 1;
-	    if (size == ci->size) {
+            if (size == ci->size) {
                 /* for links, we cannot update the atime/mtime */
                 if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
-		   (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+                    (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
                     ci->flag = 1;
-	    }
+            }
         }
     }
 #endif
diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c
index 0de0dd5..65cb20a 100644
--- a/adb/framebuffer_service.c
+++ b/adb/framebuffer_service.c
@@ -20,7 +20,7 @@
 #include <string.h>
 #include <fcntl.h>
 
-#include <cutils/fdevent.h>
+#include "fdevent.h"
 #include "adb.h"
 
 #include <linux/fb.h>
diff --git a/adb/get_my_path_darwin.c b/adb/get_my_path_darwin.c
index 00dfee4..6125cb4 100644
--- a/adb/get_my_path_darwin.c
+++ b/adb/get_my_path_darwin.c
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <utils/executablepath.h>
 #import <Carbon/Carbon.h>
 #include <unistd.h>
 
diff --git a/adb/protocol.txt b/adb/protocol.txt
index d0f307c..398d042 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -23,7 +23,7 @@
 
 
 --- protocol overview and basics ---------------------------------------
- 
+
 The transport layer deals in "messages", which consist of a 24 byte
 header followed (optionally) by a payload.  The header consists of 6
 32 bit words which are sent across the wire in little endian format.
@@ -52,7 +52,7 @@
 reversed.
 
 
-
+
 --- CONNECT(version, maxdata, "system-identity-string") ----------------
 
 The CONNECT message establishes the presence of a remote system.
@@ -114,7 +114,7 @@
 not change on later READY messages sent to the same stream.
 
 
-
+
 --- WRITE(0, remote-id, "data") ----------------------------------------
 
 The WRITE message sends data to the recipient's stream identified by
@@ -172,7 +172,7 @@
 #define A_WRTE 0x45545257
 
 
-
+
 --- implementation details ---------------------------------------------
 
 The core of the bridge program will use three threads.  One thread
diff --git a/adb/services.c b/adb/services.c
index 1de55e6..0a5edcf 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -244,6 +244,18 @@
                 cmd, strerror(errno), errno);
         exit(-1);
     } else {
+#if !ADB_HOST
+        // set child's OOM adjustment to zero
+        char text[64];
+        snprintf(text, sizeof text, "/proc/%d/oom_adj", pid);
+        int fd = adb_open(text, O_WRONLY);
+        if (fd >= 0) {
+            adb_write(fd, "0", 1);
+            adb_close(fd);
+        } else {
+           D("adb: unable to open %s\n", text);
+        }
+#endif
         return ptm;
     }
 #endif /* !HAVE_WIN32_PROC */
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index e5d17a8..389fbd2 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -67,16 +67,16 @@
 
 static __inline__ int  adb_thread_create( adb_thread_t  *thread, adb_thread_func_t  func, void*  arg)
 {
-	thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
-	if (thread->tid == (unsigned)-1L) {
-		return -1;
-	}
-	return 0;
+    thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
+    if (thread->tid == (unsigned)-1L) {
+        return -1;
+    }
+    return 0;
 }
 
 static __inline__ void  close_on_exec(int  fd)
 {
-	/* nothing really */
+    /* nothing really */
 }
 
 extern void  disable_tcp_nagle(int  fd);
@@ -138,7 +138,7 @@
 
 static __inline__ int  adb_open_mode(const char* path, int options, int mode)
 {
-	return adb_open(path, options);
+    return adb_open(path, options);
 }
 
 static __inline__ int  unix_open(const char*  path, int options,...)
@@ -169,7 +169,7 @@
 extern int socket_loopback_server(int port, int type);
 extern int socket_inaddr_any_server(int port, int type);
 
-/* normally provided by <cutils/fdevent.h> */
+/* normally provided by "fdevent.h" */
 
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
@@ -203,7 +203,7 @@
 
 static __inline__ void  adb_sleep_ms( int  mseconds )
 {
-	Sleep( mseconds );
+    Sleep( mseconds );
 }
 
 extern int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen);
@@ -252,7 +252,7 @@
 
 #else /* !_WIN32 a.k.a. Unix */
 
-#include <cutils/fdevent.h>
+#include "fdevent.h"
 #include <cutils/sockets.h>
 #include <cutils/properties.h>
 #include <cutils/misc.h>
@@ -290,7 +290,7 @@
 
 static __inline__ void  close_on_exec(int  fd)
 {
-	fcntl( fd, F_SETFD, FD_CLOEXEC );
+    fcntl( fd, F_SETFD, FD_CLOEXEC );
 }
 
 static __inline__ int  unix_open(const char*  path, int options,...)
@@ -312,7 +312,7 @@
 
 static __inline__ int  adb_open_mode( const char*  pathname, int  options, int  mode )
 {
-	return open( pathname, options, mode );
+    return open( pathname, options, mode );
 }
 
 
@@ -368,11 +368,11 @@
 {
     int  fd = creat(path, mode);
 
-	if ( fd < 0 )
-		return -1;
+    if ( fd < 0 )
+        return -1;
 
     close_on_exec(fd);
-	return fd;
+    return fd;
 }
 #undef   creat
 #define  creat  ___xxx_creat
@@ -439,12 +439,12 @@
 
 static __inline__ void  adb_sleep_ms( int  mseconds )
 {
-	usleep( mseconds*1000 );
+    usleep( mseconds*1000 );
 }
 
 static __inline__ int  adb_mkdir(const char*  path, int mode)
 {
-	return mkdir(path, mode);
+    return mkdir(path, mode);
 }
 #undef   mkdir
 #define  mkdir  ___xxx_mkdir
diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c
index c2a9a98..a8e3bb9 100644
--- a/adb/sysdeps_win32.c
+++ b/adb/sysdeps_win32.c
@@ -739,12 +739,12 @@
 {
     FH   serverfh = _fh_from_int(serverfd);
     FH   fh;
-    
+
     if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
         D( "adb_socket_accept: invalid fd %d\n", serverfd );
         return -1;
     }
-    
+
     fh = _fh_alloc( &_fh_socket_class );
     if (!fh) {
         D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
@@ -757,7 +757,7 @@
         D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
         return -1;
     }
-    
+
     snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
     D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
     return  _fh_to_int(fh);
@@ -768,7 +768,7 @@
 {
     FH   fh = _fh_from_int(fd);
     int  on;
-    
+
     if ( !fh || fh->clazz != &_fh_socket_class )
         return;
 
@@ -1746,7 +1746,7 @@
 
 /**  FILE EVENT HOOKS
  **/
- 
+
 static void  _event_file_prepare( EventHook  hook )
 {
     if (hook->wanted & (FDE_READ|FDE_WRITE)) {
diff --git a/adb/transport_usb.c b/adb/transport_usb.c
index 01c4a7e..3737c5c 100644
--- a/adb/transport_usb.c
+++ b/adb/transport_usb.c
@@ -23,6 +23,10 @@
 #define  TRACE_TAG  TRACE_TRANSPORT
 #include "adb.h"
 
+#if ADB_HOST
+#include "usb_vendors.h"
+#endif
+
 /* XXX better define? */
 #ifdef __ppc__
 #define H4(x)	(((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
@@ -125,23 +129,21 @@
 #endif
 }
 
+#if ADB_HOST
 int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
 {
-    if (vid == VENDOR_ID_GOOGLE) {
-            /* might support adb */
-    } else if (vid == VENDOR_ID_HTC) {
-            /* might support adb */
-    } else {
-            /* not supported */
-        return 0;
-    }
+    unsigned i;
+    for (i = 0; i < vendorIdCount; i++) {
+        if (vid == vendorIds[i]) {
+            if (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS &&
+                    usb_protocol == ADB_PROTOCOL) {
+                return 1;
+            }
 
-        /* class:vendor (0xff) subclass:android (0x42) proto:adb (0x01) */
-    if(usb_class == 0xff) {
-        if((usb_subclass == 0x42) && (usb_protocol == 0x01)) {
-            return 1;
+            return 0;
         }
     }
 
     return 0;
 }
+#endif
diff --git a/adb/usb_osx.c b/adb/usb_osx.c
index 49e1eef..4892c38 100644
--- a/adb/usb_osx.c
+++ b/adb/usb_osx.c
@@ -28,24 +28,12 @@
 
 #define TRACE_TAG   TRACE_USB
 #include "adb.h"
+#include "usb_vendors.h"
 
 #define  DBG   D
 
-typedef struct {
-    int vid;
-    int pid;
-} VendorProduct;
-
-#define kSupportedDeviceCount   4
-VendorProduct kSupportedDevices[kSupportedDeviceCount] = {
-    { VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER },
-    { VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER_COMP },
-    { VENDOR_ID_HTC, PRODUCT_ID_DREAM },
-    { VENDOR_ID_HTC, PRODUCT_ID_DREAM_COMP },
-};
-
 static IONotificationPortRef    notificationPort = 0;
-static io_iterator_t            notificationIterators[kSupportedDeviceCount];
+static io_iterator_t*           notificationIterators;
 
 struct usb_handle
 {
@@ -61,17 +49,20 @@
 static pthread_cond_t start_cond;
 
 
-static void AndroidDeviceAdded(void *refCon, io_iterator_t iterator);
-static void AndroidDeviceNotify(void *refCon, io_iterator_t iterator, natural_t messageType, void *messageArgument);
-static usb_handle* FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product);
+static void AndroidInterfaceAdded(void *refCon, io_iterator_t iterator);
+static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
+                                   natural_t messageType,
+                                   void *messageArgument);
+static usb_handle* CheckInterface(IOUSBInterfaceInterface **iface,
+                                  UInt16 vendor, UInt16 product);
 
 static int
 InitUSB()
 {
     CFMutableDictionaryRef  matchingDict;
     CFRunLoopSourceRef      runLoopSource;
-    SInt32					vendor, product;
-    int                     i;
+    SInt32                  vendor, if_subclass, if_protocol;
+    unsigned                i;
 
     //* To set up asynchronous notifications, create a notification port and
     //* add its run loop event source to the program's run loop
@@ -81,51 +72,57 @@
 
     memset(notificationIterators, 0, sizeof(notificationIterators));
 
-    //* loop through all supported vendor/product pairs
-    for (i = 0; i < kSupportedDeviceCount; i++) {
-        //* Create our matching dictionary to find the Android device
-        //* IOServiceAddMatchingNotification consumes the reference, so we do not need to release this
-        matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+    //* loop through all supported vendors
+    for (i = 0; i < vendorIdCount; i++) {
+        //* Create our matching dictionary to find the Android device's
+        //* adb interface
+        //* IOServiceAddMatchingNotification consumes the reference, so we do
+        //* not need to release this
+        matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
 
         if (!matchingDict) {
             DBG("ERR: Couldn't create USB matching dictionary.\n");
             return -1;
         }
 
-        //* Set up two matching dictionaries, one for each product ID we support.
-        //* This will cause the kernel to notify us only if the vendor and product IDs match.
-        vendor = kSupportedDevices[i].vid;
-        product = kSupportedDevices[i].pid;
-        CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor));
-        CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product));
-
-        //* Now set up two notifications: one to be called when a raw device
-        //* is first matched by the I/O Kit and another to be called when the
-        //* device is terminated.
-        //* we need to do this with each matching dictionary.
+        //* Match based on vendor id, interface subclass and protocol
+        vendor = vendorIds[i];
+        if_subclass = ADB_SUBCLASS;
+        if_protocol = ADB_PROTOCOL;
+        CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID),
+                             CFNumberCreate(kCFAllocatorDefault,
+                                            kCFNumberSInt32Type, &vendor));
+        CFDictionarySetValue(matchingDict, CFSTR(kUSBInterfaceSubClass),
+                             CFNumberCreate(kCFAllocatorDefault,
+                                            kCFNumberSInt32Type, &if_subclass));
+        CFDictionarySetValue(matchingDict, CFSTR(kUSBInterfaceProtocol),
+                             CFNumberCreate(kCFAllocatorDefault,
+                                            kCFNumberSInt32Type, &if_protocol));
         IOServiceAddMatchingNotification(
                 notificationPort,
                 kIOFirstMatchNotification,
                 matchingDict,
-                AndroidDeviceAdded,
+                AndroidInterfaceAdded,
                 NULL,
                 &notificationIterators[i]);
 
-        //* Iterate over set of matching devices to access already-present devices
-        //* and to arm the notification
-        AndroidDeviceAdded(NULL, notificationIterators[i]);
+        //* Iterate over set of matching interfaces to access already-present
+        //* devices and to arm the notification
+        AndroidInterfaceAdded(NULL, notificationIterators[i]);
     }
 
     return 0;
 }
 
 static void
-AndroidDeviceAdded(void *refCon, io_iterator_t iterator)
+AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
 {
     kern_return_t            kr;
     io_service_t             usbDevice;
+    io_service_t             usbInterface;
     IOCFPlugInInterface      **plugInInterface = NULL;
-    IOUSBDeviceInterface182  **dev = NULL;
+    IOUSBInterfaceInterface220  **iface = NULL;
+    IOUSBDeviceInterface197  **dev = NULL;
     HRESULT                  result;
     SInt32                   score;
     UInt16                   vendor;
@@ -133,28 +130,66 @@
     UInt8                    serialIndex;
     char                     serial[256];
 
-    while ((usbDevice = IOIteratorNext(iterator))) {
-        //* Create an intermediate plugin
+    while ((usbInterface = IOIteratorNext(iterator))) {
+        //* Create an intermediate interface plugin
+        kr = IOCreatePlugInInterfaceForService(usbInterface,
+                                               kIOUSBInterfaceUserClientTypeID,
+                                               kIOCFPlugInInterfaceID,
+                                               &plugInInterface, &score);
+        IOObjectRelease(usbInterface);
+        if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+            DBG("ERR: Unable to create an interface plug-in (%08x)\n", kr);
+            continue;
+        }
+
+        //* This gets us the interface object
+        result = (*plugInInterface)->QueryInterface(plugInInterface,
+                CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)
+                &iface);
+        //* We only needed the plugin to get the interface, so discard it
+        (*plugInInterface)->Release(plugInInterface);
+        if (result || !iface) {
+            DBG("ERR: Couldn't query the interface (%08x)\n", (int) result);
+            continue;
+        }
+
+        //* this gets us an ioservice, with which we will find the actual
+        //* device; after getting a plugin, and querying the interface, of
+        //* course.
+        //* Gotta love OS X
+        kr = (*iface)->GetDevice(iface, &usbDevice);
+        if (kIOReturnSuccess != kr || !usbDevice) {
+            DBG("ERR: Couldn't grab device from interface (%08x)\n", kr);
+            continue;
+        }
+
+        plugInInterface = NULL;
+        score = 0;
+        //* create an intermediate device plugin
         kr = IOCreatePlugInInterfaceForService(usbDevice,
                                                kIOUSBDeviceUserClientTypeID,
                                                kIOCFPlugInInterfaceID,
                                                &plugInInterface, &score);
-
+        //* only needed this to find the plugin
+        (void)IOObjectRelease(usbDevice);
         if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
-            DBG("ERR: Unable to create a plug-in (%08x)\n", kr);
-            goto continue1;
+            DBG("ERR: Unable to create a device plug-in (%08x)\n", kr);
+            continue;
         }
 
-        //* Now create the device interface
         result = (*plugInInterface)->QueryInterface(plugInInterface,
                 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
-
+        //* only needed this to query the plugin
+        (*plugInInterface)->Release(plugInInterface);
         if (result || !dev) {
-            DBG("ERR: Couldn't create a device interface (%08x)\n", (int) result);
-            goto continue2;
+            DBG("ERR: Couldn't create a device interface (%08x)\n",
+                (int) result);
+            continue;
         }
 
-        //* Check the device to see if it's ours
+        //* Now after all that, we actually have a ref to the device and
+        //* the interface that matched our criteria
+
         kr = (*dev)->GetDeviceVendor(dev, &vendor);
         kr = (*dev)->GetDeviceProduct(dev, &product);
         kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
@@ -163,7 +198,8 @@
             IOUSBDevRequest req;
             UInt16          buffer[256];
 
-            req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+            req.bmRequestType =
+                USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
             req.bRequest = kUSBRqGetDescriptor;
             req.wValue = (kUSBStringDesc << 8) | serialIndex;
             req.wIndex = 0;
@@ -174,219 +210,149 @@
             if (kr == kIOReturnSuccess && req.wLenDone > 0) {
                 int i, count;
 
-                // skip first word, and copy the rest to the serial string, changing shorts to bytes.
+                // skip first word, and copy the rest to the serial string,
+                // changing shorts to bytes.
                 count = (req.wLenDone - 1) / 2;
                 for (i = 0; i < count; i++)
                   serial[i] = buffer[i + 1];
                 serial[i] = 0;
             }
         }
+        (*dev)->Release(dev);
 
-        usb_handle* handle = NULL;
+        DBG("INFO: Found vid=%04x pid=%04x serial=%s\n", vendor, product,
+            serial);
 
-        //* Open the device
-        kr = (*dev)->USBDeviceOpen(dev);
-
-        if (kr != kIOReturnSuccess) {
-            DBG("ERR: Could not open device: %08x\n", kr);
-            goto continue3;
-        } else {
-            //* Find an interface for the device
-            handle = FindDeviceInterface((IOUSBDeviceInterface**)dev, vendor, product);
-        }
-
+        usb_handle* handle = CheckInterface((IOUSBInterfaceInterface**)iface,
+                                            vendor, product);
         if (handle == NULL) {
             DBG("ERR: Could not find device interface: %08x\n", kr);
-            (*dev)->USBDeviceClose(dev);
-            goto continue3;
+            (*iface)->Release(iface);
+            continue;
         }
 
         DBG("AndroidDeviceAdded calling register_usb_transport\n");
         register_usb_transport(handle, (serial[0] ? serial : NULL));
 
-        // Register for an interest notification of this device being removed. Pass the reference to our
-        // private data as the refCon for the notification.
+        // Register for an interest notification of this device being removed.
+        // Pass the reference to our private data as the refCon for the
+        // notification.
         kr = IOServiceAddInterestNotification(notificationPort,
-                usbDevice,
+                usbInterface,
                 kIOGeneralInterest,
-                AndroidDeviceNotify,
+                AndroidInterfaceNotify,
                 handle,
                 &handle->usbNotification);
+
         if (kIOReturnSuccess != kr) {
             DBG("ERR: Unable to create interest notification (%08x)\n", kr);
         }
-
-continue3:
-        (void)(*dev)->Release(dev);
-continue2:
-        IODestroyPlugInInterface(plugInInterface);
-continue1:
-        IOObjectRelease(usbDevice);
     }
 }
 
 static void
-AndroidDeviceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
+AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
 {
     usb_handle *handle = (usb_handle *)refCon;
 
     if (messageType == kIOMessageServiceIsTerminated) {
-        DBG("AndroidDeviceNotify\n");
+        if (!handle) {
+            DBG("ERR: NULL handle\n");
+            return;
+        }
+        DBG("AndroidInterfaceNotify\n");
         IOObjectRelease(handle->usbNotification);
         usb_kick(handle);
     }
 }
 
+//* TODO: simplify this further since we only register to get ADB interface
+//* subclass+protocol events
 static usb_handle*
-FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product)
+CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 product)
 {
     usb_handle*                 handle = NULL;
     IOReturn                    kr;
-    IOUSBFindInterfaceRequest   request;
-    io_iterator_t               iterator;
-    io_service_t                usbInterface;
-    IOCFPlugInInterface         **plugInInterface;
-    IOUSBInterfaceInterface     **interface = NULL;
-    HRESULT                     result;
-    SInt32                      score;
     UInt8  interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
-    UInt8  endpoint, configuration;
+    UInt8  endpoint;
 
-    //* Placing the constant KIOUSBFindInterfaceDontCare into the following
-    //* fields of the IOUSBFindInterfaceRequest structure will allow us to
-    //* find all of the interfaces
-    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
-    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
-    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
-    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
 
-    //* SetConfiguration will kill an existing UMS connection, so let's not do this if not necessary.
-    configuration = 0;
-    (*dev)->GetConfiguration(dev, &configuration);
-    if (configuration != 1)
-        (*dev)->SetConfiguration(dev, 1);
-
-    //* Get an iterator for the interfaces on the device
-    kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
-
+    //* Now open the interface.  This will cause the pipes associated with
+    //* the endpoints in the interface descriptor to be instantiated
+    kr = (*interface)->USBInterfaceOpen(interface);
     if (kr != kIOReturnSuccess) {
-        DBG("ERR: Couldn't create a device interface iterator: (%08x)\n", kr);
+        DBG("ERR: Could not open interface: (%08x)\n", kr);
         return NULL;
     }
 
-    while ((usbInterface = IOIteratorNext(iterator))) {
-    //* Create an intermediate plugin
-        kr = IOCreatePlugInInterfaceForService(
-                usbInterface,
-                kIOUSBInterfaceUserClientTypeID,
-                kIOCFPlugInInterfaceID,
-                &plugInInterface,
-                &score);
-
-        //* No longer need the usbInterface object now that we have the plugin
-        (void) IOObjectRelease(usbInterface);
-
-        if ((kr != kIOReturnSuccess) || (!plugInInterface)) {
-            DBG("ERR: Unable to create plugin (%08x)\n", kr);
-            break;
-        }
-
-        //* Now create the interface interface for the interface
-        result = (*plugInInterface)->QueryInterface(
-                plugInInterface,
-                CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
-                (LPVOID) &interface);
-
-        //* No longer need the intermediate plugin
-        (*plugInInterface)->Release(plugInInterface);
-
-        if (result || !interface) {
-            DBG("ERR: Couldn't create interface interface: (%08x)\n",
-               (unsigned int) result);
-            break;
-        }
-
-        //* Now open the interface.  This will cause the pipes associated with
-        //* the endpoints in the interface descriptor to be instantiated
-        kr = (*interface)->USBInterfaceOpen(interface);
-
-        if (kr != kIOReturnSuccess)
-        {
-            DBG("ERR: Could not open interface: (%08x)\n", kr);
-            (void) (*interface)->Release(interface);
-            //* continue so we can try the next interface
-            continue;
-        }
-
-        //* Get the number of endpoints associated with this interface
-        kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
-
-        if (kr != kIOReturnSuccess) {
-            DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
-            goto next_interface;
-        }
-
-        //* Get interface class, subclass and protocol
-        if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
-            (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
-            (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess)
-        {
-            DBG("ERR: Unable to get interface class, subclass and protocol\n");
-            goto next_interface;
-        }
-
-        //* check to make sure interface class, subclass and protocol match ADB
-        //* avoid opening mass storage endpoints
-        if (is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) {
-            handle = calloc(1, sizeof(usb_handle));
-
-            //* Iterate over the endpoints for this interface and find the first
-            //* bulk in/out pipes available.  These will be our read/write pipes.
-            for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
-                UInt8   transferType;
-                UInt16  maxPacketSize;
-                UInt8   interval;
-                UInt8   number;
-                UInt8   direction;
-
-                kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
-                        &number, &transferType, &maxPacketSize, &interval);
-
-                if (kIOReturnSuccess == kr) {
-                    if (kUSBBulk != transferType)
-                        continue;
-
-                    if (kUSBIn == direction)
-                        handle->bulkIn = endpoint;
-
-                    if (kUSBOut == direction)
-                        handle->bulkOut = endpoint;
-
-                    if (interfaceProtocol == 0x01) {
-                        handle->zero_mask = maxPacketSize - 1;
-                    }
-
-                } else {
-                    DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
-                }
-            }
-
-            handle->interface = interface;
-            break;
-        }
-
-next_interface:
-        (*interface)->USBInterfaceClose(interface);
-        (*interface)->Release(interface);
+    //* Get the number of endpoints associated with this interface
+    kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
+    if (kr != kIOReturnSuccess) {
+        DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
+        goto err_get_num_ep;
     }
 
+    //* Get interface class, subclass and protocol
+    if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
+            (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
+            (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) {
+            DBG("ERR: Unable to get interface class, subclass and protocol\n");
+            goto err_get_interface_class;
+    }
+
+    //* check to make sure interface class, subclass and protocol match ADB
+    //* avoid opening mass storage endpoints
+    if (!is_adb_interface(vendor, product, interfaceClass,
+                interfaceSubClass, interfaceProtocol))
+        goto err_bad_adb_interface;
+
+    handle = calloc(1, sizeof(usb_handle));
+
+    //* Iterate over the endpoints for this interface and find the first
+    //* bulk in/out pipes available.  These will be our read/write pipes.
+    for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+        UInt8   transferType;
+        UInt16  maxPacketSize;
+        UInt8   interval;
+        UInt8   number;
+        UInt8   direction;
+
+        kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
+                &number, &transferType, &maxPacketSize, &interval);
+
+        if (kIOReturnSuccess == kr) {
+            if (kUSBBulk != transferType)
+                continue;
+
+            if (kUSBIn == direction)
+                handle->bulkIn = endpoint;
+
+            if (kUSBOut == direction)
+                handle->bulkOut = endpoint;
+
+            handle->zero_mask = maxPacketSize - 1;
+        } else {
+            DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
+            goto err_get_pipe_props;
+        }
+    }
+
+    handle->interface = interface;
     return handle;
+
+err_get_pipe_props:
+    free(handle);
+err_bad_adb_interface:
+err_get_interface_class:
+err_get_num_ep:
+    (*interface)->USBInterfaceClose(interface);
+    return NULL;
 }
 
 
 void* RunLoopThread(void* unused)
 {
-    int i;
+    unsigned i;
 
     InitUSB();
 
@@ -400,7 +366,7 @@
     CFRunLoopRun();
     currentRunLoop = 0;
 
-    for (i = 0; i < kSupportedDeviceCount; i++) {
+    for (i = 0; i < vendorIdCount; i++) {
         IOObjectRelease(notificationIterators[i]);
     }
     IONotificationPortDestroy(notificationPort);
@@ -417,6 +383,9 @@
     {
         adb_thread_t    tid;
 
+        notificationIterators = (io_iterator_t*)malloc(
+            vendorIdCount * sizeof(io_iterator_t));
+
         adb_mutex_init(&start_lock, NULL);
         adb_cond_init(&start_cond, NULL);
 
@@ -441,6 +410,11 @@
     close_usb_devices();
     if (currentRunLoop)
         CFRunLoopStop(currentRunLoop);
+
+    if (notificationIterators != NULL) {
+        free(notificationIterators);
+        notificationIterators = NULL;
+    }
 }
 
 int usb_write(usb_handle *handle, const void *buf, int len)
@@ -527,6 +501,9 @@
 void usb_kick(usb_handle *handle)
 {
     /* release the interface */
+    if (!handle)
+        return;
+
     if (handle->interface)
     {
         (*handle->interface)->USBInterfaceClose(handle->interface);
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
new file mode 100644
index 0000000..9a15146
--- /dev/null
+++ b/adb/usb_vendors.c
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+#include "usb_vendors.h"
+
+#include <stdio.h>
+
+#ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include "windows.h"
+#  include "shlobj.h"
+#else
+#  include <unistd.h>
+#  include <sys/stat.h>
+#endif
+
+#include "sysdeps.h"
+#include "adb.h"
+
+#define ANDROID_PATH            ".android"
+#define ANDROID_ADB_INI         "adb_usb.ini"
+
+#define TRACE_TAG               TRACE_USB
+
+// Google's USB Vendor ID
+#define VENDOR_ID_GOOGLE        0x18d1
+// HTC's USB Vendor ID
+#define VENDOR_ID_HTC           0x0bb4
+
+/** built-in vendor list */
+int builtInVendorIds[] = {
+    VENDOR_ID_GOOGLE,
+    VENDOR_ID_HTC,
+};
+
+#define BUILT_IN_VENDOR_COUNT    (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
+
+/* max number of supported vendor ids (built-in + 3rd party). increase as needed */
+#define VENDOR_COUNT_MAX         128
+
+int vendorIds[VENDOR_COUNT_MAX];
+unsigned vendorIdCount = 0;
+
+int get_adb_usb_ini(char* buff, size_t len);
+
+void usb_vendors_init(void)
+{
+    if (VENDOR_COUNT_MAX < BUILT_IN_VENDOR_COUNT) {
+        fprintf(stderr, "VENDOR_COUNT_MAX not big enough for built-in vendor list.\n");
+        exit(2);
+    }
+
+    /* add the built-in vendors at the beginning of the array */
+    memcpy(vendorIds, builtInVendorIds, sizeof(builtInVendorIds));
+
+    /* default array size is the number of built-in vendors */
+    vendorIdCount = BUILT_IN_VENDOR_COUNT;
+
+    if (VENDOR_COUNT_MAX == BUILT_IN_VENDOR_COUNT)
+        return;
+
+    char temp[PATH_MAX];
+    if (get_adb_usb_ini(temp, sizeof(temp)) == 0) {
+        FILE * f = fopen(temp, "rt");
+
+        if (f != NULL) {
+            /* The vendor id file is pretty basic. 1 vendor id per line.
+               Lines starting with # are comments */
+            while (fgets(temp, sizeof(temp), f) != NULL) {
+                if (temp[0] == '#')
+                    continue;
+
+                long value = strtol(temp, NULL, 0);
+                if (errno == EINVAL || errno == ERANGE || value > INT_MAX || value < 0) {
+                    fprintf(stderr, "Invalid content in %s. Quitting.\n", ANDROID_ADB_INI);
+                    exit(2);
+                }
+
+                vendorIds[vendorIdCount++] = (int)value;
+
+                /* make sure we don't go beyond the array */
+                if (vendorIdCount == VENDOR_COUNT_MAX) {
+                    break;
+                }
+            }
+        }
+    }
+}
+
+/* Utils methods */
+
+/* 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) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/* fills buff with the path to the adb vendor id file. returns 0 if success */
+int get_adb_usb_ini(char* buff, size_t len)
+{
+#ifdef _WIN32
+    const char* home = getenv("ANDROID_SDK_HOME");
+    if (home != NULL) {
+        return build_path(buff, len, "%s\\%s\\%s", home);
+    } else {
+        char path[MAX_PATH];
+        SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, path);
+        return build_path(buff, len, "%s\\%s\\%s", path);
+    }
+#else
+    const char* home = getenv("HOME");
+    if (home == NULL)
+        home = "/tmp";
+
+    return build_path(buff, len, "%s/%s/%s", home);
+#endif
+}
diff --git a/adb/usb_vendors.h b/adb/usb_vendors.h
new file mode 100644
index 0000000..43790b9
--- /dev/null
+++ b/adb/usb_vendors.h
@@ -0,0 +1,25 @@
+/*
+ * 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 __USB_VENDORS_H
+#define __USB_VENDORS_H
+
+extern int vendorIds[];
+extern unsigned  vendorIdCount;
+
+void usb_vendors_init(void);
+
+#endif
\ No newline at end of file
diff --git a/adb/usb_windows.c b/adb/usb_windows.c
index 7ddaa0c..0951f67 100644
--- a/adb/usb_windows.c
+++ b/adb/usb_windows.c
@@ -446,7 +446,7 @@
 }
 
 void find_devices() {
-	usb_handle* handle = NULL;
+        usb_handle* handle = NULL;
   char entry_buffer[2048];
   char interf_name[2048];
   AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
diff --git a/fastboot/bootimg.c b/fastboot/bootimg.c
index 1d77b3c..e5aea4e 100644
--- a/fastboot/bootimg.c
+++ b/fastboot/bootimg.c
@@ -40,7 +40,7 @@
 boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size,
                         void *ramdisk, unsigned ramdisk_size,
                         void *second, unsigned second_size,
-                        unsigned page_size,
+                        unsigned page_size, unsigned base,
                         unsigned *bootimg_size)
 {
     unsigned kernel_actual;
@@ -65,15 +65,14 @@
 
     memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
     
-    hdr->kernel_size = kernel_size;
-    hdr->kernel_addr = 0x10008000;
+    hdr->kernel_size =  kernel_size;
     hdr->ramdisk_size = ramdisk_size;
-    hdr->ramdisk_addr = 0x11000000;
-    hdr->second_size = second_size;
-    hdr->second_addr = 0x10F00000;
-    
-    hdr->tags_addr = 0x10000100;
-    hdr->page_size = page_size;
+    hdr->second_size =  second_size;
+    hdr->kernel_addr =  base + 0x00008000;
+    hdr->ramdisk_addr = base + 0x01000000;
+    hdr->second_addr =  base + 0x00F00000;
+    hdr->tags_addr =    base + 0x00000100;
+    hdr->page_size =    page_size;
 
     memcpy(hdr->magic + page_size, 
            kernel, kernel_size);
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c
index 498b4a6..850e10b 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.c
@@ -42,6 +42,14 @@
 
 #include "fastboot.h"
 
+void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline);
+
+boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size,
+                        void *ramdisk, unsigned ramdisk_size,
+                        void *second, unsigned second_size,
+                        unsigned page_size, unsigned base,
+                        unsigned *bootimg_size);
+
 static usb_handle *usb = 0;
 static const char *serial = 0;
 static const char *product = 0;
@@ -49,6 +57,8 @@
 static int wipe_data = 0;
 static unsigned short vendor_id = 0;
 
+static unsigned base_addr = 0x10000000;
+
 void die(const char *fmt, ...)
 {
     va_list ap;
@@ -212,6 +222,7 @@
             "  -p <product>                             specify product name\n"
             "  -c <cmdline>                             override kernel commandline\n"
             "  -i <vendor id>                           specify a custom USB vendor id\n"
+            "  -b <base_addr>                           specify a custom kernel base address\n"
         );
     exit(1);
 }
@@ -257,7 +268,7 @@
     }
 
     fprintf(stderr,"creating boot image...\n");
-    bdata = mkbootimg(kdata, ksize, rdata, rsize, 0, 0, 2048, &bsize);
+    bdata = mkbootimg(kdata, ksize, rdata, rsize, 0, 0, 2048, base_addr, &bsize);
     if(bdata == 0) {
         fprintf(stderr,"failed to create boot.img\n");
         return 0;
@@ -545,6 +556,10 @@
         if(!strcmp(*argv, "-w")) {
             wants_wipe = 1;
             skip(1);
+        } else if(!strcmp(*argv, "-b")) {
+            require(2);
+            base_addr = strtoul(argv[1], 0, 16);
+            skip(2);
         } else if(!strcmp(*argv, "-s")) {
             require(2);
             serial = argv[1];
diff --git a/fastboot/util_osx.c b/fastboot/util_osx.c
index 068241c..b43e316 100644
--- a/fastboot/util_osx.c
+++ b/fastboot/util_osx.c
@@ -26,7 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#include <utils/executablepath.h>
 #import <Carbon/Carbon.h>
 #include <unistd.h>
 
diff --git a/include/acc/acc.h b/include/acc/acc.h
new file mode 100644
index 0000000..e25fde0
--- /dev/null
+++ b/include/acc/acc.h
@@ -0,0 +1,81 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+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);
+
+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);
+
+#ifdef __cplusplus
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+#endif
diff --git a/include/android/log.h b/include/android/log.h
new file mode 100644
index 0000000..0ea4c29
--- /dev/null
+++ b/include/android/log.h
@@ -0,0 +1,128 @@
+/*
+ * 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_LOG_H
+#define _ANDROID_LOG_H
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ *   This file is part of Android's set of stable system headers
+ *   exposed by the Android NDK (Native Development Kit) since
+ *   platform release 1.5
+ *
+ *   Third-party source AND binary code relies on the definitions
+ *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/*
+ * Support routines to send messages to the Android in-kernel log buffer,
+ * which can later be accessed through the 'logcat' utility.
+ *
+ * Each log message must have
+ *   - a priority
+ *   - a log tag
+ *   - some text
+ *
+ * The tag normally corresponds to the component that emits the log message,
+ * and should be reasonably small.
+ *
+ * Log message text may be truncated to less than an implementation-specific
+ * limit (e.g. 1023 characters max).
+ *
+ * Note that a newline character ("\n") will be appended automatically to your
+ * log message, if not already there. It is not possible to send several messages
+ * and have them appear on a single line in logcat.
+ *
+ * PLEASE USE LOGS WITH MODERATION:
+ *
+ *  - Sending log messages eats CPU and slow down your application and the
+ *    system.
+ *
+ *  - The circular log buffer is pretty small (<64KB), sending many messages
+ *    might push off other important log messages from the rest of the system.
+ *
+ *  - In release builds, only send log messages to account for exceptional
+ *    conditions.
+ *
+ * NOTE: These functions MUST be implemented by /system/lib/liblog.so
+ */
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Android log priority values, in ascending priority order.
+ */
+typedef enum android_LogPriority {
+    ANDROID_LOG_UNKNOWN = 0,
+    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
+    ANDROID_LOG_VERBOSE,
+    ANDROID_LOG_DEBUG,
+    ANDROID_LOG_INFO,
+    ANDROID_LOG_WARN,
+    ANDROID_LOG_ERROR,
+    ANDROID_LOG_FATAL,
+    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_write(int prio, const char *tag, const char *text);
+
+/*
+ * Send a formatted string to the log, used like printf(fmt,...)
+ */
+int __android_log_print(int prio, const char *tag,  const char *fmt, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 3, 4)))
+#endif
+    ;
+
+/*
+ * A variant of __android_log_print() that takes a va_list to list
+ * additional parameters.
+ */
+int __android_log_vprint(int prio, const char *tag,
+                         const char *fmt, va_list ap);
+
+/*
+ * Log an assertion failure and SIGTRAP the process to have a chance
+ * to inspect it, if a debugger is attached. This uses the FATAL priority.
+ */
+void __android_log_assert(const char *cond, const char *tag,
+			  const char *fmt, ...)    
+#if defined(__GNUC__)
+    __attribute__ ((noreturn))
+    __attribute__ ((format(printf, 3, 4)))
+#endif
+    ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ANDROID_LOG_H */
diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h
index fd71352..fd56dbe 100644
--- a/include/cutils/ashmem.h
+++ b/include/cutils/ashmem.h
@@ -10,6 +10,8 @@
 #ifndef _CUTILS_ASHMEM_H
 #define _CUTILS_ASHMEM_H
 
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -18,6 +20,7 @@
 int ashmem_set_prot_region(int fd, int prot);
 int ashmem_pin_region(int fd, size_t offset, size_t len);
 int ashmem_unpin_region(int fd, size_t offset, size_t len);
+int ashmem_get_size_region(int fd);
 
 #ifdef __cplusplus
 }
diff --git a/include/cutils/compiler.h b/include/cutils/compiler.h
new file mode 100644
index 0000000..09112d5
--- /dev/null
+++ b/include/cutils/compiler.h
@@ -0,0 +1,32 @@
+/*
+ * 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_CUTILS_COMPILER_H
+#define ANDROID_CUTILS_COMPILER_H
+
+/*
+ * helps the compiler's optimizer predicting branches
+ */
+
+#ifdef __cplusplus
+#   define CC_LIKELY( exp )    (__builtin_expect( !!(exp), true ))
+#   define CC_UNLIKELY( exp )  (__builtin_expect( !!(exp), false ))
+#else
+#   define CC_LIKELY( exp )    (__builtin_expect( !!(exp), 1 ))
+#   define CC_UNLIKELY( exp )  (__builtin_expect( !!(exp), 0 ))
+#endif
+
+#endif // ANDROID_CUTILS_COMPILER_H
diff --git a/include/cutils/logd.h b/include/cutils/logd.h
index a1cb012..8737639 100644
--- a/include/cutils/logd.h
+++ b/include/cutils/logd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 The Android Open Source Project
+ * 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.
@@ -17,6 +17,12 @@
 #ifndef _ANDROID_CUTILS_LOGD_H
 #define _ANDROID_CUTILS_LOGD_H
 
+/* the stable/frozen log-related definitions have been
+ * moved to this header, which is exposed by the NDK
+ */
+#include <android/log.h>
+
+/* the rest is only used internally by the system */
 #include <time.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -32,45 +38,10 @@
 extern "C" {
 #endif
 
-/*
- * Priority values, in ascending priority order.
- */
-typedef enum android_LogPriority {
-    ANDROID_LOG_UNKNOWN = 0,
-    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
-    ANDROID_LOG_VERBOSE,
-    ANDROID_LOG_DEBUG,
-    ANDROID_LOG_INFO,
-    ANDROID_LOG_WARN,
-    ANDROID_LOG_ERROR,
-    ANDROID_LOG_FATAL,
-    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
-} android_LogPriority;
-
-int __android_log_write(int prio, const char *tag, const char *text);
-
-int __android_log_vprint(int prio, const char *tag,
-			 const char *fmt, va_list ap);
-
 int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
 int __android_log_btwrite(int32_t tag, char type, const void *payload,
     size_t len);
 
-int __android_log_print(int prio, const char *tag,  const char *fmt, ...)
-#if defined(__GNUC__)
-    __attribute__ ((format(printf, 3, 4)))
-#endif
-    ;
-
-
-void __android_log_assert(const char *cond, const char *tag,
-			  const char *fmt, ...)    
-#if defined(__GNUC__)
-    __attribute__ ((noreturn))
-    __attribute__ ((format(printf, 3, 4)))
-#endif
-    ;
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/cutils/native_handle.h b/include/cutils/native_handle.h
index 2b64893..89d6b65 100644
--- a/include/cutils/native_handle.h
+++ b/include/cutils/native_handle.h
@@ -17,12 +17,57 @@
 #ifndef NATIVE_HANDLE_H_
 #define NATIVE_HANDLE_H_
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct
 {
-    int version;        /* sizeof(native_handle) */
+    int version;        /* sizeof(native_handle_t) */
     int numFds;         /* number of file-descriptors at &data[0] */
     int numInts;        /* number of ints at &data[numFds] */
     int data[0];        /* numFds + numInts ints */
-} native_handle;
+} native_handle_t;
+
+
+/* keep the old definition for backward source-compatibility */
+typedef native_handle_t native_handle;
+
+/*
+ * native_handle_close
+ * 
+ * closes the file descriptors contained in this native_handle_t
+ * 
+ * return 0 on success, or a negative error code on failure
+ * 
+ */
+int native_handle_close(const native_handle_t* h);
+
+
+/*
+ * native_handle_create
+ * 
+ * creates a native_handle_t and initializes it. must be destroyed with
+ * native_handle_delete().
+ * 
+ */
+native_handle_t* native_handle_create(int numFds, int numInts);
+
+/*
+ * native_handle_delete
+ * 
+ * frees a native_handle_t allocated with native_handle_create().
+ * This ONLY frees the memory allocated for the native_handle_t, but doesn't
+ * close the file descriptors; which can be achieved with native_handle_close().
+ * 
+ * return 0 on success, or a negative error code on failure
+ * 
+ */
+int native_handle_delete(native_handle_t* h);
+
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* NATIVE_HANDLE_H_ */
diff --git a/include/cutils/tztime.h b/include/cutils/tztime.h
index 9b3ece8..cf103ca 100644
--- a/include/cutils/tztime.h
+++ b/include/cutils/tztime.h
@@ -17,6 +17,8 @@
 #ifndef _CUTILS_TZTIME_H
 #define _CUTILS_TZTIME_H
 
+#include <time.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -24,9 +26,13 @@
 time_t mktime_tz(struct tm * const tmp, char const * tz);
 void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz);
 
+#ifndef HAVE_ANDROID_OS
+/* the following is defined in <time.h> in Bionic */
+
 struct strftime_locale {
     const char *mon[12];    /* short names */
     const char *month[12];  /* long names */
+    const char *standalone_month[12];  /* long standalone names */
     const char *wday[7];    /* short names */
     const char *weekday[7]; /* long names */
     const char *X_fmt;
@@ -39,6 +45,8 @@
 
 size_t strftime_tz(char *s, size_t max, const char *format, const struct tm *tm, const struct strftime_locale *locale);
 
+#endif /* !HAVE_ANDROID_OS */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index c855187..ad4bd74 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -48,6 +48,9 @@
 #define AID_INSTALL       1012  /* group for installing packages */
 #define AID_MEDIA         1013  /* mediaserver process */
 #define AID_DHCP          1014  /* dhcp client */
+#define AID_SDCARD_RW     1015  /* external storage write access */
+#define AID_VPN           1016  /* vpn system */
+#define AID_KEYSTORE      1017  /* keystore subsystem */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -59,6 +62,7 @@
 #define AID_NET_BT        3002  /* bluetooth: create sco, rfcomm or l2cap sockets */
 #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_MISC          9998  /* access to misc storage */
 #define AID_NOBODY        9999
@@ -93,8 +97,12 @@
     { "diag",      AID_DIAG, },
     { "net_bt_admin", AID_NET_BT_ADMIN, },
     { "net_bt",    AID_NET_BT, },
-    { "inet",      AID_INET, }, 
+    { "sdcard_rw", AID_SDCARD_RW, },
+    { "vpn",       AID_VPN, },
+    { "keystore",  AID_KEYSTORE, },
+    { "inet",      AID_INET, },
     { "net_raw",   AID_NET_RAW, },
+    { "net_admin", AID_NET_ADMIN, },
     { "misc",      AID_MISC, },
     { "nobody",    AID_NOBODY, },
 };
@@ -129,7 +137,7 @@
     { 00750, AID_ROOT,   AID_SHELL,  "sbin" },
     { 00755, AID_ROOT,   AID_SHELL,  "system/bin" },
     { 00755, AID_ROOT,   AID_SHELL,  "system/xbin" },
-    { 00777, AID_ROOT,   AID_ROOT,   "system/etc/ppp" }, /* REMOVE */
+    { 00755, AID_ROOT,   AID_ROOT,   "system/etc/ppp" },
     { 00777, AID_ROOT,   AID_ROOT,   "sdcard" },
     { 00755, AID_ROOT,   AID_ROOT,   0 },
 };
@@ -141,20 +149,18 @@
 ** and will allow partial matches.
 */
 static struct fs_path_config android_files[] = {
-    { 00555, AID_ROOT,      AID_ROOT,      "system/etc/ppp/ip-up" },
-    { 00555, AID_ROOT,      AID_ROOT,      "system/etc/ppp/ip-down" },
     { 00440, AID_ROOT,      AID_SHELL,     "system/etc/init.goldfish.rc" },
     { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.goldfish.sh" },
     { 00440, AID_ROOT,      AID_SHELL,     "system/etc/init.trout.rc" },
     { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.ril" },
     { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.testmenu" },
-    { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.gprs-pppd" },
     { 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/hcid.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_RADIO,     AID_AUDIO,     "/system/etc/AudioPara4.csv" },
+    { 00440, AID_RADIO,     AID_AUDIO,     "system/etc/AudioPara4.csv" },
+    { 00555, AID_ROOT,      AID_ROOT,      "system/etc/ppp/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    "data/app/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    "data/app-private/*" },
     { 00644, AID_APP,       AID_APP,       "data/data/*" },
@@ -168,6 +174,7 @@
     { 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" },
     { 00755, AID_ROOT,      AID_SHELL,     "system/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     "system/xbin/*" },
     { 00750, AID_ROOT,      AID_SHELL,     "sbin/*" },
diff --git a/include/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h
index 3a030c5..8a36fa9 100644
--- a/include/private/pixelflinger/ggl_context.h
+++ b/include/private/pixelflinger/ggl_context.h
@@ -21,8 +21,8 @@
 #include <stddef.h>
 #include <string.h>
 #include <sys/types.h>
+#include <endian.h>
 
-#include <utils/Endian.h>
 #include <pixelflinger/pixelflinger.h>
 #include <private/pixelflinger/ggl_fixed.h>
 
diff --git a/include/sysutils/FrameworkClient.h b/include/sysutils/FrameworkClient.h
new file mode 100644
index 0000000..0ef0753
--- /dev/null
+++ b/include/sysutils/FrameworkClient.h
@@ -0,0 +1,21 @@
+#ifndef _FRAMEWORK_CLIENT_H
+#define _FRAMEWORK_CLIENT_H
+
+#include "../../../frameworks/base/include/utils/List.h"
+
+#include <pthread.h>
+
+class FrameworkClient {
+    int             mSocket;
+    pthread_mutex_t mWriteMutex;
+
+public:
+    FrameworkClient(int sock);
+    virtual ~FrameworkClient() {}
+
+    int sendMsg(const char *msg);
+    int sendMsg(const char *msg, const char *data);
+};
+
+typedef android::List<FrameworkClient *> FrameworkClientCollection;
+#endif
diff --git a/include/sysutils/FrameworkCommand.h b/include/sysutils/FrameworkCommand.h
new file mode 100644
index 0000000..6c1fca6
--- /dev/null
+++ b/include/sysutils/FrameworkCommand.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+#ifndef __FRAMEWORK_CMD_HANDLER_H
+#define __FRAMEWORK_CMD_HANDLER_H
+
+#include "../../../frameworks/base/include/utils/List.h"
+
+class SocketClient;
+
+class FrameworkCommand { 
+private:
+    const char *mCommand;
+
+public:
+
+    FrameworkCommand(const char *cmd);
+    virtual ~FrameworkCommand() { }
+
+    virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
+
+    const char *getCommand() { return mCommand; }
+};
+
+typedef android::List<FrameworkCommand *> FrameworkCommandCollection;
+#endif
diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h
new file mode 100644
index 0000000..4e3d396
--- /dev/null
+++ b/include/sysutils/FrameworkListener.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+#ifndef _FRAMEWORKSOCKETLISTENER_H
+#define _FRAMEWORKSOCKETLISTENER_H
+
+#include "SocketListener.h"
+#include "FrameworkCommand.h"
+
+class SocketClient;
+
+class FrameworkListener : public SocketListener {
+public:
+    static const int CMD_ARGS_MAX = 8;
+private:
+    FrameworkCommandCollection *mCommands;
+
+public:
+    FrameworkListener(const char *socketName);
+    virtual ~FrameworkListener() {}
+
+protected:
+    void registerCmd(FrameworkCommand *cmd);
+    virtual bool onDataAvailable(SocketClient *c);
+
+private:
+    void dispatchCommand(SocketClient *c, char *data);
+};
+#endif
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
new file mode 100644
index 0000000..95e83a3
--- /dev/null
+++ b/include/sysutils/NetlinkEvent.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+#ifndef _NETLINKEVENT_H
+#define _NETLINKEVENT_H
+
+#define NL_PARAMS_MAX 32
+
+class NetlinkEvent {
+    int  mSeq;
+    char *mPath;
+    int  mAction;
+    char *mSubsystem;
+    char *mParams[NL_PARAMS_MAX];
+
+public:
+    const static int NlActionUnknown;
+    const static int NlActionAdd;
+    const static int NlActionRemove;
+    const static int NlActionChange;
+
+    NetlinkEvent();
+    virtual ~NetlinkEvent();
+
+    bool decode(char *buffer, int size);
+    const char *findParam(const char *paramName);
+
+    const char *getSubsystem() { return mSubsystem; }
+    int getAction() { return mAction; }
+};
+
+#endif
diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h
new file mode 100644
index 0000000..6dcc005
--- /dev/null
+++ b/include/sysutils/NetlinkListener.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#ifndef _NETLINKLISTENER_H
+#define _NETLINKLISTENER_H
+
+#include "SocketListener.h"
+
+class NetlinkEvent;
+
+class NetlinkListener : public SocketListener {
+    char mBuffer[64 * 1024];
+
+public:
+    NetlinkListener(int socket);
+    virtual ~NetlinkListener() {}
+protected:
+    virtual bool onDataAvailable(SocketClient *cli);
+};
+#endif
diff --git a/include/sysutils/ServiceManager.h b/include/sysutils/ServiceManager.h
new file mode 100644
index 0000000..c31dd8f
--- /dev/null
+++ b/include/sysutils/ServiceManager.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _SERVICE_MANAGER_H
+#define _SERVICE_MANAGER_H
+
+class ServiceManager {
+public:
+    ServiceManager();
+    virtual ~ServiceManager() {}
+
+    int start(const char *name);
+    int stop(const char *name);
+    bool isRunning(const char *name);
+};
+
+#endif
diff --git a/include/sysutils/SocketClient.h b/include/sysutils/SocketClient.h
new file mode 100644
index 0000000..469dd9d
--- /dev/null
+++ b/include/sysutils/SocketClient.h
@@ -0,0 +1,23 @@
+#ifndef _SOCKET_CLIENT_H
+#define _SOCKET_CLIENT_H
+
+#include "../../../frameworks/base/include/utils/List.h"
+
+#include <pthread.h>
+
+class SocketClient {
+    int             mSocket;
+    pthread_mutex_t mWriteMutex;
+
+public:
+    SocketClient(int sock);
+    virtual ~SocketClient() {}
+
+    int getSocket() { return mSocket; }
+
+    int sendMsg(int code, const char *msg, bool addErrno);
+    int sendMsg(const char *msg);
+};
+
+typedef android::List<SocketClient *> SocketClientCollection;
+#endif
diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h
new file mode 100644
index 0000000..68dcb8f
--- /dev/null
+++ b/include/sysutils/SocketListener.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+#ifndef _SOCKETLISTENER_H
+#define _SOCKETLISTENER_H
+
+#include <pthread.h>
+
+#include <sysutils/SocketClient.h>
+
+class SocketListener {
+    int                     mSock;
+    const char              *mSocketName;
+    SocketClientCollection  *mClients;
+    pthread_mutex_t         mClientsLock;
+    bool                    mListen;
+    int                     mCtrlPipe[2];
+    pthread_t               mThread;
+
+public:
+    SocketListener(const char *socketNames, bool listen);
+    SocketListener(int socketFd, bool listen);
+
+    virtual ~SocketListener() {}
+    int startListener();
+    int stopListener();
+
+    void sendBroadcast(int code, const char *msg, bool addErrno);
+    void sendBroadcast(const char *msg);
+
+protected:
+    virtual bool onDataAvailable(SocketClient *c) = 0;
+
+private:
+    static void *threadStart(void *obj);
+    void runListener();
+};
+#endif
diff --git a/init/builtins.c b/init/builtins.c
index bcdfee1..16601ce 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -49,7 +49,7 @@
     fd = open(path, O_WRONLY|O_CREAT, 0622);
 
     if (fd < 0)
-        return -1;
+        return -errno;
 
     len = strlen(value);
 
@@ -59,7 +59,7 @@
 
     close(fd);
     if (ret < 0) {
-        return -1;
+        return -errno;
     } else {
         return 0;
     }
@@ -127,7 +127,7 @@
 static void service_start_if_not_disabled(struct service *svc)
 {
     if (!(svc->flags & SVC_DISABLED)) {
-        service_start(svc);
+        service_start(svc, NULL);
     }
 }
 
@@ -372,7 +372,7 @@
     struct service *svc;
     svc = service_find_by_name(args[1]);
     if (svc) {
-        service_start(svc);
+        service_start(svc, NULL);
     }
     return 0;
 }
@@ -393,7 +393,7 @@
     svc = service_find_by_name(args[1]);
     if (svc) {
         service_stop(svc);
-        service_start(svc);
+        service_start(svc, NULL);
     }
     return 0;
 }
diff --git a/init/devices.c b/init/devices.c
index e0b1f1f..4c7b7da 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -115,8 +115,13 @@
     { "/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/msm_pcm_out",   0660,   AID_SYSTEM,     AID_AUDIO,      1 },
     { "/dev/msm_pcm_in",    0660,   AID_SYSTEM,     AID_AUDIO,      1 },
@@ -125,11 +130,19 @@
     { "/dev/msm_mp3",       0660,   AID_SYSTEM,     AID_AUDIO,      1 },
     { "/dev/msm_audpre",    0660,   AID_SYSTEM,     AID_AUDIO,      0 },
     { "/dev/htc-acoustic",  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 },
 };
 
@@ -380,7 +393,10 @@
         } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
             base = "/dev/adsp/";
             mkdir(base, 0755);
-      } else if(!strncmp(uevent->subsystem, "input", 5)) {
+        } 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)) {
diff --git a/init/init.c b/init/init.c
index 1630155..8c2a058 100644
--- a/init/init.c
+++ b/init/init.c
@@ -156,7 +156,7 @@
     fcntl(fd, F_SETFD, 0);
 }
 
-void service_start(struct service *svc)
+void service_start(struct service *svc, const char *dynamic_args)
 {
     struct stat s;
     pid_t pid;
@@ -192,6 +192,13 @@
         return;
     }
 
+    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
+        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
+               svc->args[0]);
+        svc->flags |= SVC_DISABLED;
+        return;
+    }
+
     NOTICE("starting '%s'\n", svc->name);
 
     pid = fork();
@@ -248,8 +255,27 @@
             setuid(svc->uid);
         }
 
-        if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
-            ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
+        if (!dynamic_args) {
+            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
+                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
+            }
+        } else {
+            char *arg_ptrs[SVC_MAXARGS+1];
+            int arg_idx = svc->nargs;
+            char *tmp = strdup(dynamic_args);
+            char *next = tmp;
+            char *bword;
+
+            /* Copy the static arguments */
+            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
+
+            while((bword = strsep(&next, " "))) {
+                arg_ptrs[arg_idx++] = bword;
+                if (arg_idx == SVC_MAXARGS)
+                    break;
+            }
+            arg_ptrs[arg_idx] = '\0';
+            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
         }
         _exit(127);
     }
@@ -381,7 +407,7 @@
 
     if (next_start_time <= gettime()) {
         svc->flags &= (~SVC_RESTARTING);
-        service_start(svc);
+        service_start(svc, NULL);
         return;
     }
 
@@ -407,13 +433,28 @@
 
 static void msg_start(const char *name)
 {
-    struct service *svc = service_find_by_name(name);
+    struct service *svc;
+    char *tmp = NULL;
+    char *args = NULL;
+
+    if (!strchr(name, ':'))
+        svc = service_find_by_name(name);
+    else {
+        tmp = strdup(name);
+        args = strchr(tmp, ':');
+        *args = '\0';
+        args++;
+
+        svc = service_find_by_name(tmp);
+    }
     
     if (svc) {
-        service_start(svc);
+        service_start(svc, args);
     } else {
         ERROR("no such service '%s'\n", name);
     }
+    if (tmp)
+        free(tmp);
 }
 
 static void msg_stop(const char *name)
@@ -423,7 +464,7 @@
     if (svc) {
         service_stop(svc);
     } else {
-        ERROR("no such service '%s'\n");
+        ERROR("no such service '%s'\n", name);
     }
 }
 
@@ -739,7 +780,7 @@
     svc = service_find_by_keychord(id);
     if (svc) {
         INFO("starting service %s from keychord\n", svc->name);
-        service_start(svc);   
+        service_start(svc, NULL);
     } else {
         ERROR("service for keychord %d not found\n", id);
     }
diff --git a/init/init.h b/init/init.h
index b686869..f306b7b 100644
--- a/init/init.h
+++ b/init/init.h
@@ -29,7 +29,8 @@
 void log_init(void);
 void log_set_level(int level);
 void log_close(void);
-void log_write(int level, const char *fmt, ...);
+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)
@@ -115,6 +116,8 @@
 
 #define NR_SVC_SUPP_GIDS 6    /* six supplementary groups */
 
+#define SVC_MAXARGS 64
+
 struct service {
         /* list of all services */
     struct listnode slist;
@@ -136,15 +139,17 @@
     struct socketinfo *sockets;
     struct svcenvinfo *envvars;
 
-    int nargs;
-    char *args[1];
     struct action onrestart;  /* Actions to execute on restart. */
     
     /* keycodes for triggering this service via /dev/keychord */
     int *keycodes;
     int nkeycodes;
     int keychord_id;
-};
+
+    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);
 
@@ -157,7 +162,7 @@
 void service_for_each_flags(unsigned matchflags,
                             void (*func)(struct service *svc));
 void service_stop(struct service *svc);
-void service_start(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);
diff --git a/init/parser.c b/init/parser.c
index 6a22d24..33c1a68 100644
--- a/init/parser.c
+++ b/init/parser.c
@@ -60,8 +60,6 @@
 #endif       
 }
 
-#define MAXARGS 64
-
 #define T_EOF 0
 #define T_TEXT 1
 #define T_NEWLINE 2
@@ -357,7 +355,7 @@
 static void parse_config(const char *fn, char *s)
 {
     struct parse_state state;
-    char *args[MAXARGS];
+    char *args[SVC_MAXARGS];
     int nargs;
 
     nargs = 0;
@@ -384,7 +382,7 @@
             }
             break;
         case T_TEXT:
-            if (nargs < MAXARGS) {
+            if (nargs < SVC_MAXARGS) {
                 args[nargs++] = state.text;
             }
             break;
@@ -536,7 +534,7 @@
             const char* name = act->name + strlen("property:");
             const char* equals = strchr(name, '=');
             if (equals) {
-                char* prop_name[PROP_NAME_MAX + 1];
+                char prop_name[PROP_NAME_MAX + 1];
                 const char* value;
                 int length = equals - name;
                 if (length > PROP_NAME_MAX) {
@@ -546,7 +544,7 @@
                     prop_name[length] = 0;
                     
                     /* does the property exist, and match the trigger value? */
-                    value = property_get((const char *)&prop_name[0]);
+                    value = property_get(prop_name);
                     if (value && !strcmp(equals + 1, value)) {
                         action_add_queue_tail(act);
                     }
diff --git a/init/property_service.c b/init/property_service.c
index 0bc6239..23a8821 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -67,6 +67,8 @@
     { "wlan.",		AID_SYSTEM },
     { "dhcp.",		AID_SYSTEM },
     { "dhcp.",		AID_DHCP },
+    { "vpn.",		AID_SYSTEM },
+    { "vpn.",		AID_VPN },
     { "debug.",		AID_SHELL },
     { "log.",		AID_SHELL },
     { "service.adb.root",	AID_SHELL },
@@ -296,7 +298,7 @@
         __futex_wake(&pa->serial, INT32_MAX);
     }
     /* If name starts with "net." treat as a DNS property. */
-    if (strncmp("net.", name, sizeof("net.") - 1) == 0)  {
+    if (strncmp("net.", name, strlen("net.")) == 0)  {
         if (strcmp("net.change", name) == 0) {
             return 0;
         }
@@ -307,7 +309,7 @@
         */
         property_set("net.change", name);
     } else if (persistent_properties_loaded &&
-            strncmp("persist.", name, sizeof("persist.") - 1) == 0) {
+            strncmp("persist.", name, strlen("persist.")) == 0) {
         /* 
          * Don't write properties to disk until after we have read all default properties
          * to prevent them from being overwritten by default values.
@@ -446,8 +448,7 @@
 
     if (dir) {
         while ((entry = readdir(dir)) != NULL) {
-            if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..") ||
-                    strncmp("persist.", entry->d_name, sizeof("persist.") - 1))
+            if (strncmp("persist.", entry->d_name, strlen("persist.")))
                 continue;
 #if HAVE_DIRENT_D_TYPE
             if (entry->d_type != DT_REG)
diff --git a/libacc/Android.mk b/libacc/Android.mk
new file mode 100644
index 0000000..f77e2b3
--- /dev/null
+++ b/libacc/Android.mk
@@ -0,0 +1,39 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Shared library for target
+# ========================================================
+
+LOCAL_MODULE:= libacc
+LOCAL_SRC_FILES := acc.cpp
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_SRC_FILES += disassem.cpp
+endif
+
+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
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_SRC_FILES += disassem.cpp
+endif
+
+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
new file mode 100644
index 0000000..1a44415
--- /dev/null
+++ b/libacc/FEATURES
@@ -0,0 +1,65 @@
+
+Supported C language subset (read joint example 'otccex.c' to have
+    an introduction to OTCC dialect):
+
+    - Expressions:
+
+        * binary operators, by decreasing priority order: '*' '/' '%',
+          '+' '-', '>>' '<<', '<' '<=' '>' '>=', '==' '!=', '&',
+          '^', '|', '=', '&&', '||'.
+
+        * '&&' and '||' have the same semantics as C : left to right
+          evaluation and early exit.
+
+        * Parenthesis are supported.
+
+        * Unary operators: '&', '*' (pointer indirection), '-'
+          (negation), '+', '!', '~', post fixed '++' and '--'.
+
+        * Pointer indirection ('*') only works with explicit cast to
+          'char *', 'int *' or 'int (*)()' (function pointer).
+
+        * '++', '--', and unary '&' can only be used with variable
+          lvalue (left value).
+
+        * '=' can only be used with variable or '*' (pointer
+          indirection) lvalue.
+
+        * Function calls are supported with standard i386 calling
+          convention. Function pointers are supported with explicit
+          cast. Functions can be used before being declared.
+
+    - Types: only signed integer ('int') variables and functions can
+      be declared. Variables cannot be initialized in
+      declarations. Only old K&R function declarations are parsed
+      (implicit integer return value and no types on arguments).
+
+    - Any function or variable from the libc can be used because OTCC
+      uses the libc dynamic linker to resolve undefined symbols.
+
+    - 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.
+
+    - 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).
+
+    - '#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. Only '\n',
+      '\"', '\'' and '\\' escapes are recognized.
+
+    - Both C comments ( /* */ ) and C++ comments ( // ... end-of-line ) can be used.
+
+    - No error is displayed if an incorrect program is given.
+
+    - 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
new file mode 100644
index 0000000..aea41e0
--- /dev/null
+++ b/libacc/LICENSE
@@ -0,0 +1,21 @@
+ 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/mountd/MODULE_LICENSE_APACHE2 b/libacc/MODULE_LICENSE_BSD_LIKE
similarity index 100%
rename from mountd/MODULE_LICENSE_APACHE2
rename to libacc/MODULE_LICENSE_BSD_LIKE
diff --git a/libacc/acc.cpp b/libacc/acc.cpp
new file mode 100644
index 0000000..adc8609
--- /dev/null
+++ b/libacc/acc.cpp
@@ -0,0 +1,4230 @@
+/*
+ * 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 <ctype.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cutils/hashmap.h>
+
+#if defined(__i386__)
+#include <sys/mman.h>
+#endif
+
+#if defined(__arm__)
+#include <unistd.h>
+#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
+
+#ifdef PROVIDE_ARM_CODEGEN
+#include "disassem.h"
+#endif
+
+#include <acc/acc.h>
+
+#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 ENABLE_ARM_DISASSEMBLY
+// #define PROVIDE_TRACE_CODEGEN
+
+namespace acc {
+
+// Subset of STL vector.
+template<class E> 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, TY_CHAR, TY_VOID, TY_FLOAT, TY_DOUBLE,
+        TY_POINTER, TY_FUNC, TY_PARAM
+    };
+
+    struct Type {
+        TypeTag tag;
+        tokenid_t id; // For function arguments
+        Type* pHead;
+        Type* pTail;
+    };
+
+    class CodeBuf {
+        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;
+        }
+
+        ~CodeBuf() {
+            release();
+        }
+
+        void init(int size) {
+            release();
+            mSize = size;
+            pProgramBase = (char*) calloc(1, size);
+            ind = pProgramBase;
+        }
+
+        void setErrorSink(ErrorSink* pErrorSink) {
+            mErrorSink = pErrorSink;
+        }
+
+        int o4(int n) {
+            if(check(4)) {
+                return 0;
+            }
+            intptr_t result = (intptr_t) ind;
+            * (int*) ind = n;
+            ind += 4;
+            return result;
+        }
+
+        /*
+         * Output a byte. Handles all values, 0..ff.
+         */
+        void ob(int n) {
+            if(check(1)) {
+                return;
+            }
+            *ind++ = n;
+        }
+
+        inline void* getBase() {
+            return (void*) pProgramBase;
+        }
+
+        intptr_t getSize() {
+            return ind - pProgramBase;
+        }
+
+        intptr_t getPC() {
+            return (intptr_t) ind;
+        }
+    };
+
+    /**
+     * 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(CodeBuf* pCodeBuf) {
+            this->pCodeBuf = pCodeBuf;
+            pCodeBuf->setErrorSink(mErrorSink);
+        }
+
+        virtual void setErrorSink(ErrorSink* pErrorSink) {
+            mErrorSink = pErrorSink;
+            if (pCodeBuf) {
+                pCodeBuf->setErrorSink(mErrorSink);
+            }
+        }
+
+        /* Emit a function prolog.
+         * argCount is the number of 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(int argCount) = 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(int argCount, int localVariableAddress,
+                                  int localVariableSize) = 0;
+
+        /* load immediate value to R0 */
+        virtual void li(int i, Type* pType) = 0;
+
+        /* Load floating point value from global address. */
+        virtual void loadFloat(int address, 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, Type* pResultType) = 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, Type* pResultType) = 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.
+         */
+        virtual void pushR0() = 0;
+
+        /* Store R0 to the address stored in TOS.
+         * The TOS is popped.
+         * pPointerType is the type of the pointer (of the input R0).
+         */
+        virtual void storeR0ToTOS(Type* pPointerType) = 0;
+
+        /* Load R0 from the address stored in R0.
+         * pPointerType is the type of the pointer (of the input R0).
+         */
+        virtual void loadR0FromR0(Type* pPointerType) = 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.
+         */
+        virtual void leaR0(int ea, Type* pPointerType) = 0;
+
+        /* Store R0 to a variable.
+         * If ea <= LOCAL, then this is a local variable, or an
+         * argument, addressed relative to FP.
+         * else it is an absolute global address.
+         */
+        virtual void storeR0(int ea, Type* pType) = 0;
+
+        /* load R0 from a variable.
+         * If ea <= LOCAL, then this is a local variable, or an
+         * argument, addressed relative to FP.
+         * else it is an absolute global address.
+         * If isIncDec is true, then the stored variable's value
+         * should be post-incremented or post-decremented, based
+         * on the value of op.
+         */
+        virtual void loadR0(int ea, bool isIncDec, int op, Type* pType) = 0;
+
+        /**
+         * Convert R0 to the given type.
+         */
+        virtual void convertR0(Type* pType) = 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) = 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(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 using PC-relative addressing. t is the PC-relative
+         * address of the function. It has already been adjusted for the
+         * architectural jump offset, so just store it as-is.
+         */
+        virtual void callRelative(int t, 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(int l, bool isIndirect) = 0;
+
+        /* Print a disassembly of the assembled code to out. Return
+         * non-zero if there is an error.
+         */
+        virtual int disassemble(FILE* out) = 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;
+
+        /*
+         * 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 alignment(Type* type) = 0;
+
+        /**
+         * Array element alignment (in bytes) for this type of data.
+         */
+        virtual size_t sizeOf(Type* type) = 0;
+
+        /**
+         * Stack argument size of this data type.
+         */
+        virtual size_t stackSizeOf(Type* pType) = 0;
+
+        virtual Type* getR0Type() {
+            return mExpressionStack.back();
+        }
+
+    protected:
+        /*
+         * Output a byte. Handles all values, 0..ff.
+         */
+        void ob(int n) {
+            pCodeBuf->ob(n);
+        }
+
+        intptr_t o4(int data) {
+            return pCodeBuf->o4(data);
+        }
+
+        intptr_t getBase() {
+            return (intptr_t) pCodeBuf->getBase();
+        }
+
+        intptr_t getPC() {
+            return pCodeBuf->getPC();
+        }
+
+        intptr_t getSize() {
+            return pCodeBuf->getSize();
+        }
+
+        void error(const char* fmt,...) {
+            va_list ap;
+            va_start(ap, fmt);
+            mErrorSink->verror(fmt, ap);
+            va_end(ap);
+        }
+
+        void assert(bool test) {
+            if (!test) {
+                * (char*) 0 = 0;
+                error("code generator assertion failed.");
+            }
+        }
+
+        void setR0Type(Type* pType) {
+            mExpressionStack.back() = pType;
+        }
+
+        Type* getTOSType() {
+            return mExpressionStack[mExpressionStack.size()-2];
+        }
+
+        void pushType() {
+            mExpressionStack.push_back(NULL);
+        }
+
+        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_VOID, TY_FLOAT, TY_DOUBLE, TY_INT,
+                    TY_VOID, TY_VOID};
+            return collapsedTag[tag];
+        }
+
+        TypeTag collapseTypeR0() {
+            return collapseType(getR0Type()->tag);
+        }
+
+        bool isFloatType(Type* pType) {
+            return isFloatTag(pType->tag);
+        }
+
+        bool isFloatTag(TypeTag tag) {
+            return tag == TY_FLOAT || tag == TY_DOUBLE;
+        }
+
+    private:
+        Vector<Type*> mExpressionStack;
+        CodeBuf* pCodeBuf;
+        ErrorSink* mErrorSink;
+    };
+
+#ifdef PROVIDE_ARM_CODEGEN
+
+    class ARMCodeGenerator : public CodeGenerator {
+    public:
+        ARMCodeGenerator() {}
+
+        virtual ~ARMCodeGenerator() {}
+
+        /* returns address to patch with local variable size
+        */
+        virtual int functionEntry(int argCount) {
+            LOG_API("functionEntry(%d);\n", argCount);
+            mStackUse = 0;
+            // sp -> arg4 arg5 ...
+            // Push our register-based arguments back on the stack
+            if (argCount > 0) {
+                int regArgCount = argCount <= 4 ? argCount : 4;
+                o4(0xE92D0000 | ((1 << argCount) - 1)); // stmfd    sp!, {}
+                mStackUse += regArgCount * 4;
+            }
+            // 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);
+            return o4(0xE24DD000); // sub    sp, sp, # <local variables>
+            // 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.
+        }
+
+        virtual void functionExit(int argCount, int localVariableAddress, int localVariableSize) {
+            LOG_API("functionExit(%d, %d, %d);\n", argCount, localVariableAddress, 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;
+
+            // 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 ....
+            if (argCount > 0) {
+                // 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 = argCount <= 4 ? argCount : 4;
+                o4(0xE28DD000 | (regArgCount << 2)); // add sp, sp, #argCount << 2
+            }
+            o4(0xE12FFF1E); // bx lr
+        }
+
+        /* load immediate value */
+        virtual void li(int t, Type* pType) {
+            LOG_API("li(%d);\n", t);
+            if (t >= 0 && t < 255) {
+                 o4(0xE3A00000 + t); // mov    r0, #0
+            } else if (t >= -256 && t < 0) {
+                // mvn means move constant ^ ~0
+                o4(0xE3E00001 - t); // mvn    r0, #0
+            } else {
+                  o4(0xE51F0000); //         ldr    r0, .L3
+                  o4(0xEA000000); //         b .L99
+                  o4(t);          // .L3:   .word 0
+                                  // .L99:
+            }
+            setR0Type(pType);
+        }
+
+        virtual void loadFloat(int address, Type* pType) {
+            error("Unimplemented.\n");
+            setR0Type(pType);
+        }
+
+        virtual int gjmp(int t) {
+            LOG_API("gjmp(%d);\n", t);
+            return o4(0xEA000000 | encodeAddress(t)); // b .L33
+        }
+
+        /* l = 0: je, l == 1: jne */
+        virtual int gtst(bool l, int t) {
+            LOG_API("gtst(%d, %d);\n", l, t);
+            o4(0xE3500000); // cmp r0,#0
+            int branch = l ? 0x1A000000 : 0x0A000000; // bne : beq
+            return o4(branch | encodeAddress(t));
+        }
+
+        virtual void gcmp(int op, Type* pResultType) {
+            LOG_API("gcmp(%d);\n", op);
+            o4(0xE8BD0002);  // ldmfd   sp!,{r1}
+            mStackUse -= 4;
+            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;
+            }
+            popType();
+        }
+
+        virtual void genOp(int op) {
+            LOG_API("genOp(%d);\n", op);
+            o4(0xE8BD0002);  // ldmfd   sp!,{r1}
+            mStackUse -= 4;
+            switch(op) {
+            case OP_MUL:
+                o4(0x0E0000091); // mul     r0,r1,r0
+                break;
+            case OP_DIV:
+                callRuntime(runtime_DIV);
+                break;
+            case OP_MOD:
+                callRuntime(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;
+            }
+            popType();
+        }
+
+        virtual void gUnaryCmp(int op, Type* pResultType) {
+            LOG_API("gUnaryCmp(%d);\n", op);
+            o4(0xE3A01000);  // mov    r1, #0
+            o4(0xE1510000); // cmp r1, r1
+            switch(op) {
+            case OP_LOGICAL_NOT:
+                o4(0x03A00000); // moveq r0,#0
+                o4(0x13A00001); // movne r0,#1
+                break;
+             default:
+                error("Unknown unary comparison op %d", op);
+                break;
+            }
+            setR0Type(pResultType);
+        }
+
+        virtual void genUnaryOp(int op) {
+            LOG_API("genOp(%d);\n", op);
+            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;
+            }
+        }
+
+        virtual void pushR0() {
+            LOG_API("pushR0();\n");
+            o4(0xE92D0001);  // stmfd   sp!,{r0}
+            mStackUse += 4;
+            pushType();
+            LOG_STACK("pushR0: %d\n", mStackUse);
+        }
+
+        virtual void storeR0ToTOS(Type* pPointerType) {
+            LOG_API("storeR0ToTOS(%d);\n", isInt);
+            assert(pPointerType->tag == TY_POINTER);
+            o4(0xE8BD0002);  // ldmfd   sp!,{r1}
+            mStackUse -= 4;
+            switch (pPointerType->pHead->tag) {
+                case TY_INT:
+                    o4(0xE5810000); // str r0, [r1]
+                    break;
+                case TY_CHAR:
+                    o4(0xE5C10000); // strb r0, [r1]
+                    break;
+                default:
+                    error("storeR0ToTOS: unimplemented type");
+                    break;
+            }
+            popType();
+        }
+
+        virtual void loadR0FromR0(Type* pPointerType) {
+            LOG_API("loadR0FromR0(%d);\n", pPointerType);
+            assert(pPointerType->tag == TY_POINTER);
+            switch (pPointerType->pHead->tag) {
+                case TY_INT:
+                    o4(0xE5900000); // ldr r0, [r0]
+                    break;
+                case TY_CHAR:
+                    o4(0xE5D00000); // ldrb r0, [r0]
+                    break;
+                default:
+                    error("loadR0FromR0: unimplemented type");
+                    break;
+            }
+            setR0Type(pPointerType->pHead);
+        }
+
+        virtual void leaR0(int ea, Type* pPointerType) {
+            LOG_API("leaR0(%d);\n", ea);
+            if (ea < LOCAL) {
+                // Local, fp relative
+                if (ea < -1023 || ea > 1023 || ((ea & 3) != 0)) {
+                    error("Offset out of range: %08x", ea);
+                }
+                if (ea < 0) {
+                    o4(0xE24B0F00 | (0xff & ((-ea) >> 2))); // sub    r0, fp, #ea
+                } else {
+                    o4(0xE28B0F00 | (0xff & (ea >> 2))); // add    r0, fp, #ea
+                }
+            } else {
+                // Global, absolute.
+                o4(0xE59F0000); //        ldr    r0, .L1
+                o4(0xEA000000); //        b .L99
+                o4(ea);         // .L1:   .word 0
+                                // .L99:
+            }
+            setR0Type(pPointerType);
+        }
+
+        virtual void storeR0(int ea, Type* pType) {
+            LOG_API("storeR0(%d);\n", ea);
+            if (ea < LOCAL) {
+                // Local, fp relative
+                if (ea < -4095 || ea > 4095) {
+                    error("Offset out of range: %08x", ea);
+                }
+                if (ea < 0) {
+                    o4(0xE50B0000 | (0xfff & (-ea))); // str r0, [fp,#-ea]
+                } else {
+                    o4(0xE58B0000 | (0xfff & ea)); // str r0, [fp,#ea]
+                }
+            } else{
+                // Global, absolute
+                o4(0xE59F1000); //         ldr r1, .L1
+                o4(0xEA000000); //         b .L99
+                o4(ea);         // .L1:    .word 0
+                o4(0xE5810000); // .L99:   str r0, [r1]
+            }
+        }
+
+        virtual void loadR0(int ea, bool isIncDec, int op, Type* pType) {
+            LOG_API("loadR0(%d, %d, %d, %d);\n", ea, isIncDec, op, pType);
+            if (ea < LOCAL) {
+                // Local, fp relative
+                if (ea < -4095 || ea > 4095) {
+                    error("Offset out of range: %08x", ea);
+                }
+                if (ea < 0) {
+                    o4(0xE51B0000 | (0xfff & (-ea))); // ldr r0, [fp,#-ea]
+                } else {
+                    o4(0xE59B0000 | (0xfff & ea));    // ldr r0, [fp,#ea]
+                }
+            } else {
+                // Global, absolute
+                o4(0xE59F2000); //        ldr r2, .L1
+                o4(0xEA000000); //        b .L99
+                o4(ea);         // .L1:   .word ea
+                o4(0xE5920000); // .L99:  ldr r0, [r2]
+            }
+
+            if (isIncDec) {
+                switch (op) {
+                case OP_INCREMENT:
+                    o4(0xE2801001); // add r1, r0, #1
+                    break;
+                case OP_DECREMENT:
+                    o4(0xE2401001); // sub r1, r0, #1
+                    break;
+                default:
+                    error("unknown opcode: %d", op);
+                }
+                if (ea < LOCAL) {
+                    // Local, fp relative
+                    // Don't need range check, was already checked above
+                    if (ea < 0) {
+                        o4(0xE50B1000 | (0xfff & (-ea))); // str r1, [fp,#-ea]
+                    } else {
+                        o4(0xE58B1000 | (0xfff & ea));    // str r1, [fp,#ea]
+                    }
+                } else{
+                    // Global, absolute
+                    // r2 is already set up from before.
+                    o4(0xE5821000); // str r1, [r2]
+               }
+            }
+            setR0Type(pType);
+        }
+
+        virtual void convertR0(Type* pType){
+            Type* pR0Type = getR0Type();
+            if (bitsSame(pType, pR0Type)) {
+                // do nothing special
+            } else if (isFloatType(pType) && isFloatType(pR0Type)) {
+                // do nothing special, both held in same register on x87.
+            } else {
+                error("Incompatible types old: %d new: %d",
+                      pR0Type->tag, pType->tag);
+            }
+            setR0Type(pType);
+        }
+
+        virtual int beginFunctionCallArguments() {
+            LOG_API("beginFunctionCallArguments();\n");
+            return o4(0xE24DDF00); // Placeholder
+        }
+
+        virtual size_t storeR0ToArg(int l) {
+            LOG_API("storeR0ToArg(%d);\n", l);
+            if (l < 0 || l > 4096-4) {
+                error("l out of range for stack offset: 0x%08x", l);
+            }
+            o4(0xE58D0000 + l); // str r0, [sp, #4]
+            return 4;
+        }
+
+        virtual void endFunctionCallArguments(int a, int l) {
+            LOG_API("endFunctionCallArguments(0x%08x, %d);\n", a, l);
+            int argCount = l >> 2;
+            int argumentStackUse = l;
+            if (argCount > 0) {
+                int regArgCount = argCount > 4 ? 4 : argCount;
+                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);
+            }
+            * (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) {
+            LOG_API("callForward(%d);\n", symbol);
+            setR0Type(pFunc->pHead);
+            // Forward calls are always short (local)
+            return o4(0xEB000000 | encodeAddress(symbol));
+        }
+
+        virtual void callRelative(int t, Type* pFunc) {
+            LOG_API("callRelative(%d);\n", t);
+            setR0Type(pFunc->pHead);
+            int abs = t + getPC() + jumpOffset();
+            LOG_API("abs=%d (0x%08x)\n", abs, abs);
+            if (t >= - (1 << 25) && t < (1 << 25)) {
+                o4(0xEB000000 | encodeAddress(t));
+            } else {
+                // Long call.
+                o4(0xE59FC000); //         ldr    r12, .L1
+                o4(0xEA000000); //         b .L99
+                o4(t - 12);     // .L1:    .word 0
+                o4(0xE08CC00F); // .L99:   add r12,pc
+                o4(0xE12FFF3C); //         blx r12
+           }
+        }
+
+        virtual void callIndirect(int l, Type* pFunc) {
+            LOG_API("callIndirect(%d);\n", l);
+            setR0Type(pFunc->pHead);
+            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
+        }
+
+        virtual void adjustStackAfterCall(int l, bool isIndirect) {
+            LOG_API("adjustStackAfterCall(%d, %d);\n", l, isIndirect);
+            int argCount = l >> 2;
+            int stackArgs = argCount > 4 ? argCount - 4 : 0;
+            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) {
+            LOG_API("gsym(0x%x)\n", t);
+            int n;
+            int base = getBase();
+            int pc = getPC();
+            LOG_API("pc = 0x%x\n", pc);
+            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;
+            }
+        }
+
+        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
+        }
+
+        virtual int disassemble(FILE* out) {
+#ifdef ENABLE_ARM_DISASSEMBLY
+            disasmOut = out;
+            disasm_interface_t  di;
+            di.di_readword = disassemble_readword;
+            di.di_printaddr = disassemble_printaddr;
+            di.di_printf = disassemble_printf;
+
+            int base = getBase();
+            int pc = getPC();
+            for(int i = base; i < pc; i += 4) {
+                fprintf(out, "%08x: %08x  ", i, *(int*) i);
+                ::disasm(&di, i, 0);
+            }
+#endif
+            return 0;
+        }
+
+        /**
+         * alignment (in bytes) for this type of data
+         */
+        virtual size_t alignment(Type* pType){
+            switch(pType->tag) {
+                case TY_DOUBLE:
+                    return 8;
+                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_CHAR:
+                    return 1;
+                default:
+                    return 0;
+                case TY_FLOAT:
+                    return 4;
+                case TY_DOUBLE:
+                    return 8;
+                case TY_POINTER:
+                    return 4;
+            }
+        }
+
+        virtual size_t stackSizeOf(Type* pType) {
+            switch(pType->tag) {
+                case TY_DOUBLE:
+                    return 8;
+                default:
+                    return 4;
+            }
+        }
+
+    private:
+        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 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);
+        }
+
+        typedef int (*int2FnPtr)(int a, int b);
+        void callRuntime(int2FnPtr fn) {
+            o4(0xE59F2000); // ldr    r2, .L1
+            o4(0xEA000000); // b      .L99
+            o4((int) fn);   //.L1:  .word  fn
+            o4(0xE12FFF32); //.L99: blx    r2
+        }
+
+        static int runtime_DIV(int a, int b) {
+            return b / a;
+        }
+
+        static int runtime_MOD(int a, int b) {
+            return b % a;
+        }
+
+        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(int argCount) {
+            o(0xe58955); /* push   %ebp, mov %esp, %ebp */
+            return oad(0xec81, 0); /* sub $xxx, %esp */
+        }
+
+        virtual void functionExit(int argCount, int localVariableAddress, int localVariableSize) {
+            o(0xc3c9); /* leave, ret */
+            *(int *) localVariableAddress = localVariableSize; /* save local variables */
+        }
+
+        /* load immediate value */
+        virtual void li(int i, Type* pType) {
+            oad(0xb8, i); /* mov $xx, %eax */
+            setR0Type(pType);
+        }
+
+        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 int gjmp(int t) {
+            return psym(0xe9, t);
+        }
+
+        /* l = 0: je, l == 1: jne */
+        virtual int gtst(bool l, int t) {
+            o(0x0fc085); /* test %eax, %eax, je/jne xxx */
+            return psym(0x84 + l, t);
+        }
+
+        virtual void gcmp(int op, Type* pResultType) {
+            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, NULL);
+                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(pResultType);
+        }
+
+        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) {
+                // TODO: Deal with pointer arithmetic
+                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;
+                }
+                popType();
+                setR0Type(pResultType);
+                printf("genop: result type %d\n", pResultType->tag);
+            }
+        }
+
+
+
+        virtual void gUnaryCmp(int op, Type* pResultType) {
+            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, NULL);
+                            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("genUnaryCmp unsupported type");
+                        break;
+                }
+            }
+            setR0Type(pResultType);
+        }
+
+        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 %d", r0ct);
+                    break;
+            }
+            pushType();
+        }
+
+        virtual void storeR0ToTOS(Type* pPointerType) {
+            assert(pPointerType->tag == TY_POINTER);
+            o(0x59); /* pop %ecx */
+            popType();
+            switch (pPointerType->pHead->tag) {
+                case TY_INT:
+                    o(0x0189); /* movl %eax/%al, (%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;
+                default:
+                    error("storeR0ToTOS: unsupported type");
+                    break;
+            }
+        }
+
+        virtual void loadR0FromR0(Type* pPointerType) {
+            assert(pPointerType->tag == TY_POINTER);
+            switch (pPointerType->pHead->tag) {
+                case TY_INT:
+                    o(0x8b); /* mov (%eax), %eax */
+                    break;
+                case TY_CHAR:
+                    o(0xbe0f); /* movsbl (%eax), %eax */
+                    break;
+                default:
+                    error("loadR0FromR0: unsupported type");
+                    break;
+            }
+            ob(0); /* add zero in code */
+            setR0Type(pPointerType->pHead);
+        }
+
+        virtual void leaR0(int ea, Type* pPointerType) {
+            gmov(10, ea); /* leal EA, %eax */
+            setR0Type(pPointerType);
+        }
+
+        virtual void storeR0(int ea, Type* pType) {
+            TypeTag tag = pType->tag;
+            switch (tag) {
+                case TY_INT:
+                    gmov(6, ea); /* mov %eax, EA */
+                    break;
+                case TY_FLOAT:
+                    if (ea < -LOCAL || ea > LOCAL) {
+                        oad(0x1dd9, ea); // fstps ea
+                    } else {
+                        oad(0x9dd9, ea); // fstps ea(%ebp)
+                    }
+                    break;
+                case TY_DOUBLE:
+                    if (ea < -LOCAL || ea > LOCAL) {
+                        oad(0x1ddd, ea); // fstpl ea
+                    } else {
+                        oad(0x9ddd, ea); // fstpl ea(%ebp)
+                    }
+                    break;
+                default:
+                    error("Unable to store to type %d", tag);
+                    break;
+            }
+        }
+
+        virtual void loadR0(int ea, bool isIncDec, int op, Type* pType) {
+            TypeTag tag = collapseType(pType->tag);
+            switch (tag) {
+                case TY_INT:
+                    gmov(8, ea); /* mov EA, %eax */
+                    if (isIncDec) {
+                        /* Implement post-increment or post decrement.
+                         */
+                        gmov(0, ea); /* 83 ADD */
+                        o(decodeOp(op));
+                    }
+                    break;
+                case TY_FLOAT:
+                    if (ea < -LOCAL || ea > LOCAL) {
+                        oad(0x05d9, ea); // flds ea
+                    } else {
+                        oad(0x85d9, ea); // flds ea(%ebp)
+                    }
+                    if (isIncDec) {
+                        error("inc/dec not implemented for float.");
+                    }
+                    break;
+                case TY_DOUBLE:
+                    if (ea < -LOCAL || ea > LOCAL) {
+                        oad(0x05dd, ea); // fldl ea
+                    } else {
+                        oad(0x85dd, ea); // fldl ea(%ebp)
+                    }
+                    if (isIncDec) {
+                        error("inc/dec not implemented for double.");
+                    }
+                    break;
+                default:
+                    error("Unable to load type %d", tag);
+                    break;
+            }
+            setR0Type(pType);
+        }
+
+        virtual void convertR0(Type* pType){
+            Type* pR0Type = getR0Type();
+            if (pR0Type == NULL) {
+                assert(false);
+                setR0Type(pType);
+                return;
+            }
+            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* 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(int a, int l) {
+            * (int*) a = l;
+        }
+
+        virtual int callForward(int symbol, Type* pFunc) {
+            setR0Type(pFunc->pHead);
+            return psym(0xe8, symbol); /* call xxx */
+        }
+
+        virtual void callRelative(int t, Type* pFunc) {
+            setR0Type(pFunc->pHead);
+            psym(0xe8, t); /* call xxx */
+        }
+
+        virtual void callIndirect(int l, Type* pFunc) {
+            setR0Type(pFunc->pHead);
+            oad(0x2494ff, l); /* call *xxx(%esp) */
+        }
+
+        virtual void adjustStackAfterCall(int l, bool isIndirect) {
+            if (isIndirect) {
+                l += 4;
+            }
+            if (l > 0) {
+                oad(0xc481, l); /* add $xxx, %esp */
+            }
+        }
+
+        virtual int jumpOffset() {
+            return 5;
+        }
+
+        virtual int disassemble(FILE* out) {
+            return 0;
+        }
+
+        /* 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;
+            }
+        }
+
+        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 alignment(Type* pType){
+            switch(pType->tag) {
+                case TY_DOUBLE:
+                    return 8;
+                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_CHAR:
+                    return 1;
+                default:
+                    return 0;
+                case TY_FLOAT:
+                    return 4;
+                case TY_DOUBLE:
+                    return 8;
+                case TY_POINTER:
+                    return 4;
+            }
+        }
+
+        virtual size_t stackSizeOf(Type* pType) {
+            switch(pType->tag) {
+                case TY_DOUBLE:
+                    return 8;
+                default:
+                    return 4;
+            }
+        }
+
+    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;
+            }
+        }
+
+        /* 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
+                }
+            }
+        }
+    };
+
+#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(CodeBuf* pCodeBuf) {
+            mpBase->init(pCodeBuf);
+        }
+
+        void setErrorSink(ErrorSink* pErrorSink) {
+            mpBase->setErrorSink(pErrorSink);
+        }
+
+        /* returns address to patch with local variable size
+        */
+        virtual int functionEntry(int argCount) {
+            int result = mpBase->functionEntry(argCount);
+            fprintf(stderr, "functionEntry(%d) -> %d\n", argCount, result);
+            return result;
+        }
+
+        virtual void functionExit(int argCount, int localVariableAddress, int localVariableSize) {
+            fprintf(stderr, "functionExit(%d, %d, %d)\n",
+                    argCount, localVariableAddress, localVariableSize);
+            mpBase->functionExit(argCount, localVariableAddress, localVariableSize);
+        }
+
+        /* load immediate value */
+        virtual void li(int t, Type* pType) {
+            fprintf(stderr, "li(%d)\n", t);
+            mpBase->li(t, pType);
+        }
+
+        virtual void loadFloat(int address, Type* pType) {
+            fprintf(stderr, "loadFloat(%d, type)\n", address);
+            mpBase->loadFloat(address, 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, Type* pResultType) {
+            fprintf(stderr, "gcmp(%d, pResultType)\n", op);
+            mpBase->gcmp(op, pResultType);
+        }
+
+        virtual void genOp(int op) {
+            fprintf(stderr, "genOp(%d)\n", op);
+            mpBase->genOp(op);
+        }
+
+
+        virtual void gUnaryCmp(int op, Type* pResultType) {
+            fprintf(stderr, "gUnaryCmp(%d, pResultType)\n", op);
+            mpBase->gUnaryCmp(op, pResultType);
+        }
+
+        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 storeR0ToTOS(Type* pPointerType) {
+            fprintf(stderr, "storeR0ToTOS(%d)\n", pPointerType->pHead->tag);
+            mpBase->storeR0ToTOS(pPointerType);
+        }
+
+        virtual void loadR0FromR0(Type* pPointerType) {
+            fprintf(stderr, "loadR0FromR0(%d)\n", pPointerType->pHead->tag);
+            mpBase->loadR0FromR0(pPointerType);
+        }
+
+        virtual void leaR0(int ea, Type* pPointerType) {
+            fprintf(stderr, "leaR0(%d)\n", ea);
+            mpBase->leaR0(ea, pPointerType);
+        }
+
+        virtual void storeR0(int ea, Type* pType) {
+            fprintf(stderr, "storeR0(%d, pType)\n", ea);
+            mpBase->storeR0(ea, pType);
+        }
+
+        virtual void loadR0(int ea, bool isIncDec, int op, Type* pType) {
+            fprintf(stderr, "loadR0(%d, %d, %d, pType)\n", ea, isIncDec, op);
+            mpBase->loadR0(ea, isIncDec, op, pType);
+        }
+
+        virtual void convertR0(Type* pType){
+            fprintf(stderr, "convertR0(pType)\n");
+            mpBase->convertR0(pType);
+        }
+
+        virtual int beginFunctionCallArguments() {
+            int result = mpBase->beginFunctionCallArguments();
+            fprintf(stderr, "beginFunctionCallArguments() = %d\n", result);
+            return result;
+        }
+
+        virtual size_t storeR0ToArg(int l) {
+            fprintf(stderr, "storeR0ToArg(%d)\n", l);
+            return mpBase->storeR0ToArg(l);
+        }
+
+        virtual void endFunctionCallArguments(int a, int l) {
+            fprintf(stderr, "endFunctionCallArguments(%d, %d)\n", a, l);
+            mpBase->endFunctionCallArguments(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 callRelative(int t, Type* pFunc) {
+            fprintf(stderr, "callRelative(%d)\n", t);
+            mpBase->callRelative(t, pFunc);
+        }
+
+        virtual void callIndirect(int l, Type* pFunc) {
+            fprintf(stderr, "callIndirect(%d)\n", l);
+            mpBase->callIndirect(l, pFunc);
+        }
+
+        virtual void adjustStackAfterCall(int l, bool isIndirect) {
+            fprintf(stderr, "adjustStackAfterCall(%d, %d)\n", l, isIndirect);
+            mpBase->adjustStackAfterCall(l, isIndirect);
+        }
+
+        virtual int jumpOffset() {
+            return mpBase->jumpOffset();
+        }
+
+        virtual int disassemble(FILE* out) {
+            return mpBase->disassemble(out);
+        }
+
+        /* output a symbol and patch all calls to it */
+        virtual void gsym(int t) {
+            fprintf(stderr, "gsym(%d)\n", t);
+            mpBase->gsym(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 alignment(Type* pType){
+            return mpBase->alignment(pType);
+        }
+
+        /**
+         * Array element alignment (in bytes) for this type of data.
+         */
+        virtual size_t sizeOf(Type* pType){
+            return mpBase->sizeOf(pType);
+        }
+
+
+        virtual size_t stackSizeOf(Type* pType) {
+            return mpBase->stackSizeOf(pType);
+        }
+
+
+        virtual Type* getR0Type() {
+            return mpBase->getR0Type();
+        }
+    };
+
+#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<Chunk> mData;
+    };
+
+    struct VariableInfo;
+
+    struct Token {
+        int hash;
+        size_t length;
+        char* pText;
+        tokenid_t id;
+
+        // Current values for the token
+        char* mpMacroDefinition;
+        VariableInfo* mpVariableInfo;
+    };
+
+    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<Token*> mTokens;
+        Arena* mpArena;
+    };
+
+    class InputStream {
+    public:
+        virtual ~InputStream() {}
+        int getChar() {
+            if (bumpLine) {
+                line++;
+                bumpLine = false;
+            }
+            int ch = get();
+            if (ch == '\n') {
+                bumpLine = true;
+            }
+            return ch;
+        }
+        int getLine() {
+            return line;
+        }
+    protected:
+        InputStream() :
+            line(1), bumpLine(false) {
+        }
+    private:
+        virtual int get() = 0;
+        int line;
+        bool bumpLine;
+    };
+
+    class FileInputStream : public InputStream {
+    public:
+        FileInputStream(FILE* in) : f(in) {}
+    private:
+        virtual int get() { return fgetc(f); }
+        FILE* f;
+    };
+
+    class TextInputStream : public InputStream {
+    public:
+        TextInputStream(const char* text, size_t textLength)
+            : pText(text), mTextLength(textLength), mPosition(0) {
+        }
+
+    private:
+        virtual int get() {
+            return mPosition < mTextLength ? pText[mPosition++] : EOF;
+        }
+
+        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",
+            "pragma",
+            "define",
+            "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",
+            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;
+    };
+
+    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();
+                (*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();
+        }
+
+        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* 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<VariableInfo*> mStack;
+        Vector<Mark> 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;
+    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;
+
+    // Arena for the duration of the compile
+    Arena mGlobalArena;
+    // Arena for data that's only needed when compiling a single function
+    Arena mLocalArena;
+
+    TokenTable mTokenTable;
+    SymbolStack mGlobals;
+    SymbolStack mLocals;
+
+    // Prebuilt types, makes things slightly faster.
+    Type* mkpInt;        // int
+    Type* mkpChar;       // char
+    Type* mkpVoid;       // void
+    Type* mkpFloat;
+    Type* mkpDouble;
+    Type* mkpIntFn;
+    Type* mkpIntPtr;
+    Type* mkpCharPtr;
+    Type* mkpFloatPtr;
+    Type* mkpDoublePtr;
+    Type* mkpPtrIntFn;
+
+    InputStream* file;
+
+    CodeBuf codeBuf;
+    CodeGenerator* pGen;
+
+    String mErrorBuf;
+
+    String mPragmas;
+    int mPragmaStringCount;
+
+    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;
+
+    // 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_PRAGMA,
+        TOK_DEFINE,
+        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 tokens
+        TOK_SYMBOL
+    };
+
+    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 assert(bool isTrue) {
+        if (!isTrue) {
+            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
+            ch = file->getChar();
+#if 0
+        printf("ch='%c' 0x%x\n", ch, ch);
+#endif
+    }
+
+    int isid() {
+        return isalnum(ch) | (ch == '_');
+    }
+
+    /* 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)) {
+                        int d = ch;
+                        if (isdigit(d)) {
+                            d -= '0';
+                        } else if (d <= 'F') {
+                            d = d - 'A' + 10;
+                        } else {
+                            d = d - 'a' + 10;
+                        }
+                        val = (val << 4) + d;
+                        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.
+        acceptCh('.');
+        acceptDigitsCh();
+        bool doExp = true;
+        if (acceptCh('e') || acceptCh('E')) {
+            // Don't need to do any extra work
+        } else if (ch == 'f' || ch == 'F') {
+            pdef('e'); // So it can be parsed by strtof.
+            inp();
+            tok = TOK_NUM_FLOAT;
+        } else {
+            doExp = false;
+        }
+        if (doExp) {
+            bool digitsRequired = acceptCh('-');
+            bool digitsFound = acceptDigitsCh();
+            if (digitsRequired && ! digitsFound) {
+                error("malformed exponent");
+            }
+        }
+        char* pText = mTokenString.getUnwrapped();
+        if (tok == TOK_NUM_FLOAT) {
+            tokd = strtof(pText, 0);
+        } else {
+            tokd = strtod(pText, 0);
+        }
+        //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 {
+                    error("Unsupported preprocessor directive \"%s\"",
+                          mTokenString.getUnwrapped());
+                }
+            }
+            inp();
+        }
+        tokl = 0;
+        tok = ch;
+        /* encode identifiers & numbers */
+        if (isid()) {
+            mTokenString.clear();
+            while (isid()) {
+                pdef(ch);
+                inp();
+            }
+            if (isdigit(tok)) {
+                // Start of a numeric constant. Could be integer, float, or
+                // double, won't know until we look further.
+                if (ch == '.' || ch == 'e' || ch == 'e'
+                    || ch == 'f' || ch == 'F')  {
+                    parseFloat();
+                } else {
+                    // It's an integer constant
+                    tokc = strtol(mTokenString.getUnwrapped(), 0, 0);
+                    tok = TOK_NUM;
+                }
+            } else {
+                tok = mTokenTable.intern(mTokenString.getUnwrapped(),
+                                         mTokenString.len());
+                // 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 {
+                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 */
+                        }
+                        break;
+                    }
+                    opIndex++;
+                }
+                if (l == 0) {
+                    tokl = 0;
+                    tokc = 0;
+                }
+            }
+        }
+#if 0
+        {
+            String buf;
+            decodeToken(buf, tok);
+            fprintf(stderr, "%s\n", buf.getUnwrapped());
+        }
+#endif
+    }
+
+    void doDefine() {
+        next();
+        tokenid_t name = tok;
+        String* pName = new String();
+        while (isspace(ch)) {
+            inp();
+        }
+        if (ch == '(') {
+            delete pName;
+            error("Defines with arguments not supported");
+            return;
+        }
+        while (isspace(ch)) {
+            inp();
+        }
+        String value;
+        while (ch != '\n' && 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;
+    }
+
+    virtual void verror(const char* fmt, va_list ap) {
+        mErrorBuf.printf("%ld: ", file->getLine());
+        mErrorBuf.vprintf(fmt, ap);
+        mErrorBuf.printf("\n");
+    }
+
+    void skip(intptr_t c) {
+        if (tok != c) {
+            error("'%c' expected", c);
+        }
+        next();
+    }
+
+    bool accept(intptr_t c) {
+        if (tok == c) {
+            next();
+            return true;
+        }
+        return false;
+    }
+
+    bool acceptStringLiteral() {
+        if (tok == '"') {
+            pGen->li((int) glo, mkpCharPtr);
+            // 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;
+    }
+    /* Parse and evaluate a unary expression.
+     * allowAssignment is true if '=' parsing wanted (quick hack)
+     */
+    void unary(bool allowAssignment) {
+        intptr_t n, t, a;
+        t = 0;
+        n = 1; /* type of expression 0 = forward, 1 = value, other = lvalue */
+        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, mkpInt);
+            } 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(false);
+                if (t == '!')
+                    pGen->gUnaryCmp(a, mkpInt);
+                else if (t == '+') {
+                    // ignore unary plus.
+                } else {
+                    pGen->genUnaryOp(a);
+                }
+            } else if (t == '(') {
+                expr();
+                skip(')');
+            } else if (t == '*') {
+                /* This is a pointer dereference, but we currently only
+                 * support a pointer dereference if it's immediately
+                 * in front of a cast. So parse the cast right here.
+                 */
+                skip('(');
+                Type* pCast = expectCastTypeDeclaration(mLocalArena);
+                // We currently only handle 3 types of cast:
+                // (int*), (char*) , (int (*)())
+                if(typeEqual(pCast, mkpIntPtr)) {
+                    t = TOK_INT;
+                } else if (typeEqual(pCast, mkpCharPtr)) {
+                    t = TOK_CHAR;
+                } else if (typeEqual(pCast, mkpFloatPtr)) {
+                    t = TOK_FLOAT;
+                } else if (typeEqual(pCast, mkpDoublePtr)) {
+                    t = TOK_DOUBLE;
+                } else if (typeEqual(pCast, mkpPtrIntFn)){
+                    t = 0;
+                } else {
+                    String buffer;
+                    decodeType(buffer, pCast);
+                    error("Unsupported cast type %s", buffer.getUnwrapped());
+                    decodeType(buffer, mkpPtrIntFn);
+                }
+                skip(')');
+                unary(false);
+                if (accept('=')) {
+                    pGen->pushR0();
+                    expr();
+                    pGen->storeR0ToTOS(pCast);
+                } else if (t) {
+                    pGen->loadR0FromR0(pCast);
+                }
+                // Else we fall through to the function call below, with
+                // t == 0 to trigger an indirect function call. Hack!
+            } else if (t == '&') {
+                VariableInfo* pVI = VI(tok);
+                pGen->leaR0((int) pVI->pAddress,
+                            createPtrType(pVI->pType, mLocalArena));
+                next();
+            } else if (t == EOF ) {
+                error("Unexpected EOF.");
+            } 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);
+                n = (intptr_t) pVI->pAddress;
+                /* forward reference: try dlsym */
+                if (!n) {
+                    n = (intptr_t) dlsym(RTLD_DEFAULT, nameof(t));
+                    if (tok == '(') {
+                        pVI->pType = mkpIntFn;
+                    } else {
+                        pVI->pType = mkpInt;
+                    }
+                    pVI->pAddress = (void*) n;
+                }
+                if ((tok == '=') & allowAssignment) {
+                    /* assignment */
+                    next();
+                    expr();
+                    pGen->storeR0(n, pVI->pType);
+                } else if (tok != '(') {
+                    /* variable */
+                    if (!n) {
+                        error("Undefined variable %s", nameof(t));
+                    }
+                    pGen->loadR0(n, tokl == 11, tokc, pVI->pType);
+                    if (tokl == 11) {
+                        next();
+                    }
+                }
+            }
+        }
+
+        /* function call */
+        if (accept('(')) {
+            Type* pArgList = NULL;
+            VariableInfo* pVI = NULL;
+            if (n == 1) { // Indirect function call, push address of fn.
+                pArgList = pGen->getR0Type()->pTail;
+                pGen->pushR0();
+            } else {
+                pVI = VI(t);
+                pArgList = pVI->pType->pTail;
+            }
+            bool varArgs = pArgList == NULL;
+            /* push args and invert order */
+            a = pGen->beginFunctionCallArguments();
+            int l = 0;
+            while (tok != ')' && tok != EOF) {
+                if (! varArgs && !pArgList) {
+                    error ("Unexpected argument.");
+                }
+                expr();
+                Type* pTargetType;
+                if (pArgList) {
+                    pTargetType = pArgList->pHead;
+                    pArgList = pArgList->pTail;
+                } else {
+                    pTargetType = pGen->getR0Type();
+                    if (pTargetType->tag == TY_FLOAT) {
+                        pTargetType = mkpDouble;
+                    }
+                }
+                pGen->convertR0(pTargetType);
+                l += pGen->storeR0ToArg(l);
+                if (accept(',')) {
+                    // fine
+                } else if ( tok != ')') {
+                    error("Expected ',' or ')'");
+                }
+            }
+            if (! varArgs && pArgList) {
+                error ("Expected more argument(s).");
+            }
+            pGen->endFunctionCallArguments(a, l);
+            skip(')');
+            if (!n) {
+                /* forward reference */
+                pVI->pForward = (void*) pGen->callForward((int) pVI->pForward,
+                                                          pVI->pType);
+            } else if (n == 1) {
+                pGen->callIndirect(l, mkpPtrIntFn->pHead);
+            } else {
+                pGen->callRelative(n - codeBuf.getPC() - pGen->jumpOffset(),
+                                   VI(t)->pType);
+            }
+            pGen->adjustStackAfterCall(l, n == 1);
+        }
+    }
+
+    /* Recursive descent parser for binary operations.
+     */
+    void binaryOp(int level) {
+        intptr_t t, n, a;
+        t = 0;
+        if (level-- == 1)
+            unary(true);
+        else {
+            binaryOp(level);
+            a = 0;
+            while (level == tokl) {
+                n = tok;
+                t = tokc;
+                next();
+
+                if (level > 8) {
+                    a = pGen->gtst(t == OP_LOGICAL_OR, a); /* && and || output code generation */
+                    binaryOp(level);
+                } else {
+                    pGen->pushR0();
+                    binaryOp(level);
+
+                    if ((level == 4) | (level == 5)) {
+                        pGen->gcmp(t, mkpInt);
+                    } else {
+                        pGen->genOp(t);
+                    }
+                }
+            }
+            /* && and || output code generation */
+            if (a && level > 8) {
+                a = pGen->gtst(t == OP_LOGICAL_OR, a);
+                pGen->li(t != OP_LOGICAL_OR, mkpInt);
+                pGen->gjmp(5); /* jmp $ + 5 (sizeof li, FIXME for ARM) */
+                pGen->gsym(a);
+                pGen->li(t == OP_LOGICAL_OR, mkpInt);
+            }
+        }
+    }
+
+    void expr() {
+        binaryOp(11);
+    }
+
+    int test_expr() {
+        expr();
+        return pGen->gtst(0, 0);
+    }
+
+    void block(intptr_t l, bool outermostFunctionBlock) {
+        intptr_t a, n, t;
+
+        Type* pBaseType;
+        if ((pBaseType = acceptPrimitiveType(mLocalArena))) {
+            /* 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 = codeBuf.getPC(); // top of loop, target of "next" iteration
+                a = test_expr();
+            } else {
+                if (tok != ';')
+                    expr();
+                skip(';');
+                n = codeBuf.getPC();
+                a = 0;
+                if (tok != ';')
+                    a = test_expr();
+                skip(';');
+                if (tok != ')') {
+                    t = pGen->gjmp(0);
+                    expr();
+                    pGen->gjmp(n - codeBuf.getPC() - pGen->jumpOffset());
+                    pGen->gsym(t);
+                    n = t + 4;
+                }
+            }
+            skip(')');
+            block((intptr_t) &a, false);
+            pGen->gjmp(n - codeBuf.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 != ';') {
+                    expr();
+                    pGen->convertR0(pReturnType);
+                }
+                rsym = pGen->gjmp(rsym); /* jmp */
+            } else if (accept(TOK_BREAK)) {
+                *(int *) l = pGen->gjmp(*(int *) l);
+            } else if (tok != ';')
+                expr();
+            skip(';');
+        }
+    }
+
+    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_FUNC || at == TY_PARAM) {
+            return typeEqual(a->pHead, b->pHead)
+                && typeEqual(a->pTail, b->pTail);
+        }
+        return true;
+    }
+
+    Type* createType(TypeTag tag, Type* pHead, Type* pTail, Arena& arena) {
+        assert(tag >= TY_INT && tag <= TY_PARAM);
+        Type* pType = (Type*) arena.alloc(sizeof(Type));
+        memset(pType, 0, sizeof(*pType));
+        pType->tag = tag;
+        pType->pHead = pHead;
+        pType->pTail = pTail;
+        return pType;
+    }
+
+    Type* createPtrType(Type* pType, Arena& arena) {
+        return createType(TY_POINTER, pType, NULL, arena);
+    }
+
+    /**
+     * 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);
+
+        String temp;
+        if (pType->id != 0) {
+            decodeToken(temp, pType->id);
+            buffer.append(temp);
+        }
+
+        decodeTypeImpPostfix(buffer, pType);
+    }
+
+    void decodeTypeImpPrefix(String& buffer, Type* pType) {
+        TypeTag tag = pType->tag;
+
+        if (tag >= TY_INT && tag <= TY_VOID) {
+            switch (tag) {
+                case TY_INT:
+                    buffer.appendCStr("int");
+                    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;
+                default:
+                    break;
+            }
+            buffer.append(' ');
+        }
+
+        switch (tag) {
+            case TY_INT:
+                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_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_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(Arena& arena) {
+        Type* pType;
+        if (tok == TOK_INT) {
+            pType = mkpInt;
+        } 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 {
+            return NULL;
+        }
+        next();
+        return pType;
+    }
+
+    Type* acceptDeclaration(Type* pType, bool nameAllowed, bool nameRequired,
+                            Arena& arena) {
+        tokenid_t declName = 0;
+        pType = acceptDecl2(pType, declName, nameAllowed,
+                                  nameRequired, arena);
+        if (declName) {
+            // Clone the parent type so we can set a unique ID
+            pType = createType(pType->tag, pType->pHead,
+                                      pType->pTail, arena);
+
+            pType->id = declName;
+        }
+        // fprintf(stderr, "Parsed a declaration:       ");
+        // printType(pType);
+        return pType;
+    }
+
+    Type* expectDeclaration(Type* pBaseType, Arena& arena) {
+        Type* pType = acceptDeclaration(pBaseType, true, true, arena);
+        if (! pType) {
+            error("Expected a declaration");
+        }
+        return pType;
+    }
+
+    /* Used for accepting types that appear in casts */
+    Type* acceptCastTypeDeclaration(Arena& arena) {
+        Type* pType = acceptPrimitiveType(arena);
+        if (pType) {
+            pType = acceptDeclaration(pType, false, false, arena);
+        }
+        return pType;
+    }
+
+    Type* expectCastTypeDeclaration(Arena& arena) {
+        Type* pType = acceptCastTypeDeclaration(arena);
+        if (! pType) {
+            error("Expected a declaration");
+        }
+        return pType;
+    }
+
+    Type* acceptDecl2(Type* pType, tokenid_t& declName,
+                      bool nameAllowed, bool nameRequired, Arena& arena) {
+        int ptrCounter = 0;
+        while (accept('*')) {
+            ptrCounter++;
+        }
+        pType = acceptDecl3(pType, declName, nameAllowed, nameRequired, arena);
+        while (ptrCounter-- > 0) {
+            pType = createType(TY_POINTER, pType, NULL, arena);
+        }
+        return pType;
+    }
+
+    Type* acceptDecl3(Type* pType, tokenid_t& declName,
+                      bool nameAllowed, bool nameRequired, Arena& arena) {
+        // direct-dcl :
+        //   name
+        //  (dcl)
+        //   direct-dcl()
+        //   direct-dcl[]
+        Type* pNewHead = NULL;
+        if (accept('(')) {
+            pNewHead = acceptDecl2(pNewHead, declName, nameAllowed,
+                                nameRequired, arena);
+            skip(')');
+        } else if ((declName = acceptSymbol()) != 0) {
+            if (nameAllowed == false && declName) {
+                error("Symbol %s not allowed here", nameof(declName));
+            } else if (nameRequired && ! declName) {
+                String temp;
+                decodeToken(temp, tok);
+                error("Expected symbol. Got %s", temp.getUnwrapped());
+            }
+        }
+        while (accept('(')) {
+            // Function declaration
+            Type* pTail = acceptArgs(nameAllowed, arena);
+            pType = createType(TY_FUNC, pType, pTail, arena);
+            skip(')');
+        }
+
+        if (pNewHead) {
+            Type* pA = pNewHead;
+            while (pA->pHead) {
+                pA = pA->pHead;
+            }
+            pA->pHead = pType;
+            pType = pNewHead;
+        }
+        return pType;
+    }
+
+    Type* acceptArgs(bool nameAllowed, Arena& arena) {
+        Type* pHead = NULL;
+        Type* pTail = NULL;
+        for(;;) {
+            Type* pBaseArg = acceptPrimitiveType(arena);
+            if (pBaseArg) {
+                Type* pArg = acceptDeclaration(pBaseArg, nameAllowed, false,
+                                               arena);
+                if (pArg) {
+                    Type* pParam = createType(TY_PARAM, pArg, NULL, arena);
+                    if (!pHead) {
+                        pHead = pParam;
+                        pTail = pParam;
+                    } else {
+                        pTail->pTail = pParam;
+                        pTail = pParam;
+                    }
+                }
+            }
+            if (! accept(',')) {
+                break;
+            }
+        }
+        return pHead;
+    }
+
+    Type* expectPrimitiveType(Arena& arena) {
+        Type* pType = acceptPrimitiveType(arena);
+        if (!pType) {
+            String buf;
+            decodeToken(buf, tok);
+            error("Expected a type, got %s", buf.getUnwrapped());
+        }
+        return pType;
+    }
+
+    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);
+    }
+
+    void localDeclarations(Type* pBaseType) {
+        intptr_t a;
+
+        while (pBaseType) {
+            while (tok != ';' && tok != EOF) {
+                Type* pDecl = expectDeclaration(pBaseType, mLocalArena);
+                if (!pDecl) {
+                    break;
+                }
+                int variableAddress = 0;
+                addLocalSymbol(pDecl);
+                loc = loc + pGen->sizeOf(pDecl);
+                loc = loc + 4;
+                variableAddress = -loc;
+                VI(pDecl->id)->pAddress = (void*) variableAddress;
+                if (accept('=')) {
+                    /* assignment */
+                    expr();
+                    pGen->storeR0(variableAddress, pDecl);
+                }
+                if (tok == ',')
+                    next();
+            }
+            skip(';');
+            pBaseType = acceptPrimitiveType(mLocalArena);
+        }
+    }
+
+    bool checkSymbol() {
+        return checkSymbol(tok);
+    }
+
+    void decodeToken(String& buffer, tokenid_t token) {
+        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 (token >= TOK_KEYWORD && token < TOK_SYMBOL) {
+            buffer.printf("keyword \"%s\"", nameof(token));
+        } else {
+            buffer.printf("symbol \"%s\"", nameof(token));
+        }
+    }
+
+    bool checkSymbol(tokenid_t token) {
+        bool result = token >= TOK_SYMBOL;
+        if (!result) {
+            String temp;
+            decodeToken(temp, token);
+            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() {
+        while (tok != EOF) {
+            Type* pBaseType = expectPrimitiveType(mGlobalArena);
+            if (!pBaseType) {
+                break;
+            }
+            Type* pDecl = expectDeclaration(pBaseType, mGlobalArena);
+            if (!pDecl) {
+                break;
+            }
+            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->alignment(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, mGlobalArena);
+                    if (!pDecl) {
+                        break;
+                    }
+                    if (! isDefined(pDecl->id)) {
+                        addGlobalSymbol(pDecl);
+                    }
+                    name = VI(pDecl->id);
+                }
+                skip(';');
+            } else {
+                // Function declaration
+                if (accept(';')) {
+                    // forward declaration.
+                } else {
+                    if (name) {
+                        /* patch forward references (XXX: does not work for function
+                         pointers) */
+                        pGen->gsym((int) name->pForward);
+                        /* put function address */
+                        name->pAddress = (void*) codeBuf.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;
+                        addLocalSymbol(pArg);
+                        /* read param name and compute offset */
+                        VI(pArg->id)->pAddress = (void*) a;
+                        a = a + pGen->stackSizeOf(pArg);
+                        argCount++;
+                    }
+                    rsym = loc = 0;
+                    pReturnType = pDecl->pHead;
+                    a = pGen->functionEntry(argCount);
+                    block(0, true);
+                    pGen->gsym(rsym);
+                    pGen->functionExit(argCount, a, loc);
+                    mLocals.popLevel();
+                }
+            }
+        }
+    }
+
+    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");
+            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 (file) {
+            delete file;
+            file = 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;
+        pGen = 0;
+        mPragmaStringCount = 0;
+    }
+
+    void setArchitecture(const char* architecture) {
+        delete pGen;
+        pGen = 0;
+
+        if (architecture != NULL) {
+#ifdef PROVIDE_ARM_CODEGEN
+            if (! pGen && strcmp(architecture, "arm") == 0) {
+                pGen = new ARMCodeGenerator();
+            }
+#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();
+#elif defined(DEFAULT_X86_CODEGEN)
+            pGen = new X86CodeGenerator();
+#endif
+        }
+        if (pGen == NULL) {
+            error("No code generator defined.");
+        } else {
+            pGen->setErrorSink(this);
+        }
+    }
+
+public:
+    struct args {
+        args() {
+            architecture = 0;
+        }
+        const char* architecture;
+    };
+
+    Compiler() {
+        clear();
+    }
+
+    ~Compiler() {
+        cleanup();
+    }
+
+    int compile(const char* text, size_t textLength) {
+        int result;
+
+        cleanup();
+        clear();
+        mTokenTable.setArena(&mGlobalArena);
+        mGlobals.setArena(&mGlobalArena);
+        mGlobals.setTokenTable(&mTokenTable);
+        mLocals.setArena(&mLocalArena);
+        mLocals.setTokenTable(&mTokenTable);
+
+        internKeywords();
+        createPrimitiveTypes();
+        codeBuf.init(ALLOC_SIZE);
+        setArchitecture(NULL);
+        if (!pGen) {
+            return -1;
+        }
+#ifdef PROVIDE_TRACE_CODEGEN
+            pGen = new TraceCodeGenerator(pGen);
+#endif
+            pGen->setErrorSink(this);
+        pGen->init(&codeBuf);
+        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;
+            }
+        }
+        return result;
+    }
+
+    void createPrimitiveTypes() {
+        mkpInt = createType(TY_INT, NULL, NULL, mGlobalArena);
+        mkpChar = createType(TY_CHAR, NULL, NULL, mGlobalArena);
+        mkpVoid = createType(TY_VOID, NULL, NULL, mGlobalArena);
+        mkpFloat = createType(TY_FLOAT, NULL, NULL, mGlobalArena);
+        mkpDouble = createType(TY_DOUBLE, NULL, NULL, mGlobalArena);
+        mkpIntFn =  createType(TY_FUNC, mkpInt, NULL, mGlobalArena);
+        mkpIntPtr = createPtrType(mkpInt, mGlobalArena);
+        mkpCharPtr = createPtrType(mkpChar, mGlobalArena);
+        mkpFloatPtr = createPtrType(mkpFloat, mGlobalArena);
+        mkpDoublePtr = createPtrType(mkpDouble, mGlobalArena);
+        mkpPtrIntFn = createPtrType(mkpIntFn, mGlobalArena);
+    }
+
+    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;
+    }
+
+    int dump(FILE* out) {
+        fwrite(codeBuf.getBase(), 1, codeBuf.getSize(), out);
+        return 0;
+    }
+
+    int disassemble(FILE* out) {
+        return pGen->disassemble(out);
+    }
+
+    /* Look through the symbol table to find a symbol.
+     * If found, return its value.
+     */
+    void* lookup(const char* name) {
+        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;
+            }
+        }
+    }
+
+    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_ARM_CODEGEN
+FILE* Compiler::ARMCodeGenerator::disasmOut;
+#endif
+
+#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 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 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';
+}
+
+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 accDisassemble(ACCscript* script) {
+    script->compiler.disassemble(stderr);
+}
+
+
+} // namespace acc
+
diff --git a/libacc/armreg.h b/libacc/armreg.h
new file mode 100644
index 0000000..fde81ba
--- /dev/null
+++ b/libacc/armreg.h
@@ -0,0 +1,300 @@
+/*	$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/disassem.cpp b/libacc/disassem.cpp
new file mode 100644
index 0000000..ac35342
--- /dev/null
+++ b/libacc/disassem.cpp
@@ -0,0 +1,711 @@
+/*	$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 <sys/cdefs.h>
+//__FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/arm/arm/disassem.c,v 1.2 2005/01/05 21:58:47 imp Exp $");
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "disassem.h"
+#include "armreg.h"
+//#include <ddb/ddb.h>
+
+/*
+ * 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/disassem.h b/libacc/disassem.h
new file mode 100644
index 0000000..02747cd
--- /dev/null
+++ b/libacc/disassem.h
@@ -0,0 +1,65 @@
+/*	$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 <sys/types.h>
+
+#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/.gitignore b/libacc/tests/.gitignore
new file mode 100644
index 0000000..a26b298
--- /dev/null
+++ b/libacc/tests/.gitignore
@@ -0,0 +1,2 @@
+test-acc
+*.out
diff --git a/libacc/tests/Android.mk b/libacc/tests/Android.mk
new file mode 100644
index 0000000..f8907b4
--- /dev/null
+++ b/libacc/tests/Android.mk
@@ -0,0 +1,33 @@
+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
+
+LOCAL_SHARED_LIBRARIES := \
+    libacc
+
+LOCAL_CFLAGS := -O0 -g 
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libacc/tests/data/bellard.otccex.c b/libacc/tests/data/bellard.otccex.c
new file mode 100644
index 0000000..e8f0989
--- /dev/null
+++ b/libacc/tests/data/bellard.otccex.c
@@ -0,0 +1,126 @@
+/* #!/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 <stdio.h>
+
+/* 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/constants.c b/libacc/tests/data/constants.c
new file mode 100644
index 0000000..230109a
--- /dev/null
+++ b/libacc/tests/data/constants.c
@@ -0,0 +1,30 @@
+#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/double.c b/libacc/tests/data/double.c
new file mode 100644
index 0000000..5bc20a3
--- /dev/null
+++ b/libacc/tests/data/double.c
@@ -0,0 +1,7 @@
+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
new file mode 100644
index 0000000..2e08dcc
--- /dev/null
+++ b/libacc/tests/data/error.c
@@ -0,0 +1,2 @@
+void foo;
+
diff --git a/libacc/tests/data/expr-ansi.c b/libacc/tests/data/expr-ansi.c
new file mode 100644
index 0000000..d463659
--- /dev/null
+++ b/libacc/tests/data/expr-ansi.c
@@ -0,0 +1,60 @@
+/* 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
new file mode 100644
index 0000000..4f2d2e7
--- /dev/null
+++ b/libacc/tests/data/expr.c
@@ -0,0 +1,60 @@
+/* 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
new file mode 100644
index 0000000..04b6a38
--- /dev/null
+++ b/libacc/tests/data/expr2.c
@@ -0,0 +1,6 @@
+/* Test operators */
+
+main() {
+    int a;
+    a = a++;
+}
diff --git a/libacc/tests/data/float.c b/libacc/tests/data/float.c
new file mode 100644
index 0000000..b955238
--- /dev/null
+++ b/libacc/tests/data/float.c
@@ -0,0 +1,48 @@
+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 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);
+
+    * (float*) & f0 = 1.1f;
+    * (double*) & d0 = 3.3;
+    printf("pointer tests: %g %g %g %g\n", f0, f1, d0, d1);
+}
+
+int main() {
+    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.4f)=%d\n", dtoi(2.4f));
+    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/flops.c b/libacc/tests/data/flops.c
new file mode 100644
index 0000000..347c665
--- /dev/null
+++ b/libacc/tests/data/flops.c
@@ -0,0 +1,115 @@
+// 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.0f);
+    comparisonTestid(1, 1.0f);
+    comparisonTestid(2, 1.0f);
+}
+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 main() {
+    unaryOps();
+    binaryOps();
+    comparisonOps();
+    return 0;
+}
diff --git a/libacc/tests/data/hello.c b/libacc/tests/data/hello.c
new file mode 100644
index 0000000..585ce6c
--- /dev/null
+++ b/libacc/tests/data/hello.c
@@ -0,0 +1,3 @@
+main(a,b) {
+    printf("Hello, world\n");
+}
diff --git a/libacc/tests/data/locals.c b/libacc/tests/data/locals.c
new file mode 100644
index 0000000..f1ef363
--- /dev/null
+++ b/libacc/tests/data/locals.c
@@ -0,0 +1,71 @@
+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
new file mode 100644
index 0000000..e73eec4
--- /dev/null
+++ b/libacc/tests/data/missing-main.c
@@ -0,0 +1,4 @@
+/* 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
new file mode 100644
index 0000000..72580e9
--- /dev/null
+++ b/libacc/tests/data/otcc-ansi.c
@@ -0,0 +1,466 @@
+// #include <stdio.h>
+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<<Z/03e>>`/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
new file mode 100644
index 0000000..530f9e2
--- /dev/null
+++ b/libacc/tests/data/otcc-noinclude.c
@@ -0,0 +1,446 @@
+// #include <stdio.h>
+#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<<Z/03e>>`/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
new file mode 100644
index 0000000..433ae2e
--- /dev/null
+++ b/libacc/tests/data/otcc.c
@@ -0,0 +1,448 @@
+#include <stdio.h>
+#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<<Z/03e>>`/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/returnval-ansi.c b/libacc/tests/data/returnval-ansi.c
new file mode 100644
index 0000000..6b53fd5
--- /dev/null
+++ b/libacc/tests/data/returnval-ansi.c
@@ -0,0 +1,8 @@
+
+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
new file mode 100644
index 0000000..1cf5bae
--- /dev/null
+++ b/libacc/tests/data/returnval.c
@@ -0,0 +1,4 @@
+main() {
+  return 42;
+}
+
diff --git a/libacc/tests/data/simplest.c b/libacc/tests/data/simplest.c
new file mode 100644
index 0000000..bae895a
--- /dev/null
+++ b/libacc/tests/data/simplest.c
@@ -0,0 +1 @@
+main() {}
diff --git a/libacc/tests/data/testStringConcat.c b/libacc/tests/data/testStringConcat.c
new file mode 100644
index 0000000..bf06ae1
--- /dev/null
+++ b/libacc/tests/data/testStringConcat.c
@@ -0,0 +1,4 @@
+int main() {
+    return printf("Hello" "," " world\n");
+}
+
diff --git a/libacc/tests/main.cpp b/libacc/tests/main.cpp
new file mode 100644
index 0000000..13a30d4
--- /dev/null
+++ b/libacc/tests/main.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 <ctype.h>
+#include <dlfcn.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(__arm__)
+#include <unistd.h>
+#endif
+
+#include <acc/acc.h>
+
+
+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);
+}
+
+// Private API for development:
+
+extern "C"
+void accDisassemble(ACCscript* script);
+
+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;
+
+    accCompileScript(script);
+    int result = accGetError(script);
+    MainPtr mainPointer = 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;
+        }
+    }
+
+    if (printListing) {
+        accDisassemble(script);
+    }
+
+    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/test b/libacc/tests/test
new file mode 100755
index 0000000..ef10500
--- /dev/null
+++ b/libacc/tests/test
@@ -0,0 +1,6 @@
+#!/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
new file mode 100644
index 0000000..016c587
--- /dev/null
+++ b/libacc/tests/test.py
@@ -0,0 +1,126 @@
+#
+# Test the acc compiler
+
+import unittest
+import subprocess
+import os
+
+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 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 adb(args):
+    return runCmd(["adb"] + args)
+
+gArmInitialized = False
+
+def setupArm():
+    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"])
+    gArmInitialied = 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 '%s' != '%s'" % (
+            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
+
+class TestACC(unittest.TestCase):
+ 
+    def compileCheck(self, args, stdErrResult, stdOutResult=""):
+        out, err = compile(args)
+        compare(out, stdOutResult)
+        compare(err, stdErrResult)
+        self.assertEqual(out, stdOutResult)
+        self.assertEqual(err, stdErrResult)
+
+    def compileCheckArm(self, args, result):
+        self.assertEqual(compileArm(args), result)
+
+    def testCompileReturnVal(self):
+        self.compileCheck(["data/returnval-ansi.c"], "") 
+
+    def testCompileReturnVal(self):
+        self.compileCheck(["data/otcc-ansi.c"], "")
+
+    def testRunReturnVal(self):
+        self.compileCheck(["-R", "data/returnval-ansi.c"],
+		"Executing compiled code:\nresult: 42\n")
+
+    def testStingLiteralConcatenation(self):
+        self.compileCheck(["-R", "data/testStringConcat.c"],
+		"Executing compiled code:\nresult: 13\n", "Hello, world\n")
+
+    def testRunOTCCANSI(self):
+        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")
+
+    def testRunOTCCANSI2(self):
+        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")
+
+    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 testArmRunReturnVal(self):
+        self.compileCheckArm(["-R", "/system/bin/accdata/data/returnval-ansi.c"],
+            "Executing compiled code:\nresult: 42\n")
+
+if __name__ == '__main__':
+    if not outputCanRun():
+        print "Many tests are expected to fail, because acc is not a 32-bit x86 Linux executable."
+    unittest.main()
+
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 46b8d0c..087d652 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -17,10 +17,10 @@
 include $(CLEAR_VARS)
 
 commonSources := \
-	abort_socket.c \
 	array.c \
 	hashmap.c \
 	atomic.c \
+	native_handle.c \
 	buffer.c \
 	socket_inaddr_any_server.c \
 	socket_local_client.c \
@@ -38,39 +38,44 @@
 	properties.c \
 	threads.c
 
+commonHostSources := \
+        ashmem-host.c
+
 # some files must not be compiled when building against Mingw
 # they correspond to features not used by our host development tools
 # which are also hard or even impossible to port to native Win32
-WITH_MINGW :=
+WINDOWS_HOST_ONLY :=
 ifeq ($(HOST_OS),windows)
     ifeq ($(strip $(USE_CYGWIN)),)
-        WITH_MINGW := 1
+        WINDOWS_HOST_ONLY := 1
     endif
 endif
 # USE_MINGW is defined when we build against Mingw on Linux
 ifneq ($(strip $(USE_MINGW)),)
-    WITH_MINGW := 1
+    WINDOWS_HOST_ONLY := 1
 endif
 
-ifeq ($(WITH_MINGW),1)
+ifeq ($(WINDOWS_HOST_ONLY),1)
     commonSources += \
         uio.c
 else
     commonSources += \
+        abort_socket.c \
         mspace.c \
         selector.c \
-        fdevent.c \
         tztime.c \
-        tzstrftime.c \
         adb_networking.c \
-	zygote.c
+        zygote.c
+
+    commonHostSources += \
+        tzstrftime.c
 endif
 
 
 # Static library for host
 # ========================================================
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) ashmem-host.c
+LOCAL_SRC_FILES := $(commonSources) $(commonHostSources)
 LOCAL_LDLIBS := -lpthread
 LOCAL_STATIC_LIBRARIES := liblog
 include $(BUILD_HOST_STATIC_LIBRARY)
@@ -82,7 +87,7 @@
 # ========================================================
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) memory.c dlmalloc_stubs.c ashmem-host.c
+LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) memory.c dlmalloc_stubs.c
 LOCAL_LDLIBS := -lpthread
 LOCAL_SHARED_LIBRARIES := liblog
 include $(BUILD_SHARED_LIBRARY)
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index 5e158af..8b71f87 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -83,3 +83,8 @@
 	struct ashmem_pin pin = { offset, len };
 	return ioctl(fd, ASHMEM_UNPIN, &pin);
 }
+
+int ashmem_get_size_region(int fd)
+{
+  return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
index dbb52bc..f03e130 100644
--- a/libcutils/ashmem-host.c
+++ b/libcutils/ashmem-host.c
@@ -92,3 +92,23 @@
 {
 	return ASHMEM_IS_UNPINNED;
 }
+
+int ashmem_get_size_region(int fd)
+{
+        struct stat buf;
+        int result;
+
+        result = fstat(fd, &buf);
+        if (result == -1) {
+                return -1;
+        }
+
+        // Check if this is an "ashmem" region.
+        // TODO: This is very hacky, and can easily break. We need some reliable indicator.
+        if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
+                errno = ENOTTY;
+                return -1;
+        }
+
+        return (int)buf.st_size;  // TODO: care about overflow (> 2GB file)?
+}
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
new file mode 100644
index 0000000..4089968
--- /dev/null
+++ b/libcutils/native_handle.c
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NativeHandle"
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+#include <cutils/native_handle.h>
+
+native_handle_t* native_handle_create(int numFds, int numInts)
+{
+    native_handle_t* h = malloc(
+            sizeof(native_handle_t) + sizeof(int)*(numFds+numInts));
+
+    h->version = sizeof(native_handle_t);
+    h->numFds = numFds;
+    h->numInts = numInts;
+    return h;
+}
+
+int native_handle_delete(native_handle_t* h)
+{
+    if (h) {
+        if (h->version != sizeof(native_handle_t))
+            return -EINVAL;
+        free(h);
+    }
+    return 0;
+}
+
+int native_handle_close(const native_handle_t* h)
+{
+    if (h->version != sizeof(native_handle_t))
+        return -EINVAL;
+
+    const int numFds = h->numFds;
+    int i;
+    for (i=0 ; i<numFds ; i++) {
+        close(h->data[i]);
+    }
+    return 0;
+}
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
index fadaabe..1a8ba86 100644
--- a/libcutils/strdup16to8.c
+++ b/libcutils/strdup16to8.c
@@ -15,6 +15,8 @@
 ** limitations under the License.
 */
 
+#include <limits.h>  /* for SIZE_MAX */
+
 #include <cutils/jstring.h>
 #include <assert.h>
 #include <stdlib.h>
@@ -26,19 +28,67 @@
  */
 extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
 {
-   size_t utf8Len = 0;
+    size_t utf8Len = 0;
 
-   while (len--) {
-       unsigned int uic = *utf16Str++;
+    /* A small note on integer overflow. The result can
+     * potentially be as big as 3*len, which will overflow
+     * for len > SIZE_MAX/3.
+     *
+     * Moreover, the result of a strnlen16to8 is typically used
+     * to allocate a destination buffer to strncpy16to8 which
+     * requires one more byte to terminate the UTF-8 copy, and
+     * this is generally done by careless users by incrementing
+     * the result without checking for integer overflows, e.g.:
+     *
+     *   dst = malloc(strnlen16to8(utf16,len)+1)
+     *
+     * Due to this, the following code will try to detect
+     * overflows, and never return more than (SIZE_MAX-1)
+     * when it detects one. A careless user will try to malloc
+     * SIZE_MAX bytes, which will return NULL which can at least
+     * be detected appropriately.
+     *
+     * As far as I know, this function is only used by strndup16(),
+     * but better be safe than sorry.
+     */
 
-       if (uic > 0x07ff)
-           utf8Len += 3;
-       else if (uic > 0x7f || uic == 0)
-           utf8Len += 2;
-       else
-           utf8Len++;
-   }
-   return utf8Len;
+    /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
+     */
+    if (len < (SIZE_MAX-1)/3) {
+        while (len--) {
+            unsigned int uic = *utf16Str++;
+
+            if (uic > 0x07ff)
+                utf8Len += 3;
+            else if (uic > 0x7f || uic == 0)
+                utf8Len += 2;
+            else
+                utf8Len++;
+        }
+        return utf8Len;
+    }
+
+    /* The slower but paranoid version */
+    while (len--) {
+        unsigned int  uic     = *utf16Str++;
+        size_t        utf8Cur = utf8Len;
+
+        if (uic > 0x07ff)
+            utf8Len += 3;
+        else if (uic > 0x7f || uic == 0)
+            utf8Len += 2;
+        else
+            utf8Len++;
+
+        if (utf8Len < utf8Cur) /* overflow detected */
+            return SIZE_MAX-1;
+    }
+
+    /* don't return SIZE_MAX to avoid common user bug */
+    if (utf8Len == SIZE_MAX)
+        utf8Len = SIZE_MAX-1;
+
+    return utf8Len;
 }
 
 
@@ -50,7 +100,7 @@
  *
  * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
  * not just "len".
- * 
+ *
  * Please note, a terminated \0 is always added, so your result will always
  * be "strlen16to8() + 1" bytes long.
  */
@@ -58,6 +108,10 @@
 {
     char* utf8cur = utf8Str;
 
+    /* Note on overflows: We assume the user did check the result of
+     * strnlen16to8() properly or at a minimum checked the result of
+     * its malloc(SIZE_MAX) in case of overflow.
+     */
     while (len--) {
         unsigned int uic = *utf16Str++;
 
@@ -73,8 +127,8 @@
 
             if (uic == 0) {
                 break;
-            }           
-        }       
+            }
+        }
     }
 
    *utf8cur = '\0';
@@ -85,20 +139,30 @@
 /**
  * Convert a UTF-16 string to UTF-8.
  *
- * Make sure you allocate "dest" with the result of strblen16to8(),
- * not just "strlen16()".
  */
 char * strndup16to8 (const char16_t* s, size_t n)
 {
-    char *ret;
+    char*   ret;
+    size_t  len;
 
     if (s == NULL) {
         return NULL;
     }
 
-    ret = malloc(strnlen16to8(s, n) + 1);
+    len = strnlen16to8(s, n);
+
+    /* We are paranoid, and we check for SIZE_MAX-1
+     * too since it is an overflow value for our
+     * strnlen16to8 implementation.
+     */
+    if (len >= SIZE_MAX-1)
+        return NULL;
+
+    ret = malloc(len + 1);
+    if (ret == NULL)
+        return NULL;
 
     strncpy16to8 (ret, s, n);
-    
-    return ret;    
+
+    return ret;
 }
diff --git a/libcutils/tzstrftime.c b/libcutils/tzstrftime.c
index 29c5015..e37d79a 100644
--- a/libcutils/tzstrftime.c
+++ b/libcutils/tzstrftime.c
@@ -172,10 +172,17 @@
 					pt, ptlim, modifier);
 				continue;
 			case 'B':
-				pt = _add((t->tm_mon < 0 ||
-					t->tm_mon >= MONSPERYEAR) ?
-					"?" : Locale->month[t->tm_mon],
-					pt, ptlim, modifier);
+				if (modifier == '-') {
+					pt = _add((t->tm_mon < 0 ||
+						t->tm_mon >= MONSPERYEAR) ?
+						"?" : Locale->standalone_month[t->tm_mon],
+						pt, ptlim, modifier);
+				} else {
+					pt = _add((t->tm_mon < 0 ||
+						t->tm_mon >= MONSPERYEAR) ?
+						"?" : Locale->month[t->tm_mon],
+						pt, ptlim, modifier);
+				}
 				continue;
 			case 'b':
 			case 'h':
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 80867d1..96da38b 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -143,12 +143,16 @@
     /* XXX: This needs to go! */
     if (!strcmp(tag, "HTC_RIL") ||
         !strcmp(tag, "RILJ") ||
+        !strcmp(tag, "RILB") ||
         !strcmp(tag, "RILC") ||
         !strcmp(tag, "RILD") ||
         !strcmp(tag, "RIL") ||
         !strcmp(tag, "AT") ||
         !strcmp(tag, "GSM") ||
-        !strcmp(tag, "STK"))
+        !strcmp(tag, "STK") ||
+        !strcmp(tag, "CDMA") ||
+        !strcmp(tag, "PHONE") ||
+        !strcmp(tag, "SMS"))
             log_id = LOG_ID_RADIO;
 
     vec[0].iov_base   = (unsigned char *) &prio;
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
index 8837e07..ef3b66a 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.h
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -21,8 +21,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
+#include "tinyutils/Vector.h"
+#include "tinyutils/KeyedVector.h"
+#include "tinyutils/smartpointer.h"
 
 #include "tinyutils/smartpointer.h"
 #include "codeflinger/ARMAssemblerInterface.h"
diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h
index 370ce17..8ff1366 100644
--- a/libpixelflinger/codeflinger/CodeCache.h
+++ b/libpixelflinger/codeflinger/CodeCache.h
@@ -23,8 +23,7 @@
 #include <pthread.h>
 #include <sys/types.h>
 
-#include <utils/KeyedVector.h>
-
+#include "tinyutils/KeyedVector.h"
 #include "tinyutils/smartpointer.h"
 
 namespace android {
diff --git a/libpixelflinger/tinyutils/Errors.h b/libpixelflinger/tinyutils/Errors.h
new file mode 100644
index 0000000..b9fd5f4
--- /dev/null
+++ b/libpixelflinger/tinyutils/Errors.h
@@ -0,0 +1,62 @@
+/*
+ * 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 ANDROID_ERRORS_H
+#define ANDROID_ERRORS_H
+
+#include <sys/types.h>
+#include <errno.h>
+
+namespace android {
+
+// use this type to return error codes
+typedef int32_t     status_t;
+
+/*
+ * Error codes. 
+ * All error codes are negative values.
+ */
+
+enum {
+    OK                = 0,    // Everything's swell.
+    NO_ERROR          = 0,    // No errors.
+    
+    UNKNOWN_ERROR       = 0x80000000,
+
+    NO_MEMORY           = -ENOMEM,
+    INVALID_OPERATION   = -ENOSYS,
+    BAD_VALUE           = -EINVAL,
+    BAD_TYPE            = 0x80000001,
+    NAME_NOT_FOUND      = -ENOENT,
+    PERMISSION_DENIED   = -EPERM,
+    NO_INIT             = -ENODEV,
+    ALREADY_EXISTS      = -EEXIST,
+    DEAD_OBJECT         = -EPIPE,
+    FAILED_TRANSACTION  = 0x80000002,
+    JPARKS_BROKE_IT     = -EPIPE,
+    BAD_INDEX           = -EOVERFLOW,
+    NOT_ENOUGH_DATA     = -ENODATA,
+    WOULD_BLOCK         = -EWOULDBLOCK, 
+    TIMED_OUT           = -ETIME,
+    UNKNOWN_TRANSACTION = -EBADMSG,
+};
+
+
+}; // namespace android
+    
+// ---------------------------------------------------------------------------
+    
+#endif // ANDROID_ERRORS_H
diff --git a/libpixelflinger/tinyutils/SortedVector.h b/libpixelflinger/tinyutils/SortedVector.h
new file mode 100644
index 0000000..7a6b443
--- /dev/null
+++ b/libpixelflinger/tinyutils/SortedVector.h
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SORTED_VECTOR_H
+#define ANDROID_SORTED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "tinyutils/Vector.h"
+#include "tinyutils/VectorImpl.h"
+#include "tinyutils/TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+template <class TYPE>
+class SortedVector : private SortedVectorImpl
+{
+public:
+            typedef TYPE    value_type;
+    
+    /*! 
+     * Constructors and destructors
+     */
+    
+                            SortedVector();
+                            SortedVector(const SortedVector<TYPE>& rhs);
+    virtual                 ~SortedVector();
+
+    /*! copy operator */
+    const SortedVector<TYPE>&   operator = (const SortedVector<TYPE>& rhs) const;    
+    SortedVector<TYPE>&         operator = (const SortedVector<TYPE>& rhs);    
+
+    /*
+     * empty the vector
+     */
+
+    inline  void            clear()             { VectorImpl::clear(); }
+
+    /*! 
+     * vector stats
+     */
+
+    //! returns number of items in the vector
+    inline  size_t          size() const                { return VectorImpl::size(); }
+    //! returns wether or not the vector is empty
+    inline  bool            isEmpty() const             { return VectorImpl::isEmpty(); }
+    //! returns how many items can be stored without reallocating the backing store
+    inline  size_t          capacity() const            { return VectorImpl::capacity(); }
+    //! setst the capacity. capacity can never be reduced less than size()
+    inline  ssize_t         setCapacity(size_t size)    { return VectorImpl::setCapacity(size); }
+
+    /*! 
+     * C-style array access
+     */
+     
+    //! read-only C-style access 
+    inline  const TYPE*     array() const;
+
+    //! read-write C-style access. BE VERY CAREFUL when modifying the array
+    //! you ust keep it sorted! You usually don't use this function.
+            TYPE*           editArray();
+
+            //! finds the index of an item
+            ssize_t         indexOf(const TYPE& item) const;
+            
+            //! finds where this item should be inserted
+            size_t          orderOf(const TYPE& item) const;
+            
+    
+    /*! 
+     * accessors
+     */
+
+    //! read-only access to an item at a given index
+    inline  const TYPE&     operator [] (size_t index) const;
+    //! alternate name for operator []
+    inline  const TYPE&     itemAt(size_t index) const;
+    //! stack-usage of the vector. returns the top of the stack (last element)
+            const TYPE&     top() const;
+    //! same as operator [], but allows to access the vector backward (from the end) with a negative index
+            const TYPE&     mirrorItemAt(ssize_t index) const;
+
+    /*!
+     * modifing the array
+     */
+
+            //! add an item in the right place (and replace the one that is there)
+            ssize_t         add(const TYPE& item);
+            
+            //! editItemAt() MUST NOT change the order of this item
+            TYPE&           editItemAt(size_t index) {
+                return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
+            }
+
+            //! merges a vector into this one
+            ssize_t         merge(const Vector<TYPE>& vector);
+            ssize_t         merge(const SortedVector<TYPE>& vector);
+            
+            //! removes an item
+            ssize_t         remove(const TYPE&);
+
+    //! remove several items
+    inline  ssize_t         removeItemsAt(size_t index, size_t count = 1);
+    //! remove one item
+    inline  ssize_t         removeAt(size_t index)  { return removeItemsAt(index); }
+            
+protected:
+    virtual void    do_construct(void* storage, size_t num) const;
+    virtual void    do_destroy(void* storage, size_t num) const;
+    virtual void    do_copy(void* dest, const void* from, size_t num) const;
+    virtual void    do_splat(void* dest, const void* item, size_t num) const;
+    virtual void    do_move_forward(void* dest, const void* from, size_t num) const;
+    virtual void    do_move_backward(void* dest, const void* from, size_t num) const;
+    virtual int     do_compare(const void* lhs, const void* rhs) const;
+};
+
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector()
+    : SortedVectorImpl(sizeof(TYPE),
+                ((traits<TYPE>::has_trivial_ctor   ? HAS_TRIVIAL_CTOR   : 0)
+                |(traits<TYPE>::has_trivial_dtor   ? HAS_TRIVIAL_DTOR   : 0)
+                |(traits<TYPE>::has_trivial_copy   ? HAS_TRIVIAL_COPY   : 0)
+                |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
+                )
+{
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
+    : SortedVectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::~SortedVector() {
+    finish_vector();
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+    SortedVectorImpl::operator = (rhs);
+    return *this; 
+}
+
+template<class TYPE> inline
+const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+    SortedVectorImpl::operator = (rhs);
+    return *this; 
+}
+
+template<class TYPE> inline
+const TYPE* SortedVector<TYPE>::array() const {
+    return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* SortedVector<TYPE>::editArray() {
+    return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
+    assert( index<size() );
+    return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
+    return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::mirrorItemAt(ssize_t index) const {
+    assert( (index>0 ? index : -index)<size() );
+    return *(array() + ((index<0) ? (size()-index) : index));
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::top() const {
+    return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::add(const TYPE& item) {
+    return SortedVectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
+    return SortedVectorImpl::indexOf(&item);
+}
+
+template<class TYPE> inline
+size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
+    return SortedVectorImpl::orderOf(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
+    return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
+    return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
+    return SortedVectorImpl::remove(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
+    return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
+    construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
+    destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+    copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+    splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+    move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+    move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+    return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
+}
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SORTED_VECTOR_H
diff --git a/libpixelflinger/tinyutils/Vector.h b/libpixelflinger/tinyutils/Vector.h
index 182bc7b..14cf99a 100644
--- a/libpixelflinger/tinyutils/Vector.h
+++ b/libpixelflinger/tinyutils/Vector.h
@@ -15,6 +15,7 @@
 
 #include <cutils/log.h>
 
+#include "tinyutils/Errors.h"
 #include "tinyutils/VectorImpl.h"
 #include "tinyutils/TypeHelpers.h"
 
@@ -302,16 +303,6 @@
     return VectorImpl::removeItemsAt(index, count);
 }
 
-template<class TYPE> inline
-status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) {
-    return VectorImpl::sort((VectorImpl::compar_t)cmp);
-}
-
-template<class TYPE> inline
-status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) {
-    return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state);
-}
-
 // ---------------------------------------------------------------------------
 
 template<class TYPE>
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
new file mode 100644
index 0000000..dd2b32d
--- /dev/null
+++ b/libsysutils/Android.mk
@@ -0,0 +1,35 @@
+BUILD_LIBSYSUTILS := false
+ifneq ($(TARGET_SIMULATOR),true)
+    BUILD_LIBSYSUTILS := true
+endif
+
+ifeq ($(BUILD_LIBSYSUTILS),true)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                             \
+                  src/SocketListener.cpp      \
+                  src/FrameworkListener.cpp   \
+                  src/NetlinkListener.cpp     \
+                  src/NetlinkEvent.cpp        \
+                  src/FrameworkCommand.cpp    \
+                  src/SocketClient.cpp        \
+                  src/ServiceManager.cpp      \
+
+LOCAL_MODULE:= libsysutils
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS) 
+
+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
new file mode 100644
index 0000000..1686996
--- /dev/null
+++ b/libsysutils/src/FrameworkClient.cpp
@@ -0,0 +1,40 @@
+#include <alloca.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#define LOG_TAG "FrameworkClient"
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkClient.h>
+
+FrameworkClient::FrameworkClient(int socket) {
+    mSocket = socket;
+    pthread_mutex_init(&mWriteMutex, NULL);
+}
+
+int FrameworkClient::sendMsg(const char *msg) {
+    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));
+    }
+    pthread_mutex_unlock(&mWriteMutex);
+    return 0;
+}
+
+int FrameworkClient::sendMsg(const char *msg, const char *data) {
+    char *buffer = (char *) alloca(strlen(msg) + strlen(data) + 1);
+    if (!buffer) {
+        errno = -ENOMEM;
+        return -1;
+    }
+    strcpy(buffer, msg);
+    strcat(buffer, data);
+    return sendMsg(buffer);
+}
+
diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp
new file mode 100644
index 0000000..c52eac7
--- /dev/null
+++ b/libsysutils/src/FrameworkCommand.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 <errno.h>
+
+#define LOG_TAG "FrameworkCommand"
+
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkCommand.h>
+
+FrameworkCommand::FrameworkCommand(const char *cmd) {
+    mCommand = cmd;
+}
+
+int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) {
+    LOGW("Command %s has no run handler!", getCommand());
+    errno = ENOSYS;
+    return -1;
+}
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
new file mode 100644
index 0000000..e9ca897
--- /dev/null
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <errno.h>
+#include <string.h>
+
+#define LOG_TAG "FrameworkListener"
+
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkListener.h>
+#include <sysutils/FrameworkCommand.h>
+#include <sysutils/SocketClient.h>
+
+FrameworkListener::FrameworkListener(const char *socketName) :
+                            SocketListener(socketName, true) {
+    mCommands = new FrameworkCommandCollection();
+}
+
+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;
+    } else if (!len)
+        return false;
+
+    int offset = 0;
+    int i;
+
+    for (i = 0; i < len; i++) {
+        if (buffer[i] == '\0') {
+            dispatchCommand(c, buffer + offset);
+            offset = i + 1;
+        }
+    }
+    return true;
+}
+
+void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
+    mCommands->push_back(cmd);
+}
+
+void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
+    int argc;
+    char *argv[FrameworkListener::CMD_ARGS_MAX];
+
+    if (!index(data, '"')) {
+        char *next = data;
+        char *field;
+        int i;
+
+        for (i = 0; (i < FrameworkListener::CMD_ARGS_MAX) &&
+                    (argv[i] = strsep(&next, " ")); i++);
+        argc = i+1;
+    } else {
+        LOGD("blehhh not supported");
+        return;
+    }
+
+    FrameworkCommandCollection::iterator i;
+
+    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));
+            }
+            return;
+        }
+    }
+
+    cli->sendMsg(500, "Command not recognized", false);
+    return;
+}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
new file mode 100644
index 0000000..5573c3f
--- /dev/null
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "NetlinkEvent"
+#include <cutils/log.h>
+
+#include <sysutils/NetlinkEvent.h>
+
+const int NetlinkEvent::NlActionUnknown = 0;
+const int NetlinkEvent::NlActionAdd = 1;
+const int NetlinkEvent::NlActionRemove = 2;
+const int NetlinkEvent::NlActionChange = 3;
+
+NetlinkEvent::NetlinkEvent() {
+    mAction = NlActionUnknown;
+}
+
+NetlinkEvent::~NetlinkEvent() {
+    int i;
+    if (mPath)
+        free(mPath);
+    if (mSubsystem)
+        free(mSubsystem);
+    for (i = 0; i < NL_PARAMS_MAX; i++) {
+        if (!mParams[i])
+            break;
+        free(mParams[i]);
+    }
+}
+
+bool NetlinkEvent::decode(char *buffer, int size) {
+    char *s = buffer;
+    char *end;
+    int param_idx = 0;
+    int i;
+    int first = 1;
+
+    end = s + size;
+    while (s < end) {
+        if (first) {
+            char *p;
+            for (p = s; *p != '@'; p++);
+            p++;
+            mPath = strdup(p);
+            first = 0;
+        } else {
+            if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
+                char *a = s + strlen("ACTION=");
+                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
+                mParams[param_idx++] = strdup(s);
+        }
+        s+= strlen(s) + 1;
+    }
+    return true;
+}
+
+const char *NetlinkEvent::findParam(const char *paramName) {
+    int i;
+
+    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];
+    }
+
+    LOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
+    return NULL;
+}
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
new file mode 100644
index 0000000..3ec9d9d
--- /dev/null
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#define LOG_TAG "NetlinkListener"
+#include <cutils/log.h>
+
+#include <sysutils/NetlinkListener.h>
+#include <sysutils/NetlinkEvent.h>
+
+NetlinkListener::NetlinkListener(int socket) :
+                            SocketListener(socket, false) {
+}
+
+bool NetlinkListener::onDataAvailable(SocketClient *cli)
+{
+    int socket = cli->getSocket();
+    LOGD("NetlinkListener::onDataAvailable()");
+
+    int count;
+
+    if ((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) {
+        LOGE("recv failed (%s)", strerror(errno));
+        return false;
+    }
+
+    NetlinkEvent *evt = new NetlinkEvent();
+    if (!evt->decode(mBuffer, count)) {
+        LOGE("Error decoding NetlinkEvent");
+        goto out;
+    }
+
+    LOGD("Ignoring '%s' netlink event", evt->getSubsystem());
+
+out:
+    delete evt;
+    return true;
+}
diff --git a/libsysutils/src/ServiceManager.cpp b/libsysutils/src/ServiceManager.cpp
new file mode 100644
index 0000000..700ac91
--- /dev/null
+++ b/libsysutils/src/ServiceManager.cpp
@@ -0,0 +1,73 @@
+#include <errno.h>
+
+#include <sysutils/ServiceManager.h>
+
+#define LOG_TAG "Service"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+ServiceManager::ServiceManager() {
+}
+
+int ServiceManager::start(const char *name) {
+    if (isRunning(name)) {
+        LOGW("Service '%s' is already running", name);
+        return 0;
+    }
+
+    LOGD("Starting service '%s'", name);
+    property_set("ctl.start", name);
+
+    int count = 200;
+    while(count--) {
+        sched_yield();
+        if (isRunning(name))
+            break;
+    }
+    if (!count) {
+        LOGW("Timed out waiting for service '%s' to start", name);
+        errno = ETIMEDOUT;
+        return -1;
+    }
+    LOGD("Sucessfully started '%s'", name);
+    return 0;
+}
+
+int ServiceManager::stop(const char *name) {
+    if (!isRunning(name)) {
+        LOGW("Service '%s' is already stopped", name);
+        return 0;
+    }
+
+    LOGD("Stopping service '%s'", name);
+    property_set("ctl.stop", name);
+
+    int count = 200;
+    while(count--) {
+        sched_yield();
+        if (!isRunning(name))
+            break;
+    }
+
+    if (!count) {
+        LOGW("Timed out waiting for service '%s' to stop", name);
+        errno = ETIMEDOUT;
+        return -1;
+    }
+    LOGD("Sucessfully 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);
+
+
+    if (property_get(propName, propVal, NULL)) {
+        if (!strcmp(propVal, "running"))
+            return true;
+    }
+    return false;
+}
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
new file mode 100644
index 0000000..857ed4d
--- /dev/null
+++ b/libsysutils/src/SocketClient.cpp
@@ -0,0 +1,58 @@
+#include <alloca.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <string.h>
+
+#define LOG_TAG "SocketClient"
+#include <cutils/log.h>
+
+#include <sysutils/SocketClient.h>
+
+SocketClient::SocketClient(int socket) {
+    mSocket = socket;
+    pthread_mutex_init(&mWriteMutex, NULL);
+}
+
+int SocketClient::sendMsg(int code, const char *msg, bool addErrno) {
+    char *buf;
+
+    if (addErrno) {
+        buf = (char *) alloca(strlen(msg) + strlen(strerror(errno)) + 8);
+        sprintf(buf, "%.3d %s (%s)", code, msg, strerror(errno));
+    } else {
+        buf = (char *) alloca(strlen(msg) + strlen("XXX "));
+        sprintf(buf, "%.3d %s", code, msg);
+    }
+    return sendMsg(buf);
+}
+
+int SocketClient::sendMsg(const char *msg) {
+    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;
+
+    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 :(");
+            errno = EIO;
+            pthread_mutex_unlock(&mWriteMutex);
+            return -1;
+        }
+        p += rc;
+        brtw -= rc;
+    }
+    pthread_mutex_unlock(&mWriteMutex);
+    return 0;
+}
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
new file mode 100644
index 0000000..1a937c2
--- /dev/null
+++ b/libsysutils/src/SocketListener.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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 <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#define LOG_TAG "SocketListener"
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+
+#include <sysutils/SocketListener.h>
+#include <sysutils/SocketClient.h>
+
+SocketListener::SocketListener(const char *socketName, bool listen) {
+    mListen = listen;
+    mSocketName = socketName;
+    mSock = -1;
+    pthread_mutex_init(&mClientsLock, NULL);
+    mClients = new SocketClientCollection();
+}
+
+SocketListener::SocketListener(int socketFd, bool listen) {
+    mListen = listen;
+    mSocketName = NULL;
+    mSock = socketFd;
+    pthread_mutex_init(&mClientsLock, NULL);
+    mClients = new SocketClientCollection();
+}
+
+int SocketListener::startListener() {
+
+    if (!mSocketName && mSock == -1) {
+        errno = EINVAL;
+        return -1;
+    } else if (mSocketName) {
+        if ((mSock = android_get_control_socket(mSocketName)) < 0) {
+            LOGE("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));
+        return -1;
+    } else if (!mListen)
+        mClients->push_back(new SocketClient(mSock));
+
+    if (pipe(mCtrlPipe))
+        return -1;
+
+    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this))
+        return -1;
+
+    return 0;
+}
+
+int SocketListener::stopListener() {
+    char c = 0;
+
+    if (write(mCtrlPipe[1], &c, 1) != 1) {
+        LOGE("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));
+        return -1;
+    }
+    close(mCtrlPipe[0]);
+    close(mCtrlPipe[1]);
+    return 0;
+}
+
+void *SocketListener::threadStart(void *obj) {
+    SocketListener *me = reinterpret_cast<SocketListener *>(obj);
+
+    me->runListener();
+    pthread_exit(NULL);
+    return NULL;
+}
+
+void SocketListener::runListener() {
+
+    while(1) {
+        SocketClientCollection::iterator it;
+        fd_set read_fds;
+        int rc = 0;
+        int max = 0;
+
+        FD_ZERO(&read_fds);
+
+        if (mListen) {
+            max = mSock;
+            FD_SET(mSock, &read_fds);
+        }
+
+        FD_SET(mCtrlPipe[0], &read_fds);
+        if (mCtrlPipe[0] > max)
+            max = mCtrlPipe[0];
+
+        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();
+        }
+        pthread_mutex_unlock(&mClientsLock);
+
+        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
+            LOGE("select failed (%s)", strerror(errno));
+            sleep(1);
+            continue;
+        } else if (!rc)
+            continue;
+
+        if (FD_ISSET(mCtrlPipe[0], &read_fds))
+            break;
+        if (mListen && FD_ISSET(mSock, &read_fds)) {
+            struct sockaddr addr;
+            socklen_t alen = sizeof(addr);
+            int c;
+
+            if ((c = accept(mSock, &addr, &alen)) < 0) {
+                LOGE("accept failed (%s)", strerror(errno));
+                sleep(1);
+                continue;
+            }
+            pthread_mutex_lock(&mClientsLock);
+            mClients->push_back(new SocketClient(c));
+            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);
+                    }
+                    FD_CLR(fd, &read_fds);
+                    continue;
+                }
+            }
+            pthread_mutex_unlock(&mClientsLock);
+        } while (0);
+    }
+}
+
+void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
+    pthread_mutex_lock(&mClientsLock);
+    SocketClientCollection::iterator i;
+
+    for (i = mClients->begin(); i != mClients->end(); ++i) {
+        if ((*i)->sendMsg(code, msg, addErrno)) {
+            LOGW("Error sending broadcast (%s)", strerror(errno));
+        }
+    }
+    pthread_mutex_unlock(&mClientsLock);
+}
+
+void SocketListener::sendBroadcast(const char *msg) {
+    pthread_mutex_lock(&mClientsLock);
+    SocketClientCollection::iterator i;
+
+    for (i = mClients->begin(); i != mClients->end(); ++i) {
+        if ((*i)->sendMsg(msg)) {
+            LOGW("Error sending broadcast (%s)", strerror(errno));
+        }
+    }
+    pthread_mutex_unlock(&mClientsLock);
+}
diff --git a/libzipfile/centraldir.c b/libzipfile/centraldir.c
index 4387ceb..0391c09 100644
--- a/libzipfile/centraldir.c
+++ b/libzipfile/centraldir.c
@@ -13,7 +13,7 @@
     // central directory entries

     ENTRY_SIGNATURE = 0x02014b50,

     ENTRY_LEN = 46,          // CentralDirEnt len, excl. var fields

-    

+

     // local file header

     LFH_SIZE = 30,

 };

@@ -73,8 +73,6 @@
     unsigned short  lastModFileTime;

     unsigned short  lastModFileDate;

     unsigned long   crc32;

-    unsigned long   compressedSize;

-    unsigned long   uncompressedSize;

     unsigned short  extraFieldLength;

     unsigned short  fileCommentLength;

     unsigned short  diskNumberStart;

@@ -85,7 +83,7 @@
     const unsigned char*  fileComment;

     unsigned int dataOffset;

     unsigned short lfhExtraFieldSize;

-    

+

 

     p = *buf;

 

@@ -106,7 +104,7 @@
     lastModFileTime = read_le_short(&p[0x0c]);

     lastModFileDate = read_le_short(&p[0x0e]);

     crc32 = read_le_int(&p[0x10]);

-    compressedSize = read_le_int(&p[0x14]);

+    entry->compressedSize = read_le_int(&p[0x14]);

     entry->uncompressedSize = read_le_int(&p[0x18]);

     entry->fileNameLength = read_le_short(&p[0x1c]);

     extraFieldLength = read_le_short(&p[0x1e]);

@@ -141,14 +139,14 @@
         fileComment = NULL;

     }

     p += fileCommentLength;

-    

+

     *buf = p;

 

     // the size of the extraField in the central dir is how much data there is,

     // but the one in the local file header also contains some padding.

     p = file->buf + localHeaderRelOffset;

     extraFieldLength = read_le_short(&p[0x1c]);

-    

+

     dataOffset = localHeaderRelOffset + LFH_SIZE

         + entry->fileNameLength + extraFieldLength;

     entry->data = file->buf + dataOffset;

@@ -243,7 +241,7 @@
             free(entry);

             goto bail;

         }

-        

+

         // add it to our list

         entry->next = file->entries;

         file->entries = entry;

@@ -253,4 +251,3 @@
 bail:

     return -1;

 }

-

diff --git a/libzipfile/zipfile.c b/libzipfile/zipfile.c
index b52d02d..a401a9b 100644
--- a/libzipfile/zipfile.c
+++ b/libzipfile/zipfile.c
@@ -82,13 +82,13 @@
     unsigned long crc;
     int err = 0;
     int zerr;
-    
+
     memset(&zstream, 0, sizeof(zstream));
     zstream.zalloc = Z_NULL;
     zstream.zfree = Z_NULL;
     zstream.opaque = Z_NULL;
     zstream.next_in = (void*)in;
-    zstream.avail_in = unlen;
+    zstream.avail_in = clen;
     zstream.next_out = (Bytef*) out;
     zstream.avail_out = unlen;
     zstream.data_type = Z_UNKNOWN;
@@ -99,7 +99,7 @@
     if (zerr != Z_OK) {
         return -1;
     }
-    
+
     // uncompress the data
     zerr = inflate(&zstream, Z_FINISH);
     if (zerr != Z_STREAM_END) {
@@ -107,7 +107,7 @@
                     zstream.total_out);
         err = -1;
     }
-    
+
      inflateEnd(&zstream);
     return err;
 }
diff --git a/logcat/event-log-tags b/logcat/event-log-tags
index 28cad0a..4dabbbc 100644
--- a/logcat/event-log-tags
+++ b/logcat/event-log-tags
@@ -21,7 +21,7 @@
 # 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
@@ -58,11 +58,11 @@
 # 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. 
+# 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 
+# Leave IDs through 2739 for more power logs
 #
 
 # This event is logged when the location service uploads location data.
@@ -94,7 +94,7 @@
 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, connection state, connection error, network state)
+#     (eventType << 24) + (connection state << 16) + (connection error << 8) + network state
 2801 gtalk_connection (status|1)
 
 2802 watchdog (Service|3)
@@ -135,6 +135,26 @@
 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)
+
+
 # Do not change these names without updating the checkin_events setting in
 # google3/googledata/wireless/android/provisioning/gservices.config !!
 #
@@ -160,9 +180,9 @@
 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. 
+# 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 
+# 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)
@@ -262,28 +282,28 @@
 # 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)   
+# [ 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)   
+# [ 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)   
+# [ 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)   
+# [ 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)   
+# [ 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:
@@ -323,11 +343,17 @@
 # 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)
+
 # 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 
+# 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)
 
@@ -344,3 +370,10 @@
 # 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/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c
index d803cf6..3642647 100644
--- a/mkbootimg/mkbootimg.c
+++ b/mkbootimg/mkbootimg.c
@@ -63,6 +63,7 @@
             "       [ --second <2ndbootloader-filename> ]\n"
             "       [ --cmdline <kernel-commandline> ]\n"
             "       [ --board <boardname> ]\n"
+            "       [ --base <address> ]\n"
             "       -o|--output <filename>\n"
             );
     return 1;
@@ -104,7 +105,6 @@
     char *bootimg = 0;
     char *board = "";
     unsigned pagesize = 2048;
-    unsigned saddr = 0;
     int fd;
     SHA_CTX ctx;
     uint8_t* sha;
@@ -114,6 +114,14 @@
 
     memset(&hdr, 0, sizeof(hdr));
 
+        /* default load addresses */
+    hdr.kernel_addr =  0x10008000;
+    hdr.ramdisk_addr = 0x11000000;
+    hdr.second_addr =  0x10F00000;
+    hdr.tags_addr =    0x10000100;
+
+    hdr.page_size = pagesize;
+
     while(argc > 0){
         char *arg = argv[0];
         char *val = argv[1];
@@ -132,8 +140,12 @@
             second_fn = val;
         } else if(!strcmp(arg, "--cmdline")) {
             cmdline = val;
-        } else if(!strcmp(arg, "--saddr")) {
-            saddr = strtoul(val, 0, 16);
+        } else if(!strcmp(arg, "--base")) {
+            unsigned base = strtoul(val, 0, 16);
+            hdr.kernel_addr =  base + 0x00008000;
+            hdr.ramdisk_addr = base + 0x01000000;
+            hdr.second_addr =  base + 0x00F00000;
+            hdr.tags_addr =    base + 0x00000100;
         } else if(!strcmp(arg, "--board")) {
             board = val;
         } else {
@@ -163,16 +175,6 @@
 
     strcpy(hdr.name, board);
 
-    hdr.kernel_addr =  0x10008000;
-    hdr.ramdisk_addr = 0x11000000;
-    if(saddr) {
-        hdr.second_addr =  0x00300000;
-    } else {
-        hdr.second_addr =  0x10F00000;
-    }
-    hdr.tags_addr   =  0x10000100;
-    hdr.page_size = pagesize;
-
     memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
 
     if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) {
diff --git a/mountd/ASEC.c b/mountd/ASEC.c
deleted file mode 100644
index a6aab9c..0000000
--- a/mountd/ASEC.c
+++ /dev/null
@@ -1,774 +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.
- */
-
-/*
-** Android Secure External Cache 
-*/
-
-#include "mountd.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <stdlib.h>
-#include <poll.h>
-#include <errno.h>
-
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-
-#include <linux/dm-ioctl.h>
-#include <linux/loop.h>
-
-#include <cutils/properties.h>
-#include <cutils/misc.h>
-
-#include "ASEC.h"
-
-//#define MODULE_FAILURE_IS_FATAL
-
-extern int init_module(void *, unsigned long, const char *);
-extern int delete_module(const char *, unsigned int);
-
-struct asec_context
-{
-    char *name;           // Device mapper volume name
-    char *srcPath;        // Path to the source (original) mount
-    char *backingFile;    // Name of the image file
-    unsigned int sectors; // Number of sectors
-    char *dstPath;        // Destination mount point
-    char *crypt;          // Crypt options
-
-    boolean needs_format;
-    boolean started;
-    int cacheFd;
-    int lo_num;
-    int dm_num;
-    unsigned char key[16];
-};
-
-static const char *MODULES[] = { "dm_mod", "crypto", "crypto_algapi", "crypto_blkcipher", 
-                                 "cryptomgr", "dm_crypt", "jbd",  
-                                 "twofish_common", "twofish", "cbc",
-                                 "mbcache", "ext3",
-                                 NULL };
-static const char KEY_PATH[] = "/data/system/asec.key";
-static const char MODULE_PATH[] = "/system/lib/modules";
-static const char MKE2FS_PATH[] = "/system/bin/mke2fs";
-static const char E2FSCK_PATH[] = "/system/bin/e2fsck";
-
-boolean AsecIsStarted(void *Handle)
-{
-    struct asec_context *ctx = (struct asec_context *) Handle;
-
-    return ctx->started;
-}
-
-const char *AsecMountPoint(void *Handle)
-{
-    struct asec_context *ctx = (struct asec_context *) Handle;
-
-    return ctx->dstPath;
-}
-
-static boolean AsecIsEnabled()
-{
-    char value[PROPERTY_VALUE_MAX];
-    int  enabled;
-
-    property_get(ASEC_ENABLED, value, "0");
-
-    if (atoi(value) == 1)
-        return true;
-    return false;
-}
-
-void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile,
-               const char *Size, const char *DstPath, const char *Crypt)
-{
-    struct asec_context *ctx;
-
-    if (!AsecIsEnabled())
-        return NULL;
-
-    LOG_ASEC("AsecInit(%s, %s, %s, %s, %s, %s):\n",
-             Name, SrcPath, BackingFile, Size, DstPath, Crypt);
-
-    if (!Name || !SrcPath || !BackingFile || !Size || !DstPath || !Crypt) {
-        LOG_ERROR("AsecInit(): Invalid arguments\n");
-        return NULL;
-    }
-
-    if (!(ctx = malloc(sizeof(struct asec_context)))) {
-        LOG_ERROR("AsecInit(): Out of memory\n");
-        return NULL;
-    }
-
-    memset(ctx, 0, sizeof(struct asec_context));
-    ctx->name = strdup(Name);
-    ctx->srcPath = strdup(SrcPath);
-    ctx->backingFile = strdup(BackingFile);
-    ctx->sectors = atoi(Size);
-    ctx->dstPath = strdup(DstPath);
-    ctx->crypt = strdup(Crypt);
-    return ctx;
-}
-
-void AsecDeinit(void *Handle)
-{
-    struct asec_context *ctx = (struct asec_context *) Handle;
-
-    free(ctx->name);
-    free(ctx->srcPath);
-    free(ctx->backingFile);
-    free(ctx->dstPath);
-    free(ctx->crypt);
-
-    free(ctx);
-}
-
-static int AsecLoadModules()
-{
-    int i;
-
-    for (i = 0; MODULES[i] != NULL; i++) {
-	const char *moduleName = MODULES[i];
-        char moduleFile[255];
-        int rc = 0;
-        void *module;
-        unsigned int size;
-
-        sprintf(moduleFile, "%s/%s.ko", MODULE_PATH, moduleName);
-        module = load_file(moduleFile, &size);
-        if (!module) {
-            LOG_ERROR("Failed to load module %s (%d)\n", moduleFile, errno);
-            return -1;
-        }
-
-        rc = init_module(module, size, "");
-        free(module);
-        if (rc && errno != EEXIST) {
-            LOG_ERROR("Failed to init module %s (%d)\n", moduleFile, errno);
-            return -errno;
-        }
-    }
-    return 0;
-}
-
-static int AsecUnloadModules()
-{
-    int i, j, rc;
-
-    for (i = 0; MODULES[i] != NULL; i++);
-
-    for (j = (i - 1); j >= 0; j--) {
-	const char *moduleName = MODULES[j];
-        int maxretry = 10;
-        while(maxretry-- > 0) {
-            rc = delete_module(moduleName, O_NONBLOCK | O_EXCL);
-            if (rc < 0 && errno == EAGAIN)
-                usleep(500000);
-            else
-                break;
-        }
-        if (rc != 0) {
-            LOG_ERROR("Failed to unload module %s\n", moduleName);
-            return -errno;
-        }
-    }
-    return 0;
-}
-
-static int AsecGenerateKey(struct asec_context *ctx)
-{
-    LOG_ASEC("AsecGenerateKey():\n");
-
-    memset((void *) ctx->key, 0x69, sizeof(ctx->key));
-    return 0;
-}
-
-static int AsecLoadGenerateKey(struct asec_context *ctx)
-{
-    int fd;
-    int rc = 0;
-
-    if ((fd = open(KEY_PATH, O_RDWR | O_CREAT, 0600)) < 0) {
-        LOG_ERROR("Error opening / creating keyfile (%d)\n", errno);
-        return -errno;
-    }
-
-    if (read(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) {
-        LOG_ASEC("Generating key\n");
-        if ((rc = AsecGenerateKey(ctx)) < 0) {
-            LOG_ERROR("Error generating key (%d)\n", rc);
-            goto out;
-        }
-        if (write(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) {
-            LOG_ERROR("Error writing keyfile (%d)\n", errno);
-            rc = -1;
-            goto out;
-        }
-    }
-    
- out:
-    close (fd);
-    return rc;
-}
-
-static int AsecFormatFilesystem(struct asec_context *ctx)
-{
-    char cmdline[255];
-    int rc;
-
-    sprintf(cmdline,
-            "%s -b 4096 -m 1 -j -L \"%s\" /dev/block/dm-%d",
-            MKE2FS_PATH, ctx->name, ctx->dm_num);
-
-    LOG_ASEC("Formatting filesystem (%s)\n", cmdline);
-    // XXX: PROTECT FROM VIKING KILLER
-    if ((rc = system(cmdline)) < 0) {
-        LOG_ERROR("Error executing format command (%d)\n", errno);
-        return -errno;
-    }
-
-    rc = WEXITSTATUS(rc);
-
-    if (!rc) {
-        LOG_ASEC("Format completed\n");
-    } else {
-        LOG_ASEC("Format failed (%d)\n", rc);
-    }
-
-    return rc;
-}
-
-static int AsecCheckFilesystem(struct asec_context *ctx)
-{
-    char cmdline[255];
-    int rc;
-
-    sprintf(cmdline, "%s -p /dev/block/dm-%d", E2FSCK_PATH, ctx->dm_num);
-
-    LOG_ASEC("Checking filesystem (%s)\n", cmdline);
-    // XXX: PROTECT FROM VIKING KILLER
-    if ((rc = system(cmdline)) < 0) {
-        LOG_ERROR("Error executing check command (%d)\n", errno);
-        return -errno;
-    }
-
-    rc = WEXITSTATUS(rc);
-
-    if (rc == 0) {
-        LOG_ASEC("ASEC volume '%s' had no errors\n", ctx->name);
-    } else if (rc == 1) {
-        LOG_ASEC("ASEC volume '%s' had corrected errors\n", ctx->name);
-        rc = 0;
-    } else if (rc == 2) {
-        LOG_ERROR("ASEC volume '%s' had corrected errors (system should be rebooted)\n", ctx->name);
-    } else if (rc == 4) {
-        LOG_ERROR("ASEC volume '%s' had uncorrectable errors\n", ctx->name);
-    } else if (rc == 8) {
-        LOG_ERROR("Operational error while checking volume '%s'\n", ctx->name);
-    } else {
-        LOG_ERROR("Unknown e2fsck exit code (%d)\n", rc);
-    }
-    return rc;
-}
-
-static int AsecOpenCreateCache(struct asec_context *ctx)
-{
-    char filepath[255];
-
-    sprintf(filepath, "%s/%s", ctx->srcPath, ctx->backingFile);
-
-    if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) {
-        if (errno == ENOENT) {
-            int rc = 0;
-
-            LOG_ASEC("Creating cache file (%u sectors)\n", ctx->sectors);
-            if ((ctx->cacheFd = creat(filepath, 0600)) < 0) {
-                LOG_ERROR("Error creating cache (%d)\n", errno);
-                return -errno;
-            }
-            if (ftruncate(ctx->cacheFd, ctx->sectors * 512) < 0) {
-                LOG_ERROR("Error truncating cache (%d)\n", errno);
-                close(ctx->cacheFd);
-                unlink(filepath);
-                return -errno;
-            }
-            LOG_ASEC("Cache created (%u sectors) \n", ctx->sectors);
-            close(ctx->cacheFd); // creat() is WRONLY
-           
-            if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) {
-               LOG_ERROR("Error opening cache file (%d)\n", errno);
-                close(ctx->cacheFd);
-                unlink(filepath);
-                return -errno;
-            }
-
-            ctx->needs_format = 1;
-        } else
-            return -errno;
-    } else {
-        struct stat stat_buf;
-
-        if (fstat(ctx->cacheFd, &stat_buf) < 0) {
-            LOG_ERROR("Failed to fstat cache (%d)\n", errno);
-            close(ctx->cacheFd);
-            return -errno;
-        }
-        if (stat_buf.st_size != ctx->sectors * 512) {
-            LOG_ERROR("Cache size %lld != configured size %u\n",
-                      stat_buf.st_size, ctx->sectors * 512);
-        }
-
-        // XXX: Verify volume label matches ctx->name
-    }
-
-    return 0;
-}
-
-static void AsecCloseCache(struct asec_context *ctx)
-{
-    close(ctx->cacheFd);
-}
-
-static void *_align(void *ptr, unsigned int a)
-{
-        register unsigned long agn = --a;
-
-        return (void *) (((unsigned long) ptr + agn) & ~agn);
-}
-
-static struct dm_ioctl *_dm_ioctl_setup(struct asec_context *ctx, int flags)
-{
-    void *buffer;
-    void *p;
-    const size_t min_size = 16 * 1024;
-    size_t len = sizeof(struct dm_ioctl);
-    struct dm_ioctl *io;
-    struct dm_target_spec *tgt;
-    int i;
-    char params[1024];
-    char key[80];
-
-    key[0] = '\0';
-
-    for (i = 0; i < (int) sizeof(ctx->key); i++) {
-        char tmp[8];
-
-        sprintf(tmp, "%02x", ctx->key[i]);
-        strcat(key, tmp);
-    }
-
-    // XXX: Handle ctx->crypt 
-    sprintf(params, "twofish %s 0 /dev/block/loop%d 0", key, ctx->lo_num);
- 
-    if (len < min_size)
-        len = min_size;
-
-    if (!(buffer = malloc(len))) {
-        LOG_ERROR("Unable to allocate memory\n");
-        return NULL;
-    }
-
-    memset(buffer, 0, len);
-    io = buffer;
-    tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
-    
-    io->version[0] = 4;
-    io->version[1] = 0;
-    io->version[2] = 0;
-
-    io->data_size = len;
-    io->data_start = sizeof(struct dm_ioctl);
-
-    io->flags = flags;
-    io->dev = 0; 
-
-    io->target_count = 1;
-    io->event_nr = 1;
-    strncpy(io->name, ctx->name, sizeof(io->name));
-
-    tgt->status = 0;
-    tgt->sector_start = 0;
-    tgt->length = ctx->sectors;
-    strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type));
-
-    p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
-    strcpy((char *) p, params);
-    p+= strlen(params) + 1;
-
-    p = _align(p, 8);
-    tgt->next = p - buffer;
-
-    return io;
-}
-
-static int FindNextAvailableDm()
-{
-    int i;
-
-    for (i = 0; i < 8; i++) {
-        char path[255];
-        sprintf(path, "/dev/block/dm-%d", i);
-        if ((access(path, F_OK) < 0) && (errno == ENOENT))
-            return i;
-    }
-
-    LOG_ERROR("Out of device mapper numbers\n");
-    return -1;
-}
-
-static int AsecCreateDeviceMapping(struct asec_context *ctx)
-{
-    struct dm_ioctl       *io;
-    int                   dmFd;
-    int                   rc = 0;
-
-    ctx->dm_num = FindNextAvailableDm();
-
-    if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
-        LOG_ERROR("Error opening device mapper (%d)\n", errno);
-        return -errno;
-    }
-
-    if (!(io = _dm_ioctl_setup(ctx, 0))) {
-        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
-        close(dmFd);
-        return -ENOMEM;
-    }
-
-    if ((rc = ioctl(dmFd, DM_DEV_CREATE, io)) < 0) {
-        LOG_ERROR("device-mapper create ioctl failed (%d)\n", errno);
-        rc = -errno;
-        goto out_free;
-    } 
-
-    free(io);
-
-    if (!(io = _dm_ioctl_setup(ctx, DM_STATUS_TABLE_FLAG))) {
-        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
-        rc = -ENOMEM;
-        goto out_nofree;
-    }
- 
-    if ((rc = ioctl(dmFd, DM_TABLE_LOAD, io)) < 0) {
-        LOG_ERROR("device-mapper load ioctl failed (%d)\n", errno);
-        rc = -errno;
-        goto out_free;
-    }
-
-    free(io);
- 
-    if (!(io = _dm_ioctl_setup(ctx, 0))) {
-        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
-        rc = -ENOMEM;
-        goto out_nofree;
-    }
-
-    if ((rc = ioctl(dmFd, DM_DEV_SUSPEND, io)) < 0) {
-        LOG_ERROR("device-mapper resume ioctl failed (%d)\n", errno);
-        rc = -errno;
-        goto out_free;
-    }
-
-out_free:
-    free (io);
-out_nofree:
-    close (dmFd);
-    return rc;
-}
-
-static int AsecDestroyDeviceMapping(struct asec_context *ctx)
-{
-    struct dm_ioctl       *io;
-    int                   dmFd;
-    int                   rc = 0;
-
-    if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
-        LOG_ERROR("Error opening device mapper (%d)\n", errno);
-        return -errno;
-    }
-
-    if (!(io = _dm_ioctl_setup(ctx, DM_PERSISTENT_DEV_FLAG))) {
-        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
-        rc = -ENOMEM;
-        goto out_nofree;
-    }
-
-    if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) {
-        LOG_ERROR("device-mapper remove ioctl failed (%d)\n", errno);
-        rc = -errno;
-        goto out_free;
-    } 
-
-out_free:
-    free (io);
-out_nofree:
-    close (dmFd);
-    return rc;
-}
-
-static int AsecMountCache(struct asec_context *ctx)
-{
-    int flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME;
-    char devname[255];
-
-    if (access(ctx->dstPath, R_OK)) {
-        LOG_ERROR("Destination mount point '%s' unavailable (%d)\n", ctx->dstPath, errno);
-        return -errno;
-    }
-
-    sprintf(devname, "/dev/block/dm-%d", ctx->dm_num);
-
-    if (mount(devname, ctx->dstPath, "ext3", flags, NULL)) {
-        LOG_ERROR("ASEC mount failed (%d)\n", errno);
-        return -errno;
-    }
-    
-    return 0;
-}
-
-static int AsecUnmountCache(struct asec_context *ctx)
-{
-    if (umount(ctx->dstPath)) {
-        if (errno == EBUSY) {
-            LOG_ASEC("ASEC volume '%s' still busy\n", ctx->name);
-        } else {
-            LOG_ERROR("ASEC umount failed (%d)\n", errno);
-        }
-        return -errno;
-    }
-    LOG_ASEC("ASEC volume '%s' unmounted\n", ctx->name);
-    return 0;
-}
-
-static int FindNextAvailableLoop()
-{
-    int i;
-
-    for (i = 0; i < MAX_LOOP; i++) {
-        struct loop_info info;
-        char devname[255];
-        int fd;
-
-        sprintf(devname, "/dev/block/loop%d", i);
-
-        if ((fd = open(devname, O_RDONLY)) < 0) {
-            LOG_ERROR("Unable to open %s (%d)\n", devname, errno);
-            return -errno;
-        }
-
-        if (ioctl(fd, LOOP_GET_STATUS, &info) < 0) {
-            close(fd);
-
-            if (errno == ENXIO)
-                return i;
-
-            LOG_ERROR("Unable to get loop status for %s (%d)\n", devname, errno);
-            return -errno;
-        }
-        close(fd);
-    }
-    return -ENXIO;
-}
-
-static int AsecCreateLoop(struct asec_context *ctx)
-{
-    char devname[255];
-    int device_fd;
-    int rc = 0;
-
-    ctx->lo_num = FindNextAvailableLoop();
-    if (ctx->lo_num < 0) {
-        LOG_ERROR("No loop devices available\n");
-        return -ENXIO;
-    }
-
-    sprintf(devname, "/dev/block/loop%d", ctx->lo_num);
-    device_fd = open(devname, O_RDWR);
-    if (device_fd < 0) {
-        LOG_ERROR("failed to open loop device (%d)\n", errno);
-        return -errno;
-    }
-
-    if (ioctl(device_fd, LOOP_SET_FD, ctx->cacheFd) < 0) {
-        LOG_ERROR("loop_set_fd ioctl failed (%d)\n", errno);
-        rc = -errno;
-    }
-    close(device_fd);
-    return rc;
-}
-
-static int AsecDestroyLoop(struct asec_context *ctx)
-{
-    char devname[255];
-    int device_fd;
-    int rc = 0;
-
-    sprintf(devname, "/dev/block/loop%d", ctx->lo_num);
-    device_fd = open(devname, O_RDONLY);
-    if (device_fd < 0) {
-        LOG_ERROR("Failed to open loop (%d)\n", errno);
-        return -errno;
-    }
-
-    if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
-        LOG_ERROR("Failed to destroy loop (%d)\n", errno);
-        rc = -errno;
-    }
-
-    close(device_fd);
-    return rc;
-}
-
-int AsecStart(void *Handle)
-{
-    struct asec_context *ctx = (struct asec_context *) Handle;
-    char value[PROPERTY_VALUE_MAX];
-    int rc = 0;
-
-    if (!ctx)
-        return -EINVAL;
-
-    if (ctx->started)
-        return -EBUSY;
-
-    LOG_ASEC("AsecStart(%s):\n", ctx->name);
-
-    NotifyAsecState(ASEC_BUSY, ctx->dstPath);
-
-    if ((rc = AsecLoadModules()) < 0) {
-        LOG_ERROR("AsecStart: Failed to load kernel modules\n");
-#ifdef MODULE_FAILURE_IS_FATAL
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-	return rc;
-#endif
-    }
-
-    if ((rc = AsecLoadGenerateKey(ctx))) {
-        LOG_ERROR("AsecStart: Failed to load / generate key\n");
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-	return rc;
-    }
-    
-    if ((rc = AsecOpenCreateCache(ctx)) < 0) {
-        LOG_ERROR("AsecStart: Failed to open / create cache\n");
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-	return rc;
-    }
-
-    if ((rc = AsecCreateLoop(ctx)) < 0) {
-        LOG_ERROR("AsecStart: Failed to create loop\n");
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-	goto fail_closecache;
-    }
-
-    if ((rc = AsecCreateDeviceMapping(ctx)) < 0) {
-        LOG_ERROR("AsecStart: Failed to create devmapping (%d)\n", rc);
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-        goto fail_destroyloop;
-    }
-    
-    if (ctx->needs_format) {
-        if ((rc = AsecFormatFilesystem(ctx))) {
-            LOG_ERROR("AsecStart: Failed to format cache (%d)\n", rc);
-            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-            goto fail_destroydm;
-        }
-        ctx->needs_format = 0;
-    } else {
-        if ((rc = AsecCheckFilesystem(ctx))) {
-            LOG_ERROR("AsecStart: Failed to check filesystem (%d)\n", rc);
-            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-            goto fail_destroydm;
-        }
-    }
-
-    if ((rc = AsecMountCache(ctx)) < 0) {
-        LOG_ERROR("AsecStart: Failed to mount cache (%d)\n", rc);
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-        goto fail_destroydm;
-    }
-    
-    NotifyAsecState(ASEC_AVAILABLE, ctx->dstPath);
-    ctx->started = true;
-
-    return rc;
-
- fail_destroydm:
-    AsecDestroyDeviceMapping(ctx);
- fail_destroyloop:
-    AsecDestroyLoop(ctx);
- fail_closecache:
-    AsecCloseCache(ctx);
-    return rc;
-}
-
-int AsecStop(void *Handle)
-{
-    struct asec_context *ctx = (struct asec_context *) Handle;
-    int rc = 0;
-
-    if (!ctx->started)
-        return -EINVAL;
-
-    LOG_ASEC("AsecStop(%s):\n", ctx->name);
-
-    NotifyAsecState(ASEC_BUSY, ctx->dstPath);
-
-    if ((rc = AsecUnmountCache(ctx)) < 0) {
-        LOG_ERROR("AsecStop: Failed to unmount cache (%d)\n", rc);
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-	return rc;
-    }
-
-    if ((rc = AsecDestroyDeviceMapping(ctx)) < 0) {
-        LOG_ERROR("AsecStop: Failed to destroy devmapping (%d)\n", rc);
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-	return rc;
-    }
-
-    if ((rc = AsecDestroyLoop(ctx)) < 0) {
-        LOG_ERROR("AsecStop: Failed to destroy loop device (%d)\n", rc);
-        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-	return rc;
-    }
-
-    AsecCloseCache(ctx);
- 
-    if ((rc = AsecUnloadModules()) < 0) {
-        if (rc == -EAGAIN) {
-            LOG_ASEC("AsecStop: Kernel modules still in use\n");
-        } else {
-            LOG_ERROR("AsecStop: Failed to unload kernel modules (%d)\n", rc);
-#ifdef MODULE_FAILURE_IS_FATAL
-            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
-	    return rc;
-#endif
-        }
-    }
-
-    ctx->started = false;
-    NotifyAsecState(ASEC_DISABLED, ctx->dstPath);
-    return rc;
-}
diff --git a/mountd/ASEC.h b/mountd/ASEC.h
deleted file mode 100644
index c87b288..0000000
--- a/mountd/ASEC.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef _ASEC_H
-#define _ASEC_H
-
-#define ASEC_STORES_MAX 4
-#define MAX_LOOP 8
-
-typedef enum AsecState {
-    // Feature disabled
-    ASEC_DISABLED,
-
-    // Feature enabled and operational
-    ASEC_AVAILABLE,
-
-    // Busy
-    ASEC_BUSY,
-
-    // Internal Error
-    ASEC_FAILED_INTERR,
-
-    // No media available
-    ASEC_FAILED_NOMEDIA,
-
-    // Media is corrupt
-    ASEC_FAILED_BADMEDIA,
-
-    // Key mismatch
-    ASEC_FAILED_BADKEY,
-} AsecState;
-
-/*
- * ASEC commands
- */
-#define ASEC_CMD_SEND_STATUS		"asec_send_status"
-#define ASEC_CMD_ENABLE			"asec_enable"
-#define ASEC_CMD_DISABLE		"asec_disable"
-
-/*
- * ASEC events
- */
-
-// These events correspond to the states in the AsecState enum.
-// A path to the ASEC mount point follows the colon
-#define ASEC_EVENT_DISABLED		"asec_disabled:"
-#define ASEC_EVENT_AVAILABLE		"asec_available:"
-#define ASEC_EVENT_BUSY			"asec_busy:"
-#define ASEC_EVENT_FAILED_INTERR	"asec_failed_interror:"
-#define ASEC_EVENT_FAILED_NOMEDIA	"asec_failed_nomedia"
-#define ASEC_EVENT_FAILED_BADMEDIA	"asec_failed_badmedia:"
-#define ASEC_EVENT_FAILED_BADKEY	"asec_failed_badkey:"
-
-/*
- * System Properties
- */
-
-#define ASEC_ENABLED			"asec.enabled"
-
-#define ASEC_STATUS			"ro.asec.status"
-#define ASEC_STATUS_DISABLED		"disabled"
-#define ASEC_STATUS_AVAILABLE		"available"
-#define ASEC_STATUS_BUSY			"busy"
-#define ASEC_STATUS_FAILED_INTERR	"internal_error"
-#define ASEC_STATUS_FAILED_NOMEDIA	"no_media"
-#define ASEC_STATUS_FAILED_BADMEDIA	"bad_media"
-#define ASEC_STATUS_FAILED_BADKEY	"bad_key"
-
-#endif
diff --git a/mountd/Android.mk b/mountd/Android.mk
deleted file mode 100644
index 16532fa..0000000
--- a/mountd/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=        \
-    AutoMount.c          \
-    ProcessKiller.c      \
-    Server.c             \
-    mountd.c		 \
-    ASEC.c		 \
-    logwrapper.c
-
-LOCAL_MODULE:= mountd
-
-LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
-
-LOCAL_CFLAGS := -DCREATE_MOUNT_POINTS=0
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-# disabled - we are using vold now instead
-# include $(BUILD_EXECUTABLE)
diff --git a/mountd/AutoMount.c b/mountd/AutoMount.c
deleted file mode 100644
index 12ad572..0000000
--- a/mountd/AutoMount.c
+++ /dev/null
@@ -1,1062 +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.
- */
-
-/*
-** mountd automount support
-*/
-
-#include "mountd.h"
-
-#include <pthread.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <stdlib.h>
-#include <poll.h>
-
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <linux/loop.h>
-#include <sys/inotify.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <linux/netlink.h>
-
-#define DEVPATH    "/dev/block/"
-#define DEVPATHLENGTH 11    // strlen(DEVPATH)
-
-// FIXME - only one loop mount is supported at a time
-#define LOOP_DEVICE "/dev/block/loop0"
-
-// timeout value for poll() when retries are pending
-#define POLL_TIMEOUT    1000
-
-#define MAX_MOUNT_RETRIES   3
-#define MAX_UNMOUNT_RETRIES   5
-
-typedef enum {
-    // device is unmounted
-    kUnmounted,
-    
-    // attempting to mount device
-    kMounting,
-    
-    // device is unmounted
-    kMounted,
-    
-    // attempting to unmount device
-    // so the media can be removed
-    kUnmountingForEject,
-    
-    // attempting to mount device
-    // so it can be shared via USB mass storage
-    kUnmountingForUms,
-} MountState;
-
-typedef struct MountPoint {
-    // block device to mount
-    const char* device;
-    
-    // mount point for device
-    const char* mountPoint;
-
-    // path to the UMS driver file for specifying the block device path
-    const char* driverStorePath;
-    
-    // true if device can be shared via
-    // USB mass storage
-    boolean enableUms;
- 
-    // Array of ASEC handles
-    void *asecHandles[ASEC_STORES_MAX];
-
-    // true if the device is being shared via USB mass storage
-    boolean umsActive;
-    
-    // current state of the mount point
-    MountState state;
-    
-    // number of mount or unmount retries so far, 
-    // when attempting to mount or unmount the device
-    int retryCount; 
- 
-    // next in sMountPointList linked list
-    struct MountPoint* next;   
-} MountPoint;
-
-// list of our mount points (does not change after initialization)
-static MountPoint* sMountPointList = NULL;
-boolean gMassStorageEnabled = false;
-boolean gMassStorageConnected = false;
-
-static pthread_t sAutoMountThread = 0;
-static pid_t gExcludedPids[2] = {-1, -1};
-
-static const char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck";
-
-// number of mount points that have timeouts pending
-static int sRetriesPending = 0;
-
-// for synchronization between sAutoMountThread and the server thread
-static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER;
-
-// requests the USB mass_storage driver to begin or end sharing a block device
-// via USB mass storage.
-static void SetBackingStore(MountPoint* mp, boolean enable) 
-{
-    int fd;
-
-    if (!mp->driverStorePath) {
-        LOG_ERROR("no driver_store_path specified in config file for %s", mp->device);
-        return;
-    }
-
-    LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false"));
-    fd = open(mp->driverStorePath, O_WRONLY);
-    if (fd < 0)
-    {
-        LOG_ERROR("could not open driver_store_path %s\n", mp->driverStorePath);
-    }
-    else
-    {
-        if (enable)
-        {
-            write(fd, mp->device, strlen(mp->device));
-            mp->umsActive = true;
-        }
-        else
-        {
-            char ch = 0;
-            write(fd, &ch, 1);
-            mp->umsActive = false;
-        }
-        close(fd);
-    }
-}
-
-static boolean ReadMassStorageState()
-{
-    FILE* file = fopen("/sys/class/switch/usb_mass_storage/state", "r");
-    if (file)
-    {
-        char    buffer[20];
-        fgets(buffer, sizeof(buffer), file);
-        fclose(file);
-        return (strncmp(buffer, "online", strlen("online")) == 0);
-    }
-    else
-    {
-        LOG_ERROR("could not read initial mass storage state\n");
-        return false;
-    }
-}
-
-static boolean IsLoopMounted(const char* path)
-{
-    FILE* f;
-    int count;
-    char device[256];
-    char mount_path[256];
-    char rest[256];
-    int result = 0;
-    int path_length = strlen(path);
-       
-    f = fopen("/proc/mounts", "r");
-    if (!f) {
-        LOG_ERROR("could not open /proc/mounts\n");
-        return -1;
-    }
-
-    do {
-        count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
-        if (count == 3) {
-            if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0)
-            {
-                result = 1;
-                break;
-            }
-        }
-    } while (count == 3);
-
-    fclose(f);
-    LOG_MOUNT("IsLoopMounted: %s returning %d\n", path, result);
-    return result;
-}
-
-static int CheckFilesystem(const char *device)
-{
-    char cmdline[255];
-    int rc;
-
-    // XXX: SAN: Check for FAT signature
-    
-    int result = access(FSCK_MSDOS_PATH, X_OK);
-    if (result != 0) {
-        LOG_MOUNT("CheckFilesystem(%s): %s not found (skipping checks)\n", FSCK_MSDOS_PATH, device);
-        return 0;
-    }
- 
-    char *args[7];
-    args[0] = FSCK_MSDOS_PATH;
-    args[1] = "-v";
-    args[2] = "-V";
-    args[3] = "-w";
-    args[4] = "-p";
-    args[5] = device;
-    args[6] = NULL;
-
-    LOG_MOUNT("Checking filesystem on %s\n", device);
-    rc = logwrap(6, args);
-  
-    // XXX: We need to be able to distinguish between a FS with an error
-    // and a block device which does not have a FAT fs at all on it
-    if (rc == 0) {
-        LOG_MOUNT("Filesystem check completed OK\n");
-        return 0;
-    } else if (rc == 1) {
-        LOG_MOUNT("Filesystem check failed (general failure)\n");
-        return -EINVAL;
-    } else if (rc == 2) {
-        LOG_MOUNT("Filesystem check failed (invalid usage)\n");
-        return -EIO;
-    } else {
-        LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc);
-        return -EIO;
-    }
-}
-
-static int DoMountDevice(const char* device, const char* mountPoint)
-{
-    LOG_MOUNT("Attempting mount of %s on %s\n", device, mountPoint);
-
-#if CREATE_MOUNT_POINTS
-    // make sure mount point exists
-    mkdir(mountPoint, 0000);
-#endif
-
-    int flags = 0;
-    
-    if (device && strncmp(device, "/dev/", 5))
-    {
-        // mount with the loop driver if device does not start with "/dev/"
-        int file_fd, device_fd;
-        
-        // FIXME - only one loop mount supported at a time
-        file_fd = open(device, O_RDWR);
-        if (file_fd < -1) {
-            LOG_ERROR("open backing file %s failed\n", device);
-            return 1;
-        }
-        device_fd = open(LOOP_DEVICE, O_RDWR);
-        if (device_fd < -1) {
-            LOG_ERROR("open %s failed", LOOP_DEVICE);
-            close(file_fd);
-            return 1;
-        }
-        if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
-        {
-            LOG_ERROR("ioctl LOOP_SET_FD failed\n");
-            close(file_fd);
-            close(device_fd);
-            return 1;
-        }
-
-        close(file_fd);
-        close(device_fd);
-        device = "/dev/block/loop0";
-    }
-
-    int result = access(device, R_OK);
-    if (result) {
-	LOG_ERROR("Unable to access '%s' (%d)\n", device, errno);
-   	return -errno;
-    }
-
-#if 0
-    if ((result = CheckFilesystem(device))) {
-        LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result);
-        // XXX:  Notify framework - need a new SDCARD state for the following:
-        //       - SD cards which are not present
-        //       - SD cards with no partition table
-        //       - SD cards with no filesystem
-        //       - SD cards with bad filesystem
-        return result;
-    }
-#endif
-
-    // Extra safety measures:
-    flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
-    // Also, set fmask = 711 so that files cannot be marked executable,
-    // and cannot by opened by uid 1000 (system). Similar, dmask = 700
-    // so that directories cannot be accessed by uid 1000.
-    result = mount(device, mountPoint, "vfat", flags, 
-                       "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
-    if (result && errno == EROFS) {
-        LOG_ERROR("mount failed EROFS, try again read-only\n");
-        flags |= MS_RDONLY;
-        result = mount(device, mountPoint, "vfat", flags,
-                       "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
-    }
-
-    if (result == 0) {
-        LOG_MOUNT("Partition %s mounted on %s\n", device, mountPoint);
-        NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0);
-
-        MountPoint* mp = sMountPointList;
-        while (mp) {
-            if (!strcmp(mountPoint, mp->mountPoint)) {
-                int i;
-             
-                for (i = 0; i < ASEC_STORES_MAX; i++) {
-                    if (mp->asecHandles[i] != NULL) {
-                        int a_result;
-                        if ((a_result = AsecStart(mp->asecHandles[i])) < 0) {
-                            LOG_ERROR("ASEC start failure (%d)\n", a_result);
-                        }
-                    }
-                }
-                break;
-            }
-            mp = mp -> next;
-        }
-    } else if (errno == EBUSY) {
-        LOG_MOUNT("Mount failed (already mounted)\n");
-        result = 0;
-    } else {
-#if CREATE_MOUNT_POINTS
-        rmdir(mountPoint);
-#endif
-        LOG_MOUNT("Unable to mount %s on %s\n", device, mountPoint);
-    }
-
-    return result;
-}
-
-static int DoUnmountDevice(MountPoint *mp)
-{
-    boolean loop = IsLoopMounted(mp->mountPoint);
-    int i;
-
-    for (i = 0; i < ASEC_STORES_MAX; i++) {
-        if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i]))
-            AsecStop(mp->asecHandles[i]);
-    }
-
-    int result = umount(mp->mountPoint);
-    LOG_MOUNT("umount returned %d errno: %d\n", result, errno);
-
-    if (result == 0)
-    {
-#if CREATE_MOUNT_POINTS
-        rmdir(mountPoint);
-#endif
-        NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
-    }
-
-    if (loop)
-    {
-        // free the loop device
-        int loop_fd = open(LOOP_DEVICE, O_RDONLY);
-        if (loop_fd < -1) {
-            LOG_ERROR("open loop device failed\n");
-        }
-        if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
-            LOG_ERROR("ioctl LOOP_CLR_FD failed\n");
-        }
-
-        close(loop_fd);
-    }
-
-    // ignore EINVAL and ENOENT, since it usually means the device is already unmounted
-    if (result && (errno == EINVAL || errno == ENOENT))
-        result = 0;
-
-    return result;
-}
-
-static int MountPartition(const char* device, const char* mountPoint)
-{
-    char    buf[100];
-    int i;
-    
-    // attempt to mount subpartitions of the device
-    for (i = 1; i < 10; i++)
-    {
-        int rc;
-        snprintf(buf, sizeof(buf), "%sp%d", device, i);
-        rc = DoMountDevice(buf, mountPoint);
-        LOG_MOUNT("DoMountDevice(%s, %s) = %d\n", buf, mountPoint, rc);
-        if (rc == 0)
-            return 0;
-    }
-
-    return -1;
-}
-
-/*****************************************************
- * 
- * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION
- * 
- *****************************************************/
-
-static void SetState(MountPoint* mp, MountState state)
-{
-    mp->state = state;
-}
-
-// Enter a state that requires retries and timeouts.
-static void SetRetries(MountPoint* mp, MountState state)
-{
-    SetState(mp, state);
-    mp->retryCount = 0;
-
-    sRetriesPending++;
-    // wake up the automounter thread if we are being called 
-    // from somewhere else with no retries pending
-    if (sRetriesPending == 1 && sAutoMountThread != 0 && 
-            pthread_self() != sAutoMountThread)
-        pthread_kill(sAutoMountThread, SIGUSR1);
-}
-
-// Exit a state that requires retries and timeouts.
-static void ClearRetries(MountPoint* mp, MountState state)
-{
-    SetState(mp, state);
-    sRetriesPending--;
-}
-
-// attempt to mount the specified mount point.
-// set up retry/timeout if it does not succeed at first.
-static void RequestMount(MountPoint* mp)
-{
-    LOG_MOUNT("RequestMount %s\n", mp->mountPoint);
-
-    if (mp->state != kMounted && mp->state != kMounting &&
-            access(mp->device, R_OK) == 0) {
-        // try raw device first
-        if (DoMountDevice(mp->device, mp->mountPoint) == 0 ||
-            MountPartition(mp->device, mp->mountPoint) == 0)
-        {
-            SetState(mp, kMounted);
-        }
-        else 
-        {
-            SetState(mp, kMounting);
-            mp->retryCount = 0;
-            SetRetries(mp, kMounting);
-        }
-    }
-}
-
-// Force the kernel to drop all caches.
-static void DropSystemCaches(void)
-{
-    int fd;
-
-    LOG_MOUNT("Dropping system caches\n");
-    fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
-
-    if (fd > 0) {
-        char ch = 3;
-        int rc;
-
-        rc = write(fd, &ch, 1);
-        if (rc <= 0)
-            LOG_MOUNT("Error dropping caches (%d)\n", rc);
-        close(fd);
-    }
-}
-
-// attempt to unmount the specified mount point.
-// set up retry/timeout if it does not succeed at first.
-static void RequestUnmount(MountPoint* mp, MountState retryState)
-{
-    int result;
-
-    LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState);
-    
-    if (mp->state == kMounted)
-    {
-        SendUnmountRequest(mp->mountPoint);
-
-        // do this in case the user pulls the SD card before we can successfully unmount
-        sync();
-        DropSystemCaches();
-
-        if (DoUnmountDevice(mp) == 0) 
-        {
-            SetState(mp, kUnmounted);
-            if (retryState == kUnmountingForUms) 
-            {
-                SetBackingStore(mp, true);
-                NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
-            }
-        }
-        else 
-        {
-            LOG_MOUNT("unmount failed, set retry\n");
-            SetRetries(mp, retryState);
-        }
-    } 
-    else if (mp->state == kMounting)
-    {
-        SetState(mp, kUnmounted);
-    }
-}
-
-// returns true if the mount point should be shared via USB mass storage
-static boolean MassStorageEnabledForMountPoint(const MountPoint* mp)
-{
-    return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms);
-}
-
-// handles changes in gMassStorageEnabled and gMassStorageConnected
-static void MassStorageStateChanged()
-{
-    MountPoint* mp = sMountPointList;
-
-    boolean enable = (gMassStorageEnabled && gMassStorageConnected);
-    LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false"));
-    
-    while (mp)
-    {
-        if (mp->enableUms)
-        {
-            if (enable)
-            {
-                if (mp->state == kMounting)
-                    SetState(mp, kUnmounted);
-                if (mp->state == kUnmounted) 
-                {
-                    SetBackingStore(mp, true);
-                    NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
-                }
-                else
-                {
-                    LOG_MOUNT("MassStorageStateChanged requesting unmount\n");
-                    // need to successfully unmount first
-                    RequestUnmount(mp, kUnmountingForUms);
-                }
-            } else if (mp->umsActive) {
-                SetBackingStore(mp, false);
-                if (mp->state == kUnmountingForUms)
-                {
-                    ClearRetries(mp, kMounted);
-                    NotifyMediaState(mp->mountPoint, MEDIA_MOUNTED, false);
-                }
-                else if (mp->state == kUnmounted)
-                {
-                    NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
-                    RequestMount(mp);
-                }
-            }
-        }
-
-        mp = mp->next;
-    }
-}
-
-// called when USB mass storage connected state changes
-static void HandleMassStorageOnline(boolean connected)
-{
-    if (connected != gMassStorageConnected)
-    {
-        gMassStorageConnected = connected;
-        SendMassStorageConnected(connected);
-        
-        // we automatically reset to mass storage off after USB is connected
-        if (!connected)
-            gMassStorageEnabled = false;
-    
-        MassStorageStateChanged();
-    }
-}
-
-// called when a new block device has been created
-static void HandleMediaInserted(const char* device)
-{
-    MountPoint* mp = sMountPointList;
-    
-    LOG_MOUNT("HandleMediaInserted(%s):\n", device);
-
-    while (mp)
-    {
-        // see if the device matches mount point's block device
-        if (mp->state == kUnmounted &&
-                strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0) 
-        {
-            if (MassStorageEnabledForMountPoint(mp))
-            {
-                SetBackingStore(mp, true);
-                NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
-            }
-            else
-                RequestMount(mp);
-        }  
-        mp = mp->next;
-    }
-}
-
-// called when a new block device has been deleted
-static void HandleMediaRemoved(const char* device)
-{    
-    MountPoint* mp = sMountPointList;
-    while (mp)
-    {
-        if (strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
-        {
-            if (mp->enableUms)
-                SetBackingStore(mp, false);
-
-             if (mp->state == kMounted) 
-            {
-                RequestUnmount(mp, kUnmountingForEject);
-                NotifyMediaState(mp->mountPoint, MEDIA_BAD_REMOVAL, false);
-            }
-            
-            NotifyMediaState(mp->mountPoint, MEDIA_REMOVED, false);
-            break;
-        }  
-        mp = mp->next;
-    }
-}
-
-// Handle retrying to mount or unmount devices, 
-// and handle timeout condition if we have tried too many times
-static void HandleRetries()
-{
-    MountPoint* mp = sMountPointList;
-    
-    while (mp)
-    {
-       if (mp->state == kMounting) 
-       {
-            if (MountPartition(mp->device, mp->mountPoint) == 0)
-            {
-                // mount succeeded - clear the retry for this mount point
-                ClearRetries(mp, kMounted);
-            } 
-            else 
-            {
-                mp->retryCount++;
-                if (mp->retryCount == MAX_MOUNT_RETRIES)
-                {
-                    // we failed to mount the device too many times
-                    ClearRetries(mp, kUnmounted);
-                    // notify that we failed to mount
-                    NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTABLE, false);
-                }
-            }
-       } 
-       else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms)
-       {
-            if (DoUnmountDevice(mp) == 0)
-            {
-                // unmounting succeeded
-                // start mass storage, if state is kUnmountingForUms
-                if (mp->state == kUnmountingForUms)
-                {
-                    SetBackingStore(mp, true);
-                     NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
-                }
-                // clear the retry for this mount point
-                ClearRetries(mp, kUnmounted);
-            } 
-            else 
-            {
-                mp->retryCount++;
-                if (mp->retryCount >= MAX_UNMOUNT_RETRIES)
-                {
-                    // kill any processes that are preventing the device from unmounting
-                    // send SIGKILL instead of SIGTERM if the first attempt did not succeed
-                    boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES);
-                    
-                    int i;
-
-                    for (i = 0; i < ASEC_STORES_MAX; i++) {
-                        if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) {
-                            LOG_MOUNT("Killing processes for ASEC path '%s'\n",
-                                      AsecMountPoint(mp->asecHandles[i]));
-                            KillProcessesWithOpenFiles(AsecMountPoint(mp->asecHandles[i]),
-                                                       sigkill,
-                                                       gExcludedPids, sizeof(gExcludedPids) / sizeof(pid_t));
-
-                            // Now that we've killed the processes, try to stop the volume again
-                            AsecStop(mp->asecHandles[i]);
-                        }
-                    }
-
-                    // unmounting the device is failing, so start killing processes
-                    KillProcessesWithOpenFiles(mp->mountPoint, sigkill, gExcludedPids, 
-                                               sizeof(gExcludedPids) / sizeof(pid_t));
-
-                }
-            }
-       } 
-        
-        mp = mp->next;
-    }
-}
-
-/*****************************************************
- * 
- * AUTO-MOUNTER THREAD
- * 
- *****************************************************/
-
-static void sigusr1_handler(int signo)
-{
-    // don't need to do anything here
-}
-
-// create a socket for listening to inotify events
-int CreateINotifySocket()
-{
-    // initialize inotify
-    int fd = inotify_init();
-
-    if (fd < 0) {
-        LOG_ERROR("inotify_init failed, %s\n", strerror(errno));
-        return -1;
-    }
-
-    fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
-
-    return fd;
-}
-
-
-// create a socket for listening to uevents
-int CreateUEventSocket()
-{
-    struct sockaddr_nl addr;
-    int sz = 64*1024;
-    int fd;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.nl_family = AF_NETLINK;
-    addr.nl_pid = getpid();
-    addr.nl_groups = 0xffffffff;
-
-   fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
-    if(fd < 0)
-    {
-        LOG_ERROR("could not create NETLINK_KOBJECT_UEVENT socket\n");
-        return -1;
-    }
-
-    setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
-
-    if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        LOG_ERROR("could not bind NETLINK_KOBJECT_UEVENT socket\n");
-        close(fd);
-        return -1;
-    }
-
-    return fd;
-}
-
-/*
- * Automounter main event thread.
- * This thread listens for block devices being created and deleted via inotify,
- * and listens for changes in the USB mass storage connected/disconnected via uevents from the 
- * power supply driver.
- * This thread also handles retries and timeouts for requests to mount or unmount a device.
- */
-static void* AutoMountThread(void* arg)
-{
-    int inotify_fd;
-    int uevent_fd;
-    int id;
-    struct sigaction    actions;
-
-    gExcludedPids[1] = getpid();
-
-    memset(&actions, 0, sizeof(actions));
-    sigemptyset(&actions.sa_mask);
-    actions.sa_flags = 0;
-    actions.sa_handler = sigusr1_handler;
-    sigaction(SIGUSR1, &actions, NULL);
-    
-    // initialize inotify
-    inotify_fd = CreateINotifySocket();
-    // watch for files created and deleted in "/dev"
-    inotify_add_watch(inotify_fd, DEVPATH, IN_CREATE|IN_DELETE);
-
-    // initialize uevent watcher
-    uevent_fd = CreateUEventSocket();
-    if (uevent_fd < 0) 
-    {
-        LOG_ERROR("CreateUEventSocket failed, %s\n", strerror(errno));
-        return NULL;
-    }
-    
-    while (1)
-    {
-        struct pollfd fds[2];
-        int timeout, result;
-
-#define INOTIFY_IDX 0
-#define UEVENT_IDX  1
-    
-        fds[INOTIFY_IDX].fd = inotify_fd;
-        fds[INOTIFY_IDX].events = POLLIN;
-        fds[INOTIFY_IDX].revents = 0;
-        fds[UEVENT_IDX].fd = uevent_fd;
-        fds[UEVENT_IDX].events = POLLIN;
-        fds[UEVENT_IDX].revents = 0;
-        
-        // wait for an event or a timeout to occur.
-        // poll() can also return in response to a SIGUSR1 signal
-        timeout = (sRetriesPending ? POLL_TIMEOUT : -1);
-        result = poll(fds, 2, timeout);
-
-        // lock the mutex while we are handling events
-        pthread_mutex_lock(&sMutex);
-
-        // handle inotify notifications for block device creation and deletion
-        if (fds[INOTIFY_IDX].revents == POLLIN)
-        {
-            struct inotify_event    event;
-            char    buffer[512];
-            int length = read(inotify_fd, buffer, sizeof(buffer));
-            int offset = 0;
- 
-            while (length >= (int)sizeof(struct inotify_event))
-            {
-               struct inotify_event* event = (struct inotify_event *)&buffer[offset];
-               
-               if (event->mask == IN_CREATE)
-               {
-                   LOG_MOUNT("/dev/block/%s created\n", event->name);
-                   HandleMediaInserted(event->name);
-               }
-               else if (event->mask == IN_DELETE)
-               {
-                   LOG_MOUNT("/dev/block/%s deleted\n", event->name);
-                   HandleMediaRemoved(event->name);
-               }
-               
-               int size = sizeof(struct inotify_event) + event->len;
-               length -= size;
-               offset += size;
-            }
-        }
-
-        // handle uevent notifications for USB state changes
-        if (fds[UEVENT_IDX].revents == POLLIN)
-        {
-            char buffer[64*1024];
-            int count;
-            
-            count = recv(uevent_fd, buffer, sizeof(buffer), 0);
-            if (count > 0) {
-                char* s = buffer;
-                char* end = s + count;
-                char* type = NULL;
-                char* online = NULL;
-                char* switchName = NULL;
-                char* switchState = NULL;
-                                
-                while (s < end) {
-                    if (!strncmp("POWER_SUPPLY_TYPE=", s, strlen("POWER_SUPPLY_TYPE=")))
-                        type = s + strlen("POWER_SUPPLY_TYPE=");
-                    else if (!strncmp("POWER_SUPPLY_ONLINE=", s, strlen("POWER_SUPPLY_ONLINE=")))
-                        online = s + strlen("POWER_SUPPLY_ONLINE=");                    
-                    else if (!strncmp("SWITCH_NAME=", s, strlen("SWITCH_NAME=")))
-                        switchName = s + strlen("SWITCH_NAME=");                    
-                    else if (!strncmp("SWITCH_STATE=", s, strlen("SWITCH_STATE=")))
-                        switchState = s + strlen("SWITCH_STATE=");                    
-                    s += (strlen(s) + 1);
-                }
-
-                // we use the usb_mass_storage switch state to tell us when USB is online
-                if (switchName && switchState && 
-                        !strcmp(switchName, "usb_mass_storage") && !strcmp(switchState, "online"))
-                {
-                    LOG_MOUNT("USB online\n");
-                    HandleMassStorageOnline(true);
-                }
-                
-                // and we use the power supply state to tell us when USB is offline
-                // we can't rely on the switch for offline detection because we get false positives
-                // when USB is reenumerated by the host.
-                if (type && online && !strcmp(type, "USB") && !strcmp(online, "0"))
-                {
-                    LOG_MOUNT("USB offline\n");
-                    HandleMassStorageOnline(false);
-                }
-            }
-        }
-
-       // handle retries
-       if (sRetriesPending)
-            HandleRetries();
-
-        // done handling events, so unlock the mutex
-        pthread_mutex_unlock(&sMutex);
-    }
-
-    inotify_rm_watch(inotify_fd, id);
-    close(inotify_fd);
-    close(uevent_fd);
-
-    return NULL;
-}
-
-/*****************************************************
- * 
- * THESE FUNCTIONS ARE CALLED FROM THE SERVER THREAD
- * 
- *****************************************************/
-
-// Called to enable or disable USB mass storage support
-void EnableMassStorage(boolean enable)
-{
-    pthread_mutex_lock(&sMutex);
-
-    LOG_MOUNT("EnableMassStorage %s\n", (enable ? "true" : "false"));
-    gMassStorageEnabled = enable;
-    MassStorageStateChanged();
-    pthread_mutex_unlock(&sMutex);
- }
-
-// Called to request that the specified mount point be mounted
-void MountMedia(const char* mountPoint)
-{
-    MountPoint* mp = sMountPointList;
- 
-    LOG_MOUNT("MountMedia(%s)\n", mountPoint);
-   
-    pthread_mutex_lock(&sMutex);
-    while (mp)
-    {
-        if (strcmp(mp->mountPoint, mountPoint) == 0)
-        {
-            if (mp->state == kUnmountingForEject)
-            {
-                // handle the case where we try to remount before we actually unmounted
-                ClearRetries(mp, kMounted);
-            }
-            
-            // don't attempt to mount if mass storage is active
-            if (!MassStorageEnabledForMountPoint(mp))
-                RequestMount(mp);
-        }
-        
-        mp = mp->next;
-    }
-    pthread_mutex_unlock(&sMutex);
- }
-
-// Called to request that the specified mount point be unmounted
-void UnmountMedia(const char* mountPoint)
-{
-    MountPoint* mp = sMountPointList;
-    
-    pthread_mutex_lock(&sMutex);
-    while (mp)
-    {
-        if (strcmp(mp->mountPoint, mountPoint) == 0)
-            RequestUnmount(mp, kUnmountingForEject);
-        
-        mp = mp->next;
-    }
-    pthread_mutex_unlock(&sMutex);
-}
-
-boolean IsMassStorageEnabled()
-{
-    return gMassStorageEnabled;
-}
-
-boolean IsMassStorageConnected()
-{
-    return gMassStorageConnected;
-}
-
-/***********************************************
- * 
- * THESE FUNCTIONS ARE CALLED ONLY AT STARTUP
- * 
- ***********************************************/
- 
-void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms)
-{
-    MountPoint* newMountPoint;
-    
-    LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s driverStorePath: %s\n", device, mountPoint, driverStorePath);
-    // add a new MountPoint to the head of our linked list
-    newMountPoint = (MountPoint *)malloc(sizeof(MountPoint));
-    newMountPoint->device = device;
-    newMountPoint->mountPoint = mountPoint;
-    newMountPoint->driverStorePath = driverStorePath;
-    newMountPoint->enableUms = enableUms;
-    newMountPoint->umsActive = false;
-    newMountPoint->state = kUnmounted;
-    newMountPoint->retryCount = 0;
-
-    // add to linked list
-    newMountPoint->next = sMountPointList;
-    sMountPointList = newMountPoint;
-    return newMountPoint;
-}
-
-int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, const char *size,
-                        const char *mount_point, const char *crypt)
-{
-    MountPoint *mp = (MountPoint *) Mp;
-    int i;
-
-    for (i = 0; i < ASEC_STORES_MAX; i++) {
-        if (!mp->asecHandles[i])
-            break;   
-    }
-
-    if (i == ASEC_STORES_MAX) {
-        LOG_ERROR("Maximum # of ASEC stores exceeded\n");
-        return -EINVAL;
-    }
-
-    if (!(mp->asecHandles[i] = AsecInit(name, mp->mountPoint, backing_file, size, mount_point, crypt)))
-        return -1;
-
-    return 0;
-}
-static void MountDevices()
-{
-    MountPoint* mp = sMountPointList;
-    while (mp)
-    {
-        RequestMount(mp);
-        mp = mp->next;
-    }
-}
-
-void StartAutoMounter()
-{
-    gExcludedPids[0] = getpid();
-
-    gMassStorageConnected = ReadMassStorageState();
-    LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n");
-
-    MountDevices();
-    pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL);
-}
diff --git a/mountd/NOTICE b/mountd/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/mountd/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2005-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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/mountd/ProcessKiller.c b/mountd/ProcessKiller.c
deleted file mode 100644
index e377774..0000000
--- a/mountd/ProcessKiller.c
+++ /dev/null
@@ -1,222 +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.
- */
-
-/*
-** mountd process killer
-*/
-
-#include "mountd.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <stdlib.h>
-#include <poll.h>
-#include <sys/stat.h>
-
-
-static boolean ReadSymLink(const char* path, char* link)
-{
-    struct stat s;
-    int length;
-
-    if (lstat(path, &s) < 0)
-        return false;
-    if ((s.st_mode & S_IFMT) != S_IFLNK)
-        return false;
-   
-    // we have a symlink    
-    length = readlink(path, link, PATH_MAX - 1);
-    if (length <= 0) 
-        return false;
-    link[length] = 0;
-    return true;
-}
-
-static boolean PathMatchesMountPoint(const char* path, const char* mountPoint)
-{
-    int length = strlen(mountPoint);
-    if (length > 1 && strncmp(path, mountPoint, length) == 0)
-    {
-        // we need to do extra checking if mountPoint does not end in a '/'
-        if (mountPoint[length - 1] == '/')
-            return true;
-        // if mountPoint does not have a trailing slash, we need to make sure
-        // there is one in the path to avoid partial matches.
-        return (path[length] == 0 || path[length] == '/');
-    }
-    
-    return false;
-}
-
-static void GetProcessName(int pid, char buffer[PATH_MAX])
-{
-    int fd;
-    sprintf(buffer, "/proc/%d/cmdline", pid);
-    fd = open(buffer, O_RDONLY);
-    if (fd < 0) {
-        strcpy(buffer, "???");
-    } else {
-        int length = read(fd, buffer, PATH_MAX - 1);
-        buffer[length] = 0;
-        close(fd);
-    }
-}
-
-static boolean CheckFileDescriptorSymLinks(int pid, const char* mountPoint)
-{
-    DIR*    dir;
-    struct dirent* de;
-    boolean fileOpen = false;
-    char    path[PATH_MAX];
-    char    link[PATH_MAX];
-    int     parent_length;
-
-    // compute path to process's directory of open files
-    sprintf(path, "/proc/%d/fd", pid);
-    dir = opendir(path);
-    if (!dir)
-        return false;
-
-    // remember length of the path
-    parent_length = strlen(path);
-    // append a trailing '/'
-    path[parent_length++] = '/';
-    
-    while ((de = readdir(dir)) != 0 && !fileOpen) {
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-            continue;
-        
-        // append the file name, after truncating to parent directory
-        path[parent_length] = 0;
-        strcat(path, de->d_name);
-
-        if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
-        {
-            char    name[PATH_MAX];
-            GetProcessName(pid, name);
-            LOG_ERROR("process %s (%d) has open file %s\n", name, pid, link);
-            fileOpen = true;
-        }
-    }
-
-    closedir(dir);
-    return fileOpen;
-}
-
-static boolean CheckFileMaps(int pid, const char* mountPoint)
-{
-    FILE*   file;
-    char    buffer[PATH_MAX + 100];
-    boolean mapOpen = false;
-
-    sprintf(buffer, "/proc/%d/maps", pid);
-    file = fopen(buffer, "r");
-    if (!file)
-        return false;
-    
-    while (!mapOpen && fgets(buffer, sizeof(buffer), file))
-    {
-        // skip to the path
-        const char* path = strchr(buffer, '/');
-        if (path && PathMatchesMountPoint(path, mountPoint))
-        {
-            char    name[PATH_MAX];
-            GetProcessName(pid, name);
-            LOG_ERROR("process %s (%d) has open file map for %s\n", name, pid, path);
-            mapOpen = true;
-        }
-    }
-    
-    fclose(file);
-    return mapOpen;
-}
-
-static boolean CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message)
-{
-    char    path[PATH_MAX];
-    char    link[PATH_MAX];
-
-    sprintf(path, "/proc/%d/%s", pid, name);
-    if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) 
-    {
-        char    name[PATH_MAX];
-        GetProcessName(pid, name);
-        LOG_ERROR("process %s (%d) has %s in %s\n", name, pid, message, mountPoint);
-        return true;
-    }
-    else
-        return false;
-}
-
-static int get_pid(const char* s)
-{
-    int result = 0;
-    while (*s) {
-        if (!isdigit(*s)) return -1;
-        result = 10 * result + (*s++ - '0');
-    }
-    return result;
-}
-
-// hunt down and kill processes that have files open on the given mount point
-void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded)
-{
-    DIR*    dir;
-    struct dirent* de;
-
-    LOG_ERROR("KillProcessesWithOpenFiles %s\n", mountPoint);
-    dir = opendir("/proc");
-    if (!dir) return;
-
-    while ((de = readdir(dir)) != 0)
-    {
-        boolean killed = false;
-        // does the name look like a process ID?
-        int pid = get_pid(de->d_name);
-        if (pid == -1) continue;
-
-        if (CheckFileDescriptorSymLinks(pid, mountPoint)    // check for open files
-                || CheckFileMaps(pid, mountPoint)           // check for mmap()
-                || CheckSymLink(pid, mountPoint, "cwd", "working directory")    // check working directory
-                || CheckSymLink(pid, mountPoint, "root", "chroot")              // check for chroot()
-                || CheckSymLink(pid, mountPoint, "exe", "executable path")      // check executable path
-            ) 
-        {
-            int i;
-            boolean hit = false;
-
-            for (i = 0; i < num_excluded; i++) {
-                if (pid == excluded[i]) {
-                    LOG_ERROR("I just need a little more TIME captain!\n");
-                    hit = true;
-                    break;
-                }
-            }
-
-            if (!hit) {
-                LOG_ERROR("Killing process %d\n", pid);
-                kill(pid, (sigkill ? SIGKILL : SIGTERM));
-            }
-        }
-    }
-
-    closedir(dir);
-}        
diff --git a/mountd/Server.c b/mountd/Server.c
deleted file mode 100644
index 64459bd..0000000
--- a/mountd/Server.c
+++ /dev/null
@@ -1,313 +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.
- */
-
-/*
-**	mountd server support
-*/
-
-#include "mountd.h"
-#include "ASEC.h"
-
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
-
-#include <pthread.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/socket.h>
-
-#include <private/android_filesystem_config.h>
-
-
-// current client file descriptor
-static int sFD = -1;
-
-// to synchronize writing to client
-static pthread_mutex_t sWriteMutex = PTHREAD_MUTEX_INITIALIZER;
-
-// path for media that failed to mount before the runtime is connected
-static char* sDeferredUnmountableMediaPath = NULL;
-
-// last asec msg before the runtime was connected
-static char* sAsecDeferredMessage = NULL;
-static char* sAsecDeferredArgument = NULL;
-
-static int Write(const char* message)
-{
-    int result = -1;
-
-    pthread_mutex_lock(&sWriteMutex);
-    
-    LOG_SERVER("Write: %s\n", message);
-    if (sFD >= 0)
-        result = write(sFD, message, strlen(message) + 1);
-
-    pthread_mutex_unlock(&sWriteMutex); 
-    
-    return result;
-}
-
-static int Write2(const char* message, const char* data)
-{
-    int result = -1;
-
-    char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1);
-    if (!buffer)
-    {
-        LOG_ERROR("alloca failed in Write2\n");
-        return -1;
-    }
-
-    strcpy(buffer, message);
-    strcat(buffer, data);
-    return Write(buffer);
-}
-
-static void SendStatus()
-{
-    Write(IsMassStorageConnected() ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED);
-    Write(IsMassStorageEnabled() ? MOUNTD_UMS_ENABLED : MOUNTD_UMS_DISABLED);
-}
-
-static void DoCommand(const char* command)
-{
-    LOG_SERVER("DoCommand %s\n", command);
-    
-    if (strcmp(command, MOUNTD_ENABLE_UMS) == 0)
-    {
-        EnableMassStorage(true);
-        Write(MOUNTD_UMS_ENABLED);
-     }
-    else if (strcmp(command, MOUNTD_DISABLE_UMS) == 0) 
-    {
-        EnableMassStorage(false);
-        Write(MOUNTD_UMS_DISABLED);
-    }
-    else if (strcmp(command, MOUNTD_SEND_STATUS) == 0)
-    {
-        SendStatus();
-    }
-    else if (strncmp(command, MOUNTD_MOUNT_MEDIA, strlen(MOUNTD_MOUNT_MEDIA)) == 0)
-    {
-        const char* path = command + strlen(MOUNTD_MOUNT_MEDIA);
-        MountMedia(path);
-    }
-    else if (strncmp(command, MOUNTD_EJECT_MEDIA, strlen(MOUNTD_EJECT_MEDIA)) == 0)
-    {
-        const char* path = command + strlen(MOUNTD_EJECT_MEDIA);
-        UnmountMedia(path);
-    } 
-    else if (strncmp(command, ASEC_CMD_ENABLE, strlen(ASEC_CMD_ENABLE)) == 0) {
-        LOG_ASEC("Got ASEC_CMD_ENABLE\n");
-	// XXX: SAN: Impliment
-    }
-    else if (strncmp(command, ASEC_CMD_DISABLE, strlen(ASEC_CMD_DISABLE)) == 0) {
-        LOG_ASEC("Got ASEC_CMD_DISABLE\n");
-	// XXX: SAN: Impliment
-    }
-    else if (strncmp(command, ASEC_CMD_SEND_STATUS, strlen(ASEC_CMD_SEND_STATUS)) == 0) {
-        LOG_ASEC("Got ASEC_CMD_SEND_STATUS\n");
-	// XXX: SAN: Impliment
-    }
-    else
-        LOGE("unknown command %s\n", command);
-}
-
-int RunServer()
-{
-    int socket = android_get_control_socket(MOUNTD_SOCKET);
-    if (socket < 0) {
-        LOGE("Obtaining file descriptor for socket '%s' failed: %s",
-             MOUNTD_SOCKET, strerror(errno));
-        return -1;
-    }
-
-    if (listen(socket, 4) < 0) {
-        LOGE("Unable to listen on file descriptor '%d' for socket '%s': %s",
-             socket, MOUNTD_SOCKET, strerror(errno));
-        return -1;
-    }
-
-    while (1)
-    {
-        struct sockaddr addr;
-        socklen_t alen;
-        struct ucred cred;
-        socklen_t size;
-        
-        alen = sizeof(addr);
-        sFD = accept(socket, &addr, &alen);
-        if (sFD < 0)
-            continue;
-            
-        if (sDeferredUnmountableMediaPath) {
-            NotifyMediaState(sDeferredUnmountableMediaPath, MEDIA_UNMOUNTABLE, false);
-            free(sDeferredUnmountableMediaPath);
-            sDeferredUnmountableMediaPath = NULL;
-        }
-
-        if (sAsecDeferredMessage) {
-    
-            if (Write2(sAsecDeferredMessage, sAsecDeferredArgument) < 0)
-                LOG_ERROR("Failed to deliver deferred ASEC msg to framework\n");
-            free(sAsecDeferredMessage);
-            free(sAsecDeferredArgument);
-            sAsecDeferredMessage = sAsecDeferredArgument = NULL;
-        }
-
-        while (1)
-        {    
-            char    buffer[101];
-            int result = read(sFD, buffer, sizeof(buffer) - 1);
-            if (result > 0)
-            {
-                int start = 0;
-                int i;
-                // command should be zero terminated, but just in case
-                buffer[result] = 0;
-                for (i = 0; i < result; i++) 
-                {
-                    if (buffer[i] == 0) 
-                    {
-                        DoCommand(buffer + start);
-                        start = i + 1;
-                    }                   
-                }
-            }
-            else
-            {
-                close(sFD);
-                sFD = -1;
-                break;
-            }
-        }
-    }  
-
-    // should never get here
-    return 0;
-}
-
-void SendMassStorageConnected(boolean connected)
-{
-    Write(connected ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED);
-}
-
-void SendUnmountRequest(const char* path)
-{
-    Write2(MOUNTD_REQUEST_EJECT, path);
-}
-
-void NotifyAsecState(AsecState state, const char *argument)
-{
-    const char *event = NULL;
-    const char *status = NULL;
-    boolean deferr = true;;
-
-    switch (state) {
-        case ASEC_DISABLED:
-            event = ASEC_EVENT_DISABLED;
-            status = ASEC_STATUS_DISABLED;
-            break;
-        case ASEC_AVAILABLE:
-            event = ASEC_EVENT_AVAILABLE;
-            status = ASEC_STATUS_AVAILABLE;
-            break;
-        case ASEC_BUSY:
-            event = ASEC_EVENT_BUSY;
-            status = ASEC_STATUS_BUSY;
-            deferr = false;
-            break;
-        case ASEC_FAILED_INTERR:
-            event = ASEC_EVENT_FAILED_INTERR;
-            status = ASEC_STATUS_FAILED_INTERR;
-            break;
-        case ASEC_FAILED_NOMEDIA:
-            event = ASEC_EVENT_FAILED_NOMEDIA;
-            status = ASEC_STATUS_FAILED_NOMEDIA;
-            break;
-        case ASEC_FAILED_BADMEDIA:
-            event = ASEC_EVENT_FAILED_BADMEDIA;
-            status = ASEC_STATUS_FAILED_BADMEDIA;
-            break;
-        case ASEC_FAILED_BADKEY:
-            event = ASEC_EVENT_FAILED_BADKEY;
-            status = ASEC_STATUS_FAILED_BADKEY;
-            break;
-        default:
-            LOG_ERROR("unknown AsecState %d in NotifyAsecState\n", state);
-            return;
-    }
-
-    property_set(ASEC_STATUS, status);
-
-    int result = Write2(event, argument);
-    if ((result < 0) && deferr) {
-        if (sAsecDeferredMessage) 
-            free(sAsecDeferredMessage);
-        sAsecDeferredMessage = strdup(event);
-        if (sAsecDeferredArgument)
-            free(sAsecDeferredArgument);
-        sAsecDeferredArgument = strdup(argument);
-        LOG_ASEC("Deferring event '%s' arg '%s' until framework connects\n", event, argument);
-    }
-}
-
-void NotifyMediaState(const char* path, MediaState state, boolean readOnly)
-{
-    const char* event = NULL;
-    const char* propertyValue = NULL;
-    
-    switch (state) {
-        case MEDIA_REMOVED:
-            event = MOUNTD_MEDIA_REMOVED;
-            propertyValue = EXTERNAL_STORAGE_REMOVED;
-            break;
-        case MEDIA_UNMOUNTED:
-            event = MOUNTD_MEDIA_UNMOUNTED;
-            propertyValue = EXTERNAL_STORAGE_UNMOUNTED;
-            break;
-        case MEDIA_MOUNTED:
-            event = (readOnly ? MOUNTD_MEDIA_MOUNTED_READ_ONLY : MOUNTD_MEDIA_MOUNTED);
-             propertyValue = (readOnly ? EXTERNAL_STORAGE_MOUNTED_READ_ONLY : EXTERNAL_STORAGE_MOUNTED);
-           break;
-        case MEDIA_SHARED:
-            event = MOUNTD_MEDIA_SHARED;
-            propertyValue = EXTERNAL_STORAGE_SHARED;
-            break;
-        case MEDIA_BAD_REMOVAL:
-            event = MOUNTD_MEDIA_BAD_REMOVAL;
-            propertyValue = EXTERNAL_STORAGE_BAD_REMOVAL;
-            break;
-        case MEDIA_UNMOUNTABLE:
-            event = MOUNTD_MEDIA_UNMOUNTABLE;
-            propertyValue = EXTERNAL_STORAGE_UNMOUNTABLE;
-            break;
-        default:
-            LOG_ERROR("unknown MediaState %d in NotifyMediaState\n", state);
-            return;
-    }
-    
-    property_set(EXTERNAL_STORAGE_STATE, propertyValue);
-    int result = Write2(event, path);
-    if (result < 0 && state == MEDIA_UNMOUNTABLE) {
-    
-        // if we cannot communicate with the runtime, defer this message until the runtime is available
-        sDeferredUnmountableMediaPath = strdup(path);
-    }
-}
diff --git a/mountd/logwrapper.c b/mountd/logwrapper.c
deleted file mode 100644
index 69606ab..0000000
--- a/mountd/logwrapper.c
+++ /dev/null
@@ -1,154 +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.
- */
-
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "private/android_filesystem_config.h"
-#include "cutils/log.h"
-
-int parent(const char *tag, int parent_read) {
-    int status;
-    char buffer[4096];
-
-    int a = 0;  // start index of unprocessed data
-    int b = 0;  // end index of unprocessed data
-    int sz;
-    while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
-
-        sz += b;
-        // Log one line at a time
-        for (b = 0; b < sz; b++) {
-            if (buffer[b] == '\r') {
-                buffer[b] = '\0';
-            } else if (buffer[b] == '\n') {
-                buffer[b] = '\0';
-                LOG(LOG_INFO, tag, &buffer[a]);
-                a = b + 1;
-            }
-        }
-
-        if (a == 0 && b == sizeof(buffer) - 1) {
-            // buffer is full, flush
-            buffer[b] = '\0';
-            LOG(LOG_INFO, tag, &buffer[a]);
-            b = 0;
-        } else if (a != b) {
-            // Keep left-overs
-            b -= a;
-            memmove(buffer, &buffer[a], b);
-            a = 0;
-        } else {
-            a = 0;
-            b = 0;
-        }
-
-    }
-    // Flush remaining data
-    if (a != b) {
-        buffer[b] = '\0';
-        LOG(LOG_INFO, tag, &buffer[a]);
-    }
-    status = 0xAAAA;
-    if (wait(&status) != -1) {  // Wait for child
-        if (WIFEXITED(status)) {
-            LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
-                    WEXITSTATUS(status));
-            return WEXITSTATUS(status);
-        } else if (WIFSIGNALED(status))
-            LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
-                    WTERMSIG(status));
-        else if (WIFSTOPPED(status))
-            LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
-                    WSTOPSIG(status));
-    } else
-        LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
-                strerror(errno), errno);
-    return -EAGAIN;
-}
-
-void child(int argc, char* argv[]) {
-    // create null terminated argv_child array
-    char* argv_child[argc + 1];
-    memcpy(argv_child, argv, argc * sizeof(char *));
-    argv_child[argc] = NULL;
-
-    // XXX: PROTECT FROM VIKING KILLER
-    if (execvp(argv_child[0], argv_child)) {
-        LOG(LOG_ERROR, "logwrapper",
-            "executing %s failed: %s\n", argv_child[0], strerror(errno));
-        exit(-1);
-    }
-}
-
-int logwrap(int argc, char* argv[])
-{
-    pid_t pid;
-
-    int parent_ptty;
-    int child_ptty;
-    char *child_devname = NULL;
-
-    /* Use ptty instead of socketpair so that STDOUT is not buffered */
-    parent_ptty = open("/dev/ptmx", O_RDWR);
-    if (parent_ptty < 0) {
-	LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty\n");
-	return -errno;
-    }
-
-    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
-            ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
-	LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx\n");
-	return -1;
-    }
-
-    pid = fork();
-    if (pid < 0) {
-	LOG(LOG_ERROR, "logwrapper", "Failed to fork\n");
-        return -errno;
-    } else if (pid == 0) {
-        child_ptty = open(child_devname, O_RDWR);
-        if (child_ptty < 0) {
-	    LOG(LOG_ERROR, "logwrapper", "Problem with child ptty\n");
-            return -errno;
-        }
-
-        // redirect stdout and stderr
-        close(parent_ptty);
-        dup2(child_ptty, 1);
-        dup2(child_ptty, 2);
-        close(child_ptty);
-
-        child(argc, argv);
-    } else {
-        // switch user and group to "log"
-        // this may fail if we are not root, 
-        // but in that case switching user/group is unnecessary 
-        
-      //  setgid(AID_LOG);
-      //  setuid(AID_LOG);
-
-        return parent(argv[0], parent_ptty);
-    }
-
-    return 0;
-}
diff --git a/mountd/mountd.c b/mountd/mountd.c
deleted file mode 100644
index 27ec8de..0000000
--- a/mountd/mountd.c
+++ /dev/null
@@ -1,174 +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.
- */
-
-/*
-**    mountd main program
-*/
-
-#include "mountd.h"
-
-#include <cutils/config_utils.h>
-#include <cutils/cpu_info.h>
-#include <cutils/properties.h>
-
-#include <sys/mount.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <linux/capability.h>
-#include <linux/prctl.h>
-
-#include <private/android_filesystem_config.h>
-
-#ifdef MOUNTD_LOG
-FILE*    logFile;
-#endif
-
-struct asec_cfg {
-    const char *name;
-    const char *backing_file;
-    const char *size;
-    const char *mount_point;
-    const char *crypt;
-};
-
-static int ProcessAsecData(cnode *node, struct asec_cfg *stores, int idx)
-{
-    cnode *child = node->first_child;
-    const char *name = NULL;
-    const char *file = NULL;
-    const char *size = NULL;
-    const char *mp = NULL;
-    const char *crypt = NULL;
-
-    LOG_ASEC("ProcessAsecData(%s, %p, %d)\n", node->name, stores, idx);
-
-    while (child) {
-        if (!strcmp(child->name, "name"))
-            name = child->value;
-        else if (!strcmp(child->name, "backing_file"))
-            file = child->value;
-        else if (!strcmp(child->name, "size"))
-            size = child->value;
-        else if (!strcmp(child->name, "mount_point"))
-            mp = child->value;
-        else if (!strcmp(child->name, "crypt"))
-            crypt = child->value;
-        child = child->next;
-    }
-
-    if (!name || !file || !size || !mp || !crypt) {
-        LOG_ERROR("Missing required token from config. Skipping ASEC volume\n");
-        return -1;
-    } else if (idx == ASEC_STORES_MAX) {
-        LOG_ERROR("Maximum # of ASEC stores already defined\n");
-        return -1;
-    }
-
-    stores[idx].name = name;
-    stores[idx].backing_file = file;
-    stores[idx].size = size;
-    stores[idx].mount_point = mp;
-    stores[idx].crypt = crypt;
-    return ++idx;
-}
-
-static void ReadConfigFile(const char* path)
-{
-    cnode* root = config_node("", "");
-    cnode* node;
-
-    config_load_file(root, path);
-    node = root->first_child;
-
-    while (node)
-    {
-        if (strcmp(node->name, "mount") == 0)
-        {
-            const char* block_device = NULL;
-            const char* mount_point = NULL;
-            const char* driver_store_path = NULL;
-            boolean enable_ums = false;
-            cnode* child = node->first_child;
-            struct asec_cfg asec_stores[ASEC_STORES_MAX];
-            int    asec_idx = 0;
-
-            memset(asec_stores, 0, sizeof(asec_stores));
-
-            while (child)
-            {
-                const char* name = child->name;
-                const char* value = child->value;
-
-                if (!strncmp(name, "asec_", 5)) {
-                     int rc = ProcessAsecData(child, asec_stores, asec_idx);
-                     if (rc < 0) {
-                         LOG_ERROR("Error processing ASEC cfg data\n");
-                     } else
-                         asec_idx = rc;
-                } else if (strcmp(name, "block_device") == 0)
-                    block_device = value;
-                else if (strcmp(name, "mount_point") == 0)
-                    mount_point = value;
-                else if (strcmp(name, "driver_store_path") == 0)
-                    driver_store_path = value;
-                else if (strcmp(name, "enable_ums") == 0 &&
-                        strcmp(value, "true") == 0)
-                    enable_ums = true;
-                
-                child = child->next;
-            }
-
-            // mount point and removable fields are optional
-            if (block_device && mount_point)
-            {
-                void *mp = AddMountPoint(block_device, mount_point, driver_store_path, enable_ums);
-                int i;
-
-                for (i = 0; i < asec_idx; i++) {
-                    AddAsecToMountPoint(mp, asec_stores[i].name, asec_stores[i].backing_file,
-                                        asec_stores[i].size, asec_stores[i].mount_point,
-                                        asec_stores[i].crypt);
-                }
-            }
-        }
-            
-        node = node->next;
-    }
-}
-
-int main(int argc, char* argv[])
-{
-    const char*     configPath = "/system/etc/mountd.conf";
-    int             i;    
-
-    for (i = 1; i < argc; i++)
-    {
-        const char* arg = argv[i];
-        
-        if (strcmp(arg, "-f") == 0)
-        {
-            if (i < argc - 1)
-                configPath = argv[++i];
-        }
-    }
-        
-    ReadConfigFile(configPath);
-    StartAutoMounter();
-    return RunServer();
-}
diff --git a/mountd/mountd.h b/mountd/mountd.h
deleted file mode 100644
index c4bc91d..0000000
--- a/mountd/mountd.h
+++ /dev/null
@@ -1,190 +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.
- */
-
-#ifndef MOUNTD_H__
-#define MOUNTD_H__
-
-#define LOG_TAG "mountd"
-#include "cutils/log.h"
-
-#include "ASEC.h"
-
-typedef int boolean;
-enum {
-    false = 0,
-    true = 1
-};
-
-#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
-
-// Set this for logging error messages
-#define ENABLE_LOG_ERROR
-
-// set this to log automounter events
-#define ENABLE_LOG_MOUNT
-
-// set this to log server events
-//#define ENABLE_LOG_SERVER
-
-// set this to log ASEC events
-#define ENABLE_LOG_ASEC
-
-#ifdef ENABLE_LOG_ERROR
-#define LOG_ERROR(fmt, args...) \
-    { LOGE(fmt , ## args); }
-#else
-#define LOG_ERROR(fmt, args...) \
-    do { } while (0)
-#endif /* ENABLE_LOG_ERROR */
-
-#ifdef ENABLE_LOG_MOUNT
-#define LOG_MOUNT(fmt, args...) \
-    { LOGD(fmt , ## args); }
-#else
-#define LOG_MOUNT(fmt, args...) \
-    do { } while (0)
-#endif /* ENABLE_LOG_MOUNT */
-
-#ifdef ENABLE_LOG_SERVER
-#define LOG_SERVER(fmt, args...) \
-    { LOGD(fmt , ## args); }
-#else
-#define LOG_SERVER(fmt, args...) \
-    do { } while (0)
-#endif /* ENABLE_LOG_SERVER */
-
-#ifdef ENABLE_LOG_ASEC
-#define LOG_ASEC(fmt, args...) \
-    { LOGD(fmt , ## args); }
-#else
-#define LOG_ASEC(fmt, args...) \
-    do { } while (0)
-#endif /* ENABLE_LOG_ASEC */
-
-
-typedef enum MediaState {
-    // no media in SD card slot
-    MEDIA_REMOVED,
-    
-    // media in SD card slot, but not mounted
-    MEDIA_UNMOUNTED,
-    
-    // media in SD card slot and mounted at its mount point
-    MEDIA_MOUNTED,
-    
-    // media in SD card slot, unmounted, and shared as a mass storage device
-    MEDIA_SHARED,
-    
-    // media was removed from SD card slot, but mount point was not unmounted
-    // this state is cleared after the mount point is unmounted
-    MEDIA_BAD_REMOVAL,
-
-    // media in SD card slot could not be mounted (corrupt file system?)
-    MEDIA_UNMOUNTABLE,
-} MediaState;
-
-// socket name for connecting to mountd
-#define MOUNTD_SOCKET         "mountd"
-
-// mountd commands
-// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java
-#define MOUNTD_ENABLE_UMS     "enable_ums"
-#define MOUNTD_DISABLE_UMS    "disable_ums"
-#define MOUNTD_SEND_STATUS    "send_status"
-
-// these commands should contain a mount point following the colon
-#define MOUNTD_MOUNT_MEDIA  "mount_media:"
-#define MOUNTD_EJECT_MEDIA  "eject_media:"
-
-// mountd events
-// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java
-#define MOUNTD_UMS_ENABLED              "ums_enabled"
-#define MOUNTD_UMS_DISABLED             "ums_disabled"
-#define MOUNTD_UMS_CONNECTED            "ums_connected"
-#define MOUNTD_UMS_DISCONNECTED         "ums_disconnected"
-
-// these events correspond to the states in the MediaState enum.
-// a path to the mount point follows the colon.
-#define MOUNTD_MEDIA_REMOVED            "media_removed:"
-#define MOUNTD_MEDIA_UNMOUNTED        	"media_unmounted:"
-#define MOUNTD_MEDIA_MOUNTED          	"media_mounted:"
-#define MOUNTD_MEDIA_MOUNTED_READ_ONLY  "media_mounted_ro:"
-#define MOUNTD_MEDIA_SHARED             "media_shared:"
-#define MOUNTD_MEDIA_BAD_REMOVAL        "media_bad_removal:"
-#define MOUNTD_MEDIA_UNMOUNTABLE        "media_unmountable:"
-
-// this event sent to request unmount for media mount point
-#define MOUNTD_REQUEST_EJECT            "request_eject:"
-
-// system properties
-// these must match the corresponding strings in //device/java/android/android/os/Environment.java
-#define EXTERNAL_STORAGE_STATE          "EXTERNAL_STORAGE_STATE"
-#define EXTERNAL_STORAGE_REMOVED        "removed"
-#define EXTERNAL_STORAGE_UNMOUNTED      "unmounted"
-#define EXTERNAL_STORAGE_MOUNTED        "mounted"
-#define EXTERNAL_STORAGE_MOUNTED_READ_ONLY        "mounted_ro"
-#define EXTERNAL_STORAGE_SHARED         "shared"
-#define EXTERNAL_STORAGE_BAD_REMOVAL    "bad_removal"
-#define EXTERNAL_STORAGE_UNMOUNTABLE    "unmountable"
-
-// AutoMount.c
-
-boolean IsMassStorageEnabled();
-boolean IsMassStorageConnected();
-
-void MountMedia(const char* mountPoint);
-void UnmountMedia(const char* mountPoint);
-void EnableMassStorage(boolean enable);
-
-// call this before StartAutoMounter() to add a mount point to monitor
-void *AddMountPoint(const char* device, const char* mountPoint, const char* driverStorePath,
-                    boolean enableUms);
-
-int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file,
-                        const char *size, const char *mount_point, const char *crypt);
-
-// start automounter thread
-void StartAutoMounter();
-
-// check /proc/mounts for mounted file systems, and notify mount or unmount for any that are in our automount list
-void NotifyExistingMounts();
-
-
-// ASEC.c
-
-void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile,
-               const char *Size, const char *DstPath, const char *Crypt);
-int AsecStart(void *Handle);
-int AsecStop(void *Handle);
-void AsecDeinit(void *Handle);
-boolean AsecIsStarted(void *Handle);
-const char *AsecMountPoint(void *Handle);
-
-// ProcessKiller.c
-
-void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, pid_t *excluded, int num_excluded);
-
-// logwrapper.c
-int logwrap(int argc, char* argv[]);
-
-// Server.c
-
-int RunServer();
-void SendMassStorageConnected(boolean connected); 
-void SendUnmountRequest(const char* path);
-void NotifyMediaState(const char* path, MediaState state, boolean readOnly);
-void NotifyAsecState(AsecState state, const char *argument);
-#endif // MOUNTD_H__
diff --git a/nexus/Android.mk b/nexus/Android.mk
new file mode 100644
index 0000000..cd477ef
--- /dev/null
+++ b/nexus/Android.mk
@@ -0,0 +1,64 @@
+BUILD_NEXUS := false
+ifeq ($(BUILD_NEXUS),true)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                                      \
+                  main.cpp                             \
+                  NetworkManager.cpp                   \
+                  CommandListener.cpp                  \
+                  Controller.cpp                       \
+                  WifiController.cpp                   \
+                  LoopController.cpp                   \
+                  NexusCommand.cpp                     \
+                  TiwlanWifiController.cpp             \
+                  Supplicant.cpp                       \
+                  SupplicantEvent.cpp                  \
+                  SupplicantListener.cpp               \
+                  VpnController.cpp                    \
+                  ScanResult.cpp                       \
+                  WifiScanner.cpp                      \
+                  WifiNetwork.cpp                      \
+                  OpenVpnController.cpp                \
+                  InterfaceConfig.cpp                  \
+                  PropertyManager.cpp                  \
+                  SupplicantState.cpp                  \
+                  SupplicantEventFactory.cpp           \
+                  SupplicantConnectedEvent.cpp         \
+                  SupplicantAssociatingEvent.cpp       \
+                  SupplicantAssociatedEvent.cpp        \
+                  SupplicantStateChangeEvent.cpp       \
+                  SupplicantScanResultsEvent.cpp       \
+                  SupplicantConnectionTimeoutEvent.cpp \
+                  SupplicantDisconnectedEvent.cpp      \
+                  SupplicantStatus.cpp                 \
+                  TiwlanEventListener.cpp              \
+                  DhcpClient.cpp DhcpListener.cpp      \
+
+LOCAL_MODULE:= nexus
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS) -I../../../frameworks/base/include/
+
+LOCAL_CFLAGS := 
+
+LOCAL_SHARED_LIBRARIES := libsysutils libwpa_client
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:=          \
+                  nexctl.c \
+
+LOCAL_MODULE:= nexctl
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+
+LOCAL_CFLAGS := 
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
+
+endif # ifeq ($(BUILD_NEXUS),true)
diff --git a/nexus/CommandListener.cpp b/nexus/CommandListener.cpp
new file mode 100644
index 0000000..8eb378b
--- /dev/null
+++ b/nexus/CommandListener.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#define LOG_TAG "CommandListener"
+#include <cutils/log.h>
+
+#include <sysutils/SocketClient.h>
+
+#include "CommandListener.h"
+#include "Controller.h"
+#include "Property.h"
+#include "NetworkManager.h"
+#include "WifiController.h"
+#include "VpnController.h"
+#include "ErrorCode.h"
+
+CommandListener::CommandListener() :
+                 FrameworkListener("nexus") {
+    registerCmd(new WifiScanResultsCmd());
+    registerCmd(new WifiListNetworksCmd());
+    registerCmd(new WifiCreateNetworkCmd());
+    registerCmd(new WifiRemoveNetworkCmd());
+
+    registerCmd(new GetCmd());
+    registerCmd(new SetCmd());
+    registerCmd(new ListCmd());
+}
+
+/* -------------
+ * Wifi Commands
+ * ------------ */
+
+CommandListener::WifiCreateNetworkCmd::WifiCreateNetworkCmd() :
+                 NexusCommand("wifi_create_network") {
+}
+
+int CommandListener::WifiCreateNetworkCmd::runCommand(SocketClient *cli,
+                                                      int argc, char **argv) {
+    NetworkManager *nm = NetworkManager::Instance();
+    WifiController *wc = (WifiController *) nm->findController("WIFI");
+    WifiNetwork *wn;
+
+    if (!(wn = wc->createNetwork()))
+        cli->sendMsg(ErrorCode::OperationFailed, "Failed to create network", true);
+    else {
+        char tmp[128];
+        sprintf(tmp, "Created network id %d.", wn->getNetworkId());
+        cli->sendMsg(ErrorCode::CommandOkay, tmp, false);
+    }
+    return 0;
+}
+
+CommandListener::WifiRemoveNetworkCmd::WifiRemoveNetworkCmd() :
+                 NexusCommand("wifi_remove_network") {
+}
+
+int CommandListener::WifiRemoveNetworkCmd::runCommand(SocketClient *cli,
+                                                      int argc, char **argv) {
+    NetworkManager *nm = NetworkManager::Instance();
+    WifiController *wc = (WifiController *) nm->findController("WIFI");
+
+    if (wc->removeNetwork(atoi(argv[1])))
+        cli->sendMsg(ErrorCode::OperationFailed, "Failed to remove network", true);
+    else {
+        cli->sendMsg(ErrorCode::CommandOkay, "Network removed.", false);
+    }
+    return 0;
+}
+
+CommandListener::WifiScanResultsCmd::WifiScanResultsCmd() :
+                 NexusCommand("wifi_scan_results") {
+}
+
+int CommandListener::WifiScanResultsCmd::runCommand(SocketClient *cli,
+                                                    int argc, char **argv) {
+    NetworkManager *nm = NetworkManager::Instance();
+    WifiController *wc = (WifiController *) nm->findController("WIFI");
+
+    ScanResultCollection *src = wc->createScanResults();
+    ScanResultCollection::iterator it;
+    char buffer[256];
+
+    for(it = src->begin(); it != src->end(); ++it) {
+        sprintf(buffer, "%s:%u:%d:%s:%s",
+                (*it)->getBssid(), (*it)->getFreq(), (*it)->getLevel(),
+                (*it)->getFlags(), (*it)->getSsid());
+        cli->sendMsg(ErrorCode::WifiScanResult, buffer, false);
+        delete (*it);
+        it = src->erase(it);
+    }
+
+    delete src;
+    cli->sendMsg(ErrorCode::CommandOkay, "Scan results complete.", false);
+    return 0;
+}
+
+CommandListener::WifiListNetworksCmd::WifiListNetworksCmd() :
+                 NexusCommand("wifi_list_networks") {
+}
+
+int CommandListener::WifiListNetworksCmd::runCommand(SocketClient *cli,
+                                                     int argc, char **argv) {
+    NetworkManager *nm = NetworkManager::Instance();
+    WifiController *wc = (WifiController *) nm->findController("WIFI");
+
+    WifiNetworkCollection *src = wc->createNetworkList();
+    WifiNetworkCollection::iterator it;
+    char buffer[256];
+
+    for(it = src->begin(); it != src->end(); ++it) {
+        sprintf(buffer, "%d:%s", (*it)->getNetworkId(), (*it)->getSsid());
+        cli->sendMsg(ErrorCode::WifiNetworkList, buffer, false);
+        delete (*it);
+    }
+
+    delete src;
+    cli->sendMsg(ErrorCode::CommandOkay, "Network listing complete.", false);
+    return 0;
+}
+
+/* ------------
+ * Vpn Commands
+ * ------------ */
+
+/* ----------------
+ * Generic Commands
+ * ---------------- */
+CommandListener::GetCmd::GetCmd() :
+                 NexusCommand("get") {
+}
+
+int CommandListener::GetCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+    char val[Property::ValueMaxSize];
+
+    if (!NetworkManager::Instance()->getPropMngr()->get(argv[1],
+                                                        val,
+                                                        sizeof(val))) {
+        goto out_inval;
+    }
+
+    char *tmp;
+    asprintf(&tmp, "%s %s", argv[1], val);
+    cli->sendMsg(ErrorCode::PropertyRead, tmp, false);
+    free(tmp);
+
+    cli->sendMsg(ErrorCode::CommandOkay, "Property read.", false);
+    return 0;
+out_inval:
+    errno = EINVAL;
+    cli->sendMsg(ErrorCode::CommandParameterError, "Failed to read property.", true);
+    return 0;
+}
+
+CommandListener::SetCmd::SetCmd() :
+                 NexusCommand("set") {
+}
+
+int CommandListener::SetCmd::runCommand(SocketClient *cli, int argc,
+                                        char **argv) {
+    if (NetworkManager::Instance()->getPropMngr()->set(argv[1], argv[2]))
+        goto out_inval;
+
+    cli->sendMsg(ErrorCode::CommandOkay, "Property set.", false);
+    return 0;
+
+out_inval:
+    errno = EINVAL;
+    cli->sendMsg(ErrorCode::CommandParameterError, "Failed to set property.", true);
+    return 0;
+}
+
+CommandListener::ListCmd::ListCmd() :
+                 NexusCommand("list") {
+}
+
+int CommandListener::ListCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+    android::List<char *> *pc;
+
+    if (!(pc = NetworkManager::Instance()->getPropMngr()->createPropertyList())) {
+        errno = ENODATA;
+        cli->sendMsg(ErrorCode::CommandParameterError, "Failed to list properties.", true);
+        return 0;
+    }
+
+    android::List<char *>::iterator it;
+
+    for (it = pc->begin(); it != pc->end(); ++it) {
+        char p_v[Property::ValueMaxSize];
+
+        if (!NetworkManager::Instance()->getPropMngr()->get((*it),
+                                                            p_v,
+                                                            sizeof(p_v))) {
+            LOGW("Failed to get %s (%s)", (*it), strerror(errno));
+        }
+
+        char *buf;
+        if (asprintf(&buf, "%s %s", (*it), p_v) < 0) {
+            LOGE("Failed to allocate memory");
+            free((*it));
+            continue;
+        }
+        cli->sendMsg(ErrorCode::PropertyList, buf, false);
+        free(buf);
+
+        free((*it));
+    }
+
+    delete pc;
+
+    cli->sendMsg(ErrorCode::CommandOkay, "Properties list complete.", false);
+    return 0;
+}
diff --git a/nexus/CommandListener.h b/nexus/CommandListener.h
new file mode 100644
index 0000000..30c6dc0
--- /dev/null
+++ b/nexus/CommandListener.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#ifndef _COMMANDLISTENER_H__
+#define _COMMANDLISTENER_H__
+
+#include <sysutils/FrameworkListener.h>
+#include "NexusCommand.h"
+
+class CommandListener : public FrameworkListener {
+public:
+    CommandListener();
+    virtual ~CommandListener() {}
+
+private:
+
+    class WifiScanCmd : public NexusCommand {
+    public:
+        WifiScanCmd();
+        virtual ~WifiScanCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class WifiScanResultsCmd : public NexusCommand {
+    public:
+        WifiScanResultsCmd();
+        virtual ~WifiScanResultsCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class WifiCreateNetworkCmd : public NexusCommand {
+    public:
+        WifiCreateNetworkCmd();
+        virtual ~WifiCreateNetworkCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class WifiRemoveNetworkCmd : public NexusCommand {
+    public:
+        WifiRemoveNetworkCmd();
+        virtual ~WifiRemoveNetworkCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class WifiListNetworksCmd : public NexusCommand {
+    public:
+        WifiListNetworksCmd();
+        virtual ~WifiListNetworksCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class SetCmd : public NexusCommand {
+    public:
+        SetCmd();
+        virtual ~SetCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class GetCmd : public NexusCommand {
+    public:
+        GetCmd();
+        virtual ~GetCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class ListCmd : public NexusCommand {
+    public:
+        ListCmd();
+        virtual ~ListCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+};
+
+#endif
diff --git a/nexus/Controller.cpp b/nexus/Controller.cpp
new file mode 100644
index 0000000..17fb519
--- /dev/null
+++ b/nexus/Controller.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define LOG_TAG "Controller"
+
+#include <cutils/log.h>
+
+#include "Controller.h"
+
+extern "C" int init_module(void *, unsigned int, const char *);
+extern "C" int delete_module(const char *, unsigned int);
+
+Controller::Controller(const char *name, PropertyManager *propMngr,
+                       IControllerHandler *handlers) {
+    mPropMngr = propMngr;
+    mName = strdup(name);
+    mHandlers = handlers;
+    mBoundInterface = NULL;
+}
+
+Controller::~Controller() {
+    if (mBoundInterface)
+        free(mBoundInterface);
+    if (mName)
+        free(mName);
+}
+
+int Controller::start() {
+    return 0;
+}
+
+int Controller::stop() {
+    return 0;
+}
+
+int Controller::set(const char *name, const char *value) {
+    errno = ENOENT;
+    return -1;
+}
+
+const char *Controller::get(const char *name, char *buffer, size_t maxsize) {
+    errno = ENOENT;
+    return NULL;
+}
+
+int Controller::loadKernelModule(char *modpath, const char *args) {
+    void *module;
+    unsigned int size;
+
+    module = loadFile(modpath, &size);
+    if (!module) {
+        errno = -EIO;
+        return -1;
+    }
+
+    int rc = init_module(module, size, args);
+    free (module);
+    return rc;
+}
+
+int Controller::unloadKernelModule(const char *modtag) {
+    int rc = -1;
+    int retries = 10;
+
+    while (retries--) {
+        rc = delete_module(modtag, O_NONBLOCK | O_EXCL);
+        if (rc < 0 && errno == EAGAIN)
+            usleep(1000*500);
+        else
+            break;
+    }
+
+    if (rc != 0) {
+        LOGW("Unable to unload kernel driver '%s' (%s)", modtag,
+             strerror(errno));
+    }
+    return rc;
+}
+
+bool Controller::isKernelModuleLoaded(const char *modtag) {
+    FILE *fp = fopen("/proc/modules", "r");
+
+    if (!fp) {
+        LOGE("Unable to open /proc/modules (%s)", strerror(errno));
+        return false;
+    }
+
+    char line[255];
+    while(fgets(line, sizeof(line), fp)) {
+        char *endTag = strchr(line, ' ');
+
+        if (!endTag) {
+            LOGW("Unable to find tag for line '%s'", line);
+            continue;
+        }
+        if (!strncmp(line, modtag, (endTag - line))) {
+            fclose(fp);
+            return true;
+        }
+    }
+
+    fclose(fp);
+    return false;
+}
+
+void *Controller::loadFile(char *filename, unsigned int *_size)
+{
+	int ret, fd;
+	struct stat sb;
+	ssize_t size;
+	void *buffer = NULL;
+
+	/* open the file */
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	/* find out how big it is */
+	if (fstat(fd, &sb) < 0)
+		goto bail;
+	size = sb.st_size;
+
+	/* allocate memory for it to be read into */
+	buffer = malloc(size);
+	if (!buffer)
+		goto bail;
+
+	/* slurp it into our buffer */
+	ret = read(fd, buffer, size);
+	if (ret != size)
+		goto bail;
+
+	/* let the caller know how big it is */
+	*_size = size;
+
+bail:
+	close(fd);
+	return buffer;
+}
+
+int Controller::bindInterface(const char *ifname) {
+    mBoundInterface = strdup(ifname);
+    LOGD("Controller %s bound to %s", mName, ifname);
+    return 0;
+}
+
+int Controller::unbindInterface(const char *ifname) {
+    free(mBoundInterface);
+    mBoundInterface = NULL;
+    LOGD("Controller %s unbound from %s", mName, ifname);
+    return 0;
+}
diff --git a/nexus/Controller.h b/nexus/Controller.h
new file mode 100644
index 0000000..af03d2e
--- /dev/null
+++ b/nexus/Controller.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#ifndef _CONTROLLER_H
+#define _CONTROLLER_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <utils/List.h>
+
+class PropertyManager;
+class IControllerHandler;
+
+#include "PropertyManager.h"
+#include "IPropertyProvider.h"
+
+class Controller : public IPropertyProvider {
+    /*
+     * Name of this controller - WIFI/VPN/USBNET/BTNET/BTDUN/LOOP/etc
+     */
+    char *mName;
+
+    /*
+     * Name of the system ethernet interface which this controller is
+     * bound to.
+     */
+    char *mBoundInterface;
+
+protected:
+    PropertyManager *mPropMngr;
+    IControllerHandler *mHandlers;
+    
+public:
+    Controller(const char *name, PropertyManager *propMngr,
+               IControllerHandler *handlers);
+    virtual ~Controller();
+
+    virtual int start();
+    virtual int stop();
+
+    const char *getName() { return mName; }
+    const char *getBoundInterface() { return mBoundInterface; }
+
+    /* IPropertyProvider methods */
+    virtual int set(const char *name, const char *value);
+    virtual const char *get(const char *name, char *buffer, size_t maxsize);
+
+protected:
+    int loadKernelModule(char *modpath, const char *args);
+    bool isKernelModuleLoaded(const char *modtag);
+    int unloadKernelModule(const char *modtag);
+    int bindInterface(const char *ifname);
+    int unbindInterface(const char *ifname);
+
+private:
+    void *loadFile(char *filename, unsigned int *_size);
+};
+
+typedef android::List<Controller *> ControllerCollection;
+#endif
diff --git a/nexus/DhcpClient.cpp b/nexus/DhcpClient.cpp
new file mode 100644
index 0000000..2bd9c68
--- /dev/null
+++ b/nexus/DhcpClient.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#define LOG_TAG "DhcpClient"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <sysutils/ServiceManager.h>
+
+#include "DhcpClient.h"
+#include "DhcpState.h"
+#include "DhcpListener.h"
+#include "IDhcpEventHandlers.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::STOPPED), mHandlers(handlers) {
+    mServiceManager = new ServiceManager();
+    mListener = NULL;
+}
+
+DhcpClient::~DhcpClient() {
+    delete mServiceManager;
+    if (mListener)
+        delete mListener;
+}
+
+int DhcpClient::start(const char *interface) {
+
+    char svc[PROPERTY_VALUE_MAX];
+    snprintf(svc, sizeof(svc), "dhcpcd_ng:%s", interface);
+
+    if (mServiceManager->start(svc)) {
+        LOGE("Failed to start dhcp service");
+        return -1;
+    }
+
+    mListener = new DhcpListener(mHandlers);
+    if (mListener->startListener()) {
+        LOGE("Failed to start listener");
+#if 0
+        mServiceManager->stop("dhcpcd_ng");
+        return -1;
+#endif
+        delete mListener;
+        mListener = NULL;
+    }
+
+    mState = DhcpState::STARTED;
+
+    return 0;
+}
+
+int DhcpClient::stop() {
+    if (mListener) {
+        mListener->stopListener();
+        delete mListener;
+        mListener = NULL;
+    }
+
+    if (mServiceManager->stop("dhcpcd_ng")) 
+        LOGW("Failed to stop DHCP service (%s)", strerror(errno));
+    mState = DhcpState::STOPPED;
+    return 0;
+}
diff --git a/nexus/DhcpClient.h b/nexus/DhcpClient.h
new file mode 100644
index 0000000..e0a2784
--- /dev/null
+++ b/nexus/DhcpClient.h
@@ -0,0 +1,41 @@
+
+/*
+ * 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.
+ */
+
+#ifndef _DhcpClient_H
+#define _DhcpClient_H
+
+class IDhcpEventHandlers;
+class ServiceManager;
+class DhcpListener;
+
+class DhcpClient {
+    int                mState;
+    IDhcpEventHandlers *mHandlers;
+    ServiceManager     *mServiceManager;
+    DhcpListener       *mListener;
+
+public:
+    DhcpClient(IDhcpEventHandlers *handlers);
+    virtual ~DhcpClient();
+
+    int getState() { return mState; }
+
+    int start(const char *interface);
+    int stop();
+};
+
+#endif
diff --git a/nexus/DhcpListener.cpp b/nexus/DhcpListener.cpp
new file mode 100644
index 0000000..2d07ee8
--- /dev/null
+++ b/nexus/DhcpListener.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DhcpListener"
+#include <cutils/log.h>
+
+#include <DhcpListener.h>
+#include "IDhcpEventHandlers.h"
+
+DhcpListener::DhcpListener(IDhcpEventHandlers *handlers) :
+              SocketListener("dhcp_ng", false) {
+    mHandlers = handlers;
+}
+
+DhcpListener::~DhcpListener() {
+}
+
+bool DhcpListener::onDataAvailable(SocketClient *cli) {
+    LOGD("onDataAvailable()");
+    return true;
+}
diff --git a/nexus/DhcpListener.h b/nexus/DhcpListener.h
new file mode 100644
index 0000000..bfc5a37
--- /dev/null
+++ b/nexus/DhcpListener.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef _DhcpListener_H
+#define _DhcpListener_H
+
+#include <sysutils/SocketListener.h>
+
+class IDhcpEventHandlers;
+
+class DhcpListener : public SocketListener {
+    IDhcpEventHandlers *mHandlers;
+
+public:
+
+    DhcpListener(IDhcpEventHandlers *handlers);
+    virtual ~DhcpListener();
+
+private:
+    bool onDataAvailable(SocketClient *cli);
+};
+
+#endif
diff --git a/nexus/DhcpState.h b/nexus/DhcpState.h
new file mode 100644
index 0000000..27d7c7e
--- /dev/null
+++ b/nexus/DhcpState.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#ifndef _DhcpState_H
+#define _DhcpState_H
+
+class DhcpState {
+public:
+    static const int UNKNOWN = 0;
+    static const int STOPPED = 1;
+    static const int STARTED = 2;
+};
+
+#endif
diff --git a/nexus/ErrorCode.h b/nexus/ErrorCode.h
new file mode 100644
index 0000000..414dd2c
--- /dev/null
+++ b/nexus/ErrorCode.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef _ERRORCODE_H
+#define _ERRORCODE_H
+
+class ErrorCode {
+public:
+    // 100 series - Requestion action was initiated; expect another reply
+    // before proceeding with a new command.
+    static const int ActionInitiated = 100;
+
+    static const int WifiScanResult = 125;
+    static const int WifiNetworkList = 126;
+
+    static const int PropertyRead = 127;
+    static const int PropertySet = 128;
+    static const int PropertyList = 129;
+
+    // 200 series - Requested action has been successfully completed
+    static const int CommandOkay = 200;
+
+    // 400 series - The command was accepted but the requested action
+    // did not take place.
+    static const int OperationFailed = 400;
+
+    // 500 series - The command was not accepted and the requested
+    // action did not take place.
+    static const int CommandSyntaxError = 500;
+    static const int CommandParameterError = 501;
+
+    // 600 series - Unsolicited broadcasts
+    static const int UnsolicitedInformational = 600;
+};
+#endif
diff --git a/nexus/IControllerHandler.h b/nexus/IControllerHandler.h
new file mode 100644
index 0000000..f7be39c
--- /dev/null
+++ b/nexus/IControllerHandler.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _ICONTROLLER_HANDLER_H
+#define _ICONTROLLER_HANDLER_H
+
+class Controller;
+class InterfaceConfig;
+
+class IControllerHandler {
+public:
+    virtual void onInterfaceConnected(Controller *c, const InterfaceConfig *cfg) = 0;
+    virtual void onInterfaceDisconnected(Controller *c, const char *name) = 0;
+};
+
+#endif
+
diff --git a/nexus/IDhcpEventHandlers.h b/nexus/IDhcpEventHandlers.h
new file mode 100644
index 0000000..3f51f64
--- /dev/null
+++ b/nexus/IDhcpEventHandlers.h
@@ -0,0 +1,25 @@
+
+/*
+ * 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.
+ */
+
+#ifndef _IDhcpEventHandlers_H
+#define _IDhcpEventHandlers_H
+
+class IDhcpEventHandlers {
+public:
+};
+
+#endif
diff --git a/nexus/IPropertyProvider.h b/nexus/IPropertyProvider.h
new file mode 100644
index 0000000..17ad5f8
--- /dev/null
+++ b/nexus/IPropertyProvider.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef _IPROPERTY_PROVIDER_H
+#define _IPROPERTY_PROVIDER_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <utils/List.h>
+
+class IPropertyProvider {
+public:
+    virtual int set(const char *name, const char *value) = 0;
+    virtual const char *get(const char *name, char *buffer, size_t max) = 0;
+};
+
+typedef android::List<IPropertyProvider *> IPropertyProviderCollection;
+
+#endif
diff --git a/nexus/ISupplicantEventHandler.h b/nexus/ISupplicantEventHandler.h
new file mode 100644
index 0000000..b7fd17b
--- /dev/null
+++ b/nexus/ISupplicantEventHandler.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef _ISUPPLICANT_EVENT_HANDLER_H
+#define _ISUPPLICANT_EVENT_HANDLER_H
+
+class SupplicantAssociatingEvent;
+class SupplicantAssociatedEvent;
+class SupplicantConnectedEvent;
+class SupplicantScanResultsEvent;
+class SupplicantStateChangeEvent;
+class SupplicantConnectionTimeoutEvent;
+class SupplicantDisconnectedEvent;
+
+class ISupplicantEventHandler {
+public:
+    virtual void onAssociatingEvent(SupplicantAssociatingEvent *evt) = 0;
+    virtual void onAssociatedEvent(SupplicantAssociatedEvent *evt) = 0;
+    virtual void onConnectedEvent(SupplicantConnectedEvent *evt) = 0;
+    virtual void onScanResultsEvent(SupplicantScanResultsEvent *evt) = 0;
+    virtual void onStateChangeEvent(SupplicantStateChangeEvent *evt) = 0;
+    virtual void onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt) = 0;
+    virtual void onDisconnectedEvent(SupplicantDisconnectedEvent *evt) = 0;
+#if 0
+    virtual void onTerminatingEvent(SupplicantEvent *evt) = 0;
+    virtual void onPasswordChangedEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapNotificationEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapStartedEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapMethodEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapSuccessEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapFailureEvent(SupplicantEvent *evt) = 0;
+    virtual void onLinkSpeedEvent(SupplicantEvent *evt) = 0;
+    virtual void onDriverStateEvent(SupplicantEvent *evt) = 0;
+#endif
+};
+
+#endif
+
diff --git a/nexus/InterfaceConfig.cpp b/nexus/InterfaceConfig.cpp
new file mode 100644
index 0000000..66861d0
--- /dev/null
+++ b/nexus/InterfaceConfig.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "InterfaceConfig"
+#include <cutils/log.h>
+
+#include "InterfaceConfig.h"
+#include "NetworkManager.h"
+
+const char *InterfaceConfig::PropertyNames[] = { "dhcp", "ip",
+                                                 "netmask",
+                                                 "gateway", "dns1", "dns2",
+                                                 "dns3", '\0' };
+
+InterfaceConfig::InterfaceConfig(const char *prop_prefix) {
+    mPropPrefix = strdup(prop_prefix);
+    mUseDhcp = true;
+    registerProperties();
+}
+
+InterfaceConfig::~InterfaceConfig() {
+    unregisterProperties();
+    free(mPropPrefix);
+}
+
+InterfaceConfig::InterfaceConfig(const char *prop_prefix,
+                    const char *ip, const char *nm,
+                    const char *gw, const char *dns1, const char *dns2,
+                    const char *dns3) {
+    mPropPrefix = strdup(prop_prefix);
+    mUseDhcp = false;
+
+    if (!inet_aton(ip, &mIp))
+        LOGW("Unable to parse ip (%s)", ip);
+    if (!inet_aton(nm, &mNetmask))
+        LOGW("Unable to parse netmask (%s)", nm);
+    if (!inet_aton(gw, &mGateway))
+        LOGW("Unable to parse gateway (%s)", gw);
+    if (!inet_aton(dns1, &mDns1))
+        LOGW("Unable to parse dns1 (%s)", dns1);
+    if (!inet_aton(dns2, &mDns2))
+        LOGW("Unable to parse dns2 (%s)", dns2);
+    if (!inet_aton(dns3, &mDns3))
+        LOGW("Unable to parse dns3 (%s)", dns3);
+    registerProperties();
+}
+
+InterfaceConfig::InterfaceConfig(const char *prop_prefix,
+                    const struct in_addr *ip,
+                    const struct in_addr *nm, const struct in_addr *gw,
+                    const struct in_addr *dns1, const struct in_addr *dns2,
+                    const struct in_addr *dns3) {
+    mPropPrefix = strdup(prop_prefix);
+    mUseDhcp = false;
+
+    memcpy(&mIp, ip, sizeof(struct in_addr));
+    memcpy(&mNetmask, nm, sizeof(struct in_addr));
+    memcpy(&mGateway, gw, sizeof(struct in_addr));
+    memcpy(&mDns1, dns1, sizeof(struct in_addr));
+    memcpy(&mDns2, dns2, sizeof(struct in_addr));
+    memcpy(&mDns3, dns3, sizeof(struct in_addr));
+    registerProperties();
+}
+
+int InterfaceConfig::registerProperties() {
+    for (const char **p = InterfaceConfig::PropertyNames; *p != '\0'; p++) {
+        char *tmp;
+        asprintf(&tmp, "%s.if.%s", mPropPrefix, *p);
+
+        if (NetworkManager::Instance()->getPropMngr()->registerProperty(tmp,
+                                                                        this)) {
+            free(tmp);
+            return -1;
+        }
+        free(tmp);
+    }
+    return 0;
+}
+
+int InterfaceConfig::unregisterProperties() {
+    for (const char **p = InterfaceConfig::PropertyNames; *p != '\0'; p++) {
+        char *tmp;
+        asprintf(&tmp, "%s.if.%s", mPropPrefix, *p);
+
+        if (NetworkManager::Instance()->getPropMngr()->unregisterProperty(tmp))
+            LOGW("Unable to remove property '%s' (%s)", tmp, strerror(errno));
+        free(tmp);
+    }
+    return 0;
+}
+
+int InterfaceConfig::set(const char *name, const char *value) {
+    const char *n;
+
+    for (n = &name[strlen(name)]; *n != '.'; n--);
+    n++;
+
+    if (!strcasecmp(n, "name")) {
+        errno = EROFS;
+        return -1;
+    } else if (!strcasecmp(n, "ip") && !inet_aton(value, &mIp))
+        goto out_inval;
+    else if (!strcasecmp(n, "dhcp"))
+        mUseDhcp = (atoi(value) == 0 ? false : true);
+    else if (!strcasecmp(n, "netmask") && !inet_aton(value, &mNetmask))
+        goto out_inval;
+    else if (!strcasecmp(n, "gateway") && !inet_aton(value, &mGateway))
+        goto out_inval;
+    else if (!strcasecmp(n, "dns1") && !inet_aton(value, &mDns1))
+        goto out_inval;
+    else if (!strcasecmp(n, "dns2") && !inet_aton(value, &mDns2))
+        goto out_inval;
+    else if (!strcasecmp(n, "dns3") && !inet_aton(value, &mDns3))
+        goto out_inval;
+    else {
+        errno = ENOENT;
+        return -1;
+    }
+
+    return 0;
+
+out_inval:
+    errno = EINVAL;
+    return -1;
+}
+
+const char *InterfaceConfig::get(const char *name, char *buffer, size_t max) {
+    const char *n;
+
+    for (n = &name[strlen(name)]; *n != '.'; n--);
+    n++;
+
+    if (!strcasecmp(n, "ip"))
+        strncpy(buffer, inet_ntoa(mIp), max);
+    else if (!strcasecmp(n, "dhcp"))
+        snprintf(buffer, max, "%d", mUseDhcp);
+    else if (!strcasecmp(n, "netmask"))
+        strncpy(buffer, inet_ntoa(mNetmask), max);
+    else if (!strcasecmp(n, "gateway"))
+        strncpy(buffer, inet_ntoa(mGateway), max);
+    else if (!strcasecmp(n, "dns1"))
+        strncpy(buffer, inet_ntoa(mDns1), max);
+    else if (!strcasecmp(n, "dns2"))
+        strncpy(buffer, inet_ntoa(mDns2), max);
+    else if (!strcasecmp(n, "dns3"))
+        strncpy(buffer, inet_ntoa(mDns3), max);
+    else {
+        strncpy(buffer, "(internal error)", max);
+        errno = ENOENT;
+        return NULL;
+    }
+    return buffer;
+}
diff --git a/nexus/InterfaceConfig.h b/nexus/InterfaceConfig.h
new file mode 100644
index 0000000..d97f20b
--- /dev/null
+++ b/nexus/InterfaceConfig.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#ifndef _INTERFACE_CONFIG_H
+#define _INTERFACE_CONFIG_H
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "IPropertyProvider.h"
+
+class PropertyManager;
+
+class InterfaceConfig : public IPropertyProvider {
+public:
+    static const char *PropertyNames[];
+
+private:
+    char *mPropPrefix;
+    bool mUseDhcp;
+    struct in_addr mIp;
+    struct in_addr mNetmask;
+    struct in_addr mGateway;
+    struct in_addr mDns1;
+    struct in_addr mDns2;
+    struct in_addr mDns3;
+
+public:
+    InterfaceConfig(const char *prop_prefix);
+    InterfaceConfig(const char *prop_prefix,
+                    const char *ip, const char *nm,
+                    const char *gw, const char *dns1, const char *dns2,
+                    const char *dns3);
+
+    InterfaceConfig(const char *prop_prefix,
+                    const struct in_addr *ip,
+                    const struct in_addr *nm, const struct in_addr *gw,
+                    const struct in_addr *dns1, const struct in_addr *dns2,
+                    const struct in_addr *dns3);
+
+    virtual ~InterfaceConfig();
+    
+    int set(const char *name, const char *value);
+    const char *get(const char *name, char *buffer, size_t maxsize);
+
+    bool            getUseDhcp() const { return mUseDhcp; }
+    const struct in_addr &getIp() const { return mIp; }
+    const struct in_addr &getNetmask() const { return mNetmask; }
+    const struct in_addr &getGateway() const { return mGateway; }
+    const struct in_addr &getDns1() const { return mDns1; }
+    const struct in_addr &getDns2() const { return mDns2; }
+    const struct in_addr &getDns3() const { return mDns3; }
+
+private:
+    int registerProperties();
+    int unregisterProperties();
+};
+
+
+#endif
diff --git a/nexus/LoopController.cpp b/nexus/LoopController.cpp
new file mode 100644
index 0000000..5cfb1fe
--- /dev/null
+++ b/nexus/LoopController.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 <errno.h>
+
+#include "LoopController.h"
+#include "PropertyManager.h"
+
+LoopController::LoopController(PropertyManager *propmngr,
+                               IControllerHandler *handlers) :
+                Controller("LOOP", propmngr, handlers) {
+}
+
+int LoopController::set(const char *name, const char *value) {
+    return Controller::set(name, value);
+}
+
+const char *LoopController::get(const char *name, char *buffer, size_t maxsize) {
+    return Controller::get(name, buffer, maxsize);
+}
+
diff --git a/nexus/LoopController.h b/nexus/LoopController.h
new file mode 100644
index 0000000..53d16f1
--- /dev/null
+++ b/nexus/LoopController.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef _LOOP_CONTROLLER_H
+#define _LOOP_CONTROLLER_H
+
+#include "Controller.h"
+
+class ControllerHandler;
+
+class LoopController : public Controller {
+public:
+    LoopController(PropertyManager *propmngr, IControllerHandler *h);
+    virtual ~LoopController() {}
+
+    int set(const char *name, const char *value);
+    const char *get(const char *name, char *buffer, size_t maxsize);
+};
+
+#endif
diff --git a/nexus/NetworkManager.cpp b/nexus/NetworkManager.cpp
new file mode 100644
index 0000000..7450b95
--- /dev/null
+++ b/nexus/NetworkManager.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 <stdio.h>
+#include <errno.h>
+
+#define LOG_TAG "Nexus"
+
+#include <cutils/log.h>
+
+#include "NetworkManager.h"
+#include "InterfaceConfig.h"
+#include "DhcpClient.h"
+
+NetworkManager *NetworkManager::sInstance = NULL;
+
+NetworkManager *NetworkManager::Instance() {
+    if (!sInstance)
+        sInstance = new NetworkManager(new PropertyManager());
+    return sInstance;
+}
+
+NetworkManager::NetworkManager(PropertyManager *propMngr) {
+    mBroadcaster = NULL;
+    mControllers = new ControllerCollection();
+    mPropMngr = propMngr;
+    mDhcp = new DhcpClient(this);
+}
+
+NetworkManager::~NetworkManager() {
+}
+
+int NetworkManager::run() {
+    if (startControllers()) {
+        LOGW("Unable to start all controllers (%s)", strerror(errno));
+    }
+    return 0;
+}
+
+int NetworkManager::attachController(Controller *c) {
+    mControllers->push_back(c);
+    return 0;
+}
+
+int NetworkManager::startControllers() {
+    int rc = 0;
+    ControllerCollection::iterator i;
+
+    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
+        int irc = (*i)->start();
+        LOGD("Controller '%s' start rc = %d", (*i)->getName(), irc);
+        if (irc && !rc)
+            rc = irc;
+    }
+    return rc;
+}
+
+int NetworkManager::stopControllers() {
+    int rc = 0;
+    ControllerCollection::iterator i;
+
+    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
+        int irc = (*i)->stop();
+        LOGD("Controller '%s' stop rc = %d", (*i)->getName(), irc);
+        if (irc && !rc)
+            rc = irc;
+    }
+    return rc;
+}
+
+Controller *NetworkManager::findController(const char *name) {
+    ControllerCollection::iterator i;
+    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
+        if (!strcmp((*i)->getName(), name))
+            return *i;
+    }
+    LOGW("Controller '%s' not found", name);
+    return NULL;
+}
+
+void NetworkManager::onInterfaceConnected(Controller *c, const InterfaceConfig *cfg) {
+    LOGD("Controller %s interface %s connected", c->getName(), c->getBoundInterface());
+
+    // Look up the interface
+
+    if (0) { // already started?
+    }
+
+    if (cfg) {
+        if (cfg->getUseDhcp() && mDhcp->start(c->getBoundInterface())) {
+            LOGE("DHCP start failed");
+        } else if (!cfg->getUseDhcp()) {
+            // Static configuration
+        }
+    } else {
+        LOGD("No InterfaceConfig for %s:%s - assuming self-managed",
+            c->getName(), c->getBoundInterface());
+    }
+}
+
+void NetworkManager::onInterfaceDisconnected(Controller *c, const char *name) {
+    LOGD("Controller %s interface %s disconnected", c->getName(), name);
+
+    // If we have a DHCP request out on this interface then stop it
+    if (1) {
+        mDhcp->stop();
+    }
+}
diff --git a/nexus/NetworkManager.h b/nexus/NetworkManager.h
new file mode 100644
index 0000000..44f3417
--- /dev/null
+++ b/nexus/NetworkManager.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef _NETWORKMANAGER_H
+#define _NETWORKMANAGER_H
+
+#include <sysutils/SocketListener.h>
+
+#include "Controller.h"
+#include "PropertyManager.h"
+#include "IControllerHandler.h"
+#include "IDhcpEventHandlers.h"
+
+class InterfaceConfig;
+class DhcpClient;
+
+class NetworkManager : public IControllerHandler, public IDhcpEventHandlers {
+private:
+    static NetworkManager *sInstance;
+
+private:
+    ControllerCollection *mControllers;
+    SocketListener       *mBroadcaster;
+    PropertyManager      *mPropMngr;
+    DhcpClient           *mDhcp;
+
+public:
+    virtual ~NetworkManager();
+
+    int run();
+
+    int attachController(Controller *controller);
+
+    Controller *findController(const char *name);
+
+    void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
+    SocketListener *getBroadcaster() { return mBroadcaster; }
+    PropertyManager *getPropMngr() { return mPropMngr; }
+
+    static NetworkManager *Instance();
+
+private:
+    int startControllers();
+    int stopControllers();
+
+    NetworkManager(PropertyManager *propMngr);
+
+    void onInterfaceConnected(Controller *c, const InterfaceConfig *cfg);
+    void onInterfaceDisconnected(Controller *c, const char *name);
+};
+#endif
diff --git a/nexus/NexusCommand.cpp b/nexus/NexusCommand.cpp
new file mode 100644
index 0000000..541eeeb
--- /dev/null
+++ b/nexus/NexusCommand.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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 "NexusCommand.h"
+
+NexusCommand::NexusCommand(const char *cmd) :
+              FrameworkCommand(cmd)  {
+}
diff --git a/nexus/NexusCommand.h b/nexus/NexusCommand.h
new file mode 100644
index 0000000..a7f944a
--- /dev/null
+++ b/nexus/NexusCommand.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#ifndef _NEXUS_COMMAND_H
+#define _NEXUS_COMMAND_H
+
+#include <sysutils/FrameworkCommand.h>
+
+class NexusCommand : public FrameworkCommand {
+public:
+    NexusCommand(const char *cmd);
+    virtual ~NexusCommand() {}
+};
+
+#endif
diff --git a/nexus/OpenVpnController.cpp b/nexus/OpenVpnController.cpp
new file mode 100644
index 0000000..f1ea510
--- /dev/null
+++ b/nexus/OpenVpnController.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define LOG_TAG "OpenVpnController"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <sysutils/ServiceManager.h>
+
+#include "OpenVpnController.h"
+#include "PropertyManager.h"
+
+#define DAEMON_PROP_NAME "vpn.openvpn.status"
+#define DAEMON_CONFIG_FILE "/data/misc/openvpn/openvpn.conf"
+
+OpenVpnController::OpenVpnController(PropertyManager *propmngr,
+                                     IControllerHandler *handlers) :
+                   VpnController(propmngr, handlers) {
+    mServiceManager = new ServiceManager();
+}
+
+OpenVpnController::~OpenVpnController() {
+    delete mServiceManager;
+}
+
+int OpenVpnController::start() {
+    return VpnController::start();
+}
+
+int OpenVpnController::stop() {
+    return VpnController::stop();
+}
+
+int OpenVpnController::enable() {
+    char svc[PROPERTY_VALUE_MAX];
+    char tmp[64];
+
+    if (!mPropMngr->get("vpn.gateway", tmp, sizeof(tmp))) {
+        LOGE("Error reading property 'vpn.gateway' (%s)", strerror(errno));
+        return -1;
+    }
+    snprintf(svc, sizeof(svc), "openvpn:--remote %s 1194", tmp);
+
+    if (mServiceManager->start(svc))
+        return -1;
+
+    return 0;
+}
+
+int OpenVpnController::disable() {
+    if (mServiceManager->stop("openvpn"))
+        return -1;
+    return 0;
+}
diff --git a/nexus/OpenVpnController.h b/nexus/OpenVpnController.h
new file mode 100644
index 0000000..529aab5
--- /dev/null
+++ b/nexus/OpenVpnController.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef _OPEN_VPN_CONTROLLER_H
+#define _OPEN_VPN_CONTROLLER_H
+
+#include "PropertyManager.h"
+#include "VpnController.h"
+
+class ServiceManager;
+class IControllerHandler;
+
+class OpenVpnController : public VpnController {
+private:
+    ServiceManager *mServiceManager;
+
+public:
+    OpenVpnController(PropertyManager *propmngr, IControllerHandler *handlers);
+    virtual ~OpenVpnController();
+
+    int start();
+    int stop();
+
+private:
+    int enable();
+    int disable();
+};
+
+#endif
diff --git a/nexus/Property.h b/nexus/Property.h
new file mode 100644
index 0000000..198f722
--- /dev/null
+++ b/nexus/Property.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+class Property {
+public:
+    static const int NameMaxSize = 128;
+    static const int ValueMaxSize  = 255;
+};
diff --git a/nexus/PropertyManager.cpp b/nexus/PropertyManager.cpp
new file mode 100644
index 0000000..6faf9b8
--- /dev/null
+++ b/nexus/PropertyManager.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "PropertyManager"
+
+#include <cutils/log.h>
+
+#include "PropertyManager.h"
+
+PropertyManager::PropertyManager() {
+    mPropertyPairs = new PropertyPairCollection();
+    pthread_mutex_init(&mLock, NULL);
+}
+
+PropertyManager::~PropertyManager() {
+    delete mPropertyPairs;
+}
+
+int PropertyManager::registerProperty(const char *name, IPropertyProvider *pp) {
+    PropertyPairCollection::iterator it;
+
+//    LOGD("registerProperty(%s)", name);
+    pthread_mutex_lock(&mLock);
+    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
+        if (!strcmp(name, (*it)->getName())) {
+            errno = EADDRINUSE;
+            LOGE("Failed to register property %s (%s)",
+                 name, strerror(errno));
+            pthread_mutex_unlock(&mLock);
+            return -1;
+        }
+    }
+    mPropertyPairs->push_back(new PropertyPair(name, pp));
+    pthread_mutex_unlock(&mLock);
+    return 0;
+}
+
+int PropertyManager::unregisterProperty(const char *name) {
+    PropertyPairCollection::iterator it;
+
+//    LOGD("unregisterProperty(%s)", name);
+    pthread_mutex_lock(&mLock);
+    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
+        if (!strcmp(name, (*it)->getName())) {
+            delete ((*it));
+            mPropertyPairs->erase(it);
+            pthread_mutex_unlock(&mLock);
+            return 0;
+        }
+    }
+    pthread_mutex_unlock(&mLock);
+    errno = ENOENT;
+    return -1;
+}
+
+/*
+ * IPropertyManager methods
+ */
+
+int PropertyManager::set(const char *name, const char *value) {
+    PropertyPairCollection::iterator it;
+
+    pthread_mutex_lock(&mLock);
+    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
+        if (!strcmp(name, (*it)->getName())) {
+            pthread_mutex_unlock(&mLock);
+            return (*it)->getProvider()->set(name, value);
+        }
+    }
+    pthread_mutex_unlock(&mLock);
+    errno = ENOENT;
+    return -1;
+}
+
+const char *PropertyManager::get(const char *name, char *buffer, size_t max) {
+    PropertyPairCollection::iterator it;
+
+    memset(buffer, 0, max);
+    pthread_mutex_lock(&mLock);
+    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
+        if (!strcmp(name, (*it)->getName())) {
+            pthread_mutex_unlock(&mLock);
+            return (*it)->getProvider()->get(name, buffer, max);
+            }
+    }
+    pthread_mutex_unlock(&mLock);
+    errno = ENOENT;
+    return NULL;
+}
+
+android::List<char *> *PropertyManager::createPropertyList() {
+    android::List<char *> *c = new android::List<char *>();
+
+    PropertyPairCollection::iterator it;
+
+    pthread_mutex_lock(&mLock);
+    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it)
+         c->push_back(strdup((*it)->getName()));
+    pthread_mutex_unlock(&mLock);
+    return c;
+}
+
+PropertyPair::PropertyPair(const char *name, IPropertyProvider *pp) {
+    mName = strdup(name);
+    mPp = pp;
+}
+
+PropertyPair::~PropertyPair() {
+    free(mName);
+}
diff --git a/nexus/PropertyManager.h b/nexus/PropertyManager.h
new file mode 100644
index 0000000..10d0b2a
--- /dev/null
+++ b/nexus/PropertyManager.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef _PROPERTY_MANAGER_H
+#define _PROPERTY_MANAGER_H
+
+#include <errno.h>
+#include <pthread.h>
+
+#include <utils/List.h>
+
+#include "IPropertyProvider.h"
+
+class PropertyPair {
+private:
+    char *mName;
+    IPropertyProvider *mPp;
+ 
+public:
+    PropertyPair(const char *name, IPropertyProvider *pp);
+    virtual ~PropertyPair();
+
+    const char *getName() { return mName; }
+    IPropertyProvider *getProvider() { return mPp; }
+};
+
+typedef android::List<PropertyPair *> PropertyPairCollection;
+
+class PropertyManager {
+    PropertyPairCollection *mPropertyPairs;
+    pthread_mutex_t         mLock;
+
+public:
+    PropertyManager();
+    virtual ~PropertyManager();   
+    int registerProperty(const char *name, IPropertyProvider *pp);
+    int unregisterProperty(const char *name);
+    android::List<char *> *createPropertyList();
+
+    int set(const char *name, const char *value);
+    const char *get(const char *name, char *buffer, size_t max);
+};
+
+#endif
diff --git a/nexus/ScanResult.cpp b/nexus/ScanResult.cpp
new file mode 100644
index 0000000..e9a286c
--- /dev/null
+++ b/nexus/ScanResult.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 <stdlib.h>
+#include <ctype.h>
+
+#define LOG_TAG "ScanResult"
+#include <cutils/log.h>
+
+#include "ScanResult.h"
+
+ScanResult::ScanResult() {
+}
+
+ScanResult::ScanResult(char *rawResult) {
+    char *p = rawResult, *q = rawResult;
+    char tmp[255];
+
+    // BSSID
+    for (q = p; *q != '\t'; ++q);
+    strncpy(tmp, p, (q - p));
+    tmp[q-p] = '\0';
+    mBssid = strdup(tmp);
+    ++q;
+
+    // FREQ
+    for (p = q; *q != '\t'; ++q);
+    strncpy(tmp, p, (q - p));
+    tmp[q-p] = '\0';
+    mFreq = atoi(tmp);
+    ++q;
+
+    // LEVEL
+    for (p = q; *q != '\t'; ++q);
+    strncpy(tmp, p, (q - p));
+    tmp[q-p] = '\0';
+    mLevel = atoi(tmp);
+    ++q;
+
+    // FLAGS
+    for (p = q; *q != '\t'; ++q);
+    strncpy(tmp, p, (q - p));
+    tmp[q-p] = '\0';
+    mFlags = strdup(tmp);
+    ++q;
+
+    // XXX: For some reason Supplicant sometimes sends a double-tab here.
+    // haven't had time to dig into it ...
+    if (*q == '\t')
+        q++;
+
+    for (p = q; *q != '\t'; ++q) {
+        if (*q == '\0')
+            break;
+    }
+
+    strncpy(tmp, p, (q - p));
+    tmp[q-p] = '\0';
+    mSsid = strdup(tmp);
+    ++q;
+
+    return;
+ out_bad:
+    LOGW("Malformatted scan result (%s)", rawResult);
+}
+
+ScanResult::~ScanResult() {
+    if (mBssid)
+        free(mBssid);
+    if (mFlags)
+        free(mFlags);
+    if (mSsid)
+        free(mSsid);
+}
+
+ScanResult *ScanResult::clone() {
+    ScanResult *r = new ScanResult();
+
+    r->mBssid = strdup(mBssid);
+    r->mFreq = mFreq;
+    r->mLevel = mLevel;
+    r->mFlags = strdup(mFlags);
+    r->mSsid = strdup(mSsid);
+
+    return r;
+}
diff --git a/nexus/ScanResult.h b/nexus/ScanResult.h
new file mode 100644
index 0000000..68ad0f0
--- /dev/null
+++ b/nexus/ScanResult.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef _SCAN_RESULT_H
+#define _SCAN_RESULT_H
+
+#include <sys/types.h>
+
+#include <utils/List.h>
+
+class ScanResult {
+    char     *mBssid;
+    uint32_t mFreq;
+    int      mLevel;
+    char     *mFlags;
+    char     *mSsid;
+
+private:
+    ScanResult();
+
+public:
+    ScanResult(char *rawResult);
+    virtual ~ScanResult();
+
+    ScanResult *clone();
+
+    const char *getBssid() { return mBssid; }
+    uint32_t getFreq() { return mFreq; }
+    int getLevel() { return mLevel; }
+    const char *getFlags() { return mFlags; }
+    const char *getSsid() { return mSsid; }
+};
+
+typedef android::List<ScanResult *> ScanResultCollection;
+
+#endif
diff --git a/nexus/Supplicant.cpp b/nexus/Supplicant.cpp
new file mode 100644
index 0000000..9bb6bf2
--- /dev/null
+++ b/nexus/Supplicant.cpp
@@ -0,0 +1,471 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define LOG_TAG "Supplicant"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "private/android_filesystem_config.h"
+
+#include <sysutils/ServiceManager.h>
+
+#include "Supplicant.h"
+#include "SupplicantListener.h"
+#include "NetworkManager.h"
+#include "ErrorCode.h"
+#include "WifiController.h"
+#include "SupplicantStatus.h"
+
+#include "libwpa_client/wpa_ctrl.h"
+
+#define IFACE_DIR        "/data/system/wpa_supplicant"
+#define DRIVER_PROP_NAME "wlan.driver.status"
+#define SUPPLICANT_SERVICE_NAME  "wpa_supplicant"
+#define SUPP_CONFIG_TEMPLATE "/system/etc/wifi/wpa_supplicant.conf"
+#define SUPP_CONFIG_FILE "/data/misc/wifi/wpa_supplicant.conf"
+
+Supplicant::Supplicant(WifiController *wc, ISupplicantEventHandler *handlers) {
+    mHandlers = handlers;
+    mController = wc;
+    mInterfaceName = NULL;
+    mCtrl = NULL;
+    mMonitor = NULL;
+    mListener = NULL;
+   
+    mServiceManager = new ServiceManager();
+
+    mNetworks = new WifiNetworkCollection();
+    pthread_mutex_init(&mNetworksLock, NULL);
+}
+
+Supplicant::~Supplicant() {
+    delete mServiceManager;
+    if (mInterfaceName)
+        free(mInterfaceName);
+}
+
+int Supplicant::start() {
+
+    if (setupConfig()) {
+        LOGW("Unable to setup supplicant.conf");
+    }
+
+    if (mServiceManager->start(SUPPLICANT_SERVICE_NAME)) {
+        LOGE("Error starting supplicant (%s)", strerror(errno));
+        return -1;
+    }
+
+    wpa_ctrl_cleanup();
+    if (connectToSupplicant()) {
+        LOGE("Error connecting to supplicant (%s)\n", strerror(errno));
+        return -1;
+    }
+    
+    if (retrieveInterfaceName()) {
+        LOGE("Error retrieving interface name (%s)\n", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int Supplicant::stop() {
+
+    if (mListener->stopListener()) {
+        LOGW("Unable to stop supplicant listener (%s)", strerror(errno));
+        return -1;
+    }
+
+    if (mServiceManager->stop(SUPPLICANT_SERVICE_NAME)) {
+        LOGW("Error stopping supplicant (%s)", strerror(errno));
+    }
+
+    if (mCtrl) {
+        wpa_ctrl_close(mCtrl);
+        mCtrl = NULL;
+    }
+    if (mMonitor) {
+        wpa_ctrl_close(mMonitor);
+        mMonitor = NULL;
+    }
+
+    return 0;
+}
+
+bool Supplicant::isStarted() {
+    return mServiceManager->isRunning(SUPPLICANT_SERVICE_NAME);
+}
+
+SupplicantStatus *Supplicant::getStatus() {
+    char *reply;
+    size_t len = 4096;
+
+    if (!(reply = (char *) malloc(len))) {
+        errno = ENOMEM;
+        return NULL;
+    }
+
+    if (sendCommand("STATUS", reply, &len)) {
+        free(reply);
+        return NULL;
+    }
+
+    SupplicantStatus *ss = SupplicantStatus::createStatus(reply, len);
+  
+    free (reply);
+    return ss;
+}
+
+/*
+ * Retrieves the list of networks from Supplicant
+ * and merge them into our current list
+ */
+int Supplicant::refreshNetworkList() {
+    char *reply;
+    size_t len = 4096;
+
+    if (!(reply = (char *) malloc(len))) {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    if (sendCommand("LIST_NETWORKS", reply, &len)) {
+        free(reply);
+        return -1;
+    }
+
+    char *linep;
+    char *linep_next = NULL;
+
+    if (!strtok_r(reply, "\n", &linep_next)) {
+        LOGW("Malformatted network list\n");
+        free(reply);
+        errno = EIO;
+        return -1;
+    }
+
+    pthread_mutex_lock(&mNetworksLock);
+
+    int num_added = 0;
+    int num_refreshed = 0;
+    int num_removed = 0;
+    while((linep = strtok_r(NULL, "\n", &linep_next))) {
+        // TODO: Move the decode into a static method so we
+        // don't create new_wn when we don't have to.
+        WifiNetwork *new_wn = new WifiNetwork(mController, this, linep);
+        WifiNetwork *merge_wn;
+
+        if ((merge_wn = this->lookupNetwork_UNLOCKED(new_wn->getNetworkId()))) {
+            num_refreshed++;
+            if (merge_wn->refresh()) {
+                LOGW("Error refreshing network %d (%s)",
+                     merge_wn->getNetworkId(), strerror(errno));
+                }
+            delete new_wn;
+        } else {
+            num_added++;
+            new_wn->registerProperties();
+            mNetworks->push_back(new_wn);
+            if (new_wn->refresh()) {
+                LOGW("Unable to refresh network id %d (%s)",
+                    new_wn->getNetworkId(), strerror(errno));
+            }
+        }
+    }
+
+    if (!mNetworks->empty()) {
+        // TODO: Add support for detecting removed networks
+        WifiNetworkCollection::iterator i;
+
+        for (i = mNetworks->begin(); i != mNetworks->end(); ++i) {
+            if (0) {
+                num_removed++;
+                (*i)->unregisterProperties();
+                delete (*i);
+                i = mNetworks->erase(i);
+            }
+        }
+    }
+
+
+    LOGD("Networks added %d, refreshed %d, removed %d\n", 
+         num_added, num_refreshed, num_removed);
+    pthread_mutex_unlock(&mNetworksLock);
+
+    free(reply);
+    return 0;
+}
+
+int Supplicant::connectToSupplicant() {
+    if (!isStarted())
+        LOGW("Supplicant service not running");
+
+    mCtrl = wpa_ctrl_open("tiwlan0"); // XXX:
+    if (mCtrl == NULL) {
+        LOGE("Unable to open connection to supplicant on \"%s\": %s",
+             "tiwlan0", strerror(errno));
+        return -1;
+    }
+    mMonitor = wpa_ctrl_open("tiwlan0");
+    if (mMonitor == NULL) {
+        wpa_ctrl_close(mCtrl);
+        mCtrl = NULL;
+        return -1;
+    }
+    if (wpa_ctrl_attach(mMonitor) != 0) {
+        wpa_ctrl_close(mMonitor);
+        wpa_ctrl_close(mCtrl);
+        mCtrl = mMonitor = NULL;
+        return -1;
+    }
+
+    mListener = new SupplicantListener(mHandlers, mMonitor);
+
+    if (mListener->startListener()) {
+        LOGE("Error - unable to start supplicant listener");
+        stop();
+        return -1;
+    }
+    return 0;
+}
+
+int Supplicant::sendCommand(const char *cmd, char *reply, size_t *reply_len)
+{
+    if (!mCtrl) {
+        errno = ENOTCONN;
+        return -1;
+    }
+
+//    LOGD("sendCommand(): -> '%s'", cmd);
+
+    int rc;
+    memset(reply, 0, *reply_len);
+    if ((rc = wpa_ctrl_request(mCtrl, cmd, strlen(cmd), reply, reply_len, NULL)) == -2)  {
+        errno = ETIMEDOUT;
+        return -1;
+    } else if (rc < 0 || !strncmp(reply, "FAIL", 4)) {
+        strcpy(reply, "FAIL");
+        errno = EIO;
+        return -1;
+    }
+
+//   LOGD("sendCommand(): <- '%s'", reply);
+    return 0;
+}
+
+int Supplicant::triggerScan(bool active) {
+    char reply[255];
+    size_t len = sizeof(reply);
+
+    if (sendCommand((active ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"),
+                     reply, &len)) {
+        LOGW("triggerScan(%d): Error setting scan mode (%s)", active,
+             strerror(errno));
+        return -1;
+    }
+    len = sizeof(reply);
+
+    if (sendCommand("SCAN", reply, &len)) {
+        LOGW("triggerScan(%d): Error initiating scan", active);
+        return -1;
+    }
+    return 0;
+}
+
+WifiNetwork *Supplicant::createNetwork() {
+    char reply[255];
+    size_t len = sizeof(reply) -1;
+
+    if (sendCommand("ADD_NETWORK", reply, &len))
+        return NULL;
+
+    if (reply[strlen(reply) -1] == '\n')
+        reply[strlen(reply) -1] = '\0';
+
+    WifiNetwork *wn = new WifiNetwork(mController, this, atoi(reply));
+    pthread_mutex_lock(&mNetworksLock);
+    mNetworks->push_back(wn);
+    pthread_mutex_unlock(&mNetworksLock);
+    return wn;
+}
+
+int Supplicant::removeNetwork(WifiNetwork *wn) {
+    char req[64];
+
+    sprintf(req, "REMOVE_NETWORK %d", wn->getNetworkId());
+    char reply[32];
+    size_t len = sizeof(reply) -1;
+
+    if (sendCommand(req, reply, &len))
+        return -1;
+
+    pthread_mutex_lock(&mNetworksLock);
+    WifiNetworkCollection::iterator it;
+    for (it = mNetworks->begin(); it != mNetworks->end(); ++it) {
+        if ((*it) == wn) {
+            mNetworks->erase(it);
+            break;
+        }
+    }
+    pthread_mutex_unlock(&mNetworksLock);
+    return 0;
+}
+
+WifiNetwork *Supplicant::lookupNetwork(int networkId) {
+    pthread_mutex_lock(&mNetworksLock);
+    WifiNetwork *wn = lookupNetwork_UNLOCKED(networkId);
+    pthread_mutex_unlock(&mNetworksLock);
+    return wn;
+}
+
+WifiNetwork *Supplicant::lookupNetwork_UNLOCKED(int networkId) {
+    WifiNetworkCollection::iterator it;
+    for (it = mNetworks->begin(); it != mNetworks->end(); ++it) {
+        if ((*it)->getNetworkId() == networkId) {
+            return *it;
+        }
+    }
+    errno = ENOENT;
+    return NULL;
+}
+
+WifiNetworkCollection *Supplicant::createNetworkList() {
+    WifiNetworkCollection *d = new WifiNetworkCollection();
+    WifiNetworkCollection::iterator i;
+
+    pthread_mutex_lock(&mNetworksLock);
+    for (i = mNetworks->begin(); i != mNetworks->end(); ++i)
+        d->push_back((*i)->clone());
+
+    pthread_mutex_unlock(&mNetworksLock);
+    return d;
+}
+
+int Supplicant::setupConfig() {
+    char buf[2048];
+    int srcfd, destfd;
+    int nread;
+
+    if (access(SUPP_CONFIG_FILE, R_OK|W_OK) == 0) {
+        return 0;
+    } else if (errno != ENOENT) {
+        LOGE("Cannot access \"%s\": %s", SUPP_CONFIG_FILE, strerror(errno));
+        return -1;
+    }
+
+    srcfd = open(SUPP_CONFIG_TEMPLATE, O_RDONLY);
+    if (srcfd < 0) {
+        LOGE("Cannot open \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
+        return -1;
+    }
+
+    destfd = open(SUPP_CONFIG_FILE, O_CREAT|O_WRONLY, 0660);
+    if (destfd < 0) {
+        close(srcfd);
+        LOGE("Cannot create \"%s\": %s", SUPP_CONFIG_FILE, strerror(errno));
+        return -1;
+    }
+
+    while ((nread = read(srcfd, buf, sizeof(buf))) != 0) {
+        if (nread < 0) {
+            LOGE("Error reading \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
+            close(srcfd);
+            close(destfd);
+            unlink(SUPP_CONFIG_FILE);
+            return -1;
+        }
+        write(destfd, buf, nread);
+    }
+
+    close(destfd);
+    close(srcfd);
+
+    if (chown(SUPP_CONFIG_FILE, AID_SYSTEM, AID_WIFI) < 0) {
+        LOGE("Error changing group ownership of %s to %d: %s",
+             SUPP_CONFIG_FILE, AID_WIFI, strerror(errno));
+        unlink(SUPP_CONFIG_FILE);
+        return -1;
+    }
+    return 0;
+}
+
+int Supplicant::setNetworkVar(int networkId, const char *var, const char *val) {
+    char reply[255];
+    size_t len = sizeof(reply) -1;
+
+    char *tmp;
+    asprintf(&tmp, "SET_NETWORK %d %s \"%s\"", networkId, var, val);
+    if (sendCommand(tmp, reply, &len)) {
+        free(tmp);
+        return -1;
+    }
+    free(tmp);
+
+    len = sizeof(reply) -1;
+    if (sendCommand("SAVE_CONFIG", reply, &len)) {
+        LOGE("Error saving config after %s = %s", var, val);
+        return -1;
+    }
+    return 0;
+}
+
+const char *Supplicant::getNetworkVar(int networkId, const char *var,
+                                      char *buffer, size_t max) {
+    size_t len = max - 1;
+    char *tmp;
+
+    asprintf(&tmp, "GET_NETWORK %d %s", networkId, var);
+    if (sendCommand(tmp, buffer, &len)) {
+        free(tmp);
+        return NULL;
+    }
+    free(tmp);
+    return buffer;
+}
+
+int Supplicant::enableNetwork(int networkId, bool enabled) {
+    char req[64];
+
+    if (enabled)
+        sprintf(req, "ENABLE_NETWORK %d", networkId);
+    else
+        sprintf(req, "DISABLE_NETWORK %d", networkId);
+
+    char reply[16];
+    size_t len = sizeof(reply) -1;
+
+    if (sendCommand(req, reply, &len))
+        return -1;
+    return 0;
+}
+
+
+int Supplicant::retrieveInterfaceName() {
+    char reply[255];
+    size_t len = sizeof(reply) -1;
+
+    if (sendCommand("INTERFACES", reply, &len))
+        return -1;
+
+    reply[strlen(reply)-1] = '\0';
+    mInterfaceName = strdup(reply);
+    return 0;
+}
diff --git a/nexus/Supplicant.h b/nexus/Supplicant.h
new file mode 100644
index 0000000..3efbe4c
--- /dev/null
+++ b/nexus/Supplicant.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#ifndef _SUPPLICANT_H
+#define _SUPPLICANT_H
+
+struct wpa_ctrl;
+class SupplicantListener;
+class ServiceManager;
+class Controller;
+class WifiController;
+class SupplicantStatus;
+
+#include <pthread.h>
+
+#include "WifiNetwork.h"
+#include "ISupplicantEventHandler.h"
+
+class Supplicant {
+private:
+    struct wpa_ctrl      *mCtrl;
+    struct wpa_ctrl      *mMonitor;
+    SupplicantListener   *mListener;
+    ServiceManager       *mServiceManager;
+    WifiController       *mController;
+    char                 *mInterfaceName;
+
+    WifiNetworkCollection   *mNetworks;
+    pthread_mutex_t         mNetworksLock;
+    ISupplicantEventHandler *mHandlers;
+ 
+public:
+    Supplicant(WifiController *wc, ISupplicantEventHandler *handlers);
+    virtual ~Supplicant();
+
+    int start();
+    int stop();
+    bool isStarted();
+
+    int triggerScan(bool active);
+
+    WifiNetwork *createNetwork();
+    WifiNetwork *lookupNetwork(int networkId);
+    int removeNetwork(WifiNetwork *net);
+    WifiNetworkCollection *createNetworkList();
+    int refreshNetworkList();
+
+    int setNetworkVar(int networkId, const char *var, const char *value);
+    const char *getNetworkVar(int networkid, const char *var, char *buffer,
+                              size_t max);
+    int enableNetwork(int networkId, bool enabled);
+
+    SupplicantStatus *getStatus();
+
+    Controller *getController() { return (Controller *) mController; }
+    const char *getInterfaceName() { return mInterfaceName; }
+
+    int sendCommand(const char *cmd, char *reply, size_t *reply_len);
+
+private:
+    int connectToSupplicant();
+    int setupConfig();
+    int retrieveInterfaceName();
+    WifiNetwork *lookupNetwork_UNLOCKED(int networkId);
+};
+
+#endif
diff --git a/nexus/SupplicantAssociatedEvent.cpp b/nexus/SupplicantAssociatedEvent.cpp
new file mode 100644
index 0000000..e40411e
--- /dev/null
+++ b/nexus/SupplicantAssociatedEvent.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 <stdlib.h>
+
+#define LOG_TAG "SupplicantAssociatedEvent"
+#include <cutils/log.h>
+
+#include "SupplicantAssociatedEvent.h"
+
+SupplicantAssociatedEvent::SupplicantAssociatedEvent(int level, char *event,
+                                                     size_t len) :
+                           SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATED,
+                                           level) {
+    char *p = event;
+
+    // "00:13:46:40:40:aa"
+    mBssid = (char *) malloc(18);
+    strncpy(mBssid, p, 17);
+    mBssid[17] = '\0';
+}
+
+SupplicantAssociatedEvent::~SupplicantAssociatedEvent() {
+    if (mBssid)
+        free(mBssid);
+}
+
diff --git a/nexus/SupplicantAssociatedEvent.h b/nexus/SupplicantAssociatedEvent.h
new file mode 100644
index 0000000..aa33c59
--- /dev/null
+++ b/nexus/SupplicantAssociatedEvent.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef _SupplicantAssociatedEvent_H
+#define _SupplicantAssociatedEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantAssociatedEvent : public SupplicantEvent {
+    char *mBssid;
+    char *mSsid;
+    int  mFreq;
+
+public:
+    SupplicantAssociatedEvent(int level, char *event, size_t len);
+    virtual ~SupplicantAssociatedEvent();
+
+    const char *getBssid() { return mBssid; }
+};
+
+#endif
diff --git a/nexus/SupplicantAssociatingEvent.cpp b/nexus/SupplicantAssociatingEvent.cpp
new file mode 100644
index 0000000..c6e9fe3
--- /dev/null
+++ b/nexus/SupplicantAssociatingEvent.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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 <stdlib.h>
+
+#define LOG_TAG "SupplicantAssociatingEvent"
+#include <cutils/log.h>
+
+#include "SupplicantAssociatingEvent.h"
+
+SupplicantAssociatingEvent::SupplicantAssociatingEvent(int level, char *event,
+                                                     size_t len) :
+                           SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATING,
+                                           level) {
+    char *p = event;
+
+    mBssid = NULL;
+    mSsid = NULL;
+    mFreq = -1;
+
+    // SSID 'default' 
+    // OR
+    // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)"
+
+    if (strncmp(event, "SSID", 4)) {
+        mBssid = (char *) malloc(18);
+        strncpy(mBssid, p, 17);
+        mBssid[17] = '\0';
+        p += 25;
+
+        // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)"
+        //                           ^
+        //                           p
+        char *q = index(p, '\'');
+        if (!q) {
+            LOGE("Unable to decode SSID (p = {%s})\n", p);
+            return;
+        }
+        mSsid = (char *) malloc((q - p) +1);
+        strncpy(mSsid, p, q-p);
+        mSsid[q-p] = '\0';
+
+        p = q + 7;
+    
+        // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)"
+        //                                         ^
+        //                                         p
+        if (!(q = index(p, ' '))) {
+            LOGE("Unable to decode frequency\n");
+            return;
+        }
+        *q = '\0';
+        mFreq = atoi(p);
+    } else {
+        p+= 6;
+
+        // SSID 'default' 
+        //       ^
+        //       p
+
+        char *q = index(p, '\'');
+        if (!q) {
+            LOGE("Unable to decode SSID (p = {%s})\n", p);
+            return;
+        }
+        mSsid = (char *) malloc((q - p) +1);
+        strncpy(mSsid, p, q-p);
+        mSsid[q-p] = '\0';
+    }
+}
+
+SupplicantAssociatingEvent::SupplicantAssociatingEvent(const char *bssid, 
+                                                     const char *ssid,
+                                                     int freq) :
+                           SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATING, -1) {
+    mBssid = strdup(bssid);
+    mSsid= strdup(ssid);
+    mFreq = freq;
+}
+
+SupplicantAssociatingEvent::~SupplicantAssociatingEvent() {
+    if (mBssid)
+        free(mBssid);
+    if (mSsid)
+        free(mSsid);
+}
+
diff --git a/nexus/SupplicantAssociatingEvent.h b/nexus/SupplicantAssociatingEvent.h
new file mode 100644
index 0000000..d3a4d5c
--- /dev/null
+++ b/nexus/SupplicantAssociatingEvent.h
@@ -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.
+ */
+
+#ifndef _SupplicantAssociatingEvent_H
+#define _SupplicantAssociatingEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantAssociatingEvent : public SupplicantEvent {
+    char *mBssid;
+    char *mSsid;
+    int  mFreq;
+
+public:
+    SupplicantAssociatingEvent(int level, char *event, size_t len);
+    SupplicantAssociatingEvent(const char *bssid, const char *ssid, int freq);
+    virtual ~SupplicantAssociatingEvent();
+
+    const char *getBssid() { return mBssid; }
+    const char *getSsid() { return mSsid; }
+    int getFreq() { return mFreq;}
+};
+
+#endif
diff --git a/nexus/SupplicantConnectedEvent.cpp b/nexus/SupplicantConnectedEvent.cpp
new file mode 100644
index 0000000..e58bab2
--- /dev/null
+++ b/nexus/SupplicantConnectedEvent.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SupplicantConnectedEvent"
+#include <cutils/log.h>
+
+#include "SupplicantConnectedEvent.h"
+
+SupplicantConnectedEvent::SupplicantConnectedEvent(int level, char *event,
+                                                   size_t len) :
+                          SupplicantEvent(SupplicantEvent::EVENT_CONNECTED,
+                                          level) {
+    char *p;
+
+    //  "- Connection to 00:13:46:40:40:aa completed (auth) [id=1 id_str=], 89"
+    
+    if ((p = index(event + 2, ' ')) && (++p = index(p, ' '))) {
+        mBssid = (char *) malloc(18);
+        strncpy(mBssid, ++p, 17);
+        mBssid[17] = '\0';
+
+        //  "- Connection to 00:13:46:40:40:aa completed (auth) [id=1 id_str=], 89"
+        //                   ^
+        //                   p
+        
+        if ((p = index(p, ' ')) && ((++p = index(p, ' ')))) {
+            if (!strncmp(++p, "(auth)", 6))
+                mReassociated = false;
+            else
+                mReassociated = true;
+        } else
+            LOGE("Unable to decode re-assocation");
+    } else
+        LOGE("Unable to decode event");
+}
+
+SupplicantConnectedEvent::SupplicantConnectedEvent(const char *bssid, 
+                                                   bool reassocated) :
+                          SupplicantEvent(SupplicantEvent::EVENT_CONNECTED, -1) {
+    mBssid = strdup(bssid);
+    mReassociated = reassocated;
+}
+
+SupplicantConnectedEvent::~SupplicantConnectedEvent() {
+    if (mBssid)
+        free(mBssid);
+}
+
diff --git a/nexus/SupplicantConnectedEvent.h b/nexus/SupplicantConnectedEvent.h
new file mode 100644
index 0000000..03e9842
--- /dev/null
+++ b/nexus/SupplicantConnectedEvent.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef _SupplicantConnectedEvent_H
+#define _SupplicantConnectedEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantConnectedEvent : public SupplicantEvent {
+private:
+    char *mBssid;
+    bool mReassociated;
+
+public:
+    SupplicantConnectedEvent(int level, char *event, size_t len);
+    SupplicantConnectedEvent(const char *bssid, bool reassicated);
+    virtual ~SupplicantConnectedEvent();
+
+    const char *getBssid() { return mBssid; }
+    bool getReassociated() { return mReassociated; }
+};
+
+#endif
diff --git a/nexus/SupplicantConnectionTimeoutEvent.cpp b/nexus/SupplicantConnectionTimeoutEvent.cpp
new file mode 100644
index 0000000..8b8a7d7
--- /dev/null
+++ b/nexus/SupplicantConnectionTimeoutEvent.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SupplicantConnectionTimeoutEvent"
+#include <cutils/log.h>
+
+#include "SupplicantConnectionTimeoutEvent.h"
+
+SupplicantConnectionTimeoutEvent::SupplicantConnectionTimeoutEvent(int level, char *event,
+                                                   size_t len) :
+                          SupplicantEvent(SupplicantEvent::EVENT_CONNECTIONTIMEOUT,
+                                          level) {
+    // 00:13:46:40:40:aa timed out.'
+    mBssid = (char *) malloc(18);
+    strncpy(mBssid, event, 17);
+    mBssid[17] = '\0';
+}
+
+SupplicantConnectionTimeoutEvent::~SupplicantConnectionTimeoutEvent() {
+    if (mBssid)
+        free(mBssid);
+}
+
diff --git a/nexus/SupplicantConnectionTimeoutEvent.h b/nexus/SupplicantConnectionTimeoutEvent.h
new file mode 100644
index 0000000..0d2606d
--- /dev/null
+++ b/nexus/SupplicantConnectionTimeoutEvent.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef _SupplicantConnectionTimeoutEvent_H
+#define _SupplicantConnectionTimeoutEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantConnectionTimeoutEvent : public SupplicantEvent {
+private:
+    char *mBssid;
+    bool mReassociated;
+
+public:
+    SupplicantConnectionTimeoutEvent(int level, char *event, size_t len);
+    virtual ~SupplicantConnectionTimeoutEvent();
+
+    const char *getBssid() { return mBssid; }
+};
+
+#endif
diff --git a/nexus/SupplicantDisconnectedEvent.cpp b/nexus/SupplicantDisconnectedEvent.cpp
new file mode 100644
index 0000000..11e499d
--- /dev/null
+++ b/nexus/SupplicantDisconnectedEvent.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SupplicantDisconnectedEvent"
+#include <cutils/log.h>
+
+#include "SupplicantDisconnectedEvent.h"
+
+SupplicantDisconnectedEvent::SupplicantDisconnectedEvent(int level, char *event,
+                                                       size_t len) :
+                            SupplicantEvent(SupplicantEvent::EVENT_DISCONNECTED,
+                                            level) {
+}
+
+SupplicantDisconnectedEvent::SupplicantDisconnectedEvent() :
+                            SupplicantEvent(SupplicantEvent::EVENT_DISCONNECTED, -1) {
+}
+
+SupplicantDisconnectedEvent::~SupplicantDisconnectedEvent() {
+}
diff --git a/nexus/SupplicantDisconnectedEvent.h b/nexus/SupplicantDisconnectedEvent.h
new file mode 100644
index 0000000..c09ec62
--- /dev/null
+++ b/nexus/SupplicantDisconnectedEvent.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _SupplicantDisconnectedEvent_H
+#define _SupplicantDisconnectedEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantDisconnectedEvent : public SupplicantEvent {
+
+public:
+    SupplicantDisconnectedEvent(int level, char *event, size_t len);
+    SupplicantDisconnectedEvent();
+    virtual ~SupplicantDisconnectedEvent();
+};
+
+#endif
diff --git a/nexus/SupplicantEvent.cpp b/nexus/SupplicantEvent.cpp
new file mode 100644
index 0000000..faf7b45
--- /dev/null
+++ b/nexus/SupplicantEvent.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 <stdlib.h>
+
+#define LOG_TAG "SupplicantEvent"
+#include <cutils/log.h>
+
+#include "SupplicantEvent.h"
+
+#include "libwpa_client/wpa_ctrl.h"
+
+SupplicantEvent::SupplicantEvent(int type, int level) {
+    mType = type;
+    mLevel = level;
+}
diff --git a/nexus/SupplicantEvent.h b/nexus/SupplicantEvent.h
new file mode 100644
index 0000000..9d7cbd9
--- /dev/null
+++ b/nexus/SupplicantEvent.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef _SUPPLICANT_EVENT_H
+#define _SUPPLICANT_EVENT_H
+
+#include <sys/types.h>
+
+class SupplicantEvent {
+private:
+    int mType;
+    int mLevel;
+
+public:
+    static const int EVENT_UNKNOWN           = 0;
+    static const int EVENT_CONNECTED         = 1;
+    static const int EVENT_DISCONNECTED      = 2;
+    static const int EVENT_TERMINATING       = 3;
+    static const int EVENT_PASSWORD_CHANGED  = 4;
+    static const int EVENT_EAP_NOTIFICATION  = 5;
+    static const int EVENT_EAP_STARTED       = 6;
+    static const int EVENT_EAP_METHOD        = 7;
+    static const int EVENT_EAP_SUCCESS       = 8;
+    static const int EVENT_EAP_FAILURE       = 9;
+    static const int EVENT_SCAN_RESULTS      = 10;
+    static const int EVENT_STATE_CHANGE      = 11;
+    static const int EVENT_LINK_SPEED        = 12;
+    static const int EVENT_DRIVER_STATE      = 13;
+    static const int EVENT_ASSOCIATING       = 14;
+    static const int EVENT_ASSOCIATED        = 15;
+    static const int EVENT_CONNECTIONTIMEOUT = 16;
+
+public:
+    SupplicantEvent(int type, int level);
+    virtual ~SupplicantEvent() {}
+
+    int getType() { return mType; }
+    int getLevel() { return mLevel; }
+};
+
+#endif
diff --git a/nexus/SupplicantEventFactory.cpp b/nexus/SupplicantEventFactory.cpp
new file mode 100644
index 0000000..8695aca
--- /dev/null
+++ b/nexus/SupplicantEventFactory.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 <stdlib.h>
+
+#define LOG_TAG "SupplicantEventFactory"
+#include <cutils/log.h>
+
+#include "SupplicantEvent.h"
+#include "SupplicantEventFactory.h"
+#include "SupplicantAssociatingEvent.h"
+#include "SupplicantAssociatedEvent.h"
+#include "SupplicantConnectedEvent.h"
+#include "SupplicantStateChangeEvent.h"
+#include "SupplicantScanResultsEvent.h"
+#include "SupplicantConnectionTimeoutEvent.h"
+#include "SupplicantDisconnectedEvent.h"
+#if 0
+#include "SupplicantTerminatingEvent.h"
+#include "SupplicantPasswordChangedEvent.h"
+#include "SupplicantEapNotificationEvent.h"
+#include "SupplicantEapStartedEvent.h"
+#include "SupplicantEapMethodEvent.h"
+#include "SupplicantEapSuccessEvent.h"
+#include "SupplicantEapFailureEvent.h"
+#include "SupplicantLinkSpeedEvent.h"
+#include "SupplicantDriverStateEvent.h"
+#endif
+
+#include "libwpa_client/wpa_ctrl.h"
+
+SupplicantEventFactory::SupplicantEventFactory() {
+}
+
+SupplicantEvent *SupplicantEventFactory::createEvent(char *event, size_t len) {
+    int level = 0;
+
+    if (event[0] == '<') {
+        char *match = strchr(event, '>');
+        if (match) {
+            char tmp[16];
+
+            strncpy(tmp, &event[1], (match - event));
+            level = atoi(tmp);
+            event += (match - event) + 1;
+        } else
+            LOGW("Unclosed level brace in event");
+    } else
+        LOGW("No level specified in event");
+
+    /*
+     * <N>CTRL-EVENT-XXX
+     *    ^
+     *    +---- event
+     */
+
+    if (!strncmp(event, "Authentication with ", 20)) {
+        if (!strcmp(event + strlen(event) - strlen(" timed out."),
+                    " timed out.")) {
+            return new SupplicantConnectionTimeoutEvent(level,
+                                                        event + 20,
+                                                        len);
+        } else
+            return NULL;
+        
+    } else if (!strncmp(event, "Associated with ", 16))
+        return new SupplicantAssociatedEvent(level, event + 16, len);
+    else if (!strncmp(event, "Trying to associate with ", 25))
+        return new SupplicantAssociatingEvent(level, event + 25, len);
+    else if (!strncmp(event, WPA_EVENT_CONNECTED, strlen(WPA_EVENT_CONNECTED))) {
+        return new SupplicantConnectedEvent(level,
+                                            event + strlen(WPA_EVENT_CONNECTED),
+                                            len);
+    } else if (!strncmp(event, WPA_EVENT_SCAN_RESULTS, strlen(WPA_EVENT_SCAN_RESULTS))) {
+        return new SupplicantScanResultsEvent(level,
+                                              event + strlen(WPA_EVENT_SCAN_RESULTS),
+                                              len);
+    } else if (!strncmp(event, WPA_EVENT_STATE_CHANGE, strlen(WPA_EVENT_STATE_CHANGE))) {
+        return new SupplicantStateChangeEvent(level,
+                                              event + strlen(WPA_EVENT_STATE_CHANGE),
+                                              len);
+    }
+    else if (!strncmp(event, WPA_EVENT_DISCONNECTED, strlen(WPA_EVENT_DISCONNECTED)))
+        return new SupplicantDisconnectedEvent(level, event, len);
+#if 0
+    else if (!strncmp(event, WPA_EVENT_TERMINATING, strlen(WPA_EVENT_TERMINATING)))
+        return new SupplicantTerminatingEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_PASSWORD_CHANGED, strlen(WPA_EVENT_PASSWORD_CHANGED)))
+        return new SupplicantPasswordChangedEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_NOTIFICATION, strlen(WPA_EVENT_EAP_NOTIFICATION)))
+        return new SupplicantEapNotificationEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_STARTED, strlen(WPA_EVENT_EAP_STARTED)))
+        return new SupplicantEapStartedEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_METHOD, strlen(WPA_EVENT_EAP_METHOD)))
+        return new SupplicantEapMethodEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_SUCCESS, strlen(WPA_EVENT_EAP_SUCCESS)))
+        return new SupplicantEapSuccessEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_FAILURE, strlen(WPA_EVENT_EAP_FAILURE)))
+        return new SupplicantEapFailureEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_LINK_SPEED, strlen(WPA_EVENT_LINK_SPEED)))
+        return new SupplicantLinkSpeedEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_DRIVER_STATE, strlen(WPA_EVENT_DRIVER_STATE)))
+         return new SupplicantDriverStateEvent(event, len);
+#endif
+    return NULL;
+}
diff --git a/nexus/SupplicantEventFactory.h b/nexus/SupplicantEventFactory.h
new file mode 100644
index 0000000..22e5707
--- /dev/null
+++ b/nexus/SupplicantEventFactory.h
@@ -0,0 +1,31 @@
+
+/*
+ * 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.
+ */
+
+#ifndef _SupplicantEventFactory_H
+#define _SupplicantEventFactory_H
+
+class SupplicantEvent;
+
+class SupplicantEventFactory {
+public:
+    SupplicantEventFactory();
+    virtual ~SupplicantEventFactory() {}
+
+    SupplicantEvent *createEvent(char *event, size_t len);
+};
+
+#endif
diff --git a/nexus/SupplicantListener.cpp b/nexus/SupplicantListener.cpp
new file mode 100644
index 0000000..b91fc02
--- /dev/null
+++ b/nexus/SupplicantListener.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 <errno.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#define LOG_TAG "SupplicantListener"
+#include <cutils/log.h>
+
+#include "libwpa_client/wpa_ctrl.h"
+
+#include "SupplicantListener.h"
+#include "ISupplicantEventHandler.h"
+#include "SupplicantEventFactory.h"
+#include "SupplicantEvent.h"
+#include "SupplicantAssociatingEvent.h"
+#include "SupplicantAssociatedEvent.h"
+#include "SupplicantConnectedEvent.h"
+#include "SupplicantScanResultsEvent.h"
+#include "SupplicantStateChangeEvent.h"
+
+SupplicantListener::SupplicantListener(ISupplicantEventHandler *handlers, 
+                                       struct wpa_ctrl *monitor) :
+                    SocketListener(wpa_ctrl_get_fd(monitor), false) {
+    mHandlers = handlers;
+    mMonitor = monitor;
+    mFactory = new SupplicantEventFactory();
+}
+
+bool SupplicantListener::onDataAvailable(SocketClient *cli) {
+    char buf[255];
+    size_t buflen = sizeof(buf);
+    int rc;
+    size_t nread = buflen - 1;
+
+    if ((rc = wpa_ctrl_recv(mMonitor, buf, &nread))) {
+        LOGE("wpa_ctrl_recv failed (%s)", strerror(errno));
+        return false;
+    }
+
+    buf[nread] = '\0';
+    if (!rc && !nread) {
+        LOGD("Received EOF on supplicant socket\n");
+        strncpy(buf, WPA_EVENT_TERMINATING " - signal 0 received", buflen-1);
+        buf[buflen-1] = '\0';
+        return false;
+    }
+
+    SupplicantEvent *evt = mFactory->createEvent(buf, nread);
+
+    if (!evt) {
+        LOGW("Dropping unknown supplicant event '%s'", buf);
+        return true;
+    }
+
+    // Call the appropriate handler
+    if (evt->getType() == SupplicantEvent::EVENT_ASSOCIATING)
+        mHandlers->onAssociatingEvent((SupplicantAssociatingEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_ASSOCIATED)
+        mHandlers->onAssociatedEvent((SupplicantAssociatedEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_CONNECTED)
+        mHandlers->onConnectedEvent((SupplicantConnectedEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_SCAN_RESULTS)
+        mHandlers->onScanResultsEvent((SupplicantScanResultsEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_STATE_CHANGE)
+        mHandlers->onStateChangeEvent((SupplicantStateChangeEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_CONNECTIONTIMEOUT)
+        mHandlers->onConnectionTimeoutEvent((SupplicantConnectionTimeoutEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_DISCONNECTED)
+        mHandlers->onDisconnectedEvent((SupplicantDisconnectedEvent *) evt);
+    else
+        LOGW("Whoops - no handler available for event '%s'\n", buf);
+#if 0
+    else if (evt->getType() == SupplicantEvent::EVENT_TERMINATING)
+        mHandlers->onTerminatingEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_PASSWORD_CHANGED)
+        mHandlers->onPasswordChangedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_NOTIFICATION)
+        mHandlers->onEapNotificationEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_STARTED)
+        mHandlers->onEapStartedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_SUCCESS)
+        mHandlers->onEapSuccessEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_FAILURE)
+        mHandlers->onEapFailureEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_LINK_SPEED)
+        mHandlers->onLinkSpeedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_DRIVER_STATE)
+        mHandlers->onDriverStateEvent(evt);
+#endif
+
+    delete evt;
+
+    return true;
+}
diff --git a/nexus/SupplicantListener.h b/nexus/SupplicantListener.h
new file mode 100644
index 0000000..a1da773
--- /dev/null
+++ b/nexus/SupplicantListener.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef _SUPPLICANTLISTENER_H__
+#define _SUPPLICANTLISTENER_H__
+
+#include <sysutils/SocketListener.h>
+
+struct wpa_ctrl;
+class Supplicant;
+class SocketClient;
+class ISupplicantEventHandler;
+class SupplicantEventFactory;
+
+class SupplicantListener: public SocketListener {
+    struct wpa_ctrl         *mMonitor;
+    ISupplicantEventHandler *mHandlers;
+    SupplicantEventFactory  *mFactory;
+    
+public:
+    SupplicantListener(ISupplicantEventHandler *handlers,
+                       struct wpa_ctrl *monitor);
+    virtual ~SupplicantListener() {}
+
+    struct wpa_ctrl *getMonitor() { return mMonitor; }
+
+protected:
+    virtual bool onDataAvailable(SocketClient *c);
+};
+
+#endif
diff --git a/nexus/SupplicantScanResultsEvent.cpp b/nexus/SupplicantScanResultsEvent.cpp
new file mode 100644
index 0000000..c53adad
--- /dev/null
+++ b/nexus/SupplicantScanResultsEvent.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SupplicantScanResultsEvent"
+#include <cutils/log.h>
+
+#include "SupplicantScanResultsEvent.h"
+
+SupplicantScanResultsEvent::SupplicantScanResultsEvent(int level, char *event,
+                                                       size_t len) :
+                            SupplicantEvent(SupplicantEvent::EVENT_SCAN_RESULTS,
+                                            level) {
+}
+
+SupplicantScanResultsEvent::SupplicantScanResultsEvent() :
+                            SupplicantEvent(SupplicantEvent::EVENT_SCAN_RESULTS, -1) {
+}
+
+SupplicantScanResultsEvent::~SupplicantScanResultsEvent() {
+}
+
diff --git a/nexus/SupplicantScanResultsEvent.h b/nexus/SupplicantScanResultsEvent.h
new file mode 100644
index 0000000..5f82041
--- /dev/null
+++ b/nexus/SupplicantScanResultsEvent.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _SupplicantScanResultsEvent_H
+#define _SupplicantScanResultsEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantScanResultsEvent : public SupplicantEvent {
+
+public:
+    SupplicantScanResultsEvent(int level, char *event, size_t len);
+    SupplicantScanResultsEvent();
+    virtual ~SupplicantScanResultsEvent();
+};
+
+#endif
diff --git a/nexus/SupplicantState.cpp b/nexus/SupplicantState.cpp
new file mode 100644
index 0000000..a16d370
--- /dev/null
+++ b/nexus/SupplicantState.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 <stdio.h>
+
+#define LOG_TAG "SupplicantState"
+#include <cutils/log.h>
+
+#include "SupplicantState.h"
+
+char *SupplicantState::toString(int val, char *buffer, int max) {
+    if (val == SupplicantState::UNKNOWN)
+        strncpy(buffer, "Unknown", max);
+    else if (val == SupplicantState::DISCONNECTED)
+        strncpy(buffer, "Disconnected", max);
+    else if (val == SupplicantState::INACTIVE)
+        strncpy(buffer, "Inactive", max);
+    else if (val == SupplicantState::SCANNING)
+        strncpy(buffer, "Scanning", max);
+    else if (val == SupplicantState::ASSOCIATING)
+        strncpy(buffer, "Associating", max);
+    else if (val == SupplicantState::ASSOCIATED)
+        strncpy(buffer, "Associated", max);
+    else if (val == SupplicantState::FOURWAY_HANDSHAKE)
+        strncpy(buffer, "Fourway Handshake", max);
+    else if (val == SupplicantState::GROUP_HANDSHAKE)
+        strncpy(buffer, "Group Handshake", max);
+    else if (val == SupplicantState::COMPLETED)
+        strncpy(buffer, "Completed", max);
+    else if (val == SupplicantState::IDLE)
+        strncpy(buffer, "Idle", max);
+    else
+        strncpy(buffer, "(internal error)", max);
+
+    return buffer;
+}
diff --git a/nexus/SupplicantState.h b/nexus/SupplicantState.h
new file mode 100644
index 0000000..6882f0c
--- /dev/null
+++ b/nexus/SupplicantState.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef _SUPPLICANT_STATE_H
+#define _SUPPLICANT_STATE_H
+
+class SupplicantState {
+public:
+    static const int UNKNOWN           = -1;
+    static const int DISCONNECTED      = 0;
+    static const int INACTIVE          = 1;
+    static const int SCANNING          = 2;
+    static const int ASSOCIATING       = 3;
+    static const int ASSOCIATED        = 4;
+    static const int FOURWAY_HANDSHAKE = 5;
+    static const int GROUP_HANDSHAKE   = 6;
+    static const int COMPLETED         = 7;
+    static const int IDLE              = 8;
+
+    static char *toString(int val, char *buffer, int max);
+};
+
+#endif
diff --git a/nexus/SupplicantStateChangeEvent.cpp b/nexus/SupplicantStateChangeEvent.cpp
new file mode 100644
index 0000000..cf0b9da
--- /dev/null
+++ b/nexus/SupplicantStateChangeEvent.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 <stdlib.h>
+
+#define LOG_TAG "SupplicantStateChangeEvent"
+#include <cutils/log.h>
+
+#include "SupplicantStateChangeEvent.h"
+
+SupplicantStateChangeEvent::SupplicantStateChangeEvent(int level, char *event,
+                                                       size_t len) :
+                            SupplicantEvent(SupplicantEvent::EVENT_STATE_CHANGE,
+                                            level) {
+    // XXX: move this stuff into a static creation method
+    char *p = index(event, ' ');
+    if (!p) {
+        LOGW("Bad event '%s'\n", event);
+        return;
+    }
+
+    mState = atoi(p + strlen("state=") + 1);
+}
+
+SupplicantStateChangeEvent::SupplicantStateChangeEvent(int state) :
+                            SupplicantEvent(SupplicantEvent::EVENT_STATE_CHANGE, -1) {
+    mState = state;
+}
+
+SupplicantStateChangeEvent::~SupplicantStateChangeEvent() {
+}
+
diff --git a/nexus/SupplicantStateChangeEvent.h b/nexus/SupplicantStateChangeEvent.h
new file mode 100644
index 0000000..77bff65
--- /dev/null
+++ b/nexus/SupplicantStateChangeEvent.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef _SupplicantStateChangeEvent_H
+#define _SupplicantStateChangeEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantStateChangeEvent : public SupplicantEvent {
+private:
+    int mState;
+
+public:
+    SupplicantStateChangeEvent(int level, char *event, size_t len);
+    SupplicantStateChangeEvent(int state);
+    virtual ~SupplicantStateChangeEvent();
+
+    int getState() { return mState; }
+};
+
+#endif
diff --git a/nexus/SupplicantStatus.cpp b/nexus/SupplicantStatus.cpp
new file mode 100644
index 0000000..b3c560a
--- /dev/null
+++ b/nexus/SupplicantStatus.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "SupplicantStatus"
+#include <cutils/log.h>
+
+#include "SupplicantStatus.h"
+#include "SupplicantState.h"
+
+SupplicantStatus::SupplicantStatus() {
+    mWpaState = SupplicantState::UNKNOWN;
+    mId = -1;
+    mBssid = NULL;
+    mSsid = NULL;
+}
+
+SupplicantStatus::SupplicantStatus(int state, int id, char *bssid, char *ssid) :
+                  mWpaState(state), mId(id), mBssid(bssid), mSsid(ssid) {
+
+LOGD("state %d, id %d, bssid %p, ssid %p\n", mWpaState, mId, mBssid, mSsid);
+}
+
+SupplicantStatus::~SupplicantStatus() {
+    if (mBssid)
+        free(mBssid);
+    if (mSsid)
+        free(mSsid);
+}
+
+SupplicantStatus *SupplicantStatus::createStatus(char *data, int len) {
+    char *bssid = NULL;
+    char *ssid = NULL;
+    int id = -1;
+    int state = SupplicantState::UNKNOWN;
+
+    char *next = data;
+    char *line;
+    while((line = strsep(&next, "\n"))) {
+        char *line_next =  line;
+        char *token = strsep(&line_next, "=");
+        char *value = strsep(&line_next, "=");
+        if (!strcmp(token, "bssid"))
+            bssid = strdup(value);
+        else if (!strcmp(token, "ssid"))
+            ssid = strdup(value);
+        else if (!strcmp(token, "id"))
+            id = atoi(value);
+        else if (!strcmp(token, "wpa_state")) {
+            if (!strcmp(value, "DISCONNECTED"))
+                state = SupplicantState::DISCONNECTED;
+            else if (!strcmp(value, "INACTIVE"))
+                state = SupplicantState::INACTIVE;
+            else if (!strcmp(value, "SCANNING"))
+                state = SupplicantState::SCANNING;
+            else if (!strcmp(value, "ASSOCIATING"))
+                state = SupplicantState::ASSOCIATING;
+            else if (!strcmp(value, "ASSOCIATED"))
+                state = SupplicantState::ASSOCIATED;
+            else if (!strcmp(value, "FOURWAY_HANDSHAKE"))
+                state = SupplicantState::FOURWAY_HANDSHAKE;
+            else if (!strcmp(value, "GROUP_HANDSHAKE"))
+                state = SupplicantState::GROUP_HANDSHAKE;
+            else if (!strcmp(value, "COMPLETED"))
+                state = SupplicantState::COMPLETED;
+            else if (!strcmp(value, "IDLE"))
+                state = SupplicantState::IDLE;
+            else 
+                LOGE("Unknown supplicant state '%s'", value);
+        } else
+            LOGD("Ignoring unsupported status token '%s'", token);
+    }
+
+    return new SupplicantStatus(state, id, bssid, ssid);
+    
+}
diff --git a/nexus/SupplicantStatus.h b/nexus/SupplicantStatus.h
new file mode 100644
index 0000000..ef01841
--- /dev/null
+++ b/nexus/SupplicantStatus.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef _SupplicantStatus_H
+#define _SupplicantStatus_H
+
+class SupplicantStatus {
+private:
+    int mWpaState;
+    int mId;
+    char *mBssid;
+    char *mSsid;
+
+private:
+    SupplicantStatus();
+    SupplicantStatus(int state, int id, char *bssid, char *ssid);
+
+public:
+    virtual ~SupplicantStatus();
+    static SupplicantStatus *createStatus(char *data, int len);
+
+    int getWpaState() { return mWpaState; }
+    int getId() { return mId; }
+    const char *getBssid() { return mBssid; }
+    const char *getSsid() { return mSsid; }
+   
+};
+
+#endif
diff --git a/nexus/TiwlanEventListener.cpp b/nexus/TiwlanEventListener.cpp
new file mode 100644
index 0000000..15e6930
--- /dev/null
+++ b/nexus/TiwlanEventListener.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 <errno.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define LOG_TAG "TiwlanEventListener"
+#include <cutils/log.h>
+
+#include "TiwlanEventListener.h"
+
+TiwlanEventListener::TiwlanEventListener(int socket) :
+                     SocketListener(socket, false) {
+}
+
+bool TiwlanEventListener::onDataAvailable(SocketClient *cli) {
+    struct ipc_ev_data *data;
+
+    if (!(data = (struct ipc_ev_data *) malloc(sizeof(struct ipc_ev_data)))) {
+        LOGE("Failed to allocate packet (out of memory)");
+        return true;
+    }
+
+    if (recv(cli->getSocket(), data, sizeof(struct ipc_ev_data), 0) < 0) {
+       LOGE("recv failed (%s)", strerror(errno));
+       goto out;
+    }
+
+    if (data->event_type == IPC_EVENT_LINK_SPEED) {
+        uint32_t *spd = (uint32_t *) data->buffer;
+        *spd /= 2;
+//        LOGD("Link speed = %u MB/s", *spd);
+    } else if (data->event_type == IPC_EVENT_LOW_SNR) {
+        LOGW("Low signal/noise ratio");
+    } else if (data->event_type == IPC_EVENT_LOW_RSSI) {
+        LOGW("Low RSSI");
+    } else {
+//        LOGD("Dropping unhandled driver event %d", data->event_type);
+    }
+
+    // TODO: Tell WifiController about the event
+out:
+    free(data);
+    return true;
+}
diff --git a/nexus/TiwlanEventListener.h b/nexus/TiwlanEventListener.h
new file mode 100644
index 0000000..052d6b1
--- /dev/null
+++ b/nexus/TiwlanEventListener.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef _TIWLAN_EVENT_LISTENER_H__
+#define _TIWLAN_EVENT_LISTENER_H__
+
+#include <sysutils/SocketListener.h>
+
+struct wpa_ctrl;
+class SocketClient;
+class ITiwlanEventHandler;
+class TiwlanEventFactory;
+
+class TiwlanEventListener: public SocketListener {
+    
+public:
+    TiwlanEventListener(int sock);
+    virtual ~TiwlanEventListener() {}
+
+protected:
+    virtual bool onDataAvailable(SocketClient *c);
+};
+
+// TODO: Move all this crap into a factory
+#define TI_DRIVER_MSG_PORT 9001
+
+#define IPC_EVENT_LINK_SPEED  2
+#define IPC_EVENT_LOW_SNR     13
+#define IPC_EVENT_LOW_RSSI    14
+
+struct ipc_ev_data {
+    uint32_t event_type;
+    void     *event_id;
+    uint32_t process_id;
+    uint32_t delivery_type;
+    uint32_t user_param;
+    void     *event_callback;
+    uint32_t bufferSize;
+    uint8_t  buffer[2048];
+};
+
+#endif
diff --git a/nexus/TiwlanWifiController.cpp b/nexus/TiwlanWifiController.cpp
new file mode 100644
index 0000000..61535c3
--- /dev/null
+++ b/nexus/TiwlanWifiController.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <cutils/properties.h>
+#define LOG_TAG "TiwlanWifiController"
+#include <cutils/log.h>
+
+#include "PropertyManager.h"
+#include "TiwlanWifiController.h"
+#include "TiwlanEventListener.h"
+
+#define DRIVER_PROP_NAME "wlan.driver.status"
+
+extern "C" int sched_yield(void);
+
+TiwlanWifiController::TiwlanWifiController(PropertyManager *propmngr,
+                                           IControllerHandler *handlers,
+                                           char *modpath, char *modname,
+                                           char *modargs) :
+                      WifiController(propmngr, handlers, modpath, modname,
+                                     modargs) {
+    mEventListener = NULL;
+    mListenerSock = -1;
+}
+
+int TiwlanWifiController::powerUp() {
+    return 0; // Powerup is currently done when the driver is loaded
+}
+
+int TiwlanWifiController::powerDown() {
+    if (mEventListener) {
+        delete mEventListener;
+        close(mListenerSock);
+        mListenerSock = -1;
+        mEventListener = NULL;
+    }
+   
+    return 0; // Powerdown is currently done when the driver is unloaded
+}
+
+bool TiwlanWifiController::isPoweredUp() {
+    return isKernelModuleLoaded(getModuleName());
+}
+
+int TiwlanWifiController::loadFirmware() {
+    char driver_status[PROPERTY_VALUE_MAX];
+    int count = 100;
+
+    property_set("ctl.start", "wlan_loader");
+    sched_yield();
+
+    // Wait for driver to be ready
+    while (count-- > 0) {
+        if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
+            if (!strcmp(driver_status, "ok")) {
+                LOGD("Firmware loaded OK");
+
+                if (startDriverEventListener()) {
+                    LOGW("Failed to start driver event listener");
+                }
+
+                return 0;
+            } else if (!strcmp(DRIVER_PROP_NAME, "failed")) {
+                LOGE("Firmware load failed");
+                return -1;
+            }
+        }
+        usleep(200000);
+    }
+    property_set(DRIVER_PROP_NAME, "timeout");
+    LOGE("Firmware load timed out");
+    return -1;
+}
+
+int TiwlanWifiController::startDriverEventListener() {
+    struct sockaddr_in addr;
+    int s;
+
+    if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+        return -1;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    addr.sin_port = htons(TI_DRIVER_MSG_PORT);
+
+    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    mEventListener = new TiwlanEventListener(s);
+
+    if (mEventListener->startListener()) {
+        LOGE("Error starting driver listener (%s)", strerror(errno));
+        delete mEventListener;
+        mEventListener = NULL;
+        close(s);
+        return -1;
+    }
+    mListenerSock = s;
+    return 0;
+}
+
+bool TiwlanWifiController::isFirmwareLoaded() {
+    // Always load the firmware
+    return false;
+}
diff --git a/nexus/TiwlanWifiController.h b/nexus/TiwlanWifiController.h
new file mode 100644
index 0000000..583be71
--- /dev/null
+++ b/nexus/TiwlanWifiController.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef _TIWLAN_WIFI_CONTROLLER_H
+#define _TIWLAN_WIFI_CONTROLLER_H
+
+#include "PropertyManager.h"
+#include "WifiController.h"
+
+class IControllerHandler;
+class TiwlanEventListener;
+
+class TiwlanWifiController : public WifiController {
+    int                 mListenerSock;
+    TiwlanEventListener *mEventListener;
+
+public:
+    TiwlanWifiController(PropertyManager *propmngr, IControllerHandler *handlers, char *modpath, char *modname, char *modargs);
+    virtual ~TiwlanWifiController() {}
+
+    virtual int powerUp();
+    virtual int powerDown();
+    virtual bool isPoweredUp();
+    virtual int loadFirmware();
+    virtual bool isFirmwareLoaded();
+
+private:
+    int startDriverEventListener();
+};
+#endif
diff --git a/nexus/VpnController.cpp b/nexus/VpnController.cpp
new file mode 100644
index 0000000..add4dc3
--- /dev/null
+++ b/nexus/VpnController.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "PropertyManager.h"
+#include "VpnController.h"
+
+VpnController::VpnController(PropertyManager *propmngr,
+                             IControllerHandler *handlers) :
+               Controller("VPN", propmngr, handlers) {
+    mEnabled = false;
+}
+
+int VpnController::start() {
+    mPropMngr->registerProperty("vpn.enabled", this);
+    return 0;
+}
+
+int VpnController::stop() {
+    mPropMngr->unregisterProperty("vpn.enabled");
+    return 0;
+}
+
+int VpnController::set(const char *name, const char *value) {
+    if (!strcmp(name, "vpn.enabled")) {
+        int en = atoi(value);
+        int rc;
+
+        if (en == mEnabled)
+            return 0;
+        rc = (en ? enable() : disable());
+
+        if (!rc) {
+            mEnabled = en;
+            if (en) 
+                mPropMngr->unregisterProperty("vpn.gateway");
+            else
+                mPropMngr->unregisterProperty("vpn.gateway");
+        }
+        return rc;
+    } if (!strcmp(name, "vpn.gateway")) {
+        if (!inet_aton(value, &mVpnGateway)) {
+            errno = EINVAL;
+            return -1;
+        }
+        return 0;
+    }
+
+    return Controller::set(name, value);
+}
+
+const char *VpnController::get(const char *name, char *buffer, size_t maxsize) {
+    if (!strcmp(name, "vpn.enabled")) {
+        snprintf(buffer, maxsize, "%d", mEnabled);
+        return buffer;
+    } if (!strcmp(name, "vpn.gateway")) {
+        snprintf(buffer, maxsize, "%s", inet_ntoa(mVpnGateway));
+        return buffer;
+    }
+
+    return Controller::get(name, buffer, maxsize);
+}
diff --git a/nexus/VpnController.h b/nexus/VpnController.h
new file mode 100644
index 0000000..1af4d9f
--- /dev/null
+++ b/nexus/VpnController.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef _VPN_CONTROLLER_H
+#define _VPN_CONTROLLER_H
+
+#include <netinet/in.h>
+
+#include "Controller.h"
+
+class IControllerHandler;
+
+class VpnController : public Controller {
+    bool           mEnabled;
+    /*
+     * Gateway of the VPN server to connect to
+     */
+    struct in_addr mVpnGateway;
+
+public:
+    VpnController(PropertyManager *propmngr, IControllerHandler *handlers);
+    virtual ~VpnController() {}
+
+    virtual int start();
+    virtual int stop();
+
+    virtual int set(const char *name, const char *value);
+    virtual const char *get(const char *name, char *buffer, size_t maxlen);
+
+protected:
+    virtual int enable() = 0;
+    virtual int disable() = 0;
+
+};
+
+#endif
diff --git a/nexus/WifiController.cpp b/nexus/WifiController.cpp
new file mode 100644
index 0000000..ef5ecae
--- /dev/null
+++ b/nexus/WifiController.cpp
@@ -0,0 +1,413 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define LOG_TAG "WifiController"
+#include <cutils/log.h>
+
+#include "Supplicant.h"
+#include "WifiController.h"
+#include "WifiScanner.h"
+#include "NetworkManager.h"
+#include "ErrorCode.h"
+#include "WifiNetwork.h"
+#include "ISupplicantEventHandler.h"
+#include "SupplicantState.h"
+#include "SupplicantStatus.h"
+#include "SupplicantAssociatingEvent.h"
+#include "SupplicantAssociatedEvent.h"
+#include "SupplicantConnectedEvent.h"
+#include "SupplicantScanResultsEvent.h"
+#include "SupplicantStateChangeEvent.h"
+#include "SupplicantConnectionTimeoutEvent.h"
+#include "SupplicantDisconnectedEvent.h"
+
+WifiController::WifiController(PropertyManager *mPropMngr,
+                               IControllerHandler *handlers,
+                               char *modpath, char *modname, char *modargs) :
+                Controller("WIFI", mPropMngr, handlers) {
+    strncpy(mModulePath, modpath, sizeof(mModulePath));
+    strncpy(mModuleName, modname, sizeof(mModuleName));
+    strncpy(mModuleArgs, modargs, sizeof(mModuleArgs));
+
+    mLatestScanResults = new ScanResultCollection();
+    pthread_mutex_init(&mLatestScanResultsLock, NULL);
+
+    mSupplicant = new Supplicant(this, this);
+    mScanner = new WifiScanner(mSupplicant, 10);
+    mCurrentScanMode = 0;
+
+    mEnabled = false;
+
+    mSupplicantState = SupplicantState::UNKNOWN;
+}
+
+int WifiController::start() {
+    mPropMngr->registerProperty("wifi.enabled", this);
+    return 0;
+}
+
+int WifiController::stop() {
+    mPropMngr->unregisterProperty("wifi.enabled");
+    return 0;
+}
+
+int WifiController::enable() {
+
+    if (!isPoweredUp()) {
+        LOGI("Powering up");
+        sendStatusBroadcast("Powering up WiFi hardware");
+        if (powerUp()) {
+            LOGE("Powerup failed (%s)", strerror(errno));
+            return -1;
+        }
+    }
+
+    if (mModuleName[0] != '\0' && !isKernelModuleLoaded(mModuleName)) {
+        LOGI("Loading driver");
+        sendStatusBroadcast("Loading WiFi driver");
+        if (loadKernelModule(mModulePath, mModuleArgs)) {
+            LOGE("Kernel module load failed (%s)", strerror(errno));
+            goto out_powerdown;
+        }
+    }
+
+    if (!isFirmwareLoaded()) {
+        LOGI("Loading firmware");
+        sendStatusBroadcast("Loading WiFI firmware");
+        if (loadFirmware()) {
+            LOGE("Firmware load failed (%s)", strerror(errno));
+            goto out_powerdown;
+        }
+    }
+
+    if (!mSupplicant->isStarted()) {
+        LOGI("Starting WPA Supplicant");
+        sendStatusBroadcast("Starting WPA Supplicant");
+        if (mSupplicant->start()) {
+            LOGE("Supplicant start failed (%s)", strerror(errno));
+            goto out_unloadmodule;
+        }
+    }
+
+    if (Controller::bindInterface(mSupplicant->getInterfaceName())) {
+        LOGE("Error binding interface (%s)", strerror(errno));
+        goto out_unloadmodule;
+    }
+
+    if (mSupplicant->refreshNetworkList())
+        LOGW("Error getting list of networks (%s)", strerror(errno));
+
+    mPropMngr->registerProperty("wifi.supplicant.state", this);
+    mPropMngr->registerProperty("wifi.scanmode", this);
+    mPropMngr->registerProperty("wifi.interface", this);
+
+    LOGI("Enabled successfully");
+    return 0;
+
+out_unloadmodule:
+    if (mModuleName[0] != '\0' && !isKernelModuleLoaded(mModuleName)) {
+        if (unloadKernelModule(mModuleName)) {
+            LOGE("Unable to unload module after failure!");
+        }
+    }
+
+out_powerdown:
+    if (powerDown()) {
+        LOGE("Unable to powerdown after failure!");
+    }
+    return -1;
+}
+
+void WifiController::sendStatusBroadcast(const char *msg) {
+    NetworkManager::Instance()->
+                    getBroadcaster()->
+                    sendBroadcast(ErrorCode::UnsolicitedInformational, msg, false);
+}
+
+int WifiController::disable() {
+
+    mPropMngr->unregisterProperty("wifi.scanmode");
+    mPropMngr->unregisterProperty("wifi.supplicant.state");
+    mPropMngr->unregisterProperty("wifi.scanmode");
+
+    if (mSupplicant->isStarted()) {
+        sendStatusBroadcast("Stopping WPA Supplicant");
+        if (mSupplicant->stop()) {
+            LOGE("Supplicant stop failed (%s)", strerror(errno));
+            return -1;
+        }
+    } else
+        LOGW("disable(): Supplicant not running?");
+
+    if (mModuleName[0] != '\0' && isKernelModuleLoaded(mModuleName)) {
+        sendStatusBroadcast("Unloading WiFi driver");
+        if (unloadKernelModule(mModuleName)) {
+            LOGE("Unable to unload module (%s)", strerror(errno));
+            return -1;
+        }
+    }
+
+    if (isPoweredUp()) {
+        sendStatusBroadcast("Powering down WiFi hardware");
+        if (powerDown()) {
+            LOGE("Powerdown failed (%s)", strerror(errno));
+            return -1;
+        }
+    }
+    return 0;
+}
+
+int WifiController::loadFirmware() {
+    return 0;
+}
+
+int WifiController::setScanMode(uint32_t mode) {
+    int rc = 0;
+
+    if (mCurrentScanMode == mode)
+        return 0;
+
+    if (!(mode & SCAN_ENABLE_MASK)) {
+        if (mCurrentScanMode & SCAN_REPEAT_MASK)
+            mScanner->stop();
+    } else if (mode & SCAN_REPEAT_MASK)
+        rc = mScanner->start(mode & SCAN_ACTIVE_MASK);
+    else
+        rc = mSupplicant->triggerScan(mode & SCAN_ACTIVE_MASK);
+
+    mCurrentScanMode = mode;
+    return rc;
+}
+
+WifiNetwork *WifiController::createNetwork() {
+    WifiNetwork *wn = mSupplicant->createNetwork();
+    return wn;
+}
+
+int WifiController::removeNetwork(int networkId) {
+    WifiNetwork *wn = mSupplicant->lookupNetwork(networkId);
+
+    if (!wn)
+        return -1;
+    return mSupplicant->removeNetwork(wn);
+}
+
+ScanResultCollection *WifiController::createScanResults() {
+    ScanResultCollection *d = new ScanResultCollection();
+    ScanResultCollection::iterator i;
+
+    pthread_mutex_lock(&mLatestScanResultsLock);
+    for (i = mLatestScanResults->begin(); i != mLatestScanResults->end(); ++i)
+        d->push_back((*i)->clone());
+
+    pthread_mutex_unlock(&mLatestScanResultsLock);
+    return d;
+}
+
+WifiNetworkCollection *WifiController::createNetworkList() {
+    return mSupplicant->createNetworkList();
+}
+
+int WifiController::set(const char *name, const char *value) {
+    int rc;
+
+    if (!strcmp(name, "wifi.enabled")) {
+        int en = atoi(value);
+
+        if (en == mEnabled)
+            return 0;
+        rc = (en ? enable() : disable());
+        if (!rc)
+            mEnabled = en;
+    } else if (!strcmp(name, "wifi.interface")) {
+        errno = EROFS;
+        return -1;
+    } else if (!strcmp(name, "wifi.scanmode"))
+        return setScanMode((uint32_t) strtoul(value, NULL, 0));
+    else if (!strcmp(name, "wifi.supplicant.state")) {
+        errno = EROFS;
+        return -1;
+    } else
+        return Controller::set(name, value);
+    return rc;
+}
+
+const char *WifiController::get(const char *name, char *buffer, size_t maxsize) {
+
+    if (!strcmp(name, "wifi.enabled"))
+        snprintf(buffer, maxsize, "%d", mEnabled);
+    else if (!strcmp(name, "wifi.interface")) {
+        snprintf(buffer, maxsize, "%s",
+                 (getBoundInterface() ? getBoundInterface() : "none"));
+    } else if (!strcmp(name, "wifi.scanmode"))
+        snprintf(buffer, maxsize, "0x%.8x", mCurrentScanMode);
+    else if (!strcmp(name, "wifi.supplicant.state"))
+        return SupplicantState::toString(mSupplicantState, buffer, maxsize);
+    else
+        return Controller::get(name, buffer, maxsize);
+
+    return buffer;
+}
+
+void WifiController::onAssociatingEvent(SupplicantAssociatingEvent *evt) {
+    LOGD("onAssociatingEvent(%s, %s, %d)",
+         (evt->getBssid() ? evt->getBssid() : "n/a"),
+         (evt->getSsid() ? evt->getSsid() : "n/a"),
+         evt->getFreq());
+}
+
+void WifiController::onAssociatedEvent(SupplicantAssociatedEvent *evt) {
+    LOGD("onAssociatedEvent(%s)", evt->getBssid());
+}
+
+void WifiController::onConnectedEvent(SupplicantConnectedEvent *evt) {
+    LOGD("onConnectedEvent(%s, %d)", evt->getBssid(), evt->getReassociated());
+    SupplicantStatus *ss = mSupplicant->getStatus();
+    WifiNetwork *wn;
+
+    if (ss->getWpaState() != SupplicantState::COMPLETED) {
+        char tmp[32];
+
+        LOGW("onConnected() with SupplicantState = %s!",
+             SupplicantState::toString(ss->getWpaState(), tmp,
+             sizeof(tmp)));
+        return;
+    }
+
+    if (ss->getId() == -1) {
+        LOGW("onConnected() with id = -1!");
+        return;
+    }
+    
+    if (!(wn = mSupplicant->lookupNetwork(ss->getId()))) {
+        LOGW("Error looking up connected network id %d (%s)",
+             ss->getId(), strerror(errno));
+        return;
+    }
+  
+    delete ss;
+    mHandlers->onInterfaceConnected(this, wn->getIfaceCfg());
+}
+
+void WifiController::onScanResultsEvent(SupplicantScanResultsEvent *evt) {
+    char *reply;
+
+    if (!(reply = (char *) malloc(4096))) {
+        LOGE("Out of memory");
+        return;
+    }
+
+    size_t len = 4096;
+
+    if (mSupplicant->sendCommand("SCAN_RESULTS", reply, &len)) {
+        LOGW("onScanResultsEvent: Error getting scan results (%s)",
+             strerror(errno));
+        free(reply);
+        return;
+    }
+
+    pthread_mutex_lock(&mLatestScanResultsLock);
+    if (!mLatestScanResults->empty()) {
+        ScanResultCollection::iterator i;
+
+        for (i = mLatestScanResults->begin();
+             i !=mLatestScanResults->end(); ++i) {
+            delete *i;
+        }
+        mLatestScanResults->clear();
+    }
+
+    char *linep;
+    char *linep_next = NULL;
+
+    if (!strtok_r(reply, "\n", &linep_next)) {
+        free(reply);
+        pthread_mutex_unlock(&mLatestScanResultsLock);
+        return;
+    }
+
+    while((linep = strtok_r(NULL, "\n", &linep_next)))
+        mLatestScanResults->push_back(new ScanResult(linep));
+
+    char *tmp;
+    asprintf(&tmp, "Scan results ready (%d)", mLatestScanResults->size());
+    NetworkManager::Instance()->getBroadcaster()->
+                                sendBroadcast(ErrorCode::UnsolicitedInformational, tmp, false);
+    free(tmp);
+    pthread_mutex_unlock(&mLatestScanResultsLock);
+    free(reply);
+}
+
+void WifiController::onStateChangeEvent(SupplicantStateChangeEvent *evt) {
+    char tmp[32];
+    char tmp2[32];
+    
+    LOGD("onStateChangeEvent(%s -> %s)", 
+         SupplicantState::toString(mSupplicantState, tmp, sizeof(tmp)),
+         SupplicantState::toString(evt->getState(), tmp2, sizeof(tmp2)));
+
+    mSupplicantState = evt->getState();
+}
+
+void WifiController::onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt) {
+    LOGD("onConnectionTimeoutEvent(%s)", evt->getBssid());
+}
+
+void WifiController::onDisconnectedEvent(SupplicantDisconnectedEvent *evt) {
+    mHandlers->onInterfaceDisconnected(this, getBoundInterface());
+}
+
+#if 0
+void WifiController::onTerminatingEvent(SupplicantEvent *evt) {
+    LOGD("onTerminatingEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onPasswordChangedEvent(SupplicantEvent *evt) {
+    LOGD("onPasswordChangedEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapNotificationEvent(SupplicantEvent *evt) {
+    LOGD("onEapNotificationEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapStartedEvent(SupplicantEvent *evt) {
+    LOGD("onEapStartedEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapMethodEvent(SupplicantEvent *evt) {
+    LOGD("onEapMethodEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapSuccessEvent(SupplicantEvent *evt) {
+    LOGD("onEapSuccessEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapFailureEvent(SupplicantEvent *evt) {
+    LOGD("onEapFailureEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onLinkSpeedEvent(SupplicantEvent *evt) {
+    LOGD("onLinkSpeedEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onDriverStateEvent(SupplicantEvent *evt) {
+    LOGD("onDriverStateEvent(%s)", evt->getEvent());
+}
+#endif
diff --git a/nexus/WifiController.h b/nexus/WifiController.h
new file mode 100644
index 0000000..c61d97a
--- /dev/null
+++ b/nexus/WifiController.h
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#ifndef _WIFI_CONTROLLER_H
+#define _WIFI_CONTROLLER_H
+
+#include <sys/types.h>
+
+#include "Controller.h"
+#include "ScanResult.h"
+#include "WifiNetwork.h"
+#include "ISupplicantEventHandler.h"
+
+class NetInterface;
+class Supplicant;
+class WifiScanner;
+class SupplicantAssociatingEvent;
+class SupplicantAssociatedEvent;
+class SupplicantConnectedEvent;
+class SupplicantScanResultsEvent;
+class SupplicantStateChangeEvent;
+class SupplicantDisconnectedEvent;
+
+class WifiController : public Controller, public ISupplicantEventHandler {
+public:
+    static const uint32_t SCAN_ENABLE_MASK       = 0x01;
+    static const uint32_t SCAN_ACTIVE_MASK       = 0x02;
+    static const uint32_t SCAN_REPEAT_MASK       = 0x04;
+
+    static const uint32_t SCANMODE_NONE               = 0;
+    static const uint32_t SCANMODE_PASSIVE_ONESHOT    = SCAN_ENABLE_MASK;
+    static const uint32_t SCANMODE_PASSIVE_CONTINUOUS = SCAN_ENABLE_MASK | SCAN_REPEAT_MASK;
+    static const uint32_t SCANMODE_ACTIVE_ONESHOT     = SCAN_ENABLE_MASK | SCAN_ACTIVE_MASK;
+    static const uint32_t SCANMODE_ACTIVE_CONTINUOUS  = SCAN_ENABLE_MASK | SCAN_ACTIVE_MASK | SCAN_REPEAT_MASK;
+
+private:
+    Supplicant *mSupplicant;
+    char        mModulePath[255];
+    char        mModuleName[64];
+    char        mModuleArgs[255];
+
+    uint32_t    mCurrentScanMode;
+    WifiScanner *mScanner;
+    int         mSupplicantState;
+
+    ScanResultCollection *mLatestScanResults;
+    pthread_mutex_t      mLatestScanResultsLock;
+
+    bool        mEnabled;
+
+public:
+    WifiController(PropertyManager *propmngr, IControllerHandler *handlers, char *modpath, char *modname, char *modargs);
+    virtual ~WifiController() {}
+
+    int start();
+    int stop();
+
+    WifiNetwork *createNetwork();
+    int removeNetwork(int networkId);
+    WifiNetworkCollection *createNetworkList();
+
+    virtual int set(const char *name, const char *value);
+    virtual const char *get(const char *name, char *buffer, size_t maxlen);
+
+    ScanResultCollection *createScanResults();
+
+    char *getModulePath() { return mModulePath; }
+    char *getModuleName() { return mModuleName; }
+    char *getModuleArgs() { return mModuleArgs; }
+
+    Supplicant *getSupplicant() { return mSupplicant; }
+
+protected:
+    // Move this crap into a 'driver'
+    virtual int powerUp() = 0;
+    virtual int powerDown() = 0;
+    virtual int loadFirmware();
+
+    virtual bool isFirmwareLoaded() = 0;
+    virtual bool isPoweredUp() = 0;
+
+private:
+    void sendStatusBroadcast(const char *msg);
+    int setScanMode(uint32_t mode);
+    int enable();
+    int disable();
+
+    // ISupplicantEventHandler methods
+    virtual void onAssociatingEvent(SupplicantAssociatingEvent *evt);
+    virtual void onAssociatedEvent(SupplicantAssociatedEvent *evt);
+    virtual void onConnectedEvent(SupplicantConnectedEvent *evt);
+    virtual void onScanResultsEvent(SupplicantScanResultsEvent *evt);
+    virtual void onStateChangeEvent(SupplicantStateChangeEvent *evt);
+    virtual void onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt);
+    virtual void onDisconnectedEvent(SupplicantDisconnectedEvent *evt);
+#if 0
+    virtual void onTerminatingEvent(SupplicantEvent *evt);
+    virtual void onPasswordChangedEvent(SupplicantEvent *evt);
+    virtual void onEapNotificationEvent(SupplicantEvent *evt);
+    virtual void onEapStartedEvent(SupplicantEvent *evt);
+    virtual void onEapMethodEvent(SupplicantEvent *evt);
+    virtual void onEapSuccessEvent(SupplicantEvent *evt);
+    virtual void onEapFailureEvent(SupplicantEvent *evt);
+    virtual void onLinkSpeedEvent(SupplicantEvent *evt);
+    virtual void onDriverStateEvent(SupplicantEvent *evt);
+#endif
+
+};
+
+#endif
diff --git a/nexus/WifiNetwork.cpp b/nexus/WifiNetwork.cpp
new file mode 100644
index 0000000..7059bd0
--- /dev/null
+++ b/nexus/WifiNetwork.cpp
@@ -0,0 +1,636 @@
+/*
+ * 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 <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#define LOG_TAG "WifiNetwork"
+#include <cutils/log.h>
+
+#include "NetworkManager.h"
+#include "WifiNetwork.h"
+#include "Supplicant.h"
+#include "WifiController.h"
+#include "InterfaceConfig.h"
+
+const char *WifiNetwork::PropertyNames[] = { "ssid", "bssid", "psk", "wepkey.1",
+                                             "wepkey.2", "wepkey.3", "wepkey.4",
+                                             "defkeyidx", "pri", "hiddenssid",
+                                             "AllowedKeyManagement",
+                                             "AllowedProtocols",
+                                             "AllowedAuthAlgorithms",
+                                             "AllowedPairwiseCiphers",
+                                             "AllowedGroupCiphers",
+                                             "enabled", '\0' };
+WifiNetwork::WifiNetwork() {
+   // This is private to restrict copy constructors
+}
+
+WifiNetwork::WifiNetwork(WifiController *c, Supplicant *suppl, const char *data) {
+    mController = c;
+    mSuppl = suppl;
+
+    char *tmp = strdup(data);
+    char *next = tmp;
+    char *id;
+    char *ssid;
+    char *bssid;
+    char *flags;
+
+    if (!(id = strsep(&next, "\t")))
+        LOGE("Failed to extract network id");
+    if (!(ssid = strsep(&next, "\t")))
+        LOGE("Failed to extract ssid");
+    if (!(bssid = strsep(&next, "\t")))
+        LOGE("Failed to extract bssid");
+    if (!(flags = strsep(&next, "\t")))
+        LOGE("Failed to extract flags");
+
+   // LOGD("id '%s', ssid '%s', bssid '%s', flags '%s'", id, ssid, bssid,
+   //      flags ? flags :"null");
+
+    if (id)
+        mNetid = atoi(id);
+    if (ssid)
+        mSsid = strdup(ssid);
+    if (bssid)
+        mBssid = strdup(bssid);
+
+    mPsk = NULL;
+    memset(mWepKeys, 0, sizeof(mWepKeys));
+    mDefaultKeyIndex = -1;
+    mPriority = -1;
+    mHiddenSsid = NULL;
+    mAllowedKeyManagement = KeyManagementMask::UNKNOWN;
+    mAllowedProtocols = 0;
+    mAllowedAuthAlgorithms = 0;
+    mAllowedPairwiseCiphers = 0;
+    mAllowedGroupCiphers = 0;
+    mEnabled = true;
+
+    if (flags && flags[0] != '\0') {
+        if (!strcmp(flags, "[DISABLED]"))
+            mEnabled = false;
+        else
+            LOGW("Unsupported flags '%s'", flags);
+    }
+
+    char *tmp2;
+    asprintf(&tmp2, "wifi.net.%d", mNetid);
+    mIfaceCfg = new InterfaceConfig(tmp2);
+    free(tmp2);
+    free(tmp);
+}
+
+WifiNetwork::WifiNetwork(WifiController *c, Supplicant *suppl, int networkId) {
+    mController = c;
+    mSuppl = suppl;
+    mNetid = networkId;
+    mSsid = NULL;
+    mBssid = NULL;
+    mPsk = NULL;
+    memset(mWepKeys, 0, sizeof(mWepKeys));
+    mDefaultKeyIndex = -1;
+    mPriority = -1;
+    mHiddenSsid = NULL;
+    mAllowedKeyManagement = 0;
+    mAllowedProtocols = 0;
+    mAllowedAuthAlgorithms = 0;
+    mAllowedPairwiseCiphers = 0;
+    mAllowedGroupCiphers = 0;
+    mEnabled = false;
+
+    char *tmp2;
+    asprintf(&tmp2, "wifi.net.%d", mNetid);
+    mIfaceCfg = new InterfaceConfig(tmp2);
+    free(tmp2);
+}
+
+WifiNetwork *WifiNetwork::clone() {
+    WifiNetwork *r = new WifiNetwork();
+
+    r->mSuppl = mSuppl;
+    r->mNetid = mNetid;
+
+    if (mSsid)
+        r->mSsid = strdup(mSsid);
+    if (mBssid)
+        r->mBssid = strdup(mBssid);
+    if (mPsk)
+        r->mPsk = strdup(mPsk);
+
+    r->mController = mController;
+    memcpy(r->mWepKeys, mWepKeys, sizeof(mWepKeys));
+    r->mDefaultKeyIndex = mDefaultKeyIndex;
+    r->mPriority = mPriority;
+    if (mHiddenSsid)
+        r->mHiddenSsid = strdup(mHiddenSsid);
+    r->mAllowedKeyManagement = mAllowedKeyManagement;
+    r->mAllowedProtocols = mAllowedProtocols;
+    r->mAllowedAuthAlgorithms = mAllowedAuthAlgorithms;
+    r->mAllowedPairwiseCiphers = mAllowedPairwiseCiphers;
+    r->mAllowedGroupCiphers = mAllowedGroupCiphers;
+    return r;
+}
+
+WifiNetwork::~WifiNetwork() {
+    if (mSsid)
+        free(mSsid);
+    if (mBssid)
+        free(mBssid);
+    if (mPsk)
+        free(mPsk);
+    for (int i = 0; i < 4; i++) {
+        if (mWepKeys[i])
+            free(mWepKeys[i]);
+    }
+
+    if (mHiddenSsid)
+        free(mHiddenSsid);
+    if (mIfaceCfg)
+        delete(mIfaceCfg);
+}
+
+int WifiNetwork::refresh() {
+    char buffer[255];
+    size_t len;
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "psk", buffer, len))
+        mPsk = strdup(buffer);
+
+    for (int i = 0; i < 4; i++) {
+        char *name;
+
+        asprintf(&name, "wep_key%d", i);
+        len = sizeof(buffer);
+        if (mSuppl->getNetworkVar(mNetid, name, buffer, len))
+            mWepKeys[i] = strdup(buffer);
+        free(name);
+    }
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "wep_tx_keyidx", buffer, len))
+        mDefaultKeyIndex = atoi(buffer);
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "priority", buffer, len))
+        mPriority = atoi(buffer);
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "scan_ssid", buffer, len))
+        mHiddenSsid = strdup(buffer);
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "key_mgmt", buffer, len)) {
+        if (!strcmp(buffer, "NONE"))
+            setAllowedKeyManagement(KeyManagementMask::NONE);
+        else if (index(buffer, ' ')) {
+            char *next = buffer;
+            char *token;
+            uint32_t mask = 0;
+
+            while((token = strsep(&next, " "))) {
+                if (!strcmp(token, "WPA-PSK"))
+                    mask |= KeyManagementMask::WPA_PSK;
+                else if (!strcmp(token, "WPA-EAP"))
+                    mask |= KeyManagementMask::WPA_EAP;
+                else if (!strcmp(token, "IEE8021X"))
+                    mask |= KeyManagementMask::IEEE8021X;
+                else
+                    LOGW("Unsupported key management scheme '%s'" , token);
+            }
+            setAllowedKeyManagement(mask);
+        } else
+            LOGE("Unsupported key management '%s'", buffer);
+    }
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "proto", buffer, len)) {
+        // TODO
+    }
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "auth_alg", buffer, len)) {
+        // TODO
+    }
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "pairwise", buffer, len)) {
+        // TODO
+    }
+
+    len = sizeof(buffer);
+    if (mSuppl->getNetworkVar(mNetid, "group", buffer, len)) {
+        // TODO
+    }
+
+    return 0;
+out_err:
+    LOGE("Refresh failed (%s)",strerror(errno));
+    return -1;
+}
+
+int WifiNetwork::set(const char *name, const char *value) {
+    char *n_tmp = strdup(name + strlen("wifi.net."));
+    char *n_next = n_tmp;
+    char *n_local;
+    char *n_rest;
+    int rc = 0;
+
+    if (!strsep(&n_next, ".")) // skip net id
+        goto out_inval;
+
+    if (!(n_local = strsep(&n_next, ".")))
+        goto out_inval;
+
+    n_rest = n_next;
+
+//    LOGD("set(): var '%s'(%s / %s) = %s", name, n_local, n_rest, value);
+    if (!strcasecmp(n_local, "enabled"))
+        rc = setEnabled(atoi(value));
+    else if (!strcmp(n_local, "ssid"))
+        rc = setSsid(value);
+    else if (!strcasecmp(n_local, "bssid"))
+        rc = setBssid(value);
+    else if (!strcasecmp(n_local, "psk"))
+        rc = setPsk(value);
+    else if (!strcasecmp(n_local, "wepkey"))
+        rc = setWepKey(atoi(n_rest) -1, value);
+    else if (!strcasecmp(n_local, "defkeyidx"))
+        rc = setDefaultKeyIndex(atoi(value));
+    else if (!strcasecmp(n_local, "pri"))
+        rc = setPriority(atoi(value));
+    else if (!strcasecmp(n_local, "hiddenssid"))
+        rc = setHiddenSsid(value);
+    else if (!strcasecmp(n_local, "AllowedKeyManagement")) {
+        uint32_t mask = 0;
+        bool none = false;
+        char *v_tmp = strdup(value);
+        char *v_next = v_tmp;
+        char *v_token;
+
+        while((v_token = strsep(&v_next, " "))) {
+            if (!strcasecmp(v_token, "NONE")) {
+                mask = KeyManagementMask::NONE;
+                none = true;
+            } else if (!none) {
+                if (!strcasecmp(v_token, "WPA_PSK"))
+                    mask |= KeyManagementMask::WPA_PSK;
+                else if (!strcasecmp(v_token, "WPA_EAP"))
+                    mask |= KeyManagementMask::WPA_EAP;
+                else if (!strcasecmp(v_token, "IEEE8021X"))
+                    mask |= KeyManagementMask::IEEE8021X;
+                else {
+                    errno = EINVAL;
+                    rc = -1;
+                    free(v_tmp);
+                    goto out;
+                }
+            } else {
+                errno = EINVAL;
+                rc = -1;
+                free(v_tmp);
+                goto out;
+            }
+        }
+        free(v_tmp);
+    } else if (!strcasecmp(n_local, "AllowedProtocols")) {
+        // TODO
+    } else if (!strcasecmp(n_local, "AllowedPairwiseCiphers")) {
+        // TODO
+    } else if (!strcasecmp(n_local, "AllowedAuthAlgorithms")) {
+        // TODO
+    } else if (!strcasecmp(n_local, "AllowedGroupCiphers")) {
+        // TODO
+    } else {
+        errno = ENOENT;
+        free(n_tmp);
+        return -1;
+    }
+
+out:
+    free(n_tmp);
+    return rc;
+
+out_inval:
+    errno = EINVAL;
+    free(n_tmp);
+    return -1;
+}
+
+const char *WifiNetwork::get(const char *name, char *buffer, size_t maxsize) {
+    char *n_tmp = strdup(name + strlen("wifi.net."));
+    char *n_next = n_tmp;
+    char *n_local;
+    char fc[64];
+    char rc[128];
+
+    if (!strsep(&n_next, ".")) // skip net id
+        goto out_inval;
+
+    if (!(n_local = strsep(&n_next, ".")))
+        goto out_inval;
+
+
+    strncpy(fc, n_local, sizeof(fc));
+    rc[0] = '\0';
+    if (n_next)
+        strncpy(rc, n_next, sizeof(rc));
+
+    free(n_tmp);
+
+    if (!strcasecmp(fc, "enabled"))
+        snprintf(buffer, maxsize, "%d", getEnabled());
+    else if (!strcasecmp(fc, "ssid")) {
+        strncpy(buffer,
+                getSsid() ? getSsid() : "none",
+                maxsize);
+    } else if (!strcasecmp(fc, "bssid")) {
+        strncpy(buffer,
+                getBssid() ? getBssid() : "none",
+                maxsize);
+    } else if (!strcasecmp(fc, "psk")) {
+        strncpy(buffer,
+                getPsk() ? getPsk() : "none",
+                maxsize);
+    } else if (!strcasecmp(fc, "wepkey")) {
+        strncpy(buffer,
+                getWepKey(atoi(rc)-1) ? getWepKey(atoi(rc)-1) : "none",
+                maxsize);
+    } else if (!strcasecmp(fc, "defkeyidx"))
+        snprintf(buffer, maxsize, "%d", getDefaultKeyIndex());
+    else if (!strcasecmp(fc, "pri"))
+        snprintf(buffer, maxsize, "%d", getPriority());
+    else if (!strcasecmp(fc, "AllowedKeyManagement")) {
+        if (getAllowedKeyManagement() == KeyManagementMask::NONE) 
+            strncpy(buffer, "NONE", maxsize);
+        else {
+            char tmp[80] = { '\0' };
+
+            if (getAllowedKeyManagement() & KeyManagementMask::WPA_PSK)
+                strcat(tmp, "WPA_PSK ");
+            if (getAllowedKeyManagement() & KeyManagementMask::WPA_EAP)
+                strcat(tmp, "WPA_EAP ");
+            if (getAllowedKeyManagement() & KeyManagementMask::IEEE8021X)
+                strcat(tmp, "IEEE8021X");
+            if (tmp[0] == '\0') {
+                strncpy(buffer, "(internal error)", maxsize);
+                errno = ENOENT;
+                return NULL;
+            }
+            if (tmp[strlen(tmp)] == ' ')
+                tmp[strlen(tmp)] = '\0';
+
+            strncpy(buffer, tmp, maxsize);
+        }
+    } else if (!strcasecmp(fc, "hiddenssid")) {
+        strncpy(buffer,
+                getHiddenSsid() ? getHiddenSsid() : "none",
+                maxsize);
+    } else {
+        strncpy(buffer, "(internal error)", maxsize);
+        errno = ENOENT;
+        return NULL;
+    }
+
+    return buffer;
+
+out_inval:
+    errno = EINVAL;
+    free(n_tmp);
+    return NULL;
+}
+
+int WifiNetwork::setSsid(const char *ssid) {
+    if (mSuppl->setNetworkVar(mNetid, "ssid", ssid))
+        return -1;
+    if (mSsid)
+        free(mSsid);
+    mSsid = strdup(ssid);
+    return 0;
+}
+
+int WifiNetwork::setBssid(const char *bssid) {
+    if (mSuppl->setNetworkVar(mNetid, "bssid", bssid))
+        return -1;
+    if (mBssid)
+        free(mBssid);
+    mBssid = strdup(bssid);
+    return 0;
+}
+
+int WifiNetwork::setPsk(const char *psk) {
+    if (mSuppl->setNetworkVar(mNetid, "psk", psk))
+        return -1;
+
+    if (mPsk)
+        free(mPsk);
+    mPsk = strdup(psk);
+    return 0;
+}
+
+int WifiNetwork::setWepKey(int idx, const char *key) {
+    char *name;
+
+    asprintf(&name, "wep_key%d", idx);
+    int rc = mSuppl->setNetworkVar(mNetid, name, key);
+    free(name);
+
+    if (rc)
+        return -1;
+
+    if (mWepKeys[idx])
+        free(mWepKeys[idx]);
+    mWepKeys[idx] = strdup(key);
+    return 0;
+}
+
+int WifiNetwork::setDefaultKeyIndex(int idx) {
+    char val[16];
+    sprintf(val, "%d", idx);
+    if (mSuppl->setNetworkVar(mNetid, "wep_tx_keyidx", val))
+        return -1;
+
+    mDefaultKeyIndex = idx;
+    return 0;
+}
+
+int WifiNetwork::setPriority(int priority) {
+    char val[16];
+    sprintf(val, "%d", priority);
+    if (mSuppl->setNetworkVar(mNetid, "priority", val))
+        return -1;
+
+    mPriority = priority;
+    return 0;
+}
+
+int WifiNetwork::setHiddenSsid(const char *ssid) {
+    if (mSuppl->setNetworkVar(mNetid, "scan_ssid", ssid))
+        return -1;
+
+    if (mHiddenSsid)
+        free(mHiddenSsid);
+    mHiddenSsid = strdup(ssid);
+    return 0;
+}
+
+int WifiNetwork::setAllowedKeyManagement(uint32_t mask) {
+    char accum[255];
+
+    if (mask == KeyManagementMask::NONE)
+        strcpy(accum, "NONE");
+    else {
+        if (mask & KeyManagementMask::WPA_PSK)
+            strcat(accum, "WPA_PSK ");
+        if (mask & KeyManagementMask::WPA_EAP)
+            strcat(accum, "WPA_EAP ");
+        if (mask & KeyManagementMask::IEEE8021X)
+            strcat(accum, "IEEE8021X ");
+    }
+
+    if (mSuppl->setNetworkVar(mNetid, "key_mgmt", accum))
+        return -1;
+    mAllowedKeyManagement = mask;
+    return 0;
+}
+
+int WifiNetwork::setAllowedProtocols(uint32_t mask) {
+    char accum[255];
+
+    accum[0] = '\0';
+
+    if (mask & SecurityProtocolMask::WPA)
+        strcpy(accum, "WPA ");
+
+    if (mask & SecurityProtocolMask::RSN)
+        strcat(accum, "RSN");
+
+    if (mSuppl->setNetworkVar(mNetid, "proto", accum))
+        return -1;
+    mAllowedProtocols = mask;
+    return 0;
+}
+
+int WifiNetwork::setAllowedAuthAlgorithms(uint32_t mask) {
+    char accum[255];
+
+    accum[0] = '\0';
+
+    if (mask & AuthenticationAlgorithmMask::OPEN)
+        strcpy(accum, "OPEN ");
+
+    if (mask & AuthenticationAlgorithmMask::SHARED)
+        strcat(accum, "SHARED ");
+
+    if (mask & AuthenticationAlgorithmMask::LEAP)
+        strcat(accum, "LEAP ");
+
+    if (mSuppl->setNetworkVar(mNetid, "auth_alg", accum))
+        return -1;
+
+    mAllowedAuthAlgorithms = mask;
+    return 0;
+}
+
+int WifiNetwork::setAllowedPairwiseCiphers(uint32_t mask) {
+    char accum[255];
+
+    if (mask == PairwiseCiphersMask::NONE)
+        strcpy(accum, "NONE");
+    else {
+        if (mask & PairwiseCiphersMask::TKIP)
+            strcat(accum, "TKIP ");
+        if (mask & PairwiseCiphersMask::CCMP)
+            strcat(accum, "CCMP ");
+    }
+
+    if (mSuppl->setNetworkVar(mNetid, "pairwise", accum))
+        return -1;
+
+    mAllowedPairwiseCiphers = mask;
+    return 0;
+}
+
+int WifiNetwork::setAllowedGroupCiphers(uint32_t mask) {
+    char accum[255];
+
+    if (mask & GroupCiphersMask::WEP40)
+        strcat(accum, "WEP40 ");
+    if (mask & GroupCiphersMask::WEP104)
+        strcat(accum, "WEP104 ");
+    if (mask & GroupCiphersMask::TKIP)
+        strcat(accum, "TKIP ");
+    if (mask & GroupCiphersMask::CCMP)
+        strcat(accum, "CCMP ");
+
+    if (mSuppl->setNetworkVar(mNetid, "group", accum))
+        return -1;
+    mAllowedGroupCiphers = mask;
+    return 0;
+}
+
+int WifiNetwork::setEnabled(bool enabled) {
+
+    if (enabled) {
+        if (getPriority() == -1) {
+            LOGE("Cannot enable network when priority is not set");
+            errno = EAGAIN;
+            return -1;
+        }
+        if (getAllowedKeyManagement() == KeyManagementMask::UNKNOWN) {
+            LOGE("Cannot enable network when KeyManagement is not set");
+            errno = EAGAIN;
+            return -1;
+        }
+    }
+
+    if (mSuppl->enableNetwork(mNetid, enabled))
+        return -1;
+
+    mEnabled = enabled;
+    return 0;
+}
+
+int WifiNetwork::registerProperties() {
+    for (const char **p = WifiNetwork::PropertyNames; *p != '\0'; p++) {
+        char *tmp;
+        asprintf(&tmp, "wifi.net.%d.%s", mNetid, *p);
+
+        if (NetworkManager::Instance()->getPropMngr()->registerProperty(tmp,
+                                                                        this)) {
+            free(tmp);
+            return -1;
+        }
+        free(tmp);
+    }
+    return 0;
+}
+
+int WifiNetwork::unregisterProperties() {
+    for (const char **p = WifiNetwork::PropertyNames; *p != '\0'; p++) {
+        char *tmp;
+        asprintf(&tmp, "wifi.net.%d.%s", mNetid, *p);
+
+        if (NetworkManager::Instance()->getPropMngr()->unregisterProperty(tmp))
+            LOGW("Unable to remove property '%s' (%s)", tmp, strerror(errno));
+        free(tmp);
+    }
+    return 0;
+}
diff --git a/nexus/WifiNetwork.h b/nexus/WifiNetwork.h
new file mode 100644
index 0000000..c2f5d23
--- /dev/null
+++ b/nexus/WifiNetwork.h
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#ifndef _WIFI_NETWORK_H
+#define _WIFI_NETWORK_H
+
+#include <sys/types.h>
+
+#include <utils/List.h>
+
+class KeyManagementMask {
+public:
+    static const uint32_t UNKNOWN   = 0;
+    static const uint32_t NONE      = 0x01;
+    static const uint32_t WPA_PSK   = 0x02;
+    static const uint32_t WPA_EAP   = 0x04;
+    static const uint32_t IEEE8021X = 0x08;
+    static const uint32_t ALL       = WPA_PSK | WPA_EAP | IEEE8021X;
+};
+
+class SecurityProtocolMask {
+public:
+    static const uint32_t WPA = 0x01;
+    static const uint32_t RSN = 0x02;
+};
+
+class AuthenticationAlgorithmMask {
+public:
+    static const uint32_t OPEN   = 0x01;
+    static const uint32_t SHARED = 0x02;
+    static const uint32_t LEAP   = 0x04;
+};
+
+class PairwiseCiphersMask {
+public:
+    static const uint32_t NONE = 0x00;
+    static const uint32_t TKIP = 0x01;
+    static const uint32_t CCMP = 0x02;
+};
+
+class GroupCiphersMask {
+public:
+    static const uint32_t WEP40  = 0x01;
+    static const uint32_t WEP104 = 0x02;
+    static const uint32_t TKIP   = 0x04;
+    static const uint32_t CCMP   = 0x08;
+};
+
+class Supplicant;
+class InterfaceConfig;
+class Controller;
+class WifiController;
+
+#include "IPropertyProvider.h"
+
+class WifiNetwork : public IPropertyProvider{
+public:
+    static const char *PropertyNames[];
+
+private:
+    Supplicant *mSuppl;
+    InterfaceConfig *mIfaceCfg;
+    WifiController *mController;
+
+    /*
+     * Unique network id - normally provided by supplicant
+     */
+    int mNetid;
+
+    /*
+     * The networks' SSID. Can either be an ASCII string,
+     * which must be enclosed in double quotation marks
+     * (ie: "MyNetwork"), or a string of hex digits which
+     * are not enclosed in quotes (ie: 01ab7893)
+     */
+    char *mSsid;
+
+    /*
+     * When set, this entry should only be used
+     * when associating with the AP having the specified
+     * BSSID. The value is a string in the format of an
+     * Ethernet MAC address
+     */
+    char *mBssid;
+
+    /*
+     *  Pre-shared key for use with WPA-PSK
+     */
+    char *mPsk;
+
+    /*
+     * Up to four WEP keys. Either in ASCII string enclosed in
+     * double quotes, or a string of hex digits
+     */
+    char *mWepKeys[4];
+
+    /*
+     * Default WEP key index, ranging from 0 -> NUM_WEP_KEYS -1
+     */
+    int mDefaultKeyIndex;
+
+    /*
+     * Priority determines the preference given to a network by 
+     * supplicant when choosing an access point with which
+     * to associate
+     */
+    int mPriority;
+
+    /*
+     * This is a network that does not broadcast it's SSID, so an
+     * SSID-specific probe request must be used for scans.
+     */
+    char *mHiddenSsid;
+
+    /*
+     * The set of key management protocols supported by this configuration.
+     */
+    uint32_t mAllowedKeyManagement;
+
+    /*
+     * The set of security protocols supported by this configuration.
+     */
+    uint32_t mAllowedProtocols;
+
+    /*
+     * The set of authentication protocols supported by this configuration.
+     */
+    uint32_t mAllowedAuthAlgorithms;
+
+    /*
+     * The set of pairwise ciphers for WPA supported by this configuration.
+     */
+    uint32_t mAllowedPairwiseCiphers;
+
+    /*
+     * The set of group ciphers for WPA supported by this configuration.
+     */
+    uint32_t mAllowedGroupCiphers;
+
+    /*
+     * Set if this Network is enabled
+     */
+    bool mEnabled;
+
+private:
+    WifiNetwork();
+
+public:
+    WifiNetwork(WifiController *c, Supplicant *suppl, int networkId);
+    WifiNetwork(WifiController *c, Supplicant *suppl, const char *data);
+
+    virtual ~WifiNetwork();
+
+    WifiNetwork *clone();
+    int registerProperties();
+    int unregisterProperties();
+
+    int getNetworkId() { return mNetid; }
+    const char *getSsid() { return mSsid; }
+    const char *getBssid() { return mBssid; }
+    const char *getPsk() { return mPsk; }
+    const char *getWepKey(int idx) { return mWepKeys[idx]; }
+    int getDefaultKeyIndex() { return mDefaultKeyIndex; }
+    int getPriority() { return mPriority; }
+    const char *getHiddenSsid() { return mHiddenSsid; }
+    uint32_t getAllowedKeyManagement() { return mAllowedKeyManagement; }
+    uint32_t getAllowedProtocols() { return mAllowedProtocols; }
+    uint32_t getAllowedAuthAlgorithms() { return mAllowedAuthAlgorithms; }
+    uint32_t getAllowedPairwiseCiphers() { return mAllowedPairwiseCiphers; }
+    uint32_t getAllowedGroupCiphers() { return mAllowedGroupCiphers; }
+    bool getEnabled() { return mEnabled; }
+    Controller *getController() { return (Controller *) mController; }
+
+    int set(const char *name, const char *value);
+    const char *get(const char *name, char *buffer, size_t maxsize);
+
+    InterfaceConfig *getIfaceCfg() { return mIfaceCfg; }
+
+    int setEnabled(bool enabled);
+    int setSsid(const char *ssid);
+    int setBssid(const char *bssid);
+    int setPsk(const char *psk);
+    int setWepKey(int idx, const char *key);
+    int setDefaultKeyIndex(int idx);
+    int setPriority(int pri);
+    int setHiddenSsid(const char *ssid);
+    int setAllowedKeyManagement(uint32_t mask);
+    int setAllowedProtocols(uint32_t mask);
+    int setAllowedAuthAlgorithms(uint32_t mask);
+    int setAllowedPairwiseCiphers(uint32_t mask);
+    int setAllowedGroupCiphers(uint32_t mask);
+
+    // XXX:Should this really be exposed?.. meh
+    int refresh();
+};
+
+typedef android::List<WifiNetwork *> WifiNetworkCollection;
+
+#endif
diff --git a/nexus/WifiScanner.cpp b/nexus/WifiScanner.cpp
new file mode 100644
index 0000000..bdfa497
--- /dev/null
+++ b/nexus/WifiScanner.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <pthread.h>
+
+#define LOG_TAG "WifiScanner"
+#include <cutils/log.h>
+
+#include "WifiScanner.h"
+#include "Supplicant.h"
+
+extern "C" int pthread_cancel(pthread_t thread);
+
+WifiScanner::WifiScanner(Supplicant *suppl, int period) {
+    mSuppl = suppl;
+    mPeriod = period;
+    mActive = false;
+}
+
+int WifiScanner::start(bool active) {
+    mActive = active;
+
+    if(pipe(mCtrlPipe))
+        return -1;
+
+    if (pthread_create(&mThread, NULL, WifiScanner::threadStart, this))
+        return -1;
+    return 0;
+}
+
+void *WifiScanner::threadStart(void *obj) {
+    WifiScanner *me = reinterpret_cast<WifiScanner *>(obj);
+    me->run();
+    pthread_exit(NULL);
+    return NULL;
+}
+
+int WifiScanner::stop() {
+    char c = 0;
+
+    if (write(mCtrlPipe[1], &c, 1) != 1) {
+        LOGE("Error writing to control pipe (%s)", strerror(errno));
+        return -1;
+    }
+
+    void *ret;
+    if (pthread_join(mThread, &ret)) {
+        LOGE("Error joining to scanner thread (%s)", strerror(errno));
+        return -1;
+    }
+
+    close(mCtrlPipe[0]);
+    close(mCtrlPipe[1]);
+    return 0;
+}
+
+void WifiScanner::run() {
+    LOGD("Starting wifi scanner (active = %d)", mActive);
+
+    while(1) {
+        fd_set read_fds;
+        struct timeval to;
+        int rc = 0;
+
+        to.tv_usec = 0;
+        to.tv_sec = mPeriod;
+
+        FD_ZERO(&read_fds);
+        FD_SET(mCtrlPipe[0], &read_fds);
+
+        if (mSuppl->triggerScan(mActive)) {
+            LOGW("Error triggering scan (%s)", strerror(errno));
+        }
+
+        if ((rc = select(mCtrlPipe[0] + 1, &read_fds, NULL, NULL, &to)) < 0) {
+            LOGE("select failed (%s) - sleeping for one scanner period", strerror(errno));
+            sleep(mPeriod);
+            continue;
+        } else if (!rc) {
+        } else if (FD_ISSET(mCtrlPipe[0], &read_fds))
+            break;
+    } // while
+    LOGD("Stopping wifi scanner");
+}
diff --git a/nexus/WifiScanner.h b/nexus/WifiScanner.h
new file mode 100644
index 0000000..92822e9
--- /dev/null
+++ b/nexus/WifiScanner.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef _WIFISCANNER_H
+#define _WIFISCANNER_H
+
+#include <pthread.h>
+
+class Supplicant;
+
+class WifiScanner {
+    pthread_t  mThread;
+    int        mCtrlPipe[2];
+    Supplicant *mSuppl;
+    int        mPeriod;
+    bool       mActive;
+    
+
+public:
+    WifiScanner(Supplicant *suppl, int period);
+    virtual ~WifiScanner() {}
+
+    int getPeriod() { return mPeriod; }
+
+    int start(bool active);
+    int stop();
+
+private:
+    static void *threadStart(void *obj);
+
+    void run();
+};
+
+#endif
diff --git a/nexus/main.cpp b/nexus/main.cpp
new file mode 100644
index 0000000..936d33f
--- /dev/null
+++ b/nexus/main.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 <stdlib.h>
+#include <errno.h>
+
+#define LOG_TAG "Nexus"
+
+#include "cutils/log.h"
+#include "NetworkManager.h"
+#include "CommandListener.h"
+
+#include "LoopController.h"
+#include "OpenVpnController.h"
+#include "TiwlanWifiController.h"
+
+int main() {
+    LOGI("Nexus version 0.1 firing up");
+
+    CommandListener *cl = new CommandListener();
+
+    NetworkManager *nm;
+    if (!(nm = NetworkManager::Instance())) {
+        LOGE("Unable to create NetworkManager");
+        exit (-1);
+    };
+
+    nm->setBroadcaster((SocketListener *) cl);
+
+    nm->attachController(new LoopController(nm->getPropMngr(), nm));
+    nm->attachController(new TiwlanWifiController(nm->getPropMngr(), nm, "/system/lib/modules/wlan.ko", "wlan", ""));
+//    nm->attachController(new AndroidL2TPVpnController(nm->getPropMngr(), nm));
+    nm->attachController(new OpenVpnController(nm->getPropMngr(), nm));
+
+
+    if (NetworkManager::Instance()->run()) {
+        LOGE("Unable to Run NetworkManager (%s)", strerror(errno));
+        exit (1);
+    }
+
+    if (cl->startListener()) {
+        LOGE("Unable to start CommandListener (%s)", strerror(errno));
+        exit (1);
+    }
+
+    // XXX: we'll use the main thread for the NetworkManager eventually
+
+    while(1) {
+        sleep(1000);
+    }
+
+    LOGI("Nexus exiting");
+    exit(0);
+}
diff --git a/nexus/nexctl.c b/nexus/nexctl.c
new file mode 100644
index 0000000..8e1d90c
--- /dev/null
+++ b/nexus/nexctl.c
@@ -0,0 +1,148 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+
+static void usage(char *progname);
+static int do_monitor(int sock, int stop_after_cmd);
+static int do_cmd(int sock, int argc, char **argv);
+
+int main(int argc, char **argv) {
+    int sock;
+
+    if (argc < 2)
+        usage(argv[0]);
+
+    if ((sock = socket_local_client("nexus",
+                                     ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                     SOCK_STREAM)) < 0) {
+        fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
+        exit(4);
+    }
+
+    if (!strcmp(argv[1], "monitor"))
+        exit(do_monitor(sock, 0));
+    exit(do_cmd(sock, argc, argv));
+}
+
+static int do_cmd(int sock, int argc, char **argv) {
+    char final_cmd[255] = { '\0' };
+    int i;
+
+    for (i = 1; i < argc; i++) {
+        char *cmp;
+
+        if (!index(argv[i], ' '))
+            asprintf(&cmp, "%s%s", argv[i], (i == (argc -1)) ? "" : " ");
+        else
+            asprintf(&cmp, "\"%s\"%s", argv[i], (i == (argc -1)) ? "" : " ");
+
+        strcat(final_cmd, cmp);
+        free(cmp);
+    }
+
+    if (write(sock, final_cmd, strlen(final_cmd) + 1) < 0) {
+        perror("write");
+        return errno;
+    }
+
+    return do_monitor(sock, 1);
+}
+
+static int do_monitor(int sock, int stop_after_cmd) {
+    char *buffer = malloc(4096);
+
+    if (!stop_after_cmd)
+        printf("[Connected to Nexus]\n");
+
+    while(1) {
+        fd_set read_fds;
+        struct timeval to;
+        int rc = 0;
+
+        to.tv_sec = 10;
+        to.tv_usec = 0;
+
+        FD_ZERO(&read_fds);
+        FD_SET(sock, &read_fds);
+
+        if ((rc = select(sock +1, &read_fds, NULL, NULL, &to)) < 0) {
+            fprintf(stderr, "Error in select (%s)\n", strerror(errno));
+            free(buffer);
+            return errno;
+        } else if (!rc) {
+            continue;
+            fprintf(stderr, "[TIMEOUT]\n");
+            return ETIMEDOUT;
+        } else if (FD_ISSET(sock, &read_fds)) {
+            memset(buffer, 0, 4096);
+            if ((rc = read(sock, buffer, 4096)) <= 0) {
+                if (rc == 0)
+                    fprintf(stderr, "Lost connection to Nexus - did it crash?\n");
+                else
+                    fprintf(stderr, "Error reading data (%s)\n", strerror(errno));
+                free(buffer);
+                if (rc == 0)
+                    return ECONNRESET;
+                return errno;
+            }
+            
+            int offset = 0;
+            int i = 0;
+
+            for (i = 0; i < rc; i++) {
+                if (buffer[i] == '\0') {
+                    int code;
+                    char tmp[4];
+
+                    strncpy(tmp, buffer + offset, 3);
+                    tmp[3] = '\0';
+                    code = atoi(tmp);
+
+                    printf("%s\n", buffer + offset);
+                    if (stop_after_cmd) {
+                        if (code >= 200 && code < 600)
+                            return 0;
+                    }
+                    offset = i + 1;
+                }
+            }
+        }
+    }
+    free(buffer);
+    return 0;
+}
+
+static void usage(char *progname) {
+    fprintf(stderr, "Usage: %s <monitor>|<cmd> [arg1] [arg2...]\n", progname);
+    exit(1);
+}
+
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index b2fe8cf..e66b1c3 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -6,14 +6,9 @@
 copy_from := \
 	etc/dbus.conf \
 	etc/init.goldfish.sh \
+	etc/ppp/ip-up-vpn \
 	etc/hosts
 
-dont_copy := \
-	etc/init.gprs-pppd \
-	etc/ppp/chap-secrets \
-	etc/ppp/ip-down \
-	etc/ppp/ip-up
-
 copy_to := $(addprefix $(TARGET_OUT)/,$(copy_from))
 copy_from := $(addprefix $(LOCAL_PATH)/,$(copy_from))
 
diff --git a/rootdir/etc/init.goldfish.sh b/rootdir/etc/init.goldfish.sh
index 0eb0154..f1b801d 100755
--- a/rootdir/etc/init.goldfish.sh
+++ b/rootdir/etc/init.goldfish.sh
@@ -34,6 +34,10 @@
     ;;
 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
diff --git a/rootdir/etc/init.gprs-pppd b/rootdir/etc/init.gprs-pppd
deleted file mode 100755
index 521eec9..0000000
--- a/rootdir/etc/init.gprs-pppd
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/system/bin/sh
-# An unforunate wrapper script 
-# so that the exit code of pppd may be retrieved
-
-
-# this is a workaround for issue #651747
-#trap "/system/bin/sleep 1;exit 0" TERM
-
-
-PPPD_PID=
-
-/system/bin/setprop "net.gprs.ppp-exit" ""
-
-/system/bin/log -t pppd "Starting pppd"
-
-/system/bin/pppd $*
-
-PPPD_EXIT=$?
-PPPD_PID=$!
-
-/system/bin/log -t pppd "pppd exited with $PPPD_EXIT"
-
-/system/bin/setprop "net.gprs.ppp-exit" "$PPPD_EXIT"
diff --git a/rootdir/etc/ppp/chap-secrets b/rootdir/etc/ppp/chap-secrets
deleted file mode 100644
index 6546b0f..0000000
--- a/rootdir/etc/ppp/chap-secrets
+++ /dev/null
@@ -1,2 +0,0 @@
-* * bogus
-
diff --git a/rootdir/etc/ppp/ip-down b/rootdir/etc/ppp/ip-down
deleted file mode 100755
index 672fa1e..0000000
--- a/rootdir/etc/ppp/ip-down
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/system/bin/sh
-case $1 in
-    ppp1)
-	echo 0 > /proc/sys/net/ipv4/ip_forward;
-	;;
-esac
-
-# Use interface name if linkname is not available
-NAME=${LINKNAME:-"$1"}
-
-/system/bin/setprop "net.$NAME.dns1" "$DNS1"
-/system/bin/setprop "net.$NAME.dns2" "$DNS2" 
-/system/bin/setprop "net.$NAME.local-ip" "$IPLOCAL" 
-/system/bin/setprop "net.$NAME.remote-ip" "$IPREMOTE" 
diff --git a/rootdir/etc/ppp/ip-up b/rootdir/etc/ppp/ip-up
deleted file mode 100755
index cb2d577..0000000
--- a/rootdir/etc/ppp/ip-up
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/system/bin/sh
-case $1 in
-    ppp1)
-	/android/bin/iptables --flush;
-	/android/bin/iptables --table nat --flush;
-	/android/bin/iptables --delete-chain;
-	/android/bin/iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE;
-	/android/bin/iptables --append FORWARD --in-interface ppp1 -j ACCEPT;
-	echo 0 > /proc/sys/net/ipv4/ip_forward;
-	echo 1 > /proc/sys/net/ipv4/ip_forward;
-	;;
-    ppp0)
-        /system/bin/setprop "net.interfaces.defaultroute" "gprs"
-        ;;
-esac
-
-# Use interface name if linkname is not available
-NAME=${LINKNAME:-"$1"}
-
-/system/bin/setprop "net.$NAME.dns1" "$DNS1"
-/system/bin/setprop "net.$NAME.dns2" "$DNS2" 
-/system/bin/setprop "net.$NAME.local-ip" "$IPLOCAL" 
-/system/bin/setprop "net.$NAME.remote-ip" "$IPREMOTE" 
-
diff --git a/rootdir/etc/ppp/ip-up-vpn b/rootdir/etc/ppp/ip-up-vpn
new file mode 100755
index 0000000..5d6b611
--- /dev/null
+++ b/rootdir/etc/ppp/ip-up-vpn
@@ -0,0 +1,18 @@
+#!/system/bin/sh
+
+/system/bin/setprop "vpn.dns1" "$DNS1"
+/system/bin/setprop "vpn.dns2" "$DNS2"
+
+GATEWAY=${6#*@}
+VPNSERVER=${6%@*}
+
+# Protect the route to vpn server
+/system/bin/route add -net "$VPNSERVER" netmask 255.255.255.255 gw "$GATEWAY"
+
+if (exp $?) ; then exit $?; fi
+
+# Route all traffic to vpn connection
+/system/bin/route add -net 0.0.0.0 netmask 128.0.0.0 gw $IPREMOTE
+/system/bin/route add -net 128.0.0.0 netmask 128.0.0.0 gw $IPREMOTE
+
+/system/bin/setprop "vpn.up" "1"
diff --git a/rootdir/init.rc b/rootdir/init.rc
index bcabecb..799995a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -23,6 +23,7 @@
     mkdir /system
     mkdir /data 0771 system system
     mkdir /cache 0770 system cache
+    mkdir /config 0500 root root
     mkdir /sqlite_stmt_journals 01777 root root
     mount tmpfs tmpfs /sqlite_stmt_journals size=4m
 
@@ -33,10 +34,29 @@
     write /proc/cpu/alignment 4
     write /proc/sys/kernel/sched_latency_ns 10000000
     write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
+    write /proc/sys/kernel/sched_compat_yield 1
+
+# Create cgroup mount points for process groups
+    mkdir /dev/cpuctl
+    mount cgroup none /dev/cpuctl cpu
+    chown sytem system /dev/cpuctl
+    chown system system /dev/cpuctl/tasks
+    chmod 0777 /dev/cpuctl/tasks
+    write /dev/cpuctl/cpu.shares 1024
+
+    mkdir /dev/cpuctl/fg_boost
+    chown system system /dev/cpuctl/fg_boost/tasks
+    chmod 0777 /dev/cpuctl/fg_boost/tasks
+    write /dev/cpuctl/fg_boost/cpu.shares 1024
+
+    mkdir /dev/cpuctl/bg_non_interactive
+    chown system system /dev/cpuctl/bg_non_interactive/tasks
+    chmod 0777 /dev/cpuctl/bg_non_interactive/tasks
+    write /dev/cpuctl/bg_non_interactive/cpu.shares 16
 
 # mount mtd partitions
     # Mount /system rw first to give the filesystem a chance to save a checkpoint
-    mount yaffs2 mtd@system /system 
+    mount yaffs2 mtd@system /system
     mount yaffs2 mtd@system /system ro remount
 
     # We chown/chmod /data again so because mount is run as root + defaults
@@ -55,7 +75,14 @@
 
 # create basic filesystem structure
     mkdir /data/misc 01771 system misc
-    mkdir /data/misc/hcid 0770 bluetooth bluetooth
+    mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth
+    mkdir /data/misc/keystore 0770 keystore keystore
+    mkdir /data/misc/vpn 0770 system system
+    mkdir /data/misc/vpn/profiles 0770 system system
+    # give system access to wpa_supplicant.conf for backup and restore
+    mkdir /data/misc/wifi 0770 wifi wifi
+    chmod 0770 /data/misc/wifi
+    chmod 0660 /data/misc/wifi/wpa_supplicant.conf
     mkdir /data/local 0771 shell shell
     mkdir /data/local/tmp 0771 shell shell
     mkdir /data/data 0771 system system
@@ -87,14 +114,12 @@
 # set RLIMIT_NICE to allow priorities from 19 to -20
     setrlimit 13 40 40
 
-# Set timeout value for rmnet stats.
-    write /sys/devices/virtual/net/rmnet0/timeout_suspend 5000000
-
 # Define the oom_adj values for the classes of processes that can be
 # killed by the kernel.  These are used in ActivityManagerService.
     setprop ro.FOREGROUND_APP_ADJ 0
     setprop ro.VISIBLE_APP_ADJ 1
     setprop ro.SECONDARY_SERVER_ADJ 2
+    setprop ro.BACKUP_APP_ADJ 2
     setprop ro.HOME_APP_ADJ 4
     setprop ro.HIDDEN_APP_MIN_ADJ 7
     setprop ro.CONTENT_PROVIDER_ADJ 14
@@ -105,6 +130,7 @@
     setprop ro.FOREGROUND_APP_MEM 1536
     setprop ro.VISIBLE_APP_MEM 2048
     setprop ro.SECONDARY_SERVER_MEM 4096
+    setprop ro.BACKUP_APP_MEM 4096
     setprop ro.HOME_APP_MEM 4096
     setprop ro.HIDDEN_APP_MEM 5120
     setprop ro.CONTENT_PROVIDER_MEM 5632
@@ -199,6 +225,10 @@
 service vold /system/bin/vold
     socket vold stream 0660 root mount
 
+service nexus /system/bin/nexus
+    socket nexus stream 0660 root system
+    disabled
+
 #service mountd /system/bin/mountd
 #    socket mountd stream 0660 root mount
 
@@ -224,16 +254,24 @@
     group audio
     oneshot
 
-service dbus /system/bin/dbus-daemon --system --nofork
+service bootanim /system/bin/bootanimation
+    user graphics
+    group graphics
+    disabled
+    oneshot
+
+#STOPSHIP: Remove logwrapper before production
+service dbus /system/bin/logwrapper /system/bin/dbus-daemon --system --nofork
     socket dbus stream 660 bluetooth bluetooth
     user bluetooth
     group bluetooth net_bt_admin
 
-service hcid /system/bin/hcid -s -n -f /etc/bluez/hcid.conf
+#STOPSHIP: Remove logwrapper and -d option before production
+service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd -d -n
     socket bluetooth stream 660 bluetooth bluetooth
     socket dbus_bluetooth stream 660 bluetooth bluetooth
     # init.rc does not yet support applying capabilities, so run as root and
-    # let hcid drop uid to bluetooth with the right linux capabilities
+    # let bluetoothd drop uid to bluetooth with the right linux capabilities
     group bluetooth net_bt_admin misc
     disabled
 
@@ -254,3 +292,23 @@
 
 service flash_recovery /system/bin/flash_image recovery /system/recovery.img
     oneshot
+
+service racoon /system/bin/racoon
+    socket racoon stream 600 system system
+    # racoon will setuid to vpn after getting necessary resources.
+    group net_admin keystore
+    disabled
+    oneshot
+
+service mtpd /system/bin/mtpd
+    socket mtpd stream 600 system system
+    user vpn
+    group vpn net_admin net_raw
+    disabled
+    oneshot
+
+service keystore /system/bin/keystore
+    user keystore
+    group keystore
+    socket keystore stream 666
+
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 5a8dc0b..70b13dc 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -42,7 +42,7 @@
 	smd \
 	chmod \
     chown \
-	mkdosfs \
+	newfs_msdos \
 	netstat \
 	ioctl \
 	mv \
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index 14372cb..256720d 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -28,6 +28,7 @@
 {
     uint8_t *bits = NULL;
     ssize_t bits_size = 0;
+    const char* label;
     int i, j, k;
     int res, res2;
     
@@ -45,21 +46,48 @@
                 return 1;
             }
         }
+        res2 = 0;
         switch(i) {
+            case EV_SYN:
+                label = "SYN";
+                break;
             case EV_KEY:
                 res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size);
+                label = "KEY";
+                break;
+            case EV_REL:
+                label = "REL";
+                break;
+            case EV_ABS:
+                label = "ABS";
+                break;
+            case EV_MSC:
+                label = "MSC";
                 break;
             case EV_LED:
                 res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size);
+                label = "LED";
                 break;
             case EV_SND:
                 res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size);
+                label = "SND";
                 break;
             case EV_SW:
                 res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size);
+                label = "SW ";
+                break;
+            case EV_REP:
+                label = "REP";
+                break;
+            case EV_FF:
+                label = "FF ";
+                break;
+            case EV_PWR:
+                label = "PWR";
                 break;
             default:
                 res2 = 0;
+                label = "???";
         }
         for(j = 0; j < res; j++) {
             for(k = 0; k < 8; k++)
@@ -70,9 +98,9 @@
                     else
                         down = ' ';
                     if(count == 0)
-                        printf("    type %04x:", i);
+                        printf("    %s (%04x):", label, i);
                     else if((count & 0x7) == 0 || i == EV_ABS)
-                        printf("\n              ");
+                        printf("\n               ");
                     printf(" %04x%c", j * 8 + k, down);
                     if(i == EV_ABS) {
                         struct input_absinfo abs;
@@ -264,7 +292,16 @@
 
 static void usage(int argc, char *argv[])
 {
-    fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-q] [-c count] [-r] [device]\n", argv[0]);
+    fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-p] [-q] [-c count] [-r] [device]\n", argv[0]);
+    fprintf(stderr, "    -t: show time stamps\n");
+    fprintf(stderr, "    -n: don't print newlines\n");
+    fprintf(stderr, "    -s: print switch states for given bits\n");
+    fprintf(stderr, "    -S: print all switch states\n");
+    fprintf(stderr, "    -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32)\n");
+    fprintf(stderr, "    -p: show possible events (errs, dev, name, pos. events)\n");
+    fprintf(stderr, "    -q: quiet (clear verbosity mask)\n");
+    fprintf(stderr, "    -c: print given number of events then exit\n");
+    fprintf(stderr, "    -r: print rate events are received\n");
 }
 
 int getevent_main(int argc, char *argv[])
@@ -290,7 +327,7 @@
 
     opterr = 0;
     do {
-        c = getopt(argc, argv, "tns:Sv::qc:rh");
+        c = getopt(argc, argv, "tns:Sv::pqc:rh");
         if (c == EOF)
             break;
         switch (c) {
@@ -317,6 +354,12 @@
                 print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
             print_flags_set = 1;
             break;
+        case 'p':
+            print_flags = PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS;
+            print_flags_set = 1;
+            if(dont_block == -1)
+                dont_block = 1;
+            break;
         case 'q':
             print_flags = 0;
             print_flags_set = 1;
diff --git a/toolbox/ifconfig.c b/toolbox/ifconfig.c
index e83cd8b..80c0e5c 100644
--- a/toolbox/ifconfig.c
+++ b/toolbox/ifconfig.c
@@ -28,20 +28,32 @@
 
 static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr)
 {
-	sin->sin_family = AF_INET;
-	sin->sin_port = 0;
-	sin->sin_addr.s_addr = inet_addr(addr);
+    sin->sin_family = AF_INET;
+    sin->sin_port = 0;
+    sin->sin_addr.s_addr = inet_addr(addr);
+}
+
+static void setmtu(int s, struct ifreq *ifr, const char *mtu)
+{
+    int m = atoi(mtu);
+    ifr->ifr_mtu = m;
+    if(ioctl(s, SIOCSIFMTU, ifr) < 0) die("SIOCSIFMTU");
+}
+static void setdstaddr(int s, struct ifreq *ifr, const char *addr)
+{
+    init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_dstaddr, addr);
+    if(ioctl(s, SIOCSIFDSTADDR, ifr) < 0) die("SIOCSIFDSTADDR");
 }
 
 static void setnetmask(int s, struct ifreq *ifr, const char *addr)
 {
-	init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_netmask, addr);
+    init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_netmask, addr);
     if(ioctl(s, SIOCSIFNETMASK, ifr) < 0) die("SIOCSIFNETMASK");
 }
 
 static void setaddr(int s, struct ifreq *ifr, const char *addr)
 {
-	init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_addr, addr);
+    init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_addr, addr);
     if(ioctl(s, SIOCSIFADDR, ifr) < 0) die("SIOCSIFADDR");
 }
 
@@ -109,31 +121,43 @@
         running = (flags & IFF_RUNNING)      ? " running" : "";
         multi =   (flags & IFF_MULTICAST)    ? " multicast" : "";
         printf("%s%s%s%s%s%s]\n", updown, brdcst, loopbk, ppp, running, multi);
-
-
-
-/*    char *updown, *brdcst, *loopbk, *ppp, *running, *multi; */
-
         return 0;
     }
     
-    while(argc > 0){
-        if(!strcmp(argv[0], "up")) {
+    while(argc > 0) {
+        if (!strcmp(argv[0], "up")) {
             setflags(s, &ifr, IFF_UP, 0);
-        } else if(!strcmp(argv[0], "down")) {
+        } else if (!strcmp(argv[0], "mtu")) {
+            argc--, argv++;
+            if (!argc) {
+                errno = EINVAL;
+                die("expecting a value for parameter \"mtu\"");
+            }
+            setmtu(s, &ifr, argv[0]);
+        } else if (!strcmp(argv[0], "-pointopoint")) {
+            setflags(s, &ifr, IFF_POINTOPOINT, 1);
+        } else if (!strcmp(argv[0], "pointopoint")) {
+            argc--, argv++;
+            if (!argc) { 
+                errno = EINVAL;
+                die("expecting an IP address for parameter \"pointtopoint\"");
+            }
+            setdstaddr(s, &ifr, argv[0]);
+            setflags(s, &ifr, IFF_POINTOPOINT, 0);
+        } else if (!strcmp(argv[0], "down")) {
             setflags(s, &ifr, 0, IFF_UP);
-		} else if(!strcmp(argv[0], "netmask")) {
-			argc--, argv++;
-			if (0 == argc) { 
-				errno = EINVAL;
-				die("expecting an IP address for parameter \"netmask\"");
-			}
-			setnetmask(s, &ifr, argv[0]);
-        } else if(isdigit(argv[0][0])){
+        } else if (!strcmp(argv[0], "netmask")) {
+            argc--, argv++;
+            if (!argc) { 
+                errno = EINVAL;
+                die("expecting an IP address for parameter \"netmask\"");
+            }
+            setnetmask(s, &ifr, argv[0]);
+        } else if (isdigit(argv[0][0])) {
             setaddr(s, &ifr, argv[0]);
+            setflags(s, &ifr, IFF_UP, 0);
         }
         argc--, argv++;
     }
-
     return 0;
 }
diff --git a/toolbox/ls.c b/toolbox/ls.c
index f609df21..087e4d5 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -15,9 +15,10 @@
 #include <linux/kdev_t.h>
 
 // bits for flags argument
-#define LIST_LONG       (1 << 0)
-#define LIST_ALL        (1 << 1)
-#define LIST_RECURSIVE  (1 << 2)
+#define LIST_LONG           (1 << 0)
+#define LIST_ALL            (1 << 1)
+#define LIST_RECURSIVE      (1 << 2)
+#define LIST_DIRECTORIES    (1 << 3)
 
 // fwd
 static int listpath(const char *name, int flags);
@@ -238,7 +239,7 @@
         return -1;
     }
 
-    if (S_ISDIR(s.st_mode)) {
+    if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
         if (flags & LIST_RECURSIVE)
             printf("\n%s:\n", name);
         return listdir(name, flags);
@@ -269,6 +270,8 @@
                 flags |= LIST_ALL;
             } else if (!strcmp(argv[i], "-R")) {
                 flags |= LIST_RECURSIVE;
+            } else if (!strcmp(argv[i], "-d")) {
+                flags |= LIST_DIRECTORIES;
             } else {
                 listed++;
                 if(listpath(argv[i], flags) != 0) {
diff --git a/toolbox/mkdosfs.c b/toolbox/mkdosfs.c
deleted file mode 100644
index 744aad1..0000000
--- a/toolbox/mkdosfs.c
+++ /dev/null
@@ -1,848 +0,0 @@
-/*	$NetBSD: newfs_msdos.c,v 1.18.2.1 2005/05/01 18:44:02 tron Exp $	*/
-
-/*
- * Copyright (c) 1998 Robert Nordier
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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 __USE_FILE_OFFSET64
-
-#include <sys/cdefs.h>
-
-#include <sys/types.h>
-#include <sys/param.h>
-#ifdef __FreeBSD__
-#include <sys/diskslice.h>
-#endif
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/ioctl.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <paths.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#ifdef __NetBSD__
-#include <disktab.h>
-#include <util.h>
-#endif
-
-#define MAXU16	  0xffff	/* maximum unsigned 16-bit quantity */
-#define BPN	  4		/* bits per nibble */
-#define NPB	  2		/* nibbles per byte */
-
-#define DOSMAGIC  0xaa55	/* DOS magic number */
-#define MINBPS	  128		/* minimum bytes per sector */
-#define MAXSPC	  128		/* maximum sectors per cluster */
-#define MAXNFT	  16		/* maximum number of FATs */
-#define DEFBLK	  4096		/* default block size */
-#define DEFBLK16  2048		/* default block size FAT16 */
-#define DEFRDE	  512		/* default root directory entries */
-#define RESFTE	  2		/* reserved FAT entries */
-#define MINCLS12  1		/* minimum FAT12 clusters */
-#define MINCLS16  0x1000	/* minimum FAT16 clusters */
-#define MINCLS32  2		/* minimum FAT32 clusters */
-#define MAXCLS12  0xfed 	/* maximum FAT12 clusters */
-#define MAXCLS16  0xfff5	/* maximum FAT16 clusters */
-#define MAXCLS32  0xffffff5	/* maximum FAT32 clusters */
-
-#define mincls(fat)  ((fat) == 12 ? MINCLS12 :	\
-		      (fat) == 16 ? MINCLS16 :	\
-				    MINCLS32)
-
-#define maxcls(fat)  ((fat) == 12 ? MAXCLS12 :	\
-		      (fat) == 16 ? MAXCLS16 :	\
-				    MAXCLS32)
-
-#define mk1(p, x)				\
-    (p) = (u_int8_t)(x)
-
-#define mk2(p, x)				\
-    (p)[0] = (u_int8_t)(x),			\
-    (p)[1] = (u_int8_t)((x) >> 010)
-
-#define mk4(p, x)				\
-    (p)[0] = (u_int8_t)(x),			\
-    (p)[1] = (u_int8_t)((x) >> 010),		\
-    (p)[2] = (u_int8_t)((x) >> 020),		\
-    (p)[3] = (u_int8_t)((x) >> 030)
-
-#define argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
-#define argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
-#define argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
-#define argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
-
-#ifndef MAX
-#define MAX(x, y) ((x) > (y) ? (x) : (y))
-#endif
-#ifndef MIN
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-#endif
-
-static int powerof2(int x) {
-    int i;
-    for (i = 0; i < 32; i++) {
-        if (x & 1) {
-            x >>= 1;
-            // if x is zero, then original x was a power of two
-            return (x == 0);
-        }
-        x >>= 1;
-    }
-    
-    return 0;
-}
-
-#ifndef howmany
-#define   howmany(x, y)   (((x)+((y)-1))/(y))
-#endif
- 
-#pragma pack(push, 1)
-struct bs {
-    u_int8_t jmp[3];		/* bootstrap entry point */
-    u_int8_t oem[8];		/* OEM name and version */
-};
-#define BS_SIZE 11
-
-struct bsbpb {
-    u_int8_t bps[2];		/* bytes per sector */
-    u_int8_t spc;		/* sectors per cluster */
-    u_int8_t res[2];		/* reserved sectors */
-    u_int8_t nft;		/* number of FATs */
-    u_int8_t rde[2];		/* root directory entries */
-    u_int8_t sec[2];		/* total sectors */
-    u_int8_t mid;		/* media descriptor */
-    u_int8_t spf[2];		/* sectors per FAT */
-    u_int8_t spt[2];		/* sectors per track */
-    u_int8_t hds[2];		/* drive heads */
-    u_int8_t hid[4];		/* hidden sectors */
-    u_int8_t bsec[4];		/* big total sectors */
-};
-#define BSBPB_SIZE 25
-
-struct bsxbpb {
-    u_int8_t bspf[4];		/* big sectors per FAT */
-    u_int8_t xflg[2];		/* FAT control flags */
-    u_int8_t vers[2];		/* file system version */
-    u_int8_t rdcl[4];		/* root directory start cluster */
-    u_int8_t infs[2];		/* file system info sector */
-    u_int8_t bkbs[2];		/* backup boot sector */
-    u_int8_t rsvd[12];		/* reserved */
-};
-#define BSXBPB_SIZE 28
-
-struct bsx {
-    u_int8_t drv;		/* drive number */
-    u_int8_t rsvd;		/* reserved */
-    u_int8_t sig;		/* extended boot signature */
-    u_int8_t volid[4];		/* volume ID number */
-    u_int8_t label[11]; 	/* volume label */
-    u_int8_t type[8];		/* file system type */
-};
-#define BSX_SIZE 26
-
-struct de {
-    u_int8_t namext[11];	/* name and extension */
-    u_int8_t attr;		/* attributes */
-    u_int8_t rsvd[10];		/* reserved */
-    u_int8_t time[2];		/* creation time */
-    u_int8_t date[2];		/* creation date */
-    u_int8_t clus[2];		/* starting cluster */
-    u_int8_t size[4];		/* size */
-#define DE_SIZE 32
-};
-#pragma pack(pop)
-
-struct bpb {
-    u_int bps;			/* bytes per sector */
-    u_int spc;			/* sectors per cluster */
-    u_int res;			/* reserved sectors */
-    u_int nft;			/* number of FATs */
-    u_int rde;			/* root directory entries */
-    u_int sec;			/* total sectors */
-    u_int mid;			/* media descriptor */
-    u_int spf;			/* sectors per FAT */
-    u_int spt;			/* sectors per track */
-    u_int hds;			/* drive heads */
-    u_int hid;			/* hidden sectors */
-    u_int bsec; 		/* big total sectors */
-    u_int bspf; 		/* big sectors per FAT */
-    u_int rdcl; 		/* root directory start cluster */
-    u_int infs; 		/* file system info sector */
-    u_int bkbs; 		/* backup boot sector */
-};
-
-static u_int8_t bootcode[] = {
-    0xfa,			/* cli		    */
-    0x31, 0xc0, 		/* xor	   ax,ax    */
-    0x8e, 0xd0, 		/* mov	   ss,ax    */
-    0xbc, 0x00, 0x7c,		/* mov	   sp,7c00h */
-    0xfb,			/* sti		    */
-    0x8e, 0xd8, 		/* mov	   ds,ax    */
-    0xe8, 0x00, 0x00,		/* call    $ + 3    */
-    0x5e,			/* pop	   si	    */
-    0x83, 0xc6, 0x19,		/* add	   si,+19h  */
-    0xbb, 0x07, 0x00,		/* mov	   bx,0007h */
-    0xfc,			/* cld		    */
-    0xac,			/* lodsb	    */
-    0x84, 0xc0, 		/* test    al,al    */
-    0x74, 0x06, 		/* jz	   $ + 8    */
-    0xb4, 0x0e, 		/* mov	   ah,0eh   */
-    0xcd, 0x10, 		/* int	   10h	    */
-    0xeb, 0xf5, 		/* jmp	   $ - 9    */
-    0x30, 0xe4, 		/* xor	   ah,ah    */
-    0xcd, 0x16, 		/* int	   16h	    */
-    0xcd, 0x19, 		/* int	   19h	    */
-    0x0d, 0x0a,
-    'N', 'o', 'n', '-', 's', 'y', 's', 't',
-    'e', 'm', ' ', 'd', 'i', 's', 'k',
-    0x0d, 0x0a,
-    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
-    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
-    ' ', 'r', 'e', 'b', 'o', 'o', 't',
-    0x0d, 0x0a,
-    0
-};
-
-static void print_bpb(struct bpb *);
-static u_int ckgeom(const char *, u_int, const char *);
-static u_int argtou(const char *, u_int, u_int, const char *);
-static int oklabel(const char *);
-static void mklabel(u_int8_t *, const char *);
-static void setstr(u_int8_t *, const char *, size_t);
-static void usage(char* progname);
-
-/*
- * Construct a FAT12, FAT16, or FAT32 file system.
- */
-int
-mkdosfs_main(int argc, char *argv[])
-{
-    static char opts[] = "NB:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
-    static const char *opt_B, *opt_L, *opt_O;
-    static u_int opt_F, opt_I, opt_S, opt_a, opt_b, opt_c, opt_e;
-    static u_int opt_h, opt_i, opt_k, opt_m, opt_n, opt_o, opt_r;
-    static u_int opt_s, opt_u;
-    static int opt_N;
-    static int Iflag, mflag, oflag;
-    char buf[MAXPATHLEN];
-    struct stat sb;
-    struct timeval tv;
-    struct bpb bpb;
-    struct tm *tm;
-    struct bs *bs;
-    struct bsbpb *bsbpb;
-    struct bsxbpb *bsxbpb;
-    struct bsx *bsx;
-    struct de *de;
-    u_int8_t *img;
-    const char *fname, *dtype, *bname;
-    ssize_t n;
-    time_t now;
-    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
-    int ch, fd, fd1;
-    char* progname = argv[0];
-
-    while ((ch = getopt(argc, argv, opts)) != -1)
-	switch (ch) {
-	case 'N':
-	    opt_N = 1;
-	    break;
-	case 'B':
-	    opt_B = optarg;
-	    break;
-	case 'F':
-	    if (strcmp(optarg, "12") &&
-		strcmp(optarg, "16") &&
-		strcmp(optarg, "32"))
-		fprintf(stderr, "%s: bad FAT type\n", optarg);
-	    opt_F = atoi(optarg);
-	    break;
-	case 'I':
-	    opt_I = argto4(optarg, 0, "volume ID");
-	    Iflag = 1;
-	    break;
-	case 'L':
-	    if (!oklabel(optarg))
-		fprintf(stderr, "%s: bad volume label\n", optarg);
-	    opt_L = optarg;
-	    break;
-	case 'O':
-	    if (strlen(optarg) > 8)
-		fprintf(stderr, "%s: bad OEM string\n", optarg);
-	    opt_O = optarg;
-	    break;
-	case 'S':
-	    opt_S = argto2(optarg, 1, "bytes/sector");
-	    break;
-	case 'a':
-	    opt_a = argto4(optarg, 1, "sectors/FAT");
-	    break;
-	case 'b':
-	    opt_b = argtox(optarg, 1, "block size");
-	    opt_c = 0;
-	    break;
-	case 'c':
-	    opt_c = argto1(optarg, 1, "sectors/cluster");
-	    opt_b = 0;
-	    break;
-	case 'e':
-	    opt_e = argto2(optarg, 1, "directory entries");
-	    break;
-	case 'h':
-	    opt_h = argto2(optarg, 1, "drive heads");
-	    break;
-	case 'i':
-	    opt_i = argto2(optarg, 1, "info sector");
-	    break;
-	case 'k':
-	    opt_k = argto2(optarg, 1, "backup sector");
-	    break;
-	case 'm':
-	    opt_m = argto1(optarg, 0, "media descriptor");
-	    mflag = 1;
-	    break;
-	case 'n':
-	    opt_n = argto1(optarg, 1, "number of FATs");
-	    break;
-	case 'o':
-	    opt_o = argto4(optarg, 0, "hidden sectors");
-	    oflag = 1;
-	    break;
-	case 'r':
-	    opt_r = argto2(optarg, 1, "reserved sectors");
-	    break;
-	case 's':
-	    opt_s = argto4(optarg, 1, "file system size");
-	    break;
-	case 'u':
-	    opt_u = argto2(optarg, 1, "sectors/track");
-	    break;
-	default:
-	    usage(progname);
-	}
-    argc -= optind;
-    argv += optind;
-    if (argc < 1 || argc > 2)
-	usage(progname);
-    fname = *argv++;
-    if (!strchr(fname, '/')) {
-	snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname);
-	if (!(fname = strdup(buf)))
-	    fprintf(stderr, NULL);
-    }
-    dtype = *argv;
-    if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1 ||
-	fstat(fd, &sb))
-	fprintf(stderr, "%s\n", fname);
-    memset(&bpb, 0, sizeof(bpb));
-
-    if (opt_h)
-	bpb.hds = opt_h;
-    if (opt_u)
-	bpb.spt = opt_u;
-    if (opt_S)
-	bpb.bps = opt_S;
-    if (opt_s)
-	bpb.bsec = opt_s;
-    if (oflag)
-	bpb.hid = opt_o;
-
-    bpb.bps = 512;  // 512 bytes/sector
-    bpb.spc = 8;    // 4K clusters
-    
-
-	fprintf(stderr, "opening %s\n", fname);
-	if ((fd1 = open(fname, O_RDONLY)) == -1) {
-	    fprintf(stderr, "failed to open %s\n", fname);
-	    exit(1);
-	}
-
-    lseek64(fd1, 0, SEEK_SET);
-    loff_t length = lseek64(fd1, 0, SEEK_END);
-    if (length > 0) {
-        bpb.bsec = length / bpb.bps;
-        bpb.spt = bpb.bsec;
-        // use FAT32 for 2 gig or greater 
-        if (length >= 2 *1024 *1024 *1024) {
-            fat = 32;
-        } else {
-            fat = 16;
-        }
-    }
-    close(fd1);
-    fd1 = -1;
-
-    if (!powerof2(bpb.bps))
-	fprintf(stderr, "bytes/sector (%u) is not a power of 2\n", bpb.bps);
-    if (bpb.bps < MINBPS)
-	fprintf(stderr, "bytes/sector (%u) is too small; minimum is %u\n",
-	     bpb.bps, MINBPS);
-
-    if (!(fat = opt_F)) {
-	if (!opt_e && (opt_i || opt_k))
-	    fat = 32;
-    }
-
-    if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
-	fprintf(stderr, "-%c is not a legal FAT%s option\n",
-	     fat == 32 ? 'e' : opt_i ? 'i' : 'k',
-	     fat == 32 ? "32" : "12/16");
-    if (fat == 32)
-	bpb.rde = 0;
-    if (opt_b) {
-	if (!powerof2(opt_b))
-	    fprintf(stderr, "block size (%u) is not a power of 2\n", opt_b);
-	if (opt_b < bpb.bps)
-	    fprintf(stderr, "block size (%u) is too small; minimum is %u\n",
-		 opt_b, bpb.bps);
-	if (opt_b > bpb.bps * MAXSPC)
-	    fprintf(stderr, "block size (%u) is too large; maximum is %u\n",
-		 opt_b, bpb.bps * MAXSPC);
-	bpb.spc = opt_b / bpb.bps;
-    }
-    if (opt_c) {
-	if (!powerof2(opt_c))
-	    fprintf(stderr, "sectors/cluster (%u) is not a power of 2\n", opt_c);
-	bpb.spc = opt_c;
-    }
-    if (opt_r)
-	bpb.res = opt_r;
-    if (opt_n) {
-	if (opt_n > MAXNFT)
-	    fprintf(stderr, "number of FATs (%u) is too large; maximum is %u\n",
-		 opt_n, MAXNFT);
-	bpb.nft = opt_n;
-    }
-    if (opt_e)
-	bpb.rde = opt_e;
-    if (mflag) {
-	if (opt_m < 0xf0)
-	    fprintf(stderr, "illegal media descriptor (%#x)\n", opt_m);
-	bpb.mid = opt_m;
-    }
-    if (opt_a)
-	bpb.bspf = opt_a;
-    if (opt_i)
-	bpb.infs = opt_i;
-    if (opt_k)
-	bpb.bkbs = opt_k;
-    bss = 1;
-    bname = NULL;
-    fd1 = -1;
-    if (opt_B) {
-	bname = opt_B;
-	if (!strchr(bname, '/')) {
-	    snprintf(buf, sizeof(buf), "/boot/%s", bname);
-	    if (!(bname = strdup(buf)))
-		fprintf(stderr, NULL);
-	}
-	if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
-	    fprintf(stderr, "%s", bname);
-	if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
-	    sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
-	    fprintf(stderr, "%s: inappropriate file type or format\n", bname);
-	bss = sb.st_size / bpb.bps;
-    }
-    if (!bpb.nft)
-	bpb.nft = 2;
-    if (!fat) {
-	if (bpb.bsec < (bpb.res ? bpb.res : bss) +
-	    howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
-		    ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
-	    bpb.nft +
-	    howmany(bpb.rde ? bpb.rde : DEFRDE,
-		    bpb.bps / DE_SIZE) +
-	    (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
-	    (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
-	    fat = 12;
-	else if (bpb.rde || bpb.bsec <
-		 (bpb.res ? bpb.res : bss) +
-		 howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
-		 howmany(DEFRDE, bpb.bps / DE_SIZE) +
-		 (MAXCLS16 + 1) *
-		 (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
-	    fat = 16;
-	else
-	    fat = 32;
-    }
-    x = bss;
-    if (fat == 32) {
-	if (!bpb.infs) {
-	    if (x == MAXU16 || x == bpb.bkbs)
-		fprintf(stderr, "no room for info sector\n");
-	    bpb.infs = x;
-	}
-	if (bpb.infs != MAXU16 && x <= bpb.infs)
-	    x = bpb.infs + 1;
-	if (!bpb.bkbs) {
-	    if (x == MAXU16)
-		fprintf(stderr, "no room for backup sector\n");
-	    bpb.bkbs = x;
-	} else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
-	    fprintf(stderr, "backup sector would overwrite info sector\n");
-	if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
-	    x = bpb.bkbs + 1;
-    }
-    if (!bpb.res)
-	bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x;
-    else if (bpb.res < x)
-	fprintf(stderr, "too few reserved sectors (need %d have %d)\n", x, bpb.res);
-    if (fat != 32 && !bpb.rde)
-	bpb.rde = DEFRDE;
-    rds = howmany(bpb.rde, bpb.bps / DE_SIZE);
-    if (!bpb.spc)
-	for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
-	     bpb.spc < MAXSPC &&
-	     bpb.res +
-	     howmany((RESFTE + maxcls(fat)) * (fat / BPN),
-		     bpb.bps * NPB) * bpb.nft +
-	     rds +
-	     (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
-	     bpb.spc <<= 1);
-    if (fat != 32 && bpb.bspf > MAXU16)
-	fprintf(stderr, "too many sectors/FAT for FAT12/16\n");
-    x1 = bpb.res + rds;
-    x = bpb.bspf ? bpb.bspf : 1;
-    if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
-	fprintf(stderr, "meta data exceeds file system size\n");
-    x1 += x * bpb.nft;
-    x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
-	(bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
-    x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
-		 bpb.bps * NPB);
-    if (!bpb.bspf) {
-	bpb.bspf = x2;
-	x1 += (bpb.bspf - 1) * bpb.nft;
-    }
-    cls = (bpb.bsec - x1) / bpb.spc;
-    x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
-    if (cls > x)
-	cls = x;
-    if (bpb.bspf < x2)
-	fprintf(stderr, "warning: sectors/FAT limits file system to %u clusters\n",
-	      cls);
-    if (cls < mincls(fat))
-	fprintf(stderr, "%u clusters too few clusters for FAT%u, need %u\n", cls, fat,
-	    mincls(fat));
-    if (cls > maxcls(fat)) {
-	cls = maxcls(fat);
-	bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
-	fprintf(stderr, "warning: FAT type limits file system to %u sectors\n",
-	      bpb.bsec);
-    }
-    printf("%s: %u sector%s in %u FAT%u cluster%s "
-	   "(%u bytes/cluster)\n", fname, cls * bpb.spc,
-	   cls * bpb.spc == 1 ? "" : "s", cls, fat,
-	   cls == 1 ? "" : "s", bpb.bps * bpb.spc);
-    if (!bpb.mid)
-	bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
-    if (fat == 32)
-	bpb.rdcl = RESFTE;
-    if (bpb.hid + bpb.bsec <= MAXU16) {
-	bpb.sec = bpb.bsec;
-	bpb.bsec = 0;
-    }
-    if (fat != 32) {
-	bpb.spf = bpb.bspf;
-	bpb.bspf = 0;
-    }
-    ch = 0;
-    if (fat == 12)
-	ch = 1;			/* 001 Primary DOS with 12 bit FAT */
-    else if (fat == 16) {
-	if (bpb.bsec == 0)
-	    ch = 4;		/* 004 Primary DOS with 16 bit FAT <32M */
-	else
-	    ch = 6;		/* 006 Primary 'big' DOS, 16-bit FAT (> 32MB) */
-				/*
-				 * XXX: what about:
-				 * 014 DOS (16-bit FAT) - LBA
-				 *  ?
-				 */
-    } else if (fat == 32) {
-	ch = 11;		/* 011 Primary DOS with 32 bit FAT */
-				/*
-				 * XXX: what about:
-				 * 012 Primary DOS with 32 bit FAT - LBA
-				 *  ?
-				 */
-    }
-    if (ch != 0)
-	printf("MBR type: %d\n", ch);
-    print_bpb(&bpb);
-    if (!opt_N) {
-	gettimeofday(&tv, NULL);
-	now = tv.tv_sec;
-	tm = localtime(&now);
-	if (!(img = malloc(bpb.bps)))
-	    fprintf(stderr, NULL);
-	dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
-
-	for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
-	    x = lsn;
-	    if (opt_B &&
-		fat == 32 && bpb.bkbs != MAXU16 &&
-		bss <= bpb.bkbs && x >= bpb.bkbs) {
-		x -= bpb.bkbs;
-		if (!x && lseek64(fd1, 0, SEEK_SET))
-		    fprintf(stderr, "lseek64 failed for %s\n", bname);
-	    }
-	    if (opt_B && x < bss) {
-		if ((n = read(fd1, img, bpb.bps)) == -1)
-		    fprintf(stderr, "%s\n", bname);
-		if (n != bpb.bps)
-		    fprintf(stderr, "%s: can't read sector %u\n", bname, x);
-	    } else
-		memset(img, 0, bpb.bps);
-	    if (!lsn ||
-	      (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
-		x1 = BS_SIZE;
-		bsbpb = (struct bsbpb *)(img + x1);
-		mk2(bsbpb->bps, bpb.bps);
-		mk1(bsbpb->spc, bpb.spc);
-		mk2(bsbpb->res, bpb.res);
-		mk1(bsbpb->nft, bpb.nft);
-		mk2(bsbpb->rde, bpb.rde);
-		mk2(bsbpb->sec, bpb.sec);
-		mk1(bsbpb->mid, bpb.mid);
-		mk2(bsbpb->spf, bpb.spf);
-		mk2(bsbpb->spt, bpb.spt);
-		mk2(bsbpb->hds, bpb.hds);
-		mk4(bsbpb->hid, bpb.hid);
-		mk4(bsbpb->bsec, bpb.bsec);
-		x1 += BSBPB_SIZE;
-		if (fat == 32) {
-		    bsxbpb = (struct bsxbpb *)(img + x1);
-		    mk4(bsxbpb->bspf, bpb.bspf);
-		    mk2(bsxbpb->xflg, 0);
-		    mk2(bsxbpb->vers, 0);
-		    mk4(bsxbpb->rdcl, bpb.rdcl);
-		    mk2(bsxbpb->infs, bpb.infs);
-		    mk2(bsxbpb->bkbs, bpb.bkbs);
-		    x1 += BSXBPB_SIZE;
-		}
-		bsx = (struct bsx *)(img + x1);
-		mk1(bsx->sig, 0x29);
-		if (Iflag)
-		    x = opt_I;
-		else
-		    x = (((u_int)(1 + tm->tm_mon) << 8 |
-			  (u_int)tm->tm_mday) +
-			 ((u_int)tm->tm_sec << 8 |
-			  (u_int)(tv.tv_usec / 10))) << 16 |
-			((u_int)(1900 + tm->tm_year) +
-			 ((u_int)tm->tm_hour << 8 |
-			  (u_int)tm->tm_min));
-		mk4(bsx->volid, x);
-		mklabel(bsx->label, opt_L ? opt_L : "NO_NAME");
-		snprintf(buf, sizeof(buf), "FAT%u", fat);
-		setstr(bsx->type, buf, sizeof(bsx->type));
-		if (!opt_B) {
-		    x1 += BSX_SIZE;
-		    bs = (struct bs *)img;
-		    mk1(bs->jmp[0], 0xeb);
-		    mk1(bs->jmp[1], x1 - 2);
-		    mk1(bs->jmp[2], 0x90);
-		    setstr(bs->oem, opt_O ? opt_O : "NetBSD",
-			   sizeof(bs->oem));
-		    memcpy(img + x1, bootcode, sizeof(bootcode));
-		    mk2(img + bpb.bps - 2, DOSMAGIC);
-		}
-	    } else if (fat == 32 && bpb.infs != MAXU16 &&
-		       (lsn == bpb.infs ||
-			(bpb.bkbs != MAXU16 &&
-			 lsn == bpb.bkbs + bpb.infs))) {
-		mk4(img, 0x41615252);
-		mk4(img + bpb.bps - 28, 0x61417272);
-		mk4(img + bpb.bps - 24, 0xffffffff);
-		mk4(img + bpb.bps - 20, bpb.rdcl);
-		mk2(img + bpb.bps - 2, DOSMAGIC);
-	    } else if (lsn >= bpb.res && lsn < dir &&
-		       !((lsn - bpb.res) %
-			 (bpb.spf ? bpb.spf : bpb.bspf))) {
-		mk1(img[0], bpb.mid);
-		for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
-		    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
-	    } else if (lsn == dir && opt_L) {
-		de = (struct de *)img;
-		mklabel(de->namext, opt_L);
-		mk1(de->attr, 050);
-		x = (u_int)tm->tm_hour << 11 |
-		    (u_int)tm->tm_min << 5 |
-		    (u_int)tm->tm_sec >> 1;
-		mk2(de->time, x);
-		x = (u_int)(tm->tm_year - 80) << 9 |
-		    (u_int)(tm->tm_mon + 1) << 5 |
-		    (u_int)tm->tm_mday;
-		mk2(de->date, x);
-	    }
-	    if ((n = write(fd, img, bpb.bps)) == -1)
-		fprintf(stderr, "%s\n", fname);
-	    if (n != bpb.bps)
-		fprintf(stderr, "%s: can't write sector %u\n", fname, lsn);
-	}
-    }
-    return 0;
-}
-
-/*
- * Print out BPB values.
- */
-static void
-print_bpb(struct bpb *bpb)
-{
-    printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
-	   bpb->nft);
-    if (bpb->rde)
-	printf(" rde=%u", bpb->rde);
-    if (bpb->sec)
-	printf(" sec=%u", bpb->sec);
-    printf(" mid=%#x", bpb->mid);
-    if (bpb->spf)
-	printf(" spf=%u", bpb->spf);
-    printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
-    if (bpb->bsec)
-	printf(" bsec=%u", bpb->bsec);
-    if (!bpb->spf) {
-	printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
-	printf(" infs=");
-	printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
-	printf(" bkbs=");
-	printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
-    }
-    printf("\n");
-}
-
-/*
- * Check a disk geometry value.
- */
-static u_int
-ckgeom(const char *fname, u_int val, const char *msg)
-{
-    if (!val)
-	fprintf(stderr, "%s: no default %s\n", fname, msg);
-    if (val > MAXU16)
-	fprintf(stderr, "%s: illegal %s\n", fname, msg);
-    return val;
-}
-
-/*
- * Convert and check a numeric option argument.
- */
-static u_int
-argtou(const char *arg, u_int lo, u_int hi, const char *msg)
-{
-    char *s;
-    u_long x;
-
-    errno = 0;
-    x = strtoul(arg, &s, 0);
-    if (errno || !*arg || *s || x < lo || x > hi)
-	fprintf(stderr, "%s: bad %s\n", arg, msg);
-    return x;
-}
-
-/*
- * Check a volume label.
- */
-static int
-oklabel(const char *src)
-{
-    int c, i;
-
-    for (i = 0; i <= 11; i++) {
-	c = (u_char)*src++;
-	if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
-	    break;
-    }
-    return i && !c;
-}
-
-/*
- * Make a volume label.
- */
-static void
-mklabel(u_int8_t *dest, const char *src)
-{
-    int c, i;
-
-    for (i = 0; i < 11; i++) {
-	c = *src ? toupper((unsigned char)*src++) : ' ';
-	*dest++ = !i && c == '\xe5' ? 5 : c;
-    }
-}
-
-/*
- * Copy string, padding with spaces.
- */
-static void
-setstr(u_int8_t *dest, const char *src, size_t len)
-{
-    while (len--)
-	*dest++ = *src ? *src++ : ' ';
-}
-
-/*
- * Print usage message.
- */
-static void
-usage(char* progname)
-{
-    fprintf(stderr,
-	    "usage: %s [ -options ] special [disktype]\n", progname);
-    fprintf(stderr, "where the options are:\n");
-    fprintf(stderr, "\t-N don't create file system: "
-	    "just print out parameters\n");
-    fprintf(stderr, "\t-B get bootstrap from file\n");
-    fprintf(stderr, "\t-F FAT type (12, 16, or 32)\n");
-    fprintf(stderr, "\t-I volume ID\n");
-    fprintf(stderr, "\t-L volume label\n");
-    fprintf(stderr, "\t-O OEM string\n");
-    fprintf(stderr, "\t-S bytes/sector\n");
-    fprintf(stderr, "\t-a sectors/FAT\n");
-    fprintf(stderr, "\t-b block size\n");
-    fprintf(stderr, "\t-c sectors/cluster\n");
-    fprintf(stderr, "\t-e root directory entries\n");
-    fprintf(stderr, "\t-h drive heads\n");
-    fprintf(stderr, "\t-i file system info sector\n");
-    fprintf(stderr, "\t-k backup boot sector\n");
-    fprintf(stderr, "\t-m media descriptor\n");
-    fprintf(stderr, "\t-n number of FATs\n");
-    fprintf(stderr, "\t-o hidden sectors\n");
-    fprintf(stderr, "\t-r reserved sectors\n");
-    fprintf(stderr, "\t-s file system size (sectors)\n");
-    fprintf(stderr, "\t-u sectors/track\n");
-    exit(1);
-}
-
-
diff --git a/toolbox/mount.c b/toolbox/mount.c
index 395c943..472c952 100644
--- a/toolbox/mount.c
+++ b/toolbox/mount.c
@@ -226,7 +226,7 @@
 {
 	char *type = NULL;
 	int c;
-	int loop;
+	int loop = 0;
 
 	progname = argv[0];
 	rwflag = MS_VERBOSE;
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
new file mode 100644
index 0000000..4483cc0
--- /dev/null
+++ b/toolbox/newfs_msdos.c
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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 lint
+static const char rcsid[] =
+  "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#ifndef ANDROID
+  #include <sys/fdcio.h>
+  #include <sys/disk.h>
+  #include <sys/disklabel.h>
+  #include <sys/mount.h>
+#else
+  #include <stdarg.h>
+  #include <linux/fs.h>
+  #include <linux/hdreg.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MAXU16	  0xffff	/* maximum unsigned 16-bit quantity */
+#define BPN	  4		/* bits per nibble */
+#define NPB	  2		/* nibbles per byte */
+
+#define DOSMAGIC  0xaa55	/* DOS magic number */
+#define MINBPS	  512		/* minimum bytes per sector */
+#define MAXSPC	  128		/* maximum sectors per cluster */
+#define MAXNFT	  16		/* maximum number of FATs */
+#define DEFBLK	  4096		/* default block size */
+#define DEFBLK16  2048		/* default block size FAT16 */
+#define DEFRDE	  512		/* default root directory entries */
+#define RESFTE	  2		/* reserved FAT entries */
+#define MINCLS12  1		/* minimum FAT12 clusters */
+#define MINCLS16  0x1000	/* minimum FAT16 clusters */
+#define MINCLS32  2		/* minimum FAT32 clusters */
+#define MAXCLS12  0xfed 	/* maximum FAT12 clusters */
+#define MAXCLS16  0xfff5	/* maximum FAT16 clusters */
+#define MAXCLS32  0xffffff5	/* maximum FAT32 clusters */
+
+#define mincls(fat)  ((fat) == 12 ? MINCLS12 :	\
+		      (fat) == 16 ? MINCLS16 :	\
+				    MINCLS32)
+
+#define maxcls(fat)  ((fat) == 12 ? MAXCLS12 :	\
+		      (fat) == 16 ? MAXCLS16 :	\
+				    MAXCLS32)
+
+#define mk1(p, x)				\
+    (p) = (u_int8_t)(x)
+
+#define mk2(p, x)				\
+    (p)[0] = (u_int8_t)(x),			\
+    (p)[1] = (u_int8_t)((x) >> 010)
+
+#define mk4(p, x)				\
+    (p)[0] = (u_int8_t)(x),			\
+    (p)[1] = (u_int8_t)((x) >> 010),		\
+    (p)[2] = (u_int8_t)((x) >> 020),		\
+    (p)[3] = (u_int8_t)((x) >> 030)
+
+#define argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
+#define argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
+#define argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
+#define argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
+
+struct bs {
+    u_int8_t jmp[3];		/* bootstrap entry point */
+    u_int8_t oem[8];		/* OEM name and version */
+};
+
+struct bsbpb {
+    u_int8_t bps[2];		/* bytes per sector */
+    u_int8_t spc;		/* sectors per cluster */
+    u_int8_t res[2];		/* reserved sectors */
+    u_int8_t nft;		/* number of FATs */
+    u_int8_t rde[2];		/* root directory entries */
+    u_int8_t sec[2];		/* total sectors */
+    u_int8_t mid;		/* media descriptor */
+    u_int8_t spf[2];		/* sectors per FAT */
+    u_int8_t spt[2];		/* sectors per track */
+    u_int8_t hds[2];		/* drive heads */
+    u_int8_t hid[4];		/* hidden sectors */
+    u_int8_t bsec[4];		/* big total sectors */
+};
+
+struct bsxbpb {
+    u_int8_t bspf[4];		/* big sectors per FAT */
+    u_int8_t xflg[2];		/* FAT control flags */
+    u_int8_t vers[2];		/* file system version */
+    u_int8_t rdcl[4];		/* root directory start cluster */
+    u_int8_t infs[2];		/* file system info sector */
+    u_int8_t bkbs[2];		/* backup boot sector */
+    u_int8_t rsvd[12];		/* reserved */
+};
+
+struct bsx {
+    u_int8_t drv;		/* drive number */
+    u_int8_t rsvd;		/* reserved */
+    u_int8_t sig;		/* extended boot signature */
+    u_int8_t volid[4];		/* volume ID number */
+    u_int8_t label[11]; 	/* volume label */
+    u_int8_t type[8];		/* file system type */
+};
+
+struct de {
+    u_int8_t namext[11];	/* name and extension */
+    u_int8_t attr;		/* attributes */
+    u_int8_t rsvd[10];		/* reserved */
+    u_int8_t time[2];		/* creation time */
+    u_int8_t date[2];		/* creation date */
+    u_int8_t clus[2];		/* starting cluster */
+    u_int8_t size[4];		/* size */
+};
+
+struct bpb {
+    u_int bps;			/* bytes per sector */
+    u_int spc;			/* sectors per cluster */
+    u_int res;			/* reserved sectors */
+    u_int nft;			/* number of FATs */
+    u_int rde;			/* root directory entries */
+    u_int sec;			/* total sectors */
+    u_int mid;			/* media descriptor */
+    u_int spf;			/* sectors per FAT */
+    u_int spt;			/* sectors per track */
+    u_int hds;			/* drive heads */
+    u_int hid;			/* hidden sectors */
+    u_int bsec; 		/* big total sectors */
+    u_int bspf; 		/* big sectors per FAT */
+    u_int rdcl; 		/* root directory start cluster */
+    u_int infs; 		/* file system info sector */
+    u_int bkbs; 		/* backup boot sector */
+};
+
+#define BPBGAP 0, 0, 0, 0, 0, 0
+
+static struct {
+    const char *name;
+    struct bpb bpb;
+} const stdfmt[] = {
+    {"160",  {512, 1, 1, 2,  64,  320, 0xfe, 1,  8, 1, BPBGAP}},
+    {"180",  {512, 1, 1, 2,  64,  360, 0xfc, 2,  9, 1, BPBGAP}},
+    {"320",  {512, 2, 1, 2, 112,  640, 0xff, 1,  8, 2, BPBGAP}},
+    {"360",  {512, 2, 1, 2, 112,  720, 0xfd, 2,  9, 2, BPBGAP}},
+    {"640",  {512, 2, 1, 2, 112, 1280, 0xfb, 2,  8, 2, BPBGAP}},    
+    {"720",  {512, 2, 1, 2, 112, 1440, 0xf9, 3,  9, 2, BPBGAP}},
+    {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
+    {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2,  8, 2, BPBGAP}},    
+    {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
+    {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
+};
+
+static const u_int8_t bootcode[] = {
+    0xfa,			/* cli		    */
+    0x31, 0xc0, 		/* xor	   ax,ax    */
+    0x8e, 0xd0, 		/* mov	   ss,ax    */
+    0xbc, 0x00, 0x7c,		/* mov	   sp,7c00h */
+    0xfb,			/* sti		    */
+    0x8e, 0xd8, 		/* mov	   ds,ax    */
+    0xe8, 0x00, 0x00,		/* call    $ + 3    */
+    0x5e,			/* pop	   si	    */
+    0x83, 0xc6, 0x19,		/* add	   si,+19h  */
+    0xbb, 0x07, 0x00,		/* mov	   bx,0007h */
+    0xfc,			/* cld		    */
+    0xac,			/* lodsb	    */
+    0x84, 0xc0, 		/* test    al,al    */
+    0x74, 0x06, 		/* jz	   $ + 8    */
+    0xb4, 0x0e, 		/* mov	   ah,0eh   */
+    0xcd, 0x10, 		/* int	   10h	    */
+    0xeb, 0xf5, 		/* jmp	   $ - 9    */
+    0x30, 0xe4, 		/* xor	   ah,ah    */
+    0xcd, 0x16, 		/* int	   16h	    */
+    0xcd, 0x19, 		/* int	   19h	    */
+    0x0d, 0x0a,
+    'N', 'o', 'n', '-', 's', 'y', 's', 't',
+    'e', 'm', ' ', 'd', 'i', 's', 'k',
+    0x0d, 0x0a,
+    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
+    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
+    ' ', 'r', 'e', 'b', 'o', 'o', 't',
+    0x0d, 0x0a,
+    0
+};
+
+static void check_mounted(const char *, mode_t);
+static void getstdfmt(const char *, struct bpb *);
+static void getdiskinfo(int, const char *, const char *, int,
+			struct bpb *);
+static void print_bpb(struct bpb *);
+static u_int ckgeom(const char *, u_int, const char *);
+static u_int argtou(const char *, u_int, u_int, const char *);
+static off_t argtooff(const char *, const char *);
+static int oklabel(const char *);
+static void mklabel(u_int8_t *, const char *);
+static void setstr(u_int8_t *, const char *, size_t);
+static void usage(void);
+
+#ifdef ANDROID
+static void err(int val, const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    char *fmt2;
+    asprintf(&fmt2, "%s\n", fmt);
+    vfprintf(stderr, fmt2, ap);
+    free(fmt2);
+    va_end(ap);
+}
+
+static void errx(int val, const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    char *fmt2;
+    asprintf(&fmt2, "%s\n", fmt);
+    vfprintf(stderr, fmt2, ap);
+    free(fmt2);
+    va_end(ap);
+}
+
+static void warnx(const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    char *fmt2;
+    asprintf(&fmt2, "%s\n", fmt);
+    vfprintf(stderr, fmt2, ap);
+    free(fmt2);
+    va_end(ap);
+}
+#define powerof2(x)     ((((x) - 1) & (x)) == 0)
+#define howmany(x, y)   (((x) + ((y) - 1)) / (y))
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#endif
+/*
+ * Construct a FAT12, FAT16, or FAT32 file system.
+ */
+int
+newfs_msdos_main(int argc, char *argv[])
+{
+    static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
+    const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
+    u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
+    u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
+    u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
+    int opt_N = 0;
+    int Iflag = 0, mflag = 0, oflag = 0;
+    char buf[MAXPATHLEN];
+    struct stat sb;
+    struct timeval tv;
+    struct bpb bpb;
+    struct tm *tm;
+    struct bs *bs;
+    struct bsbpb *bsbpb;
+    struct bsxbpb *bsxbpb;
+    struct bsx *bsx;
+    struct de *de;
+    u_int8_t *img;
+    const char *fname, *dtype, *bname;
+    ssize_t n;
+    time_t now;
+    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
+    int ch, fd, fd1;
+    off_t opt_create = 0, opt_ofs = 0;
+
+    while ((ch = getopt(argc, argv, opts)) != -1)
+	switch (ch) {
+	case '@':
+	    opt_ofs = argtooff(optarg, "offset");
+	    break;
+	case 'N':
+	    opt_N = 1;
+	    break;
+	case 'B':
+	    opt_B = optarg;
+	    break;
+	case 'C':
+	    opt_create = argtooff(optarg, "create size");
+	    break;
+	case 'F':
+	    if (strcmp(optarg, "12") &&
+		strcmp(optarg, "16") &&
+		strcmp(optarg, "32"))
+		errx(1, "%s: bad FAT type", optarg);
+	    opt_F = atoi(optarg);
+	    break;
+	case 'I':
+	    opt_I = argto4(optarg, 0, "volume ID");
+	    Iflag = 1;
+	    break;
+	case 'L':
+	    if (!oklabel(optarg))
+		errx(1, "%s: bad volume label", optarg);
+	    opt_L = optarg;
+	    break;
+	case 'O':
+	    if (strlen(optarg) > 8)
+		errx(1, "%s: bad OEM string", optarg);
+	    opt_O = optarg;
+	    break;
+	case 'S':
+	    opt_S = argto2(optarg, 1, "bytes/sector");
+	    break;
+	case 'a':
+	    opt_a = argto4(optarg, 1, "sectors/FAT");
+	    break;
+	case 'b':
+	    opt_b = argtox(optarg, 1, "block size");
+	    opt_c = 0;
+	    break;
+	case 'c':
+	    opt_c = argto1(optarg, 1, "sectors/cluster");
+	    opt_b = 0;
+	    break;
+	case 'e':
+	    opt_e = argto2(optarg, 1, "directory entries");
+	    break;
+	case 'f':
+	    opt_f = optarg;
+	    break;
+	case 'h':
+	    opt_h = argto2(optarg, 1, "drive heads");
+	    break;
+	case 'i':
+	    opt_i = argto2(optarg, 1, "info sector");
+	    break;
+	case 'k':
+	    opt_k = argto2(optarg, 1, "backup sector");
+	    break;
+	case 'm':
+	    opt_m = argto1(optarg, 0, "media descriptor");
+	    mflag = 1;
+	    break;
+	case 'n':
+	    opt_n = argto1(optarg, 1, "number of FATs");
+	    break;
+	case 'o':
+	    opt_o = argto4(optarg, 0, "hidden sectors");
+	    oflag = 1;
+	    break;
+	case 'r':
+	    opt_r = argto2(optarg, 1, "reserved sectors");
+	    break;
+	case 's':
+	    opt_s = argto4(optarg, 1, "file system size");
+	    break;
+	case 'u':
+	    opt_u = argto2(optarg, 1, "sectors/track");
+	    break;
+	default:
+	    usage();
+	}
+    argc -= optind;
+    argv += optind;
+    if (argc < 1 || argc > 2)
+	usage();
+    fname = *argv++;
+    if (!opt_create && !strchr(fname, '/')) {
+	snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+	if (!(fname = strdup(buf)))
+	    err(1, NULL);
+    }
+    dtype = *argv;
+    if (opt_create) {
+	if (opt_N)
+	    errx(1, "create (-C) is incompatible with -N");
+	fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
+	if (fd == -1)
+	    errx(1, "failed to create %s", fname);
+	if (ftruncate(fd, opt_create))
+	    errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
+    } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
+	err(1, "%s", fname);
+    if (fstat(fd, &sb))
+	err(1, "%s", fname);
+    if (opt_create) {
+	if (!S_ISREG(sb.st_mode))
+	    warnx("warning, %s is not a regular file", fname);
+    } else {
+	if (!S_ISCHR(sb.st_mode))
+	    warnx("warning, %s is not a character device", fname);
+    }
+    if (!opt_N)
+	check_mounted(fname, sb.st_mode);
+    if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
+	errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
+    memset(&bpb, 0, sizeof(bpb));
+    if (opt_f) {
+	getstdfmt(opt_f, &bpb);
+	bpb.bsec = bpb.sec;
+	bpb.sec = 0;
+	bpb.bspf = bpb.spf;
+	bpb.spf = 0;
+    }
+    if (opt_h)
+	bpb.hds = opt_h;
+    if (opt_u)
+	bpb.spt = opt_u;
+    if (opt_S)
+	bpb.bps = opt_S;
+    if (opt_s)
+	bpb.bsec = opt_s;
+    if (oflag)
+	bpb.hid = opt_o;
+    if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
+	off_t delta;
+	getdiskinfo(fd, fname, dtype, oflag, &bpb);
+	bpb.bsec -= (opt_ofs / bpb.bps);
+	delta = bpb.bsec % bpb.spt;
+	if (delta != 0) {
+	    warnx("trim %d sectors to adjust to a multiple of %d",
+		(int)delta, bpb.spt);
+	    bpb.bsec -= delta;
+	}
+	if (bpb.spc == 0) {	/* set defaults */
+	    if (bpb.bsec <= 6000)	/* about 3MB -> 512 bytes */
+		bpb.spc = 1;
+	    else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
+		bpb.spc = 8;
+	    else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
+		bpb.spc = 16;
+	    else if (bpb.bsec <= (1<<21)) /* 1G -> 16k */
+		bpb.spc = 32;
+	    else
+		bpb.spc = 64;		/* otherwise 32k */
+	}
+    }
+    if (!powerof2(bpb.bps))
+	errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
+    if (bpb.bps < MINBPS)
+	errx(1, "bytes/sector (%u) is too small; minimum is %u",
+	     bpb.bps, MINBPS);
+    if (!(fat = opt_F)) {
+	if (opt_f)
+	    fat = 12;
+	else if (!opt_e && (opt_i || opt_k))
+	    fat = 32;
+    }
+    if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
+	errx(1, "-%c is not a legal FAT%s option",
+	     fat == 32 ? 'e' : opt_i ? 'i' : 'k',
+	     fat == 32 ? "32" : "12/16");
+    if (opt_f && fat == 32)
+	bpb.rde = 0;
+    if (opt_b) {
+	if (!powerof2(opt_b))
+	    errx(1, "block size (%u) is not a power of 2", opt_b);
+	if (opt_b < bpb.bps)
+	    errx(1, "block size (%u) is too small; minimum is %u",
+		 opt_b, bpb.bps);
+	if (opt_b > bpb.bps * MAXSPC)
+	    errx(1, "block size (%u) is too large; maximum is %u",
+		 opt_b, bpb.bps * MAXSPC);
+	bpb.spc = opt_b / bpb.bps;
+    }
+    if (opt_c) {
+	if (!powerof2(opt_c))
+	    errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
+	bpb.spc = opt_c;
+    }
+    if (opt_r)
+	bpb.res = opt_r;
+    if (opt_n) {
+	if (opt_n > MAXNFT)
+	    errx(1, "number of FATs (%u) is too large; maximum is %u",
+		 opt_n, MAXNFT);
+	bpb.nft = opt_n;
+    }
+    if (opt_e)
+	bpb.rde = opt_e;
+    if (mflag) {
+	if (opt_m < 0xf0)
+	    errx(1, "illegal media descriptor (%#x)", opt_m);
+	bpb.mid = opt_m;
+    }
+    if (opt_a)
+	bpb.bspf = opt_a;
+    if (opt_i)
+	bpb.infs = opt_i;
+    if (opt_k)
+	bpb.bkbs = opt_k;
+    bss = 1;
+    bname = NULL;
+    fd1 = -1;
+    if (opt_B) {
+	bname = opt_B;
+	if (!strchr(bname, '/')) {
+	    snprintf(buf, sizeof(buf), "/boot/%s", bname);
+	    if (!(bname = strdup(buf)))
+		err(1, NULL);
+	}
+	if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
+	    err(1, "%s", bname);
+	if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
+	    sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
+	    errx(1, "%s: inappropriate file type or format", bname);
+	bss = sb.st_size / bpb.bps;
+    }
+    if (!bpb.nft)
+	bpb.nft = 2;
+    if (!fat) {
+	if (bpb.bsec < (bpb.res ? bpb.res : bss) +
+	    howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
+		    ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
+	    bpb.nft +
+	    howmany(bpb.rde ? bpb.rde : DEFRDE,
+		    bpb.bps / sizeof(struct de)) +
+	    (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
+	    (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
+	    fat = 12;
+	else if (bpb.rde || bpb.bsec <
+		 (bpb.res ? bpb.res : bss) +
+		 howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
+		 howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
+		 (MAXCLS16 + 1) *
+		 (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
+	    fat = 16;
+	else
+	    fat = 32;
+    }
+    x = bss;
+    if (fat == 32) {
+	if (!bpb.infs) {
+	    if (x == MAXU16 || x == bpb.bkbs)
+		errx(1, "no room for info sector");
+	    bpb.infs = x;
+	}
+	if (bpb.infs != MAXU16 && x <= bpb.infs)
+	    x = bpb.infs + 1;
+	if (!bpb.bkbs) {
+	    if (x == MAXU16)
+		errx(1, "no room for backup sector");
+	    bpb.bkbs = x;
+	} else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
+	    errx(1, "backup sector would overwrite info sector");
+	if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
+	    x = bpb.bkbs + 1;
+    }
+    if (!bpb.res)
+	bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x;
+    else if (bpb.res < x)
+	errx(1, "too few reserved sectors");
+    if (fat != 32 && !bpb.rde)
+	bpb.rde = DEFRDE;
+    rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
+    if (!bpb.spc)
+	for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
+	     bpb.spc < MAXSPC &&
+	     bpb.res +
+	     howmany((RESFTE + maxcls(fat)) * (fat / BPN),
+		     bpb.bps * NPB) * bpb.nft +
+	     rds +
+	     (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
+	     bpb.spc <<= 1);
+    if (fat != 32 && bpb.bspf > MAXU16)
+	errx(1, "too many sectors/FAT for FAT12/16");
+    x1 = bpb.res + rds;
+    x = bpb.bspf ? bpb.bspf : 1;
+    if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
+	errx(1, "meta data exceeds file system size");
+    x1 += x * bpb.nft;
+    x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
+	(bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
+    x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
+		 bpb.bps * NPB);
+    if (!bpb.bspf) {
+	bpb.bspf = x2;
+	x1 += (bpb.bspf - 1) * bpb.nft;
+    }
+    cls = (bpb.bsec - x1) / bpb.spc;
+    x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
+    if (cls > x)
+	cls = x;
+    if (bpb.bspf < x2)
+	warnx("warning: sectors/FAT limits file system to %u clusters",
+	      cls);
+    if (cls < mincls(fat))
+	errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat,
+	    mincls(fat));
+    if (cls > maxcls(fat)) {
+	cls = maxcls(fat);
+	bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
+	warnx("warning: FAT type limits file system to %u sectors",
+	      bpb.bsec);
+    }
+    printf("%s: %u sector%s in %u FAT%u cluster%s "
+	   "(%u bytes/cluster)\n", fname, cls * bpb.spc,
+	   cls * bpb.spc == 1 ? "" : "s", cls, fat,
+	   cls == 1 ? "" : "s", bpb.bps * bpb.spc);
+    if (!bpb.mid)
+	bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
+    if (fat == 32)
+	bpb.rdcl = RESFTE;
+    if (bpb.hid + bpb.bsec <= MAXU16) {
+	bpb.sec = bpb.bsec;
+	bpb.bsec = 0;
+    }
+    if (fat != 32) {
+	bpb.spf = bpb.bspf;
+	bpb.bspf = 0;
+    }
+    print_bpb(&bpb);
+    if (!opt_N) {
+	gettimeofday(&tv, NULL);
+	now = tv.tv_sec;
+	tm = localtime(&now);
+	if (!(img = malloc(bpb.bps)))
+	    err(1, NULL);
+	dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
+	for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
+	    x = lsn;
+	    if (opt_B &&
+		fat == 32 && bpb.bkbs != MAXU16 &&
+		bss <= bpb.bkbs && x >= bpb.bkbs) {
+		x -= bpb.bkbs;
+		if (!x && lseek(fd1, opt_ofs, SEEK_SET))
+		    err(1, "%s", bname);
+	    }
+	    if (opt_B && x < bss) {
+		if ((n = read(fd1, img, bpb.bps)) == -1)
+		    err(1, "%s", bname);
+		if ((unsigned)n != bpb.bps)
+		    errx(1, "%s: can't read sector %u", bname, x);
+	    } else
+		memset(img, 0, bpb.bps);
+	    if (!lsn ||
+	      (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
+		x1 = sizeof(struct bs);
+		bsbpb = (struct bsbpb *)(img + x1);
+		mk2(bsbpb->bps, bpb.bps);
+		mk1(bsbpb->spc, bpb.spc);
+		mk2(bsbpb->res, bpb.res);
+		mk1(bsbpb->nft, bpb.nft);
+		mk2(bsbpb->rde, bpb.rde);
+		mk2(bsbpb->sec, bpb.sec);
+		mk1(bsbpb->mid, bpb.mid);
+		mk2(bsbpb->spf, bpb.spf);
+		mk2(bsbpb->spt, bpb.spt);
+		mk2(bsbpb->hds, bpb.hds);
+		mk4(bsbpb->hid, bpb.hid);
+		mk4(bsbpb->bsec, bpb.bsec);
+		x1 += sizeof(struct bsbpb);
+		if (fat == 32) {
+		    bsxbpb = (struct bsxbpb *)(img + x1);
+		    mk4(bsxbpb->bspf, bpb.bspf);
+		    mk2(bsxbpb->xflg, 0);
+		    mk2(bsxbpb->vers, 0);
+		    mk4(bsxbpb->rdcl, bpb.rdcl);
+		    mk2(bsxbpb->infs, bpb.infs);
+		    mk2(bsxbpb->bkbs, bpb.bkbs);
+		    x1 += sizeof(struct bsxbpb);
+		}
+		bsx = (struct bsx *)(img + x1);
+		mk1(bsx->sig, 0x29);
+		if (Iflag)
+		    x = opt_I;
+		else
+		    x = (((u_int)(1 + tm->tm_mon) << 8 |
+			  (u_int)tm->tm_mday) +
+			 ((u_int)tm->tm_sec << 8 |
+			  (u_int)(tv.tv_usec / 10))) << 16 |
+			((u_int)(1900 + tm->tm_year) +
+			 ((u_int)tm->tm_hour << 8 |
+			  (u_int)tm->tm_min));
+		mk4(bsx->volid, x);
+		mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
+		sprintf(buf, "FAT%u", fat);
+		setstr(bsx->type, buf, sizeof(bsx->type));
+		if (!opt_B) {
+		    x1 += sizeof(struct bsx);
+		    bs = (struct bs *)img;
+		    mk1(bs->jmp[0], 0xeb);
+		    mk1(bs->jmp[1], x1 - 2);
+		    mk1(bs->jmp[2], 0x90);
+		    setstr(bs->oem, opt_O ? opt_O : "BSD  4.4",
+			   sizeof(bs->oem));
+		    memcpy(img + x1, bootcode, sizeof(bootcode));
+		    mk2(img + MINBPS - 2, DOSMAGIC);
+		}
+	    } else if (fat == 32 && bpb.infs != MAXU16 &&
+		       (lsn == bpb.infs ||
+			(bpb.bkbs != MAXU16 &&
+			 lsn == bpb.bkbs + bpb.infs))) {
+		mk4(img, 0x41615252);
+		mk4(img + MINBPS - 28, 0x61417272);
+		mk4(img + MINBPS - 24, 0xffffffff);
+		mk4(img + MINBPS - 20, bpb.rdcl);
+		mk2(img + MINBPS - 2, DOSMAGIC);
+	    } else if (lsn >= bpb.res && lsn < dir &&
+		       !((lsn - bpb.res) %
+			 (bpb.spf ? bpb.spf : bpb.bspf))) {
+		mk1(img[0], bpb.mid);
+		for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
+		    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
+	    } else if (lsn == dir && opt_L) {
+		de = (struct de *)img;
+		mklabel(de->namext, opt_L);
+		mk1(de->attr, 050);
+		x = (u_int)tm->tm_hour << 11 |
+		    (u_int)tm->tm_min << 5 |
+		    (u_int)tm->tm_sec >> 1;
+		mk2(de->time, x);
+		x = (u_int)(tm->tm_year - 80) << 9 |
+		    (u_int)(tm->tm_mon + 1) << 5 |
+		    (u_int)tm->tm_mday;
+		mk2(de->date, x);
+	    }
+	    if ((n = write(fd, img, bpb.bps)) == -1)
+		err(1, "%s", fname);
+	    if ((unsigned)n != bpb.bps)
+		errx(1, "%s: can't write sector %u", fname, lsn);
+	}
+    }
+    return 0;
+}
+
+/*
+ * Exit with error if file system is mounted.
+ */
+static void
+check_mounted(const char *fname, mode_t mode)
+{
+    struct statfs *mp;
+    const char *s1, *s2;
+    size_t len;
+    int n, r;
+
+#ifdef ANDROID
+    warnx("Skipping mount checks");
+#else
+    if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
+	err(1, "getmntinfo");
+    len = strlen(_PATH_DEV);
+    s1 = fname;
+    if (!strncmp(s1, _PATH_DEV, len))
+	s1 += len;
+    r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
+    for (; n--; mp++) {
+	s2 = mp->f_mntfromname;
+	if (!strncmp(s2, _PATH_DEV, len))
+	    s2 += len;
+	if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) ||
+	    !strcmp(s1, s2))
+	    errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
+    }
+#endif
+}
+
+/*
+ * Get a standard format.
+ */
+static void
+getstdfmt(const char *fmt, struct bpb *bpb)
+{
+    u_int x, i;
+
+    x = sizeof(stdfmt) / sizeof(stdfmt[0]);
+    for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
+    if (i == x)
+	errx(1, "%s: unknown standard format", fmt);
+    *bpb = stdfmt[i].bpb;
+}
+
+/*
+ * Get disk slice, partition, and geometry information.
+ */
+
+#ifdef ANDROID
+static void
+getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag,
+	    struct bpb *bpb)
+{
+    struct hd_geometry geom;
+
+    if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
+        fprintf(stderr, "Error getting bytes / sector (%s)", strerror(errno));
+        exit(1);
+    }
+
+    ckgeom(fname, bpb->bps, "bytes/sector");
+
+    if (ioctl(fd, BLKGETSIZE, &bpb->bsec)) {
+        fprintf(stderr, "Error getting blocksize (%s)", strerror(errno));
+        exit(1);
+    }
+
+    if (ioctl(fd, HDIO_GETGEO, &geom)) {
+        fprintf(stderr, "Error getting gemoetry (%s)", strerror(errno));
+        exit(1);
+    }
+
+    bpb->spt = geom.sectors;
+    ckgeom(fname, bpb->spt, "sectors/track");
+
+    bpb->hds = geom.heads;
+    ckgeom(fname, bpb->hds, "drive heads");
+}
+
+#else
+
+static void
+getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag,
+	    struct bpb *bpb)
+{
+    struct disklabel *lp, dlp;
+    struct fd_type type;
+    off_t ms, hs = 0;
+
+    lp = NULL;
+
+    /* If the user specified a disk type, try to use that */
+    if (dtype != NULL) {
+	lp = getdiskbyname(dtype);
+    }
+
+    /* Maybe it's a floppy drive */
+    if (lp == NULL) {
+	if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
+	    struct stat st;
+
+	    if (fstat(fd, &st))
+		err(1, "Cannot get disk size");
+	    /* create a fake geometry for a file image */
+	    ms = st.st_size;
+	    dlp.d_secsize = 512;
+	    dlp.d_nsectors = 63;
+	    dlp.d_ntracks = 255;
+	    dlp.d_secperunit = ms / dlp.d_secsize;
+	    lp = &dlp;
+	} else if (ioctl(fd, FD_GTYPE, &type) != -1) {
+	    dlp.d_secsize = 128 << type.secsize;
+	    dlp.d_nsectors = type.sectrac;
+	    dlp.d_ntracks = type.heads;
+	    dlp.d_secperunit = ms / dlp.d_secsize;
+	    lp = &dlp;
+	}
+    }
+
+    /* Maybe it's a fixed drive */
+    if (lp == NULL) {
+	if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
+	    if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
+		errx(1, "Cannot get sector size, %s", strerror(errno));
+
+	    /* XXX Should we use bpb->bps if it's set? */
+	    dlp.d_secperunit = ms / dlp.d_secsize;
+
+	    if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) {
+		warnx("Cannot get number of sectors per track, %s", strerror(errno));
+		dlp.d_nsectors = 63;
+	    }
+	    if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
+		warnx("Cannot get number of heads, %s", strerror(errno));
+		if (dlp.d_secperunit <= 63*1*1024)
+		    dlp.d_ntracks = 1;
+		else if (dlp.d_secperunit <= 63*16*1024)
+		    dlp.d_ntracks = 16;
+		else
+		    dlp.d_ntracks = 255;
+	    }
+	}
+
+	hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
+	lp = &dlp;
+    }
+
+    if (bpb->bps == 0)
+	bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
+    if (bpb->spt == 0)
+	bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
+    if (bpb->hds == 0)
+	bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
+    if (bpb->bsec == 0)
+	bpb->bsec = lp->d_secperunit;
+    if (bpb->hid == 0)
+	bpb->hid = hs;
+}
+#endif
+
+/*
+ * Print out BPB values.
+ */
+static void
+print_bpb(struct bpb *bpb)
+{
+    printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
+	   bpb->nft);
+    if (bpb->rde)
+	printf(" rde=%u", bpb->rde);
+    if (bpb->sec)
+	printf(" sec=%u", bpb->sec);
+    printf(" mid=%#x", bpb->mid);
+    if (bpb->spf)
+	printf(" spf=%u", bpb->spf);
+    printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
+    if (bpb->bsec)
+	printf(" bsec=%u", bpb->bsec);
+    if (!bpb->spf) {
+	printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
+	printf(" infs=");
+	printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
+	printf(" bkbs=");
+	printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
+    }
+    printf("\n");
+}
+
+/*
+ * Check a disk geometry value.
+ */
+static u_int
+ckgeom(const char *fname, u_int val, const char *msg)
+{
+    if (!val)
+	errx(1, "%s: no default %s", fname, msg);
+    if (val > MAXU16)
+	errx(1, "%s: illegal %s %d", fname, msg, val);
+    return val;
+}
+
+/*
+ * Convert and check a numeric option argument.
+ */
+static u_int
+argtou(const char *arg, u_int lo, u_int hi, const char *msg)
+{
+    char *s;
+    u_long x;
+
+    errno = 0;
+    x = strtoul(arg, &s, 0);
+    if (errno || !*arg || *s || x < lo || x > hi)
+	errx(1, "%s: bad %s", arg, msg);
+    return x;
+}
+
+/*
+ * Same for off_t, with optional skmgpP suffix
+ */
+static off_t
+argtooff(const char *arg, const char *msg)
+{
+    char *s;
+    off_t x;
+
+    x = strtoll(arg, &s, 0);
+    /* allow at most one extra char */
+    if (errno || x < 0 || (s[0] && s[1]) )
+	errx(1, "%s: bad %s", arg, msg);
+    if (*s) {	/* the extra char is the multiplier */
+	switch (*s) {
+	default:
+	    errx(1, "%s: bad %s", arg, msg);
+	    /* notreached */
+	
+	case 's':	/* sector */
+	case 'S':
+	    x <<= 9;	/* times 512 */
+	    break;
+
+	case 'k':	/* kilobyte */
+	case 'K':
+	    x <<= 10;	/* times 1024 */
+	    break;
+
+	case 'm':	/* megabyte */
+	case 'M':
+	    x <<= 20;	/* times 1024*1024 */
+	    break;
+
+	case 'g':	/* gigabyte */
+	case 'G':
+	    x <<= 30;	/* times 1024*1024*1024 */
+	    break;
+
+	case 'p':	/* partition start */
+	case 'P':	/* partition start */
+	case 'l':	/* partition length */
+	case 'L':	/* partition length */
+	    errx(1, "%s: not supported yet %s", arg, msg);
+	    /* notreached */
+	}
+    }
+    return x;
+}
+
+/*
+ * Check a volume label.
+ */
+static int
+oklabel(const char *src)
+{
+    int c, i;
+
+    for (i = 0; i <= 11; i++) {
+	c = (u_char)*src++;
+	if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
+	    break;
+    }
+    return i && !c;
+}
+
+/*
+ * Make a volume label.
+ */
+static void
+mklabel(u_int8_t *dest, const char *src)
+{
+    int c, i;
+
+    for (i = 0; i < 11; i++) {
+	c = *src ? toupper(*src++) : ' ';
+	*dest++ = !i && c == '\xe5' ? 5 : c;
+    }
+}
+
+/*
+ * Copy string, padding with spaces.
+ */
+static void
+setstr(u_int8_t *dest, const char *src, size_t len)
+{
+    while (len--)
+	*dest++ = *src ? *src++ : ' ';
+}
+
+/*
+ * Print usage message.
+ */
+static void
+usage(void)
+{
+	fprintf(stderr,
+	    "usage: newfs_msdos [ -options ] special [disktype]\n"
+	    "where the options are:\n"
+	    "\t-@ create file system at specified offset\n"                         
+	    "\t-B get bootstrap from file\n"
+	    "\t-C create image file with specified size\n"
+	    "\t-F FAT type (12, 16, or 32)\n"
+	    "\t-I volume ID\n"
+	    "\t-L volume label\n"
+	    "\t-N don't create file system: just print out parameters\n"
+	    "\t-O OEM string\n"
+	    "\t-S bytes/sector\n"
+	    "\t-a sectors/FAT\n"
+	    "\t-b block size\n"
+	    "\t-c sectors/cluster\n"
+	    "\t-e root directory entries\n"
+	    "\t-f standard format\n"
+	    "\t-h drive heads\n"
+	    "\t-i file system info sector\n"
+	    "\t-k backup boot sector\n"
+	    "\t-m media descriptor\n"
+	    "\t-n number of FATs\n"
+	    "\t-o hidden sectors\n"
+	    "\t-r reserved sectors\n"
+	    "\t-s file system size (sectors)\n"
+	    "\t-u sectors/track\n");
+	exit(1);
+}
diff --git a/toolbox/route.c b/toolbox/route.c
index adf5c69..4f66201 100644
--- a/toolbox/route.c
+++ b/toolbox/route.c
@@ -1,97 +1,103 @@
+/*
+ * Copyright (c) 2009, The Android Open Source Project
+ * 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 Google, Inc. 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.
+ */
 
 #include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <errno.h>
 #include <string.h>
-#include <ctype.h>
-
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <linux/if.h>
-#include <linux/sockios.h>
 #include <arpa/inet.h>
 #include <linux/route.h>
 
-static void die(const char *s)
-{
-    fprintf(stderr,"error: %s (%s)\n", s, strerror(errno));
-    exit(-1);
+static inline int set_address(const char *address, struct sockaddr *sa) {
+    return inet_aton(address, &((struct sockaddr_in *)sa)->sin_addr);
 }
 
-static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr)
-{
-	sin->sin_family = AF_INET;
-	sin->sin_port = 0;
-	sin->sin_addr.s_addr = inet_addr(addr);
-}
-
-#define ADVANCE(argc, argv) do { argc--, argv++; } while(0)
-#define EXPECT_NEXT(argc, argv) do {        \
-    ADVANCE(argc, argv);                    \
-	if (0 == argc) {  						\
-		errno = EINVAL;                     \
-		die("expecting one more argument"); \
-	}                                       \
-} while(0)		
-
-/* current support two kinds of usage */
+/* current support the following routing entries */
 /* route add default dev wlan0 */
-/* route add default gw 192.168.20.1 dev wlan0 */
+/* route add default gw 192.168.1.1 dev wlan0 */
+/* route add -net 192.168.1.2 netmask 255.255.255.0 gw 192.168.1.1 */
 
 int route_main(int argc, char *argv[])
 {
-    struct ifreq ifr;
-    int s,i;
-	struct rtentry rt;
-	struct sockaddr_in ina;
-   
-    if(argc == 0) return 0;
-    
-    strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
-    ifr.ifr_name[IFNAMSIZ-1] = 0;
-	ADVANCE(argc, argv);
+    struct rtentry rt = {
+        .rt_dst     = {.sa_family = AF_INET},
+        .rt_genmask = {.sa_family = AF_INET},
+        .rt_gateway = {.sa_family = AF_INET},
+    };
 
-    if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-        die("cannot open control socket\n");
-    }
+    errno = EINVAL;
+    if (argc > 2 && !strcmp(argv[1], "add")) {
+        if (!strcmp(argv[2], "default")) {
+            /* route add default dev wlan0 */
+            if (argc > 4 && !strcmp(argv[3], "dev")) {
+                rt.rt_flags = RTF_UP | RTF_HOST;
+                rt.rt_dev = argv[4];
+                errno = 0;
+                goto apply;
+            }
 
-    while(argc > 0){
-        if(!strcmp(argv[0], "add")) {
-			EXPECT_NEXT(argc, argv);
-			if(!strcmp(argv[0], "default")) {
-				EXPECT_NEXT(argc, argv);
-				memset((char *) &rt, 0, sizeof(struct rtentry));
-				rt.rt_dst.sa_family = AF_INET;	
-				if(!strcmp(argv[0], "dev")) {
-				  EXPECT_NEXT(argc, argv);
-				  rt.rt_flags = RTF_UP | RTF_HOST;
-				  rt.rt_dev = argv[0];
-				  if (ioctl(s, SIOCADDRT, &rt) < 0) die("SIOCADDRT");
-				}else if(!strcmp(argv[0], "gw")) {
-				  EXPECT_NEXT(argc, argv);
-				  rt.rt_flags = RTF_UP | RTF_GATEWAY;
-				  init_sockaddr_in((struct sockaddr_in *)&(rt.rt_genmask), "0.0.0.0");
-				  if(isdigit(argv[0][0])){
-					init_sockaddr_in((struct sockaddr_in *)&(rt.rt_gateway), argv[0]);
-				  }else{
-					die("expecting an IP address for parameter \"gw\"");
-				  }
-				  EXPECT_NEXT(argc, argv);
-				  if(!strcmp(argv[0], "dev")) {
-					EXPECT_NEXT(argc, argv);
-					rt.rt_dev = argv[0];
-					if (ioctl(s, SIOCADDRT, &rt) < 0){
-					  die("SIOCADDRT");
-					}
-				  }
-				}
-			}
+            /* route add default gw 192.168.1.1 dev wlan0 */
+            if (argc > 6 && !strcmp(argv[3], "gw") && !strcmp(argv[5], "dev")) {
+                rt.rt_flags = RTF_UP | RTF_GATEWAY;
+                rt.rt_dev = argv[6];
+                if (set_address(argv[4], &rt.rt_gateway)) {
+                    errno = 0;
+                }
+                goto apply;
+            }
         }
-		ADVANCE(argc, argv);
+
+        /* route add -net 192.168.1.2 netmask 255.255.255.0 gw 192.168.1.1 */
+        if (argc > 7 && !strcmp(argv[2], "-net") &&
+            !strcmp(argv[4], "netmask") && !strcmp(argv[6], "gw")) {
+            rt.rt_flags = RTF_UP | RTF_GATEWAY;
+            if (set_address(argv[3], &rt.rt_dst) &&
+                set_address(argv[5], &rt.rt_genmask) &&
+                set_address(argv[7], &rt.rt_gateway)) {
+                errno = 0;
+            }
+            goto apply;
+        }
     }
 
-    return 0;
+apply:
+    if (!errno) {
+        int s = socket(AF_INET, SOCK_DGRAM, 0);
+        if (s != -1 && (ioctl(s, SIOCADDRT, &rt) != -1 || errno == EEXIST)) {
+            return 0;
+        }
+    }
+    puts(strerror(errno));
+    return errno;
 }
-
diff --git a/toolbox/smd.c b/toolbox/smd.c
index 65ff994e..91e495c 100644
--- a/toolbox/smd.c
+++ b/toolbox/smd.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
diff --git a/vold/format.c b/vold/format.c
index 3383949..cd40197 100755
--- a/vold/format.c
+++ b/vold/format.c
@@ -26,7 +26,7 @@
 #include "diskmbr.h"
 #include "logwrapper.h"
 
-static char MKDOSFS_PATH[] = "/system/bin/mkdosfs";
+static char MKDOSFS_PATH[] = "/system/bin/newfs_msdos";
 static char MKE2FS_PATH[] = "/system/bin/mke2fs";
 
 int format_partition(blkdev_t *part, char *type)
@@ -37,14 +37,17 @@
     devpath = blkdev_get_devpath(part);
 
     if (!strcmp(type, FORMAT_TYPE_FAT32)) {
-        char *args[6];
+        char *args[9];
         args[0] = MKDOSFS_PATH;
-        args[1] = "-c 32";
-        args[2] = "-n 2";
-        args[3] = "-O android";
-        args[4] = devpath;
-        args[5] = NULL;
-        rc = logwrap(5, args);
+        args[1] = "-F";
+        args[2] = "32";
+        args[3] = "-c";
+        args[4] = "16";
+        args[5] = "-O";
+        args[6] = "android";
+        args[7] = devpath;
+        args[8] = NULL;
+        rc = logwrap(8, args, 1);
     } else {
         char *args[7];
         args[0] = MKE2FS_PATH;
@@ -54,7 +57,7 @@
         args[4] = "-v";
         args[5] = devpath;
         args[6] = NULL;
-        rc = logwrap(6, args);
+        rc = logwrap(6, args, 1);
     }
  
     free(devpath);
diff --git a/vold/logwrapper.c b/vold/logwrapper.c
index 2900f2e..8da4892 100644
--- a/vold/logwrapper.c
+++ b/vold/logwrapper.c
@@ -42,7 +42,8 @@
                 buffer[b] = '\0';
             } else if (buffer[b] == '\n') {
                 buffer[b] = '\0';
-                LOG(LOG_INFO, tag, &buffer[a]);
+
+                LOG(LOG_INFO, tag, "%s", &buffer[a]);
                 a = b + 1;
             }
         }
@@ -100,7 +101,7 @@
     }
 }
 
-int logwrap(int argc, char* argv[], pid_t *childPid)
+int logwrap(int argc, char* argv[], pid_t *childPid, int background)
 {
     pid_t pid;
 
@@ -138,6 +139,25 @@
         dup2(child_ptty, 2);
         close(child_ptty);
 
+        if (background) {
+            int fd = open("/dev/cpuctl/bg_non_interactive/tasks", O_WRONLY);
+      
+            if (fd >=0 ) {
+                char text[64];
+
+                sprintf(text, "%d", getpid());
+                if (write(fd, text, strlen(text)) < 0) {
+                    LOG(LOG_WARN, "logwrapper",
+                        "Unable to background process (%s)", strerror(errno));
+                    close(fd);
+                }
+                close(fd);
+            } else {
+                LOG(LOG_WARN, "logwrapper",
+                    "Unable to background process (%s)", strerror(errno));
+            }
+        }
+
         child(argc, argv);
     } else {
         return parent(argv[0], parent_ptty);
diff --git a/vold/logwrapper.h b/vold/logwrapper.h
index 602e24c..bf28aae 100644
--- a/vold/logwrapper.h
+++ b/vold/logwrapper.h
@@ -19,5 +19,5 @@
 #define _LOGWRAPPER_H
 
 #include <stdlib.h>
-int logwrap(int argc, char* argv[]);
+int logwrap(int argc, char* argv[], int background);
 #endif
diff --git a/vold/mmc.c b/vold/mmc.c
index 0f08964..fb61b2f 100644
--- a/vold/mmc.c
+++ b/vold/mmc.c
@@ -250,7 +250,7 @@
     char filename[255];
     char *uevent_buffer;
     ssize_t sz;
-    char *uevent_params[4];
+    char *uevent_params[5];
     char tmp[255];
     FILE *fp;
     char line[255];
diff --git a/vold/uevent.c b/vold/uevent.c
index cfb5786..66e70c5 100644
--- a/vold/uevent.c
+++ b/vold/uevent.c
@@ -272,8 +272,7 @@
         else
             door_open = false;
         volmgr_safe_mode(low_batt || door_open);
-    } else
-        LOG_VOL("handle_switch_event(): Ignoring switch '%s'", name);
+    }
 
     return 0;
 }
diff --git a/vold/volmgr_ext3.c b/vold/volmgr_ext3.c
index 680be21..fe3b2bb 100644
--- a/vold/volmgr_ext3.c
+++ b/vold/volmgr_ext3.c
@@ -107,7 +107,7 @@
     args[3] = devpath;
     args[4] = NULL;
 
-    int rc = logwrap(4, args);
+    int rc = logwrap(4, args, 1);
 
     if (rc == 0) {
         LOG_VOL("filesystem '%s' had no errors", devpath);
diff --git a/vold/volmgr_vfat.c b/vold/volmgr_vfat.c
index 344a166..7a4e12f 100644
--- a/vold/volmgr_vfat.c
+++ b/vold/volmgr_vfat.c
@@ -26,7 +26,7 @@
 
 #define VFAT_DEBUG 0
 
-static char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck";
+static char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos";
 
 int vfat_identify(blkdev_t *dev)
 {
@@ -39,6 +39,7 @@
 int vfat_check(blkdev_t *dev)
 {
     int rc;
+    boolean rw = true;
 
 #if VFAT_DEBUG
     LOG_VOL("vfat_check(%d:%d):", dev->major, dev->minor);
@@ -50,43 +51,24 @@
         return 0;
     }
 
-#ifdef VERIFY_PASS
-    char *args[7];
+    char *args[5];
     args[0] = FSCK_MSDOS_PATH;
-    args[1] = "-v";
-    args[2] = "-V";
-    args[3] = "-w";
-    args[4] = "-p";
-    args[5] = blkdev_get_devpath(dev);
-    args[6] = NULL;
-    rc = logwrap(6, args);
-    free(args[5]);
-#else
-    char *args[6];
-    args[0] = FSCK_MSDOS_PATH;
-    args[1] = "-v";
-    args[2] = "-w";
-    args[3] = "-p";
-    args[4] = blkdev_get_devpath(dev);
-    args[5] = NULL;
-    rc = logwrap(5, args);
-    free(args[4]);
-#endif
+    args[1] = "-p";
+    args[2] = "-f";
+    args[3] = blkdev_get_devpath(dev);
+    args[4] = NULL;
+    rc = logwrap(4, args, 1);
+    free(args[3]);
 
     if (rc == 0) {
         LOG_VOL("Filesystem check completed OK");
         return 0;
-    } else if (rc == 1) {
-        LOG_VOL("Filesystem check failed (general failure)");
-        return -EINVAL;
     } else if (rc == 2) {
-        LOG_VOL("Filesystem check failed (invalid usage)");
-        return -EIO;
-    } else if (rc == 4) {
-        LOG_VOL("Filesystem check completed (errors fixed)");
-    } else if (rc == 8) {
         LOG_VOL("Filesystem check failed (not a FAT filesystem)");
         return -ENODATA;
+    } else if (rc == -11) {
+        LOG_VOL("Filesystem check crashed");
+        return -EIO;
     } else {
         LOG_VOL("Filesystem check failed (unknown exit code %d)", rc);
         return -EIO;
@@ -113,15 +95,22 @@
         flags |= MS_REMOUNT;
     }
 
+    /*
+     * The mount masks restrict access so that:
+     * 1. The 'system' user cannot access the SD card at all - 
+     *    (protects system_server from grabbing file references)
+     * 2. Group users can RWX
+     * 3. Others can only RX
+     */
     rc = mount(devpath, vol->mount_point, "vfat", flags,
-               "utf8,uid=1000,gid=1000,fmask=711,dmask=700,shortname=mixed");
+               "utf8,uid=1000,gid=1015,fmask=702,dmask=702,shortname=mixed");
 
     if (rc && errno == EROFS) {
         LOGE("vfat_mount(%d:%d, %s): Read only filesystem - retrying mount RO",
              dev->major, dev->minor, vol->mount_point);
         flags |= MS_RDONLY;
         rc = mount(devpath, vol->mount_point, "vfat", flags,
-                   "utf8,uid=1000,gid=1000,fmask=711,dmask=700,shortname=mixed");
+                   "utf8,uid=1000,gid=1015,fmask=702,dmask=702,shortname=mixed");
     }
 
 #if VFAT_DEBUG