android: Update android_pipe virtual device.

WARNING: This makes the device dependent on the AndroidEmu
header android/emulation/android_pipe_device.h.
diff --git a/hw/arm/ranchu.c b/hw/arm/ranchu.c
index 4667a1a..8450a9a 100644
--- a/hw/arm/ranchu.c
+++ b/hw/arm/ranchu.c
@@ -38,7 +38,6 @@
 #include "qemu/config-file.h"
 #include "sysemu/char.h"
 #include "monitor/monitor.h"
-#include "hw/misc/android_pipe.h"
 
 #include "android/android.h"
 
diff --git a/hw/mips/mips_ranchu.c b/hw/mips/mips_ranchu.c
index 420d763..65d85c4 100644
--- a/hw/mips/mips_ranchu.c
+++ b/hw/mips/mips_ranchu.c
@@ -13,7 +13,6 @@
 #include "qemu/config-file.h"
 #include "sysemu/char.h"
 #include "monitor/monitor.h"
-#include "hw/misc/android_pipe.h"
 #include "hw/loader.h"
 #include "elf.h"
 #include "hw/intc/goldfish_pic.h"
diff --git a/hw/misc/android_pipe.c b/hw/misc/android_pipe.c
index 17a7a47..2e7f2a0 100644
--- a/hw/misc/android_pipe.c
+++ b/hw/misc/android_pipe.c
@@ -1,5 +1,6 @@
 /* Copyright (C) 2011 The Android Open Source Project
 ** Copyright (C) 2014 Linaro Limited
+** Copyright (C) 2015 Intel Corporation
 **
 ** This software is licensed under the terms of the GNU General Public
 ** License version 2, as published by the Free Software Foundation, and
@@ -27,15 +28,15 @@
 ** should give some thought to if this needs re-writing to take
 ** advantage of that infrastructure to create the pipes.
 */
-
+#include "android-qemu2-glue/utils/stream.h"
+#include "android/emulation/android_pipe_device.h"
 #include "hw/hw.h"
 #include "hw/sysbus.h"
-
-#include "hw/misc/android_pipe.h"
-#include "qemu-common.h"
-#include "qemu/timer.h"
 #include "qemu/error-report.h"
 
+#include <assert.h>
+#include <glib.h>
+
 /* Set to > 0 for debug output */
 #define PIPE_DEBUG 0
 
@@ -71,6 +72,70 @@
         exit(1);                        \
     } while (0);
 
+/* The following definitions must match those in the kernel driver
+ * found at android.googlesource.com/kernel/goldfish.git
+ *
+ *    drivers/platform/goldfish/goldfish_pipe.c
+ */
+
+/* pipe device registers */
+#define PIPE_REG_COMMAND            0x00  /* write: value = command */
+#define PIPE_REG_STATUS             0x04  /* read */
+#define PIPE_REG_CHANNEL            0x08  /* read/write: channel id */
+#define PIPE_REG_SIZE               0x0c  /* read/write: buffer size */
+#define PIPE_REG_ADDRESS            0x10  /* write: physical address */
+#define PIPE_REG_WAKES              0x14  /* read: wake flags */
+/* read/write: parameter buffer address */
+#define PIPE_REG_PARAMS_ADDR_LOW     0x18
+#define PIPE_REG_PARAMS_ADDR_HIGH    0x1c
+/* write: access with paremeter buffer */
+#define PIPE_REG_ACCESS_PARAMS       0x20
+#define PIPE_REG_VERSION             0x24 /* read: device version */
+#define PIPE_REG_CHANNEL_HIGH        0x30 /* read/write: high 32 bit channel id */
+#define PIPE_REG_ADDRESS_HIGH        0x34 /* write: high 32 bit physical address */
+
+/* list of commands for PIPE_REG_COMMAND */
+#define PIPE_CMD_OPEN               1  /* open new channel */
+#define PIPE_CMD_CLOSE              2  /* close channel (from guest) */
+#define PIPE_CMD_POLL               3  /* poll read/write status */
+
+/* The following commands are related to write operations */
+#define PIPE_CMD_WRITE_BUFFER       4  /* send a user buffer to the emulator */
+#define PIPE_CMD_WAKE_ON_WRITE      5  /* tell the emulator to wake us when writing is possible */
+
+/* The following commands are related to read operations, they must be
+ * listed in the same order than the corresponding write ones, since we
+ * will use (CMD_READ_BUFFER - CMD_WRITE_BUFFER) as a special offset
+ * in qemu_pipe_read_write() below.
+ */
+#define PIPE_CMD_READ_BUFFER        6  /* receive a page-contained buffer from the emulator */
+#define PIPE_CMD_WAKE_ON_READ       7  /* tell the emulator to wake us when reading is possible */
+
+struct access_params_32 {
+    uint32_t channel;   /* 0x00 */
+    uint32_t size;      /* 0x04 */
+    uint32_t address;   /* 0x08 */
+    uint32_t cmd;       /* 0x0c */
+    uint32_t result;    /* 0x10 */
+    /* reserved for future extension */
+    uint32_t flags;     /* 0x14 */
+};
+
+struct access_params_64 {
+    uint64_t channel;   /* 0x00 */
+    uint32_t size;      /* 0x08 */
+    uint64_t address;   /* 0x0c */
+    uint32_t cmd;       /* 0x14 */
+    uint32_t result;    /* 0x18 */
+    /* reserved for future extension */
+    uint32_t flags;     /* 0x1c */
+};
+
+union access_params {
+    struct access_params_32 aps32;
+    struct access_params_64 aps64;
+};
+
 /* Maximum length of pipe service name, in characters (excluding final 0) */
 #define MAX_PIPE_SERVICE_NAME_SIZE  255
 
