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