| /* 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 <math.h> |
| #include "android/hw-sensors.h" |
| #include "android/utils/debug.h" |
| #include "android/utils/misc.h" |
| #include "android/utils/system.h" |
| #include "android/hw-qemud.h" |
| #include "android/globals.h" |
| #include "hw/hw.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 { |
| float value; |
| } Proximity; |
| |
| typedef struct { |
| char enabled; |
| union { |
| Acceleration acceleration; |
| MagneticField magnetic; |
| Orientation orientation; |
| Temperature temperature; |
| Proximity proximity; |
| } 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 HwSensorClient HwSensorClient; |
| |
| typedef struct { |
| QemudService* service; |
| Sensor sensors[MAX_SENSORS]; |
| HwSensorClient* clients; |
| } HwSensors; |
| |
| struct HwSensorClient { |
| HwSensorClient* next; |
| HwSensors* sensors; |
| QemudClient* client; |
| QEMUTimer* timer; |
| uint32_t enabledMask; |
| int32_t delay_ms; |
| }; |
| |
| static void |
| _hwSensorClient_free( HwSensorClient* cl ) |
| { |
| /* remove from sensors's list */ |
| if (cl->sensors) { |
| HwSensorClient** pnode = &cl->sensors->clients; |
| for (;;) { |
| HwSensorClient* node = *pnode; |
| if (node == NULL) |
| break; |
| if (node == cl) { |
| *pnode = cl->next; |
| break; |
| } |
| pnode = &node->next; |
| } |
| cl->next = NULL; |
| cl->sensors = NULL; |
| } |
| |
| /* close QEMUD client, if any */ |
| if (cl->client) { |
| qemud_client_close(cl->client); |
| cl->client = NULL; |
| } |
| /* remove timer, if any */ |
| if (cl->timer) { |
| qemu_del_timer(cl->timer); |
| qemu_free_timer(cl->timer); |
| cl->timer = NULL; |
| } |
| AFREE(cl); |
| } |
| |
| /* forward */ |
| static void _hwSensorClient_tick(void* opaque); |
| |
| |
| static HwSensorClient* |
| _hwSensorClient_new( HwSensors* sensors ) |
| { |
| HwSensorClient* cl; |
| |
| ANEW0(cl); |
| |
| cl->sensors = sensors; |
| cl->enabledMask = 0; |
| cl->delay_ms = 800; |
| cl->timer = qemu_new_timer(vm_clock, _hwSensorClient_tick, cl); |
| |
| cl->next = sensors->clients; |
| sensors->clients = cl; |
| |
| return cl; |
| } |
| |
| /* forward */ |
| |
| static void _hwSensorClient_receive( HwSensorClient* cl, |
| uint8_t* query, |
| int querylen ); |
| |
| /* Qemud service management */ |
| |
| static void |
| _hwSensorClient_recv( void* opaque, uint8_t* msg, int msglen, |
| QemudClient* client ) |
| { |
| HwSensorClient* cl = opaque; |
| |
| _hwSensorClient_receive(cl, msg, msglen); |
| } |
| |
| static void |
| _hwSensorClient_close( void* opaque ) |
| { |
| HwSensorClient* cl = opaque; |
| |
| /* the client is already closed here */ |
| cl->client = NULL; |
| _hwSensorClient_free(cl); |
| } |
| |
| /* send a one-line message to the HAL module through a qemud channel */ |
| static void |
| _hwSensorClient_send( HwSensorClient* cl, const uint8_t* msg, int msglen ) |
| { |
| D("%s: '%s'", __FUNCTION__, quote_bytes((const void*)msg, msglen)); |
| qemud_client_send(cl->client, msg, msglen); |
| } |
| |
| static int |
| _hwSensorClient_enabled( HwSensorClient* cl, int sensorId ) |
| { |
| return (cl->enabledMask & (1 << sensorId)) != 0; |
| } |
| |
| /* this function is called periodically to send sensor reports |
| * to the HAL module, and re-arm the timer if necessary |
| */ |
| static void |
| _hwSensorClient_tick( void* opaque ) |
| { |
| HwSensorClient* cl = opaque; |
| HwSensors* hw = cl->sensors; |
| int64_t delay = cl->delay_ms; |
| int64_t now_ns; |
| uint32_t mask = cl->enabledMask; |
| Sensor* sensor; |
| char buffer[128]; |
| |
| if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_ACCELERATION)) { |
| sensor = &hw->sensors[ANDROID_SENSOR_ACCELERATION]; |
| snprintf(buffer, sizeof buffer, "acceleration:%g:%g:%g", |
| sensor->u.acceleration.x, |
| sensor->u.acceleration.y, |
| sensor->u.acceleration.z); |
| _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer)); |
| } |
| |
| if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_MAGNETIC_FIELD)) { |
| sensor = &hw->sensors[ANDROID_SENSOR_MAGNETIC_FIELD]; |
| snprintf(buffer, sizeof buffer, "magnetic-field:%g:%g:%g", |
| sensor->u.magnetic.x, |
| sensor->u.magnetic.y, |
| sensor->u.magnetic.z); |
| _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer)); |
| } |
| |
| if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_ORIENTATION)) { |
| sensor = &hw->sensors[ANDROID_SENSOR_ORIENTATION]; |
| snprintf(buffer, sizeof buffer, "orientation:%g:%g:%g", |
| sensor->u.orientation.azimuth, |
| sensor->u.orientation.pitch, |
| sensor->u.orientation.roll); |
| _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer)); |
| } |
| |
| if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_TEMPERATURE)) { |
| sensor = &hw->sensors[ANDROID_SENSOR_TEMPERATURE]; |
| snprintf(buffer, sizeof buffer, "temperature:%g", |
| sensor->u.temperature.celsius); |
| _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer)); |
| } |
| |
| if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_PROXIMITY)) { |
| sensor = &hw->sensors[ANDROID_SENSOR_PROXIMITY]; |
| snprintf(buffer, sizeof buffer, "proximity:%g", |
| sensor->u.proximity.value); |
| _hwSensorClient_send(cl, (uint8_t*) buffer, strlen(buffer)); |
| } |
| |
| now_ns = qemu_get_clock(vm_clock); |
| |
| snprintf(buffer, sizeof buffer, "sync:%lld", now_ns/1000); |
| _hwSensorClient_send(cl, (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(cl->timer, now_ns + delay); |
| } |
| |
| /* handle incoming messages from the HAL module */ |
| static void |
| _hwSensorClient_receive( HwSensorClient* cl, uint8_t* msg, int msglen ) |
| { |
| HwSensors* hw = cl->sensors; |
| |
| 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; |
| int nn; |
| |
| for (nn = 0; nn < MAX_SENSORS; nn++) { |
| if (hw->sensors[nn].enabled) |
| mask |= (1 << nn); |
| } |
| |
| snprintf(buff, sizeof buff, "%d", mask); |
| _hwSensorClient_send(cl, (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)) { |
| _hwSensorClient_send(cl, (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)) { |
| cl->delay_ms = atoi((const char*)msg+10); |
| if (cl->enabledMask != 0) |
| _hwSensorClient_tick(cl); |
| |
| 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 = cl->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 || id >= MAX_SENSORS) { |
| D("%s: ignore unknown sensor name '%s'", __FUNCTION__, msg); |
| return; |
| } |
| |
| if (!hw->sensors[id].enabled) { |
| D("%s: trying to set disabled %s sensor", __FUNCTION__, msg); |
| return; |
| } |
| enabled = (q[0] == '1'); |
| |
| if (enabled) |
| cl->enabledMask |= (1 << id); |
| else |
| cl->enabledMask &= ~(1 << id); |
| |
| if (cl->enabledMask != oldEnabledMask) { |
| D("%s: %s %s sensor", __FUNCTION__, |
| (cl->enabledMask & (1 << id)) ? "enabling" : "disabling", msg); |
| } |
| _hwSensorClient_tick(cl); |
| return; |
| } |
| |
| D("%s: ignoring unknown query", __FUNCTION__); |
| } |
| |
| /* Saves sensor-specific client data to snapshot */ |
| static void |
| _hwSensorClient_save( QEMUFile* f, QemudClient* client, void* opaque ) |
| { |
| HwSensorClient* sc = opaque; |
| |
| qemu_put_be32(f, sc->delay_ms); |
| qemu_put_be32(f, sc->enabledMask); |
| qemu_put_timer(f, sc->timer); |
| } |
| |
| /* Loads sensor-specific client data from snapshot */ |
| static int |
| _hwSensorClient_load( QEMUFile* f, QemudClient* client, void* opaque ) |
| { |
| HwSensorClient* sc = opaque; |
| |
| sc->delay_ms = qemu_get_be32(f); |
| sc->enabledMask = qemu_get_be32(f); |
| qemu_get_timer(f, sc->timer); |
| |
| return 0; |
| } |
| |
| static QemudClient* |
| _hwSensors_connect( void* opaque, QemudService* service, int channel ) |
| { |
| HwSensors* sensors = opaque; |
| HwSensorClient* cl = _hwSensorClient_new(sensors); |
| QemudClient* client = qemud_client_new(service, channel, cl, |
| _hwSensorClient_recv, |
| _hwSensorClient_close, |
| _hwSensorClient_save, |
| _hwSensorClient_load ); |
| qemud_client_set_framing(client, 1); |
| cl->client = client; |
| |
| return client; |
| } |
| |
| /* change the value of the emulated acceleration vector */ |
| static void |
| _hwSensors_setAcceleration( 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; |
| } |
| |
| /* Saves available sensors to allow checking availability when loaded. |
| */ |
| static void |
| _hwSensors_save( QEMUFile* f, QemudService* sv, void* opaque) |
| { |
| HwSensors* h = opaque; |
| |
| // number of sensors |
| qemu_put_be32(f, MAX_SENSORS); |
| AndroidSensor i; |
| for (i = 0 ; i < MAX_SENSORS; i++) { |
| Sensor* s = &h->sensors[i]; |
| qemu_put_be32(f, s->enabled); |
| |
| /* this switch ensures that a warning is raised when a new sensor is |
| * added and is not added here as well. |
| */ |
| switch (i) { |
| case ANDROID_SENSOR_ACCELERATION: |
| qemu_put_float(f, s->u.acceleration.x); |
| qemu_put_float(f, s->u.acceleration.y); |
| qemu_put_float(f, s->u.acceleration.z); |
| break; |
| case ANDROID_SENSOR_MAGNETIC_FIELD: |
| qemu_put_float(f, s->u.magnetic.x); |
| qemu_put_float(f, s->u.magnetic.y); |
| qemu_put_float(f, s->u.magnetic.z); |
| break; |
| case ANDROID_SENSOR_ORIENTATION: |
| qemu_put_float(f, s->u.orientation.azimuth); |
| qemu_put_float(f, s->u.orientation.pitch); |
| qemu_put_float(f, s->u.orientation.roll); |
| break; |
| case ANDROID_SENSOR_TEMPERATURE: |
| qemu_put_float(f, s->u.temperature.celsius); |
| break; |
| case ANDROID_SENSOR_PROXIMITY: |
| qemu_put_float(f, s->u.proximity.value); |
| break; |
| case MAX_SENSORS: |
| break; |
| } |
| } |
| } |
| |
| |
| static int |
| _hwSensors_load( QEMUFile* f, QemudService* s, void* opaque) |
| { |
| HwSensors* h = opaque; |
| |
| /* check number of sensors */ |
| int32_t num_sensors = qemu_get_be32(f); |
| if (num_sensors > MAX_SENSORS) { |
| D("%s: cannot load: snapshot requires %d sensors, %d available\n", |
| __FUNCTION__, num_sensors, MAX_SENSORS); |
| return -EIO; |
| } |
| |
| /* load sensor state */ |
| AndroidSensor i; |
| for (i = 0 ; i < num_sensors; i++) { |
| Sensor* s = &h->sensors[i]; |
| s->enabled = qemu_get_be32(f); |
| |
| /* this switch ensures that a warning is raised when a new sensor is |
| * added and is not added here as well. |
| */ |
| switch (i) { |
| case ANDROID_SENSOR_ACCELERATION: |
| s->u.acceleration.x = qemu_get_float(f); |
| s->u.acceleration.y = qemu_get_float(f); |
| s->u.acceleration.z = qemu_get_float(f); |
| break; |
| case ANDROID_SENSOR_MAGNETIC_FIELD: |
| s->u.magnetic.x = qemu_get_float(f); |
| s->u.magnetic.y = qemu_get_float(f); |
| s->u.magnetic.z = qemu_get_float(f); |
| break; |
| case ANDROID_SENSOR_ORIENTATION: |
| s->u.orientation.azimuth = qemu_get_float(f); |
| s->u.orientation.pitch = qemu_get_float(f); |
| s->u.orientation.roll = qemu_get_float(f); |
| break; |
| case ANDROID_SENSOR_TEMPERATURE: |
| s->u.temperature.celsius = qemu_get_float(f); |
| break; |
| case ANDROID_SENSOR_PROXIMITY: |
| s->u.proximity.value = qemu_get_float(f); |
| break; |
| case MAX_SENSORS: |
| break; |
| } |
| } |
| |
| /* The following is necessary when we resume a snaphost |
| * created by an older version of the emulator that provided |
| * less hardware sensors. |
| */ |
| for ( ; i < MAX_SENSORS; i++ ) { |
| h->sensors[i].enabled = 0; |
| } |
| |
| return 0; |
| } |
| |
| |
| #if 0 /* not used yet */ |
| /* change the value of the emulated magnetic vector */ |
| static void |
| _hwSensors_setMagneticField( 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 |
| _hwSensors_setOrientation( HwSensors* h, float azimuth, float pitch, float roll ) |
| { |
| Sensor* s = &h->sensors[ANDROID_SENSOR_ORIENTATION]; |
| s->u.orientation.azimuth = azimuth; |
| s->u.orientation.pitch = pitch; |
| s->u.orientation.roll = roll; |
| } |
| |
| /* change the emulated temperature */ |
| static void |
| _hwSensors_setTemperature( HwSensors* h, float celsius ) |
| { |
| Sensor* s = &h->sensors[ANDROID_SENSOR_TEMPERATURE]; |
| s->u.temperature.celsius = celsius; |
| } |
| #endif |
| |
| /* change the emulated proximity */ |
| static void |
| _hwSensors_setProximity( HwSensors* h, float value ) |
| { |
| Sensor* s = &h->sensors[ANDROID_SENSOR_PROXIMITY]; |
| s->u.proximity.value = value; |
| } |
| |
| /* change the coarse orientation (landscape/portrait) of the emulated device */ |
| static void |
| _hwSensors_setCoarseOrientation( 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 angle = 20.0; |
| const double cos_angle = cos(angle/M_PI); |
| const double sin_angle = sin(angle/M_PI); |
| |
| switch (orient) { |
| case ANDROID_COARSE_PORTRAIT: |
| _hwSensors_setAcceleration( h, 0., g*cos_angle, g*sin_angle ); |
| break; |
| |
| case ANDROID_COARSE_LANDSCAPE: |
| _hwSensors_setAcceleration( h, g*cos_angle, 0., g*sin_angle ); |
| break; |
| default: |
| ; |
| } |
| } |
| |
| |
| /* initialize the sensors state */ |
| static void |
| _hwSensors_init( HwSensors* h ) |
| { |
| h->service = qemud_service_register("sensors", 0, h, _hwSensors_connect, |
| _hwSensors_save, _hwSensors_load); |
| |
| if (android_hw->hw_accelerometer) |
| h->sensors[ANDROID_SENSOR_ACCELERATION].enabled = 1; |
| |
| if (android_hw->hw_sensors_proximity) |
| h->sensors[ANDROID_SENSOR_PROXIMITY].enabled = 1; |
| |
| /* XXX: TODO: Add other tests when we add the corresponding |
| * properties to hardware-properties.ini et al. */ |
| |
| _hwSensors_setCoarseOrientation(h, ANDROID_COARSE_PORTRAIT); |
| _hwSensors_setProximity(h, 1); |
| } |
| |
| static HwSensors _sensorsState[1]; |
| |
| void |
| android_hw_sensors_init( void ) |
| { |
| HwSensors* hw = _sensorsState; |
| |
| if (hw->service == NULL) { |
| _hwSensors_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(); |
| _hwSensors_setCoarseOrientation(_sensorsState, orient); |
| } |
| |