@@ -106,362 +171,89 @@
 /***********************************************************************
  ***********************************************************************
  *****
- *****   P I P E   S E R V I C E   R E G I S T R A T I O N
- *****
- *****/
-
-#define MAX_PIPE_SERVICES  8
-typedef struct {
-    const char          *name;
-    void                *opaque;        /* pipe specific data */
-    AndroidPipeFuncs    funcs;
-} PipeService;
-
-typedef struct {
-    int          count;
-    PipeService  services[MAX_PIPE_SERVICES];
-} PipeServices;
-
-static PipeServices  _pipeServices[1];
-
-void
-android_pipe_add_type(const char *pipeName,
-                      void *pipeOpaque,
-                      const AndroidPipeFuncs *pipeFuncs)
-{
-    PipeServices *list = _pipeServices;
-    int          count = list->count;
-
-    if (count >= MAX_PIPE_SERVICES) {
-        APANIC("Too many goldfish pipe services (%d)", count);
-    }
-
-    if (strlen(pipeName) > MAX_PIPE_SERVICE_NAME_SIZE) {
-        APANIC("Pipe service name too long: '%s'", pipeName);
-    }
-
-    list->services[count].name   = pipeName;
-    list->services[count].opaque = pipeOpaque;
-    list->services[count].funcs  = pipeFuncs[0];
-
-    list->count++;
-}
-
-static const PipeService* android_pipe_find_type(const char *pipeName)
-{
-    PipeServices* list = _pipeServices;
-    int           count = list->count;
-    int           nn;
-
-    for (nn = 0; nn < count; nn++) {
-        if (!strcmp(list->services[nn].name, pipeName)) {
-            return &list->services[nn];
-        }
-    }
-    return NULL;
-}
-
-
-/***********************************************************************
- ***********************************************************************
- *****
  *****    P I P E   C O N N E C T I O N S
  *****
  *****/
 
-typedef struct Pipe {
-    struct Pipe                 *next;
-    struct Pipe                 *next_waked;
+typedef struct HwPipe {
+    struct HwPipe               *next;
+    struct HwPipe               *wanted_next;
+    struct HwPipe               *wanted_prev;
     PipeDevice                  *device;
     uint64_t                    channel; /* opaque kernel handle */
-    void                        *opaque;
-    const AndroidPipeFuncs      *funcs;
-    const PipeService           *service;
-    char*                       args;
     unsigned char               wanted;
     char                        closed;
-} Pipe;
+    void                        *pipe;
+} HwPipe;
 
-/* Forward */
-static void*  pipeConnector_new(Pipe*  pipe);
+static HwPipe* hwpipe_new0(struct PipeDevice* device) {
+    HwPipe* hwp;
+    ANEW0(hwp);
+    hwp->device = device;
+    return hwp;
+}
 
-static Pipe*
+static void hwpipe_free(HwPipe* hwp) {
+    android_pipe_guest_close(hwp->pipe);
+    free(hwp);
+}
+
+static unsigned char get_and_clear_pipe_wanted(HwPipe* pipe) {
+    unsigned char val = pipe->wanted;
+    pipe->wanted = 0;
+    return val;
+}
+
+static void set_pipe_wanted_bits(HwPipe* pipe, unsigned char val) {
+    pipe->wanted |= val;
+}
+
+static HwPipe*
 pipe_new0(PipeDevice* dev)
 {
-    Pipe*  pipe;
-    pipe = g_malloc0(sizeof(Pipe));
+    HwPipe*  pipe;
+    pipe = g_malloc0(sizeof(HwPipe));
     pipe->device = dev;
     return pipe;
 }
 
-static Pipe*
+static HwPipe*
 pipe_new(uint64_t channel, PipeDevice* dev)
 {
-    Pipe*  pipe = pipe_new0(dev);
+    HwPipe*  pipe = pipe_new0(dev);
     pipe->channel = channel;
-    pipe->opaque  = pipeConnector_new(pipe);
+    pipe->pipe  = android_pipe_guest_open(pipe);
     return pipe;
 }
 
