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(...)   ALOGD(__VA_ARGS__)
35#else
36#  define  D(...)   ((void)0)
37#endif
38
39#include "hardware/qemu_pipe.h"
40
41int
42qemu_check(void)
43{
44    static int  in_qemu = -1;
45
46    if (__builtin_expect(in_qemu < 0,0)) {
47        char  propBuf[PROPERTY_VALUE_MAX];
48        property_get("ro.kernel.qemu", propBuf, "");
49        in_qemu = (propBuf[0] == '1');
50    }
51    return in_qemu;
52}
53
54static int
55qemu_fd_write( int  fd, const char*  cmd, int  len )
56{
57    int  len2;
58    do {
59        len2 = write(fd, cmd, len);
60    } while (len2 < 0 && errno == EINTR);
61    return len2;
62}
63
64static int
65qemu_fd_read( int  fd, char*  buff, int  len )
66{
67    int  len2;
68    do {
69        len2 = read(fd, buff, len);
70    } while (len2 < 0 && errno == EINTR);
71    return len2;
72}
73
74static int
75qemu_channel_open_qemud_pipe( QemuChannel*  channel,
76                              const char*   name )
77{
78    int   fd;
79    char  pipe_name[512];
80
81    snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", name);
82    fd = qemu_pipe_open(pipe_name);
83    if (fd < 0) {
84        D("no qemud pipe: %s", strerror(errno));
85        return -1;
86    }
87
88    channel->is_qemud = 1;
89    channel->fd       = fd;
90    return 0;
91}
92
93static int
94qemu_channel_open_qemud( QemuChannel*  channel,
95                         const char*   name )
96{
97    int   fd, ret, namelen = strlen(name);
98    char  answer[2];
99
100    fd = socket_local_client( "qemud",
101                              ANDROID_SOCKET_NAMESPACE_RESERVED,
102                              SOCK_STREAM );
103    if (fd < 0) {
104        D("no qemud control socket: %s", strerror(errno));
105        return -1;
106    }
107
108    /* send service name to connect */
109    if (qemu_fd_write(fd, name, namelen) != namelen) {
110        D("can't send service name to qemud: %s",
111           strerror(errno));
112        close(fd);
113        return -1;
114    }
115
116    /* read answer from daemon */
117    if (qemu_fd_read(fd, answer, 2) != 2 ||
118        answer[0] != 'O' || answer[1] != 'K') {
119        D("cant' connect to %s service through qemud", name);
120        close(fd);
121        return -1;
122    }
123
124    channel->is_qemud = 1;
125    channel->fd       = fd;
126    return 0;
127}
128
129
130static int
131qemu_channel_open_qemud_old( QemuChannel*  channel,
132                             const char*   name )
133{
134    int  fd;
135
136    snprintf(channel->device, sizeof channel->device,
137                "qemud_%s", name);
138
139    fd = socket_local_client( channel->device,
140                              ANDROID_SOCKET_NAMESPACE_RESERVED,
141                              SOCK_STREAM );
142    if (fd < 0) {
143        D("no '%s' control socket available: %s",
144            channel->device, strerror(errno));
145        return -1;
146    }
147
148    close(fd);
149    channel->is_qemud_old = 1;
150    return 0;
151}
152
153
154static int
155qemu_channel_open_tty( QemuChannel*  channel,
156                       const char*   name,
157                       int           mode )
158{
159    char   key[PROPERTY_KEY_MAX];
160    char   prop[PROPERTY_VALUE_MAX];
161    int    ret;
162
163    ret = snprintf(key, sizeof key, "ro.kernel.android.%s", name);
164    if (ret >= (int)sizeof key)
165        return -1;
166
167    if (property_get(key, prop, "") == 0) {
168        D("no kernel-provided %s device name", name);
169        return -1;
170    }
171
172    ret = snprintf(channel->device, sizeof channel->device,
173                    "/dev/%s", prop);
174    if (ret >= (int)sizeof channel->device) {
175        D("%s device name too long: '%s'", name, prop);
176        return -1;
177    }
178
179    channel->is_tty = !memcmp("/dev/tty", channel->device, 8);
180    return 0;
181}
182
183int
184qemu_channel_open( QemuChannel*  channel,
185                   const char*   name,
186                   int           mode )
187{
188    int  fd = -1;
189
190    /* initialize the channel is needed */
191    if (!channel->is_inited)
192    {
193        channel->is_inited = 1;
194
195        do {
196            if (qemu_channel_open_qemud_pipe(channel, name) == 0)
197                break;
198
199            if (qemu_channel_open_qemud(channel, name) == 0)
200                break;
201
202            if (qemu_channel_open_qemud_old(channel, name) == 0)
203                break;
204
205            if (qemu_channel_open_tty(channel, name, mode) == 0)
206                break;
207
208            channel->is_available = 0;
209            return -1;
210        } while (0);
211
212        channel->is_available = 1;
213    }
214
215    /* try to open the file */
216    if (!channel->is_available) {
217        errno = ENOENT;
218        return -1;
219    }
220
221    if (channel->is_qemud) {
222        return dup(channel->fd);
223    }
224
225    if (channel->is_qemud_old) {
226        do {
227            fd = socket_local_client( channel->device,
228                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
229                                      SOCK_STREAM );
230        } while (fd < 0 && errno == EINTR);
231    }
232    else /* /dev/ttySn ? */
233    {
234        do {
235            fd = open(channel->device, mode);
236        } while (fd < 0 && errno == EINTR);
237
238        /* disable ECHO on serial lines */
239        if (fd >= 0 && channel->is_tty) {
240            struct termios  ios;
241            tcgetattr( fd, &ios );
242            ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
243            tcsetattr( fd, TCSANOW, &ios );
244        }
245    }
246    return fd;
247}
248
249
250static int
251qemu_command_vformat( char*        buffer,
252                      int          buffer_size,
253                      const char*  format,
254                      va_list      args )
255{
256    char     header[5];
257    int      len;
258
259    if (buffer_size < 6)
260        return -1;
261
262    len = vsnprintf(buffer+4, buffer_size-4, format, args);
263    if (len >= buffer_size-4)
264        return -1;
265
266    snprintf(header, sizeof header, "%04x", len);
267    memcpy(buffer, header, 4);
268    return len + 4;
269}
270
271extern int
272qemu_command_format( char*        buffer,
273                     int          buffer_size,
274                     const char*  format,
275                     ... )
276{
277    va_list  args;
278    int      ret;
279
280    va_start(args, format);
281    ret = qemu_command_format(buffer, buffer_size, format, args);
282    va_end(args);
283    return ret;
284}
285
286
287static int
288qemu_control_fd(void)
289{
290    static QemuChannel  channel[1];
291    int                 fd;
292
293    fd = qemu_channel_open( channel, "hw-control", O_RDWR );
294    if (fd < 0) {
295        D("%s: could not open control channel: %s", __FUNCTION__,
296          strerror(errno));
297    }
298    return fd;
299}
300
301static int
302qemu_control_send(const char*  cmd, int  len)
303{
304    int  fd, len2;
305
306    if (len < 0) {
307        errno = EINVAL;
308        return -1;
309    }
310
311    fd = qemu_control_fd();
312    if (fd < 0)
313        return -1;
314
315    len2 = qemu_fd_write(fd, cmd, len);
316    close(fd);
317    if (len2 != len) {
318        D("%s: could not send everything %d < %d",
319          __FUNCTION__, len2, len);
320        return -1;
321    }
322    return 0;
323}
324
325
326int
327qemu_control_command( const char*  fmt, ... )
328{
329    va_list  args;
330    char     command[256];
331    int      len, fd;
332
333    va_start(args, fmt);
334    len = qemu_command_vformat( command, sizeof command, fmt, args );
335    va_end(args);
336
337    if (len < 0 || len >= (int)sizeof command) {
338        if (len < 0) {
339            D("%s: could not send: %s", __FUNCTION__, strerror(errno));
340        } else {
341            D("%s: too large %d > %d", __FUNCTION__, len, (int)(sizeof command));
342        }
343        errno = EINVAL;
344        return -1;
345    }
346
347    return qemu_control_send( command, len );
348}
349
350extern int  qemu_control_query( const char*  question, int  questionlen,
351                                char*        answer,   int  answersize )
352{
353    int   ret, fd, len, result = -1;
354    char  header[5], *end;
355
356    if (questionlen <= 0) {
357        errno = EINVAL;
358        return -1;
359    }
360
361    fd = qemu_control_fd();
362    if (fd < 0)
363        return -1;
364
365    ret = qemu_fd_write( fd, question, questionlen );
366    if (ret != questionlen) {
367        D("%s: could not write all: %d < %d", __FUNCTION__,
368          ret, questionlen);
369        goto Exit;
370    }
371
372    /* read a 4-byte header giving the length of the following content */
373    ret = qemu_fd_read( fd, header, 4 );
374    if (ret != 4) {
375        D("%s: could not read header (%d != 4)",
376          __FUNCTION__, ret);
377        goto Exit;
378    }
379
380    header[4] = 0;
381    len = strtol( header, &end,  16 );
382    if ( len < 0 || end == NULL || end != header+4 || len > answersize ) {
383        D("%s: could not parse header: '%s'",
384          __FUNCTION__, header);
385        goto Exit;
386    }
387
388    /* read the answer */
389    ret = qemu_fd_read( fd, answer, len );
390    if (ret != len) {
391        D("%s: could not read all of answer %d < %d",
392          __FUNCTION__, ret, len);
393        goto Exit;
394    }
395
396    result = len;
397
398Exit:
399    close(fd);
400    return result;
401}
402