diff options
-rw-r--r-- | CHANGES.TXT | 6 | ||||
-rw-r--r-- | android/avd/info.c | 34 | ||||
-rw-r--r-- | android/hw-qemud.c | 41 | ||||
-rw-r--r-- | android/hw-qemud.h | 41 | ||||
-rw-r--r-- | charpipe.c | 196 | ||||
-rw-r--r-- | charpipe.h | 7 | ||||
-rw-r--r-- | docs/ANDROID-QEMUD.TXT | 241 | ||||
-rw-r--r-- | docs/AUDIO.TXT | 71 | ||||
-rw-r--r-- | docs/CHAR-DEVICES.TXT | 201 | ||||
-rw-r--r-- | proxy/proxy_http_rewriter.c | 46 |
10 files changed, 800 insertions, 84 deletions
diff --git a/CHANGES.TXT b/CHANGES.TXT index 9e5bb50..93d3689 100644 --- a/CHANGES.TXT +++ b/CHANGES.TXT @@ -21,7 +21,9 @@ IMPORTANT CHANGES: - Many features have been integrated from upstream QEMU sources, including the new TCG code generator used by the ARM translator. This should result - in slightly faster execution speed on all supported platforms. + in slightly faster execution speed on all supported platforms. Another + benefit is that you no longer require a specific (and obsolete) version + of GCC to build the emulator. - The emulator now requires that you specify a virtual device name when starting the emulator, prefixed with the '@' sign. For example, to start @@ -95,7 +97,7 @@ OTHER: of the SDK installation path. - Environment variable ANDROID_SDK_HOME can be used to specify the location - of the '.android' data directory. + of the '.android' data directory (which defaults to your $HOME). - A new console command 'avd name' can be used to query the name of the virtual device running in the emulator. Note that it will be '<build>' diff --git a/android/avd/info.c b/android/avd/info.c index 11c5d19..ec74249 100644 --- a/android/avd/info.c +++ b/android/avd/info.c @@ -86,6 +86,12 @@ AvdInfo* android_avdInfo; /* default skin name */ #define SKIN_DEFAULT "HVGA" +/* the config.ini key that is used to indicate the absolute path + * to the SD Card image file, if you don't want to place it in + * the content directory. + */ +#define SDCARD_PATH "sdcard.path" + /* certain disk image files are mounted read/write by the emulator * to ensure that several emulators referencing the same files * do not corrupt these files, we need to lock them and respond @@ -591,7 +597,12 @@ imageLoader_load( ImageLoader* l, { const char* path = NULL; - /* first, check user-provided path */ + /* set image state */ + l->pState[0] = (flags & IMAGE_DONT_LOCK) == 0 + ? IMAGE_STATE_MUSTLOCK + : IMAGE_STATE_READONLY; + + /* check user-provided path */ path = l->params->forcePaths[l->id]; if (path != NULL) { imageLoader_setPath(l, path); @@ -696,7 +707,7 @@ _getImagePaths(AvdInfo* i, AvdInfoParams* params ) * and use it. */ imageLoader_set ( l, AVD_IMAGE_INITSYSTEM ); - imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK ); + imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK ); /* the data partition - this one is special because if it * is missing, we need to copy the initial image file into it. @@ -1031,6 +1042,24 @@ _getSkin( AvdInfo* i, AvdInfoParams* params ) return 0; } +/* If the user didn't explicitely provide an SD Card path, + * check the SDCARD_PATH key in config.ini and use that if + * available. + */ +static void +_getSDCardPath( AvdInfo* i, AvdInfoParams* params ) +{ + const char* path; + + if (params->forcePaths[AVD_IMAGE_SDCARD] != NULL) + return; + + path = iniFile_getString(i->configIni, SDCARD_PATH); + if (path == NULL) + return; + + params->forcePaths[AVD_IMAGE_SDCARD] = path; +} AvdInfo* avdInfo_new( const char* name, AvdInfoParams* params ) @@ -1058,6 +1087,7 @@ avdInfo_new( const char* name, AvdInfoParams* params ) * obsolete SDKs. */ _getSearchPaths(i); + _getSDCardPath(i, params); /* don't need this anymore */ iniFile_free(i->rootIni); diff --git a/android/hw-qemud.c b/android/hw-qemud.c index bebf221..25d8717 100644 --- a/android/hw-qemud.c +++ b/android/hw-qemud.c @@ -99,26 +99,26 @@ * internal unique channel number > 0, then sends a connection * initiation request to the emulator (i.e. through channel 0): * - * connect:<hxid>:<name> + * connect:<id>:<name> * - * where <name> is the service name, and <hxid> is a 4-hexchar + * where <name> is the service name, and <id> is a 2-hexchar * number corresponding to the channel number. * * * in case of success, the emulator responds through channel 0 * with: * - * ok:connect:<hxid> + * ok:connect:<id> * * after this, all messages between the client and the emulator * are passed in pass-through mode. If the client closes the * connection, qemud sends the following to the emulator: * - * disconnect:<hxid> + * disconnect:<id> * * * if the emulator refuses the service connection, it will * send the following through channel 0: * - * ko:connect:<hxid>:reason-for-failure + * ko:connect:<id>:reason-for-failure * * * any command sent through channel 0 to the emulator that is * not properly recognized will be answered by: @@ -562,7 +562,7 @@ qemud_client_disconnect( void* opaque ) /* send a disconnect command to the daemon */ if (c->channel > 0) { char tmp[128], *p=tmp, *end=p+sizeof(tmp); - p = bufprint(tmp, end, "disconnect:%04x", c->channel); + p = bufprint(tmp, end, "disconnect:%02x", c->channel); qemud_serial_send(c->serial, 0, 0, (uint8_t*)tmp, p-tmp); } @@ -837,7 +837,7 @@ qemud_multiplexer_control_recv( void* opaque, /* handle connection attempts. * the client message must be "connect:<service-name>:<id>" - * where <id> is a 4-char hexadecimal string, which must be > 0 + * where <id> is a 2-char hexadecimal string, which must be > 0 */ if (msglen > 8 && !memcmp(msg, "connect:", 8)) { @@ -846,16 +846,16 @@ qemud_multiplexer_control_recv( void* opaque, char* q; q = strchr(service_name, ':'); - if (q == NULL || q+5 != (char*)msgend) { + if (q == NULL || q+3 != (char*)msgend) { D("%s: malformed connect message: '%.*s' (offset=%d)", __FUNCTION__, msglen, (const char*)msg, q ? q-(char*)msg : -1); return; } *q++ = 0; /* zero-terminate service name */ - channel = hex2int((uint8_t*)q, 4); + channel = hex2int((uint8_t*)q, 2); if (channel <= 0) { D("%s: malformed channel id '%.*s", - __FUNCTION__, 4, q); + __FUNCTION__, 2, q); return; } @@ -867,13 +867,13 @@ qemud_multiplexer_control_recv( void* opaque, if (ret < 0) { if (ret == -1) { /* could not connect */ - p = bufprint(tmp, end, "ko:connect:%04x:unknown service", channel); + p = bufprint(tmp, end, "ko:connect:%02x:unknown service", channel); } else { - p = bufprint(tmp, end, "ko:connect:%04x:service busy", channel); + p = bufprint(tmp, end, "ko:connect:%02x:service busy", channel); } } else { - p = bufprint(tmp, end, "ok:connect:%04x", channel); + p = bufprint(tmp, end, "ok:connect:%02x", channel); } qemud_serial_send(mult->serial, 0, 0, (uint8_t*)tmp, p-tmp); return; @@ -881,13 +881,13 @@ qemud_multiplexer_control_recv( void* opaque, /* handle client disconnections, * this message arrives when the client has closed the connection. - * format: "disconnect:<id>" where <id> is a 4-hex channel id > 0 + * format: "disconnect:<id>" where <id> is a 2-hex channel id > 0 */ - if (msglen == 15 && !memcmp(msg, "disconnect:", 11)) { - int channel_id = hex2int(msg+11, 4); + if (msglen == 13 && !memcmp(msg, "disconnect:", 11)) { + int channel_id = hex2int(msg+11, 2); if (channel_id <= 0) { D("%s: malformed disconnect channel id: '%.*s'", - __FUNCTION__, 4, msg+11); + __FUNCTION__, 2, msg+11); return; } qemud_multiplexer_disconnect(mult, channel_id); @@ -1189,6 +1189,11 @@ android_qemud_get_channel( const char* name, CharDriverState* *pcs ) int android_qemud_set_channel( const char* name, CharDriverState* peer_cs ) { - qemud_service_register(name, 1, peer_cs, _qemud_char_service_connect); + CharDriverState* char_buffer = qemu_chr_open_buffer(peer_cs); + + if (char_buffer == NULL) + return -1; + + qemud_service_register(name, 1, char_buffer, _qemud_char_service_connect); return 0; } diff --git a/android/hw-qemud.h b/android/hw-qemud.h index 7101c2a..5b45a94 100644 --- a/android/hw-qemud.h +++ b/android/hw-qemud.h @@ -14,30 +14,8 @@ #include "qemu-common.h" -/* recent versions of the emulated Android system contains a background - * daemon, named 'qemud', which runs as root and opens /dev/ttyS0 - * - * its purpose is to multiplex several communication channels between - * the emulator and the system through a single serial port. - * - * each channel will be connected to a qemud-created unix socket on the - * system, and to either a emulated character device or other facility in - * the emulator. - * - * +--------> /dev/socket/qemud_gsm - * emulated GSM <-----+ ______|_ - * | emulated | | - * +====> /dev/ttyS0 <===>| qemud |------> /dev/socket/qemud_gps - * | |________| - * emulated GPS <-----+ | - * | +---------> other - * | - * other <--------------+ - * - * - * this is done to overcome specific permission problems, as well as to add various - * features that would require special kernel drivers otherwise even though they - * only need a simple character channel. +/* Support for the qemud-based 'services' in the emulator. + * Please read docs/ANDROID-QEMUD.TXT to understand what this is about. */ /* initialize the qemud support code in the emulator @@ -50,14 +28,21 @@ extern void android_qemud_init( void ); */ extern CharDriverState* android_qemud_get_cs( void ); -/* return the character driver state corresponding to a named qemud communication - * channel. this can be used to send/data the channel. +/* returns in '*pcs' a CharDriverState object that will be connected to + * a single client in the emulated system for a given named service. + * + * this is only used to connect GPS and GSM service clients to the + * implementation that requires a CharDriverState object for legacy + * reasons. + * * returns 0 on success, or -1 in case of error */ extern int android_qemud_get_channel( const char* name, CharDriverState* *pcs ); -/* set the character driver state for a given qemud communication channel. this - * is used to attach the channel to an external char driver device directly. +/* set an explicit CharDriverState object for a given qemud communication channel. this + * is used to attach the channel to an external char driver device (e.g. one + * created with "-serial <device>") directly. + * * returns 0 on success, -1 on error */ extern int android_qemud_set_channel( const char* name, CharDriverState* peer_cs ); @@ -253,12 +253,202 @@ qemu_chr_open_charpipe( CharDriverState* *pfirst, CharDriverState* *psecond ) return 0; } +/** This models a charbuffer, an object used to buffer + ** the data that is sent to a given endpoint CharDriverState + ** object. + ** + ** On the other hand, any can_read() / read() request performed + ** by the endpoint will be passed to the CharBuffer's corresponding + ** handlers. + **/ + +typedef struct CharBuffer { + CharDriverState cs[1]; + BipBuffer* bip_first; + BipBuffer* bip_last; + CharDriverState* endpoint; /* NULL if closed */ + char closing; +} CharBuffer; + + +static void +charbuffer_close( CharDriverState* cs ) +{ + CharBuffer* cbuf = cs->opaque; + + while (cbuf->bip_first) { + BipBuffer* bip = cbuf->bip_first; + cbuf->bip_first = bip->next; + bip_buffer_free(bip); + } + cbuf->bip_last = NULL; + cbuf->endpoint = NULL; + + if (cbuf->endpoint != NULL) { + qemu_chr_close(cbuf->endpoint); + cbuf->endpoint = NULL; + } +} + +static int +charbuffer_write( CharDriverState* cs, const uint8_t* buf, int len ) +{ + CharBuffer* cbuf = cs->opaque; + CharDriverState* peer = cbuf->endpoint; + BipBuffer* bip = cbuf->bip_last; + int ret = 0; + + D("%s: writing %d bytes to %p: '%s'", __FUNCTION__, + len, cbuf, quote_bytes( buf, len )); + + if (bip == NULL && peer != NULL) { + /* no buffered data, try to write directly to the peer */ + int size = qemu_chr_write(peer, buf, len); + + if (size < 0) /* just to be safe */ + size = 0; + else if (size > len) + size = len; + + buf += size; + ret += size; + len -= size; + } + + if (len == 0) + return ret; + + /* buffer the remaining data */ + if (bip == NULL) { + bip = bip_buffer_alloc(); + cbuf->bip_first = cbuf->bip_last = bip; + } + + while (len > 0) { + int len2 = cbuffer_write( bip->cb, buf, len ); + + buf += len2; + ret += len2; + len -= len2; + if (len == 0) + break; + + /* ok, we need another buffer */ + cbuf->bip_last = bip_buffer_alloc(); + bip->next = cbuf->bip_last; + bip = cbuf->bip_last; + } + return ret; +} + + +static void +charbuffer_poll( CharBuffer* cbuf ) +{ + CharDriverState* peer = cbuf->endpoint; + + if (peer == NULL) + return; + + while (1) { + BipBuffer* bip = cbuf->bip_first; + uint8_t* base; + int avail; + int size; + + if (bip == NULL) + break; + + avail = cbuffer_read_peek( bip->cb, &base ); + if (avail == 0) { + cbuf->bip_first = bip->next; + if (cbuf->bip_first == NULL) + cbuf->bip_last = NULL; + bip_buffer_free(bip); + continue; + } + + size = qemu_chr_write( peer, base, avail ); + + if (size < 0) /* just to be safe */ + size = 0; + else if (size > avail) + size = avail; + + cbuffer_read_step( bip->cb, size ); + + if (size < avail) + break; + } +} + + +static void +charbuffer_update_handlers( CharDriverState* cs ) +{ + CharBuffer* cbuf = cs->opaque; + + qemu_chr_add_handlers( cbuf->endpoint, + cs->chr_can_read, + cs->chr_read, + cs->chr_event, + cs->handler_opaque ); +} + + +static void +charbuffer_init( CharBuffer* cbuf, CharDriverState* endpoint ) +{ + CharDriverState* cs = cbuf->cs; + + cbuf->bip_first = NULL; + cbuf->bip_last = NULL; + cbuf->endpoint = endpoint; + + cs->chr_write = charbuffer_write; + cs->chr_ioctl = NULL; + cs->chr_send_event = NULL; + cs->chr_close = charbuffer_close; + cs->chr_update_read_handler = charbuffer_update_handlers; + cs->opaque = cbuf; +} + +#define MAX_CHAR_BUFFERS 8 + +static CharBuffer _s_charbuffers[ MAX_CHAR_BUFFERS ]; + +CharDriverState* +qemu_chr_open_buffer( CharDriverState* endpoint ) +{ + CharBuffer* cbuf = _s_charbuffers; + CharBuffer* cbuf_end = cbuf + MAX_CHAR_BUFFERS; + + if (endpoint == NULL) + return NULL; + + for ( ; cbuf < cbuf_end; cbuf++ ) { + if (cbuf->endpoint == NULL) + break; + } + + if (cbuf == cbuf_end) + return NULL; + + charbuffer_init(cbuf, endpoint); + return cbuf->cs; +} + + void charpipe_poll( void ) { CharPipeState* cp = _s_charpipes; CharPipeState* cp_end = cp + MAX_CHAR_PIPES; + CharBuffer* cb = _s_charbuffers; + CharBuffer* cb_end = cb + MAX_CHAR_BUFFERS; + + /* poll the charpipes */ for ( ; cp < cp_end; cp++ ) { CharPipeHalf* half; @@ -270,4 +460,10 @@ charpipe_poll( void ) if (half->peer != NULL) charpipehalf_poll(half); } + + /* poll the charbuffers */ + for ( ; cb < cb_end; cb++ ) { + if (cb->endpoint != NULL) + charbuffer_poll(cb); + } } @@ -20,6 +20,13 @@ */ extern int qemu_chr_open_charpipe( CharDriverState* *pfirst, CharDriverState* *psecond ); +/* create a buffering character driver for a given endpoint. The result will buffer + * anything that is sent to it but cannot be sent to the endpoint immediately. + * On the other hand, if the endpoint calls can_read() or read(), these calls + * are passed immediately to the can_read() or read() handlers of the result. + */ +extern CharDriverState* qemu_chr_open_buffer( CharDriverState* endpoint ); + /* must be called from the main event loop to poll all charpipes */ extern void charpipe_poll( void ); diff --git a/docs/ANDROID-QEMUD.TXT b/docs/ANDROID-QEMUD.TXT new file mode 100644 index 0000000..6415a47 --- /dev/null +++ b/docs/ANDROID-QEMUD.TXT @@ -0,0 +1,241 @@ + THE ANDROID "QEMUD" MULTIPLEXING DAEMON + +I. Overview: +------------ + +The Android system image includes a small daemon program named "qemud" +which is started at boot time. Its purpose is to provide a multiplexing +communication channel between the emulated system and the emulator program +itself. + +Its goal is to allow certain parts of the system to talk directly to the +emulator without requiring special kernel support; this simplifies a lot of +things since it does *not* require: + +- writing/configuring a specific kernel driver +- writing the corresponding hardware emulation code in hw/goldfish_xxxx.c +- dealing with device allocation and permission issues in the emulated system + +The emulator provides 'services' to various parts of the emulated system. +Each service is identified by a name and serves a specific purpose. For +example: + + "gsm" Used to communicate with the emulated GSM modem with + AT commands. + + "gps" Used to receive NMEA sentences broadcasted from the + emulated GPS device. + + "sensors" Used to list the number of emulated sensors, as well as + enable/disable reception of specific sensor events. + + "control" Used to control misc. simple emulated hardware devices + (e.g. vibrator, leds, LCD backlight, etc...) + + +II. Implementation: +------------------- + +Since the "cupcake" platform, this works as follows: + +- A 'qemud client' is any part of the emulated system that wants to talk + to the emulator. It does so by: + + - connecting to the /dev/socket/qemud Unix domain socket + - sending the service name through the socket + - receives two bytes of data, which will be "OK" in case of + success, or "KO" in case of failure. + + After an OK, the same connection can be used to talk directly to the + corresponding service. + + +- The /dev/socket/qemud Unix socket is created by init and owned by the + 'qemud' daemon started at boot by /system/etc/init.goldfish.rc + + The daemon also opens an emulated serial port (e.g. /dev/ttyS1) and + will pass all messages between clients and emulator services. Thus, + everything looks like the following: + + + emulator <==serial==> qemud <---> /dev/socket/qemud <-+--> client1 + | + +--> client2 + + A very simple multiplexing protocol is used on the serial connection: + + offset size description + + 0 2 2-char hex string giving the destination or + source channel + + 2 4 4-char hex string giving the payload size + + 6 n the message payload + + Where each client gets a 'channel' number allocated by the daemon + at connection time. + + Note that packets larger than 65535 bytes cannot be sent directly + through the qemud channel. This is intentional; for large data + communication, the client and service should use a fragmentation + convention that deals with this. + + Zero-sized packets are silently discard by qemud and the emulator and + should normally not appear on the serial port. + + Channel 0 is reserved for control messages between the daemon and the + emulator. These are the following: + + - When a client connects to /dev/socket/qemud and sends a service + name to the daemon, the later sends to the emulator: + + connect:<service>:<id> + + where <service> is the service name, and <id> is a 4-hexchar string + giving the allocated channel index for the client. + + + - The emulator can respond in case of success with: + + ok:connect:<id> + + or, in case of failure, with: + + ok:connect:<id>:<reason> + + where <reason> is a liberal string giving the reason for failure. + It is never sent to clients (which will only receive a "KO") and + is used strictly for debugging purposes. + + - After a succesful connect, all messages between the client and + the corresponding emulator service will be passed through the + corresponding numbered channel. + + But if the client disconnects from the socket, the daemon will + send through channel 0 this message to the emulator: + + disconnect:<id> + + - If an emulator service decides, for some reason, to disconnect + a client, the emulator will send to the daemon (on channel 0): + + disconnect:<id> + + The daemon deals with this gracefully (e.g. it will wait that the + client has read all buffered data in the daemon before closing the + socket, to avoid packet loss). + + - Any other command sent from the daemon to the emulator will result + in the following answer: + + ko:bad command + +- Which exact serial port to open is determined by the emulator at startup + and is passed to the system as a kernel parameter, e.g.: + + android.qemud=ttyS1 + + +- The code to support services and their clients in the emulator is located + in android/hw-qemud.c. This code is heavily commented. + + The daemon's source is in $ROOT/development/emulator/qemud/qemud.c + + The header in $ROOT/hardware/libhardware/include/hardware/qemud.h + can be used by clients to ease connecting and talking to QEMUD-based + services. + + This is used by $ROOT/developement/emulator/sensors/sensors_qemu.c which + implements emulator-specific sensor support in the system by talking to + the "sensors" service provided by the emulator (if available). + + Code in $ROOT/hardware/libhardware_legacy also uses QEMUD-based services. + + +- Certain services also implement a simple framing protocol when exchanging + messages with their clients. The framing happens *after* serial port + multiplexing and looks like: + + offset size description + + 0 4 4-char hex string giving the payload size + + 4 n the message payload + + This is needed because the framing protocol used on the serial port is + not preserved when talking to clients through /dev/socket/qemud. + + Certain services do not need it at all (GSM, GPS) so it is optional and + must be used depending on which service you talk to by clients. + + +III. Legacy 'qemud': +-------------------- + +The system images provided by the 1.0 and 1.1 releases of the Android SDK +implement an older variant of the qemud daemon that uses a slightly +different protocol to communicate with the emulator. + +This is documented here since this explains some subtleties in the +implementation code of android/hw-qemud.c + +The old scheme also used a serial port to allow the daemon and the emulator +to communicate. Besides, the same multiplexing protocol was used to pass +messages through multiple channels. However, several other differences +exist, best illustrated by the following graphics: + + emulator <==serial==> qemud <-+--> /dev/socket/qemud_gsm <--> GSM client + | + +--> /dev/socket/qemud_gps <--> GPS client + | + +--> /dev/socket/qemud_control <--> client(s) + +Now, for the details: + + - instead of a single /dev/socket/qemud, init created several Unix domain + sockets, one per service: + + /dev/socket/qemud_gsm + /dev/socket/qemud_gps + /dev/socket/qemud_control + + note that there is no "sensors" socket in 1.0 and 1.1 + + - the daemon created a de-facto numbered channel for each one of these + services, even if no client did connect to it (only one client could + connect to a given service at a time). + + - at startup, the emulator does query the channel numbers of all services + it implements, e.g. it would send *to* the daemon on channel 0: + + connect:<service> + + where <service> can be one of "gsm", "gps" or "control" + + (Note that on the current implementation, the daemon is sending connection + messages to the emulator instead). + + - the daemon would respond with either: + + ok:connect:<service>:<hxid> + + where <service> would be the service name, and <hxid> a 4-hexchar channel + number (NOTE: 4 chars, not 2). Or with: + + ko:connect:bad name + + +This old scheme was simpler to implement in both the daemon and the emulator +but lacked a lot of flexibility: + + - adding a new service required to modify /system/etc/init.goldfish.rc + as well as the daemon source file (which contained a hard-coded list + of sockets to listen to for client connections). + + - only one client could be connected to a given service at a time, + except for the GPS special case which was a unidirectionnal broadcast + by convention. + +The current implementation moves any service-specific code to the emulator, +only uses a single socket and allows concurrent clients for a all services. diff --git a/docs/AUDIO.TXT b/docs/AUDIO.TXT index 1a25b77..71ec288 100644 --- a/docs/AUDIO.TXT +++ b/docs/AUDIO.TXT @@ -34,7 +34,8 @@ SOUND PLAYBACK DETAILS: Each HWVoiceOut has the following too: - A fixed-size circular buffer of stereo samples (for stereo). - whose format is either floats or int64_t per sample (depending on build configuration). + whose format is either floats or int64_t per sample (depending on build + configuration). - A 'samples' field giving the (constant) number of sample pairs in the stereo buffer. @@ -45,17 +46,32 @@ Each HWVoiceOut has the following too: - A 'rpos' offset into the circular buffer which tells where to read the next samples from the stereo buffer for the next conversion through 'clip'. + + |<----------------- samples ----------------------->| + + | | + + | rpos | + | + |_______v___________________________________________| + | | | + | | | + |_______|___________________________________________| + + - A 'run_out' method that is called each time to tell the output backend to send samples from the stereo buffer to the host sound card/server. This method shall also modify 'rpos' and returns the number of samples 'played'. A more detailed description of this process appears below. - A 'write' method callback used to write a buffer of emulated sound samples from - a SWVoiceOut into the stereo buffer. *All* backends simply call the generic - function audio_pcm_sw_write() to implement this. It's difficult to see why - it's needed at all ? + a SWVoiceOut into the stereo buffer. Currently all backends simply call the generic + function audio_pcm_sw_write() to implement this. + + According to malc, the audio sub-system's original author, this is to allow + a backend to use a platform-specific function to do the same thing if available. - (Similarly, all backends have a 'read' methods which simply calls 'audio_pcm_sw_read') + (Similarly, all input backends have a 'read' methods which simply calls 'audio_pcm_sw_read') Each SWVoiceOut has the following: @@ -67,6 +83,27 @@ Each SWVoiceOut has the following: HWVoiceOut's 'rpos' offset). NOTE: this is a count of samples in the HWVoiceOut stereo buffer, not emulated hardware sound samples, which can have different properties (frequency, size, endianess). + ______________ + | | + | SWVoiceOut2 | + |______________| + ______________ | + | | | + | SWVoiceOut1 | | thsm<N> := total_hw_samples_mixed + |______________| | for SWVoiceOut<N> + | | + | | + |<-----|------------thsm2-->| + | | | + |<---thsm1-------->| | + _______|__________________v________|_______________ + | |111111111111111111| v | + | |222222222222222222222222222| | + |_______|___________________________________________| + ^ + | HWVoiceOut stereo buffer + rpos + - a 'ratio' value, which is the ratio of the target HWVoiceOut's frequency by the SWVoiceOut's frequency, multiplied by (1 << 32), as a 64-bit integer. @@ -86,24 +123,19 @@ Each SWVoiceOut has the following: Here's a small graphics that explains it better: SWVoiceOut: emulated hardware sound buffers: - - | - | (mixed through AUD_write() from user-provided callback) | + | (mixed through AUD_write() called from user-provided + | callback which is itself called on each audio timer + | tick). v - HWVoiceOut: stereo sample circular buffer - | - | (through HWVoiceOut's 'clip' function, invoked from the - | 'run_out' method) + | (sent through HWVoiceOut's 'clip' function, which is + | invoked from the 'run_out' method, also called on each + | audio timer tick) v - backend-specific sound buffers -THERE IS NO COMMON TIMEBASE BETWEEN ALL LAYERS. DON'T EXPECT ANY HIGH-ACCURACY / -LOW-LATENCY IN THIS IMPLEMENTATION. - The function audio_timer() in audio/audio.c is called periodically and it is used as a pulse to perform sound buffer transfers and mixing. More specifically for audio @@ -123,7 +155,7 @@ output voices: It's important to note that the SWVoiceOut callback: -- takes a 'free' parameter which is the number of emulated sound samples that can +- takes a 'free' parameter which is the number of stereo sound samples that can be sent to the hardware stereo buffer (before rate adjustment, i.e. not the number of sound samples in the SWVoiceOut emulated hardware sound buffer). @@ -157,4 +189,7 @@ So, in the end, we have the pseudo-code: SOUND RECORDING DETAILS: ======================== -Things are similar but in reverse order. +Things are similar but in reverse order. I.e. the HWVoiceIn acquires sound samples +in its stereo sound buffer, and the SWVoiceIn objects must consume them as soon as +they can. + diff --git a/docs/CHAR-DEVICES.TXT b/docs/CHAR-DEVICES.TXT new file mode 100644 index 0000000..9805a4a --- /dev/null +++ b/docs/CHAR-DEVICES.TXT @@ -0,0 +1,201 @@ +QEMU CHARACTER "DEVICES" MANAGEMENT + +I. CharDriverState objects: +--------------------------- + +One of the strangest abstraction in QEMU is the "CharDriverState" +(abbreviated here as "CS"). + +The CS is essentially an object used to model a character stream that +can be connected to things like a host serial port, a host network socket, +an emulated device, etc... + +What's really unusual is its interface though, which comes from the fact +that QEMU implements a big event loop with no blocking i/o allowed. You +can see "qemu-char.h" for the full interface, but here we will only describe +a few important functions: + + - qemu_chr_write() is used to try to write data into a CS object. Note that + success is not guaranteed: the function returns the number of bytes that + were really written (which can be 0) and the caller must deal with it. + This is very similar to writing to a non-blocking BSD socket on Unix. + + int qemu_chr_read( CharDriverState* cs, + const uint8_t* data, + int datalen ); + + This function may return -1 in case of error, but this depends entirely + on the underlying implementation (some of them will just return 0 instead). + In practice, this means it's not possible to reliably differentiate between + a "connection reset by peer" and an "operation in progress" :-( + + There is no way to know in advance how many bytes a given CharDriverState + can accept, nor to be notified when its underlying implementation is ready + to accept data again. + + + - qemu_chr_add_handler() is used to add "read" and "event" handlers + to a CS object. We will ignore "events" here and focus on the + "read" part. + + Thing is, you cannot directly read from a CS object. Instead, you provide + two functions that will be called whenever the object has something for + you: + + - a 'can_read' function that shall return the number of bytes + that you are ready to accept from the CharDriverState. Its + interface is: + + typedef int IOCanRWHandler (void* opaque); + + - a 'read' function that will send you bytes from the CharDriverState + + typedef void IOReadHandler (void* opaque, + const uint8_t* data, + int datalen); + + normally, the value of 'datalen' cannot be larger than the result + of a previous 'can_read' call. + + For both callbacks, 'opaque' is a value that you pass to the function + qemu_chr_add_handler() which signature is: + + void qemu_chr_add_handlers(CharDriverState *s, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque); + + - qemu_chr_open() is used to create a new CharDriverState object from a + descriptive string, its interface is: + + CharDriverState* qemu_chr_open(const char* filename); + + there are various formats for acceptable 'filenames', and they correspond + to the parameters of the '-serial' QEMU option described here: + + http://www.nongnu.org/qemu/qemu-doc.html#SEC10 + + For example: + + "/dev/<file>" (Linux and OS X only): + connect to a host character device file (e.g. /dev/ttyS0) + + "file:<filename>": + Write output to a given file (write only) + + "stdio": + Standard input/output + + "udp:[<remote_host>]:<remote_port>[@[<src_ip>]:<src_port>]": + Connect to a UDP socket for both read/write. + + "tcp:[<host>]:<port>[,server][,nowait][,nodelay]" + Connect to a TCP socket either as a client or a server. + + The 'nowait' option is used to avoid waiting for a client + connection. + + The 'nodelay' is used to disable the TCP Nagle algorithm to + improve throughput. + + For Android, a few special names have been added to the internal + implementation and redirect to program functions: + + "android-kmsg": + A CharDriverState that is used to receive kernel log messages + from the emulated /dev/ttyS0 serial port. + + "android-qemud": + A CharDriverState that is used to exchange messages between the + emulator program and the "qemud" multiplexing daemon that runs in + the emulated system. + + The "qemud" daemon is used to allow one or more clients in the + system to connect to various services running in the emulator + program. This is mainly used to bypass the kernel in order to + implement certain features with ease. + + (see docs/ANDROID-QEMUD.TXT for details) + + "android-gsm": + A CharDriverState that is used to connect the emulated system to + a host modem device with the -radio <device> option. Otherwise, + the system uses qemud to connect to the emulator's internal modem + emulation. + + "android-gps": + A CharDriverState that is used to connect the emulated system to a + host GPS device with the -gps <device> option. Otherwise the + system uses qemud to connect to the emulator's internal GPS + emulation. + + +II. CharDriverState users: +-------------------------- + +As described above, a CharDriverState "user" is a piece of code that can write +to a CharDriverState (by calling qemu_chr_write() explicitely) and can also +read from it after registering can_read/read handlers for it through +qemu_chr_add_handlers(). + +Typical examples are the following: + + - The hardware serial port emulation (e.g. hw/goldfish_tty.c) will read data + from the kernel then send it to a CS. It also uses a small buffer that is + used to read data from the CS and send it back to the kernel. + + - The Android emulated modem also uses a CS to talk with its client, + which will in most cases be an emulated serial port. + + +III. CharBuffer objects: +------------------------ + +The Android emulator provides an object called a CharBuffer which acts as +a CharDriverState object that implements a *write* buffer to send data to a +given CS object, called the endpoint. You can create one with: + + #include "charpipe.h" + CharDriverState* qemu_chr_open_buffer( CharDriverState* endpoint ); + +This function returns a new CS object that will buffer in the heap any data +that is sent to it, but cannot be sent to the endpoint yet. On each event loop +iteration, the CharBuffer will try to send data to the endpoint until it +doesn't have any data left. + +This can be useful to simplify certain CS users who don't want to maintain +their own emit buffer. Note that writing to a CharBuffer always succeeds. + +Note also that calling qemu_chr_add_handler() on the CharBuffer will do the +same on the endpoint. Any endpoint-initiated calls to can_read()/read() +callbacks are passed directly to your handler functions. + + +IV. CharPipe objects: +--------------------- + +The Android emulator also provides a convenient abstraction called a "charpipe" +used to connect two CharDriverState users together. For example, this is used +to connect a serial port emulation (in hw/goldfish_tty.c) to the internal +GSM modem emulation (see telephony/modem_driver.c). + +Essentially, a "charpipe" is a bi-directionnal communication pipe whose two +endpoints are both CS objects. You call "qemu_chr_open_pipe()" to create the +pipe, and this function will return the two endpoints to you: + + #include "charpipe.h" + int qemu_chr_open_pipe(CharDriverState* *pfirst, + CharDriverState* *psecond); + +When you write to one end of the pipe (with qemu_chr_write()), the charpipe will +try to write as much data as possible to the other end. Any remaining data is stored +in a heap-allocated buffer. + +The charpipe will try to re-send the buffered data on the next event loop +iteration by calling the can_read/read functions of the corresponding user, +if there is one. + +Note that there is no limit on the amount of data buffered in a charpipe, +and writing to it is never blocking. This simplifies CharDriverState +users who don't need to worry about buffering issues. diff --git a/proxy/proxy_http_rewriter.c b/proxy/proxy_http_rewriter.c index 812bf9c..cb7c94b 100644 --- a/proxy/proxy_http_rewriter.c +++ b/proxy/proxy_http_rewriter.c @@ -62,6 +62,9 @@ ** **/ +/* HttpHeader is a simple structure used to hold a (key,value) + * pair in a linked list. + */ typedef struct HttpHeader { struct HttpHeader* next; const char* key; @@ -104,6 +107,12 @@ http_header_alloc( const char* key, const char* value ) return h; } +/** ************************************************************* + ** + ** HTTP HEADERS LIST + ** + **/ + typedef struct { HttpHeader* first; HttpHeader* last; @@ -166,15 +175,18 @@ typedef enum { HTTP_REQUEST_DELETE, } HttpRequestType; +/* HttpRequest is used both to store information about a specific + * request and the corresponding reply + */ typedef struct { - HttpRequestType req_type; - char* req_method; - char* req_uri; - char* req_version; - char* rep_version; - int rep_code; - char* rep_readable; - HttpHeaderList headers[1]; + HttpRequestType req_type; /* request type */ + char* req_method; /* "GET", "POST", "HEAD", etc... */ + char* req_uri; /* the request URI */ + char* req_version; /* "HTTP/1.0" or "HTTP/1.1" */ + char* rep_version; /* reply version string */ + int rep_code; /* reply code as decimal */ + char* rep_readable; /* human-friendly reply/error message */ + HttpHeaderList headers[1]; /* headers */ } HttpRequest; @@ -641,6 +653,16 @@ rewrite_connection_get_body_length( RewriteConnection* conn, conn->body_length = body_len; } } else { + transfer_encoding = http_request_find_header(r, "Transfer-Encoding"); + if (transfer_encoding && !strcasecmp(transfer_encoding, "Chunked")) { + conn->body_mode = BODY_CHUNKED; + conn->parse_chunk_header = 0; + conn->parse_chunk_trailer = 0; + conn->chunk_length = -1; + conn->chunk_total = 0; + } + } + if (conn->body_mode == BODY_NONE) { char* connection = http_request_find_header(r, "Proxy-Connection"); if (!connection) @@ -658,14 +680,6 @@ rewrite_connection_get_body_length( RewriteConnection* conn, */ conn->body_mode = BODY_UNTIL_CLOSE; } - transfer_encoding = http_request_find_header(r, "Transfer-Encoding"); - if (transfer_encoding && !strcasecmp(transfer_encoding, "Chunked")) { - conn->body_mode = BODY_CHUNKED; - conn->parse_chunk_header = 0; - conn->parse_chunk_trailer = 0; - conn->chunk_length = -1; - conn->chunk_total = 0; - } D("%s: body_length=%lld body_mode=%s", root->name, conn->body_length, body_mode_str[conn->body_mode]); |