-static Pipe**
-pipe_list_findp_channel(Pipe **list, uint64_t channel)
+static void pipe_free(HwPipe* pipe)
 {
-    Pipe** pnode = list;
-    for (;;) {
-        Pipe* node = *pnode;
-        if (node == NULL || node->channel == channel) {
-            break;
-        }
-        pnode = &node->next;
-    }
-    return pnode;
-}
-
-static Pipe**
-pipe_list_findp_waked(Pipe **list, Pipe *pipe)
-{
-    Pipe** pnode = list;
-    for (;;) {
-        Pipe* node = *pnode;
-        if (node == NULL || node == pipe) {
-            break;
-        }
-        pnode = &node->next_waked;
-    }
-    return pnode;
-}
-
-
-static void pipe_list_remove_waked(Pipe **list, Pipe *pipe)
-{
-    Pipe** lookup = pipe_list_findp_waked(list, pipe);
-    Pipe*  node   = *lookup;
-
-    if (node != NULL) {
-        (*lookup) = node->next_waked;
-        node->next_waked = NULL;
-    }
-}
-
-static void pipe_free(Pipe* pipe)
-{
-    /* Call close callback */
-    if (pipe->funcs->close) {
-        pipe->funcs->close(pipe->opaque);
-    }
-    /* Free stuff */
-    g_free(pipe->args);
+    android_pipe_guest_close(pipe->pipe);
     g_free(pipe);
 }
 
 /***********************************************************************
  ***********************************************************************
  *****
- *****    P I P E   C O N N E C T O R S
- *****
- *****/
-
-/* These are used to handle the initial connection attempt, where the
- * client is going to write the name of the pipe service it wants to
- * connect to, followed by a terminating zero.
- */
-typedef struct {
-    Pipe*  pipe;
-    char   buffer[128];
-    int    buffpos;
-} PipeConnector;
-
-static const AndroidPipeFuncs  pipeConnector_funcs;  // forward
-
-void*
-pipeConnector_new(Pipe*  pipe)
-{
-    PipeConnector*  pcon;
-
-    pcon = g_malloc0(sizeof(PipeConnector));
-    pcon->pipe  = pipe;
-    pipe->funcs = &pipeConnector_funcs;
-    return pcon;
-}
-
-static void
-pipeConnector_close( void* opaque )
-{
-    PipeConnector*  pcon = opaque;
-    g_free(pcon);
-}
-
-static int
-pipeConnector_sendBuffers( void* opaque, const AndroidPipeBuffer* buffers, int numBuffers )
-{
-    PipeConnector* pcon = opaque;
-    const AndroidPipeBuffer*  buffers_limit = buffers + numBuffers;
-    int ret = 0;
-
-    DD("%s: channel=0x%llx numBuffers=%d", __FUNCTION__,
-       (unsigned long long)pcon->pipe->channel,
-       numBuffers);
-
-    while (buffers < buffers_limit) {
-        int  avail;
-
-        DD("%s: buffer data (%3zd bytes): '%.*s'", __FUNCTION__,
-           buffers[0].size, (int) buffers[0].size, buffers[0].data);
-
-        if (buffers[0].size == 0) {
-            buffers++;
-            continue;
-        }
-
-        avail = sizeof(pcon->buffer) - pcon->buffpos;
-        if (avail > buffers[0].size)
-            avail = buffers[0].size;
-
-        if (avail > 0) {
-            memcpy(pcon->buffer + pcon->buffpos, buffers[0].data, avail);
-            pcon->buffpos += avail;
-            ret += avail;
-        }
-        buffers++;
-    }
-
-    /* Now check that our buffer contains a zero-terminated string */
-    if (memchr(pcon->buffer, '\0', pcon->buffpos) != NULL) {
-        /* Acceptable formats for the connection string are:
-         *
-         *   pipe:<name>
-         *   pipe:<name>:<arguments>
-         */
-        char* pipeName;
-        char* pipeArgs;
-
-        D("%s: connector: '%s'", __FUNCTION__, pcon->buffer);
-
-        if (memcmp(pcon->buffer, "pipe:", 5) != 0) {
-            /* Nope, we don't handle these for now. */
-            qemu_log_mask(LOG_UNIMP, "%s: Unknown pipe connection: '%s'\n",
-                          __func__, pcon->buffer);
-            return PIPE_ERROR_INVAL;
-        }
-
-        pipeName = pcon->buffer + 5;
-        pipeArgs = strchr(pipeName, ':');
-
-        /* Directly connect qemud:adb pipes to their adb backends without
-         * going through the qemud multiplexer.  All other uses of the ':'
-         * char than an initial "qemud:" will be parsed as arguments to the
-         * pipe name preceeding the colon.
-         */
-        if (pipeArgs && pipeArgs - pipeName == 5
-                && strncmp(pipeName, "qemud", 5) == 0) {
-            pipeArgs = strchr(pipeArgs + 1, ':');
-        }
-
-        if (pipeArgs != NULL) {
-            *pipeArgs++ = '\0';
-            if (!*pipeArgs)
-                pipeArgs = NULL;
-        }
-
-        Pipe* pipe = pcon->pipe;
-        const PipeService* svc = android_pipe_find_type(pipeName);
-        if (svc == NULL) {
-            qemu_log_mask(LOG_UNIMP, "%s: Couldn't find service: '%s'\n",
-                          __func__, pipeName);
-            return PIPE_ERROR_INVAL;
-        }
-
-        void*  peer = svc->funcs.init(pipe, svc->opaque, pipeArgs);
-        if (peer == NULL) {
-            fprintf(stderr,"%s: error initialising pipe:'%s' with args '%s'\n",
-                    __func__, pipeName, pipeArgs);
-            return PIPE_ERROR_INVAL;
-        }
-
-        /* Do the evil switch now */
-        pipe->opaque = peer;
-        pipe->service = svc;
-        pipe->funcs  = &svc->funcs;
-        pipe->args   = g_strdup(pipeArgs);
-        g_free(pcon);
-    }
-
-    return ret;
-}
-
-static int
-pipeConnector_recvBuffers( void* opaque, AndroidPipeBuffer* buffers, int numBuffers )
-{
-    return PIPE_ERROR_IO;
-}
-
-static unsigned
-pipeConnector_poll( void* opaque )
-{
-    return PIPE_POLL_OUT;
-}
-
-static void
-pipeConnector_wakeOn( void* opaque, int flags )
-{
-    /* nothing, really should never happen */
-}
-
-static void
-pipeConnector_save( void* pipe, QEMUFile* file )
-{
-    PipeConnector*  pcon = pipe;
-    qemu_put_sbe32(file, pcon->buffpos);
-    qemu_put_sbuffer(file, (const int8_t*)pcon->buffer, pcon->buffpos);
-}
-
-static void*
-pipeConnector_load( void* hwpipe, void* pipeOpaque, const char* args, QEMUFile* file )
-{
-    PipeConnector*  pcon;
-
-    int len = qemu_get_sbe32(file);
-    if (len < 0 || len > sizeof(pcon->buffer)) {
-        return NULL;
-    }
-    pcon = pipeConnector_new(hwpipe);
-    pcon->buffpos = len;
-    if (qemu_get_buffer(file, (uint8_t*)pcon->buffer, pcon->buffpos) != pcon->buffpos) {
-        g_free(pcon);
-        return NULL;
-    }
-    return pcon;
-}
-
-static const AndroidPipeFuncs  pipeConnector_funcs = {
-    NULL,  /* init */
-    pipeConnector_close,        /* should rarely happen */
-    pipeConnector_sendBuffers,  /* the interesting stuff */
-    pipeConnector_recvBuffers,  /* should not happen */
-    pipeConnector_poll,         /* should not happen */
-    pipeConnector_wakeOn,       /* should not happen */
-    pipeConnector_save,
-    pipeConnector_load,
-};
-
-
-/***********************************************************************
- ***********************************************************************
- *****
  *****    G O L D F I S H   P I P E   D E V I C E
  *****
  *****/
 
 struct PipeDevice {
-    AndroidPipeState *ps;       /* FIXME: backlink to instance state */
+    AndroidPipeState *ps;       // FIXME: backlink to instance state
 
-    /* the list of all pipes */
-    Pipe*  pipes;
+    // The list of all pipes.
+    HwPipe*  pipes;
+    // The list of the pipes that signalled some 'wanted' state.
+    HwPipe*  wanted_pipes_first;
+    // A cached wanted pipe after the guest's _CHANNEL_HIGH call. We need to
+    // return the very same pipe during the following *_CHANNEL call.
+    HwPipe*  wanted_pipe_after_channel_high;
 
-    /* the list of signalled pipes */
-    Pipe*  signaled_pipes;
+    // Cache of the pipes by channel for a faster lookup.
+    GHashTable* pipes_by_channel;
 
-    /* i/o registers */
+    // i/o registers
     uint64_t  address;
     uint32_t  size;
     uint32_t  status;
@@ -470,29 +262,122 @@
     uint64_t  params_addr;
 };
 
