qemu.c revision 5ce01674d44addab570009ceddeaa08d64534092
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* this file contains various functions used by all libhardware modules
18 * that support QEMU emulation
19 */
20#include "qemu.h"
21#define  LOG_TAG  "hardware-qemu"
22#include <cutils/log.h>
23#include <cutils/properties.h>
24#include <cutils/sockets.h>
25#include <errno.h>
26#include <fcntl.h>
27#include <termios.h>
28#include <stdio.h>
29#include <stdarg.h>
30
31#define  QEMU_DEBUG  0
32
33#if QEMU_DEBUG
34#  define  D(...)   LOGD(__VA_ARGS__)
35#else
36#  define  D(...)   ((void)0)
37#endif
38
39
40int
41qemu_check(void)
42{
43    static int  in_qemu = -1;
44
45    if (__builtin_expect(in_qemu < 0,0)) {
46        char  propBuf[PROPERTY_VALUE_MAX];
47        property_get("ro.kernel.qemu", propBuf, "");
48        in_qemu = (propBuf[0] == '1');
49    }
50    return in_qemu;
51}
52
53
54int
55qemu_channel_open( QemuChannel*  channel,
56                   const char*   name,
57                   int           mode )
58{
59    int  fd = -1;
60
61    /* initialize the channel is needed */
62    if (!channel->is_inited)
63    {
64        int  done = 0;
65
66        // try to connect to qemud socket first
67        do {
68            snprintf(channel->device, sizeof channel->device,
69                     "qemud_%s", name);
70
71            fd = socket_local_client( channel->device,
72                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
73                                      SOCK_STREAM );
74            if (fd < 0) {
75                D("no '%s' control socket available: %s",
76                  channel->device, strerror(errno));
77                break;
78            }
79            close(fd);
80            channel->is_qemud = 1;
81            done = 1;
82        } while (0);
83
84        // otherwise, look for a kernel-provided device name
85        if (!done) do {
86            char   key[PROPERTY_KEY_MAX];
87            char   prop[PROPERTY_VALUE_MAX];
88            int    ret;
89
90            ret = snprintf(key, sizeof key, "ro.kernel.android.%s", name);
91            if (ret >= (int)sizeof key)
92                break;
93
94            if (property_get(key, prop, "") == 0) {
95                D("no kernel-provided %s device name", name);
96                break;
97            }
98
99            ret = snprintf(channel->device, sizeof channel->device,
100                           "/dev/%s", prop);
101            if (ret >= (int)sizeof channel->device) {
102                D("%s device name too long: '%s'", name, prop);
103                break;
104            }
105            channel->is_tty = !memcmp("/dev/tty", channel->device, 8);
106            done            = 1;
107
108        } while (0);
109
110        channel->is_available = done;
111        channel->is_inited    = 1;
112    }
113
114    /* try to open the file */
115    if (!channel->is_available) {
116        fd = -1;
117        errno = ENOENT;
118    } else if (channel->is_qemud) {
119        do {
120            fd = socket_local_client( channel->device,
121                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
122                                      SOCK_STREAM );
123        } while (fd < 0 && errno == EINTR);
124    } else {
125        do {
126            fd = open(channel->device, mode);
127        } while (fd < 0 && errno == EINTR);
128
129        /* disable ECHO on serial lines */
130        if (fd >= 0 && channel->is_tty) {
131            struct termios  ios;
132            tcgetattr( fd, &ios );
133            ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
134            tcsetattr( fd, TCSANOW, &ios );
135        }
136    }
137    return fd;
138}
139
140
141static int
142qemu_command_vformat( char*        buffer,
143                      int          buffer_size,
144                      const char*  format,
145                      va_list      args )
146{
147    char     header[5];
148    int      len;
149
150    if (buffer_size < 6)
151        return -1;
152
153    len = vsnprintf(buffer+4, buffer_size-4, format, args);
154    if (len >= buffer_size-4)
155        return -1;
156
157    snprintf(header, sizeof header, "%04x", len);
158    memcpy(buffer, header, 4);
159    return len + 4;
160}
161
162extern int
163qemu_command_format( char*        buffer,
164                     int          buffer_size,
165                     const char*  format,
166                     ... )
167{
168    va_list  args;
169    int      ret;
170
171    va_start(args, format);
172    ret = qemu_command_format(buffer, buffer_size, format, args);
173    va_end(args);
174    return ret;
175}
176
177
178static int
179qemu_control_fd(void)
180{
181    static QemuChannel  channel[1];
182    int                 fd;
183
184    fd = qemu_channel_open( channel, "control", O_RDWR );
185    if (fd < 0) {
186        D("%s: could not open control channel: %s", __FUNCTION__,
187          strerror(errno));
188    }
189    return fd;
190}
191
192static int
193qemu_control_write( int  fd, const char*  cmd, int  len )
194{
195    int  len2;
196    do {
197        len2 = write(fd, cmd, len);
198    } while (len2 < 0 && errno == EINTR);
199    return len2;
200}
201
202static int
203qemu_control_read( int  fd, char*  buff, int  len )
204{
205    int  len2;
206    do {
207        len2 = read(fd, buff, len);
208    } while (len2 < 0 && errno == EINTR);
209    return len2;
210}
211
212static int
213qemu_control_send(const char*  cmd, int  len)
214{
215    int  fd, len2;
216
217    if (len < 0) {
218        errno = EINVAL;
219        return -1;
220    }
221
222    fd = qemu_control_fd();
223    if (fd < 0)
224        return -1;
225
226    len2 = qemu_control_write(fd, cmd, len);
227    close(fd);
228    if (len2 != len) {
229        D("%s: could not send everything %d < %d",
230          __FUNCTION__, len2, len);
231        return -1;
232    }
233    return 0;
234}
235
236
237int
238qemu_control_command( const char*  fmt, ... )
239{
240    va_list  args;
241    char     command[256];
242    int      len, fd;
243
244    va_start(args, fmt);
245    len = qemu_command_vformat( command, sizeof command, fmt, args );
246    va_end(args);
247
248    if (len < 0 || len >= (int)sizeof command) {
249        if (len < 0) {
250            D("%s: could not send: %s", __FUNCTION__, strerror(errno));
251        } else {
252            D("%s: too large %d > %d", __FUNCTION__, len, (int)(sizeof command));
253        }
254        errno = EINVAL;
255        return -1;
256    }
257
258    return qemu_control_send( command, len );
259}
260
261extern int  qemu_control_query( const char*  question, int  questionlen,
262                                char*        answer,   int  answersize )
263{
264    int   ret, fd, len, result = -1;
265    char  header[5], *end;
266
267    if (questionlen <= 0) {
268        errno = EINVAL;
269        return -1;
270    }
271
272    fd = qemu_control_fd();
273    if (fd < 0)
274        return -1;
275
276    ret = qemu_control_write( fd, question, questionlen );
277    if (ret != questionlen) {
278        D("%s: could not write all: %d < %d", __FUNCTION__,
279          ret, questionlen);
280        goto Exit;
281    }
282
283    /* read a 4-byte header giving the length of the following content */
284    ret = qemu_control_read( fd, header, 4 );
285    if (ret != 4) {
286        D("%s: could not read header (%d != 4)",
287          __FUNCTION__, ret);
288        goto Exit;
289    }
290
291    header[5] = 0;
292    len = strtol( header, &end,  16 );
293    if ( len < 0 || end == NULL || end != header+4 || len > answersize ) {
294        D("%s: could not parse header: '%s'",
295          __FUNCTION__, header);
296        goto Exit;
297    }
298
299    /* read the answer */
300    ret = qemu_control_read( fd, answer, len );
301    if (ret != len) {
302        D("%s: could not read all of answer %d < %d",
303          __FUNCTION__, ret, len);
304        goto Exit;
305    }
306
307    result = len;
308
309Exit:
310    close(fd);
311    return result;
312}
313