diff options
author | Alex Bennée <alex.bennee@linaro.org> | 2014-06-12 16:42:50 +0100 |
---|---|---|
committer | Greg Bellows <greg.bellows@linaro.org> | 2015-01-15 16:18:26 -0600 |
commit | 4644a38b05a88224c1b7467eb7daf12146c772d8 (patch) | |
tree | 9356ec37ff700fbeab5f6fe19d17c8747c1824d3 | |
parent | 4a428526ebc75b3fcc63efd2027e673d9abad181 (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.mak | 2 | ||||
-rw-r--r-- | hw/arm/ranchu.c | 5 | ||||
-rw-r--r-- | hw/misc/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/misc/android_pipe.c | 1315 | ||||
-rw-r--r-- | include/hw/misc/android_pipe.h | 220 |
5 files changed, 1544 insertions, 0 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index b1dc85fb1..f10cc6977 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 ad70ce6b6..8f8478d5b 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 */ }; @@ -487,6 +490,8 @@ static void ranchu_init(MachineState *machine) #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 2477d4cb9..b7c695d1e 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 000000000..90bce9526 --- /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 000000000..3fa7e1869 --- /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 */ |