diff options
-rw-r--r-- | Makefile.android | 1 | ||||
-rw-r--r-- | android/boot-properties.c | 214 | ||||
-rw-r--r-- | android/boot-properties.h | 47 | ||||
-rw-r--r-- | android/cmdline-option.c | 61 | ||||
-rw-r--r-- | android/cmdline-option.h | 8 | ||||
-rw-r--r-- | android/cmdline-options.h | 6 | ||||
-rw-r--r-- | android/help.c | 20 | ||||
-rw-r--r-- | android/hw-control.c | 7 | ||||
-rw-r--r-- | android/hw-qemud.c | 16 | ||||
-rw-r--r-- | android/hw-qemud.h | 32 | ||||
-rw-r--r-- | android/hw-sensors.c | 3 | ||||
-rw-r--r-- | android/main.c | 15 | ||||
-rw-r--r-- | docs/ANDROID-QEMUD-SERVICES.TXT | 25 |
13 files changed, 435 insertions, 20 deletions
diff --git a/Makefile.android b/Makefile.android index 4c697fb..549136a 100644 --- a/Makefile.android +++ b/Makefile.android @@ -421,6 +421,7 @@ VL_SOURCES := vl.c osdep.c cutils.c \ shaper.c charpipe.c loadpng.c \ framebuffer.c \ tcpdump.c \ + android/boot-properties.c \ android/charmap.c \ android/cmdline-option.c \ android/config.c \ diff --git a/android/boot-properties.c b/android/boot-properties.c new file mode 100644 index 0000000..1c714e9 --- /dev/null +++ b/android/boot-properties.c @@ -0,0 +1,214 @@ +/* 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/boot-properties.h" +#include "android/utils/debug.h" +#include "android/utils/system.h" +#include "android/hw-qemud.h" +#include "android/globals.h" + +#define D(...) VERBOSE_PRINT(init,__VA_ARGS__) + +/* define T_ACTIVE to 1 to debug transport communications */ +#define T_ACTIVE 0 + +#if T_ACTIVE +#define T(...) VERBOSE_PRINT(init,__VA_ARGS__) +#else +#define T(...) ((void)0) +#endif + +typedef struct BootProperty { + struct BootProperty* next; + char* property; + int length; +} BootProperty; + +static BootProperty* +boot_property_alloc( const char* name, int namelen, + const char* value, int valuelen ) +{ + int length = namelen + 1 + valuelen; + BootProperty* prop = android_alloc( sizeof(*prop) + length + 1 ); + char* p; + + prop->next = NULL; + prop->property = p = (char*)(prop + 1); + prop->length = length; + + memcpy( p, name, namelen ); + p += namelen; + *p++ = '='; + memcpy( p, value, valuelen ); + p += valuelen; + *p = '\0'; + + return prop; +} + +static BootProperty* _boot_properties; +static BootProperty** _boot_properties_tail = &_boot_properties; +static int _inited; + +/* this code supports the list of system properties that will + * be set on boot in the emulated system. + */ + +int +boot_property_add2( const char* name, int namelen, + const char* value, int valuelen ) +{ + BootProperty* prop; + + /* check the lengths + */ + if (namelen > PROPERTY_MAX_NAME) + return -1; + + if (valuelen > PROPERTY_MAX_VALUE) + return -2; + + /* check that there are not invalid characters in the + * property name + */ + const char* reject = " =$*?'\""; + int nn; + + for (nn = 0; nn < namelen; nn++) { + if (strchr(reject, name[nn]) != NULL) + return -3; + } + + /* init service if needed */ + if (!_inited) { + boot_property_init_service(); + _inited = 1; + } + + D("Adding boot property: '%.*s' = '%.*s'", + namelen, name, valuelen, value); + + /* add to the internal list */ + prop = boot_property_alloc(name, namelen, value, valuelen); + + *_boot_properties_tail = prop; + _boot_properties_tail = &prop->next; + + return 0; +} + + +int +boot_property_add( const char* name, const char* value ) +{ + int namelen = strlen(name); + int valuelen = strlen(value); + + return boot_property_add2(name, namelen, value, valuelen); +} + + + +#define SERVICE_NAME "boot-properties" + +static void +boot_property_client_recv( void* opaque, + uint8_t* msg, + int msglen, + QemudClient* client ) +{ + /* the 'list' command shall send all boot properties + * to the client, then close the connection. + */ + if (msglen == 4 && !memcmp(msg, "list", 4)) { + BootProperty* prop; + for (prop = _boot_properties; prop != NULL; prop = prop->next) { + qemud_client_send(client, (uint8_t*)prop->property, prop->length); + } + qemud_client_close(client); + return; + } + + /* unknown command ? */ + D("%s: ignoring unknown command: %.*s", __FUNCTION__, msglen, msg); +} + +static QemudClient* +boot_property_service_connect( void* opaque, + QemudService* serv, + int channel ) +{ + QemudClient* client; + + client = qemud_client_new( serv, channel, NULL, + boot_property_client_recv, + NULL ); + + qemud_client_set_framing(client, 1); + return client; +} + + +void +boot_property_init_service( void ) +{ + if (!_inited) { + QemudService* serv = qemud_service_register( SERVICE_NAME, + 1, NULL, + boot_property_service_connect ); + if (serv == NULL) { + derror("could not register '%s' service", SERVICE_NAME); + return; + } + D("registered '%s' qemud service", SERVICE_NAME); + } +} + + + +void +boot_property_parse_option( const char* param ) +{ + char* q = strchr(param,'='); + const char* name; + const char* value; + int namelen, valuelen, ret; + + if (q == NULL) { + dwarning("boot property missing (=) separator: %s", param); + return; + } + + name = param; + namelen = q - param; + + value = q+1; + valuelen = strlen(name) - (namelen+1); + + ret = boot_property_add2(name, namelen, value, valuelen); + if (ret < 0) { + switch (ret) { + case -1: + dwarning("boot property name too long: '%.*s'", + namelen, name); + break; + case -2: + dwarning("boot property value too long: '%.*s'", + valuelen, value); + break; + case -3: + dwarning("boot property name contains invalid chars: %.*s", + namelen, name); + break; + } + } +} diff --git a/android/boot-properties.h b/android/boot-properties.h new file mode 100644 index 0000000..6711c59 --- /dev/null +++ b/android/boot-properties.h @@ -0,0 +1,47 @@ +/* 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_BOOT_PROPERTIES_H +#define _ANDROID_BOOT_PROPERTIES_H + +/* these values give the maximum length of system property + * names and values. They must match the corresponding definitions + * in the Android source tree (in system/core/include/cutils/properties.h) + */ +#define PROPERTY_MAX_NAME 32 +#define PROPERTY_MAX_VALUE 92 + +/* record a new boot system property, this must be performed before the + * VM is started. Returns 0 on success, or < 0 on error. + * Possible errors are: + * -1 property name too long + * -2 property value too long + * -3 invalid characters in property name + */ +int boot_property_add( const char* name, const char* value ); + +/* same as boot_property_add, but allows to use non-zero terminated strings. + */ +int boot_property_add2( const char* name, int namelen, + const char* value, int valuelen ); + +/* init the boot property QEMUD service. This must be performed before + * the VM is started. This is also performed automatically if you call + * boot_property_add(). + */ +void boot_property_init_service( void ); + +/* parse the parameter the list of -prop options passed on the command line + */ +void boot_property_parse_option( const char* param ); + +#endif /* _ANDROID_BOOT_PROPERTIES_H */ diff --git a/android/cmdline-option.c b/android/cmdline-option.c index b773d9d..fb5aa23 100644 --- a/android/cmdline-option.c +++ b/android/cmdline-option.c @@ -1,6 +1,7 @@ #include "android/cmdline-option.h" #include "android/utils/debug.h" #include "android/utils/misc.h" +#include "android/utils/system.h" #include <stdlib.h> #include <stddef.h> #include <string.h> @@ -15,11 +16,16 @@ debug_tags[] = { static void parse_debug_tags( const char* tags ); void parse_env_debug_tags( void ); +enum { + OPTION_IS_FLAG = 0, + OPTION_IS_PARAM, + OPTION_IS_LIST, +}; typedef struct { const char* name; int var_offset; - int var_is_param; + int var_type; int var_is_config; } OptionInfo; @@ -28,10 +34,11 @@ typedef struct { static const OptionInfo option_keys[] = { -#define OPT_PARAM(_name,_template,_descr) OPTION(_name,1,0) -#define OPT_FLAG(_name,_descr) OPTION(_name,0,0) -#define CFG_PARAM(_name,_template,_descr) OPTION(_name,1,1) -#define CFG_FLAG(_name,_descr) OPTION(_name,0,1) +#define OPT_FLAG(_name,_descr) OPTION(_name,OPTION_IS_FLAG,0) +#define OPT_PARAM(_name,_template,_descr) OPTION(_name,OPTION_IS_PARAM,0) +#define OPT_LIST(_name,_template,_descr) OPTION(_name,OPTION_IS_LIST,0) +#define CFG_FLAG(_name,_descr) OPTION(_name,OPTION_IS_FLAG,1) +#define CFG_PARAM(_name,_template,_descr) OPTION(_name,OPTION_IS_PARAM,1) #include "android/cmdline-options.h" { NULL, 0, 0, 0 } }; @@ -145,15 +152,31 @@ android_parse_options( int *pargc, char** *pargv, AndroidOptions* opt ) if ( !strcmp( oo->name, arg2 ) ) { void* field = (char*)opt + oo->var_offset; - if (oo->var_is_param) { - /* parameter option */ + if (oo->var_type != OPTION_IS_FLAG) { + /* parameter/list option */ if (nargs == 0) { derror( "-%s must be followed by parameter (see -help-%s)", arg, arg ); exit(1); } nargs--; - ((char**)field)[0] = *aread++; + + if (oo->var_type == OPTION_IS_PARAM) + { + ((char**)field)[0] = *aread++; + } + else if (oo->var_type == OPTION_IS_LIST) + { + ParamList** head = (ParamList**)field; + ParamList* pl; + ANEW0(pl); + /* note: store list items in reverse order here + * the list is reversed later in this function. + */ + pl->param = *aread++; + pl->next = *head; + *head = pl; + } } else { /* flag option */ ((int*)field)[0] = 1; @@ -182,6 +205,28 @@ android_parse_options( int *pargc, char** *pargv, AndroidOptions* opt ) awrite[0] = NULL; + /* reverse any parameter list before exit. + */ + { + const OptionInfo* oo = option_keys; + + for ( ; oo->name; oo++ ) { + if ( oo->var_type == OPTION_IS_LIST ) { + ParamList** head = (ParamList**)((char*)opt + oo->var_offset); + ParamList* prev = NULL; + ParamList* cur = *head; + + while (cur != NULL) { + ParamList* next = cur->next; + cur->next = prev; + prev = cur; + cur = next; + } + *head = prev; + } + } + } + return 0; } diff --git a/android/cmdline-option.h b/android/cmdline-option.h index b87144d..90a7e64 100644 --- a/android/cmdline-option.h +++ b/android/cmdline-option.h @@ -12,9 +12,17 @@ #ifndef _ANDROID_OPTION_H #define _ANDROID_OPTION_H +/* a structure used to model a linked list of parameters + */ +typedef struct ParamList { + char* param; + struct ParamList* next; +} ParamList; + /* define a structure that will hold all option variables */ typedef struct { +#define OPT_LIST(n,t,d) ParamList* n; #define OPT_PARAM(n,t,d) char* n; #define OPT_FLAG(n,d) int n; #include "android/cmdline-options.h" diff --git a/android/cmdline-options.h b/android/cmdline-options.h index 5291177..0689e83 100644 --- a/android/cmdline-options.h +++ b/android/cmdline-options.h @@ -5,6 +5,9 @@ #ifndef OPT_PARAM #error OPT_PARAM is not defined #endif +#ifndef OPT_LIST +#error OPT_LIST is not defined +#endif #ifndef OPT_FLAG #error OPT_FLAG is not defined #endif @@ -128,6 +131,8 @@ OPT_PARAM( tcpdump, "<file>", "capture network packets to file" ) OPT_PARAM( bootchart, "<timeout>", "enable bootcharting") +OPT_LIST( prop, "<name>=<value>", "set system property on boot") + #ifdef CONFIG_NAND_LIMITS OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds" ) #endif @@ -137,3 +142,4 @@ OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds" #undef CFG_PARAM #undef OPT_FLAG #undef OPT_PARAM +#undef OPT_LIST diff --git a/android/help.c b/android/help.c index 391b63d..d01ccc9 100644 --- a/android/help.c +++ b/android/help.c @@ -5,6 +5,7 @@ #include "android/utils/debug.h" #include "android/utils/misc.h" #include "android/skin/keyset.h" +#include "android/boot-properties.h" #include "android/android.h" #include <stdint.h> #include "audio/audio.h" @@ -1281,6 +1282,24 @@ help_tcpdump(stralloc_t *out) ); } +static void +help_prop(stralloc_t *out) +{ + PRINTF( + " use '-prop <name>=<value>' to set a boot-time system property.\n" + " <name> must be a property name of at most %d characters, without any\n" + " space in it, and <value> must be a string of at most %d characters.\n\n", + PROPERTY_MAX_NAME, PROPERTY_MAX_VALUE ); + + PRINTF( + " the corresponding system property will be set at boot time in the\n" + " emulated system. This can be useful for debugging purposes.\n\n" + + " note that you can use several -prop options to define more than one\n" + " boot property.\n\n" + ); +} + #define help_no_skin NULL #define help_netspeed help_shaper #define help_netdelay help_shaper @@ -1307,6 +1326,7 @@ typedef struct { static const OptionHelp option_help[] = { #define OPT_FLAG(_name,_descr) { STRINGIFY(_name), NULL, _descr, help_##_name }, #define OPT_PARAM(_name,_template,_descr) { STRINGIFY(_name), _template, _descr, help_##_name }, +#define OPT_LIST OPT_PARAM #include "android/cmdline-options.h" { NULL, NULL, NULL, NULL } }; diff --git a/android/hw-control.c b/android/hw-control.c index a82a92a..d99f7e3 100644 --- a/android/hw-control.c +++ b/android/hw-control.c @@ -51,9 +51,10 @@ 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_qemud_client_recv( void* opaque, - uint8_t* msg, - int msglen ) +_hw_control_qemud_client_recv( void* opaque, + uint8_t* msg, + int msglen, + QemudClient* client ) { hw_control_do_query(opaque, msg, msglen); } diff --git a/android/hw-qemud.c b/android/hw-qemud.c index ba4ab42..efe6a99 100644 --- a/android/hw-qemud.c +++ b/android/hw-qemud.c @@ -545,7 +545,7 @@ qemud_client_recv( void* opaque, uint8_t* msg, int msglen ) /* no framing, things are simple */ if (!c->framing) { if (c->clie_recv) - c->clie_recv( c->clie_opaque, msg, msglen ); + c->clie_recv( c->clie_opaque, msg, msglen, c ); return; } @@ -566,7 +566,7 @@ qemud_client_recv( void* opaque, uint8_t* msg, int msglen ) if (c->clie_recv) c->clie_recv( c->clie_opaque, msg+FRAME_HEADER_SIZE, - msglen-FRAME_HEADER_SIZE); + msglen-FRAME_HEADER_SIZE, c ); return; } } @@ -606,7 +606,7 @@ qemud_client_recv( void* opaque, uint8_t* msg, int msglen ) if (c->clie_recv) - c->clie_recv( c->clie_opaque, c->payload->buff, c->payload->size ); + c->clie_recv( c->clie_opaque, c->payload->buff, c->payload->size, c ); AFREE(c->payload->buff); c->need_header = 1; @@ -893,9 +893,10 @@ qemud_multiplexer_disconnect( QemudMultiplexer* m, * (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 ) +qemud_multiplexer_control_recv( void* opaque, + uint8_t* msg, + int msglen, + QemudClient* client ) { QemudMultiplexer* mult = opaque; uint8_t* msgend = msg + msglen; @@ -1226,7 +1227,8 @@ typedef struct { * this simply sends the message through the charpipe to the user. */ static void -_qemud_char_client_recv( void* opaque, uint8_t* msg, int msglen ) +_qemud_char_client_recv( void* opaque, uint8_t* msg, int msglen, + QemudClient* client ) { CharDriverState* cs = opaque; qemu_chr_write(cs, msg, msglen); diff --git a/android/hw-qemud.h b/android/hw-qemud.h index 5b45a94..8aae74d 100644 --- a/android/hw-qemud.h +++ b/android/hw-qemud.h @@ -62,28 +62,58 @@ typedef struct QemudClient QemudClient; typedef struct QemudService QemudService; +/* A function that will be called when the client running in the emulated + * system has closed its connection to qemud. + */ typedef void (*QemudClientClose)( void* opaque ); -typedef void (*QemudClientRecv) ( void* opaque, uint8_t* msg, int msglen ); +/* A function that will be called when the client sends a message to the + * service through qemud. + */ +typedef void (*QemudClientRecv) ( void* opaque, uint8_t* msg, int msglen, QemudClient* client ); + +/* Register a new client for a given service. + * 'clie_opaque' will be sent as the first argument to 'clie_recv' and 'clie_close' + * 'clie_recv' and 'clie_close' are both optional and may be NULL. + * + * You should typically use this function within a QemudServiceConnect callback + * (see below). + */ extern QemudClient* qemud_client_new( QemudService* service, int channel_id, void* clie_opaque, QemudClientRecv clie_recv, QemudClientClose clie_close ); +/* Enable framing on a given client channel. + */ extern void qemud_client_set_framing( QemudClient* client, int enabled ); +/* Send a message to a given qemud client + */ extern void qemud_client_send ( QemudClient* client, const uint8_t* msg, int msglen ); + +/* Force-close the connection to a given qemud client. + */ extern void qemud_client_close( QemudClient* client ); +/* A function that will be called each time a new client in the emulated + * system tries to connect to a given qemud service. This should typically + * call qemud_client_new() to register a new client. + */ typedef QemudClient* (*QemudServiceConnect)( void* opaque, QemudService* service, int channel ); +/* Register a new qemud service. + * 'serv_opaque' is the first parameter to 'serv_connect' + */ extern QemudService* qemud_service_register( const char* serviceName, int max_clients, void* serv_opaque, QemudServiceConnect serv_connect ); +/* Sends a message to all clients of a given service. + */ extern void qemud_service_broadcast( QemudService* sv, const uint8_t* msg, int msglen ); diff --git a/android/hw-sensors.c b/android/hw-sensors.c index eb2cc78..37d4af7 100644 --- a/android/hw-sensors.c +++ b/android/hw-sensors.c @@ -158,7 +158,8 @@ 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_qemud_client_recv( void* opaque, uint8_t* msg, int msglen, + QemudClient* client ) { hw_sensors_receive(opaque, msg, msglen); } diff --git a/android/main.c b/android/main.c index ed005d2..cd4162a 100644 --- a/android/main.c +++ b/android/main.c @@ -52,6 +52,7 @@ #include "android/hw-kmsg.h" #include "android/hw-control.h" #include "android/hw-sensors.h" +#include "android/boot-properties.h" #include "android/user-config.h" #include "android/utils/bufprint.h" #include "android/utils/dirscanner.h" @@ -2493,6 +2494,20 @@ int main(int argc, char **argv) } } + /* start the 'boot-properties service, and parse the -prop + * options, if any. + */ + boot_property_init_service(); + + if (opts->prop != NULL) { + ParamList* pl = opts->prop; + for ( ; pl != NULL; pl = pl->next ) { + boot_property_parse_option(pl->param); + } + } + + /* Setup the kernel init options + */ { static char params[1024]; char *p = params, *end = p + sizeof(params); diff --git a/docs/ANDROID-QEMUD-SERVICES.TXT b/docs/ANDROID-QEMUD-SERVICES.TXT index 24636e5..4ac53f4 100644 --- a/docs/ANDROID-QEMUD-SERVICES.TXT +++ b/docs/ANDROID-QEMUD-SERVICES.TXT @@ -153,3 +153,28 @@ image. Implementation: android/hw-sensors.c Since: SDK 1.5 (cupcake) + + +"boot-properties" service: +-------------------------- + + This service is used to set system properties in the emulated system at + boot time. It is invoked by the 'qemu-props' helper program that is invoked + by /system/etc/init.goldfish.rc. All messages are framed and the protocol + is the following: + + 1/ Clients sends the 'list' command + + 2/ Service answers by listing all system properties to set. One per + message with the following format: + + <property-name>=<property-value> + + Note that <property-value> is not zero-terminated. + + + 3/ After sending all the properties, the service force-closes the + connection. + + Implementation: android/boot-properties.c + Since: SDK 1.XXX (donut) |