diff options
author | David 'Digit' Turner <digit@android.com> | 2011-03-21 17:51:03 +0100 |
---|---|---|
committer | David 'Digit' Turner <digit@android.com> | 2011-04-11 18:19:02 +0200 |
commit | 89217f57a256796b67b4d3f319e0f18f6225666a (patch) | |
tree | 92d21483086414b53778cf58942b2040dfb45ada /hw | |
parent | bb1f432421288dae208c7189356643023ccbffbc (diff) | |
download | external_qemu-89217f57a256796b67b4d3f319e0f18f6225666a.zip external_qemu-89217f57a256796b67b4d3f319e0f18f6225666a.tar.gz external_qemu-89217f57a256796b67b4d3f319e0f18f6225666a.tar.bz2 |
hw/goldfish_pipe.c: New support code for QEMUD fast-pipes.
Change-Id: I338334d53fa9bc52c87e9da18341d0cb94fd4269
Diffstat (limited to 'hw')
-rw-r--r-- | hw/goldfish_pipe.c | 740 | ||||
-rw-r--r-- | hw/goldfish_pipe.h | 98 |
2 files changed, 838 insertions, 0 deletions
diff --git a/hw/goldfish_pipe.c b/hw/goldfish_pipe.c new file mode 100644 index 0000000..6c24208 --- /dev/null +++ b/hw/goldfish_pipe.c @@ -0,0 +1,740 @@ +/* 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. +*/ +#include "android/utils/intmap.h" +#include "android/utils/panic.h" +#include "android/utils/reflist.h" +#include "android/utils/system.h" +#include "android/hw-qemud.h" +#include "hw/goldfish_pipe.h" + +#define DEBUG 0 + +#if DEBUG >= 1 +# define D(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n") +#else +# define D(...) (void)0 +#endif + +#if DEBUG >= 2 +# define DD(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n") +#else +# define DD(...) (void)0 +#endif + +#define E(...) fprintf(stderr, "ERROR:" __VA_ARGS__), fprintf(stderr, "\n") + +/* Must match hw/goldfish_trace.h */ +#define SLOT_COMMAND 0 +#define SLOT_STATUS 0 +#define SLOT_ADDRESS 1 +#define SLOT_SIZE 2 +#define SLOT_CHANNEL 3 + +#define QEMUD_PIPE_CMD_CLOSE 1 +#define QEMUD_PIPE_CMD_SEND 2 +#define QEMUD_PIPE_CMD_RECV 3 +#define QEMUD_PIPE_CMD_WAKE_ON_SEND 4 +#define QEMUD_PIPE_CMD_WAKE_ON_RECV 5 + +#define QEMUD_PIPE_ERROR_INVAL 22 /* EINVAL */ +#define QEMUD_PIPE_ERROR_AGAIN 11 /* EAGAIN */ +#define QEMUD_PIPE_ERROR_CONNRESET 104 /* ECONNRESET */ +#define QEMUD_PIPE_ERROR_NOMEM 12 /* ENOMEM */ + +/********************************************************************** + ********************************************************************** + ** + ** TECHNICAL NOTE ON THE FOLLOWING IMPLEMENTATION: + ** + ** PipeService :: + ** The global state for the QEMUD fast pipes service. + ** Holds a tid -> ThreadState map. Registers as a Qemud + ** service named "fast-pipes" which creates PipeClient + ** objects on connection. + ** + ** PipeClient :: + ** The state of each QEMUD pipe. This handles the initial + ** connection, message exchanges and signalling. + ** + ** ThreadState :: + ** Hold the thread-specific state corresponding to each guest + ** thread that has created a least one qemud pipe. Stores the + ** state of our 4 I/O Registers, and a map of localId -> PipeState + ** + ** + ** The following graphics is an example corresponding to the following + ** situation: + ** + ** - two guest threads have opened pipe connections + ** - the first thread has opened two different pipes + ** - the second thread has opened only one pipe + ** + ** + ** QEMUD-SERVICE + ** | | + ** | |________________________________________ + ** | | | | + ** | v v v + ** | QEMUD-CLIENT#1 QEMUD-CLIENT#2 QEMUD-CLIENT#3 + ** | ^ ^ ^ + ** | | | | + ** | | | | + ** | v v v + ** | PIPE-CLIENT#1 PIPE-CLIENT#2 PIPE-CLIENT#3 + ** | ^ ^ ^ + ** | | | | + ** | |________________| | + ** | | + + ** | | | + ** | THREAD-STATE#1 THREAD-STATE#2 + ** | ^ ^ + ** | ____|___________________________________| + ** | | + ** | | + ** PIPE-SERVICE + ** + ** Note that the QemudService and QemudClient objects are created by + ** hw-qemud.c and not defined here. + ** + **/ + +typedef struct PipeService PipeService; +typedef struct PipeClient PipeClient; + +static void pipeService_removeState(PipeService* pipeSvc, int tid); + +/********************************************************************** + ********************************************************************** + ***** + ***** REGISTRY OF SUPPORTED PIPE SERVICES + ***** + *****/ + +typedef struct { + const char* pipeName; + void* pipeOpaque; + const QemudPipeHandlerFuncs* pipeFuncs; +} PipeType; + +#define MAX_PIPE_TYPES 4 + +static PipeType sPipeTypes[MAX_PIPE_TYPES]; +static int sPipeTypeCount; + +void +goldfish_pipe_add_type( const char* pipeName, + void* pipeOpaque, + const QemudPipeHandlerFuncs* pipeFuncs ) +{ + int count = sPipeTypeCount; + + if (count >= MAX_PIPE_TYPES) { + APANIC("%s: Too many qemud pipe types!", __FUNCTION__); + } + + sPipeTypes[count].pipeName = pipeName; + sPipeTypes[count].pipeOpaque = pipeOpaque; + sPipeTypes[count].pipeFuncs = pipeFuncs; + sPipeTypeCount = ++count; +} + +static const PipeType* +goldfish_pipe_find_type( const char* pipeName ) +{ + const PipeType* ptype = sPipeTypes; + const PipeType* limit = ptype + sPipeTypeCount; + + for ( ; ptype < limit; ptype++ ) { + if (!strcmp(pipeName, ptype->pipeName)) { + return ptype; + } + } + return NULL; +} + + +/********************************************************************** + ********************************************************************** + ***** + ***** THREAD-SPECIFIC STATE + ***** + *****/ + +static void +pipeClient_closeFromThread( PipeClient* pcl ); + +static uint32_t +pipeClient_doCommand( PipeClient* pcl, + uint32_t command, + uint32_t address, + uint32_t* pSize ); + + +/* For each guest thread, we will store the following state: + * + * - The current state of the 'address', 'size', 'localId' and 'status' + * I/O slots provided through the magic page by hw/goldfish_trace.c + * + * - A list of PipeClient objects, corresponding to all the pipes in + * this thread, identified by localId. + */ +typedef struct { + uint32_t address; + uint32_t size; + uint32_t localId; + uint32_t status; + AIntMap* pipes; +} ThreadState; + +static void +threadState_free( ThreadState* ts ) +{ + /* Get rid of the localId -> PipeClient map */ + AINTMAP_FOREACH_VALUE(ts->pipes, pcl, pipeClient_closeFromThread(pcl)); + aintMap_free(ts->pipes); + + AFREE(ts); +} + +static ThreadState* +threadState_new( void ) +{ + ThreadState* ts; + ANEW0(ts); + ts->pipes = aintMap_new(); + return ts; +} + +static int +threadState_addPipe( ThreadState* ts, int localId, PipeClient* pcl ) +{ + /* We shouldn't already have a pipe for this localId */ + if (aintMap_get(ts->pipes, localId) != NULL) { + errno = EBADF; + return -1; + } + aintMap_set(ts->pipes, localId, pcl); + return 0; +} + +static void +threadState_delPipe( ThreadState* ts, int localId ) +{ + aintMap_del(ts->pipes, localId); +} + +static void +threadState_write( ThreadState* ts, int offset, uint32_t value ) +{ + PipeClient* pcl; + + switch (offset) { + case SLOT_COMMAND: + pcl = aintMap_get(ts->pipes, (int)ts->localId); + if (pcl == NULL) { + D("%s: Invalid localId (%d)", + __FUNCTION__, ts->localId); + ts->status = QEMUD_PIPE_ERROR_INVAL; + } else { + ts->status = pipeClient_doCommand(pcl, + value, + ts->address, + &ts->size); + } + break; + case SLOT_ADDRESS: + ts->address = value; + break; + case SLOT_SIZE: + ts->size = value; + break; + case SLOT_CHANNEL: + ts->localId = value; + break; + default: + /* XXX: PRINT ERROR? */ + ; + } +} + +static uint32_t +threadState_read( ThreadState* ts, int offset ) +{ + switch (offset) { + case SLOT_STATUS: return ts->status; + case SLOT_ADDRESS: return ts->address; + case SLOT_SIZE: return ts->size; + case SLOT_CHANNEL: return ts->localId; + default: return 0; + } +} + +/********************************************************************** + ********************************************************************** + ***** + ***** PIPE CLIENT STATE + ***** + *****/ + +/* Each client object points to a PipeState after it has received the + * initial request from the guest, which shall look like: + * <tid>:<localId>:<name> + */ +struct PipeClient { + char* pipeName; + QemudClient* client; + PipeService* pipeSvc; + + int tid; + uint32_t localId; + void* handler; + const QemudPipeHandlerFuncs* handlerFuncs; +}; + +static int pipeService_addPipe(PipeService* pipeSvc, int tid, int localId, PipeClient* pcl); +static void pipeService_removePipe(PipeService* pipeSvc, int tid, int localId); + +static void +pipeClient_closeFromThread( PipeClient* pcl ) +{ + qemud_client_close(pcl->client); +} + +/* This function should only be invoked through qemud_client_close(). + * Never call it explicitely. */ +static void +pipeClient_close( void* opaque ) +{ + PipeClient* pcl = opaque; + + if (pcl->handler && pcl->handlerFuncs && pcl->handlerFuncs->close) { + pcl->handlerFuncs->close(pcl->handler); + } + + D("qemud:pipe: closing client (%d,%x)", pcl->tid, pcl->localId); + qemud_client_close(pcl->client); + pcl->client = NULL; + + /* The guest is closing the connection, so remove it from our state */ + pipeService_removePipe(pcl->pipeSvc, pcl->tid, pcl->localId); + + AFREE(pcl->pipeName); +} + +static void +pipeClient_recv( void* opaque, uint8_t* msg, int msgLen, QemudClient* client ) +{ + PipeClient* pcl = opaque; + const PipeType* ptype; + + const char* p; + int failure = 1; + char answer[64]; + + if (pcl->pipeName != NULL) { + /* Should never happen, the only time we'll receive something + * is when the connection is created! Simply ignore any incoming + * message. + */ + return; + } + + /* The message format is <tid>:<localIdHex>:<name> */ + if (sscanf((char*)msg, "%d:%x:", &pcl->tid, &pcl->localId) != 2) { + goto BAD_FORMAT; + } + p = strchr((const char*)msg, ':'); + if (p != NULL) { + p = strchr(p+1, ':'); + if (p != NULL) + p += 1; + } + if (p == NULL || *p == '\0') { + goto BAD_FORMAT; + } + + pcl->pipeName = ASTRDUP(p); + + ptype = goldfish_pipe_find_type(pcl->pipeName); + if (ptype == NULL) { + goto UNKNOWN_PIPE_TYPE; + } + + pcl->handlerFuncs = ptype->pipeFuncs; + pcl->handler = ptype->pipeFuncs->init( client, ptype->pipeOpaque ); + if (pcl->handler == NULL) { + goto BAD_INIT; + } + + if (pipeService_addPipe(pcl->pipeSvc, pcl->tid, pcl->localId, pcl) < 0) { + goto DUPLICATE_REQUEST; + } + + D("qemud:pipe: Added new client: %s", msg); + failure = 0; + snprintf(answer, sizeof answer, "OK"); + goto SEND_ANSWER; + +BAD_INIT: + /* Initialization failed for some reason! */ + E("qemud:pipe: Could not initialize pipe: '%s'", pcl->pipeName); + snprintf(answer, sizeof answer, "KO:%d:Could not initialize pipe", + QEMUD_PIPE_ERROR_INVAL); + goto SEND_ANSWER; + +UNKNOWN_PIPE_TYPE: + E("qemud:pipe: Unknown pipe type: '%s'", p); + snprintf(answer, sizeof answer, "KO:%d:Unknown pipe type name", + QEMUD_PIPE_ERROR_INVAL); + goto SEND_ANSWER; + +BAD_FORMAT: + E("qemud:pipe: Invalid connection request: '%s'", msg); + snprintf(answer, sizeof answer, "KO:%d:Invalid connection request", + QEMUD_PIPE_ERROR_INVAL); + goto SEND_ANSWER; + +DUPLICATE_REQUEST: + E("qemud:pipe: Duplicate connection request: '%s'", msg); + snprintf(answer, sizeof answer, "KO:%d:Duplicate connection request", + QEMUD_PIPE_ERROR_INVAL); + goto SEND_ANSWER; + +SEND_ANSWER: + qemud_client_send(client, (uint8_t*)answer, strlen(answer)); + if (failure) { + qemud_client_close(client); + } else { + /* Disable framing for the rest of signalling */ + qemud_client_set_framing(client, 0); + } +} + +/* Count the number of GoldfishPipeBuffers we will need to transfer + * memory from/to [address...address+size) + */ +static int +_countPipeBuffers( uint32_t address, uint32_t size ) +{ + CPUState* env = cpu_single_env; + int count = 0; + + while (size > 0) { + uint32_t vstart = address & TARGET_PAGE_MASK; + uint32_t vend = vstart + TARGET_PAGE_SIZE; + uint32_t next = address + size; + + DD("%s: trying to map (0x%x - 0x%0x, %d bytes) -> page=0x%x - 0x%x", + __FUNCTION__, address, address+size, size, vstart, vend); + + if (next > vend) { + next = vend; + } + + /* Check that the address is valid */ + if (cpu_get_phys_page_debug(env, vstart) == -1) { + DD("%s: bad guest address!", __FUNCTION__); + return -1; + } + + count++; + + size -= (next - address); + address = next; + } + return count; +} + +/* Fill the pipe buffers to prepare memory transfer from/to + * [address...address+size). This assumes 'buffers' points to an array + * which size corresponds to _countPipeBuffers(address, size) + */ +static void +_fillPipeBuffers( uint32_t address, uint32_t size, GoldfishPipeBuffer* buffers ) +{ + CPUState* env = cpu_single_env; + + while (size > 0) { + uint32_t vstart = address & TARGET_PAGE_MASK; + uint32_t vend = vstart + TARGET_PAGE_SIZE; + uint32_t next = address + size; + + if (next > vend) { + next = vend; + } + + /* Translate virtual address into physical one, into emulator + * memory. */ + target_phys_addr_t phys = cpu_get_phys_page_debug(env, vstart); + + buffers[0].data = qemu_get_ram_ptr(phys) + (address - vstart); + buffers[0].size = next - address; + buffers++; + + size -= (next - address); + address = next; + } +} + +static uint32_t +pipeClient_doCommand( PipeClient* pcl, + uint32_t command, + uint32_t address, + uint32_t* pSize ) +{ + uint32_t size = *pSize; + + D("%s: TID=%4d CHANNEL=%08x COMMAND=%d ADDRESS=%08x SIZE=%d\n", + __FUNCTION__, pcl->tid, pcl->localId, command, address, size); + + /* XXX: TODO */ + switch (command) { + case QEMUD_PIPE_CMD_CLOSE: + /* The client is asking us to close the connection, so + * just do that. */ + qemud_client_close(pcl->client); + return 0; + + case QEMUD_PIPE_CMD_SEND: { + /* First, try to allocate a buffer from the handler */ + void* opaque = pcl->handler; + GoldfishPipeBuffer* buffers; + int numBuffers = 0; + + /* Count the number of buffers we need, allocate them, + * then fill them. */ + numBuffers = _countPipeBuffers(address, size); + if (numBuffers < 0) { + D("%s: Invalid guest address range 0x%x - 0x%x (%d bytes)", + __FUNCTION__, address, address+size, size); + return QEMUD_PIPE_ERROR_NOMEM; + } + buffers = alloca( sizeof(*buffers) * numBuffers ); + _fillPipeBuffers(address, size, buffers); + + D("%s: Sending %d bytes using %d buffers", __FUNCTION__, + size, numBuffers); + + /* Send the data */ + if (pcl->handlerFuncs->sendBuffers(opaque, buffers, numBuffers) < 0) { + /* When .sendBuffers() returns -1, it usually means + * that the handler isn't ready to accept a new message. There + * is however once exception: when the message is too large + * and it wasn't possible to allocate the buffer. + * + * Differentiate between these two cases by looking at errno. + */ + if (errno == ENOMEM) + return QEMUD_PIPE_ERROR_NOMEM; + else + return QEMUD_PIPE_ERROR_AGAIN; + } + return 0; + } + + case QEMUD_PIPE_CMD_RECV: { + void* opaque = pcl->handler; + GoldfishPipeBuffer* buffers; + int numBuffers, ret; + + /* Count the number of buffers we have */ + numBuffers = _countPipeBuffers(address, size); + if (numBuffers < 0) { + D("%s: Invalid guest address range 0x%x - 0x%x (%d bytes)", + __FUNCTION__, address, address+size, size); + return QEMUD_PIPE_ERROR_NOMEM; + } + buffers = alloca(sizeof(*buffers)*numBuffers); + _fillPipeBuffers(address, size, buffers); + + /* Receive data */ + ret = pcl->handlerFuncs->recvBuffers(opaque, buffers, numBuffers); + if (ret < 0) { + if (errno == ENOMEM) { + // XXXX: TODO *pSize = msgSize; + return QEMUD_PIPE_ERROR_NOMEM; + } else { + return QEMUD_PIPE_ERROR_AGAIN; + } + } + *pSize = ret; + return 0; + } + + case QEMUD_PIPE_CMD_WAKE_ON_SEND: { + pcl->handlerFuncs->wakeOn(pcl->handler, QEMUD_PIPE_WAKE_ON_SEND); + return 0; + } + + case QEMUD_PIPE_CMD_WAKE_ON_RECV: { + pcl->handlerFuncs->wakeOn(pcl->handler, QEMUD_PIPE_WAKE_ON_RECV); + return 0; + } + + default: + return QEMUD_PIPE_ERROR_CONNRESET; + } +} + + + +QemudClient* +pipeClient_connect( void* opaque, QemudService* svc, int channel ) +{ + PipeClient* pcl; + + ANEW0(pcl); + pcl->pipeSvc = opaque; + pcl->client = qemud_client_new( svc, channel, pcl, + pipeClient_recv, + pipeClient_close, + NULL, /* TODO: NO SNAPSHOT SAVE */ + NULL ); /* TODO: NO SNAPHOT LOAD */ + + /* Only for the initial connection message */ + qemud_client_set_framing(pcl->client, 1); + return pcl->client; +} + +/********************************************************************** + ********************************************************************** + ***** + ***** GLOBAL PIPE STATE + ***** + *****/ + +struct PipeService { + AIntMap* threadMap; /* maps tid to ThreadState */ +}; + +#if 0 +static void +pipeService_done(PipeService* pipeSvc) +{ + /* Get rid of the tid -> ThreadState map */ + AINTMAP_FOREACH_VALUE(pipeSvc->threadMap, ts, threadState_free(ts)); + aintMap_free(pipeSvc->threadMap); + pipeSvc->threadMap = NULL; +} +#endif + +static void +pipeService_init(PipeService* pipeSvc) +{ + pipeSvc->threadMap = aintMap_new(); + + qemud_service_register( "fast-pipe", 0, pipeSvc, + pipeClient_connect, + NULL, /* TODO: NO SNAPSHOT SAVE SUPPORT */ + NULL /* TODO: NO SNAPSHOT LOAD SUPPORT */ ); +} + +static ThreadState* +pipeService_getState(PipeService* pipeSvc, int tid) +{ + if (pipeSvc->threadMap == NULL) + pipeSvc->threadMap = aintMap_new(); + + return (ThreadState*) aintMap_get(pipeSvc->threadMap, tid); +} + +static void +pipeService_removeState(PipeService* pipeSvc, int tid) +{ + ThreadState* ts = pipeService_getState(pipeSvc, tid); + + if (ts == NULL) + return; + + aintMap_del(pipeSvc->threadMap,tid); + threadState_free(ts); +} + +static int +pipeService_addPipe(PipeService* pipeSvc, int tid, int localId, PipeClient* pcl) +{ + ThreadState* ts = pipeService_getState(pipeSvc, tid); + + if (ts == NULL) { + ts = threadState_new(); + aintMap_set(pipeSvc->threadMap, tid, ts); + } + + return threadState_addPipe(ts, localId, pcl); +} + +static void +pipeService_removePipe(PipeService* pipeSvc, int tid, int localId) +{ + ThreadState* ts = pipeService_getState(pipeSvc, tid); + + if (ts == NULL) + return; + + threadState_delPipe(ts, localId); +} + +/********************************************************************** + ********************************************************************** + ***** + ***** HARDWARE API - AS SEEN FROM hw/goldfish_trace.c + ***** + *****/ + +static PipeService _globalState[1]; + +void init_qemud_pipes(void) +{ + pipeService_init(_globalState); +} + +void +goldfish_pipe_thread_death(int tid) +{ + PipeService* pipeSvc = _globalState; + pipeService_removeState(pipeSvc, tid); +} + +void +goldfish_pipe_write(int tid, int offset, uint32_t value) +{ + PipeService* pipeSvc = _globalState; + DD("%s: tid=%d offset=%d value=%d (0x%x)", __FUNCTION__, tid, + offset, value, value); + + ThreadState* ts = pipeService_getState(pipeSvc, tid); + + if (ts == NULL) { + D("%s: no thread state for tid=%d", __FUNCTION__, tid); + return; + } + threadState_write(ts, offset, value); +} + +uint32_t +goldfish_pipe_read(int tid, int offset) +{ + PipeService* pipeSvc = _globalState; + uint32_t ret; + + DD("%s: tid=%d offset=%d", __FUNCTION__, tid, offset); + + ThreadState* ts = pipeService_getState(pipeSvc, tid); + + if (ts == NULL) { + D("%s: no thread state for tid=%d", __FUNCTION__, tid); + return 0; + } + ret = threadState_read(ts, offset); + DD("%s: result=%d (0x%d)", __FUNCTION__, ret, ret); + return ret; +} diff --git a/hw/goldfish_pipe.h b/hw/goldfish_pipe.h new file mode 100644 index 0000000..03bc443 --- /dev/null +++ b/hw/goldfish_pipe.h @@ -0,0 +1,98 @@ +/* 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_GOLDFISH_PIPE_H +#define _HW_GOLDFISH_PIPE_H + +#include <stdint.h> +#include "android/hw-qemud.h" + +/* The following functions should called from hw/goldfish_trace.c and are + * used to implement QEMUD 'fast-pipes' in the Android emulator. + */ +extern void goldfish_pipe_thread_death(int tid); +extern void goldfish_pipe_write(int tid, int offset, uint32_t value); +extern uint32_t goldfish_pipe_read(int tid, int offset); + +/* The following definitions are used to define a "pipe handler" type. + * Each pipe handler manages a given, named pipe type, and must provide + * a few callbacks that will be used at appropriate times: + * + * - init :: + * is called when a guest client has connected to a given + * pipe. The function should return an opaque pointer that + * will be passed as the first parameter to other callbacks. + * + * Note: pipeOpaque is the value that was passed to + * goldfish_pipe_add_type() to register the pipe handler. + * + * - close :: + * is called when the pipe is closed.(either from the guest, or + * when the handler itself calls qemud_client_close() on the + * corresponding QemudClient object passed to init()). + * + * - sendBuffers :: + * is called when the guest is sending data through the pipe. This + * callback receives a list of buffer descriptors that indicate + * where the data is located in memory. + * + * Must return 0 on success, or -1 on failure, with errno of: + * + * ENOMEM -> indicates that the message is too large + * EAGAIN -> indicates that the handler is not ready + * to accept the message yet. + * + * - recvBuffers :: + * Is called when the guest wants to receive data from the pipe. + * The caller provides a list of memory buffers to put the data into. + * + * Must return the size of the incoming data on success, or -1 + * on error, with errno of: + * + * ENOMEM -> buffer too small to receive the message + * EAGAIN -> no incoming data yet + * + * - wakeOn :: + * is called to indicate that the guest wants to be waked when the + * pipe becomes able to either receive data from the guest, or send it + * new incoming data. It is the responsability of the pipe handler to + * signal the corresponding events by sending a single byte containing + * QEMUD_PIPE_WAKE_XXX bit flags through qemud_client_send() to do so. + */ + +enum { + QEMUD_PIPE_WAKE_ON_SEND = (1 << 0), + QEMUD_PIPE_WAKE_ON_RECV = (1 << 1), +}; + +/* Buffer descriptor for sendBuffers() and recvBuffers() callbacks */ +typedef struct GoldfishPipeBuffer { + uint8_t* data; + size_t size; +} GoldfishPipeBuffer; + +/* Pipe handler funcs */ +typedef struct { + void* (*init)( QemudClient* client, void* pipeOpaque ); + void (*close)( void* opaque ); + int (*sendBuffers)( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers ); + int (*recvBuffers)( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers ); + void (*wakeOn)( void* opaque, int flags ); +} QemudPipeHandlerFuncs; + +/* Register a new pipe handler type. 'pipeOpaque' is passed directly + * to 'init() when a new pipe is connected to. + */ +extern void goldfish_pipe_add_type(const char* pipeName, + void* pipeOpaque, + const QemudPipeHandlerFuncs* pipeFuncs ); + +#endif /* _HW_GOLDFISH_PIPE_H */ |