1 /* Copyright (C) 2007-2008 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 /* 13 * Android emulator control console 14 * 15 * this console is enabled automatically at emulator startup, on port 5554 by default, 16 * unless some other emulator is already running. See (android_emulation_start in android_sdl.c 17 * for details) 18 * 19 * you can telnet to the console, then use commands like 'help' or others to dynamically 20 * change emulator settings. 21 * 22 */ 23 24 #include "android/sockets.h" 25 #include "sysemu/char.h" 26 #include "sysemu/sysemu.h" 27 #include "android/android.h" 28 #include "cpu.h" 29 #include "hw/android/goldfish/device.h" 30 #include "hw/power_supply.h" 31 #include "android/shaper.h" 32 #include "modem_driver.h" 33 #include "android/gps.h" 34 #include "android/globals.h" 35 #include "android/utils/bufprint.h" 36 #include "android/utils/debug.h" 37 #include "android/utils/eintr_wrapper.h" 38 #include "android/utils/stralloc.h" 39 #include "android/config/config.h" 40 #include "android/tcpdump.h" 41 #include "net/net.h" 42 #include "monitor/monitor.h" 43 44 #include <stdlib.h> 45 #include <stdio.h> 46 #include <stdarg.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <fcntl.h> 50 #include "android/hw-events.h" 51 #include "android/user-events.h" 52 #include "android/hw-sensors.h" 53 #include "android/keycode-array.h" 54 #include "android/charmap.h" 55 #include "android/display-core.h" 56 57 #if defined(CONFIG_SLIRP) 58 #include "libslirp.h" 59 #endif 60 61 extern void android_emulator_set_window_scale(double, int); 62 63 #define DEBUG 1 64 65 #if 1 66 # define D_ACTIVE VERBOSE_CHECK(console) 67 #else 68 # define D_ACTIVE DEBUG 69 #endif 70 71 #if DEBUG 72 # define D(x) do { if (D_ACTIVE) ( printf x , fflush(stdout) ); } while (0) 73 #else 74 # define D(x) do{}while(0) 75 #endif 76 77 typedef struct ControlGlobalRec_* ControlGlobal; 78 79 typedef struct ControlClientRec_* ControlClient; 80 81 typedef struct { 82 int host_port; 83 int host_udp; 84 unsigned int guest_ip; 85 int guest_port; 86 } RedirRec, *Redir; 87 88 89 typedef int Socket; 90 91 typedef struct ControlClientRec_ 92 { 93 struct ControlClientRec_* next; /* next client in list */ 94 Socket sock; /* socket used for communication */ 95 ControlGlobal global; 96 char finished; 97 char buff[ 4096 ]; 98 int buff_len; 99 100 } ControlClientRec; 101 102 103 typedef struct ControlGlobalRec_ 104 { 105 /* listening socket */ 106 Socket listen_fd; 107 108 /* the list of current clients */ 109 ControlClient clients; 110 111 /* the list of redirections currently active */ 112 Redir redirs; 113 int num_redirs; 114 int max_redirs; 115 116 } ControlGlobalRec; 117 118 #ifdef CONFIG_STANDALONE_CORE 119 /* UI client currently attached to the core. */ 120 ControlClient attached_ui_client = NULL; 121 122 /* User events service client. */ 123 ControlClient user_events_client = NULL; 124 125 /* UI control service client (UI -> Core). */ 126 ControlClient ui_core_ctl_client = NULL; 127 128 /* UI control service (UI -> Core. */ 129 // CoreUICtl* ui_core_ctl = NULL; 130 131 /* UI control service client (Core-> UI). */ 132 ControlClient core_ui_ctl_client = NULL; 133 #endif // CONFIG_STANDALONE_CORE 134 135 static int 136 control_global_add_redir( ControlGlobal global, 137 int host_port, 138 int host_udp, 139 unsigned int guest_ip, 140 int guest_port ) 141 { 142 Redir redir; 143 144 if (global->num_redirs >= global->max_redirs) 145 { 146 int old_max = global->max_redirs; 147 int new_max = old_max + (old_max >> 1) + 4; 148 149 Redir new_redirs = realloc( global->redirs, new_max*sizeof(global->redirs[0]) ); 150 if (new_redirs == NULL) 151 return -1; 152 153 global->redirs = new_redirs; 154 global->max_redirs = new_max; 155 } 156 157 redir = &global->redirs[ global->num_redirs++ ]; 158 159 redir->host_port = host_port; 160 redir->host_udp = host_udp; 161 redir->guest_ip = guest_ip; 162 redir->guest_port = guest_port; 163 164 return 0; 165 } 166 167 static int 168 control_global_del_redir( ControlGlobal global, 169 int host_port, 170 int host_udp ) 171 { 172 int nn; 173 174 for (nn = 0; nn < global->num_redirs; nn++) 175 { 176 Redir redir = &global->redirs[nn]; 177 178 if ( redir->host_port == host_port && 179 redir->host_udp == host_udp ) 180 { 181 memmove( redir, redir + 1, ((global->num_redirs - nn)-1)*sizeof(*redir) ); 182 global->num_redirs -= 1; 183 return 0; 184 } 185 } 186 /* we didn't find it */ 187 return -1; 188 } 189 190 /* Detach the socket descriptor from a given ControlClient 191 * and return its value. This is useful either when destroying 192 * the client, or redirecting the socket to another service. 193 * 194 * NOTE: this does not close the socket. 195 */ 196 static int 197 control_client_detach( ControlClient client ) 198 { 199 int result; 200 201 if (client->sock < 0) 202 return -1; 203 204 qemu_set_fd_handler( client->sock, NULL, NULL, NULL ); 205 result = client->sock; 206 client->sock = -1; 207 208 return result; 209 } 210 211 static void control_client_read( void* _client ); /* forward */ 212 213 static void 214 control_client_destroy( ControlClient client ) 215 { 216 ControlGlobal global = client->global; 217 ControlClient *pnode = &global->clients; 218 int sock; 219 220 D(( "destroying control client %p\n", client )); 221 222 #ifdef CONFIG_STANDALONE_CORE 223 if (client == attached_ui_client) { 224 attachUiProxy_destroy(); 225 attached_ui_client = NULL; 226 } 227 228 if (client == user_events_client) { 229 userEventsImpl_destroy(); 230 user_events_client = NULL; 231 } 232 233 if (client == ui_core_ctl_client) { 234 coreCmdImpl_destroy(); 235 ui_core_ctl_client = NULL; 236 } 237 238 if (client == core_ui_ctl_client) { 239 uiCmdProxy_destroy(); 240 core_ui_ctl_client = NULL; 241 } 242 #endif // CONFIG_STANDALONE_CORE 243 244 sock = control_client_detach( client ); 245 if (sock >= 0) 246 socket_close(sock); 247 248 for ( ;; ) { 249 ControlClient node = *pnode; 250 if ( node == NULL ) 251 break; 252 if ( node == client ) { 253 *pnode = node->next; 254 node->next = NULL; 255 break; 256 } 257 pnode = &node->next; 258 } 259 260 free( client ); 261 } 262 263 264 265 static void control_control_write( ControlClient client, const char* buff, int len ) 266 { 267 int ret; 268 269 if (len < 0) 270 len = strlen(buff); 271 272 while (len > 0) { 273 ret = HANDLE_EINTR(socket_send( client->sock, buff, len)); 274 if (ret < 0) { 275 if (errno != EWOULDBLOCK && errno != EAGAIN) 276 return; 277 } else { 278 buff += ret; 279 len -= ret; 280 } 281 } 282 } 283 284 static int control_vwrite( ControlClient client, const char* format, va_list args ) 285 { 286 static char temp[1024]; 287 int ret = vsnprintf( temp, sizeof(temp), format, args ); 288 temp[ sizeof(temp)-1 ] = 0; 289 control_control_write( client, temp, -1 ); 290 291 return ret; 292 } 293 294 static int control_write( ControlClient client, const char* format, ... ) 295 { 296 int ret; 297 va_list args; 298 va_start(args, format); 299 ret = control_vwrite(client, format, args); 300 va_end(args); 301 302 return ret; 303 } 304 305 306 static ControlClient 307 control_client_create( Socket socket, 308 ControlGlobal global ) 309 { 310 ControlClient client = calloc( sizeof(*client), 1 ); 311 312 if (client) { 313 socket_set_nodelay( socket ); 314 socket_set_nonblock( socket ); 315 client->finished = 0; 316 client->global = global; 317 client->sock = socket; 318 client->next = global->clients; 319 global->clients = client; 320 321 qemu_set_fd_handler( socket, control_client_read, NULL, client ); 322 } 323 return client; 324 } 325 326 typedef const struct CommandDefRec_ *CommandDef; 327 328 typedef struct CommandDefRec_ { 329 const char* names; 330 const char* abstract; 331 const char* description; 332 void (*descriptor)( ControlClient client ); 333 int (*handler)( ControlClient client, char* args ); 334 CommandDef subcommands; /* if handler is NULL */ 335 336 } CommandDefRec; 337 338 static const CommandDefRec main_commands[]; /* forward */ 339 340 static CommandDef 341 find_command( char* input, CommandDef commands, char* *pend, char* *pargs ) 342 { 343 int nn; 344 char* args = strchr(input, ' '); 345 346 if (args != NULL) { 347 while (*args == ' ') 348 args++; 349 350 if (args[0] == 0) 351 args = NULL; 352 } 353 354 for (nn = 0; commands[nn].names != NULL; nn++) 355 { 356 const char* name = commands[nn].names; 357 const char* sep; 358 359 do { 360 int len, c; 361 362 sep = strchr( name, '|' ); 363 if (sep) 364 len = sep - name; 365 else 366 len = strlen(name); 367 368 c = input[len]; 369 if ( !memcmp( name, input, len ) && (c == ' ' || c == 0) ) { 370 *pend = input + len; 371 *pargs = args; 372 return &commands[nn]; 373 } 374 375 if (sep) 376 name = sep + 1; 377 378 } while (sep != NULL && *name); 379 } 380 /* NOTE: don't touch *pend and *pargs if no command is found */ 381 return NULL; 382 } 383 384 static void 385 dump_help( ControlClient client, 386 CommandDef cmd, 387 const char* prefix ) 388 { 389 if (cmd->description) { 390 control_write( client, "%s", cmd->description ); 391 } else if (cmd->descriptor) { 392 cmd->descriptor( client ); 393 } else 394 control_write( client, "%s\r\n", cmd->abstract ); 395 396 if (cmd->subcommands) { 397 cmd = cmd->subcommands; 398 control_write( client, "\r\navailable sub-commands:\r\n" ); 399 for ( ; cmd->names != NULL; cmd++ ) { 400 control_write( client, " %s %-15s %s\r\n", prefix, cmd->names, cmd->abstract ); 401 } 402 control_write( client, "\r\n" ); 403 } 404 } 405 406 static void 407 control_client_do_command( ControlClient client ) 408 { 409 char* line = client->buff; 410 char* args = NULL; 411 CommandDef commands = main_commands; 412 char* cmdend = client->buff; 413 CommandDef cmd = find_command( line, commands, &cmdend, &args ); 414 415 if (cmd == NULL) { 416 control_write( client, "KO: unknown command, try 'help'\r\n" ); 417 return; 418 } 419 420 for (;;) { 421 CommandDef subcmd; 422 423 if (cmd->handler) { 424 if ( !cmd->handler( client, args ) ) { 425 control_write( client, "OK\r\n" ); 426 } 427 break; 428 } 429 430 /* no handler means we should have sub-commands */ 431 if (cmd->subcommands == NULL) { 432 control_write( client, "KO: internal error: buggy command table for '%.*s'\r\n", 433 cmdend - client->buff, client->buff ); 434 break; 435 } 436 437 /* we need a sub-command here */ 438 if ( !args ) { 439 dump_help( client, cmd, "" ); 440 control_write( client, "KO: missing sub-command\r\n" ); 441 break; 442 } 443 444 line = args; 445 commands = cmd->subcommands; 446 subcmd = find_command( line, commands, &cmdend, &args ); 447 if (subcmd == NULL) { 448 dump_help( client, cmd, "" ); 449 control_write( client, "KO: bad sub-command\r\n" ); 450 break; 451 } 452 cmd = subcmd; 453 } 454 } 455 456 /* implement the 'help' command */ 457 static int 458 do_help( ControlClient client, char* args ) 459 { 460 char* line; 461 char* start = args; 462 char* end = start; 463 CommandDef cmd = main_commands; 464 465 /* without arguments, simply dump all commands */ 466 if (args == NULL) { 467 control_write( client, "Android console command help:\r\n\r\n" ); 468 for ( ; cmd->names != NULL; cmd++ ) { 469 control_write( client, " %-15s %s\r\n", cmd->names, cmd->abstract ); 470 } 471 control_write( client, "\r\ntry 'help <command>' for command-specific help\r\n" ); 472 return 0; 473 } 474 475 /* with an argument, find the corresponding command */ 476 for (;;) { 477 CommandDef subcmd; 478 479 line = args; 480 subcmd = find_command( line, cmd, &end, &args ); 481 if (subcmd == NULL) { 482 control_write( client, "try one of these instead:\r\n\r\n" ); 483 for ( ; cmd->names != NULL; cmd++ ) { 484 control_write( client, " %.*s %s\r\n", 485 end - start, start, cmd->names ); 486 } 487 control_write( client, "\r\nKO: unknown command\r\n" ); 488 return -1; 489 } 490 491 if ( !args || !subcmd->subcommands ) { 492 dump_help( client, subcmd, start ); 493 return 0; 494 } 495 cmd = subcmd->subcommands; 496 } 497 } 498 499 500 static void 501 control_client_read_byte( ControlClient client, unsigned char ch ) 502 { 503 if (ch == '\r') 504 { 505 /* filter them out */ 506 } 507 else if (ch == '\n') 508 { 509 client->buff[ client->buff_len ] = 0; 510 control_client_do_command( client ); 511 if (client->finished) 512 return; 513 514 client->buff_len = 0; 515 } 516 else 517 { 518 if (client->buff_len >= sizeof(client->buff)-1) 519 client->buff_len = 0; 520 521 client->buff[ client->buff_len++ ] = ch; 522 } 523 } 524 525 static void 526 control_client_read( void* _client ) 527 { 528 ControlClient client = _client; 529 unsigned char buf[4096]; 530 int size; 531 532 D(( "in control_client read: " )); 533 size = socket_recv( client->sock, buf, sizeof(buf) ); 534 if (size < 0) { 535 D(( "size < 0, exiting with %d: %s\n", errno, errno_str )); 536 if (errno != EWOULDBLOCK && errno != EAGAIN) 537 control_client_destroy( client ); 538 return; 539 } 540 541 if (size == 0) { 542 /* end of connection */ 543 D(( "end of connection detected !!\n" )); 544 control_client_destroy( client ); 545 } 546 else { 547 int nn; 548 #ifdef _WIN32 549 # if DEBUG 550 char temp[16]; 551 int count = size > sizeof(temp)-1 ? sizeof(temp)-1 : size; 552 for (nn = 0; nn < count; nn++) { 553 int c = buf[nn]; 554 if (c == '\n') 555 temp[nn] = '!'; 556 else if (c < 32) 557 temp[nn] = '.'; 558 else 559 temp[nn] = (char)c; 560 } 561 temp[nn] = 0; 562 D(( "received %d bytes: %s\n", size, temp )); 563 # endif 564 #else 565 D(( "received %.*s\n", size, buf )); 566 #endif 567 for (nn = 0; nn < size; nn++) { 568 control_client_read_byte( client, buf[nn] ); 569 if (client->finished) { 570 control_client_destroy(client); 571 return; 572 } 573 } 574 } 575 } 576 577 578 /* this function is called on each new client connection */ 579 static void 580 control_global_accept( void* _global ) 581 { 582 ControlGlobal global = _global; 583 ControlClient client; 584 Socket fd; 585 586 D(( "control_global_accept: just in (fd=%d)\n", global->listen_fd )); 587 588 fd = HANDLE_EINTR(socket_accept(global->listen_fd, NULL)); 589 if (fd < 0) { 590 D(( "problem in accept: %d: %s\n", errno, errno_str )); 591 perror("accept"); 592 return; 593 } 594 595 socket_set_xreuseaddr( fd ); 596 597 D(( "control_global_accept: creating new client\n" )); 598 client = control_client_create( fd, global ); 599 if (client) { 600 D(( "control_global_accept: new client %p\n", client )); 601 control_write( client, "Android Console: type 'help' for a list of commands\r\n" ); 602 control_write( client, "OK\r\n" ); 603 } 604 } 605 606 607 static int 608 control_global_init( ControlGlobal global, 609 int control_port ) 610 { 611 Socket fd; 612 int ret; 613 SockAddress sockaddr; 614 615 memset( global, 0, sizeof(*global) ); 616 617 fd = socket_create_inet( SOCKET_STREAM ); 618 if (fd < 0) { 619 perror("socket"); 620 return -1; 621 } 622 623 socket_set_xreuseaddr( fd ); 624 625 sock_address_init_inet( &sockaddr, SOCK_ADDRESS_INET_LOOPBACK, control_port ); 626 627 ret = socket_bind(fd, &sockaddr ); 628 if (ret < 0) { 629 perror("bind"); 630 socket_close( fd ); 631 return -1; 632 } 633 634 ret = socket_listen(fd, 0); 635 if (ret < 0) { 636 perror("listen"); 637 socket_close( fd ); 638 return -1; 639 } 640 641 socket_set_nonblock(fd); 642 643 global->listen_fd = fd; 644 645 qemu_set_fd_handler( fd, control_global_accept, NULL, global ); 646 return 0; 647 } 648 649 650 651 static int 652 do_quit( ControlClient client, char* args ) 653 { 654 client->finished = 1; 655 return -1; 656 } 657 658 /********************************************************************************************/ 659 /********************************************************************************************/ 660 /***** ******/ 661 /***** N E T W O R K S E T T I N G S ******/ 662 /***** ******/ 663 /********************************************************************************************/ 664 /********************************************************************************************/ 665 666 static int 667 do_network_status( ControlClient client, char* args ) 668 { 669 control_write( client, "Current network status:\r\n" ); 670 671 control_write( client, " download speed: %8d bits/s (%.1f KB/s)\r\n", 672 (long)qemu_net_download_speed, qemu_net_download_speed/8192. ); 673 674 control_write( client, " upload speed: %8d bits/s (%.1f KB/s)\r\n", 675 (long)qemu_net_upload_speed, qemu_net_upload_speed/8192. ); 676 677 control_write( client, " minimum latency: %ld ms\r\n", qemu_net_min_latency ); 678 control_write( client, " maximum latency: %ld ms\r\n", qemu_net_max_latency ); 679 return 0; 680 } 681 682 static void 683 dump_network_speeds( ControlClient client ) 684 { 685 const NetworkSpeed* speed = android_netspeeds; 686 const char* const format = " %-8s %s\r\n"; 687 for ( ; speed->name; speed++ ) { 688 control_write( client, format, speed->name, speed->display ); 689 } 690 control_write( client, format, "<num>", "selects both upload and download speed" ); 691 control_write( client, format, "<up>:<down>", "select individual upload/download speeds" ); 692 } 693 694 695 static int 696 do_network_speed( ControlClient client, char* args ) 697 { 698 if ( !args ) { 699 control_write( client, "KO: missing <speed> argument, see 'help network speed'\r\n" ); 700 return -1; 701 } 702 if ( android_parse_network_speed( args ) < 0 ) { 703 control_write( client, "KO: invalid <speed> argument, see 'help network speed' for valid values\r\n" ); 704 return -1; 705 } 706 707 netshaper_set_rate( slirp_shaper_in, qemu_net_download_speed ); 708 netshaper_set_rate( slirp_shaper_out, qemu_net_upload_speed ); 709 710 if (android_modem) { 711 amodem_set_data_network_type( android_modem, 712 android_parse_network_type( args ) ); 713 } 714 return 0; 715 } 716 717 static void 718 describe_network_speed( ControlClient client ) 719 { 720 control_write( client, 721 "'network speed <speed>' allows you to dynamically change the speed of the emulated\r\n" 722 "network on the device, where <speed> is one of the following:\r\n\r\n" ); 723 dump_network_speeds( client ); 724 } 725 726 static int 727 do_network_delay( ControlClient client, char* args ) 728 { 729 if ( !args ) { 730 control_write( client, "KO: missing <delay> argument, see 'help network delay'\r\n" ); 731 return -1; 732 } 733 if ( android_parse_network_latency( args ) < 0 ) { 734 control_write( client, "KO: invalid <delay> argument, see 'help network delay' for valid values\r\n" ); 735 return -1; 736 } 737 netdelay_set_latency( slirp_delay_in, qemu_net_min_latency, qemu_net_max_latency ); 738 return 0; 739 } 740 741 static void 742 describe_network_delay( ControlClient client ) 743 { 744 control_write( client, 745 "'network delay <latency>' allows you to dynamically change the latency of the emulated\r\n" 746 "network on the device, where <latency> is one of the following:\r\n\r\n" ); 747 /* XXX: TODO */ 748 } 749 750 static int 751 do_network_capture_start( ControlClient client, char* args ) 752 { 753 if ( !args ) { 754 control_write( client, "KO: missing <file> argument, see 'help network capture start'\r\n" ); 755 return -1; 756 } 757 if ( qemu_tcpdump_start(args) < 0) { 758 control_write( client, "KO: could not start capture: %s", strerror(errno) ); 759 return -1; 760 } 761 return 0; 762 } 763 764 static int 765 do_network_capture_stop( ControlClient client, char* args ) 766 { 767 /* no need to return an error here */ 768 qemu_tcpdump_stop(); 769 return 0; 770 } 771 772 static const CommandDefRec network_capture_commands[] = 773 { 774 { "start", "start network capture", 775 "'network capture start <file>' starts a new capture of network packets\r\n" 776 "into a specific <file>. This will stop any capture already in progress.\r\n" 777 "the capture file can later be analyzed by tools like WireShark. It uses\r\n" 778 "the libpcap file format.\r\n\r\n" 779 "you can stop the capture anytime with 'network capture stop'\r\n", NULL, 780 do_network_capture_start, NULL }, 781 782 { "stop", "stop network capture", 783 "'network capture stop' stops a currently running packet capture, if any.\r\n" 784 "you can start one with 'network capture start <file>'\r\n", NULL, 785 do_network_capture_stop, NULL }, 786 787 { NULL, NULL, NULL, NULL, NULL, NULL } 788 }; 789 790 static const CommandDefRec network_commands[] = 791 { 792 { "status", "dump network status", NULL, NULL, 793 do_network_status, NULL }, 794 795 { "speed", "change network speed", NULL, describe_network_speed, 796 do_network_speed, NULL }, 797 798 { "delay", "change network latency", NULL, describe_network_delay, 799 do_network_delay, NULL }, 800 801 { "capture", "dump network packets to file", 802 "allows to start/stop capture of network packets to a file for later analysis\r\n", NULL, 803 NULL, network_capture_commands }, 804 805 { NULL, NULL, NULL, NULL, NULL, NULL } 806 }; 807 808 /********************************************************************************************/ 809 /********************************************************************************************/ 810 /***** ******/ 811 /***** P O R T R E D I R E C T I O N S ******/ 812 /***** ******/ 813 /********************************************************************************************/ 814 /********************************************************************************************/ 815 816 static int 817 do_redir_list( ControlClient client, char* args ) 818 { 819 ControlGlobal global = client->global; 820 821 if (global->num_redirs == 0) 822 control_write( client, "no active redirections\r\n" ); 823 else { 824 int nn; 825 for (nn = 0; nn < global->num_redirs; nn++) { 826 Redir redir = &global->redirs[nn]; 827 control_write( client, "%s:%-5d => %-5d\r\n", 828 redir->host_udp ? "udp" : "tcp", 829 redir->host_port, 830 redir->guest_port ); 831 } 832 } 833 return 0; 834 } 835 836 /* parse a protocol:port specification */ 837 static int 838 redir_parse_proto_port( char* args, int *pport, int *pproto ) 839 { 840 int proto = -1; 841 int len = 0; 842 char* end; 843 844 if ( !memcmp( args, "tcp:", 4 ) ) { 845 proto = 0; 846 len = 4; 847 } 848 else if ( !memcmp( args, "udp:", 4 ) ) { 849 proto = 1; 850 len = 4; 851 } 852 else 853 return 0; 854 855 args += len; 856 *pproto = proto; 857 *pport = strtol( args, &end, 10 ); 858 if (end == args) 859 return 0; 860 861 len += end - args; 862 return len; 863 } 864 865 static int 866 redir_parse_guest_port( char* arg, int *pport ) 867 { 868 char* end; 869 870 *pport = strtoul( arg, &end, 10 ); 871 if (end == arg) 872 return 0; 873 874 return end - arg; 875 } 876 877 static Redir 878 redir_find( ControlGlobal global, int port, int isudp ) 879 { 880 int nn; 881 882 for (nn = 0; nn < global->num_redirs; nn++) { 883 Redir redir = &global->redirs[nn]; 884 885 if (redir->host_port == port && redir->host_udp == isudp) 886 return redir; 887 } 888 return NULL; 889 } 890 891 892 static int 893 do_redir_add( ControlClient client, char* args ) 894 { 895 int len, host_proto, host_port, guest_port; 896 uint32_t guest_ip; 897 Redir redir; 898 899 if ( !args ) 900 goto BadFormat; 901 902 if (!slirp_is_inited()) { 903 control_write( client, "KO: network emulation disabled\r\n"); 904 return -1; 905 } 906 907 len = redir_parse_proto_port( args, &host_port, &host_proto ); 908 if (len == 0 || args[len] != ':') 909 goto BadFormat; 910 911 args += len + 1; 912 len = redir_parse_guest_port( args, &guest_port ); 913 if (len == 0 || args[len] != 0) 914 goto BadFormat; 915 916 redir = redir_find( client->global, host_port, host_proto ); 917 if ( redir != NULL ) { 918 control_write( client, "KO: host port already active, use 'redir del' to remove first\r\n" ); 919 return -1; 920 } 921 922 if (inet_strtoip("10.0.2.15", &guest_ip) < 0) { 923 control_write( client, "KO: unexpected internal failure when resolving 10.0.2.15\r\n" ); 924 return -1; 925 } 926 927 D(("pattern hport=%d gport=%d proto=%d\n", host_port, guest_port, host_proto )); 928 if ( control_global_add_redir( client->global, host_port, host_proto, 929 guest_ip, guest_port ) < 0 ) 930 { 931 control_write( client, "KO: not enough memory to allocate redirection\r\n" ); 932 return -1; 933 } 934 935 if (slirp_redir(host_proto, host_port, guest_ip, guest_port) < 0) { 936 control_write( client, "KO: can't setup redirection, port probably used by another program on host\r\n" ); 937 control_global_del_redir( client->global, host_port, host_proto ); 938 return -1; 939 } 940 941 return 0; 942 943 BadFormat: 944 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport:guestport\r\n", -1 ); 945 return -1; 946 } 947 948 949 static int 950 do_redir_del( ControlClient client, char* args ) 951 { 952 int len, proto, port; 953 Redir redir; 954 955 if ( !args ) 956 goto BadFormat; 957 len = redir_parse_proto_port( args, &port, &proto ); 958 if ( len == 0 || args[len] != 0 ) 959 goto BadFormat; 960 961 redir = redir_find( client->global, port, proto ); 962 if (redir == NULL) { 963 control_write( client, "KO: can't remove unknown redirection (%s:%d)\r\n", 964 proto ? "udp" : "tcp", port ); 965 return -1; 966 } 967 968 slirp_unredir( redir->host_udp, redir->host_port ); 969 control_global_del_redir( client->global, port, proto );\ 970 971 return 0; 972 973 BadFormat: 974 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport\r\n" ); 975 return -1; 976 } 977 978 static const CommandDefRec redir_commands[] = 979 { 980 { "list", "list current redirections", 981 "list current port redirections. use 'redir add' and 'redir del' to add and remove them\r\n", NULL, 982 do_redir_list, NULL }, 983 984 { "add", "add new redirection", 985 "add a new port redirection, arguments must be:\r\n\r\n" 986 " redir add <protocol>:<host-port>:<guest-port>\r\n\r\n" 987 "where: <protocol> is either 'tcp' or 'udp'\r\n" 988 " <host-port> a number indicating which port on the host to open\r\n" 989 " <guest-port> a number indicating which port to route to on the device\r\n" 990 "\r\nas an example, 'redir tcp:5000:6000' will allow any packets sent to\r\n" 991 "the host's TCP port 5000 to be routed to TCP port 6000 of the emulated device\r\n", NULL, 992 do_redir_add, NULL }, 993 994 { "del", "remove existing redirection", 995 "remove a port redirecion that was created with 'redir add', arguments must be:\r\n\r\n" 996 " redir del <protocol>:<host-port>\r\n\r\n" 997 "see the 'help redir add' for the meaning of <protocol> and <host-port>\r\n", NULL, 998 do_redir_del, NULL }, 999 1000 { NULL, NULL, NULL, NULL, NULL, NULL } 1001 }; 1002 1003 1004 1005 /********************************************************************************************/ 1006 /********************************************************************************************/ 1007 /***** ******/ 1008 /***** C D M A M O D E M ******/ 1009 /***** ******/ 1010 /********************************************************************************************/ 1011 /********************************************************************************************/ 1012 1013 static const struct { 1014 const char * name; 1015 const char * display; 1016 ACdmaSubscriptionSource source; 1017 } _cdma_subscription_sources[] = { 1018 { "nv", "Read subscription from non-volatile RAM", A_SUBSCRIPTION_NVRAM }, 1019 { "ruim", "Read subscription from RUIM", A_SUBSCRIPTION_RUIM }, 1020 }; 1021 1022 static void 1023 dump_subscription_sources( ControlClient client ) 1024 { 1025 int i; 1026 for (i = 0; 1027 i < sizeof(_cdma_subscription_sources) / sizeof(_cdma_subscription_sources[0]); 1028 i++) { 1029 control_write( client, " %s: %s\r\n", 1030 _cdma_subscription_sources[i].name, 1031 _cdma_subscription_sources[i].display ); 1032 } 1033 } 1034 1035 static void 1036 describe_subscription_source( ControlClient client ) 1037 { 1038 control_write( client, 1039 "'cdma ssource <ssource>' allows you to specify where to read the subscription from\r\n" ); 1040 dump_subscription_sources( client ); 1041 } 1042 1043 static int 1044 do_cdma_ssource( ControlClient client, char* args ) 1045 { 1046 int nn; 1047 if (!args) { 1048 control_write( client, "KO: missing argument, try 'cdma ssource <source>'\r\n" ); 1049 return -1; 1050 } 1051 1052 for (nn = 0; ; nn++) { 1053 const char* name = _cdma_subscription_sources[nn].name; 1054 ACdmaSubscriptionSource ssource = _cdma_subscription_sources[nn].source; 1055 1056 if (!name) 1057 break; 1058 1059 if (!strcasecmp( args, name )) { 1060 amodem_set_cdma_subscription_source( android_modem, ssource ); 1061 return 0; 1062 } 1063 } 1064 control_write( client, "KO: Don't know source %s\r\n", args ); 1065 return -1; 1066 } 1067 1068 static int 1069 do_cdma_prl_version( ControlClient client, char * args ) 1070 { 1071 int version = 0; 1072 char *endptr; 1073 1074 if (!args) { 1075 control_write( client, "KO: missing argument, try 'cdma prl_version <version>'\r\n"); 1076 return -1; 1077 } 1078 1079 version = strtol(args, &endptr, 0); 1080 if (endptr != args) { 1081 amodem_set_cdma_prl_version( android_modem, version ); 1082 } 1083 return 0; 1084 } 1085 /********************************************************************************************/ 1086 /********************************************************************************************/ 1087 /***** ******/ 1088 /***** G S M M O D E M ******/ 1089 /***** ******/ 1090 /********************************************************************************************/ 1091 /********************************************************************************************/ 1092 1093 static const struct { 1094 const char* name; 1095 const char* display; 1096 ARegistrationState state; 1097 } _gsm_states[] = { 1098 { "unregistered", "no network available", A_REGISTRATION_UNREGISTERED }, 1099 { "home", "on local network, non-roaming", A_REGISTRATION_HOME }, 1100 { "roaming", "on roaming network", A_REGISTRATION_ROAMING }, 1101 { "searching", "searching networks", A_REGISTRATION_SEARCHING }, 1102 { "denied", "emergency calls only", A_REGISTRATION_DENIED }, 1103 { "off", "same as 'unregistered'", A_REGISTRATION_UNREGISTERED }, 1104 { "on", "same as 'home'", A_REGISTRATION_HOME }, 1105 { NULL, NULL, A_REGISTRATION_UNREGISTERED } 1106 }; 1107 1108 static const char* 1109 gsm_state_to_string( ARegistrationState state ) 1110 { 1111 int nn; 1112 for (nn = 0; _gsm_states[nn].name != NULL; nn++) { 1113 if (state == _gsm_states[nn].state) 1114 return _gsm_states[nn].name; 1115 } 1116 return "<unknown>"; 1117 } 1118 1119 static int 1120 do_gsm_status( ControlClient client, char* args ) 1121 { 1122 if (args) { 1123 control_write( client, "KO: no argument required\r\n" ); 1124 return -1; 1125 } 1126 if (!android_modem) { 1127 control_write( client, "KO: modem emulation not running\r\n" ); 1128 return -1; 1129 } 1130 control_write( client, "gsm voice state: %s\r\n", 1131 gsm_state_to_string( 1132 amodem_get_voice_registration(android_modem) ) ); 1133 control_write( client, "gsm data state: %s\r\n", 1134 gsm_state_to_string( 1135 amodem_get_data_registration(android_modem) ) ); 1136 return 0; 1137 } 1138 1139 1140 static void 1141 help_gsm_data( ControlClient client ) 1142 { 1143 int nn; 1144 control_write( client, 1145 "the 'gsm data <state>' allows you to change the state of your GPRS connection\r\n" 1146 "valid values for <state> are the following:\r\n\r\n" ); 1147 for (nn = 0; ; nn++) { 1148 const char* name = _gsm_states[nn].name; 1149 const char* display = _gsm_states[nn].display; 1150 1151 if (!name) 1152 break; 1153 1154 control_write( client, " %-15s %s\r\n", name, display ); 1155 } 1156 control_write( client, "\r\n" ); 1157 } 1158 1159 1160 static int 1161 do_gsm_data( ControlClient client, char* args ) 1162 { 1163 int nn; 1164 1165 if (!args) { 1166 control_write( client, "KO: missing argument, try 'gsm data <state>'\r\n" ); 1167 return -1; 1168 } 1169 1170 for (nn = 0; ; nn++) { 1171 const char* name = _gsm_states[nn].name; 1172 ARegistrationState state = _gsm_states[nn].state; 1173 1174 if (!name) 1175 break; 1176 1177 if ( !strcmp( args, name ) ) { 1178 if (!android_modem) { 1179 control_write( client, "KO: modem emulation not running\r\n" ); 1180 return -1; 1181 } 1182 amodem_set_data_registration( android_modem, state ); 1183 qemu_net_disable = (state != A_REGISTRATION_HOME && 1184 state != A_REGISTRATION_ROAMING ); 1185 return 0; 1186 } 1187 } 1188 control_write( client, "KO: bad GSM data state name, try 'help gsm data' for list of valid values\r\n" ); 1189 return -1; 1190 } 1191 1192 static void 1193 help_gsm_voice( ControlClient client ) 1194 { 1195 int nn; 1196 control_write( client, 1197 "the 'gsm voice <state>' allows you to change the state of your GPRS connection\r\n" 1198 "valid values for <state> are the following:\r\n\r\n" ); 1199 for (nn = 0; ; nn++) { 1200 const char* name = _gsm_states[nn].name; 1201 const char* display = _gsm_states[nn].display; 1202 1203 if (!name) 1204 break; 1205 1206 control_write( client, " %-15s %s\r\n", name, display ); 1207 } 1208 control_write( client, "\r\n" ); 1209 } 1210 1211 1212 static int 1213 do_gsm_voice( ControlClient client, char* args ) 1214 { 1215 int nn; 1216 1217 if (!args) { 1218 control_write( client, "KO: missing argument, try 'gsm voice <state>'\r\n" ); 1219 return -1; 1220 } 1221 1222 for (nn = 0; ; nn++) { 1223 const char* name = _gsm_states[nn].name; 1224 ARegistrationState state = _gsm_states[nn].state; 1225 1226 if (!name) 1227 break; 1228 1229 if ( !strcmp( args, name ) ) { 1230 if (!android_modem) { 1231 control_write( client, "KO: modem emulation not running\r\n" ); 1232 return -1; 1233 } 1234 amodem_set_voice_registration( android_modem, state ); 1235 return 0; 1236 } 1237 } 1238 control_write( client, "KO: bad GSM data state name, try 'help gsm voice' for list of valid values\r\n" ); 1239 return -1; 1240 } 1241 1242 1243 static int 1244 gsm_check_number( char* args ) 1245 { 1246 int nn; 1247 1248 for (nn = 0; args[nn] != 0; nn++) { 1249 int c = args[nn]; 1250 if ( !isdigit(c) && c != '+' && c != '#' ) { 1251 return -1; 1252 } 1253 } 1254 if (nn == 0) 1255 return -1; 1256 1257 return 0; 1258 } 1259 1260 static int 1261 do_gsm_call( ControlClient client, char* args ) 1262 { 1263 /* check that we have a phone number made of digits */ 1264 if (!args) { 1265 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" ); 1266 return -1; 1267 } 1268 1269 if (gsm_check_number(args)) { 1270 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" ); 1271 return -1; 1272 } 1273 1274 if (!android_modem) { 1275 control_write( client, "KO: modem emulation not running\r\n" ); 1276 return -1; 1277 } 1278 amodem_add_inbound_call( android_modem, args ); 1279 return 0; 1280 } 1281 1282 static int 1283 do_gsm_cancel( ControlClient client, char* args ) 1284 { 1285 if (!args) { 1286 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" ); 1287 return -1; 1288 } 1289 if (gsm_check_number(args)) { 1290 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" ); 1291 return -1; 1292 } 1293 if (!android_modem) { 1294 control_write( client, "KO: modem emulation not running\r\n" ); 1295 return -1; 1296 } 1297 if ( amodem_disconnect_call( android_modem, args ) < 0 ) { 1298 control_write( client, "KO: could not cancel this number\r\n" ); 1299 return -1; 1300 } 1301 return 0; 1302 } 1303 1304 1305 static const char* 1306 call_state_to_string( ACallState state ) 1307 { 1308 switch (state) { 1309 case A_CALL_ACTIVE: return "active"; 1310 case A_CALL_HELD: return "held"; 1311 case A_CALL_ALERTING: return "ringing"; 1312 case A_CALL_WAITING: return "waiting"; 1313 case A_CALL_INCOMING: return "incoming"; 1314 default: return "unknown"; 1315 } 1316 } 1317 1318 static int 1319 do_gsm_list( ControlClient client, char* args ) 1320 { 1321 /* check that we have a phone number made of digits */ 1322 int count = amodem_get_call_count( android_modem ); 1323 int nn; 1324 for (nn = 0; nn < count; nn++) { 1325 ACall call = amodem_get_call( android_modem, nn ); 1326 const char* dir; 1327 1328 if (call == NULL) 1329 continue; 1330 1331 if (call->dir == A_CALL_OUTBOUND) 1332 dir = "outbound to "; 1333 else 1334 dir = "inbound from"; 1335 1336 control_write( client, "%s %-10s : %s\r\n", dir, 1337 call->number, call_state_to_string(call->state) ); 1338 } 1339 return 0; 1340 } 1341 1342 static int 1343 do_gsm_busy( ControlClient client, char* args ) 1344 { 1345 ACall call; 1346 1347 if (!args) { 1348 control_write( client, "KO: missing argument, try 'gsm busy <phonenumber>'\r\n" ); 1349 return -1; 1350 } 1351 call = amodem_find_call_by_number( android_modem, args ); 1352 if (call == NULL || call->dir != A_CALL_OUTBOUND) { 1353 control_write( client, "KO: no current outbound call to number '%s' (call %p)\r\n", args, call ); 1354 return -1; 1355 } 1356 if ( amodem_disconnect_call( android_modem, args ) < 0 ) { 1357 control_write( client, "KO: could not cancel this number\r\n" ); 1358 return -1; 1359 } 1360 return 0; 1361 } 1362 1363 static int 1364 do_gsm_hold( ControlClient client, char* args ) 1365 { 1366 ACall call; 1367 1368 if (!args) { 1369 control_write( client, "KO: missing argument, try 'gsm out hold <phonenumber>'\r\n" ); 1370 return -1; 1371 } 1372 call = amodem_find_call_by_number( android_modem, args ); 1373 if (call == NULL) { 1374 control_write( client, "KO: no current call to/from number '%s'\r\n", args ); 1375 return -1; 1376 } 1377 if ( amodem_update_call( android_modem, args, A_CALL_HELD ) < 0 ) { 1378 control_write( client, "KO: could put this call on hold\r\n" ); 1379 return -1; 1380 } 1381 return 0; 1382 } 1383 1384 1385 static int 1386 do_gsm_accept( ControlClient client, char* args ) 1387 { 1388 ACall call; 1389 1390 if (!args) { 1391 control_write( client, "KO: missing argument, try 'gsm accept <phonenumber>'\r\n" ); 1392 return -1; 1393 } 1394 call = amodem_find_call_by_number( android_modem, args ); 1395 if (call == NULL) { 1396 control_write( client, "KO: no current call to/from number '%s'\r\n", args ); 1397 return -1; 1398 } 1399 if ( amodem_update_call( android_modem, args, A_CALL_ACTIVE ) < 0 ) { 1400 control_write( client, "KO: could not activate this call\r\n" ); 1401 return -1; 1402 } 1403 return 0; 1404 } 1405 1406 static int 1407 do_gsm_signal( ControlClient client, char* args ) 1408 { 1409 enum { SIGNAL_RSSI = 0, SIGNAL_BER, NUM_SIGNAL_PARAMS }; 1410 char* p = args; 1411 int top_param = -1; 1412 int params[ NUM_SIGNAL_PARAMS ]; 1413 1414 static int last_ber = 99; 1415 1416 if (!p) 1417 p = ""; 1418 1419 /* tokenize */ 1420 while (*p) { 1421 char* end; 1422 int val = strtol( p, &end, 10 ); 1423 1424 if (end == p) { 1425 control_write( client, "KO: argument '%s' is not a number\n", p ); 1426 return -1; 1427 } 1428 1429 params[++top_param] = val; 1430 if (top_param + 1 == NUM_SIGNAL_PARAMS) 1431 break; 1432 1433 p = end; 1434 while (*p && (p[0] == ' ' || p[0] == '\t')) 1435 p += 1; 1436 } 1437 1438 /* sanity check */ 1439 if (top_param < SIGNAL_RSSI) { 1440 control_write( client, "KO: not enough arguments: see 'help gsm signal' for details\r\n" ); 1441 return -1; 1442 } 1443 1444 int rssi = params[SIGNAL_RSSI]; 1445 if ((rssi < 0 || rssi > 31) && rssi != 99) { 1446 control_write( client, "KO: invalid RSSI - must be 0..31 or 99\r\n"); 1447 return -1; 1448 } 1449 1450 /* check ber is 0..7 or 99 */ 1451 if (top_param >= SIGNAL_BER) { 1452 int ber = params[SIGNAL_BER]; 1453 if ((ber < 0 || ber > 7) && ber != 99) { 1454 control_write( client, "KO: invalid BER - must be 0..7 or 99\r\n"); 1455 return -1; 1456 } 1457 last_ber = ber; 1458 } 1459 1460 amodem_set_signal_strength( android_modem, rssi, last_ber ); 1461 1462 return 0; 1463 } 1464 1465 1466 #if 0 1467 static const CommandDefRec gsm_in_commands[] = 1468 { 1469 { "new", "create a new 'waiting' inbound call", 1470 "'gsm in create <phonenumber>' creates a new inbound phone call, placed in\r\n" 1471 "the 'waiting' state by default, until the system answers/holds/closes it\r\n", NULL 1472 do_gsm_in_create, NULL }, 1473 1474 { "hold", "change the state of an oubtound call to 'held'", 1475 "change the state of an outbound call to 'held'. this is only possible\r\n" 1476 "if the call in the 'waiting' or 'active' state\r\n", NULL, 1477 do_gsm_out_hold, NULL }, 1478 1479 { "accept", "change the state of an outbound call to 'active'", 1480 "change the state of an outbound call to 'active'. this is only possible\r\n" 1481 "if the call is in the 'waiting' or 'held' state\r\n", NULL, 1482 do_gsm_out_accept, NULL }, 1483 1484 { NULL, NULL, NULL, NULL, NULL, NULL } 1485 }; 1486 #endif 1487 1488 1489 static const CommandDefRec cdma_commands[] = 1490 { 1491 { "ssource", "Set the current CDMA subscription source", 1492 NULL, describe_subscription_source, 1493 do_cdma_ssource, NULL }, 1494 { "prl_version", "Dump the current PRL version", 1495 NULL, NULL, 1496 do_cdma_prl_version, NULL }, 1497 }; 1498 1499 static const CommandDefRec gsm_commands[] = 1500 { 1501 { "list", "list current phone calls", 1502 "'gsm list' lists all inbound and outbound calls and their state\r\n", NULL, 1503 do_gsm_list, NULL }, 1504 1505 { "call", "create inbound phone call", 1506 "'gsm call <phonenumber>' allows you to simulate a new inbound call\r\n", NULL, 1507 do_gsm_call, NULL }, 1508 1509 { "busy", "close waiting outbound call as busy", 1510 "'gsm busy <remoteNumber>' closes an outbound call, reporting\r\n" 1511 "the remote phone as busy. only possible if the call is 'waiting'.\r\n", NULL, 1512 do_gsm_busy, NULL }, 1513 1514 { "hold", "change the state of an oubtound call to 'held'", 1515 "'gsm hold <remoteNumber>' change the state of a call to 'held'. this is only possible\r\n" 1516 "if the call in the 'waiting' or 'active' state\r\n", NULL, 1517 do_gsm_hold, NULL }, 1518 1519 { "accept", "change the state of an outbound call to 'active'", 1520 "'gsm accept <remoteNumber>' change the state of a call to 'active'. this is only possible\r\n" 1521 "if the call is in the 'waiting' or 'held' state\r\n", NULL, 1522 do_gsm_accept, NULL }, 1523 1524 { "cancel", "disconnect an inbound or outbound phone call", 1525 "'gsm cancel <phonenumber>' allows you to simulate the end of an inbound or outbound call\r\n", NULL, 1526 do_gsm_cancel, NULL }, 1527 1528 { "data", "modify data connection state", NULL, help_gsm_data, 1529 do_gsm_data, NULL }, 1530 1531 { "voice", "modify voice connection state", NULL, help_gsm_voice, 1532 do_gsm_voice, NULL }, 1533 1534 { "status", "display GSM status", 1535 "'gsm status' displays the current state of the GSM emulation\r\n", NULL, 1536 do_gsm_status, NULL }, 1537 1538 { "signal", "set sets the rssi and ber", 1539 "'gsm signal <rssi> [<ber>]' changes the reported strength and error rate on next (15s) update.\r\n" 1540 "rssi range is 0..31 and 99 for unknown\r\n" 1541 "ber range is 0..7 percent and 99 for unknown\r\n", 1542 NULL, do_gsm_signal, NULL }, 1543 1544 { NULL, NULL, NULL, NULL, NULL, NULL } 1545 }; 1546 1547 /********************************************************************************************/ 1548 /********************************************************************************************/ 1549 /***** ******/ 1550 /***** S M S C O M M A N D ******/ 1551 /***** ******/ 1552 /********************************************************************************************/ 1553 /********************************************************************************************/ 1554 1555 static int 1556 do_sms_send( ControlClient client, char* args ) 1557 { 1558 char* p; 1559 int textlen; 1560 SmsAddressRec sender; 1561 SmsPDU* pdus; 1562 int nn; 1563 1564 /* check that we have a phone number made of digits */ 1565 if (!args) { 1566 MissingArgument: 1567 control_write( client, "KO: missing argument, try 'sms send <phonenumber> <text message>'\r\n" ); 1568 return -1; 1569 } 1570 p = strchr( args, ' ' ); 1571 if (!p) { 1572 goto MissingArgument; 1573 } 1574 1575 if ( sms_address_from_str( &sender, args, p - args ) < 0 ) { 1576 control_write( client, "KO: bad phone number format, must be [+](0-9)*\r\n" ); 1577 return -1; 1578 } 1579 1580 1581 /* un-secape message text into proper utf-8 (conversion happens in-site) */ 1582 p += 1; 1583 textlen = strlen(p); 1584 textlen = sms_utf8_from_message_str( p, textlen, (unsigned char*)p, textlen ); 1585 if (textlen < 0) { 1586 control_write( client, "message must be utf8 and can use the following escapes:\r\n" 1587 " \\n for a newline\r\n" 1588 " \\xNN where NN are two hexadecimal numbers\r\n" 1589 " \\uNNNN where NNNN are four hexadecimal numbers\r\n" 1590 " \\\\ to send a '\\' character\r\n\r\n" 1591 " anything else is an error\r\n" 1592 "KO: badly formatted text\r\n" ); 1593 return -1; 1594 } 1595 1596 if (!android_modem) { 1597 control_write( client, "KO: modem emulation not running\r\n" ); 1598 return -1; 1599 } 1600 1601 /* create a list of SMS PDUs, then send them */ 1602 pdus = smspdu_create_deliver_utf8( (cbytes_t)p, textlen, &sender, NULL ); 1603 if (pdus == NULL) { 1604 control_write( client, "KO: internal error when creating SMS-DELIVER PDUs\n" ); 1605 return -1; 1606 } 1607 1608 for (nn = 0; pdus[nn] != NULL; nn++) 1609 amodem_receive_sms( android_modem, pdus[nn] ); 1610 1611 smspdu_free_list( pdus ); 1612 return 0; 1613 } 1614 1615 static int 1616 do_sms_sendpdu( ControlClient client, char* args ) 1617 { 1618 SmsPDU pdu; 1619 1620 /* check that we have a phone number made of digits */ 1621 if (!args) { 1622 control_write( client, "KO: missing argument, try 'sms sendpdu <hexstring>'\r\n" ); 1623 return -1; 1624 } 1625 1626 if (!android_modem) { 1627 control_write( client, "KO: modem emulation not running\r\n" ); 1628 return -1; 1629 } 1630 1631 pdu = smspdu_create_from_hex( args, strlen(args) ); 1632 if (pdu == NULL) { 1633 control_write( client, "KO: badly formatted <hexstring>\r\n" ); 1634 return -1; 1635 } 1636 1637 amodem_receive_sms( android_modem, pdu ); 1638 smspdu_free( pdu ); 1639 return 0; 1640 } 1641 1642 static const CommandDefRec sms_commands[] = 1643 { 1644 { "send", "send inbound SMS text message", 1645 "'sms send <phonenumber> <message>' allows you to simulate a new inbound sms message\r\n", NULL, 1646 do_sms_send, NULL }, 1647 1648 { "pdu", "send inbound SMS PDU", 1649 "'sms pdu <hexstring>' allows you to simulate a new inbound sms PDU\r\n" 1650 "(used internally when one emulator sends SMS messages to another instance).\r\n" 1651 "you probably don't want to play with this at all\r\n", NULL, 1652 do_sms_sendpdu, NULL }, 1653 1654 { NULL, NULL, NULL, NULL, NULL, NULL } 1655 }; 1656 1657 static void 1658 do_control_write(void* data, const char* string) 1659 { 1660 control_write((ControlClient)data, string); 1661 } 1662 1663 static int 1664 do_power_display( ControlClient client, char* args ) 1665 { 1666 goldfish_battery_display(do_control_write, client); 1667 return 0; 1668 } 1669 1670 static int 1671 do_ac_state( ControlClient client, char* args ) 1672 { 1673 if (args) { 1674 if (strcasecmp(args, "on") == 0) { 1675 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 1); 1676 return 0; 1677 } 1678 if (strcasecmp(args, "off") == 0) { 1679 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 0); 1680 return 0; 1681 } 1682 } 1683 1684 control_write( client, "KO: Usage: \"ac on\" or \"ac off\"\n" ); 1685 return -1; 1686 } 1687 1688 static int 1689 do_battery_status( ControlClient client, char* args ) 1690 { 1691 if (args) { 1692 if (strcasecmp(args, "unknown") == 0) { 1693 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_UNKNOWN); 1694 return 0; 1695 } 1696 if (strcasecmp(args, "charging") == 0) { 1697 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_CHARGING); 1698 return 0; 1699 } 1700 if (strcasecmp(args, "discharging") == 0) { 1701 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_DISCHARGING); 1702 return 0; 1703 } 1704 if (strcasecmp(args, "not-charging") == 0) { 1705 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_NOT_CHARGING); 1706 return 0; 1707 } 1708 if (strcasecmp(args, "full") == 0) { 1709 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_FULL); 1710 return 0; 1711 } 1712 } 1713 1714 control_write( client, "KO: Usage: \"status unknown|charging|discharging|not-charging|full\"\n" ); 1715 return -1; 1716 } 1717 1718 static int 1719 do_battery_present( ControlClient client, char* args ) 1720 { 1721 if (args) { 1722 if (strcasecmp(args, "true") == 0) { 1723 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 1); 1724 return 0; 1725 } 1726 if (strcasecmp(args, "false") == 0) { 1727 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 0); 1728 return 0; 1729 } 1730 } 1731 1732 control_write( client, "KO: Usage: \"present true\" or \"present false\"\n" ); 1733 return -1; 1734 } 1735 1736 static int 1737 do_battery_health( ControlClient client, char* args ) 1738 { 1739 if (args) { 1740 if (strcasecmp(args, "unknown") == 0) { 1741 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNKNOWN); 1742 return 0; 1743 } 1744 if (strcasecmp(args, "good") == 0) { 1745 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_GOOD); 1746 return 0; 1747 } 1748 if (strcasecmp(args, "overheat") == 0) { 1749 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERHEAT); 1750 return 0; 1751 } 1752 if (strcasecmp(args, "dead") == 0) { 1753 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_DEAD); 1754 return 0; 1755 } 1756 if (strcasecmp(args, "overvoltage") == 0) { 1757 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERVOLTAGE); 1758 return 0; 1759 } 1760 if (strcasecmp(args, "failure") == 0) { 1761 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE); 1762 return 0; 1763 } 1764 } 1765 1766 control_write( client, "KO: Usage: \"health unknown|good|overheat|dead|overvoltage|failure\"\n" ); 1767 return -1; 1768 } 1769 1770 static int 1771 do_battery_capacity( ControlClient client, char* args ) 1772 { 1773 if (args) { 1774 int capacity; 1775 1776 if (sscanf(args, "%d", &capacity) == 1 && capacity >= 0 && capacity <= 100) { 1777 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_CAPACITY, capacity); 1778 return 0; 1779 } 1780 } 1781 1782 control_write( client, "KO: Usage: \"capacity <percentage>\"\n" ); 1783 return -1; 1784 } 1785 1786 1787 static const CommandDefRec power_commands[] = 1788 { 1789 { "display", "display battery and charger state", 1790 "display battery and charger state\r\n", NULL, 1791 do_power_display, NULL }, 1792 1793 { "ac", "set AC charging state", 1794 "'ac on|off' allows you to set the AC charging state to on or off\r\n", NULL, 1795 do_ac_state, NULL }, 1796 1797 { "status", "set battery status", 1798 "'status unknown|charging|discharging|not-charging|full' allows you to set battery status\r\n", NULL, 1799 do_battery_status, NULL }, 1800 1801 { "present", "set battery present state", 1802 "'present true|false' allows you to set battery present state to true or false\r\n", NULL, 1803 do_battery_present, NULL }, 1804 1805 { "health", "set battery health state", 1806 "'health unknown|good|overheat|dead|overvoltage|failure' allows you to set battery health state\r\n", NULL, 1807 do_battery_health, NULL }, 1808 1809 { "capacity", "set battery capacity state", 1810 "'capacity <percentage>' allows you to set battery capacity to a value 0 - 100\r\n", NULL, 1811 do_battery_capacity, NULL }, 1812 1813 { NULL, NULL, NULL, NULL, NULL, NULL } 1814 }; 1815 1816 /********************************************************************************************/ 1817 /********************************************************************************************/ 1818 /***** ******/ 1819 /***** E V E N T C O M M A N D S ******/ 1820 /***** ******/ 1821 /********************************************************************************************/ 1822 /********************************************************************************************/ 1823 1824 1825 static int 1826 do_event_send( ControlClient client, char* args ) 1827 { 1828 char* p; 1829 1830 if (!args) { 1831 control_write( client, "KO: Usage: event send <type>:<code>:<value> ...\r\n" ); 1832 return -1; 1833 } 1834 1835 p = args; 1836 while (*p) { 1837 char* q; 1838 char temp[128]; 1839 int type, code, value, ret; 1840 1841 p += strspn( p, " \t" ); /* skip spaces */ 1842 if (*p == 0) 1843 break; 1844 1845 q = p + strcspn( p, " \t" ); 1846 1847 if (q == p) 1848 break; 1849 1850 snprintf(temp, sizeof temp, "%.*s", (int)(intptr_t)(q-p), p); 1851 ret = android_event_from_str( temp, &type, &code, &value ); 1852 if (ret < 0) { 1853 if (ret == -1) { 1854 control_write( client, 1855 "KO: invalid event type in '%.*s', try 'event list types' for valid values\r\n", 1856 q-p, p ); 1857 } else if (ret == -2) { 1858 control_write( client, 1859 "KO: invalid event code in '%.*s', try 'event list codes <type>' for valid values\r\n", 1860 q-p, p ); 1861 } else { 1862 control_write( client, 1863 "KO: invalid event value in '%.*s', must be an integer\r\n", 1864 q-p, p); 1865 } 1866 return -1; 1867 } 1868 1869 user_event_generic( type, code, value ); 1870 p = q; 1871 } 1872 return 0; 1873 } 1874 1875 static int 1876 do_event_types( ControlClient client, char* args ) 1877 { 1878 int count = android_event_get_type_count(); 1879 int nn; 1880 1881 control_write( client, "event <type> can be an integer or one of the following aliases\r\n" ); 1882 for (nn = 0; nn < count; nn++) { 1883 char tmp[16]; 1884 char* p = tmp; 1885 char* end = p + sizeof(tmp); 1886 int count2 = android_event_get_code_count( nn );; 1887 1888 p = android_event_bufprint_type_str( p, end, nn ); 1889 1890 control_write( client, " %-8s", tmp ); 1891 if (count2 > 0) 1892 control_write( client, " (%d code aliases)", count2 ); 1893 1894 control_write( client, "\r\n" ); 1895 } 1896 return 0; 1897 } 1898 1899 static int 1900 do_event_codes( ControlClient client, char* args ) 1901 { 1902 int count; 1903 int nn, type, dummy; 1904 1905 if (!args) { 1906 control_write( client, "KO: argument missing, try 'event codes <type>'\r\n" ); 1907 return -1; 1908 } 1909 1910 if ( android_event_from_str( args, &type, &dummy, &dummy ) < 0 ) { 1911 control_write( client, "KO: bad argument, see 'event types' for valid values\r\n" ); 1912 return -1; 1913 } 1914 1915 count = android_event_get_code_count( type ); 1916 if (count == 0) { 1917 control_write( client, "no code aliases defined for this type\r\n" ); 1918 } else { 1919 control_write( client, "type '%s' accepts the following <code> aliases:\r\n", 1920 args ); 1921 for (nn = 0; nn < count; nn++) { 1922 char temp[20], *p = temp, *end = p + sizeof(temp); 1923 android_event_bufprint_code_str( p, end, type, nn ); 1924 control_write( client, " %-12s\r\n", temp ); 1925 } 1926 } 1927 1928 return 0; 1929 } 1930 1931 static __inline__ int 1932 utf8_next( unsigned char* *pp, unsigned char* end ) 1933 { 1934 unsigned char* p = *pp; 1935 int result = -1; 1936 1937 if (p < end) { 1938 int c= *p++; 1939 if (c >= 128) { 1940 if ((c & 0xe0) == 0xc0) 1941 c &= 0x1f; 1942 else if ((c & 0xf0) == 0xe0) 1943 c &= 0x0f; 1944 else 1945 c &= 0x07; 1946 1947 while (p < end && (p[0] & 0xc0) == 0x80) { 1948 c = (c << 6) | (p[0] & 0x3f); 1949 } 1950 } 1951 result = c; 1952 *pp = p; 1953 } 1954 return result; 1955 } 1956 1957 static int 1958 do_event_text( ControlClient client, char* args ) 1959 { 1960 AKeycodeBuffer keycodes; 1961 unsigned char* p = (unsigned char*) args; 1962 unsigned char* end = p + strlen(args); 1963 int textlen; 1964 const AKeyCharmap* charmap; 1965 1966 if (!args) { 1967 control_write( client, "KO: argument missing, try 'event text <message>'\r\n" ); 1968 return -1; 1969 } 1970 1971 /* Get active charmap. */ 1972 charmap = android_get_charmap(); 1973 if (charmap == NULL) { 1974 control_write( client, "KO: no character map active in current device layout/config\r\n" ); 1975 return -1; 1976 } 1977 1978 keycodes.keycode_count = 0; 1979 1980 /* un-secape message text into proper utf-8 (conversion happens in-site) */ 1981 textlen = strlen((char*)p); 1982 textlen = sms_utf8_from_message_str( args, textlen, (unsigned char*)p, textlen ); 1983 if (textlen < 0) { 1984 control_write( client, "message must be utf8 and can use the following escapes:\r\n" 1985 " \\n for a newline\r\n" 1986 " \\xNN where NN are two hexadecimal numbers\r\n" 1987 " \\uNNNN where NNNN are four hexadecimal numbers\r\n" 1988 " \\\\ to send a '\\' character\r\n\r\n" 1989 " anything else is an error\r\n" 1990 "KO: badly formatted text\r\n" ); 1991 return -1; 1992 } 1993 1994 end = p + textlen; 1995 while (p < end) { 1996 int c = utf8_next( &p, end ); 1997 if (c <= 0) 1998 break; 1999 2000 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 1, &keycodes ); 2001 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 0, &keycodes ); 2002 android_keycodes_flush( &keycodes ); 2003 } 2004 2005 return 0; 2006 } 2007 2008 static const CommandDefRec event_commands[] = 2009 { 2010 { "send", "send a series of events to the kernel", 2011 "'event send <type>:<code>:<value> ...' allows your to send one or more hardware events\r\n" 2012 "to the Android kernel. you can use text names or integers for <type> and <code>\r\n", NULL, 2013 do_event_send, NULL }, 2014 2015 { "types", "list all <type> aliases", 2016 "'event types' list all <type> string aliases supported by the 'event' subcommands\r\n", 2017 NULL, do_event_types, NULL }, 2018 2019 { "codes", "list all <code> aliases for a given <type>", 2020 "'event codes <type>' lists all <code> string aliases for a given event <type>\r\n", 2021 NULL, do_event_codes, NULL }, 2022 2023 { "text", "simulate keystrokes from a given text", 2024 "'event text <message>' allows you to simulate keypresses to generate a given text\r\n" 2025 "message. <message> must be an utf-8 string. Unicode points will be reverse-mapped\r\n" 2026 "according to the current device keyboard. unsupported characters will be discarded\r\n" 2027 "silently\r\n", NULL, do_event_text, NULL }, 2028 2029 { NULL, NULL, NULL, NULL, NULL, NULL } 2030 }; 2031 2032 2033 /********************************************************************************************/ 2034 /********************************************************************************************/ 2035 /***** ******/ 2036 /***** S N A P S H O T C O M M A N D S ******/ 2037 /***** ******/ 2038 /********************************************************************************************/ 2039 /********************************************************************************************/ 2040 2041 static int 2042 control_write_out_cb(void* opaque, const char* str, int strsize) 2043 { 2044 ControlClient client = opaque; 2045 control_control_write(client, str, strsize); 2046 return strsize; 2047 } 2048 2049 static int 2050 control_write_err_cb(void* opaque, const char* str, int strsize) 2051 { 2052 int ret = 0; 2053 ControlClient client = opaque; 2054 ret += control_write(client, "KO: "); 2055 control_control_write(client, str, strsize); 2056 return ret + strsize; 2057 } 2058 2059 static int 2060 do_snapshot_list( ControlClient client, char* args ) 2061 { 2062 int64_t ret; 2063 Monitor *out = monitor_fake_new(client, control_write_out_cb); 2064 Monitor *err = monitor_fake_new(client, control_write_err_cb); 2065 do_info_snapshots(out, err); 2066 ret = monitor_fake_get_bytes(err); 2067 monitor_fake_free(err); 2068 monitor_fake_free(out); 2069 2070 return ret > 0; 2071 } 2072 2073 static int 2074 do_snapshot_save( ControlClient client, char* args ) 2075 { 2076 int64_t ret; 2077 2078 if (args == NULL) { 2079 control_write(client, "KO: argument missing, try 'avd snapshot save <name>'\r\n"); 2080 return -1; 2081 } 2082 2083 Monitor *err = monitor_fake_new(client, control_write_err_cb); 2084 do_savevm(err, args); 2085 ret = monitor_fake_get_bytes(err); 2086 monitor_fake_free(err); 2087 2088 return ret > 0; // no output on error channel indicates success 2089 } 2090 2091 static int 2092 do_snapshot_load( ControlClient client, char* args ) 2093 { 2094 int64_t ret; 2095 2096 if (args == NULL) { 2097 control_write(client, "KO: argument missing, try 'avd snapshot load <name>'\r\n"); 2098 return -1; 2099 } 2100 2101 Monitor *err = monitor_fake_new(client, control_write_err_cb); 2102 do_loadvm(err, args); 2103 ret = monitor_fake_get_bytes(err); 2104 monitor_fake_free(err); 2105 2106 return ret > 0; 2107 } 2108 2109 static int 2110 do_snapshot_del( ControlClient client, char* args ) 2111 { 2112 int64_t ret; 2113 2114 if (args == NULL) { 2115 control_write(client, "KO: argument missing, try 'avd snapshot del <name>'\r\n"); 2116 return -1; 2117 } 2118 2119 Monitor *err = monitor_fake_new(client, control_write_err_cb); 2120 do_delvm(err, args); 2121 ret = monitor_fake_get_bytes(err); 2122 monitor_fake_free(err); 2123 2124 return ret > 0; 2125 } 2126 2127 static const CommandDefRec snapshot_commands[] = 2128 { 2129 { "list", "list available state snapshots", 2130 "'avd snapshot list' will show a list of all state snapshots that can be loaded\r\n", 2131 NULL, do_snapshot_list, NULL }, 2132 2133 { "save", "save state snapshot", 2134 "'avd snapshot save <name>' will save the current (run-time) state to a snapshot with the given name\r\n", 2135 NULL, do_snapshot_save, NULL }, 2136 2137 { "load", "load state snapshot", 2138 "'avd snapshot load <name>' will load the state snapshot of the given name\r\n", 2139 NULL, do_snapshot_load, NULL }, 2140 2141 { "del", "delete state snapshot", 2142 "'avd snapshot del <name>' will delete the state snapshot with the given name\r\n", 2143 NULL, do_snapshot_del, NULL }, 2144 2145 { NULL, NULL, NULL, NULL, NULL, NULL } 2146 }; 2147 2148 2149 2150 /********************************************************************************************/ 2151 /********************************************************************************************/ 2152 /***** ******/ 2153 /***** V M C O M M A N D S ******/ 2154 /***** ******/ 2155 /********************************************************************************************/ 2156 /********************************************************************************************/ 2157 2158 static int 2159 do_avd_stop( ControlClient client, char* args ) 2160 { 2161 if (!vm_running) { 2162 control_write( client, "KO: virtual device already stopped\r\n" ); 2163 return -1; 2164 } 2165 vm_stop(EXCP_INTERRUPT); 2166 return 0; 2167 } 2168 2169 static int 2170 do_avd_start( ControlClient client, char* args ) 2171 { 2172 if (vm_running) { 2173 control_write( client, "KO: virtual device already running\r\n" ); 2174 return -1; 2175 } 2176 vm_start(); 2177 return 0; 2178 } 2179 2180 static int 2181 do_avd_status( ControlClient client, char* args ) 2182 { 2183 control_write( client, "virtual device is %s\r\n", vm_running ? "running" : "stopped" ); 2184 return 0; 2185 } 2186 2187 static int 2188 do_avd_name( ControlClient client, char* args ) 2189 { 2190 control_write( client, "%s\r\n", android_hw->avd_name); 2191 return 0; 2192 } 2193 2194 static const CommandDefRec vm_commands[] = 2195 { 2196 { "stop", "stop the virtual device", 2197 "'avd stop' stops the virtual device immediately, use 'avd start' to continue execution\r\n", 2198 NULL, do_avd_stop, NULL }, 2199 2200 { "start", "start/restart the virtual device", 2201 "'avd start' will start or continue the virtual device, use 'avd stop' to stop it\r\n", 2202 NULL, do_avd_start, NULL }, 2203 2204 { "status", "query virtual device status", 2205 "'avd status' will indicate whether the virtual device is running or not\r\n", 2206 NULL, do_avd_status, NULL }, 2207 2208 { "name", "query virtual device name", 2209 "'avd name' will return the name of this virtual device\r\n", 2210 NULL, do_avd_name, NULL }, 2211 2212 { "snapshot", "state snapshot commands", 2213 "allows you to save and restore the virtual device state in snapshots\r\n", 2214 NULL, NULL, snapshot_commands }, 2215 2216 { NULL, NULL, NULL, NULL, NULL, NULL } 2217 }; 2218 2219 /********************************************************************************************/ 2220 /********************************************************************************************/ 2221 /***** ******/ 2222 /***** G E O C O M M A N D S ******/ 2223 /***** ******/ 2224 /********************************************************************************************/ 2225 /********************************************************************************************/ 2226 2227 static int 2228 do_geo_nmea( ControlClient client, char* args ) 2229 { 2230 if (!args) { 2231 control_write( client, "KO: NMEA sentence missing, try 'help geo nmea'\r\n" ); 2232 return -1; 2233 } 2234 if (!android_gps_cs) { 2235 control_write( client, "KO: no GPS emulation in this virtual device\r\n" ); 2236 return -1; 2237 } 2238 android_gps_send_nmea( args ); 2239 return 0; 2240 } 2241 2242 static int 2243 do_geo_fix( ControlClient client, char* args ) 2244 { 2245 // GEO_SAT2 provides bug backwards compatibility. 2246 enum { GEO_LONG = 0, GEO_LAT, GEO_ALT, GEO_SAT, GEO_SAT2, NUM_GEO_PARAMS }; 2247 char* p = args; 2248 int top_param = -1; 2249 double params[ NUM_GEO_PARAMS ]; 2250 int n_satellites = 1; 2251 2252 static int last_time = 0; 2253 2254 if (!p) 2255 p = ""; 2256 2257 /* tokenize */ 2258 while (*p) { 2259 char* end; 2260 double val = strtod( p, &end ); 2261 2262 if (end == p) { 2263 control_write( client, "KO: argument '%s' is not a number\n", p ); 2264 return -1; 2265 } 2266 2267 params[++top_param] = val; 2268 if (top_param + 1 == NUM_GEO_PARAMS) 2269 break; 2270 2271 p = end; 2272 while (*p && (p[0] == ' ' || p[0] == '\t')) 2273 p += 1; 2274 } 2275 2276 /* sanity check */ 2277 if (top_param < GEO_LAT) { 2278 control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" ); 2279 return -1; 2280 } 2281 2282 /* check number of satellites, must be integer between 1 and 12 */ 2283 if (top_param >= GEO_SAT) { 2284 int sat_index = (top_param >= GEO_SAT2) ? GEO_SAT2 : GEO_SAT; 2285 n_satellites = (int) params[sat_index]; 2286 if (n_satellites != params[sat_index] 2287 || n_satellites < 1 || n_satellites > 12) { 2288 control_write( client, "KO: invalid number of satellites. Must be an integer between 1 and 12\r\n"); 2289 return -1; 2290 } 2291 } 2292 2293 /* generate an NMEA sentence for this fix */ 2294 { 2295 STRALLOC_DEFINE(s); 2296 double val; 2297 int deg, min; 2298 char hemi; 2299 2300 /* format overview: 2301 * time of fix 123519 12:35:19 UTC 2302 * latitude 4807.038 48 degrees, 07.038 minutes 2303 * north/south N or S 2304 * longitude 01131.000 11 degrees, 31. minutes 2305 * east/west E or W 2306 * fix quality 1 standard GPS fix 2307 * satellites 1 to 12 number of satellites being tracked 2308 * HDOP <dontcare> horizontal dilution 2309 * altitude 546. altitude above sea-level 2310 * altitude units M to indicate meters 2311 * diff <dontcare> height of sea-level above ellipsoid 2312 * diff units M to indicate meters (should be <dontcare>) 2313 * dgps age <dontcare> time in seconds since last DGPS fix 2314 * dgps sid <dontcare> DGPS station id 2315 */ 2316 2317 /* first, the time */ 2318 stralloc_add_format( s, "$GPGGA,%06d", last_time ); 2319 last_time ++; 2320 2321 /* then the latitude */ 2322 hemi = 'N'; 2323 val = params[GEO_LAT]; 2324 if (val < 0) { 2325 hemi = 'S'; 2326 val = -val; 2327 } 2328 deg = (int) val; 2329 val = 60*(val - deg); 2330 min = (int) val; 2331 val = 10000*(val - min); 2332 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi ); 2333 2334 /* the longitude */ 2335 hemi = 'E'; 2336 val = params[GEO_LONG]; 2337 if (val < 0) { 2338 hemi = 'W'; 2339 val = -val; 2340 } 2341 deg = (int) val; 2342 val = 60*(val - deg); 2343 min = (int) val; 2344 val = 10000*(val - min); 2345 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi ); 2346 2347 /* bogus fix quality, satellite count and dilution */ 2348 stralloc_add_format( s, ",1,%02d,", n_satellites ); 2349 2350 /* optional altitude + bogus diff */ 2351 if (top_param >= GEO_ALT) { 2352 stralloc_add_format( s, ",%.1g,M,0.,M", params[GEO_ALT] ); 2353 } else { 2354 stralloc_add_str( s, ",,,," ); 2355 } 2356 /* bogus rest and checksum */ 2357 stralloc_add_str( s, ",,,*47" ); 2358 2359 /* send it, then free */ 2360 android_gps_send_nmea( stralloc_cstr(s) ); 2361 stralloc_reset( s ); 2362 } 2363 return 0; 2364 } 2365 2366 static const CommandDefRec geo_commands[] = 2367 { 2368 { "nmea", "send an GPS NMEA sentence", 2369 "'geo nema <sentence>' sends a NMEA 0183 sentence to the emulated device, as\r\n" 2370 "if it came from an emulated GPS modem. <sentence> must begin with '$GP'. only\r\n" 2371 "'$GPGGA' and '$GPRCM' sentences are supported at the moment.\r\n", 2372 NULL, do_geo_nmea, NULL }, 2373 2374 { "fix", "send a simple GPS fix", 2375 "'geo fix <longitude> <latitude> [<altitude> [<satellites>]]'\r\n" 2376 " allows you to send a simple GPS fix to the emulated system.\r\n" 2377 " The parameters are:\r\n\r\n" 2378 " <longitude> longitude, in decimal degrees\r\n" 2379 " <latitude> latitude, in decimal degrees\r\n" 2380 " <altitude> optional altitude in meters\r\n" 2381 " <satellites> number of satellites being tracked (1-12)\r\n" 2382 "\r\n", 2383 NULL, do_geo_fix, NULL }, 2384 2385 { NULL, NULL, NULL, NULL, NULL, NULL } 2386 }; 2387 2388 2389 /********************************************************************************************/ 2390 /********************************************************************************************/ 2391 /***** ******/ 2392 /***** S E N S O R S C O M M A N D S ******/ 2393 /***** ******/ 2394 /********************************************************************************************/ 2395 /********************************************************************************************/ 2396 2397 /* For sensors user prompt string size.*/ 2398 #define SENSORS_INFO_SIZE 150 2399 2400 /* Get sensor data - (a,b,c) from sensor name */ 2401 static int 2402 do_sensors_get( ControlClient client, char* args ) 2403 { 2404 if (! args) { 2405 control_write( client, "KO: Usage: \"get <sensorname>\"\n" ); 2406 return -1; 2407 } 2408 2409 int status = SENSOR_STATUS_UNKNOWN; 2410 char sensor[strlen(args) + 1]; 2411 if (1 != sscanf( args, "%s", &sensor[0] )) 2412 goto SENSOR_STATUS_ERROR; 2413 2414 int sensor_id = android_sensors_get_id_from_name( sensor ); 2415 char buffer[SENSORS_INFO_SIZE] = { 0 }; 2416 float a, b, c; 2417 2418 if (sensor_id < 0) { 2419 status = sensor_id; 2420 goto SENSOR_STATUS_ERROR; 2421 } else { 2422 status = android_sensors_get( sensor_id, &a, &b, &c ); 2423 if (status != SENSOR_STATUS_OK) 2424 goto SENSOR_STATUS_ERROR; 2425 snprintf( buffer, sizeof(buffer), 2426 "%s = %g:%g:%g\r\n", sensor, a, b, c ); 2427 do_control_write( client, buffer ); 2428 return 0; 2429 } 2430 2431 SENSOR_STATUS_ERROR: 2432 switch(status) { 2433 case SENSOR_STATUS_NO_SERVICE: 2434 snprintf( buffer, sizeof(buffer), "KO: No sensor service found!\r\n" ); 2435 break; 2436 case SENSOR_STATUS_DISABLED: 2437 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor is disabled.\r\n", sensor ); 2438 break; 2439 case SENSOR_STATUS_UNKNOWN: 2440 snprintf( buffer, sizeof(buffer), 2441 "KO: unknown sensor name: %s, run 'sensor status' to get available sensors.\r\n", sensor ); 2442 break; 2443 default: 2444 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor: exception happens.\r\n", sensor ); 2445 } 2446 do_control_write( client, buffer ); 2447 return -1; 2448 } 2449 2450 /* set sensor data - (a,b,c) from sensor name */ 2451 static int 2452 do_sensors_set( ControlClient client, char* args ) 2453 { 2454 if (! args) { 2455 control_write( client, "KO: Usage: \"set <sensorname> <value-a>[:<value-b>[:<value-c>]]\"\n" ); 2456 return -1; 2457 } 2458 2459 int status; 2460 char* sensor; 2461 char* value; 2462 char* args_dup = strdup( args ); 2463 if (args_dup == NULL) { 2464 control_write( client, "KO: Memory allocation failed.\n" ); 2465 return -1; 2466 } 2467 char* p = args_dup; 2468 2469 /* Parsing the args to get sensor name string */ 2470 while (*p && isspace(*p)) p++; 2471 if (*p == 0) 2472 goto INPUT_ERROR; 2473 sensor = p; 2474 2475 /* Parsing the args to get value string */ 2476 while (*p && (! isspace(*p))) p++; 2477 if (*p == 0 || *(p + 1) == 0/* make sure value isn't NULL */) 2478 goto INPUT_ERROR; 2479 *p = 0; 2480 value = p + 1; 2481 2482 if (! (strlen(sensor) && strlen(value))) 2483 goto INPUT_ERROR; 2484 2485 int sensor_id = android_sensors_get_id_from_name( sensor ); 2486 char buffer[SENSORS_INFO_SIZE] = { 0 }; 2487 2488 if (sensor_id < 0) { 2489 status = sensor_id; 2490 goto SENSOR_STATUS_ERROR; 2491 } else { 2492 float fvalues[3]; 2493 status = android_sensors_get( sensor_id, &fvalues[0], &fvalues[1], &fvalues[2] ); 2494 if (status != SENSOR_STATUS_OK) 2495 goto SENSOR_STATUS_ERROR; 2496 2497 /* Parsing the value part to get the sensor values(a, b, c) */ 2498 int i; 2499 char* pnext; 2500 char* pend = value + strlen(value); 2501 for (i = 0; i < 3; i++, value = pnext + 1) { 2502 pnext=strchr( value, ':' ); 2503 if (pnext) { 2504 *pnext = 0; 2505 } else { 2506 pnext = pend; 2507 } 2508 2509 if (pnext > value) { 2510 if (1 != sscanf( value,"%g", &fvalues[i] )) 2511 goto INPUT_ERROR; 2512 } 2513 } 2514 2515 status = android_sensors_set( sensor_id, fvalues[0], fvalues[1], fvalues[2] ); 2516 if (status != SENSOR_STATUS_OK) 2517 goto SENSOR_STATUS_ERROR; 2518 2519 free( args_dup ); 2520 return 0; 2521 } 2522 2523 SENSOR_STATUS_ERROR: 2524 switch(status) { 2525 case SENSOR_STATUS_NO_SERVICE: 2526 snprintf( buffer, sizeof(buffer), "KO: No sensor service found!\r\n" ); 2527 break; 2528 case SENSOR_STATUS_DISABLED: 2529 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor is disabled.\r\n", sensor ); 2530 break; 2531 case SENSOR_STATUS_UNKNOWN: 2532 snprintf( buffer, sizeof(buffer), 2533 "KO: unknown sensor name: %s, run 'sensor status' to get available sensors.\r\n", sensor ); 2534 break; 2535 default: 2536 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor: exception happens.\r\n", sensor ); 2537 } 2538 do_control_write( client, buffer ); 2539 free( args_dup ); 2540 return -1; 2541 2542 INPUT_ERROR: 2543 control_write( client, "KO: Usage: \"set <sensorname> <value-a>[:<value-b>[:<value-c>]]\"\n" ); 2544 free( args_dup ); 2545 return -1; 2546 } 2547 2548 /* get all available sensor names and enable status respectively. */ 2549 static int 2550 do_sensors_status( ControlClient client, char* args ) 2551 { 2552 uint8_t id, status; 2553 char buffer[SENSORS_INFO_SIZE] = { 0 }; 2554 2555 for(id = 0; id < MAX_SENSORS; id++) { 2556 status = android_sensors_get_sensor_status( id ); 2557 snprintf( buffer, sizeof(buffer), "%s: %s\n", 2558 android_sensors_get_name_from_id(id), (status ? "enabled.":"disabled.") ); 2559 control_write( client, buffer ); 2560 } 2561 2562 return 0; 2563 } 2564 2565 /* Sensor commands for get/set sensor values and get available sensor names. */ 2566 static const CommandDefRec sensor_commands[] = 2567 { 2568 { "status", "list all sensors and their status.", 2569 "'status': list all sensors and their status.\r\n", 2570 NULL, do_sensors_status, NULL }, 2571 2572 { "get", "get sensor values", 2573 "'get <sensorname>' returns the values of a given sensor.\r\n", 2574 NULL, do_sensors_get, NULL }, 2575 2576 { "set", "set sensor values", 2577 "'set <sensorname> <value-a>[:<value-b>[:<value-c>]]' set the values of a given sensor.\r\n", 2578 NULL, do_sensors_set, NULL }, 2579 2580 { NULL, NULL, NULL, NULL, NULL, NULL } 2581 }; 2582 2583 /********************************************************************************************/ 2584 /********************************************************************************************/ 2585 /***** ******/ 2586 /***** M A I N C O M M A N D S ******/ 2587 /***** ******/ 2588 /********************************************************************************************/ 2589 /********************************************************************************************/ 2590 2591 static int 2592 do_window_scale( ControlClient client, char* args ) 2593 { 2594 double scale; 2595 int is_dpi = 0; 2596 char* end; 2597 2598 if (!args) { 2599 control_write( client, "KO: argument missing, try 'window scale <scale>'\r\n" ); 2600 return -1; 2601 } 2602 2603 scale = strtol( args, &end, 10 ); 2604 if (end > args && !memcmp( end, "dpi", 4 )) { 2605 is_dpi = 1; 2606 } 2607 else { 2608 scale = strtod( args, &end ); 2609 if (end == args || end[0]) { 2610 control_write( client, "KO: argument <scale> must be a real number, or an integer followed by 'dpi'\r\n" ); 2611 return -1; 2612 } 2613 } 2614 2615 android_emulator_set_window_scale(scale, is_dpi); 2616 return 0; 2617 } 2618 2619 static const CommandDefRec window_commands[] = 2620 { 2621 { "scale", "change the window scale", 2622 "'window scale <scale>' allows you to change the scale of the emulator window at runtime\r\n" 2623 "<scale> must be either a real number between 0.1 and 3.0, or an integer followed by\r\n" 2624 "the 'dpi' prefix (as in '120dpi')\r\n", 2625 NULL, do_window_scale, NULL }, 2626 2627 { NULL, NULL, NULL, NULL, NULL, NULL } 2628 }; 2629 2630 /********************************************************************************************/ 2631 /********************************************************************************************/ 2632 /***** ******/ 2633 /***** Q E M U C O M M A N D S ******/ 2634 /***** ******/ 2635 /********************************************************************************************/ 2636 /********************************************************************************************/ 2637 2638 static int 2639 do_qemu_monitor( ControlClient client, char* args ) 2640 { 2641 control_write(client, "KO: QEMU support no longer available\r\n"); 2642 return -1; 2643 } 2644 2645 #ifdef CONFIG_STANDALONE_CORE 2646 /* UI settings, passed to the core via -ui-settings command line parameter. */ 2647 extern char* android_op_ui_settings; 2648 2649 static int 2650 do_attach_ui( ControlClient client, char* args ) 2651 { 2652 // Make sure that there are no UI already attached to this console. 2653 if (attached_ui_client != NULL) { 2654 control_write( client, "KO: Another UI is attached to this core!\r\n" ); 2655 control_client_destroy(client); 2656 return -1; 2657 } 2658 2659 if (!attachUiProxy_create(client->sock)) { 2660 char reply_buf[4096]; 2661 attached_ui_client = client; 2662 // Reply "OK" with the saved -ui-settings property. 2663 snprintf(reply_buf, sizeof(reply_buf), "OK: %s\r\n", android_op_ui_settings); 2664 control_write( client, reply_buf); 2665 } else { 2666 control_write( client, "KO\r\n" ); 2667 control_client_destroy(client); 2668 return -1; 2669 } 2670 2671 return 0; 2672 } 2673 2674 void 2675 destroy_attach_ui_client(void) 2676 { 2677 if (attached_ui_client != NULL) { 2678 control_client_destroy(attached_ui_client); 2679 } 2680 } 2681 2682 static int 2683 do_create_framebuffer_service( ControlClient client, char* args ) 2684 { 2685 ProxyFramebuffer* core_fb; 2686 const char* protocol = "-raw"; // Default framebuffer exchange protocol. 2687 char reply_buf[64]; 2688 2689 // Protocol type is defined by the arguments passed with the stream switch 2690 // command. 2691 if (args != NULL && *args != '\0') { 2692 size_t token_len; 2693 const char* param_end = strchr(args, ' '); 2694 if (param_end == NULL) { 2695 param_end = args + strlen(args); 2696 } 2697 token_len = param_end - args; 2698 protocol = args; 2699 2700 // Make sure that this is one of the supported protocols. 2701 if (strncmp(protocol, "-raw", token_len) && 2702 strncmp(protocol, "-shared", token_len)) { 2703 derror("Invalid framebuffer parameter %s\n", protocol); 2704 control_write( client, "KO: Invalid parameter\r\n" ); 2705 control_client_destroy(client); 2706 return -1; 2707 } 2708 } 2709 2710 core_fb = proxyFb_create(client->sock, protocol); 2711 if (core_fb == NULL) { 2712 control_write( client, "KO\r\n" ); 2713 control_client_destroy(client); 2714 return -1; 2715 } 2716 2717 // Reply "OK" with the framebuffer's bits per pixel 2718 snprintf(reply_buf, sizeof(reply_buf), "OK: -bitsperpixel=%d\r\n", 2719 proxyFb_get_bits_per_pixel(core_fb)); 2720 control_write( client, reply_buf); 2721 return 0; 2722 } 2723 2724 static int 2725 do_create_user_events_service( ControlClient client, char* args ) 2726 { 2727 // Make sure that there are no user events client already existing. 2728 if (user_events_client != NULL) { 2729 control_write( client, "KO: Another user events service is already existing!\r\n" ); 2730 control_client_destroy(client); 2731 return -1; 2732 } 2733 2734 if (!userEventsImpl_create(client->sock)) { 2735 char reply_buf[4096]; 2736 user_events_client = client; 2737 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n"); 2738 control_write( client, reply_buf); 2739 } else { 2740 control_write( client, "KO\r\n" ); 2741 control_client_destroy(client); 2742 return -1; 2743 } 2744 2745 return 0; 2746 } 2747 2748 void 2749 destroy_user_events_client(void) 2750 { 2751 if (user_events_client != NULL) { 2752 control_client_destroy(user_events_client); 2753 } 2754 } 2755 2756 static int 2757 do_create_ui_core_ctl_service( ControlClient client, char* args ) 2758 { 2759 // Make sure that there are no ui control client already existing. 2760 if (ui_core_ctl_client != NULL) { 2761 control_write( client, "KO: Another UI control service is already existing!\r\n" ); 2762 control_client_destroy(client); 2763 return -1; 2764 } 2765 2766 if (!coreCmdImpl_create(client->sock)) { 2767 char reply_buf[4096]; 2768 ui_core_ctl_client = client; 2769 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n"); 2770 control_write( client, reply_buf); 2771 } else { 2772 control_write( client, "KO\r\n" ); 2773 control_client_destroy(client); 2774 return -1; 2775 } 2776 2777 return 0; 2778 } 2779 2780 void 2781 destroy_ui_core_ctl_client(void) 2782 { 2783 if (ui_core_ctl_client != NULL) { 2784 control_client_destroy(ui_core_ctl_client); 2785 } 2786 } 2787 2788 void 2789 destroy_corecmd_client(void) 2790 { 2791 if (ui_core_ctl_client != NULL) { 2792 control_client_destroy(ui_core_ctl_client); 2793 } 2794 } 2795 2796 static int 2797 do_create_core_ui_ctl_service( ControlClient client, char* args ) 2798 { 2799 // Make sure that there are no ui control client already existing. 2800 if (core_ui_ctl_client != NULL) { 2801 control_write( client, "KO: Another UI control service is already existing!\r\n" ); 2802 control_client_destroy(client); 2803 return -1; 2804 } 2805 2806 if (!uiCmdProxy_create(client->sock)) { 2807 char reply_buf[4096]; 2808 core_ui_ctl_client = client; 2809 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n"); 2810 control_write( client, reply_buf); 2811 } else { 2812 control_write( client, "KO\r\n" ); 2813 control_client_destroy(client); 2814 return -1; 2815 } 2816 2817 return 0; 2818 } 2819 2820 void 2821 destroy_core_ui_ctl_client(void) 2822 { 2823 if (core_ui_ctl_client != NULL) { 2824 control_client_destroy(core_ui_ctl_client); 2825 } 2826 } 2827 2828 void 2829 destroy_uicmd_client(void) 2830 { 2831 if (core_ui_ctl_client != NULL) { 2832 control_client_destroy(core_ui_ctl_client); 2833 } 2834 } 2835 2836 #endif // CONFIG_STANDALONE_CORE 2837 2838 static const CommandDefRec qemu_commands[] = 2839 { 2840 { "monitor", "enter QEMU monitor", 2841 "Enter the QEMU virtual machine monitor\r\n", 2842 NULL, do_qemu_monitor, NULL }, 2843 2844 #ifdef CONFIG_STANDALONE_CORE 2845 { "attach-UI", "attach UI to the core", 2846 "Attach UI to the core\r\n", 2847 NULL, do_attach_ui, NULL }, 2848 2849 { "framebuffer", "create framebuffer service", 2850 "Create framebuffer service\r\n", 2851 NULL, do_create_framebuffer_service, NULL }, 2852 2853 { "user-events", "create user events service", 2854 "Create user events service\r\n", 2855 NULL, do_create_user_events_service, NULL }, 2856 2857 { "ui-core-control", "create UI control service", 2858 "Create UI control service\r\n", 2859 NULL, do_create_ui_core_ctl_service, NULL }, 2860 2861 { "core-ui-control", "create UI control service", 2862 "Create UI control service\r\n", 2863 NULL, do_create_core_ui_ctl_service, NULL }, 2864 #endif // CONFIG_STANDALONE_CORE 2865 2866 { NULL, NULL, NULL, NULL, NULL, NULL } 2867 }; 2868 2869 2870 /********************************************************************************************/ 2871 /********************************************************************************************/ 2872 /***** ******/ 2873 /***** M A I N C O M M A N D S ******/ 2874 /***** ******/ 2875 /********************************************************************************************/ 2876 /********************************************************************************************/ 2877 2878 static int 2879 do_kill( ControlClient client, char* args ) 2880 { 2881 control_write( client, "OK: killing emulator, bye bye\r\n" ); 2882 exit(0); 2883 } 2884 2885 static const CommandDefRec main_commands[] = 2886 { 2887 { "help|h|?", "print a list of commands", NULL, NULL, do_help, NULL }, 2888 2889 { "event", "simulate hardware events", 2890 "allows you to send fake hardware events to the kernel\r\n", NULL, 2891 NULL, event_commands }, 2892 2893 { "geo", "Geo-location commands", 2894 "allows you to change Geo-related settings, or to send GPS NMEA sentences\r\n", NULL, 2895 NULL, geo_commands }, 2896 2897 { "gsm", "GSM related commands", 2898 "allows you to change GSM-related settings, or to make a new inbound phone call\r\n", NULL, 2899 NULL, gsm_commands }, 2900 2901 { "cdma", "CDMA related commands", 2902 "allows you to change CDMA-related settings\r\n", NULL, 2903 NULL, cdma_commands }, 2904 2905 { "kill", "kill the emulator instance", NULL, NULL, 2906 do_kill, NULL }, 2907 2908 { "network", "manage network settings", 2909 "allows you to manage the settings related to the network data connection of the\r\n" 2910 "emulated device.\r\n", NULL, 2911 NULL, network_commands }, 2912 2913 { "power", "power related commands", 2914 "allows to change battery and AC power status\r\n", NULL, 2915 NULL, power_commands }, 2916 2917 { "quit|exit", "quit control session", NULL, NULL, 2918 do_quit, NULL }, 2919 2920 { "redir", "manage port redirections", 2921 "allows you to add, list and remove UDP and/or PORT redirection from the host to the device\r\n" 2922 "as an example, 'redir tcp:5000:6000' will route any packet sent to the host's TCP port 5000\r\n" 2923 "to TCP port 6000 of the emulated device\r\n", NULL, 2924 NULL, redir_commands }, 2925 2926 { "sms", "SMS related commands", 2927 "allows you to simulate an inbound SMS\r\n", NULL, 2928 NULL, sms_commands }, 2929 2930 { "avd", "control virtual device execution", 2931 "allows you to control (e.g. start/stop) the execution of the virtual device\r\n", NULL, 2932 NULL, vm_commands }, 2933 2934 { "window", "manage emulator window", 2935 "allows you to modify the emulator window\r\n", NULL, 2936 NULL, window_commands }, 2937 2938 { "qemu", "QEMU-specific commands", 2939 "allows to connect to the QEMU virtual machine monitor\r\n", NULL, 2940 NULL, qemu_commands }, 2941 2942 { "sensor", "manage emulator sensors", 2943 "allows you to request the emulator sensors\r\n", NULL, 2944 NULL, sensor_commands }, 2945 2946 { NULL, NULL, NULL, NULL, NULL, NULL } 2947 }; 2948 2949 2950 static ControlGlobalRec _g_global; 2951 2952 int 2953 control_console_start( int port ) 2954 { 2955 return control_global_init( &_g_global, port ); 2956 } 2957