blob: 998ba49d828d71f26a8eabaff2aa3f5beb008634 [file] [log] [blame]
David 'Digit' Turner89217f52011-03-21 17:51:03 +01001/* Copyright (C) 2011 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
David 'Digit' Turner89217f52011-03-21 17:51:03 +010012#include "android/utils/panic.h"
David 'Digit' Turner89217f52011-03-21 17:51:03 +010013#include "android/utils/system.h"
David 'Digit' Turner89217f52011-03-21 17:51:03 +010014#include "hw/goldfish_pipe.h"
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020015#include "hw/goldfish_device.h"
16#include "qemu-timer.h"
David 'Digit' Turner89217f52011-03-21 17:51:03 +010017
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020018#define DEBUG 1
19
20/* Set to 1 to debug i/o register reads/writes */
21#define DEBUG_REGS 0
David 'Digit' Turner89217f52011-03-21 17:51:03 +010022
23#if DEBUG >= 1
24# define D(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
25#else
26# define D(...) (void)0
27#endif
28
29#if DEBUG >= 2
30# define DD(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
31#else
32# define DD(...) (void)0
33#endif
34
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020035#if DEBUG_REGS >= 1
36# define DR(...) D(__VA_ARGS__)
37#else
38# define DR(...) (void)0
39#endif
40
David 'Digit' Turner89217f52011-03-21 17:51:03 +010041#define E(...) fprintf(stderr, "ERROR:" __VA_ARGS__), fprintf(stderr, "\n")
42
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020043/* Set to 1 to enable the 'zero' pipe type, useful for debugging */
44#define DEBUG_ZERO_PIPE 1
David 'Digit' Turner89217f52011-03-21 17:51:03 +010045
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020046/* Set to 1 to enable the 'pingpong' pipe type, useful for debugging */
47#define DEBUG_PINGPONG_PIPE 1
David 'Digit' Turner89217f52011-03-21 17:51:03 +010048
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020049/* Set to 1 to enable the 'throttle' pipe type, useful for debugging */
50#define DEBUG_THROTTLE_PIPE 1
David 'Digit' Turner89217f52011-03-21 17:51:03 +010051
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020052/***********************************************************************
53 ***********************************************************************
David 'Digit' Turner89217f52011-03-21 17:51:03 +010054 *****
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020055 ***** P I P E S E R V I C E R E G I S T R A T I O N
David 'Digit' Turner89217f52011-03-21 17:51:03 +010056 *****
57 *****/
58
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020059#define MAX_PIPE_SERVICES 8
David 'Digit' Turner89217f52011-03-21 17:51:03 +010060typedef struct {
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020061 const char* name;
62 void* opaque;
63 GoldfishPipeFuncs funcs;
64} PipeService;
David 'Digit' Turner89217f52011-03-21 17:51:03 +010065
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020066typedef struct {
67 int count;
68 PipeService services[MAX_PIPE_SERVICES];
69} PipeServices;
David 'Digit' Turner89217f52011-03-21 17:51:03 +010070
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020071static PipeServices _pipeServices[1];
David 'Digit' Turner89217f52011-03-21 17:51:03 +010072
73void
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020074goldfish_pipe_add_type(const char* pipeName,
75 void* pipeOpaque,
76 const GoldfishPipeFuncs* pipeFuncs )
David 'Digit' Turner89217f52011-03-21 17:51:03 +010077{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020078 PipeServices* list = _pipeServices;
79 int count = list->count;
David 'Digit' Turner89217f52011-03-21 17:51:03 +010080
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020081 if (count >= MAX_PIPE_SERVICES) {
82 APANIC("Too many goldfish pipe services (%d)", count);
David 'Digit' Turner89217f52011-03-21 17:51:03 +010083 }
84
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020085 list->services[count].name = pipeName;
86 list->services[count].opaque = pipeOpaque;
87 list->services[count].funcs = pipeFuncs[0];
88
89 list->count++;
David 'Digit' Turner89217f52011-03-21 17:51:03 +010090}
91
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020092static const PipeService*
93goldfish_pipe_find_type(const char* pipeName)
David 'Digit' Turner89217f52011-03-21 17:51:03 +010094{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020095 PipeServices* list = _pipeServices;
96 int count = list->count;
97 int nn;
David 'Digit' Turner89217f52011-03-21 17:51:03 +010098
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +020099 for (nn = 0; nn < count; nn++) {
100 if (!strcmp(list->services[nn].name, pipeName)) {
101 return &list->services[nn];
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100102 }
103 }
104 return NULL;
105}
106
107
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200108/***********************************************************************
109 ***********************************************************************
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100110 *****
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200111 ***** P I P E C O N N E C T I O N S
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100112 *****
113 *****/
114
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200115typedef struct PipeDevice PipeDevice;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100116
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200117typedef struct Pipe {
118 struct Pipe* next;
119 struct Pipe* next_waked;
120 PipeDevice* device;
121 uint32_t channel;
122 void* opaque;
123 const GoldfishPipeFuncs* funcs;
124 unsigned char wanted;
125 char closed;
126} Pipe;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100127
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200128/* Forward */
129static void* pipeConnector_new(Pipe* pipe);
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100130
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200131Pipe*
132pipe_new(uint32_t channel, PipeDevice* dev)
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100133{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200134 Pipe* pipe;
135 ANEW0(pipe);
136 pipe->channel = channel;
137 pipe->device = dev;
138 pipe->opaque = pipeConnector_new(pipe);
139 return pipe;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100140}
141
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200142static Pipe**
143pipe_list_findp_channel( Pipe** list, uint32_t channel )
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100144{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200145 Pipe** pnode = list;
146 for (;;) {
147 Pipe* node = *pnode;
148 if (node == NULL || node->channel == channel) {
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100149 break;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100150 }
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200151 pnode = &node->next;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100152 }
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200153 return pnode;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100154}
155
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100156#if 0
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200157static Pipe**
158pipe_list_findp_opaque( Pipe** list, void* opaque )
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100159{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200160 Pipe** pnode = list;
161 for (;;) {
162 Pipe* node = *pnode;
163 if (node == NULL || node->opaque == opaque) {
164 break;
165 }
166 pnode = &node->next;
167 }
168 return pnode;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100169}
170#endif
171
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200172static Pipe**
173pipe_list_findp_waked( Pipe** list, Pipe* pipe )
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100174{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200175 Pipe** pnode = list;
176 for (;;) {
177 Pipe* node = *pnode;
178 if (node == NULL || node == pipe) {
179 break;
180 }
181 pnode = &node->next_waked;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100182 }
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200183 return pnode;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100184}
185
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200186
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100187static void
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200188pipe_list_remove_waked( Pipe** list, Pipe* pipe )
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100189{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200190 Pipe** lookup = pipe_list_findp_waked(list, pipe);
191 Pipe* node = *lookup;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100192
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200193 if (node != NULL) {
194 (*lookup) = node->next_waked;
195 node->next_waked = NULL;
196 }
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100197}
198
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200199/***********************************************************************
200 ***********************************************************************
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100201 *****
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200202 ***** P I P E C O N N E C T O R S
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100203 *****
204 *****/
205
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200206/* These are used to handle the initial connection attempt, where the
207 * client is going to write the name of the pipe service it wants to
208 * connect to, followed by a terminating zero.
209 */
210typedef struct {
211 Pipe* pipe;
212 char buffer[128];
213 int buffpos;
214} PipeConnector;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100215
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200216static const GoldfishPipeFuncs pipeConnector_funcs; // forward
217
218void*
219pipeConnector_new(Pipe* pipe)
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100220{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200221 PipeConnector* pcon;
222
223 ANEW0(pcon);
224 pcon->pipe = pipe;
225 pipe->funcs = &pipeConnector_funcs;
226 return pcon;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100227}
228
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200229static void
230pipeConnector_close( void* opaque )
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100231{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200232 PipeConnector* pcon = opaque;
233 AFREE(pcon);
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100234}
235
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200236static int
237pipeConnector_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100238{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200239 PipeConnector* pcon = opaque;
240 const GoldfishPipeBuffer* buffers_limit = buffers + numBuffers;
241 int ret = 0;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100242
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200243 DD("%s: channel=0x%x numBuffers=%d", __FUNCTION__,
244 pcon->pipe->channel,
245 numBuffers);
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100246
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200247 while (buffers < buffers_limit) {
248 int avail;
249
250 DD("%s: buffer data (%3d bytes): '%.*s'", __FUNCTION__,
251 buffers[0].size, buffers[0].size, buffers[0].data);
252
253 if (buffers[0].size == 0) {
254 buffers++;
255 continue;
256 }
257
258 avail = sizeof(pcon->buffer) - pcon->buffpos;
259 if (avail > buffers[0].size)
260 avail = buffers[0].size;
261
262 if (avail > 0) {
263 memcpy(pcon->buffer + pcon->buffpos, buffers[0].data, avail);
264 pcon->buffpos += avail;
265 ret += avail;
266 }
267 buffers++;
268 }
269
270 /* Now check that our buffer contains a zero-terminated string */
271 if (memchr(pcon->buffer, '\0', pcon->buffpos) != NULL) {
272 /* Acceptable formats for the connection string are:
273 *
274 * pipe:<name>
275 * pipe:<name>:<arguments>
276 */
277 char* pipeName;
278 char* pipeArgs;
279
280 D("%s: connector: '%s'", __FUNCTION__, pcon->buffer);
281
282 if (memcmp(pcon->buffer, "pipe:", 5) != 0) {
283 /* Nope, we don't handle these for now. */
284 D("%s: Unknown pipe connection: '%s'", __FUNCTION__, pcon->buffer);
285 return PIPE_ERROR_INVAL;
286 }
287
288 pipeName = pcon->buffer + 5;
289 pipeArgs = strchr(pipeName, ':');
290
291 if (pipeArgs != NULL) {
292 *pipeArgs++ = '\0';
293 }
294
295 Pipe* pipe = pcon->pipe;
296 const PipeService* svc = goldfish_pipe_find_type(pipeName);
297 if (svc == NULL) {
298 D("%s: Unknown server!", __FUNCTION__);
299 return PIPE_ERROR_INVAL;
300 }
301
302 void* peer = svc->funcs.init(pipe, svc->opaque, pipeArgs);
303 if (peer == NULL) {
304 D("%s: Initialization failed!", __FUNCTION__);
305 return PIPE_ERROR_INVAL;
306 }
307
308 /* Do the evil switch now */
309 pipe->opaque = peer;
310 pipe->funcs = &svc->funcs;
311 AFREE(pcon);
312 }
313
314 return ret;
315}
316
317static int
318pipeConnector_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
319{
320 return PIPE_ERROR_IO;
321}
322
323static unsigned
324pipeConnector_poll( void* opaque )
325{
326 return PIPE_WAKE_WRITE;
327}
328
329static void
330pipeConnector_wakeOn( void* opaque, int flags )
331{
332 /* nothing, really should never happen */
333}
334
335static const GoldfishPipeFuncs pipeConnector_funcs = {
336 NULL, /* init */
337 pipeConnector_close, /* should rarely happen */
338 pipeConnector_sendBuffers, /* the interesting stuff */
339 pipeConnector_recvBuffers, /* should not happen */
340 pipeConnector_poll, /* should not happen */
341 pipeConnector_wakeOn, /* should not happen */
342};
343
344/***********************************************************************
345 ***********************************************************************
346 *****
347 ***** Z E R O P I P E S
348 *****
349 *****/
350
351/* A simple pipe service that mimics /dev/zero, you can write anything to
352 * it, and you can always read any number of zeros from it. Useful for debugging
353 * the kernel driver.
354 */
355#if DEBUG_ZERO_PIPE
356
357typedef struct {
358 void* hwpipe;
359} ZeroPipe;
360
361static void*
362zeroPipe_init( void* hwpipe, void* svcOpaque, const char* args )
363{
364 ZeroPipe* zpipe;
365
366 D("%s: hwpipe=%p", __FUNCTION__, hwpipe);
367 ANEW0(zpipe);
368 zpipe->hwpipe = hwpipe;
369 return zpipe;
370}
371
372static void
373zeroPipe_close( void* opaque )
374{
375 ZeroPipe* zpipe = opaque;
376
377 D("%s: hwpipe=%p", __FUNCTION__, zpipe->hwpipe);
378 AFREE(zpipe);
379}
380
381static int
382zeroPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
383{
384 int ret = 0;
385 while (numBuffers > 0) {
386 ret += buffers[0].size;
387 buffers++;
388 numBuffers--;
389 }
390 return ret;
391}
392
393static int
394zeroPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
395{
396 int ret = 0;
397 while (numBuffers > 0) {
398 ret += buffers[0].size;
399 memset(buffers[0].data, 0, buffers[0].size);
400 buffers++;
401 numBuffers--;
402 }
403 return ret;
404}
405
406static unsigned
407zeroPipe_poll( void* opaque )
408{
409 return PIPE_WAKE_READ | PIPE_WAKE_WRITE;
410}
411
412static void
413zeroPipe_wakeOn( void* opaque, int flags )
414{
415 /* nothing to do here */
416}
417
418static const GoldfishPipeFuncs zeroPipe_funcs = {
419 zeroPipe_init,
420 zeroPipe_close,
421 zeroPipe_sendBuffers,
422 zeroPipe_recvBuffers,
423 zeroPipe_poll,
424 zeroPipe_wakeOn,
425};
426
427#endif /* DEBUG_ZERO */
428
429/***********************************************************************
430 ***********************************************************************
431 *****
432 ***** P I N G P O N G P I P E S
433 *****
434 *****/
435
436/* Similar debug service that sends back anything it receives */
437/* All data is kept in a circular dynamic buffer */
438
439#if DEBUG_PINGPONG_PIPE
440
441/* Initial buffer size */
442#define PINGPONG_SIZE 1024
443
444typedef struct {
445 void* hwpipe;
446 uint8_t* buffer;
447 size_t size;
448 size_t pos;
449 size_t count;
450 unsigned flags;
451} PingPongPipe;
452
453static void
454pingPongPipe_init0( PingPongPipe* pipe, void* hwpipe, void* svcOpaque )
455{
456 pipe->hwpipe = hwpipe;
457 pipe->size = PINGPONG_SIZE;
458 pipe->buffer = malloc(pipe->size);
459 pipe->pos = 0;
460 pipe->count = 0;
461}
462
463static void*
464pingPongPipe_init( void* hwpipe, void* svcOpaque, const char* args )
465{
466 PingPongPipe* ppipe;
467
468 D("%s: hwpipe=%p", __FUNCTION__, hwpipe);
469 ANEW0(ppipe);
470 pingPongPipe_init0(ppipe, hwpipe, svcOpaque);
471 return ppipe;
472}
473
474static void
475pingPongPipe_close( void* opaque )
476{
477 PingPongPipe* ppipe = opaque;
478
479 D("%s: hwpipe=%p (pos=%d count=%d size=%d)", __FUNCTION__,
480 ppipe->hwpipe, ppipe->pos, ppipe->count, ppipe->size);
481 free(ppipe->buffer);
482 AFREE(ppipe);
483}
484
485static int
486pingPongPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
487{
488 PingPongPipe* pipe = opaque;
489 int ret = 0;
490 int count;
491 const GoldfishPipeBuffer* buff = buffers;
492 const GoldfishPipeBuffer* buffEnd = buff + numBuffers;
493
494 count = 0;
495 for ( ; buff < buffEnd; buff++ )
496 count += buff->size;
497
498 /* Do we need to grow the pingpong buffer? */
499 while (count > pipe->size - pipe->count) {
500 size_t newsize = pipe->size*2;
501 uint8_t* newbuff = realloc(pipe->buffer, newsize);
502 int wpos = pipe->pos + pipe->count;
503 if (newbuff == NULL) {
504 break;
505 }
506 if (wpos > pipe->size) {
507 wpos -= pipe->size;
508 memcpy(newbuff + pipe->size, newbuff, wpos);
509 }
510 pipe->buffer = newbuff;
511 pipe->size = newsize;
512 D("pingpong buffer is now %d bytes", newsize);
513 }
514
515 for ( buff = buffers; buff < buffEnd; buff++ ) {
516 int avail = pipe->size - pipe->count;
517 if (avail <= 0) {
518 if (ret == 0)
519 ret = PIPE_ERROR_AGAIN;
520 break;
521 }
522 if (avail > buff->size) {
523 avail = buff->size;
524 }
525
526 int wpos = pipe->pos + pipe->count;
527 if (wpos >= pipe->size) {
528 wpos -= pipe->size;
529 }
530 if (wpos + avail <= pipe->size) {
531 memcpy(pipe->buffer + wpos, buff->data, avail);
532 } else {
533 int avail2 = pipe->size - wpos;
534 memcpy(pipe->buffer + wpos, buff->data, avail2);
535 memcpy(pipe->buffer, buff->data + avail2, avail - avail2);
536 }
537 pipe->count += avail;
538 ret += avail;
539 }
540
541 /* Wake up any waiting readers if we wrote something */
542 if (pipe->count > 0 && (pipe->flags & PIPE_WAKE_READ)) {
543 goldfish_pipe_wake(pipe->hwpipe, PIPE_WAKE_READ);
544 }
545
546 return ret;
547}
548
549static int
550pingPongPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
551{
552 PingPongPipe* pipe = opaque;
553 int ret = 0;
554
555 while (numBuffers > 0) {
556 int avail = pipe->count;
557 if (avail <= 0) {
558 if (ret == 0)
559 ret = PIPE_ERROR_AGAIN;
560 break;
561 }
562 if (avail > buffers[0].size) {
563 avail = buffers[0].size;
564 }
565
566 int rpos = pipe->pos;
567
568 if (rpos + avail <= pipe->size) {
569 memcpy(buffers[0].data, pipe->buffer + rpos, avail);
570 } else {
571 int avail2 = pipe->size - rpos;
572 memcpy(buffers[0].data, pipe->buffer + rpos, avail2);
573 memcpy(buffers[0].data + avail2, pipe->buffer, avail - avail2);
574 }
575 pipe->count -= avail;
576 pipe->pos += avail;
577 if (pipe->pos >= pipe->size) {
578 pipe->pos -= pipe->size;
579 }
580 ret += avail;
581 numBuffers--;
582 buffers++;
583 }
584
585 /* Wake up any waiting readers if we wrote something */
586 if (pipe->count < PINGPONG_SIZE && (pipe->flags & PIPE_WAKE_WRITE)) {
587 goldfish_pipe_wake(pipe->hwpipe, PIPE_WAKE_WRITE);
588 }
589
590 return ret;
591}
592
593static unsigned
594pingPongPipe_poll( void* opaque )
595{
596 PingPongPipe* pipe = opaque;
597 unsigned ret = 0;
598
599 if (pipe->count < pipe->size)
600 ret |= PIPE_WAKE_WRITE;
601
602 if (pipe->count > 0)
603 ret |= PIPE_WAKE_READ;
604
605 return ret;
606}
607
608static void
609pingPongPipe_wakeOn( void* opaque, int flags )
610{
611 PingPongPipe* pipe = opaque;
612 pipe->flags |= (unsigned)flags;
613}
614
615static const GoldfishPipeFuncs pingPongPipe_funcs = {
616 pingPongPipe_init,
617 pingPongPipe_close,
618 pingPongPipe_sendBuffers,
619 pingPongPipe_recvBuffers,
620 pingPongPipe_poll,
621 pingPongPipe_wakeOn,
622};
623
624#endif /* DEBUG_PINGPONG_PIPE */
625
626/***********************************************************************
627 ***********************************************************************
628 *****
629 ***** T H R O T T L E P I P E S
630 *****
631 *****/
632
633/* Similar to PingPongPipe, but will throttle the bandwidth to test
634 * blocking I/O.
635 */
636
637#ifdef DEBUG_THROTTLE_PIPE
638
639typedef struct {
640 PingPongPipe pingpong;
641 double sendRate;
642 int64_t sendExpiration;
643 double recvRate;
644 int64_t recvExpiration;
645 QEMUTimer* timer;
646} ThrottlePipe;
647
648/* forward declaration */
649static void throttlePipe_timerFunc( void* opaque );
650
651static void*
652throttlePipe_init( void* hwpipe, void* svcOpaque, const char* args )
653{
654 ThrottlePipe* pipe;
655
656 ANEW0(pipe);
657 pingPongPipe_init0(&pipe->pingpong, hwpipe, svcOpaque);
658 pipe->timer = qemu_new_timer(vm_clock, throttlePipe_timerFunc, pipe);
659 /* For now, limit to 500 KB/s in both directions */
660 pipe->sendRate = 1e9 / (500*1024*8);
661 pipe->recvRate = pipe->sendRate;
662 return pipe;
663}
664
665static void
666throttlePipe_close( void* opaque )
667{
668 ThrottlePipe* pipe = opaque;
669
670 qemu_del_timer(pipe->timer);
671 qemu_free_timer(pipe->timer);
672 pingPongPipe_close(&pipe->pingpong);
673}
674
675static void
676throttlePipe_rearm( ThrottlePipe* pipe )
677{
678 int64_t minExpiration = 0;
679
680 DD("%s: sendExpiration=%lld recvExpiration=%lld\n", __FUNCTION__, pipe->sendExpiration, pipe->recvExpiration);
681
682 if (pipe->sendExpiration) {
683 if (minExpiration == 0 || pipe->sendExpiration < minExpiration)
684 minExpiration = pipe->sendExpiration;
685 }
686
687 if (pipe->recvExpiration) {
688 if (minExpiration == 0 || pipe->recvExpiration < minExpiration)
689 minExpiration = pipe->recvExpiration;
690 }
691
692 if (minExpiration != 0) {
693 DD("%s: Arming for %lld\n", __FUNCTION__, minExpiration);
694 qemu_mod_timer(pipe->timer, minExpiration);
695 }
696}
697
698static void
699throttlePipe_timerFunc( void* opaque )
700{
701 ThrottlePipe* pipe = opaque;
702 int64_t now = qemu_get_clock_ns(vm_clock);
703
704 DD("%s: TICK! now=%lld sendExpiration=%lld recvExpiration=%lld\n",
705 __FUNCTION__, now, pipe->sendExpiration, pipe->recvExpiration);
706
707 /* Timer has expired, signal wake up if needed */
708 int flags = 0;
709
710 if (pipe->sendExpiration && now > pipe->sendExpiration) {
711 flags |= PIPE_WAKE_WRITE;
712 pipe->sendExpiration = 0;
713 }
714 if (pipe->recvExpiration && now > pipe->recvExpiration) {
715 flags |= PIPE_WAKE_READ;
716 pipe->recvExpiration = 0;
717 }
718 flags &= pipe->pingpong.flags;
719 if (flags != 0) {
720 DD("%s: WAKE %d\n", __FUNCTION__, flags);
721 goldfish_pipe_wake(pipe->pingpong.hwpipe, flags);
722 }
723
724 throttlePipe_rearm(pipe);
725}
726
727static int
728throttlePipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
729{
730 ThrottlePipe* pipe = opaque;
731 int ret;
732
733 if (pipe->sendExpiration > 0) {
734 return PIPE_ERROR_AGAIN;
735 }
736
737 ret = pingPongPipe_sendBuffers(&pipe->pingpong, buffers, numBuffers);
738 if (ret > 0) {
739 /* Compute next send expiration time */
740 pipe->sendExpiration = qemu_get_clock_ns(vm_clock) + ret*pipe->sendRate;
741 throttlePipe_rearm(pipe);
742 }
743 return ret;
744}
745
746static int
747throttlePipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
748{
749 ThrottlePipe* pipe = opaque;
750 int ret;
751
752 if (pipe->recvExpiration > 0) {
753 return PIPE_ERROR_AGAIN;
754 }
755
756 ret = pingPongPipe_recvBuffers(&pipe->pingpong, buffers, numBuffers);
757 if (ret > 0) {
758 pipe->recvExpiration = qemu_get_clock_ns(vm_clock) + ret*pipe->recvRate;
759 throttlePipe_rearm(pipe);
760 }
761 return ret;
762}
763
764static unsigned
765throttlePipe_poll( void* opaque )
766{
767 ThrottlePipe* pipe = opaque;
768 unsigned ret = pingPongPipe_poll(&pipe->pingpong);
769
770 if (pipe->sendExpiration > 0)
771 ret &= ~PIPE_WAKE_WRITE;
772
773 if (pipe->recvExpiration > 0)
774 ret &= ~PIPE_WAKE_READ;
775
776 return ret;
777}
778
779static void
780throttlePipe_wakeOn( void* opaque, int flags )
781{
782 ThrottlePipe* pipe = opaque;
783 pingPongPipe_wakeOn(&pipe->pingpong, flags);
784}
785
786static const GoldfishPipeFuncs throttlePipe_funcs = {
787 throttlePipe_init,
788 throttlePipe_close,
789 throttlePipe_sendBuffers,
790 throttlePipe_recvBuffers,
791 throttlePipe_poll,
792 throttlePipe_wakeOn,
793};
794
795#endif /* DEBUG_THROTTLE_PIPE */
796
797/***********************************************************************
798 ***********************************************************************
799 *****
800 ***** G O L D F I S H P I P E D E V I C E
801 *****
802 *****/
803
804struct PipeDevice {
805 struct goldfish_device dev;
806
807 /* the list of all pipes */
808 Pipe* pipes;
809
810 /* the list of signalled pipes */
811 Pipe* signaled_pipes;
812
813 /* i/o registers */
814 uint32_t address;
815 uint32_t size;
816 uint32_t status;
817 uint32_t channel;
818 uint32_t wakes;
819};
820
821
822static void
823pipeDevice_doCommand( PipeDevice* dev, uint32_t command )
824{
825 Pipe** lookup = pipe_list_findp_channel(&dev->pipes, dev->channel);
826 Pipe* pipe = *lookup;
827 CPUState* env = cpu_single_env;
828
829 /* Check that we're referring a known pipe channel */
830 if (command != PIPE_CMD_OPEN && pipe == NULL) {
831 dev->status = PIPE_ERROR_INVAL;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100832 return;
833 }
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200834
835 /* If the pipe is closed by the host, return an error */
836 if (pipe != NULL && pipe->closed && command != PIPE_CMD_CLOSE) {
837 dev->status = PIPE_ERROR_IO;
838 return;
839 }
840
841 switch (command) {
842 case PIPE_CMD_OPEN:
843 DD("%s: CMD_OPEN channel=0x%x", __FUNCTION__, dev->channel);
844 if (pipe != NULL) {
845 dev->status = PIPE_ERROR_INVAL;
846 break;
847 }
848 pipe = pipe_new(dev->channel, dev);
849 pipe->next = dev->pipes;
850 dev->pipes = pipe;
851 dev->status = 0;
852 break;
853
854 case PIPE_CMD_CLOSE:
855 DD("%s: CMD_CLOSE channel=0x%x", __FUNCTION__, dev->channel);
856 /* Remove from device's lists */
857 *lookup = pipe->next;
858 pipe->next = NULL;
859 pipe_list_remove_waked(&dev->signaled_pipes, pipe);
860 /* Call close callback */
861 if (pipe->funcs->close) {
862 pipe->funcs->close(pipe->opaque);
863 }
864 /* Free stuff */
865 AFREE(pipe);
866 break;
867
868 case PIPE_CMD_POLL:
869 dev->status = pipe->funcs->poll(pipe->opaque);
870 DD("%s: CMD_POLL > status=%d", __FUNCTION__, dev->status);
871 break;
872
873 case PIPE_CMD_READ_BUFFER: {
874 /* Translate virtual address into physical one, into emulator memory. */
875 GoldfishPipeBuffer buffer;
876 uint32_t address = dev->address;
877 uint32_t page = address & TARGET_PAGE_MASK;
878 target_phys_addr_t phys = cpu_get_phys_page_debug(env, page);
879 buffer.data = qemu_get_ram_ptr(phys) + (address - page);
880 buffer.size = dev->size;
881 dev->status = pipe->funcs->recvBuffers(pipe->opaque, &buffer, 1);
882 DD("%s: CMD_READ_BUFFER channel=0x%x address=0x%08x size=%d > status=%d",
883 __FUNCTION__, dev->channel, dev->address, dev->size, dev->status);
884 break;
885 }
886
887 case PIPE_CMD_WRITE_BUFFER: {
888 /* Translate virtual address into physical one, into emulator memory. */
889 GoldfishPipeBuffer buffer;
890 uint32_t address = dev->address;
891 uint32_t page = address & TARGET_PAGE_MASK;
892 target_phys_addr_t phys = cpu_get_phys_page_debug(env, page);
893 buffer.data = qemu_get_ram_ptr(phys) + (address - page);
894 buffer.size = dev->size;
895 dev->status = pipe->funcs->sendBuffers(pipe->opaque, &buffer, 1);
896 DD("%s: CMD_WRITE_BUFFER channel=0x%x address=0x%08x size=%d > status=%d",
897 __FUNCTION__, dev->channel, dev->address, dev->size, dev->status);
898 break;
899 }
900
901 case PIPE_CMD_WAKE_ON_READ:
902 DD("%s: CMD_WAKE_ON_READ channel=0x%x", __FUNCTION__, dev->channel);
903 if ((pipe->wanted & PIPE_WAKE_READ) == 0) {
904 pipe->wanted |= PIPE_WAKE_READ;
905 pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
906 }
907 dev->status = 0;
908 break;
909
910 case PIPE_CMD_WAKE_ON_WRITE:
911 DD("%s: CMD_WAKE_ON_WRITE channel=0x%x", __FUNCTION__, dev->channel);
912 if ((pipe->wanted & PIPE_WAKE_WRITE) == 0) {
913 pipe->wanted |= PIPE_WAKE_WRITE;
914 pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
915 }
916 dev->status = 0;
917 break;
918
919 default:
920 D("%s: command=%d (0x%x)\n", __FUNCTION__, command, command);
921 }
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100922}
923
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200924static void pipe_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100925{
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200926 PipeDevice *s = (PipeDevice *)opaque;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100927
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200928 switch (offset) {
929 case PIPE_REG_COMMAND:
930 DR("%s: command=%d (0x%x)", __FUNCTION__, value, value);
931 pipeDevice_doCommand(s, value);
932 break;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100933
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200934 case PIPE_REG_SIZE:
935 DR("%s: size=%d (0x%x)", __FUNCTION__, value, value);
936 s->size = value;
937 break;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100938
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200939 case PIPE_REG_ADDRESS:
940 DR("%s: address=%d (0x%x)", __FUNCTION__, value, value);
941 s->address = value;
942 break;
943
944 case PIPE_REG_CHANNEL:
945 DR("%s: channel=%d (0x%x)", __FUNCTION__, value, value);
946 s->channel = value;
947 break;
948
949 default:
950 D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset,
951 offset, value, value);
952 break;
David 'Digit' Turner89217f52011-03-21 17:51:03 +0100953 }
David 'Digit' Turnerd4d688e2011-04-26 18:09:17 +0200954}
955
956/* I/O read */
957static uint32_t pipe_dev_read(void *opaque, target_phys_addr_t offset)
958{
959 PipeDevice *dev = (PipeDevice *)opaque;
960
961 switch (offset) {
962 case PIPE_REG_STATUS:
963 DR("%s: REG_STATUS status=%d (0x%x)", __FUNCTION__, dev->status, dev->status);
964 return dev->status;
965
966 case PIPE_REG_CHANNEL:
967 if (dev->signaled_pipes != NULL) {
968 Pipe* pipe = dev->signaled_pipes;
969 DR("%s: channel=0x%x wanted=%d", __FUNCTION__,
970 pipe->channel, pipe->wanted);
971 dev->wakes = pipe->wanted;
972 pipe->wanted = 0;
973 dev->signaled_pipes = pipe->next_waked;
974 pipe->next_waked = NULL;
975 if (dev->signaled_pipes == NULL) {
976 goldfish_device_set_irq(&dev->dev, 0, 0);
977 DD("%s: lowering IRQ", __FUNCTION__);
978 }
979 return pipe->channel;
980 }
981 DR("%s: no signaled channels", __FUNCTION__);
982 return 0;
983
984 case PIPE_REG_WAKES:
985 DR("%s: wakes %d", __FUNCTION__, dev->wakes);
986 return dev->wakes;
987
988 default:
989 D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset);
990 }
991 return 0;
992}
993
994static CPUReadMemoryFunc *pipe_dev_readfn[] = {
995 pipe_dev_read,
996 pipe_dev_read,
997 pipe_dev_read
998};
999
1000static CPUWriteMemoryFunc *pipe_dev_writefn[] = {
1001 pipe_dev_write,
1002 pipe_dev_write,
1003 pipe_dev_write
1004};
1005
1006/* initialize the trace device */
1007void pipe_dev_init()
1008{
1009 PipeDevice *s;
1010
1011 s = (PipeDevice *) qemu_mallocz(sizeof(*s));
1012
1013 s->dev.name = "qemu_pipe";
1014 s->dev.id = -1;
1015 s->dev.base = 0; // will be allocated dynamically
1016 s->dev.size = 0x2000;
1017 s->dev.irq = 0;
1018 s->dev.irq_count = 1;
1019
1020 goldfish_device_add(&s->dev, pipe_dev_readfn, pipe_dev_writefn, s);
1021
1022#if DEBUG_ZERO_PIPE
1023 goldfish_pipe_add_type("zero", NULL, &zeroPipe_funcs);
1024#endif
1025#if DEBUG_PINGPONG_PIPE
1026 goldfish_pipe_add_type("pingpong", NULL, &pingPongPipe_funcs);
1027#endif
1028#if DEBUG_THROTTLE_PIPE
1029 goldfish_pipe_add_type("throttle", NULL, &throttlePipe_funcs);
1030#endif
1031}
1032
1033void
1034goldfish_pipe_wake( void* hwpipe, unsigned flags )
1035{
1036 Pipe* pipe = hwpipe;
1037 Pipe** lookup;
1038 PipeDevice* dev = pipe->device;
1039
1040 DD("%s: channel=0x%x flags=%d", __FUNCTION__, pipe->channel, flags);
1041
1042 /* If not already there, add to the list of signaled pipes */
1043 lookup = pipe_list_findp_waked(&dev->signaled_pipes, pipe);
1044 if (!*lookup) {
1045 pipe->next_waked = dev->signaled_pipes;
1046 dev->signaled_pipes = pipe;
1047 }
1048 pipe->wanted |= (unsigned)flags;
1049
1050 /* Raise IRQ to indicate there are items on our list ! */
1051 goldfish_device_set_irq(&dev->dev, 0, 1);
1052 DD("%s: raising IRQ", __FUNCTION__);
1053}
1054
1055void
1056goldfish_pipe_close( void* hwpipe )
1057{
1058 Pipe* pipe = hwpipe;
1059
1060 D("%s: channel=0x%x (closed=%d)", __FUNCTION__, pipe->channel, pipe->closed);
1061
1062 if (!pipe->closed) {
1063 pipe->closed = 1;
1064 goldfish_pipe_wake( hwpipe, PIPE_WAKE_CLOSED );
1065 }
David 'Digit' Turner89217f52011-03-21 17:51:03 +01001066}