aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Bennée <alex.bennee@linaro.org>2014-06-12 16:42:50 +0100
committerPeter Maydell <peter.maydell@linaro.org>2014-06-23 16:33:29 +0100
commitffb7c1f9f983e41ae80001fb1ad5905e03af582a (patch)
treea80650c2542939ad5c2a17fdf5a2b6f402fd36e2
parentc9bfac34bad08e5d115c6693e97a372cc5f4572e (diff)
android_pipe: import the qemu_pipe/goldfish_pipe code
This brings in the android emulator pipe code from the current aosp-qemu master branch. It works with and has been tested with the pingpong pipe device. Currently the device cannot save and restore. - remove dependency on android utils (ANEW/ASTRDUP/AFREE) - detach from the goldfish_device bus bits (inc irq raising) - import a few helper functions (uint64_set_high/low, goldfish_guest_is_64bit) - disable the VM save/restore code - use current_cpu for memory translation ops instead of cpu_single_env - fix debugging prints using portable formats - common code path for translating vaddr->qemu addr* - make some debug statments user-visible (unimp and guest errors) There is a hacking backlink between AndroidPipeState and the PipeDevice stuff (which should be merged cleanly later) just so we can get back to ->irq when we need to. Signed-off-by: Alex Bennée <alex.bennee@linaro.org> [cdall: special casing "qemud:<name>"] Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
-rw-r--r--default-configs/arm-softmmu.mak2
-rw-r--r--hw/arm/ranchu.c5
-rw-r--r--hw/misc/Makefile.objs2
-rw-r--r--hw/misc/android_pipe.c1315
-rw-r--r--include/hw/misc/android_pipe.h220
5 files changed, 1544 insertions, 0 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index b1dc85fb1d..f10cc69773 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -89,3 +89,5 @@ CONFIG_INTEGRATOR_DEBUG=y
CONFIG_ALLWINNER_A10_PIT=y
CONFIG_ALLWINNER_A10_PIC=y
CONFIG_ALLWINNER_A10=y
+
+CONFIG_ANDROID_PIPE=y
diff --git a/hw/arm/ranchu.c b/hw/arm/ranchu.c
index f9696a3bec..be2dbfcc5d 100644
--- a/hw/arm/ranchu.c
+++ b/hw/arm/ranchu.c
@@ -63,6 +63,7 @@ enum {
RANCHU_GF_BATTERY,
RANCHU_GF_AUDIO,
RANCHU_GF_EVDEV,
+ RANCHU_ANDROID_PIPE,
RANCHU_MMIO,
};
@@ -107,6 +108,7 @@ static const MemMapEntry memmap[] = {
[RANCHU_GF_AUDIO] = { 0x9030000, 0x100 },
[RANCHU_GF_EVDEV] = { 0x9040000, 0x1000 },
[RANCHU_MMIO] = { 0xa000000, 0x200 },
+ [RANCHU_ANDROID_PIPE] = {0xa010000, 0x2000 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
/* 0x10000000 .. 0x40000000 reserved for PCI */
[RANCHU_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
@@ -118,6 +120,7 @@ static const int irqmap[] = {
[RANCHU_GF_BATTERY] = 3,
[RANCHU_GF_AUDIO] = 4,
[RANCHU_GF_EVDEV] = 5,
+ [RANCHU_ANDROID_PIPE] = 6,
[RANCHU_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
};
@@ -458,6 +461,8 @@ static void ranchu_init(QEMUMachineInitArgs *args)
#endif
create_simple_device(vbi, pic, RANCHU_GF_EVDEV, "goldfish-events",
"generic,goldfish-events-keypad", 1, 0, 0);
+ create_simple_device(vbi, pic, RANCHU_ANDROID_PIPE, "android_pipe",
+ "generic,android-pipe", 1, 0, 0);
/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 8a5a0dfcba..315d1285c6 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -14,6 +14,8 @@ common-obj-$(CONFIG_INTEGRATOR_DEBUG) += arm_integrator_debug.o
common-obj-$(CONFIG_A9SCU) += a9scu.o
common-obj-$(CONFIG_ARM11SCU) += arm11scu.o
+obj-$(CONFIG_ANDROID_PIPE) += android_pipe.o
+
# PKUnity SoC devices
common-obj-$(CONFIG_PUV3) += puv3_pm.o
diff --git a/hw/misc/android_pipe.c b/hw/misc/android_pipe.c
new file mode 100644
index 0000000000..90bce9526d
--- /dev/null
+++ b/hw/misc/android_pipe.c
@@ -0,0 +1,1315 @@
+/* Copyright (C) 2011 The Android Open Source Project
+** Copyright (C) 2014 Linaro Limited
+**
+** 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.
+**
+** Description
+**
+** This device provides a virtual pipe device (originally called
+** goldfish_pipe and latterly qemu_pipe). This allows the android
+** running under the emulator to open a fast connection to the host
+** for various purposes including the adb debug bridge and
+** (eventually) the opengles pass-through. This file contains only the
+** basic pipe infrastructure and a couple of test pipes. Additional
+** pipes are registered with the android_pipe_add_type() call.
+**
+** Open Questions
+**
+** Since this was originally written there have been a number of other
+** virtual devices added to QEMU using the virtio infrastructure. We
+** should give some thought to if this needs re-writing to take
+** advantage of that infrastructure to create the pipes.
+*/
+
+#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"
+
+/* Set to > 0 for debug output */
+#define PIPE_DEBUG 0
+
+/* Set to 1 to debug i/o register reads/writes */
+#define PIPE_DEBUG_REGS 0
+
+#if PIPE_DEBUG >= 1
+#define D(fmt, ...) \
+ do { fprintf(stdout, "android_pipe: " fmt "\n", ## __VA_ARGS__); } while (0)
+#else
+#define D(fmt, ...) do { /* nothing */ } while (0)
+#endif
+
+#if PIPE_DEBUG >= 2
+#define DD(fmt, ...) \
+ do { fprintf(stdout, "android_pipe: " fmt "\n", ## __VA_ARGS__); } while (0)
+#else
+#define DD(fmt, ...) do { /* nothing */ } while (0)
+#endif
+
+#if PIPE_DEBUG_REGS >= 1
+# define DR(...) D(__VA_ARGS__)
+#else
+# define DR(...) do { /* nothing */ } while (0)
+#endif
+
+#define E(fmt, ...) \
+ do { fprintf(stdout, "ERROR:" fmt "\n", ## __VA_ARGS__); } while (0)
+
+#define APANIC(...) \
+ do { \
+ error_report(__VA_ARGS__); \
+ exit(1); \
+ } while (0);
+
+/* Set to 1 to enable the 'zero' pipe type, useful for debugging */
+#define DEBUG_ZERO_PIPE 1
+
+/* Set to 1 to enable the 'pingpong' pipe type, useful for debugging */
+#define DEBUG_PINGPONG_PIPE 1
+
+/* Set to 1 to enable the 'throttle' pipe type, useful for debugging */
+#define DEBUG_THROTTLE_PIPE 1
+
+/* Maximum length of pipe service name, in characters (excluding final 0) */
+#define MAX_PIPE_SERVICE_NAME_SIZE 255
+
+/* from AOSP version include/hw/android/goldfish/device.h
+ * FIXME?: needs to use proper qemu abstractions
+ */
+static inline void uint64_set_low(uint64_t *addr, uint32 value)
+{
+ *addr = (*addr & ~(0xFFFFFFFFULL)) | value;
+}
+
+static inline void uint64_set_high(uint64_t *addr, uint32 value)
+{
+ *addr = (*addr & 0xFFFFFFFFULL) | ((uint64_t)value << 32);
+}
+
+/* FIXME: this is hardcoded for now but will break if used for
+ * lionhead/goldfish (32 bit) */
+static inline gboolean android_guest_is_64bit(void) {
+ return TRUE;
+}
+
+#define TYPE_ANDROID_PIPE "android_pipe"
+#define ANDROID_PIPE(obj) \
+ OBJECT_CHECK(AndroidPipeState, (obj), TYPE_ANDROID_PIPE)
+
+typedef struct PipeDevice PipeDevice;
+
+typedef struct {
+ SysBusDevice parent;
+ MemoryRegion iomem;
+ qemu_irq irq;
+
+ /* TODO: roll into shared state */
+ PipeDevice *dev;
+} AndroidPipeState;
+
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ ***** 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;
+ PipeDevice *device;
+ uint64_t channel; /* opaque kernel handle */
+ void *opaque;
+ const AndroidPipeFuncs *funcs;
+ const PipeService *service;
+ char* args;
+ unsigned char wanted;
+ char closed;
+} Pipe;
+
+/* Forward */
+static void* pipeConnector_new(Pipe* pipe);
+
+static Pipe*
+pipe_new0(PipeDevice* dev)
+{
+ Pipe* pipe;
+ pipe = g_malloc0(sizeof(Pipe));
+ pipe->device = dev;
+ return pipe;
+}
+
+static Pipe*
+pipe_new(uint64_t channel, PipeDevice* dev)
+{
+ Pipe* pipe = pipe_new0(dev);
+ pipe->channel = channel;
+ pipe->opaque = pipeConnector_new(pipe);
+ return pipe;
+}
+
+static Pipe**
+pipe_list_findp_channel(Pipe **list, uint64_t channel)
+{
+ 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);
+ 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,
+};
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ ***** Z E R O P I P E S
+ *****
+ *****/
+
+/* A simple pipe service that mimics /dev/zero, you can write anything to
+ * it, and you can always read any number of zeros from it. Useful for debugging
+ * the kernel driver.
+ */
+#if DEBUG_ZERO_PIPE
+
+typedef struct {
+ void* hwpipe;
+} ZeroPipe;
+
+static void*
+zeroPipe_init( void* hwpipe, void* svcOpaque, const char* args )
+{
+ ZeroPipe* zpipe;
+
+ D("%s: hwpipe=%p", __FUNCTION__, hwpipe);
+ zpipe = g_malloc0(sizeof(ZeroPipe));
+ zpipe->hwpipe = hwpipe;
+ return zpipe;
+}
+
+static void
+zeroPipe_close( void* opaque )
+{
+ ZeroPipe* zpipe = opaque;
+
+ D("%s: hwpipe=%p", __FUNCTION__, zpipe->hwpipe);
+ g_free(zpipe);
+}
+
+static int
+zeroPipe_sendBuffers( void* opaque, const AndroidPipeBuffer* buffers, int numBuffers )
+{
+ int ret = 0;
+ while (numBuffers > 0) {
+ ret += buffers[0].size;
+ buffers++;
+ numBuffers--;
+ }
+ return ret;
+}
+
+static int
+zeroPipe_recvBuffers( void* opaque, AndroidPipeBuffer* buffers, int numBuffers )
+{
+ int ret = 0;
+ while (numBuffers > 0) {
+ ret += buffers[0].size;
+ memset(buffers[0].data, 0, buffers[0].size);
+ buffers++;
+ numBuffers--;
+ }
+ return ret;
+}
+
+static unsigned
+zeroPipe_poll( void* opaque )
+{
+ return PIPE_POLL_IN | PIPE_POLL_OUT;
+}
+
+static void
+zeroPipe_wakeOn( void* opaque, int flags )
+{
+ /* nothing to do here */
+}
+
+static const AndroidPipeFuncs zeroPipe_funcs = {
+ zeroPipe_init,
+ zeroPipe_close,
+ zeroPipe_sendBuffers,
+ zeroPipe_recvBuffers,
+ zeroPipe_poll,
+ zeroPipe_wakeOn,
+};
+
+#endif /* DEBUG_ZERO */
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ ***** P I N G P O N G P I P E S
+ *****
+ *****/
+
+/* Similar debug service that sends back anything it receives */
+/* All data is kept in a circular dynamic buffer */
+
+#if DEBUG_PINGPONG_PIPE
+
+/* Initial buffer size */
+#define PINGPONG_SIZE 1024
+
+typedef struct {
+ void* hwpipe;
+ uint8_t* buffer;
+ size_t size;
+ size_t pos;
+ size_t count;
+ unsigned flags;
+} PingPongPipe;
+
+static void
+pingPongPipe_init0( PingPongPipe* pipe, void* hwpipe, void* svcOpaque )
+{
+ pipe->hwpipe = hwpipe;
+ pipe->size = PINGPONG_SIZE;
+ pipe->buffer = malloc(pipe->size);
+ pipe->pos = 0;
+ pipe->count = 0;
+}
+
+static void*
+pingPongPipe_init( void* hwpipe, void* svcOpaque, const char* args )
+{
+ PingPongPipe* ppipe;
+
+ D("%s: hwpipe=%p", __FUNCTION__, hwpipe);
+ ppipe = g_malloc0(sizeof(PingPongPipe));
+ pingPongPipe_init0(ppipe, hwpipe, svcOpaque);
+ return ppipe;
+}
+
+static void
+pingPongPipe_close( void* opaque )
+{
+ PingPongPipe* ppipe = opaque;
+
+ D("%s: hwpipe=%p (pos=%zd count=%zd size=%zd)", __FUNCTION__,
+ ppipe->hwpipe, ppipe->pos, ppipe->count, ppipe->size);
+ free(ppipe->buffer);
+ g_free(ppipe);
+}
+
+static int
+pingPongPipe_sendBuffers( void* opaque, const AndroidPipeBuffer* buffers, int numBuffers )
+{
+ PingPongPipe* pipe = opaque;
+ int ret = 0;
+ int count;
+ const AndroidPipeBuffer* buff = buffers;
+ const AndroidPipeBuffer* buffEnd = buff + numBuffers;
+
+ count = 0;
+ for ( ; buff < buffEnd; buff++ )
+ count += buff->size;
+
+ /* Do we need to grow the pingpong buffer? */
+ while (count > pipe->size - pipe->count) {
+ size_t newsize = pipe->size*2;
+ uint8_t* newbuff = realloc(pipe->buffer, newsize);
+ int wpos = pipe->pos + pipe->count;
+ if (newbuff == NULL) {
+ break;
+ }
+ if (wpos > pipe->size) {
+ wpos -= pipe->size;
+ memcpy(newbuff + pipe->size, newbuff, wpos);
+ }
+ pipe->buffer = newbuff;
+ pipe->size = newsize;
+ D("pingpong buffer is now %zd bytes", newsize);
+ }
+
+ for ( buff = buffers; buff < buffEnd; buff++ ) {
+ int avail = pipe->size - pipe->count;
+ if (avail <= 0) {
+ if (ret == 0)
+ ret = PIPE_ERROR_AGAIN;
+ break;
+ }
+ if (avail > buff->size) {
+ avail = buff->size;
+ }
+
+ int wpos = pipe->pos + pipe->count;
+ if (wpos >= pipe->size) {
+ wpos -= pipe->size;
+ }
+ if (wpos + avail <= pipe->size) {
+ memcpy(pipe->buffer + wpos, buff->data, avail);
+ } else {
+ int avail2 = pipe->size - wpos;
+ memcpy(pipe->buffer + wpos, buff->data, avail2);
+ memcpy(pipe->buffer, buff->data + avail2, avail - avail2);
+ }
+ pipe->count += avail;
+ ret += avail;
+ }
+
+ /* Wake up any waiting readers if we wrote something */
+ if (pipe->count > 0 && (pipe->flags & PIPE_WAKE_READ)) {
+ android_pipe_wake(pipe->hwpipe, PIPE_WAKE_READ);
+ }
+
+ return ret;
+}
+
+static int
+pingPongPipe_recvBuffers( void* opaque, AndroidPipeBuffer* buffers, int numBuffers )
+{
+ PingPongPipe* pipe = opaque;
+ int ret = 0;
+
+ while (numBuffers > 0) {
+ int avail = pipe->count;
+ if (avail <= 0) {
+ if (ret == 0)
+ ret = PIPE_ERROR_AGAIN;
+ break;
+ }
+ if (avail > buffers[0].size) {
+ avail = buffers[0].size;
+ }
+
+ int rpos = pipe->pos;
+
+ if (rpos + avail <= pipe->size) {
+ memcpy(buffers[0].data, pipe->buffer + rpos, avail);
+ } else {
+ int avail2 = pipe->size - rpos;
+ memcpy(buffers[0].data, pipe->buffer + rpos, avail2);
+ memcpy(buffers[0].data + avail2, pipe->buffer, avail - avail2);
+ }
+ pipe->count -= avail;
+ pipe->pos += avail;
+ if (pipe->pos >= pipe->size) {
+ pipe->pos -= pipe->size;
+ }
+ ret += avail;
+ numBuffers--;
+ buffers++;
+ }
+
+ /* Wake up any waiting readers if we wrote something */
+ if (pipe->count < PINGPONG_SIZE && (pipe->flags & PIPE_WAKE_WRITE)) {
+ android_pipe_wake(pipe->hwpipe, PIPE_WAKE_WRITE);
+ }
+
+ return ret;
+}
+
+static unsigned
+pingPongPipe_poll( void* opaque )
+{
+ PingPongPipe* pipe = opaque;
+ unsigned ret = 0;
+
+ if (pipe->count < pipe->size)
+ ret |= PIPE_POLL_OUT;
+
+ if (pipe->count > 0)
+ ret |= PIPE_POLL_IN;
+
+ return ret;
+}
+
+static void
+pingPongPipe_wakeOn( void* opaque, int flags )
+{
+ PingPongPipe* pipe = opaque;
+ pipe->flags |= (unsigned)flags;
+}
+
+static const AndroidPipeFuncs pingPongPipe_funcs = {
+ pingPongPipe_init,
+ pingPongPipe_close,
+ pingPongPipe_sendBuffers,
+ pingPongPipe_recvBuffers,
+ pingPongPipe_poll,
+ pingPongPipe_wakeOn,
+};
+
+#endif /* DEBUG_PINGPONG_PIPE */
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ ***** T H R O T T L E P I P E S
+ *****
+ *****/
+
+/* Similar to PingPongPipe, but will throttle the bandwidth to test
+ * blocking I/O.
+ */
+
+#ifdef DEBUG_THROTTLE_PIPE
+
+typedef struct {
+ PingPongPipe pingpong;
+ double sendRate;
+ int64_t sendExpiration;
+ double recvRate;
+ int64_t recvExpiration;
+ QEMUTimer* timer;
+} ThrottlePipe;
+
+/* forward declaration */
+static void throttlePipe_timerFunc( void* opaque );
+
+static void*
+throttlePipe_init( void* hwpipe, void* svcOpaque, const char* args )
+{
+ ThrottlePipe* pipe;
+
+ pipe = g_malloc0(sizeof(ThrottlePipe));
+ pingPongPipe_init0(&pipe->pingpong, hwpipe, svcOpaque);
+ pipe->timer = timer_new(QEMU_CLOCK_VIRTUAL, SCALE_NS, throttlePipe_timerFunc, pipe);
+ /* For now, limit to 500 KB/s in both directions */
+ pipe->sendRate = 1e9 / (500*1024*8);
+ pipe->recvRate = pipe->sendRate;
+ return pipe;
+}
+
+static void
+throttlePipe_close( void* opaque )
+{
+ ThrottlePipe* pipe = opaque;
+
+ timer_del(pipe->timer);
+ timer_free(pipe->timer);
+ pingPongPipe_close(&pipe->pingpong);
+}
+
+static void
+throttlePipe_rearm( ThrottlePipe* pipe )
+{
+ int64_t minExpiration = 0;
+
+ DD("%s: sendExpiration=%" PRId64 " recvExpiration=%" PRId64"\n", __FUNCTION__, pipe->sendExpiration, pipe->recvExpiration);
+
+ if (pipe->sendExpiration) {
+ if (minExpiration == 0 || pipe->sendExpiration < minExpiration)
+ minExpiration = pipe->sendExpiration;
+ }
+
+ if (pipe->recvExpiration) {
+ if (minExpiration == 0 || pipe->recvExpiration < minExpiration)
+ minExpiration = pipe->recvExpiration;
+ }
+
+ if (minExpiration != 0) {
+ DD("%s: Arming for %" PRId64 "\n", __FUNCTION__, minExpiration);
+ timer_mod(pipe->timer, minExpiration);
+ }
+}
+
+static void
+throttlePipe_timerFunc( void* opaque )
+{
+ ThrottlePipe* pipe = opaque;
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ DD("%s: TICK! now=%" PRId64 " sendExpiration=%" PRId64 " recvExpiration=%" PRId64 "\n",
+ __FUNCTION__, now, pipe->sendExpiration, pipe->recvExpiration);
+
+ /* Timer has expired, signal wake up if needed */
+ int flags = 0;
+
+ if (pipe->sendExpiration && now > pipe->sendExpiration) {
+ flags |= PIPE_WAKE_WRITE;
+ pipe->sendExpiration = 0;
+ }
+ if (pipe->recvExpiration && now > pipe->recvExpiration) {
+ flags |= PIPE_WAKE_READ;
+ pipe->recvExpiration = 0;
+ }
+ flags &= pipe->pingpong.flags;
+ if (flags != 0) {
+ DD("%s: WAKE %d\n", __FUNCTION__, flags);
+ android_pipe_wake(pipe->pingpong.hwpipe, flags);
+ }
+
+ throttlePipe_rearm(pipe);
+}
+
+static int
+throttlePipe_sendBuffers( void* opaque, const AndroidPipeBuffer* buffers, int numBuffers )
+{
+ ThrottlePipe* pipe = opaque;
+ int ret;
+
+ if (pipe->sendExpiration > 0) {
+ return PIPE_ERROR_AGAIN;
+ }
+
+ ret = pingPongPipe_sendBuffers(&pipe->pingpong, buffers, numBuffers);
+ if (ret > 0) {
+ /* Compute next send expiration time */
+ pipe->sendExpiration = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ret*pipe->sendRate;
+ throttlePipe_rearm(pipe);
+ }
+ return ret;
+}
+
+static int
+throttlePipe_recvBuffers( void* opaque, AndroidPipeBuffer* buffers, int numBuffers )
+{
+ ThrottlePipe* pipe = opaque;
+ int ret;
+
+ if (pipe->recvExpiration > 0) {
+ return PIPE_ERROR_AGAIN;
+ }
+
+ ret = pingPongPipe_recvBuffers(&pipe->pingpong, buffers, numBuffers);
+ if (ret > 0) {
+ pipe->recvExpiration = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ret*pipe->recvRate;
+ throttlePipe_rearm(pipe);
+ }
+ return ret;
+}
+
+static unsigned
+throttlePipe_poll( void* opaque )
+{
+ ThrottlePipe* pipe = opaque;
+ unsigned ret = pingPongPipe_poll(&pipe->pingpong);
+
+ if (pipe->sendExpiration > 0)
+ ret &= ~PIPE_POLL_OUT;
+
+ if (pipe->recvExpiration > 0)
+ ret &= ~PIPE_POLL_IN;
+
+ return ret;
+}
+
+static void
+throttlePipe_wakeOn( void* opaque, int flags )
+{
+ ThrottlePipe* pipe = opaque;
+ pingPongPipe_wakeOn(&pipe->pingpong, flags);
+}
+
+static const AndroidPipeFuncs throttlePipe_funcs = {
+ throttlePipe_init,
+ throttlePipe_close,
+ throttlePipe_sendBuffers,
+ throttlePipe_recvBuffers,
+ throttlePipe_poll,
+ throttlePipe_wakeOn,
+};
+
+#endif /* DEBUG_THROTTLE_PIPE */
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ ***** 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 */
+
+ /* the list of all pipes */
+ Pipe* pipes;
+
+ /* the list of signalled pipes */
+ Pipe* signaled_pipes;
+
+ /* i/o registers */
+ uint64_t address;
+ uint32_t size;
+ uint32_t status;
+ uint64_t channel;
+ uint32_t wakes;
+ uint64_t params_addr;
+};
+
+/* Map the guest buffer specified by the guest vaddr 'address'.
+ * 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).
+ * 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)
+{
+ 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 */
+ return NULL;
+ }
+ if (l != size) {
+ /* This will only happen if the address pointed at non-RAM,
+ * or if the size means the buffer end is beyond the end of
+ * the RAM block.
+ */
+ cpu_physical_memory_unmap(ptr, l, 0, 0);
+ return NULL;
+ }
+
+ return ptr;
+}
+
+static void
+pipeDevice_doCommand( PipeDevice* dev, uint32_t command )
+{
+ Pipe** lookup = pipe_list_findp_channel(&dev->pipes, dev->channel);
+ Pipe* pipe = *lookup;
+
+ /* Check that we're referring a known pipe channel */
+ if (command != PIPE_CMD_OPEN && pipe == NULL) {
+ dev->status = PIPE_ERROR_INVAL;
+ return;
+ }
+
+ /* If the pipe is closed by the host, return an error */
+ if (pipe != NULL && pipe->closed && command != PIPE_CMD_CLOSE) {
+ dev->status = PIPE_ERROR_IO;
+ return;
+ }
+
+ switch (command) {
+ case PIPE_CMD_OPEN:
+ DD("%s: CMD_OPEN channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);
+ if (pipe != NULL) {
+ dev->status = PIPE_ERROR_INVAL;
+ break;
+ }
+ pipe = pipe_new(dev->channel, dev);
+ pipe->next = dev->pipes;
+ dev->pipes = pipe;
+ dev->status = 0;
+ break;
+
+ 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;
+ pipe->next = NULL;
+ pipe_list_remove_waked(&dev->signaled_pipes, pipe);
+ pipe_free(pipe);
+ break;
+
+ case PIPE_CMD_POLL:
+ dev->status = pipe->funcs->poll(pipe->opaque);
+ DD("%s: CMD_POLL > status=%d", __FUNCTION__, dev->status);
+ break;
+
+ case PIPE_CMD_READ_BUFFER: {
+ /* Translate virtual address into physical one, into emulator memory. */
+ AndroidPipeBuffer buffer;
+ buffer.data = map_guest_buffer(dev->address, dev->size, 1);
+ if (!buffer.data) {
+ dev->status = PIPE_ERROR_INVAL;
+ break;
+ }
+ buffer.size = dev->size;
+ dev->status = pipe->funcs->recvBuffers(pipe->opaque, &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);
+ cpu_physical_memory_unmap(buffer.data, dev->size, 1, dev->size);
+ break;
+ }
+
+ case PIPE_CMD_WRITE_BUFFER: {
+ /* Translate virtual address into physical one, into emulator memory. */
+ AndroidPipeBuffer buffer;
+ buffer.data = map_guest_buffer(dev->address, dev->size, 0);
+ if (!buffer.data) {
+ dev->status = PIPE_ERROR_INVAL;
+ break;
+ }
+ buffer.size = dev->size;
+ dev->status = pipe->funcs->sendBuffers(pipe->opaque, &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);
+ cpu_physical_memory_unmap(buffer.data, dev->size, 0, dev->size);
+ break;
+ }
+
+ case PIPE_CMD_WAKE_ON_READ:
+ 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);
+ }
+ dev->status = 0;
+ break;
+
+ case PIPE_CMD_WAKE_ON_WRITE:
+ 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);
+ }
+ dev->status = 0;
+ break;
+
+ default:
+ D("%s: command=%d (0x%x)\n", __FUNCTION__, command, command);
+ }
+}
+
+static void pipe_dev_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
+{
+ AndroidPipeState *state = (AndroidPipeState *) opaque;
+ PipeDevice *s = state->dev;
+
+ DR("%s: offset = 0x%" HWADDR_PRIx " value=%" PRIu64 "/0x%" PRIx64,
+ __func__, offset, value, value);
+ switch (offset) {
+ case PIPE_REG_COMMAND:
+ pipeDevice_doCommand(s, value);
+ break;
+
+ case PIPE_REG_SIZE:
+ s->size = value;
+ break;
+
+ case PIPE_REG_ADDRESS:
+ uint64_set_low(&s->address, value);
+ break;
+
+ case PIPE_REG_ADDRESS_HIGH:
+ uint64_set_high(&s->address, value);
+ break;
+
+ case PIPE_REG_CHANNEL:
+ uint64_set_low(&s->channel, value);
+ break;
+
+ case PIPE_REG_CHANNEL_HIGH:
+ uint64_set_high(&s->channel, value);
+ break;
+
+ case PIPE_REG_PARAMS_ADDR_HIGH:
+ uint64_set_high(&s->params_addr, value);
+ break;
+
+ case PIPE_REG_PARAMS_ADDR_LOW:
+ uint64_set_low(&s->params_addr, value);
+ break;
+
+ case PIPE_REG_ACCESS_PARAMS:
+ {
+ struct access_params aps;
+ struct access_params_64 aps64;
+ uint32_t cmd;
+
+ /* Don't touch aps.result if anything wrong */
+ if (s->params_addr == 0)
+ break;
+
+ if (android_guest_is_64bit()) {
+ cpu_physical_memory_read(s->params_addr, (void*)&aps64,
+ sizeof(aps64));
+ s->channel = aps64.channel;
+ s->size = aps64.size;
+ s->address = aps64.address;
+ cmd = aps64.cmd;
+ } else {
+ cpu_physical_memory_read(s->params_addr, (void*)&aps,
+ sizeof(aps));
+ s->channel = aps.channel;
+ s->size = aps.size;
+ s->address = aps.address;
+ cmd = aps.cmd;
+ }
+
+ if ((cmd != PIPE_CMD_READ_BUFFER) && (cmd != PIPE_CMD_WRITE_BUFFER))
+ break;
+
+ pipeDevice_doCommand(s, cmd);
+
+ if (android_guest_is_64bit()) {
+ aps64.result = s->status;
+ cpu_physical_memory_write(s->params_addr, (void*)&aps64,
+ sizeof(aps64));
+ } else {
+ aps.result = s->status;
+ cpu_physical_memory_write(s->params_addr, (void*)&aps,
+ sizeof(aps));
+ }
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown register offset = 0x%"
+ HWADDR_PRIx " value=%" PRIu64 "/0x%" PRIx64 "\n",
+ __func__, offset, value, value);
+ break;
+ }
+}
+
+/* I/O read */
+static uint64_t pipe_dev_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AndroidPipeState *s = (AndroidPipeState *)opaque;
+ PipeDevice *dev = s->dev;
+
+ switch (offset) {
+ case PIPE_REG_STATUS:
+ 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;
+ 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);
+ }
+ DR("%s: no signaled channels", __FUNCTION__);
+ return 0;
+
+ case PIPE_REG_CHANNEL_HIGH:
+ if (dev->signaled_pipes != NULL) {
+ Pipe* pipe = dev->signaled_pipes;
+ DR("%s: channel_high=0x%llx wanted=%d", __FUNCTION__,
+ (unsigned long long)pipe->channel, pipe->wanted);
+ return (uint32_t)(pipe->channel >> 32);
+ }
+ DR("%s: no signaled channels", __FUNCTION__);
+ return 0;
+
+ case PIPE_REG_WAKES:
+ DR("%s: wakes %d", __FUNCTION__, dev->wakes);
+ return dev->wakes;
+
+ case PIPE_REG_PARAMS_ADDR_HIGH:
+ return (uint32_t)(dev->params_addr >> 32);
+
+ case PIPE_REG_PARAMS_ADDR_LOW:
+ return (uint32_t)(dev->params_addr & 0xFFFFFFFFUL);
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown register %" HWADDR_PRId
+ " (0x%" HWADDR_PRIx ")\n", __FUNCTION__, offset, offset);
+ }
+ return 0;
+}
+
+static const MemoryRegionOps android_pipe_iomem_ops = {
+ .read = pipe_dev_read,
+ .write = pipe_dev_write,
+ .endianness = DEVICE_NATIVE_ENDIAN
+};
+
+static void android_pipe_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
+ AndroidPipeState *s = ANDROID_PIPE(dev);
+
+ s->dev = (PipeDevice *) g_malloc0(sizeof(PipeDevice));
+ s->dev->ps = s; /* HACK: backlink */
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &android_pipe_iomem_ops, s,
+ "android_pipe", 0x1000 /*TODO: ?how big?*/);
+ sysbus_init_mmio(sbdev, &s->iomem);
+ sysbus_init_irq(sbdev, &s->irq);
+
+#if DEBUG_ZERO_PIPE
+ android_pipe_add_type("zero", NULL, &zeroPipe_funcs);
+#endif
+#if DEBUG_PINGPONG_PIPE
+ android_pipe_add_type("pingpong", NULL, &pingPongPipe_funcs);
+#endif
+#if DEBUG_THROTTLE_PIPE
+ android_pipe_add_type("throttle", NULL, &throttlePipe_funcs);
+#endif
+}
+
+void
+android_pipe_wake( void* hwpipe, unsigned flags )
+{
+ Pipe* pipe = hwpipe;
+ Pipe** lookup;
+ 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;
+
+ /* 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 )
+{
+ Pipe* 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 );
+ }
+}
+
+static void android_pipe_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = android_pipe_realize;
+ dc->desc = "android pipe";
+}
+
+static const TypeInfo android_pipe_info = {
+ .name = TYPE_ANDROID_PIPE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AndroidPipeState),
+ .class_init = android_pipe_class_init
+};
+
+static void android_pipe_register(void)
+{
+ type_register_static(&android_pipe_info);
+}
+
+type_init(android_pipe_register);
diff --git a/include/hw/misc/android_pipe.h b/include/hw/misc/android_pipe.h
new file mode 100644
index 0000000000..3fa7e18693
--- /dev/null
+++ b/include/hw/misc/android_pipe.h
@@ -0,0 +1,220 @@
+/* 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"
+
+/* TECHNICAL NOTE:
+ *
+ * A android pipe is a very fast communication channel between the guest
+ * system and the emulator program.
+ *
+ * To open a new pipe to the emulator, a guest client will do the following:
+ *
+ * fd = open("/dev/qemu_pipe", O_RDWR);
+ * char invite[64];
+ * snprintf(invite, sizeof invite, "%s", pipeName);
+ * ret = write(fd, invite, strlen(invite));
+ *
+ * if (ret < 0) {
+ * // something bad happened, see errno
+ * }
+ *
+ * now read()/write() to communicate with <pipeName> service in the
+ * emulator.
+ *
+ * This header provides the interface used by pipe services in the emulator
+ * to receive new client connection and deal with them.
+ *
+ *
+ * 1/ Call android_pipe_add_type() to register a new pipe service by name.
+ * This must provide a pointer to a series of functions that will be called
+ * during normal pipe operations.
+ *
+ * 2/ When a client connects to the service, the 'init' callback will be called
+ * to create a new service-specific client identifier (which must returned
+ * by the function).
+ *
+ * 3/ Call android_pipe_close() to force the closure of a given pipe.
+ *
+ * 4/ Call android_pipe_signal() to signal a change of state to the pipe.
+ *
+ */
+
+/* Buffer descriptor for sendBuffers() and recvBuffers() callbacks */
+typedef struct AndroidPipeBuffer {
+ uint8_t* data;
+ size_t size;
+} AndroidPipeBuffer;
+
+/* Pipe handler funcs */
+typedef struct {
+ /* Create new client connection, 'hwpipe' must be passed to other
+ * android_pipe_xxx functions, while the returned value will be passed
+ * to other callbacks (e.g. close). 'pipeOpaque' is the value passed
+ * to android_pipe_add_type() when registering a given pipe service.
+ */
+ void* (*init)( void* hwpipe, void* pipeOpaque, const char* args );
+
+ /* Called when the guest kernel has finally closed a pipe connection.
+ * This is the only place where you can release/free the client connection.
+ * You should never invoke this callback directly. Call android_pipe_close()
+ * instead.
+ */
+ void (*close)( void* pipe );
+
+ /* Called when the guest is write()-ing to the pipe. Should return the
+ * number of bytes transfered, 0 for EOF status, or a negative error
+ * value otherwise, including PIPE_ERROR_AGAIN to indicate that the
+ * emulator is not ready to receive data yet.
+ */
+ int (*sendBuffers)( void* pipe, const AndroidPipeBuffer* buffers, int numBuffers );
+
+ /* Same as sendBuffers when the guest is read()-ing from the pipe. */
+ int (*recvBuffers)( void* pipe, AndroidPipeBuffer* buffers, int numBuffers );
+
+ /* Called when guest wants to poll the read/write status for the pipe.
+ * Should return a combination of PIPE_POLL_XXX flags.
+ */
+ unsigned (*poll)( void* pipe );
+
+ /* Called to signal that the guest wants to be woken when the set of
+ * PIPE_WAKE_XXX bit-flags in 'flags' occur. When the condition occurs,
+ * then the pipe implementation shall call android_pipe_wake().
+ */
+ void (*wakeOn)( void* opaque, int flags );
+
+ /* Called to save the pipe's state to a QEMUFile, i.e. when saving
+ * snapshots. This can be NULL to indicate that no state can be saved.
+ * In this case, when the pipe is loaded, the emulator will automatically
+ * force-close so the next operation the guest performs on it will return
+ * a PIPE_ERROR_IO error code.
+ */
+ void (*save)( void* pipe, QEMUFile* file );
+
+ /* Called to load the sate of a pipe from a QEMUFile. This will always
+ * correspond to the state of the pipe as saved by a previous call to
+ * the 'save' method. Can be NULL to indicate that the pipe state cannot
+ * be loaded. In this case, the emulator will automatically force-close
+ * it.
+ *
+ * In case of success, this returns 0, and the new pipe object is returned
+ * in '*ppipe'. In case of errno code is returned to indicate a failure.
+ * 'hwpipe' and 'pipeOpaque' are the same arguments than those passed
+ * to 'init'.
+ */
+ void* (*load)( void* hwpipe, void* pipeOpaque, const char* args, QEMUFile* file);
+} AndroidPipeFuncs;
+
+/* Register a new pipe handler type. 'pipeOpaque' is passed directly
+ * to 'init() when a new pipe is connected to.
+ */
+extern void android_pipe_add_type(const char* pipeName,
+ void* pipeOpaque,
+ const AndroidPipeFuncs* pipeFuncs );
+
+/* This tells the guest system that we want to close the pipe and that
+ * further attempts to read or write to it will fail. This will not
+ * necessarily call the 'close' callback immediately though.
+ *
+ * This will also wake-up any blocked guest threads waiting for i/o.
+ */
+extern void android_pipe_close( void* hwpipe );
+
+/* Signal that the pipe can be woken up. 'flags' must be a combination of
+ * PIPE_WAKE_READ and PIPE_WAKE_WRITE.
+ */
+extern void android_pipe_wake( void* hwpipe, unsigned flags );
+
+/* The following definitions must match those in the kernel driver:
+ *
+ * For goldfish:
+ * android.googlesource.com/kernel/goldfish.git.
+ * $KERNEL/drivers/misc/qemupipe/qemu_pipe.c
+ *
+ * For ranchu:
+ * android.googlesource.com/kernel/common.git
+ * $KERNEL/drivers/staging/android/android_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_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 */
+
+/* List of bitflags returned in status of CMD_POLL command */
+#define PIPE_POLL_IN (1 << 0)
+#define PIPE_POLL_OUT (1 << 1)
+#define PIPE_POLL_HUP (1 << 2)
+
+/* 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 */
+
+/* Possible status values used to signal errors - see qemu_pipe_error_convert */
+#define PIPE_ERROR_INVAL -1
+#define PIPE_ERROR_AGAIN -2
+#define PIPE_ERROR_NOMEM -3
+#define PIPE_ERROR_IO -4
+
+/* Bit-flags used to signal events from the emulator */
+#define PIPE_WAKE_CLOSED (1 << 0) /* emulator closed pipe */
+#define PIPE_WAKE_READ (1 << 1) /* pipe can now be read from */
+#define PIPE_WAKE_WRITE (1 << 2) /* pipe can now be written to */
+
+struct access_params{
+ uint32_t channel;
+ uint32_t size;
+ uint32_t address;
+ uint32_t cmd;
+ uint32_t result;
+ /* reserved for future extension */
+ uint32_t flags;
+};
+
+struct access_params_64 {
+ uint64_t channel;
+ uint32_t size;
+ uint64_t address;
+ uint32_t cmd;
+ uint32_t result;
+ /* reserved for future extension */
+ uint32_t flags;
+};
+
+#endif /* _HW_ANDROID_PIPE_H */