blob: 681e85b80defd1f366ede3c1c31795a939c45548 [file] [log] [blame]
/* 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/misc.h"
#include "android/utils/debug.h"
#include "qemu-char.h"
#include <stdio.h>
#include <string.h>
#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( (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;
}
}
}
static uint8_t*
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 (uint8_t*)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((const char*)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, (char*)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_handlers( cs, hw_control_can_read, hw_control_read, NULL, hwstate );
D("%s: hw-control char pipe initialized", __FUNCTION__);
}
hw_control_client = opaque;
hw_control_funcs = funcs[0];
}