Commit [9877e2] Maximize Restore History

auto import from //branches/cupcake_rel/...@140373

The Android Open Source Project The Android Open Source Project 2009-03-19

added android/hw-qemud.c
added android/hw-sensors.h
changed android/avd/hardware-properties.ini
changed android/avd/hw-config-defs.h
changed android/skin/window.c
changed android/utils/debug.h
changed android/hw-control.c
changed android/main.c
changed hw/goldfish_events_device.c
changed Makefile.android
changed vl.c
copied android/qemud.c -> android/hw-sensors.c
copied android/qemud.h -> android/hw-qemud.h
android/hw-qemud.c Diff Switch to side-by-side view
Loading...
android/hw-sensors.h Diff Switch to side-by-side view
Loading...
android/avd/hardware-properties.ini Diff Switch to side-by-side view
Loading...
android/avd/hw-config-defs.h Diff Switch to side-by-side view
Loading...
android/skin/window.c Diff Switch to side-by-side view
Loading...
android/utils/debug.h Diff Switch to side-by-side view
Loading...
android/hw-control.c Diff Switch to side-by-side view
Loading...
android/main.c Diff Switch to side-by-side view
Loading...
hw/goldfish_events_device.c Diff Switch to side-by-side view
Loading...
Makefile.android Diff Switch to side-by-side view
Loading...
vl.c Diff Switch to side-by-side view
Loading...
android/qemud.c to android/hw-sensors.c
--- a/android/qemud.c
+++ b/android/hw-sensors.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2008 The Android Open Source Project
+/* 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
@@ -9,448 +9,442 @@
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU General Public License for more details.
 */
-#include "android/qemud.h"
+
+#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 "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
+#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(qemud,__VA_ARGS__)
+#define  T(...)  VERBOSE_PRINT(sensors,__VA_ARGS__)
 #else
-#define  T(...)    ((void)0)
+#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);
+/* 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;
         }
-    }
-    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 );
+        *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;
     }
 
-    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;
-
+    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_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;
-}
+android_sensors_set_coarse_orientation( AndroidCoarseOrientation  orient )
+{
+    android_hw_sensors_init();
+    hw_sensors_set_coarse_orientation(_sensorsState, orient);
+}
+
android/qemud.h to android/hw-qemud.h
--- a/android/qemud.h
+++ b/android/hw-qemud.h
@@ -66,7 +66,41 @@
 #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 */