aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoffer Dall <christoffer.dall@linaro.org>2014-06-23 15:50:05 +0100
committerPeter Maydell <peter.maydell@linaro.org>2014-06-23 16:33:29 +0100
commit8fa7507ae68e7e86562107f4204662230b8acdfe (patch)
treeabf977894013c4abc02968194ff8dae3edd27439
parentffb7c1f9f983e41ae80001fb1ad5905e03af582a (diff)
android_pipe: Factor out example/test pipes from android_pipe
The Android Pipe main implementation file should only contain the actual implementation. Keeping all that test backend implementation code in there just makes it confusing to grep in the file and figure out what is going on. This could be made dependent on some sort of debug flag to avoid compiling test code as part of shipped binaries in the future. Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/android_pipe.c473
-rw-r--r--hw/misc/android_pipe_test.c521
-rw-r--r--include/hw/misc/android_pipe.h4
4 files changed, 529 insertions, 470 deletions
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 315d1285c6..54a1f65bf6 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -15,6 +15,7 @@ common-obj-$(CONFIG_A9SCU) += a9scu.o
common-obj-$(CONFIG_ARM11SCU) += arm11scu.o
obj-$(CONFIG_ANDROID_PIPE) += android_pipe.o
+obj-$(CONFIG_ANDROID_PIPE) += android_pipe_test.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
index 90bce9526d..5b44f0c11d 100644
--- a/hw/misc/android_pipe.c
+++ b/hw/misc/android_pipe.c
@@ -71,15 +71,6 @@
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
@@ -459,458 +450,6 @@ static const AndroidPipeFuncs pipeConnector_funcs = {
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 */
/***********************************************************************
***********************************************************************
@@ -1246,15 +785,9 @@ static void android_pipe_realize(DeviceState *dev, Error **errp)
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
+ android_zero_pipe_init();
+ android_pingpong_init();
+ android_throttle_init();
}
void
diff --git a/hw/misc/android_pipe_test.c b/hw/misc/android_pipe_test.c
new file mode 100644
index 0000000000..dc4a6e5330
--- /dev/null
+++ b/hw/misc/android_pipe_test.c
@@ -0,0 +1,521 @@
+/* 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
+**
+** Example uses of Android Pipes.
+*/
+#include "hw/misc/android_pipe.h"
+
+#define DEBUG 0
+
+/* Set to 1 to debug i/o register reads/writes */
+#define DEBUG_REGS 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
+
+#if DEBUG_REGS >= 1
+# define DR(...) D(__VA_ARGS__)
+#else
+# define DR(...) (void)0
+#endif
+
+#define E(...) fprintf(stderr, "ERROR:" __VA_ARGS__), fprintf(stderr, "\n")
+
+/* 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
+
+
+/***********************************************************************
+ ***********************************************************************
+ *****
+ ***** 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.
+ */
+
+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,
+};
+
+#if DEBUG_ZERO_PIPE
+void android_zero_pipe_init(void)
+{
+ android_pipe_add_type("zero", NULL, &zeroPipe_funcs);
+}
+#else
+void android_zero_pipe_init(void) { }
+#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 */
+
+/* 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,
+};
+
+#if DEBUG_PINGPONG_PIPE
+void android_pingpong_init(void)
+{
+ android_pipe_add_type("pingpong", NULL, &pingPongPipe_funcs);
+}
+#else
+void android_pingpong_init(void) { }
+#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.
+ */
+
+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,
+};
+
+#ifdef DEBUG_THROTTLE_PIPE
+void android_throttle_init(void)
+{
+ android_pipe_add_type("throttle", NULL, &throttlePipe_funcs);
+}
+#else
+void android_throttle_init(void) { }
+#endif /* DEBUG_THROTTLE_PIPE */
diff --git a/include/hw/misc/android_pipe.h b/include/hw/misc/android_pipe.h
index 3fa7e18693..4db3731560 100644
--- a/include/hw/misc/android_pipe.h
+++ b/include/hw/misc/android_pipe.h
@@ -217,4 +217,8 @@ struct access_params_64 {
uint32_t flags;
};
+extern void android_zero_pipe_init(void);
+extern void android_pingpong_init(void);
+extern void android_throttle_init(void);
+
#endif /* _HW_ANDROID_PIPE_H */