diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-01-15 16:12:14 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-01-15 16:12:14 -0800 |
commit | 5ce01674d44addab570009ceddeaa08d64534092 (patch) | |
tree | 7efa8951d39fd254c9a0eba20180548670228ae5 /qemu | |
download | hardware_libhardware_legacy-5ce01674d44addab570009ceddeaa08d64534092.zip hardware_libhardware_legacy-5ce01674d44addab570009ceddeaa08d64534092.tar.gz hardware_libhardware_legacy-5ce01674d44addab570009ceddeaa08d64534092.tar.bz2 |
auto import from //branches/cupcake/...@126645
Diffstat (limited to 'qemu')
-rw-r--r-- | qemu/Android.mk | 3 | ||||
-rw-r--r-- | qemu/qemu.c | 312 |
2 files changed, 315 insertions, 0 deletions
diff --git a/qemu/Android.mk b/qemu/Android.mk new file mode 100644 index 0000000..5171754 --- /dev/null +++ b/qemu/Android.mk @@ -0,0 +1,3 @@ +ifeq ($(QEMU_HARDWARE),true) +LOCAL_SRC_FILES += qemu/qemu.c +endif diff --git a/qemu/qemu.c b/qemu/qemu.c new file mode 100644 index 0000000..2631c58 --- /dev/null +++ b/qemu/qemu.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* this file contains various functions used by all libhardware modules + * that support QEMU emulation + */ +#include "qemu.h" +#define LOG_TAG "hardware-qemu" +#include <cutils/log.h> +#include <cutils/properties.h> +#include <cutils/sockets.h> +#include <errno.h> +#include <fcntl.h> +#include <termios.h> +#include <stdio.h> +#include <stdarg.h> + +#define QEMU_DEBUG 0 + +#if QEMU_DEBUG +# define D(...) LOGD(__VA_ARGS__) +#else +# define D(...) ((void)0) +#endif + + +int +qemu_check(void) +{ + static int in_qemu = -1; + + if (__builtin_expect(in_qemu < 0,0)) { + char propBuf[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", propBuf, ""); + in_qemu = (propBuf[0] == '1'); + } + return in_qemu; +} + + +int +qemu_channel_open( QemuChannel* channel, + const char* name, + int mode ) +{ + int fd = -1; + + /* initialize the channel is needed */ + if (!channel->is_inited) + { + int done = 0; + + // try to connect to qemud socket first + do { + snprintf(channel->device, sizeof channel->device, + "qemud_%s", name); + + fd = socket_local_client( channel->device, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM ); + if (fd < 0) { + D("no '%s' control socket available: %s", + channel->device, strerror(errno)); + break; + } + close(fd); + channel->is_qemud = 1; + done = 1; + } while (0); + + // otherwise, look for a kernel-provided device name + if (!done) do { + char key[PROPERTY_KEY_MAX]; + char prop[PROPERTY_VALUE_MAX]; + int ret; + + ret = snprintf(key, sizeof key, "ro.kernel.android.%s", name); + if (ret >= (int)sizeof key) + break; + + if (property_get(key, prop, "") == 0) { + D("no kernel-provided %s device name", name); + break; + } + + ret = snprintf(channel->device, sizeof channel->device, + "/dev/%s", prop); + if (ret >= (int)sizeof channel->device) { + D("%s device name too long: '%s'", name, prop); + break; + } + channel->is_tty = !memcmp("/dev/tty", channel->device, 8); + done = 1; + + } while (0); + + channel->is_available = done; + channel->is_inited = 1; + } + + /* try to open the file */ + if (!channel->is_available) { + fd = -1; + errno = ENOENT; + } else if (channel->is_qemud) { + do { + fd = socket_local_client( channel->device, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM ); + } while (fd < 0 && errno == EINTR); + } else { + do { + fd = open(channel->device, mode); + } while (fd < 0 && errno == EINTR); + + /* disable ECHO on serial lines */ + if (fd >= 0 && channel->is_tty) { + struct termios ios; + tcgetattr( fd, &ios ); + ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */ + tcsetattr( fd, TCSANOW, &ios ); + } + } + return fd; +} + + +static int +qemu_command_vformat( char* buffer, + int buffer_size, + const char* format, + va_list args ) +{ + char header[5]; + int len; + + if (buffer_size < 6) + return -1; + + len = vsnprintf(buffer+4, buffer_size-4, format, args); + if (len >= buffer_size-4) + return -1; + + snprintf(header, sizeof header, "%04x", len); + memcpy(buffer, header, 4); + return len + 4; +} + +extern int +qemu_command_format( char* buffer, + int buffer_size, + const char* format, + ... ) +{ + va_list args; + int ret; + + va_start(args, format); + ret = qemu_command_format(buffer, buffer_size, format, args); + va_end(args); + return ret; +} + + +static int +qemu_control_fd(void) +{ + static QemuChannel channel[1]; + int fd; + + fd = qemu_channel_open( channel, "control", O_RDWR ); + if (fd < 0) { + D("%s: could not open control channel: %s", __FUNCTION__, + strerror(errno)); + } + return fd; +} + +static int +qemu_control_write( int fd, const char* cmd, int len ) +{ + int len2; + do { + len2 = write(fd, cmd, len); + } while (len2 < 0 && errno == EINTR); + return len2; +} + +static int +qemu_control_read( int fd, char* buff, int len ) +{ + int len2; + do { + len2 = read(fd, buff, len); + } while (len2 < 0 && errno == EINTR); + return len2; +} + +static int +qemu_control_send(const char* cmd, int len) +{ + int fd, len2; + + if (len < 0) { + errno = EINVAL; + return -1; + } + + fd = qemu_control_fd(); + if (fd < 0) + return -1; + + len2 = qemu_control_write(fd, cmd, len); + close(fd); + if (len2 != len) { + D("%s: could not send everything %d < %d", + __FUNCTION__, len2, len); + return -1; + } + return 0; +} + + +int +qemu_control_command( const char* fmt, ... ) +{ + va_list args; + char command[256]; + int len, fd; + + va_start(args, fmt); + len = qemu_command_vformat( command, sizeof command, fmt, args ); + va_end(args); + + if (len < 0 || len >= (int)sizeof command) { + if (len < 0) { + D("%s: could not send: %s", __FUNCTION__, strerror(errno)); + } else { + D("%s: too large %d > %d", __FUNCTION__, len, (int)(sizeof command)); + } + errno = EINVAL; + return -1; + } + + return qemu_control_send( command, len ); +} + +extern int qemu_control_query( const char* question, int questionlen, + char* answer, int answersize ) +{ + int ret, fd, len, result = -1; + char header[5], *end; + + if (questionlen <= 0) { + errno = EINVAL; + return -1; + } + + fd = qemu_control_fd(); + if (fd < 0) + return -1; + + ret = qemu_control_write( fd, question, questionlen ); + if (ret != questionlen) { + D("%s: could not write all: %d < %d", __FUNCTION__, + ret, questionlen); + goto Exit; + } + + /* read a 4-byte header giving the length of the following content */ + ret = qemu_control_read( fd, header, 4 ); + if (ret != 4) { + D("%s: could not read header (%d != 4)", + __FUNCTION__, ret); + goto Exit; + } + + header[5] = 0; + len = strtol( header, &end, 16 ); + if ( len < 0 || end == NULL || end != header+4 || len > answersize ) { + D("%s: could not parse header: '%s'", + __FUNCTION__, header); + goto Exit; + } + + /* read the answer */ + ret = qemu_control_read( fd, answer, len ); + if (ret != len) { + D("%s: could not read all of answer %d < %d", + __FUNCTION__, ret, len); + goto Exit; + } + + result = len; + +Exit: + close(fd); + return result; +} |