-/* Map the guest buffer specified by the guest vaddr 'address'.
+// hashtable-related functions
+// 64-bit emulator just casts uint64_t to gpointer to get a key for the
+// GLib's hash table. 32-bit one has to dynamically allocate an 8-byte chunk
+// of memory and copy the channel value there, storing pointer as a key.
+static uint64_t hash_channel_from_key(gconstpointer key) {
+#ifdef __x86_64__
+    // keys are just channels
+    return (uint64_t)key;
+#else
+    // keys are pointers to channels
+    return *(uint64_t*)key;
+#endif
+}
+
+static gpointer hash_create_key_from_channel(uint64_t channel) {
+#ifdef __x86_64__
+    return (gpointer)channel;
+#else
+    uint64_t* on_heap = (uint64_t*)malloc(sizeof(channel));
+    if (!on_heap) {
+        APANIC("%s: failed to allocate RAM for a channel value\n", __func__);
+    }
+    *on_heap = channel;
+    return on_heap;
+#endif
+}
+
+static gconstpointer hash_cast_key_from_channel(const uint64_t* channel) {
+#ifdef __x86_64__
+    return (gconstpointer)*channel;
+#else
+    return (gconstpointer)channel;
+#endif
+}
+
+static guint hash_channel(gconstpointer key) {
+    uint64_t channel = hash_channel_from_key(key);
+    return (guint)(channel ^ (channel >> 6));
+}
+
+static gboolean hash_channel_equal(gconstpointer a, gconstpointer b) {
+    return hash_channel_from_key(a) == hash_channel_from_key(b);
+}
+
+#ifdef __x86_64__
+// we don't need to free channels in 64-bit emulator as we store them in-place
+static void (*hash_channel_destroy)(gpointer a) = NULL;
+#else
+static void hash_channel_destroy(gpointer a) {
+    free((uint64_t*)a);
+}
+#endif
+
+// Wanted pipe linked list operations
+static HwPipe* wanted_pipes_pop_first(PipeDevice* dev) {
+    if (dev->wanted_pipe_after_channel_high) {
+        HwPipe* val = dev->wanted_pipe_after_channel_high;
+        dev->wanted_pipe_after_channel_high = NULL;
+        return val;
+    }
+    HwPipe* pipe = dev->wanted_pipes_first;
+    if (pipe) {
+        dev->wanted_pipes_first = pipe->wanted_next;
+        assert(pipe->wanted_prev == NULL);
+        pipe->wanted_next = NULL;
+        if (dev->wanted_pipes_first) {
+            dev->wanted_pipes_first->wanted_prev = NULL;
+        }
+    }
+    return pipe;
+}
+
+static void wanted_pipes_add(PipeDevice* dev, HwPipe* pipe) {
+    if (!pipe->wanted_next && !pipe->wanted_prev
+        && dev->wanted_pipes_first != pipe
+        && dev->wanted_pipe_after_channel_high != pipe) {
+        pipe->wanted_next = dev->wanted_pipes_first;
+        if (dev->wanted_pipes_first) {
+            dev->wanted_pipes_first->wanted_prev = pipe;
+        }
+        dev->wanted_pipes_first = pipe;
+    }
+}
+
+static void wanted_pipes_remove(PipeDevice* dev, HwPipe* pipe) {
+    if (dev->wanted_pipe_after_channel_high == pipe) {
+        dev->wanted_pipe_after_channel_high = NULL;
+    }
+
+    if (pipe->wanted_next) {
+        pipe->wanted_next->wanted_prev = pipe->wanted_prev;
+    }
+    if (pipe->wanted_prev) {
+        pipe->wanted_prev->wanted_next = pipe->wanted_next;
+        pipe->wanted_prev = NULL;
+    } else if (dev->wanted_pipes_first == pipe) {
+        dev->wanted_pipes_first = pipe->wanted_next;
+    }
+    pipe->wanted_next = NULL;
+}
+
+/* Update this version number if the device's interface changes. */
+#define PIPE_DEVICE_VERSION  1
+
+/* Map the guest buffer specified by the guest paddr 'phys'.
  * Returns a host pointer which should be unmapped later via
  * cpu_physical_memory_unmap(), or NULL if mapping failed (likely
- * because the vaddr doesn't actually point at RAM).
+ * because the paddr doesn't actually point at RAM).
  * Note that for RAM the "mapping" process doesn't actually involve a
  * data copy.
- *
- * TODO: using cpu_get_phys_page_debug() is a bit bogus, and we could
- * avoid it if we fixed the driver to do the sane thing and pass us
- * physical addresses rather than virtual ones.
  */
