aboutsummaryrefslogtreecommitdiffstats
path: root/android
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-27 15:31:05 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-27 15:31:05 -0700
commit543053775a4375b1f1901eeeef749270a10f8734 (patch)
tree8e71dca4d5fa6b44ef6291de8a13eab1ec04e191 /android
parentd0cdbf4c54bae19971050434396853f15844a4bb (diff)
parent84adcaa1435d78778250b819763429dc332c7b44 (diff)
downloadexternal_qemu-543053775a4375b1f1901eeeef749270a10f8734.zip
external_qemu-543053775a4375b1f1901eeeef749270a10f8734.tar.gz
external_qemu-543053775a4375b1f1901eeeef749270a10f8734.tar.bz2
Merge commit 'korg/cupcake'
Diffstat (limited to 'android')
-rw-r--r--android/avd/hardware-properties.ini7
-rw-r--r--android/avd/hw-config-defs.h7
-rw-r--r--android/avd/info.c13
-rw-r--r--android/cmdline-options.h5
-rw-r--r--android/help.c16
-rw-r--r--android/hw-control.c147
-rw-r--r--android/hw-qemud.c1194
-rw-r--r--android/hw-qemud.h (renamed from android/qemud.h)36
-rw-r--r--android/hw-sensors.c450
-rw-r--r--android/hw-sensors.h48
-rw-r--r--android/main.c40
-rw-r--r--android/qemud.c456
-rw-r--r--android/skin/window.c6
-rw-r--r--android/utils/debug.h1
14 files changed, 1827 insertions, 599 deletions
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini
index 3093b16..60a7493 100644
--- a/android/avd/hardware-properties.ini
+++ b/android/avd/hardware-properties.ini
@@ -96,6 +96,13 @@ default = yes
abstract = Battery support
description = Whether the device can run on a battery.
+# Accelerometer (used for auto-rotation)
+name = hw.accelerometer
+type = boolean
+default = yes
+abstract = Accelerometer
+description = Whether there is an accelerometer in the device.
+
# Audio input
name = hw.audioInput
type = boolean
diff --git a/android/avd/hw-config-defs.h b/android/avd/hw-config-defs.h
index fd85bf2..3e7cec6 100644
--- a/android/avd/hw-config-defs.h
+++ b/android/avd/hw-config-defs.h
@@ -95,6 +95,13 @@ HWCFG_BOOL(
"Whether the device can run on a battery.")
HWCFG_BOOL(
+ hw_accelerometer,
+ "hw.accelerometer",
+ "yes",
+ "Accelerometer",
+ "Whether there is an accelerometer in the device.")
+
+HWCFG_BOOL(
hw_audioInput,
"hw.audioInput",
"yes",
diff --git a/android/avd/info.c b/android/avd/info.c
index afff342..11c5d19 100644
--- a/android/avd/info.c
+++ b/android/avd/info.c
@@ -1242,7 +1242,7 @@ _getBuildSkin( AvdInfo* i, AvdInfoParams* params )
if (!skinName) {
/* the (current) default skin name for the build system */
skinName = SKIN_DEFAULT;
- DD("selecting default skin name '%s'", skinName);
+ D("selecting default skin name '%s'", skinName);
}
i->skinName = ASTRDUP(skinName);
@@ -1354,6 +1354,17 @@ avdInfo_getHwConfig( AvdInfo* i, AndroidHwConfig* hw )
if (ini != i->configIni)
iniFile_free(ini);
+ /* special product-specific hardware configuration */
+ if (i->androidOut != NULL)
+ {
+ char* p = strrchr(i->androidOut, '/');
+ if (p != NULL && p[0] != 0) {
+ if (p[1] == 's') {
+ hw->hw_keyboard = 0;
+ }
+ }
+ }
+
return ret;
}
diff --git a/android/cmdline-options.h b/android/cmdline-options.h
index c57ed10..5291177 100644
--- a/android/cmdline-options.h
+++ b/android/cmdline-options.h
@@ -16,7 +16,7 @@
#endif
/* required to ensure that the CONFIG_XXX macros are properly defined */
-//XXX#include "config.h"
+#include "android/config/config.h"
/* Some options acts like flags, while others must be followed by a parameter
* string. Nothing really new here.
@@ -126,11 +126,12 @@ OPT_PARAM( shell_serial, "<device>", "specific character device for root shell"
OPT_FLAG ( old_system, "support old (pre 1.4) system images" )
OPT_PARAM( tcpdump, "<file>", "capture network packets to file" )
+OPT_PARAM( bootchart, "<timeout>", "enable bootcharting")
+
#ifdef CONFIG_NAND_LIMITS
OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds" )
#endif
-OPT_PARAM( bootchart, "<timeout>", "enable bootcharting")
#undef CFG_FLAG
#undef CFG_PARAM
diff --git a/android/help.c b/android/help.c
index ae4dbc0..391b63d 100644
--- a/android/help.c
+++ b/android/help.c
@@ -35,8 +35,10 @@ help_virtual_device( stralloc_t* out )
" The 'android' helper tool can be used to manage virtual devices.\n"
" For example:\n\n"
- " android avd -- creates a new virtual device.\n"
- " android list -- list all virtual devices available.\n\n"
+ " android create avd -n <name> -t 1 # creates a new virtual device.\n"
+ " android list avd # list all virtual devices available.\n\n"
+
+ " Try 'android --help' for more commands.\n\n"
" Each AVD really corresponds to a content directory which stores\n"
" persistent and writable disk images as well as configuration files.\n"
@@ -446,14 +448,14 @@ static void
help_avd(stralloc_t* out)
{
PRINTF(
- " use '-avd <name>' to start the emulator program with a given avd,\n"
- " where <name> must correspond to the name of one of the\n"
- " Android Virtual Devices available on your host.\n\n"
+ " use '-avd <name>' to start the emulator program with a given Android\n"
+ " Virtual Device (a.k.a. AVD), where <name> must correspond to the name\n"
+ " of one of the existing AVDs available on your host machine.\n\n"
+
+ "See -help-virtual-device to learn how to create/list/manage AVDs.\n\n"
" As a special convenience, using '@<name>' is equivalent to using\n"
" '-avd <name>'.\n\n"
-
- " For more information about virtual devices, see -help-virtual-device.\n"
);
}
diff --git a/android/hw-control.c b/android/hw-control.c
index 681e85b..a82a92a 100644
--- a/android/hw-control.c
+++ b/android/hw-control.c
@@ -22,7 +22,7 @@
*/
#include "android/hw-control.h"
#include "cbuffer.h"
-#include "android/qemud.h"
+#include "android/hw-qemud.h"
#include "android/utils/misc.h"
#include "android/utils/debug.h"
#include "qemu-char.h"
@@ -40,104 +40,37 @@
#define T(...) ((void)0)
#endif
-static void* hw_control_client;
-static AndroidHwControlFuncs hw_control_funcs;
-
-#define BUFFER_SIZE 512
-
typedef struct {
- CharDriverState* cs;
- int overflow;
- int wanted;
- CBuffer input[1];
- char input_0[ BUFFER_SIZE ];
- /* note: 1 more byte to zero-terminate the query */
- char query[ BUFFER_SIZE+1 ];
+ void* client;
+ AndroidHwControlFuncs client_funcs;
+ QemudService* service;
} HwControl;
-/* forward */
-static void hw_control_do_query( HwControl* h,
- uint8_t* query,
- int querylen );
+/* handle query */
+static void hw_control_do_query( HwControl* h, uint8_t* query, int querylen );
+/* called when a qemud client sends a command */
static void
-hw_control_init( HwControl* h, CharDriverState* cs )
+_hw_control_qemud_client_recv( void* opaque,
+ uint8_t* msg,
+ int msglen )
{
- h->cs = cs;
- h->overflow = 0;
- h->wanted = 0;
- cbuffer_reset( h->input, h->input_0, sizeof h->input_0 );
+ hw_control_do_query(opaque, msg, msglen);
}
-static int
-hw_control_can_read( void* _hw )
+/* called when a qemud client connects to the service */
+static QemudClient*
+_hw_control_qemud_connect( void* opaque, QemudService* service, int channel )
{
- HwControl* h = _hw;
- return cbuffer_write_avail( h->input );
-}
-
-static void
-hw_control_read( void* _hw, const uint8_t* data, int len )
-{
- HwControl* h = _hw;
- CBuffer* input = h->input;
-
- T("%s: %4d '%.*s'", __FUNCTION__, len, len, data);
-
- cbuffer_write( input, data, len );
-
- while ( input->count > 0 )
- {
- /* skip over unwanted data, if any */
- while (h->overflow > 0) {
- uint8_t* dummy;
- int avail = cbuffer_read_peek( input, &dummy );
-
- if (avail == 0)
- return;
-
- if (avail > h->overflow)
- avail = h->overflow;
-
- cbuffer_read_step( input, avail );
- h->overflow -= avail;
- }
+ QemudClient* client;
- /* all incoming messages are made of a 4-byte hexchar sequence giving */
- /* the length of the following payload */
- if (h->wanted == 0)
- {
- char header[4];
- int len;
+ client = qemud_client_new( service, channel,
+ opaque,
+ _hw_control_qemud_client_recv,
+ NULL );
- if (input->count < 4)
- return;
-
- cbuffer_read( input, header, 4 );
- len = hex2int( (uint8_t*)header, 4 );
- if (len >= 0) {
- /* if the message is too long, skip it */
- if (len > input->size) {
- T("%s: skipping oversized message (%d > %d)",
- __FUNCTION__, len, input->size);
- h->overflow = len;
- } else {
- T("%s: waiting for %d bytes", __FUNCTION__, len);
- h->wanted = len;
- }
- }
- }
- else
- {
- if (input->count < h->wanted)
- break;
-
- cbuffer_read( input, h->query, h->wanted );
- h->query[h->wanted] = 0;
- hw_control_do_query( h, (uint8_t*)h->query, h->wanted );
- h->wanted = 0;
- }
- }
+ qemud_client_set_framing(client, 1);
+ return client;
}
@@ -160,11 +93,11 @@ hw_control_do_query( HwControl* h,
{
uint8_t* q;
- D("%s: query %4d '%.*s'", __FUNCTION__, querylen, querylen, query );
+ T("%s: query %4d '%.*s'", __FUNCTION__, querylen, querylen, query );
q = if_starts_with( query, querylen, "power:light:brightness:" );
if (q != NULL) {
- if (hw_control_funcs.light_brightness) {
+ if (h->client_funcs.light_brightness) {
char* qq = strchr((const char*)q, ':');
int value;
if (qq == NULL) {
@@ -173,32 +106,30 @@ hw_control_do_query( HwControl* h,
}
*qq++ = 0;
value = atoi(qq);
- hw_control_funcs.light_brightness( hw_control_client, (char*)q, value );
+ h->client_funcs.light_brightness( h->client, (char*)q, value );
}
return;
}
}
+static void
+hw_control_init( HwControl* control,
+ void* client,
+ const AndroidHwControlFuncs* client_funcs )
+{
+ control->client = client;
+ control->client_funcs = client_funcs[0];
+ control->service = qemud_service_register( "hw-control", 0,
+ control,
+ _hw_control_qemud_connect );
+}
+
void
android_hw_control_init( void* opaque, const AndroidHwControlFuncs* funcs )
{
- static CharDriverState* hw_control_cs;
- static HwControl hwstate[1];
-
- if (hw_control_cs == NULL) {
- CharDriverState* cs;
- if ( android_qemud_get_channel( ANDROID_QEMUD_CONTROL, &cs ) < 0 ) {
- derror( "could not create hardware control charpipe" );
- exit(1);
- }
-
- hw_control_cs = cs;
- hw_control_init( hwstate, cs );
- qemu_chr_add_handlers( cs, hw_control_can_read, hw_control_read, NULL, hwstate );
+ static HwControl hwstate[1];
- D("%s: hw-control char pipe initialized", __FUNCTION__);
- }
- hw_control_client = opaque;
- hw_control_funcs = funcs[0];
+ hw_control_init(hwstate, opaque, funcs);
+ D("%s: hw-control qemud handler initialized", __FUNCTION__);
}
diff --git a/android/hw-qemud.c b/android/hw-qemud.c
new file mode 100644
index 0000000..bebf221
--- /dev/null
+++ b/android/hw-qemud.c
@@ -0,0 +1,1194 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+#include "android/hw-qemud.h"
+#include "android/utils/debug.h"
+#include "android/utils/misc.h"
+#include "android/utils/system.h"
+#include "android/utils/bufprint.h"
+#include "qemu-char.h"
+#include "charpipe.h"
+#include "cbuffer.h"
+
+#define D(...) VERBOSE_PRINT(qemud,__VA_ARGS__)
+#define D_ACTIVE VERBOSE_CHECK(qemud)
+
+/* the T(...) macro is used to dump traffic */
+#define T_ACTIVE 0
+
+#if T_ACTIVE
+#define T(...) VERBOSE_PRINT(qemud,__VA_ARGS__)
+#else
+#define T(...) ((void)0)
+#endif
+
+/* max serial MTU. Don't change this without modifying
+ * development/emulator/qemud/qemud.c as well.
+ */
+#define MAX_SERIAL_PAYLOAD 4000
+
+/* max framed data payload. Must be < (1 << 16)
+ */
+#define MAX_FRAME_PAYLOAD 65535
+
+
+/*
+ * the qemud daemon program is only used within Android as a bridge
+ * between the emulator program and the emulated system. it really works as
+ * a simple stream multiplexer that works as follows:
+ *
+ * - qemud is started by init following instructions in
+ * /system/etc/init.goldfish.rc (i.e. it is never started on real devices)
+ *
+ * - qemud communicates with the emulator program through a single serial
+ * port, whose name is passed through a kernel boot parameter
+ * (e.g. android.qemud=ttyS1)
+ *
+ * - qemud binds one unix local stream socket (/dev/socket/qemud, created
+ * by init through /system/etc/init.goldfish.rc).
+ *
+ *
+ * emulator <==serial==> qemud <---> /dev/socket/qemud <-+--> client1
+ * |
+ * +--> client2
+ *
+ * - the protocol used on the serial connection is pretty simple:
+ *
+ * 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
+ *
+ * for emulator->system messages, the 'channel' index indicates
+ * to which channel the payload must be sent
+ *
+ * for system->emulator messages, the 'channel' index indicates from
+ * which channel the payload comes from.
+ *
+ * the payload size is limited to MAX_SERIAL_PAYLOAD bytes.
+ *
+ * - communication between a client and qemud is stream based, but supports
+ * an optional framing mode which can be used to send packets larger than
+ * MAX_SERIAL_PAYLOAD bytes and provides packet bounding.
+ *
+ * offset size description
+ * 0 4 4-char hex string giving the payload size
+ * 6 n the message payload
+ *
+ * The size of the payload is then limited to 65535 bytes.
+ *
+ * - the special channel index 0 is used by the emulator and qemud only.
+ * other channel numbers correspond to clients. More specifically,
+ * connection are created like this:
+ *
+ * * the client connects to /dev/socket/qemud
+ *
+ * * the client sends the service name through the socket, as
+ * <service-name>
+ *
+ * * qemud creates a "Client" object internally, assigns it an
+ * internal unique channel number > 0, then sends a connection
+ * initiation request to the emulator (i.e. through channel 0):
+ *
+ * connect:<hxid>:<name>
+ *
+ * where <name> is the service name, and <hxid> is a 4-hexchar
+ * number corresponding to the channel number.
+ *
+ * * in case of success, the emulator responds through channel 0
+ * with:
+ *
+ * ok:connect:<hxid>
+ *
+ * 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>
+ *
+ * * if the emulator refuses the service connection, it will
+ * send the following through channel 0:
+ *
+ * ko:connect:<hxid>:reason-for-failure
+ *
+ * * any command sent through channel 0 to the emulator that is
+ * not properly recognized will be answered by:
+ *
+ * ko:unknown command
+ *
+ * Internally, the daemon maintains a "Client" object for each client
+ * connection (i.e. accepting socket connection).
+ */
+
+/*
+ * IMPLEMENTATION DETAILS:
+ *
+ * We use one charpipe to connect the emulated serial port to the 'QemudSerial'
+ * object. This object is used to receive data from the serial port, and
+ * unframe messages (i.e. extract payload length + channel id from header,
+ * then the payload itself), before sending them to a generic receiver.
+ *
+ * The QemudSerial object can also be used to send messages to the daemon
+ * through the serial port (see qemud_serial_send())
+ *
+ * The multiplexer is connected to one or more 'service' objects.
+ * are themselves connected through a charpipe to an emulated device or
+ * control sub-module in the emulator.
+ *
+ * tty <==charpipe==> QemudSerial ---> QemudMultiplexer ----> QemudClient
+ * ^ |
+ * | |
+ * +--------------------------------------+
+ *
+ */
+
+/** HANDLING INCOMING DATA FRAMES
+ **/
+
+typedef struct QemudSink {
+ int len;
+ int size;
+ uint8_t* buff;
+} QemudSink;
+
+static void
+qemud_sink_reset( QemudSink* ss, int size, uint8_t* buffer )
+{
+ ss->len = 0;
+ ss->size = size;
+ ss->buff = buffer;
+}
+
+static int
+qemud_sink_fill( QemudSink* ss, const uint8_t* *pmsg, int *plen)
+{
+ int avail = ss->size - ss->len;
+
+ if (avail <= 0)
+ return 1;
+
+ if (avail > *plen)
+ avail = *plen;
+
+ memcpy(ss->buff + ss->len, *pmsg, avail);
+ *pmsg += avail;
+ *plen -= avail;
+ ss->len += avail;
+
+ return (ss->len == ss->size);
+}
+
+static int
+qemud_sink_needed( QemudSink* ss )
+{
+ return ss->size - ss->len;
+}
+
+/** HANDLING SERIAL PORT CONNECTION
+ **/
+
+/* The QemudSerial object receives data from the serial port charpipe.
+ * It parses the header to extract the channel id and payload length,
+ * then the message itself.
+ *
+ * Incoming messages are sent to a generic receiver identified by
+ * the 'recv_opaque' and 'recv_func' parameters to qemud_serial_init()
+ *
+ * It also provides qemud_serial_send() which can be used to send
+ * messages back through the serial port.
+ */
+
+#define HEADER_SIZE 6
+
+#define LENGTH_OFFSET 2
+#define LENGTH_SIZE 4
+
+#define CHANNEL_OFFSET 0
+#define CHANNEL_SIZE 2
+
+/* length of the framed header */
+#define FRAME_HEADER_SIZE 4
+
+#define BUFFER_SIZE MAX_SERIAL_PAYLOAD
+
+/* out of convenience, the incoming message is zero-terminated
+ * and can be modified by the receiver (e.g. for tokenization).
+ */
+typedef void (*QemudSerialReceive)( void* opaque, int channel, uint8_t* msg, int msglen);
+
+typedef struct QemudSerial {
+ CharDriverState* cs; /* serial charpipe endpoint */
+
+ /* managing incoming packets from the serial port */
+ ABool need_header;
+ int overflow;
+ int in_size;
+ int in_channel;
+ QemudSink header[1];
+ QemudSink payload[1];
+ uint8_t data0[MAX_SERIAL_PAYLOAD+1];
+
+ /* receiver */
+ QemudSerialReceive recv_func; /* receiver callback */
+ void* recv_opaque; /* receiver user-specific data */
+} QemudSerial;
+
+
+/* called by the charpipe to see how much bytes can be
+ * read from the serial port.
+ */
+static int
+qemud_serial_can_read( void* opaque )
+{
+ QemudSerial* s = opaque;
+
+ if (s->overflow > 0) {
+ return s->overflow;
+ }
+
+ /* if in_size is 0, we're reading the header */
+ if (s->need_header)
+ return qemud_sink_needed(s->header);
+
+ /* otherwise, we're reading the payload */
+ return qemud_sink_needed(s->payload);
+}
+
+/* called by the charpipe to read data from the serial
+ * port. 'len' cannot be more than the value returned
+ * by 'qemud_serial_can_read'.
+ */
+static void
+qemud_serial_read( void* opaque, const uint8_t* from, int len )
+{
+ QemudSerial* s = opaque;
+
+ T("%s: received %3d bytes: '%s'", __FUNCTION__, len, quote_bytes((const void*)from, len));
+
+ while (len > 0) {
+ int avail;
+
+ /* skip overflow bytes */
+ if (s->overflow > 0) {
+ avail = s->overflow;
+ if (avail > len)
+ avail = len;
+
+ from += avail;
+ len -= avail;
+ continue;
+ }
+
+ /* read header if needed */
+ if (s->need_header) {
+ if (!qemud_sink_fill(s->header, (const uint8_t**)&from, &len))
+ break;
+
+ /* extract payload length + channel id */
+ s->in_size = hex2int( s->data0 + LENGTH_OFFSET, LENGTH_SIZE );
+ s->in_channel = hex2int( s->data0 + CHANNEL_OFFSET, CHANNEL_SIZE );
+ s->header->len = 0;
+
+ if (s->in_size <= 0 || s->in_channel < 0) {
+ D("%s: bad header: '%.*s'", __FUNCTION__, HEADER_SIZE, s->data0);
+ continue;
+ }
+
+ if (s->in_size > MAX_SERIAL_PAYLOAD) {
+ D("%s: ignoring huge serial packet: length=%d channel=%1",
+ __FUNCTION__, s->in_size, s->in_channel);
+ s->overflow = s->in_size;
+ continue;
+ }
+
+ /* prepare 'in_data' for payload */
+ s->need_header = 0;
+ qemud_sink_reset(s->payload, s->in_size, s->data0);
+ }
+
+ /* read payload bytes */
+ if (!qemud_sink_fill(s->payload, &from, &len))
+ break;
+
+ /* zero-terminate payload, then send it to receiver */
+ s->payload->buff[s->payload->size] = 0;
+ D("%s: channel=%2d len=%3d '%s'", __FUNCTION__,
+ s->in_channel, s->payload->size,
+ quote_bytes((const void*)s->payload->buff, s->payload->size));
+
+ s->recv_func( s->recv_opaque, s->in_channel, s->payload->buff, s->payload->size );
+
+ /* prepare for new header */
+ s->need_header = 1;
+ }
+}
+
+/* intialize a QemudSerial object with a charpipe endpoint
+ * and a receiver.
+ */
+static void
+qemud_serial_init( QemudSerial* s,
+ CharDriverState* cs,
+ QemudSerialReceive recv_func,
+ void* recv_opaque )
+{
+ s->cs = cs;
+ s->recv_func = recv_func;
+ s->recv_opaque = recv_opaque;
+ s->need_header = 1;
+ s->overflow = 0;
+
+ qemud_sink_reset( s->header, HEADER_SIZE, s->data0 );
+ s->in_size = 0;
+ s->in_channel = -1;
+
+ qemu_chr_add_handlers( cs,
+ qemud_serial_can_read,
+ qemud_serial_read,
+ NULL,
+ s );
+}
+
+/* send a message to the serial port. This will add the necessary
+ * header.
+ */
+static void
+qemud_serial_send( QemudSerial* s,
+ int channel,
+ ABool framing,
+ const uint8_t* msg,
+ int msglen )
+{
+ uint8_t header[HEADER_SIZE];
+ uint8_t frame[FRAME_HEADER_SIZE];
+ int avail, len = msglen;
+
+ if (msglen <= 0 || channel < 0)
+ return;
+
+ D("%s: channel=%2d len=%3d '%s'",
+ __FUNCTION__, channel, msglen,
+ quote_bytes((const void*)msg, msglen));
+
+ if (framing) {
+ len += FRAME_HEADER_SIZE;
+ }
+
+ /* packetize the payload for the serial MTU */
+ while (len > 0)
+ {
+ avail = len;
+ if (avail > MAX_SERIAL_PAYLOAD)
+ avail = MAX_SERIAL_PAYLOAD;
+
+ /* write this packet's header */
+ int2hex(header + LENGTH_OFFSET, LENGTH_SIZE, avail);
+ int2hex(header + CHANNEL_OFFSET, CHANNEL_SIZE, channel);
+ qemu_chr_write(s->cs, header, HEADER_SIZE);
+
+ /* insert frame header when needed */
+ if (framing) {
+ int2hex(frame, FRAME_HEADER_SIZE, msglen);
+ qemu_chr_write(s->cs, frame, FRAME_HEADER_SIZE);
+ avail -= FRAME_HEADER_SIZE;
+ len -= FRAME_HEADER_SIZE;
+ framing = 0;
+ }
+
+ /* write message content */
+ qemu_chr_write(s->cs, msg, avail);
+ msg += avail;
+ len -= avail;
+ }
+}
+
+/** CLIENTS
+ **/
+
+/* A QemudClient models a single client as seen by the emulator.
+ * Each client has its own channel id, and belongs to a given
+ * QemudService (see below).
+ *
+ * There is a global list of clients used to multiplex incoming
+ * messages from the channel id (see qemud_multiplexer_serial_recv()).
+ *
+ */
+
+struct QemudClient {
+ int channel;
+ QemudSerial* serial;
+ void* clie_opaque;
+ QemudClientRecv clie_recv;
+ QemudClientClose clie_close;
+ QemudService* service;
+ QemudClient* next_serv; /* next in same service */
+ QemudClient* next;
+ QemudClient** pref;
+
+ /* framing support */
+ int framing;
+ ABool need_header;
+ QemudSink header[1];
+ uint8_t header0[FRAME_HEADER_SIZE];
+ QemudSink payload[1];
+};
+
+static void qemud_service_remove_client( QemudService* service,
+ QemudClient* client );
+
+/* remove a QemudClient from global list */
+static void
+qemud_client_remove( QemudClient* c )
+{
+ c->pref[0] = c->next;
+ if (c->next)
+ c->next->pref = c->pref;
+
+ c->next = NULL;
+ c->pref = &c->next;
+}
+
+/* add a QemudClient to global list */
+static void
+qemud_client_prepend( QemudClient* c, QemudClient** plist )
+{
+ c->next = *plist;
+ c->pref = plist;
+ *plist = c;
+ if (c->next)
+ c->next->pref = &c->next;
+}
+
+/* receive a new message from a client, and dispatch it to
+ * the real service implementation.
+ */
+static void
+qemud_client_recv( void* opaque, uint8_t* msg, int msglen )
+{
+ QemudClient* c = opaque;
+
+ /* no framing, things are simple */
+ if (!c->framing) {
+ if (c->clie_recv)
+ c->clie_recv( c->clie_opaque, msg, msglen );
+ return;
+ }
+
+ /* framing */
+
+#if 1
+ /* special case, in 99% of cases, everything is in
+ * the incoming message, and we can do all we need
+ * directly without dynamic allocation.
+ */
+ if (msglen > FRAME_HEADER_SIZE &&
+ c->need_header == 1 &&
+ qemud_sink_needed(c->header) == 0)
+ {
+ int len = hex2int( msg, FRAME_HEADER_SIZE );
+
+ if (len >= 0 && msglen == len + FRAME_HEADER_SIZE) {
+ if (c->clie_recv)
+ c->clie_recv( c->clie_opaque,
+ msg+FRAME_HEADER_SIZE,
+ msglen-FRAME_HEADER_SIZE);
+ return;
+ }
+ }
+#endif
+
+ while (msglen > 0) {
+ /* read the header */
+ if (c->need_header) {
+ int frame_size;
+ uint8_t* data;
+
+ if (!qemud_sink_fill(c->header, (const uint8_t**)&msg, &msglen))
+ break;
+
+ frame_size = hex2int(c->header0, 4);
+ if (frame_size == 0) {
+ D("%s: ignoring empty frame", __FUNCTION__);
+ continue;
+ }
+ if (frame_size < 0) {
+ D("%s: ignoring corrupted frame header '.*s'",
+ __FUNCTION__, FRAME_HEADER_SIZE, c->header0 );
+ continue;
+ }
+
+ AARRAY_NEW(data, frame_size+1); /* +1 for terminating zero */
+ qemud_sink_reset(c->payload, frame_size, data);
+ c->need_header = 0;
+ c->header->len = 0;
+ }
+
+ /* read the payload */
+ if (!qemud_sink_fill(c->payload, (const uint8_t**)&msg, &msglen))
+ break;
+
+ c->payload->buff[c->payload->size] = 0;
+
+
+ if (c->clie_recv)
+ c->clie_recv( c->clie_opaque, c->payload->buff, c->payload->size );
+
+ AFREE(c->payload->buff);
+ c->need_header = 1;
+ }
+}
+
+/* disconnect a client. this automatically frees the QemudClient.
+ * note that this also removes the client from the global list
+ * and from its service's list, if any.
+ */
+static void
+qemud_client_disconnect( void* opaque )
+{
+ QemudClient* c = opaque;
+
+ /* remove from current list */
+ qemud_client_remove(c);
+
+ /* 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);
+ qemud_serial_send(c->serial, 0, 0, (uint8_t*)tmp, p-tmp);
+ }
+
+ /* call the client close callback */
+ if (c->clie_close) {
+ c->clie_close(c->clie_opaque);
+ c->clie_close = NULL;
+ }
+ c->clie_recv = NULL;
+
+ /* remove from service list, if any */
+ if (c->service) {
+ qemud_service_remove_client(c->service, c);
+ c->service = NULL;
+ }
+
+ AFREE(c);
+}
+
+/* allocate a new QemudClient object */
+static QemudClient*
+qemud_client_alloc( int channel_id,
+ void* clie_opaque,
+ QemudClientRecv clie_recv,
+ QemudClientClose clie_close,
+ QemudSerial* serial,
+ QemudClient** pclients )
+{
+ QemudClient* c;
+
+ ANEW0(c);
+
+ c->serial = serial;
+ c->channel = channel_id;
+ c->clie_opaque = clie_opaque;
+ c->clie_recv = clie_recv;
+ c->clie_close = clie_close;
+
+ c->framing = 0;
+ c->need_header = 1;
+ qemud_sink_reset(c->header, FRAME_HEADER_SIZE, c->header0);
+
+ qemud_client_prepend(c, pclients);
+
+ return c;
+}
+
+/** SERVICES
+ **/
+
+/* A QemudService models a _named_ service facility implemented
+ * by the emulator, that clients in the emulated system can connect
+ * to.
+ *
+ * Each service can have a limit on the number of clients they
+ * accept (this number if unlimited if 'max_clients' is 0).
+ *
+ * Each service maintains a list of active QemudClients and
+ * can also be used to create new QemudClient objects through
+ * its 'serv_opaque' and 'serv_connect' fields.
+ */
+struct QemudService {
+ const char* name;
+ int max_clients;
+ int num_clients;
+ QemudClient* clients;
+ QemudServiceConnect serv_connect;
+ void* serv_opaque;
+ QemudService* next;
+};
+
+/* Create a new QemudService object */
+static QemudService*
+qemud_service_new( const char* name,
+ int max_clients,
+ void* serv_opaque,
+ QemudServiceConnect serv_connect,
+ QemudService** pservices )
+{
+ QemudService* s;
+
+ ANEW0(s);
+ s->name = ASTRDUP(name);
+ s->max_clients = max_clients;
+ s->num_clients = 0;
+ s->clients = NULL;
+
+ s->serv_opaque = serv_opaque;
+ s->serv_connect = serv_connect;
+
+ s->next = *pservices;
+ *pservices = s;
+
+ return s;
+}
+
+/* used internally to populate a QemudService object with a
+ * new QemudClient */
+static void
+qemud_service_add_client( QemudService* s, QemudClient* c )
+{
+ c->service = s;
+ c->next_serv = s->clients;
+ s->clients = c;
+ s->num_clients += 1;
+}
+
+/* used internally to remove a QemudClient from a QemudService */
+static void
+qemud_service_remove_client( QemudService* s, QemudClient* c )
+{
+ QemudClient** pnode = &s->clients;
+ QemudClient* node;
+
+ /* remove from clients linked-list */
+ for (;;) {
+ node = *pnode;
+ if (node == NULL) {
+ D("%s: could not find client %d for service '%s'",
+ __FUNCTION__, c->channel, s->name);
+ return;
+ }
+ if (node == c)
+ break;
+ pnode = &node->next_serv;
+ }
+
+ *pnode = node->next_serv;
+ s->num_clients -= 1;
+}
+
+/** MULTIPLEXER
+ **/
+
+/* A QemudMultiplexer object maintains the global state of the
+ * qemud service facility. It holds a QemudSerial object to
+ * maintain the state of the serial port connection.
+ *
+ * The QemudMultiplexer receives all incoming messages from
+ * the serial port, and dispatches them to the appropriate
+ * QemudClient.
+ *
+ * It also has a global list of clients, and a global list of
+ * services.
+ *
+ * Finally, the QemudMultiplexer has a special QemudClient used
+ * to handle channel 0, i.e. the control channel used to handle
+ * connections and disconnections of clients.
+ */
+typedef struct QemudMultiplexer QemudMultiplexer;
+
+struct QemudMultiplexer {
+ QemudSerial serial[1];
+ QemudClient* clients;
+ QemudService* services;
+};
+
+/* this is the serial_recv callback that is called
+ * whenever an incoming message arrives through the serial port
+ */
+static void
+qemud_multiplexer_serial_recv( void* opaque,
+ int channel,
+ uint8_t* msg,
+ int msglen )
+{
+ QemudMultiplexer* m = opaque;
+ QemudClient* c = m->clients;
+
+ /* dispatch to an existing client if possible
+ * note that channel 0 is handled by a special
+ * QemudClient that is setup in qemud_multiplexer_init()
+ */
+ for ( ; c != NULL; c = c->next ) {
+ if (c->channel == channel) {
+ qemud_client_recv(c, msg, msglen);
+ return;
+ }
+ }
+
+ D("%s: ignoring %d bytes for unknown channel %d",
+ __FUNCTION__, msglen, channel);
+}
+
+/* handle a new connection attempt. This returns 0 on
+ * success, -1 if the service name is unknown, or -2
+ * if the service's maximum number of clients has been
+ * reached.
+ */
+static int
+qemud_multiplexer_connect( QemudMultiplexer* m,
+ const char* service_name,
+ int channel_id )
+{
+ QemudService* sv;
+ QemudClient* client;
+
+ /* find the corresponding registered service by name */
+ for (sv = m->services; sv != NULL; sv = sv->next) {
+ if (!strcmp(sv->name, service_name)) {
+ break;
+ }
+ }
+
+ if (sv == NULL) {
+ D("%s: no registered '%s' service", __FUNCTION__, service_name);
+ return -1;
+ }
+
+ /* check service's client count */
+ if (sv->max_clients > 0 && sv->num_clients >= sv->max_clients) {
+ D("%s: registration failed for '%s' service: too many clients (%d)",
+ __FUNCTION__, service_name, sv->num_clients);
+ return -2;
+ }
+
+ /* ask the service to create a new QemudClient. Note that we
+ * assume that this calls qemud_client_new() which will add
+ * the client to the service's list automatically.
+ */
+ client = sv->serv_connect( sv->serv_opaque, sv, channel_id );
+ if (client == NULL) {
+ D("%s: registration failed for '%s' service",
+ __FUNCTION__, service_name);
+ return -1;
+ }
+
+ D("%s: registered client channel %d for '%s' service",
+ __FUNCTION__, channel_id, service_name);
+ return 0;
+}
+
+/* disconnect a given client from its channel id */
+static void
+qemud_multiplexer_disconnect( QemudMultiplexer* m,
+ int channel )
+{
+ QemudClient* c;
+
+ /* find the client by its channel id, then disconnect it */
+ for (c = m->clients; c; c = c->next) {
+ if (c->channel == channel) {
+ D("%s: disconnecting client %d",
+ __FUNCTION__, channel);
+ /* note thatt this removes the client from
+ * m->clients automatically.
+ */
+ c->channel = -1; /* no need to send disconnect:<id> */
+ qemud_client_disconnect(c);
+ return;
+ }
+ }
+ D("%s: disconnecting unknown channel %d",
+ __FUNCTION__, channel);
+}
+
+/* handle control messages. This is used as the receive
+ * callback for the special QemudClient setup to manage
+ * channel 0.
+ *
+ * note that the message is zero-terminated for convenience
+ * (i.e. msg[msglen] is a valid memory read that returns '\0')
+ */
+static void
+qemud_multiplexer_control_recv( void* opaque,
+ uint8_t* msg,
+ int msglen )
+{
+ QemudMultiplexer* mult = opaque;
+ uint8_t* msgend = msg + msglen;
+ char tmp[64], *p=tmp, *end=p+sizeof(tmp);
+
+ /* handle connection attempts.
+ * the client message must be "connect:<service-name>:<id>"
+ * where <id> is a 4-char hexadecimal string, which must be > 0
+ */
+ if (msglen > 8 && !memcmp(msg, "connect:", 8))
+ {
+ const char* service_name = (const char*)msg + 8;
+ int channel, ret;
+ char* q;
+
+ q = strchr(service_name, ':');
+ if (q == NULL || q+5 != (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);
+ if (channel <= 0) {
+ D("%s: malformed channel id '%.*s",
+ __FUNCTION__, 4, q);
+ return;
+ }
+
+ ret = qemud_multiplexer_connect(mult, service_name, channel);
+ /* the answer can be one of:
+ * ok:connect:<id>
+ * ko:connect:<id>:<reason-for-failure>
+ */
+ if (ret < 0) {
+ if (ret == -1) {
+ /* could not connect */
+ p = bufprint(tmp, end, "ko:connect:%04x:unknown service", channel);
+ } else {
+ p = bufprint(tmp, end, "ko:connect:%04x:service busy", channel);
+ }
+ }
+ else {
+ p = bufprint(tmp, end, "ok:connect:%04x", channel);
+ }
+ qemud_serial_send(mult->serial, 0, 0, (uint8_t*)tmp, p-tmp);
+ return;
+ }
+
+ /* 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
+ */
+ if (msglen == 15 && !memcmp(msg, "disconnect:", 11)) {
+ int channel_id = hex2int(msg+11, 4);
+ if (channel_id <= 0) {
+ D("%s: malformed disconnect channel id: '%.*s'",
+ __FUNCTION__, 4, msg+11);
+ return;
+ }
+ qemud_multiplexer_disconnect(mult, channel_id);
+ return;
+ }
+
+ /* anything else is a problem */
+ p = bufprint(tmp, end, "ko:unknown command");
+ qemud_serial_send(mult->serial, 0, 0, (uint8_t*)tmp, p-tmp);
+}
+
+/* initialize the global QemudMultiplexer.
+ */
+static void
+qemud_multiplexer_init( QemudMultiplexer* mult,
+ CharDriverState* serial_cs )
+{
+ QemudClient* control;
+
+ /* initialize serial handler */
+ qemud_serial_init( mult->serial,
+ serial_cs,
+ qemud_multiplexer_serial_recv,
+ mult );
+
+ /* setup listener for channel 0 */
+ control = qemud_client_alloc( 0,
+ mult,
+ qemud_multiplexer_control_recv,
+ NULL,
+ mult->serial,
+ &mult->clients );
+}
+
+/* the global multiplexer state */
+static QemudMultiplexer _multiplexer[1];
+
+/** HIGH-LEVEL API
+ **/
+
+/* this function must be used in the serv_connect callback
+ * of a given QemudService object (see qemud_service_register()
+ * below). It is used to register a new QemudClient to acknowledge
+ * a new client connection.
+ *
+ * 'clie_opaque', 'clie_recv' and 'clie_close' are used to
+ * send incoming client messages to the corresponding service
+ * implementation, or notify the service that a client has
+ * disconnected.
+ */
+QemudClient*
+qemud_client_new( QemudService* service,
+ int channelId,
+ void* clie_opaque,
+ QemudClientRecv clie_recv,
+ QemudClientClose clie_close )
+{
+ QemudMultiplexer* m = _multiplexer;
+ QemudClient* c = qemud_client_alloc( channelId,
+ clie_opaque,
+ clie_recv,
+ clie_close,
+ m->serial,
+ &m->clients );
+
+ qemud_service_add_client(service, c);
+ return c;
+}
+
+/* this can be used by a service implementation to send an answer
+ * or message to a specific client.
+ */
+void
+qemud_client_send ( QemudClient* client, const uint8_t* msg, int msglen )
+{
+ qemud_serial_send(client->serial, client->channel, client->framing != 0, msg, msglen);
+}
+
+/* enable framing for this client. When TRUE, this will
+ * use internally a simple 4-hexchar header before each
+ * message exchanged through the serial port.
+ */
+void
+qemud_client_set_framing( QemudClient* client, int framing )
+{
+ /* release dynamic buffer if we're disabling framing */
+ if (client->framing) {
+ if (!client->need_header) {
+ AFREE(client->payload->buff);
+ client->need_header = 1;
+ }
+ }
+ client->framing = !!framing;
+}
+
+/* this can be used by a service implementation to close a
+ * specific client connection.
+ */
+void
+qemud_client_close( QemudClient* client )
+{
+ qemud_client_disconnect(client);
+}
+
+
+
+/* this is the end of the serial charpipe that must be passed
+ * to the emulated tty implementation. The other end of the
+ * charpipe must be passed to qemud_multiplexer_init().
+ */
+static CharDriverState* android_qemud_cs;
+
+extern void
+android_qemud_init( void )
+{
+ CharDriverState* cs;
+
+ if (android_qemud_cs != NULL)
+ return;
+
+ if (qemu_chr_open_charpipe( &android_qemud_cs, &cs ) < 0) {
+ derror( "%s: can't create charpipe to serial port",
+ __FUNCTION__ );
+ exit(1);
+ }
+
+ qemud_multiplexer_init(_multiplexer, cs);
+}
+
+/* return the serial charpipe endpoint that must be used
+ * by the emulated tty implementation.
+ */
+CharDriverState* android_qemud_get_cs( void )
+{
+ if (android_qemud_cs == NULL)
+ android_qemud_init();
+
+ return android_qemud_cs;
+}
+
+/* this function is used to register a new named qemud-based
+ * service. You must provide 'serv_opaque' and 'serv_connect'
+ * which will be called whenever a new client tries to connect
+ * to the services.
+ *
+ * 'serv_connect' shall return NULL if the connection is refused,
+ * or a handle to a new QemudClient otherwise. The latter can be
+ * created through qemud_client_new() defined above.
+ *
+ * 'max_clients' is the maximum number of clients accepted by
+ * the service concurrently. If this value is 0, then any number
+ * of clients can connect.
+ */
+QemudService*
+qemud_service_register( const char* service_name,
+ int max_clients,
+ void* serv_opaque,
+ QemudServiceConnect serv_connect )
+{
+ QemudMultiplexer* m = _multiplexer;
+ QemudService* sv;
+
+ if (android_qemud_cs == NULL)
+ android_qemud_init();
+
+ sv = qemud_service_new(service_name,
+ max_clients,
+ serv_opaque,
+ serv_connect,
+ &m->services);
+
+ return sv;
+}
+
+/* broadcast a given message to all clients of a given QemudService
+ */
+extern void
+qemud_service_broadcast( QemudService* sv,
+ const uint8_t* msg,
+ int msglen )
+{
+ QemudClient* c;
+
+ for (c = sv->clients; c; c = c->next_serv)
+ qemud_client_send(c, msg, msglen);
+}
+
+
+
+/*
+ * The following code is used for backwards compatibility reasons.
+ * It allows you to implement a given qemud-based service through
+ * a charpipe.
+ *
+ * In other words, this implements a QemudService and corresponding
+ * QemudClient that connects a qemud client running in the emulated
+ * system, to a CharDriverState object implemented through a charpipe.
+ *
+ * QemudCharClient <===charpipe====> (char driver user)
+ *
+ * For example, this is used to implement the "gsm" service when the
+ * modem emulation is provided through an external serial device.
+ *
+ * A QemudCharService can have only one client by definition.
+ * There is no QemudCharClient object because we can store a single
+ * CharDriverState handle in the 'opaque' field for simplicity.
+ */
+
+typedef struct {
+ QemudService* service;
+ CharDriverState* cs;
+} QemudCharService;
+
+/* called whenever a new message arrives from a qemud client.
+ * this simply sends the message through the charpipe to the user.
+ */
+static void
+_qemud_char_client_recv( void* opaque, uint8_t* msg, int msglen )
+{
+ CharDriverState* cs = opaque;
+ qemu_chr_write(cs, msg, msglen);
+}
+
+/* we don't expect clients of char. services to exit. Just
+ * print an error to signal an unexpected situation. We should
+ * be able to recover from these though, so don't panic.
+ */
+static void
+_qemud_char_client_close( void* opaque )
+{
+ derror("unexpected qemud char. channel close");
+}
+
+
+/* called by the charpipe to know how much data can be read from
+ * the user. Since we send everything directly to the serial port
+ * we can return an arbitrary number.
+ */
+static int
+_qemud_char_service_can_read( void* opaque )
+{
+ return 8192; /* whatever */
+}
+
+/* called to read data from the charpipe and send it to the client.
+ * used qemud_service_broadcast() even if there is a single client
+ * because we don't need a QemudCharClient object this way.
+ */
+static void
+_qemud_char_service_read( void* opaque, const uint8_t* from, int len )
+{
+ QemudService* sv = opaque;
+ qemud_service_broadcast( sv, from, len );
+}
+
+/* called when a qemud client tries to connect to a char. service.
+ * we simply create a new client and open the charpipe to receive
+ * data from it.
+ */
+static QemudClient*
+_qemud_char_service_connect( void* opaque, QemudService* sv, int channel )
+{
+ CharDriverState* cs = opaque;
+ QemudClient* c = qemud_client_new( sv, channel,
+ cs,
+ _qemud_char_client_recv,
+ _qemud_char_client_close);
+
+ /* now we can open the gates :-) */
+ qemu_chr_add_handlers( cs,
+ _qemud_char_service_can_read,
+ _qemud_char_service_read,
+ NULL,
+ sv );
+
+ return c;
+}
+
+/* returns a charpipe endpoint that can be used by an emulated
+ * device or external serial port to implement a char. service
+ */
+int
+android_qemud_get_channel( const char* name, CharDriverState* *pcs )
+{
+ CharDriverState* cs;
+
+ if (qemu_chr_open_charpipe(&cs, pcs) < 0) {
+ derror("can't open charpipe for '%s' qemud service", name);
+ exit(2);
+ }
+ qemud_service_register(name, 1, cs, _qemud_char_service_connect);
+ return 0;
+}
+
+/* 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.
+ * returns 0 on success, -1 on error
+ */
+int
+android_qemud_set_channel( const char* name, CharDriverState* peer_cs )
+{
+ qemud_service_register(name, 1, peer_cs, _qemud_char_service_connect);
+ return 0;
+}
diff --git a/android/qemud.h b/android/hw-qemud.h
index 4fa71d0..7101c2a 100644
--- a/android/qemud.h
+++ b/android/hw-qemud.h
@@ -66,7 +66,41 @@ extern int android_qemud_set_channel( const char* name, CharDriverState* peer
#define ANDROID_QEMUD_GSM "gsm"
#define ANDROID_QEMUD_GPS "gps"
#define ANDROID_QEMUD_CONTROL "control"
+#define ANDROID_QEMUD_SENSORS "sensors"
-/* add new channel names here when you need them */
+/* A QemudService service is used to connect one or more clients to
+ * a given emulator facility. Only one client can be connected at any
+ * given time, but the connection can be closed periodically.
+ */
+
+typedef struct QemudClient QemudClient;
+typedef struct QemudService QemudService;
+
+
+typedef void (*QemudClientClose)( void* opaque );
+typedef void (*QemudClientRecv) ( void* opaque, uint8_t* msg, int msglen );
+
+extern QemudClient* qemud_client_new( QemudService* service,
+ int channel_id,
+ void* clie_opaque,
+ QemudClientRecv clie_recv,
+ QemudClientClose clie_close );
+
+extern void qemud_client_set_framing( QemudClient* client, int enabled );
+
+extern void qemud_client_send ( QemudClient* client, const uint8_t* msg, int msglen );
+extern void qemud_client_close( QemudClient* client );
+
+
+typedef QemudClient* (*QemudServiceConnect)( void* opaque, QemudService* service, int channel );
+
+extern QemudService* qemud_service_register( const char* serviceName,
+ int max_clients,
+ void* serv_opaque,
+ QemudServiceConnect serv_connect );
+
+extern void qemud_service_broadcast( QemudService* sv,
+ const uint8_t* msg,
+ int msglen );
#endif /* _android_qemud_h */
diff --git a/android/hw-sensors.c b/android/hw-sensors.c
new file mode 100644
index 0000000..eb2cc78
--- /dev/null
+++ b/android/hw-sensors.c
@@ -0,0 +1,450 @@
+/* Copyright (C) 2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+
+#include "android/hw-sensors.h"
+#include "android/utils/debug.h"
+#include "android/utils/misc.h"
+#include "android/hw-qemud.h"
+#include "android/globals.h"
+#include "qemu-char.h"
+#include "qemu-timer.h"
+
+#define D(...) VERBOSE_PRINT(sensors,__VA_ARGS__)
+
+/* define T_ACTIVE to 1 to debug transport communications */
+#define T_ACTIVE 0
+
+#if T_ACTIVE
+#define T(...) VERBOSE_PRINT(sensors,__VA_ARGS__)
+#else
+#define T(...) ((void)0)
+#endif
+
+/* this code supports emulated sensor hardware
+ *
+ * Note that currently, only the accelerometer is really emulated, and only
+ * for the purpose of allowing auto-rotating the screen in keyboard-less
+ * configurations.
+ *
+ *
+ */
+
+
+static const struct {
+ const char* name;
+ int id;
+} _sSensors[MAX_SENSORS] = {
+#define SENSOR_(x,y) { y, ANDROID_SENSOR_##x },
+ SENSORS_LIST
+#undef SENSOR_
+};
+
+
+static int
+_sensorIdFromName( const char* name )
+{
+ int nn;
+ for (nn = 0; nn < MAX_SENSORS; nn++)
+ if (!strcmp(_sSensors[nn].name,name))
+ return _sSensors[nn].id;
+ return -1;
+}
+
+
+typedef struct {
+ float x, y, z;
+} Acceleration;
+
+
+typedef struct {
+ float x, y, z;
+} MagneticField;
+
+
+typedef struct {
+ float azimuth;
+ float pitch;
+ float roll;
+} Orientation;
+
+
+typedef struct {
+ float celsius;
+} Temperature;
+
+
+typedef struct {
+ char enabled;
+ union {
+ Acceleration acceleration;
+ MagneticField magnetic;
+ Orientation orientation;
+ Temperature temperature;
+ } u;
+} Sensor;
+
+/*
+ * - when the qemu-specific sensors HAL module starts, it sends
+ * "list-sensors"
+ *
+ * - this code replies with a string containing an integer corresponding
+ * to a bitmap of available hardware sensors in the current AVD
+ * configuration (e.g. "1" a.k.a (1 << ANDROID_SENSOR_ACCELERATION))
+ *
+ * - the HAL module sends "set:<sensor>:<flag>" to enable or disable
+ * the report of a given sensor state. <sensor> must be the name of
+ * a given sensor (e.g. "accelerometer"), and <flag> must be either
+ * "1" (to enable) or "0" (to disable).
+ *
+ * - Once at least one sensor is "enabled", this code should periodically
+ * send information about the corresponding enabled sensors. The default
+ * period is 200ms.
+ *
+ * - the HAL module sends "set-delay:<delay>", where <delay> is an integer
+ * corresponding to a time delay in milli-seconds. This corresponds to
+ * a new interval between sensor events sent by this code to the HAL
+ * module.
+ *
+ * - the HAL module can also send a "wake" command. This code should simply
+ * send the "wake" back to the module. This is used internally to wake a
+ * blocking read that happens in a different thread. This ping-pong makes
+ * the code in the HAL module very simple.
+ *
+ * - each timer tick, this code sends sensor reports in the following
+ * format (each line corresponds to a different line sent to the module):
+ *
+ * acceleration:<x>:<y>:<z>
+ * magnetic-field:<x>:<y>:<z>
+ * orientation:<azimuth>:<pitch>:<roll>
+ * temperature:<celsius>
+ * sync:<time_us>
+ *
+ * Where each line before the sync:<time_us> is optional and will only
+ * appear if the corresponding sensor has been enabled by the HAL module.
+ *
+ * Note that <time_us> is the VM time in micro-seconds when the report
+ * was "taken" by this code. This is adjusted by the HAL module to
+ * emulated system time (using the first sync: to compute an adjustment
+ * offset).
+ */
+#define HEADER_SIZE 4
+#define BUFFER_SIZE 512
+
+typedef struct {
+ QemudService* service;
+ int32_t delay_ms;
+ uint32_t enabledMask;
+ QEMUTimer* timer;
+ Sensor sensors[MAX_SENSORS];
+} HwSensors;
+
+/* forward */
+
+static void hw_sensors_receive( HwSensors* h,
+ uint8_t* query,
+ int querylen );
+
+static void hw_sensors_timer_tick(void* opaque);
+
+/* Qemud service management */
+
+static void
+_hw_sensors_qemud_client_recv( void* opaque, uint8_t* msg, int msglen )
+{
+ hw_sensors_receive(opaque, msg, msglen);
+}
+
+static QemudClient*
+_hw_sensors_service_connect( void* opaque, QemudService* service, int channel )
+{
+ HwSensors* sensors = opaque;
+ QemudClient* client = qemud_client_new(service, channel,
+ sensors,
+ _hw_sensors_qemud_client_recv,
+ NULL);
+ qemud_client_set_framing(client, 1);
+ return client;
+}
+
+/* change the value of the emulated acceleration vector */
+static void
+hw_sensors_set_acceleration( HwSensors* h, float x, float y, float z )
+{
+ Sensor* s = &h->sensors[ANDROID_SENSOR_ACCELERATION];
+ s->u.acceleration.x = x;
+ s->u.acceleration.y = y;
+ s->u.acceleration.z = z;
+}
+
+#if 0 /* not used yet */
+/* change the value of the emulated magnetic vector */
+static void
+hw_sensors_set_magnetic_field( HwSensors* h, float x, float y, float z )
+{
+ Sensor* s = &h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD];
+ s->u.magnetic.x = x;
+ s->u.magnetic.y = y;
+ s->u.magnetic.z = z;
+}
+
+/* change the values of the emulated orientation */
+static void
+hw_sensors_set_orientation( HwSensors* h, float azimuth, float pitch, float roll )
+{
+ Sensor* s = &h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD];
+ s->u.orientation.azimuth = azimuth;
+ s->u.orientation.pitch = pitch;
+ s->u.orientation.roll = roll;
+}
+
+/* change the emulated temperature */
+static void
+hw_sensors_set_temperature( HwSensors* h, float celsius )
+{
+ Sensor* s = &h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD];
+ s->u.temperature.celsius = celsius;
+}
+#endif
+
+/* change the coarse orientation (landscape/portrait) of the emulated device */
+static void
+hw_sensors_set_coarse_orientation( HwSensors* h, AndroidCoarseOrientation orient )
+{
+ /* The Android framework computes the orientation by looking at
+ * the accelerometer sensor (*not* the orientation sensor !)
+ *
+ * That's because the gravity is a constant 9.81 vector that
+ * can be determined quite easily.
+ *
+ * Also, for some reason, the framework code considers that the phone should
+ * be inclined by 30 degrees along the phone's X axis to be considered
+ * in its ideal "vertical" position
+ *
+ * If the phone is completely vertical, rotating it will not do anything !
+ */
+ const double g = 9.81;
+ const double cos_30 = 0.866025403784;
+ const double sin_30 = 0.5;
+
+ switch (orient) {
+ case ANDROID_COARSE_PORTRAIT:
+ hw_sensors_set_acceleration( h, 0., g*cos_30, g*sin_30 );
+ break;
+
+ case ANDROID_COARSE_LANDSCAPE:
+ hw_sensors_set_acceleration( h, g*cos_30, 0., g*sin_30 );
+ break;
+ default:
+ ;
+ }
+}
+
+
+/* initialize the sensors state */
+static void
+hw_sensors_init( HwSensors* h )
+{
+ h->service = qemud_service_register("sensors", 1, h,
+ _hw_sensors_service_connect );
+ h->enabledMask = 0;
+ h->delay_ms = 1000;
+ h->timer = qemu_new_timer(vm_clock, hw_sensors_timer_tick, h);
+
+ hw_sensors_set_coarse_orientation(h, ANDROID_COARSE_PORTRAIT);
+}
+
+/* send a one-line message to the HAL module through a qemud channel */
+static void
+hw_sensors_send( HwSensors* hw, const uint8_t* msg, int msglen )
+{
+ D("%s: '%s'", __FUNCTION__, quote_bytes((const void*)msg, msglen));
+ qemud_service_broadcast(hw->service, msg, msglen);
+}
+
+/* this function is called periodically to send sensor reports
+ * to the HAL module, and re-arm the timer if necessary
+ */
+static void
+hw_sensors_timer_tick( void* opaque )
+{
+ HwSensors* h = opaque;
+ int64_t delay = h->delay_ms;
+ int64_t now_ns;
+ uint32_t mask = h->enabledMask;
+ Sensor* sensor;
+ char buffer[128];
+
+ sensor = &h->sensors[ANDROID_SENSOR_ACCELERATION];
+ if (sensor->enabled) {
+ snprintf(buffer, sizeof buffer, "acceleration:%g:%g:%g",
+ sensor->u.acceleration.x,
+ sensor->u.acceleration.y,
+ sensor->u.acceleration.z);
+ hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer));
+ }
+
+ sensor = &h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD];
+ if (sensor->enabled) {
+ snprintf(buffer, sizeof buffer, "magnetic-field:%g:%g:%g",
+ sensor->u.magnetic.x,
+ sensor->u.magnetic.y,
+ sensor->u.magnetic.z);
+ hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer));
+ }
+
+ sensor = &h->sensors[ANDROID_SENSOR_ORIENTATION];
+ if (sensor->enabled) {
+ snprintf(buffer, sizeof buffer, "orientation:%g:%g:%g",
+ sensor->u.orientation.azimuth,
+ sensor->u.orientation.pitch,
+ sensor->u.orientation.roll);
+ hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer));
+ }
+
+ sensor = &h->sensors[ANDROID_SENSOR_TEMPERATURE];
+ if (sensor->enabled) {
+ snprintf(buffer, sizeof buffer, "temperature:%g",
+ sensor->u.temperature.celsius);
+ hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer));
+ }
+
+ now_ns = qemu_get_clock(vm_clock);
+
+ snprintf(buffer, sizeof buffer, "sync:%lld", now_ns/1000);
+ hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer));
+
+ /* rearm timer, use a minimum delay of 20 ms, just to
+ * be safe.
+ */
+ if (mask == 0)
+ return;
+
+ if (delay < 20)
+ delay = 20;
+
+ delay *= 1000000LL; /* convert to nanoseconds */
+ qemu_mod_timer(h->timer, now_ns + delay);
+}
+
+/* handle incoming messages from the HAL module */
+static void
+hw_sensors_receive( HwSensors* hw, uint8_t* msg, int msglen )
+{
+ D("%s: '%.*s'", __FUNCTION__, msglen, msg);
+
+ /* "list-sensors" is used to get an integer bit map of
+ * available emulated sensors. We compute the mask from the
+ * current hardware configuration.
+ */
+ if (msglen == 12 && !memcmp(msg, "list-sensors", 12)) {
+ char buff[12];
+ int mask = 0;
+
+ if (android_hw->hw_accelerometer)
+ mask |= (1 << ANDROID_SENSOR_ACCELERATION);
+
+ /* XXX: TODO: Add other tests when we add the corresponding
+ * properties to hardware-properties.ini et al. */
+
+ snprintf(buff, sizeof buff, "%d", mask);
+ hw_sensors_send(hw, (const uint8_t*)buff, strlen(buff));
+ return;
+ }
+
+ /* "wake" is a special message that must be sent back through
+ * the channel. It is used to exit a blocking read.
+ */
+ if (msglen == 4 && !memcmp(msg, "wake", 4)) {
+ hw_sensors_send(hw, (const uint8_t*)"wake", 4);
+ return;
+ }
+
+ /* "set-delay:<delay>" is used to set the delay in milliseconds
+ * between sensor events
+ */
+ if (msglen > 10 && !memcmp(msg, "set-delay:", 10)) {
+ hw->delay_ms = atoi((const char*)msg+10);
+ if (hw->enabledMask != 0)
+ hw_sensors_timer_tick(hw);
+
+ return;
+ }
+
+ /* "set:<name>:<state>" is used to enable/disable a given
+ * sensor. <state> must be 0 or 1
+ */
+ if (msglen > 4 && !memcmp(msg, "set:", 4)) {
+ char* q;
+ int id, enabled, oldEnabledMask = hw->enabledMask;
+ msg += 4;
+ q = strchr((char*)msg, ':');
+ if (q == NULL) { /* should not happen */
+ D("%s: ignore bad 'set' command", __FUNCTION__);
+ return;
+ }
+ *q++ = 0;
+
+ id = _sensorIdFromName((const char*)msg);
+ if (id < 0) {
+ D("%s: ignore unknown sensor name '%s'", __FUNCTION__, msg);
+ return;
+ }
+
+ enabled = (q[0] == '1');
+
+ hw->sensors[id].enabled = (char) enabled;
+ if (enabled)
+ hw->enabledMask |= (1 << id);
+ else
+ hw->enabledMask &= ~(1 << id);
+
+ D("%s: %s %s sensor", __FUNCTION__,
+ hw->sensors[id].enabled ? "enabling" : "disabling", msg);
+
+ if (oldEnabledMask == 0 && enabled) {
+ /* we enabled our first sensor, start event reporting */
+ D("%s: starting event reporting (mask=%04x)", __FUNCTION__,
+ hw->enabledMask);
+ }
+ else if (hw->enabledMask == 0 && !enabled) {
+ /* we disabled our last sensor, stop event reporting */
+ D("%s: stopping event reporting", __FUNCTION__);
+ }
+ hw_sensors_timer_tick(hw);
+ return;
+ }
+
+ D("%s: ignoring unknown query", __FUNCTION__);
+}
+
+
+static HwSensors _sensorsState[1];
+
+void
+android_hw_sensors_init( void )
+{
+ HwSensors* hw = _sensorsState;
+
+ if (hw->service == NULL) {
+ hw_sensors_init(hw);
+ D("%s: sensors qemud service initialized", __FUNCTION__);
+ }
+}
+
+/* change the coarse orientation value */
+extern void
+android_sensors_set_coarse_orientation( AndroidCoarseOrientation orient )
+{
+ android_hw_sensors_init();
+ hw_sensors_set_coarse_orientation(_sensorsState, orient);
+}
+
diff --git a/android/hw-sensors.h b/android/hw-sensors.h
new file mode 100644
index 0000000..37bf20e
--- /dev/null
+++ b/android/hw-sensors.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_sensors_h
+#define _android_sensors_h
+
+#include "qemu-common.h"
+
+/* initialize sensor emulation */
+extern void android_hw_sensors_init( void );
+
+/* NOTE: this list must be the same that the one defined in
+ * the sensors_qemu.c source of the libsensors.goldfish.so
+ * library.
+ */
+#define SENSORS_LIST \
+ SENSOR_(ACCELERATION,"acceleration") \
+ SENSOR_(MAGNETIC_FIELD,"magnetic-field") \
+ SENSOR_(ORIENTATION,"orientation") \
+ SENSOR_(TEMPERATURE,"temperature") \
+
+typedef enum {
+#define SENSOR_(x,y) ANDROID_SENSOR_##x,
+ SENSORS_LIST
+#undef SENSOR_
+ MAX_SENSORS /* do not remove */
+} AndroidSensor;
+
+extern void android_hw_sensor_enable( AndroidSensor sensor );
+
+/* COARSE ORIENTATION VALUES */
+typedef enum {
+ ANDROID_COARSE_PORTRAIT,
+ ANDROID_COARSE_LANDSCAPE
+} AndroidCoarseOrientation;
+
+/* change the coarse orientation value */
+extern void android_sensors_set_coarse_orientation( AndroidCoarseOrientation orient );
+
+#endif /* _android_gps_h */
diff --git a/android/main.c b/android/main.c
index 38f14f5..b90cf90 100644
--- a/android/main.c
+++ b/android/main.c
@@ -48,9 +48,10 @@
#include "android/skin/keyset.h"
#include "android/gps.h"
-#include "android/qemud.h"
+#include "android/hw-qemud.h"
#include "android/hw-kmsg.h"
#include "android/hw-control.h"
+#include "android/hw-sensors.h"
#include "android/user-config.h"
#include "android/utils/bufprint.h"
#include "android/utils/dirscanner.h"
@@ -1873,7 +1874,7 @@ int main(int argc, char **argv)
break;
if (!path_exists(out)) {
- derror("Can't access ANDROID_PRODUCT_OUT as '%s\n"
+ derror("Can't access ANDROID_PRODUCT_OUT as '%s'\n"
"You need to build the Android system before launching the emulator",
out);
exit(2);
@@ -1998,21 +1999,9 @@ int main(int argc, char **argv)
exit(1);
}
}
- else {
- if (!opts->skin && android_build_out) {
- /* select default skin based on product type */
- const char* p = strrchr(android_build_out,'/');
- if (p) {
- if (p[1] == 's') {
- opts->skin = "HVGA"; /* used to be QVGA-L */
- } else if (p[1] == 'd') {
- opts->skin = "HVGA";
- }
- }
- D("autoconfig: -skin %s", opts->skin);
- }
- android_avdParams->skinName = opts->skin;
- }
+ android_avdParams->skinName = opts->skin;
+ android_avdParams->skinRootPath = opts->skindir;
+
/* setup the virtual device differently depending on whether
* we are in the Android build system or not
*/
@@ -2778,6 +2767,9 @@ void android_emulation_setup( void )
}
while (0);
+ /* initialize sensors, this must be done here due to timer issues */
+ android_hw_sensors_init();
+
/* cool, now try to run the "ddms ping" command, which will take care of pinging usage
* if the user agreed for it. the emulator itself never sends anything to any outside
* machine
@@ -2844,15 +2836,15 @@ void android_emulation_setup( void )
* under VMWare.
*/
BEGIN_NOSIGALRM
- pid = fork();
+ pid = fork();
+ if (pid == 0) {
+ int fd = open("/dev/null", O_WRONLY);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL );
+ }
END_NOSIGALRM
- if (pid == 0) {
- int fd = open("/dev/null", O_WRONLY);
- dup2(fd, 1);
- dup2(fd, 2);
- execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL );
- }
/* don't do anything in the parent or in case of error */
strncat( tmp, " ping emulator " VERSION_STRING, PATH_MAX - strlen(tmp) );
D( "ping command: %s", tmp );
diff --git a/android/qemud.c b/android/qemud.c
deleted file mode 100644
index b127fc9..0000000
--- a/android/qemud.c
+++ /dev/null
@@ -1,456 +0,0 @@
-/* Copyright (C) 2007-2008 The Android Open Source Project
-**
-** This software is licensed under the terms of the GNU General Public
-** License version 2, as published by the Free Software Foundation, and
-** may be copied, distributed, and modified under those terms.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-*/
-#include "android/qemud.h"
-#include "android/utils/debug.h"
-#include "android/utils/misc.h"
-#include "qemu-char.h"
-#include "charpipe.h"
-#include "cbuffer.h"
-
-#define D(...) VERBOSE_PRINT(qemud,__VA_ARGS__)
-#define D_ACTIVE VERBOSE_CHECK(qemud)
-
-/* the T(...) macro is used to dump traffic */
-#define T_ACTIVE 0
-
-#if T_ACTIVE
-#define T(...) VERBOSE_PRINT(qemud,__VA_ARGS__)
-#else
-#define T(...) ((void)0)
-#endif
-
-#define MAX_PAYLOAD 4000
-#define MAX_CHANNELS 8
-
-#define CHANNEL_CONTROL_INDEX 0
-
-/** packets
- **/
-#define HEADER_SIZE 6
-
-typedef struct Packet {
- struct Packet* next;
- int len;
- uint8_t header[HEADER_SIZE];
- uint8_t data[MAX_PAYLOAD];
-} Packet;
-
-static Packet* _free_packets;
-
-static void
-packet_free( Packet* p )
-{
- p->next = _free_packets;
- _free_packets = p;
-}
-
-static Packet*
-packet_alloc( void )
-{
- Packet* p = _free_packets;
- if (p != NULL) {
- _free_packets = p->next;
- } else {
- p = malloc(sizeof(*p));
- if (p == NULL) {
- derror("%s: not enough memory", __FUNCTION__);
- exit(1);
- }
- }
- p->next = NULL;
- p->len = 0;
- return p;
-}
-
-/** channels
- **/
-typedef void (*EnqueueFunc)( void* user, Packet* p );
-
-typedef struct {
- const char* name;
- int index;
- CharDriverState* cs;
- EnqueueFunc enq_func;
- void* enq_user;
-} Channel;
-
-
-static int
-channel_can_read( void* opaque )
-{
- Channel* c = opaque;
-
- return c->index < 0 ? 0 : MAX_PAYLOAD;
-}
-
-
-/* here, the data comes from the emulated device (e.g. GSM modem) through
- * a charpipe, we simply need to send it through the multiplexer */
-static void
-channel_read( void* opaque, const uint8_t* from, int len )
-{
- Channel* c = opaque;
-
- if (c->enq_func != NULL) {
- Packet* p = packet_alloc();
-
- if (len > MAX_PAYLOAD)
- len = MAX_PAYLOAD;
-
- memcpy( p->data, from, len );
- p->len = len + HEADER_SIZE;
- int2hex( p->header+0, 4, len );
- int2hex( p->header+4, 2, c->index );
-
- c->enq_func( c->enq_user, p );
- }
- else
- {
- D("%s: discarding %d bytes for channel '%s'",
- __FUNCTION__, len, c->name);
- }
-}
-
-static void
-channel_init( Channel* c, const char* name, CharDriverState* peer_cs )
-{
- c->name = name;
- c->index = -1;
- c->enq_func = NULL;
- c->enq_user = NULL;
- c->cs = peer_cs;
-}
-
-
-static void
-channel_set_peer( Channel* c, int index, EnqueueFunc enq_func, void* enq_user )
-{
- c->index = index;
- qemu_chr_add_handlers( c->cs,
- channel_can_read,
- channel_read,
- NULL,
- c );
- c->enq_func = enq_func;
- c->enq_user = enq_user;
-}
-
-
-static int
-channel_write( Channel*c , const uint8_t* buf, int len )
-{
- return qemu_chr_write( c->cs, buf, len );
-}
-
-/** multiplexer
- **/
-#define IN_BUFF_SIZE (2*MAX_PAYLOAD)
-
-typedef struct {
- CharDriverState* cs;
-
- CBuffer in_cbuffer[1];
- int in_datalen;
- int in_channel;
-
- int count;
- Channel channels[MAX_CHANNELS];
- uint8_t in_buff[ IN_BUFF_SIZE + HEADER_SIZE ];
-} Multiplexer;
-
-
-/* called by channel_read when data comes from an emulated
- * device, and needs to be multiplexed through the serial
- * port
- */
-static void
-multiplexer_enqueue( Multiplexer* m, Packet* p )
-{
- T("%s: sending %d bytes: '%s'", __FUNCTION__,
- p->len - HEADER_SIZE, quote_bytes( p->data, p->len - HEADER_SIZE ) );
-
- qemu_chr_write( m->cs, p->header, HEADER_SIZE );
- qemu_chr_write( m->cs, p->data, p->len - HEADER_SIZE );
- packet_free(p);
-}
-
-/* called when we received a channel registration from the
- * qemud daemon
- */
-static void
-multiplexer_register_channel( Multiplexer* m,
- const char* name,
- int index )
-{
- Channel* c = m->channels;
- Channel* c_end = c + m->count;
-
- for ( ; c < c_end; c++ ) {
- if ( !strcmp(c->name, name) )
- break;
- }
-
- if (c >= c_end) {
- D( "%s: unknown channel name '%s'",
- __FUNCTION__, name );
- return;
- }
-
- if (c->index >= 0) {
- D( "%s: channel '%s' re-assigned index %d",
- __FUNCTION__, name, index );
- c->index = index;
- return;
- }
- channel_set_peer( c, index, (EnqueueFunc) multiplexer_enqueue, m );
- D( "%s: channel '%s' registered as index %d",
- __FUNCTION__, c->name, c->index );
-}
-
-
-/* handle answers from the control channel */
-static void
-multiplexer_handle_control( Multiplexer* m, Packet* p )
-{
- int len = p->len - HEADER_SIZE;
-
- /* for now, the only supported answer is 'ok:connect:<name>:<XX>' where
- * <XX> is a hexdecimal channel numner */
- D( "%s: received '%s'", __FUNCTION__, quote_bytes( (const void*)p->data, (unsigned)len ) );
- if ( !memcmp( p->data, "ok:connect:", 11 ) ) do {
- char* name = (char*)p->data + 11;
- char* q = strchr( name, ':' );
- int index;
-
- if (q == NULL)
- break;
-
- q[0] = 0;
- if (q + 3 > (char*)p->data + len)
- break;
-
- index = hex2int( (uint8_t*)q+1, 2 );
- if (index < 0)
- break;
-
- multiplexer_register_channel( m, name, index );
- goto Exit;
- }
- while(0);
-
- D( "%s: unsupported message !!", __FUNCTION__ );
-Exit:
- packet_free(p);
-}
-
-
-static int
-multiplexer_can_read( void* opaque )
-{
- Multiplexer* m = opaque;
-
- return cbuffer_write_avail( m->in_cbuffer );
-}
-
-/* the data comes from the serial port, we need to reconstruct packets then
- * dispatch them to the appropriate channel */
-static void
-multiplexer_read( void* opaque, const uint8_t* from, int len )
-{
- Multiplexer* m = opaque;
- CBuffer* cb = m->in_cbuffer;
- int ret = 0;
-
- T("%s: received %d bytes from serial: '%s'",
- __FUNCTION__, len, quote_bytes( from, len ));
-
- ret = cbuffer_write( cb, from, len );
- if (ret == 0)
- return;
-
- for (;;) {
- int len = cbuffer_read_avail( cb );
-
- if (m->in_datalen == 0) {
- uint8_t header[HEADER_SIZE];
-
- if (len < HEADER_SIZE)
- break;
-
- cbuffer_read( cb, header, HEADER_SIZE );
- m->in_datalen = hex2int( header+0, 4 );
- m->in_channel = hex2int( header+4, 2 );
- }
- else
- {
- Packet* p;
-
- if (len < m->in_datalen)
- break;
-
- /* a full packet was received */
- p = packet_alloc();
- cbuffer_read( cb, p->data, m->in_datalen );
- p->len = HEADER_SIZE + m->in_datalen;
-
- /* find the channel for this packet */
- if (m->in_channel == CHANNEL_CONTROL_INDEX)
- multiplexer_handle_control( m, p );
- else {
- Channel* c = m->channels;
- Channel* c_end = c + m->count;
-
- for ( ; c < c_end; c++ ) {
- if (c->index == m->in_channel) {
- channel_write( c, p->data, m->in_datalen );
- break;
- }
- }
- packet_free(p);
- }
- m->in_datalen = 0;
- }
-
- }
- return;
-}
-
-static void
-multiplexer_query_channel( Multiplexer* m, const char* name )
-{
- Packet* p = packet_alloc();
- int len;
-
- len = snprintf( (char*)p->data, MAX_PAYLOAD, "connect:%s", name );
-
- int2hex( p->header+0, 4, len );
- int2hex( p->header+4, 2, CHANNEL_CONTROL_INDEX );
- p->len = HEADER_SIZE + len;
-
- multiplexer_enqueue( m, p );
-}
-
-
-static Channel*
-multiplexer_find_channel( Multiplexer* m, const char* name )
-{
- int n;
- for (n = 0; n < m->count; n++)
- if ( !strcmp(m->channels[n].name, name) )
- return m->channels + n;
-
- return NULL;
-}
-
-
-static Multiplexer _multiplexer[1];
-static CharDriverState* android_qemud_cs;
-
-extern void
-android_qemud_init( void )
-{
- Multiplexer* m = _multiplexer;
-
- if (android_qemud_cs != NULL)
- return;
-
- m->count = 0;
-
- cbuffer_reset( m->in_cbuffer, m->in_buff, sizeof(m->in_buff) );
- m->in_datalen = 0;
- m->in_channel = 0;
-
- if (qemu_chr_open_charpipe( &android_qemud_cs, &m->cs ) < 0) {
- derror( "%s: can't create charpipe to serial port",
- __FUNCTION__ );
- exit(1);
- }
-
- qemu_chr_add_handlers( m->cs, multiplexer_can_read,
- multiplexer_read, NULL, m );
-}
-
-
-CharDriverState* android_qemud_get_cs( void )
-{
- if (android_qemud_cs == NULL)
- android_qemud_init();
-
- return android_qemud_cs;
-}
-
-
-extern int
-android_qemud_get_channel( const char* name, CharDriverState** pcs )
-{
- Multiplexer* m = _multiplexer;
- Channel* c;
- CharDriverState* peer_cs;
- int ret;
-
- if (m->cs == NULL)
- android_qemud_init();
-
- c = multiplexer_find_channel( m, name );
- if (c) {
- derror( "%s: trying to get already-opened qemud channel '%s'",
- __FUNCTION__, name );
- return -1;
- }
-
- if (m->count >= MAX_CHANNELS) {
- derror( "%s: too many registered channels (%d)",
- __FUNCTION__, m->count );
- return -1;
- }
-
- c = m->channels + m->count;
-
- ret = qemu_chr_open_charpipe( &peer_cs, pcs );
- if (ret == 0) {
- channel_init(c, name, peer_cs);
- m->count += 1;
- multiplexer_query_channel( m, c->name );
- }
-
- return ret;
-}
-
-extern int
-android_qemud_set_channel( const char* name, CharDriverState* peer_cs )
-{
- Multiplexer* m = _multiplexer;
- Channel* c;
-
- if (m->cs == NULL)
- android_qemud_init();
-
- c = multiplexer_find_channel(m, name);
- if (c != NULL) {
- derror( "%s: trying to set opened qemud channel '%s'",
- __FUNCTION__, name );
- return -1;
- }
-
- if (m->count >= MAX_CHANNELS) {
- derror( "%s: too many registered channels (%d)",
- __FUNCTION__, m->count );
- return -1;
- }
-
- c = m->channels + m->count;
- channel_init(c, name, peer_cs);
- m->count += 1;
- multiplexer_query_channel( m, c->name );
-
- return 0;
-}
diff --git a/android/skin/window.c b/android/skin/window.c
index 13bdca9..7ce5759 100644
--- a/android/skin/window.c
+++ b/android/skin/window.c
@@ -15,6 +15,7 @@
#include "android/charmap.h"
#include "android/utils/debug.h"
#include "android/utils/display.h"
+#include "android/hw-sensors.h"
#include <SDL_syswm.h>
#include "qemu-common.h"
#include <math.h>
@@ -1204,6 +1205,11 @@ skin_window_reset_internal ( SkinWindow* window, SkinLayout* slayout )
if (slayout->event_type != 0) {
kbd_generic_event( slayout->event_type, slayout->event_code, slayout->event_value );
+ /* XXX: hack, replace by better code here */
+ if (slayout->event_value != 0)
+ android_sensors_set_coarse_orientation( ANDROID_COARSE_PORTRAIT );
+ else
+ android_sensors_set_coarse_orientation( ANDROID_COARSE_LANDSCAPE );
}
return 0;
diff --git a/android/utils/debug.h b/android/utils/debug.h
index fdf93c9..d6bd3f9 100644
--- a/android/utils/debug.h
+++ b/android/utils/debug.h
@@ -33,6 +33,7 @@
_VERBOSE_TAG(nand_limits, "nand/flash read/write thresholding") \
_VERBOSE_TAG(hw_control, "emulated power/flashlight/led/vibrator") \
_VERBOSE_TAG(avd_config, "android virtual device configuration") \
+ _VERBOSE_TAG(sensors, "emulated sensors") \
#define _VERBOSE_TAG(x,y) VERBOSE_##x,
typedef enum {