blob: 2631c584d720c8e962b58c1b25c1f47ee3cc0c92 [file] [log] [blame]
The Android Open Source Project51704be2008-12-17 18:05:50 -08001/*
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}