-static void *map_guest_buffer(target_ulong address, size_t size, int is_write)
+static void *map_guest_buffer(hwaddr phys, size_t size, int is_write)
 {
     hwaddr l = size;
     void *ptr;
 
-    /* Convert virtual address to physical address */
-    hwaddr phys = cpu_get_phys_page_debug(current_cpu, address);
-
-    if (phys == -1) {
-        return NULL;
-    }
-
     ptr = cpu_physical_memory_map(phys, &l, is_write);
     if (!ptr) {
         /* Can't happen for RAM */
@@ -510,11 +395,9 @@
     return ptr;
 }
 
-static void
-pipeDevice_doCommand( PipeDevice* dev, uint32_t command )
-{
-    Pipe** lookup = pipe_list_findp_channel(&dev->pipes, dev->channel);
-    Pipe*  pipe   = *lookup;
+static void pipeDevice_doCommand(PipeDevice* dev, uint32_t command) {
+    HwPipe* pipe = (HwPipe*)g_hash_table_lookup(
+            dev->pipes_by_channel, hash_cast_key_from_channel(&dev->channel));
 
     /* Check that we're referring a known pipe channel */
     if (command != PIPE_CMD_OPEN && pipe == NULL) {
@@ -539,24 +422,40 @@
         pipe->next = dev->pipes;
         dev->pipes = pipe;
         dev->status = 0;
+        g_hash_table_insert(dev->pipes_by_channel,
+                            hash_create_key_from_channel(dev->channel), pipe);
         break;
 
-    case PIPE_CMD_CLOSE:
+    case PIPE_CMD_CLOSE: {
         DD("%s: CMD_CLOSE channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);
-        /* Remove from device's lists */
-        *lookup = pipe->next;
+        // Remove from device's lists.
+        // This linear lookup is potentially slow, but we don't delete pipes
+        // often enough for it to become noticable.
+        HwPipe** pnode = &dev->pipes;
+        while (*pnode && *pnode != pipe) {
+            pnode = &(*pnode)->next;
+        }
+        if (!*pnode) {
+            dev->status = PIPE_ERROR_INVAL;
+            break;
+        }
+        *pnode = pipe->next;
         pipe->next = NULL;
-        pipe_list_remove_waked(&dev->signaled_pipes, pipe);
+        g_hash_table_remove(dev->pipes_by_channel,
+                            hash_cast_key_from_channel(&pipe->channel));
+        wanted_pipes_remove(dev, pipe);
+
         pipe_free(pipe);
         break;
+    }
 
     case PIPE_CMD_POLL:
-        dev->status = pipe->funcs->poll(pipe->opaque);
+        dev->status = android_pipe_guest_poll(pipe->pipe);
         DD("%s: CMD_POLL > status=%d", __FUNCTION__, dev->status);
         break;
 
     case PIPE_CMD_READ_BUFFER: {
-        /* Translate virtual address into physical one, into emulator memory. */
+        /* Translate guest physical address into emulator memory. */
         AndroidPipeBuffer  buffer;
         buffer.data = map_guest_buffer(dev->address, dev->size, 1);
         if (!buffer.data) {
@@ -564,7 +463,7 @@
             break;
         }
         buffer.size = dev->size;
-        dev->status = pipe->funcs->recvBuffers(pipe->opaque, &buffer, 1);
+        dev->status = android_pipe_guest_recv(pipe->pipe, &buffer, 1);
         DD("%s: CMD_READ_BUFFER channel=0x%llx address=0x%16llx size=%d > status=%d",
            __FUNCTION__, (unsigned long long)dev->channel, (unsigned long long)dev->address,
            dev->size, dev->status);
@@ -573,7 +472,7 @@
     }
 
     case PIPE_CMD_WRITE_BUFFER: {
-        /* Translate virtual address into physical one, into emulator memory. */
+        /* Translate guest physical address into emulator memory. */
         AndroidPipeBuffer  buffer;
         buffer.data = map_guest_buffer(dev->address, dev->size, 0);
         if (!buffer.data) {
@@ -581,7 +480,7 @@
             break;
         }
         buffer.size = dev->size;
-        dev->status = pipe->funcs->sendBuffers(pipe->opaque, &buffer, 1);
+        dev->status = android_pipe_guest_send(pipe->pipe, &buffer, 1);
         DD("%s: CMD_WRITE_BUFFER channel=0x%llx address=0x%16llx size=%d > status=%d",
            __FUNCTION__, (unsigned long long)dev->channel, (unsigned long long)dev->address,
            dev->size, dev->status);
@@ -593,7 +492,7 @@
         DD("%s: CMD_WAKE_ON_READ channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);
         if ((pipe->wanted & PIPE_WAKE_READ) == 0) {
             pipe->wanted |= PIPE_WAKE_READ;
-            pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
+            android_pipe_guest_wake_on(pipe->pipe, pipe->wanted);
         }
         dev->status = 0;
         break;
@@ -602,7 +501,7 @@
         DD("%s: CMD_WAKE_ON_WRITE channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);
         if ((pipe->wanted & PIPE_WAKE_WRITE) == 0) {
             pipe->wanted |= PIPE_WAKE_WRITE;
-            pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
+            android_pipe_guest_wake_on(pipe->pipe, pipe->wanted);
         }
         dev->status = 0;
         break;
@@ -721,34 +620,41 @@
         DR("%s: REG_STATUS status=%d (0x%x)", __FUNCTION__, dev->status, dev->status);
         return dev->status;
 
-    case PIPE_REG_CHANNEL:
-        if (dev->signaled_pipes != NULL) {
-            Pipe* pipe = dev->signaled_pipes;
+    case PIPE_REG_CHANNEL: {
+        HwPipe* wanted_pipe = wanted_pipes_pop_first(dev);
+        if (wanted_pipe != NULL) {
+            dev->wakes = get_and_clear_pipe_wanted(wanted_pipe);
             DR("%s: channel=0x%llx wanted=%d", __FUNCTION__,
-               (unsigned long long)pipe->channel, pipe->wanted);
-            dev->wakes = pipe->wanted;
-            pipe->wanted = 0;
-            dev->signaled_pipes = pipe->next_waked;
-            pipe->next_waked = NULL;
-            if (dev->signaled_pipes == NULL) {
-                /* android_device_set_irq(&dev->dev, 0, 0); */
-                qemu_set_irq(s->irq, 0);
-                DD("%s: lowering IRQ", __FUNCTION__);
-            }
-            return (uint32_t)(pipe->channel & 0xFFFFFFFFUL);
+               (unsigned long long)wanted_pipe->channel, dev->wakes);
+            return (uint32_t)(wanted_pipe->channel & 0xFFFFFFFFUL);
+        } else {
+            qemu_set_irq(s->irq, 0);
+            DD("%s: no signaled channels, lowering IRQ", __FUNCTION__);
+            return 0;
         }
-        DR("%s: no signaled channels", __FUNCTION__);
-        return 0;
+    }
 
-    case PIPE_REG_CHANNEL_HIGH:
-        if (dev->signaled_pipes != NULL) {
-            Pipe* pipe = dev->signaled_pipes;
+    case PIPE_REG_CHANNEL_HIGH: {
+        // TODO(zyy): this call is really dangerous; currently the device would
+        //  stop the calls as soon as we return 0 here; but it means that if the
+        //  channel's upper 32 bits are zeroes (that happens), we won't be able
+        //  to wake either that channel or any following ones.
+        //  I think we should create a new pipe protocol to deal with this
+        //  issue and also reduce chattiness of the pipe communication at the
+        //  same time.
+        HwPipe* wanted_pipe = wanted_pipes_pop_first(dev);
+        if (wanted_pipe != NULL) {
+            dev->wanted_pipe_after_channel_high = wanted_pipe;
             DR("%s: channel_high=0x%llx wanted=%d", __FUNCTION__,
-               (unsigned long long)pipe->channel, pipe->wanted);
-            return (uint32_t)(pipe->channel >> 32);
+               (unsigned long long)wanted_pipe->channel, wanted_pipe->wanted);
+            assert((uint32_t)(wanted_pipe->channel >> 32) != 0);
+            return (uint32_t)(wanted_pipe->channel >> 32);
+        } else {
+            qemu_set_irq(s->irq, 0);
+            DD("%s: no signaled channels (for high), lowering IRQ", __func__);
+            return 0;
         }
-        DR("%s: no signaled channels", __FUNCTION__);
-        return 0;
+    }
 
     case PIPE_REG_WAKES:
         DR("%s: wakes %d", __FUNCTION__, dev->wakes);
@@ -760,6 +666,9 @@
     case PIPE_REG_PARAMS_ADDR_LOW:
         return (uint32_t)(dev->params_addr & 0xFFFFFFFFUL);
 
+    case PIPE_REG_VERSION:
+        return PIPE_DEVICE_VERSION;
+
     default:
         qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown register %" HWADDR_PRId
                       " (0x%" HWADDR_PRIx ")\n", __FUNCTION__, offset, offset);
@@ -773,6 +682,196 @@
     .endianness = DEVICE_NATIVE_ENDIAN
 };
 
+static void qemu2_android_pipe_reset(void* hwpipe, void* internal_pipe);
+static void qemu2_android_pipe_host_signal_wake(void* hwpipe, unsigned flags);
+static void qemu2_android_pipe_host_close(void* hwpipe);
+
+static const AndroidPipeHwFuncs qemu2_android_pipe_hw_funcs = {
+    .resetPipe = qemu2_android_pipe_reset,
+    .closeFromHost = qemu2_android_pipe_host_close,
+    .signalWake = qemu2_android_pipe_host_signal_wake,
+};
+
+#define ANDROID_PIPE_SAVE_VERSION 1
+
+static void android_pipe_save(QEMUFile* f, void* opaque) {
+    AndroidPipeState* s = opaque;
+    PipeDevice* dev = s->dev;
+    Stream* stream = stream_from_qemufile(f);
+
+    /* Save i/o registers. */
+    stream_put_be64(stream, dev->address);
+    stream_put_be32(stream, dev->size);
+    stream_put_be32(stream, dev->status);
+    stream_put_be64(stream, dev->channel);
+    stream_put_be32(stream, dev->wakes);
+    stream_put_be64(stream, dev->params_addr);
+
+    /* Save the pipe count and state of the pipes. */
+    int pipe_count = 0;
+    HwPipe* pipe;
+    for (pipe = dev->pipes; pipe; pipe = pipe->next) {
+        ++pipe_count;
+    }
+    stream_put_be32(stream, pipe_count);
+    for (pipe = dev->pipes; pipe; pipe = pipe->next) {
+        stream_put_be64(stream, pipe->channel);
+        stream_put_byte(stream, pipe->closed);
+        stream_put_byte(stream, pipe->wanted);
+        android_pipe_guest_save(pipe->pipe, stream);
+    }
+
+    /* Save wanted pipes list. */
+    int wanted_pipes_count = 0;
+    for (pipe = dev->wanted_pipes_first; pipe; pipe = pipe->wanted_next) {
+        wanted_pipes_count++;
+    }
+    stream_put_be32(stream, wanted_pipes_count);
+    for (pipe = dev->wanted_pipes_first; pipe; pipe = pipe->wanted_next) {
+        stream_put_be64(stream, pipe->channel);
+    }
+    if (dev->wanted_pipe_after_channel_high) {
+        stream_put_byte(stream, 1);
+        stream_put_be64(stream, dev->wanted_pipe_after_channel_high->channel);
+    } else {
+        stream_put_byte(stream, 0);
+    }
+
+    stream_free(stream);
+}
+
+static int android_pipe_load(QEMUFile* f, void* opaque, int version_id) {
+    AndroidPipeState* s = opaque;
+    PipeDevice* dev = s->dev;
+    Stream* stream = stream_from_qemufile(f);
+
+    /* Load i/o registers. */
+    dev->address = stream_get_be64(stream);
+    dev->size = stream_get_be32(stream);
+    dev->status = stream_get_be32(stream);
+    dev->channel = stream_get_be64(stream);
+    dev->wakes = stream_get_be32(stream);
+    dev->params_addr = stream_get_be64(stream);
+
+    /* Clean up old pipe objects. */
+    HwPipe* pipe = dev->pipes;
+    while(pipe) {
+        HwPipe* old_pipe = pipe;
+        pipe = pipe->next;
+        hwpipe_free(old_pipe);
+    }
+
+    /* Restore pipes. */
+    int pipe_count = stream_get_be32(stream);
+    int pipe_n;
+    HwPipe** pipe_list_end = &(dev->pipes);
+    *pipe_list_end = NULL;
+    uint64_t* force_closed_pipes = malloc(sizeof(uint64_t) * pipe_count);
+    int force_closed_pipes_count = 0;
+    for (pipe_n = 0; pipe_n < pipe_count; pipe_n++) {
+        HwPipe* pipe = hwpipe_new0(dev);
+        char force_close = 0;
+        pipe->channel = stream_get_be64(stream);
+        pipe->closed = stream_get_byte(stream);
+        pipe->wanted = stream_get_byte(stream);
+        pipe->pipe = android_pipe_guest_load(stream, pipe, &force_close);
+        pipe->wanted_next = pipe->wanted_prev = NULL;
+
+        // |pipe| might be NULL in case it couldn't be saved. However,
+        // in that case |force_close| will be set by android_pipe_guest_load,
+        // so |force_close| should be checked first so that the function
+        // can continue.
+        if (force_close) {
+            pipe->closed = 1;
+            force_closed_pipes[force_closed_pipes_count++] = pipe->channel;
+        } else if (!pipe->pipe) {
+            hwpipe_free(pipe);
+            free(force_closed_pipes);
+            return -EIO;
+        }
+
+        pipe->next = NULL;
+        *pipe_list_end = pipe;
+        pipe_list_end = &(pipe->next);
+    }
+
+    /* Rebuild the pipes-by-channel table. */
+    g_hash_table_remove_all(dev->pipes_by_channel); /* Clean up old data. */
+    for(pipe = dev->pipes; pipe; pipe = pipe->next) {
+        g_hash_table_insert(dev->pipes_by_channel,
+                            hash_create_key_from_channel(pipe->channel),
+                            pipe);
+    }
+
+    /* Reconstruct wanted pipes list. */
+    HwPipe** wanted_pipe_list_end = &(dev->wanted_pipes_first);
+    *wanted_pipe_list_end = NULL;
+    int wanted_pipes_count = stream_get_be32(stream);
+    uint64_t channel;
+    for (pipe_n = 0; pipe_n < wanted_pipes_count; ++pipe_n) {
+        channel = stream_get_be64(stream);
+        HwPipe* pipe = g_hash_table_lookup(dev->pipes_by_channel,
+                                           hash_cast_key_from_channel(&channel));
+        if (pipe) {
+            pipe->wanted_prev = *wanted_pipe_list_end;
+            pipe->wanted_next = NULL;
+            *wanted_pipe_list_end = pipe;
+            wanted_pipe_list_end = &(pipe->wanted_next);
+        } else {
+            stream_free(stream);
+            free(force_closed_pipes);
+            return -EIO;
+        }
+    }
+    if (stream_get_byte(stream)) {
+        channel = stream_get_be64(stream);
+        dev->wanted_pipe_after_channel_high =
+            g_hash_table_lookup(dev->pipes_by_channel,
+                                hash_cast_key_from_channel(&channel));
+        if (!dev->wanted_pipe_after_channel_high) {
+            stream_free(stream);
+            free(force_closed_pipes);
+            return -EIO;
+        }
+    } else {
+        dev->wanted_pipe_after_channel_high = NULL;
+    }
+
+    /* Add forcibly closed pipes to wanted pipes list */
+    for (pipe_n = 0; pipe_n < force_closed_pipes_count; pipe_n++) {
+        HwPipe* pipe =
+            g_hash_table_lookup(dev->pipes_by_channel,
+                                hash_cast_key_from_channel(
+                                    &force_closed_pipes[pipe_n]));
+        set_pipe_wanted_bits(pipe, PIPE_WAKE_CLOSED);
+        pipe->closed = 1;
+        if (!pipe->wanted_next &&
+            !pipe->wanted_prev &&
+            pipe != dev->wanted_pipe_after_channel_high) {
+            pipe->wanted_prev = *wanted_pipe_list_end;
+            *wanted_pipe_list_end = pipe;
+            wanted_pipe_list_end = &(pipe->wanted_next);
+        }
+    }
+
+    stream_free(stream);
+    free(force_closed_pipes);
+    return 0;
+}
+
+static void android_pipe_post_load(void* opaque) {
+    /* This function gets invoked after all VM state has
+     * been loaded. Raising IRQ in the load handler causes
+     * problems.
+     */
+    PipeDevice* dev = ((AndroidPipeState*)opaque)->dev;
+    if (dev->wanted_pipes_first) {
+        qemu_set_irq(dev->ps->irq, 1);
+    } else {
+        qemu_set_irq(dev->ps->irq, 0);
+    }
+}
+
 static void android_pipe_realize(DeviceState *dev, Error **errp)
 {
     SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
@@ -780,59 +879,61 @@
 
     s->dev = (PipeDevice *) g_malloc0(sizeof(PipeDevice));
     s->dev->ps = s; /* HACK: backlink */
-
+    s->dev->wanted_pipes_first = NULL;
+    s->dev->wanted_pipe_after_channel_high = NULL;
+    s->dev->pipes_by_channel = g_hash_table_new_full(
+                                   hash_channel, hash_channel_equal,
+                                   hash_channel_destroy, NULL);
+    if (!s->dev->pipes_by_channel) {
+        APANIC("%s: failed to initialize pipes hash\n", __func__);
+    }
     memory_region_init_io(&s->iomem, OBJECT(s), &android_pipe_iomem_ops, s,
-                          "android_pipe", 0x1000 /*TODO: ?how big?*/);
+                          "android_pipe", 0x2000 /*TODO: ?how big?*/);
     sysbus_init_mmio(sbdev, &s->iomem);
     sysbus_init_irq(sbdev, &s->irq);
 
-    android_zero_pipe_init();
-    android_pingpong_init();
-    android_throttle_init();
-    android_sensors_init();
+    android_pipe_set_hw_funcs(&qemu2_android_pipe_hw_funcs);
 
-    /* TODO: This may be a complete hack and there may be beautiful QOM ways
-     * to accomplish this.
-     *
-     * Initialize android pipe backends
-     */
-    android_adb_dbg_backend_init();
-
+    register_savevm_with_post_load
+        (dev,
+         "android_pipe",
+         0,
+         ANDROID_PIPE_SAVE_VERSION,
+         android_pipe_save,
+         android_pipe_load,
+         android_pipe_post_load,
+         s);
 }
 
-void
-android_pipe_wake( void* hwpipe, unsigned flags )
+static void qemu2_android_pipe_reset(void* hwpipe, void* internal_pipe) {
+    HwPipe* pipe = hwpipe;
+    pipe->pipe = internal_pipe;
+}
+
+static void qemu2_android_pipe_host_signal_wake( void* hwpipe, unsigned flags )
 {
-    Pipe*  pipe = hwpipe;
-    Pipe** lookup;
+    HwPipe*  pipe = hwpipe;
     PipeDevice*  dev = pipe->device;
 
     DD("%s: channel=0x%llx flags=%d", __FUNCTION__, (unsigned long long)pipe->channel, flags);
 
-    /* If not already there, add to the list of signaled pipes */
-    lookup = pipe_list_findp_waked(&dev->signaled_pipes, pipe);
-    if (!*lookup) {
-        pipe->next_waked = dev->signaled_pipes;
-        dev->signaled_pipes = pipe;
-    }
-    pipe->wanted |= (unsigned)flags;
+    set_pipe_wanted_bits(pipe, (unsigned char)flags);
+    wanted_pipes_add(dev, pipe);
 
     /* Raise IRQ to indicate there are items on our list ! */
-    /* android_device_set_irq(&dev->dev, 0, 1);*/
     qemu_set_irq(dev->ps->irq, 1);
     DD("%s: raising IRQ", __FUNCTION__);
 }
 
-void
-android_pipe_close( void* hwpipe )
+static void qemu2_android_pipe_host_close( void* hwpipe )
 {
-    Pipe* pipe = hwpipe;
+    HwPipe* pipe = hwpipe;
 
     D("%s: channel=0x%llx (closed=%d)", __FUNCTION__, (unsigned long long)pipe->channel, pipe->closed);
 
     if (!pipe->closed) {
         pipe->closed = 1;
-        android_pipe_wake( hwpipe, PIPE_WAKE_CLOSED );
+        qemu2_android_pipe_host_signal_wake( hwpipe, PIPE_WAKE_CLOSED );
     }
 }
 
@@ -856,3 +957,4 @@
 }
 
 type_init(android_pipe_register);
+
diff --git a/include/hw/misc/android_pipe.h b/include/hw/misc/android_pipe.h
deleted file mode 100644
index 9afabc8..0000000
--- a/include/hw/misc/android_pipe.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (C) 2011 The Android Open Source Project
-**
-** This software is licensed under the terms of the GNU General Public
-** License version 2, as published by the Free Software Foundation, and
-** may be copied, distributed, and modified under those terms.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-** GNU General Public License for more details.
-*/
-#ifndef _HW_ANDROID_PIPE_H
-#define _HW_ANDROID_PIPE_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include "hw/hw.h"
-
-extern bool qemu2_adb_server_init(int port);
-
-#include "android/emulation/android_pipe_device.h"
-#include "android/opengles-pipe.h"
-
-extern void android_zero_pipe_init(void);
-extern void android_pingpong_init(void);
-extern void android_throttle_init(void);
-extern void android_adb_dbg_backend_init(void);
-extern void android_adb_backend_init(void);
-extern void android_sensors_init(void);
-
-#endif /* _HW_ANDROID_PIPE_H */