/* 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. */ /* this file implements the support of the new 'hardware control' * qemud communication channel, which is used by libs/hardware on * the system image to communicate with the emulator program for * emulating the following: * * - power management * - led(s) brightness * - vibrator * - flashlight */ #include "android_hw_control.h" #include "cbuffer.h" #include "android_qemud.h" #include "android_utils.h" #include #define D(...) VERBOSE_PRINT(hw_control,__VA_ARGS__) /* define T_ACTIVE to 1 to debug transport communications */ #define T_ACTIVE 0 #if T_ACTIVE #define T(...) VERBOSE_PRINT(hw_control,__VA_ARGS__) #else #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 ]; } HwControl; /* forward */ static void hw_control_do_query( HwControl* h, uint8_t* query, int querylen ); static void hw_control_init( HwControl* h, CharDriverState* cs ) { h->cs = cs; h->overflow = 0; h->wanted = 0; cbuffer_reset( h->input, h->input_0, sizeof h->input_0 ); } static int hw_control_can_read( void* _hw ) { 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; } /* 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; if (input->count < 4) return; cbuffer_read( input, header, 4 ); len = hex2int( 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, h->query, h->wanted ); h->wanted = 0; } } } static char* if_starts_with( uint8_t* buf, int buflen, const char* prefix ) { int prefixlen = strlen(prefix); if (buflen < prefixlen || memcmp(buf, prefix, prefixlen)) return NULL; return (char*)buf + prefixlen; } static void hw_control_do_query( HwControl* h, uint8_t* query, int querylen ) { uint8_t* q; D("%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) { char* qq = strchr(q, ':'); int value; if (qq == NULL) { D("%s: badly formatted", __FUNCTION__ ); return; } *qq++ = 0; value = atoi(qq); hw_control_funcs.light_brightness( hw_control_client, q, value ); } return; } } 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_read_handler( cs, hw_control_can_read, hw_control_read, hwstate ); D("%s: hw-control char pipe initialized", __FUNCTION__); } hw_control_client = opaque; hw_control_funcs = funcs[0]; }