/* * Copyright (C) 2013 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "if-main.h" #include "terminal.h" #include "pollhandler.h" #include "history.h" static void process_line(char *line_buffer); const struct interface *interfaces[] = { &audio_if, &sco_if, &bluetooth_if, &av_if, &rc_if, &gatt_if, &gatt_client_if, &gatt_server_if, &hf_if, &hh_if, &pan_if, &hl_if, &sock_if, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) &hf_client_if, &mce_if, &ctrl_rc_if, &av_sink_if, #endif NULL }; static struct method commands[]; struct method *get_method(struct method *methods, const char *name) { while (strcmp(methods->name, "") != 0) { if (strcmp(methods->name, name) == 0) return methods; methods++; } return NULL; } /* function returns interface of given name or NULL if not found */ const struct interface *get_interface(const char *name) { int i; for (i = 0; interfaces[i] != NULL; ++i) { if (strcmp(interfaces[i]->name, name) == 0) break; } return interfaces[i]; } int haltest_error(const char *format, ...) { va_list args; int ret; va_start(args, format); ret = terminal_vprint(format, args); va_end(args); return ret; } int haltest_info(const char *format, ...) { va_list args; int ret; va_start(args, format); ret = terminal_vprint(format, args); va_end(args); return ret; } int haltest_warn(const char *format, ...) { va_list args; int ret; va_start(args, format); ret = terminal_vprint(format, args); va_end(args); return ret; } static void help_print_interface(const struct interface *i) { struct method *m; for (m = i->methods; strcmp(m->name, "") != 0; m++) haltest_info("%s %s %s\n", i->name, m->name, (m->help ? m->help : "")); } /* Help completion */ static void help_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 2) *enum_func = interface_name; } /* Help execution */ static void help_p(int argc, const char **argv) { const struct method *m = commands; const struct interface **ip = interfaces; const struct interface *i; if (argc == 1) { terminal_print("haltest allows to call Android HAL methods.\n"); terminal_print("\nAvailable commands:\n"); while (0 != strcmp(m->name, "")) { terminal_print("\t%s %s\n", m->name, (m->help ? m->help : "")); m++; } terminal_print("\nAvailable interfaces to use:\n"); while (NULL != *ip) { terminal_print("\t%s\n", (*ip)->name); ip++; } terminal_print("\nTo get help on methods for each interface type:\n"); terminal_print("\n\thelp \n"); terminal_print("\nBasic scenario:\n\tbluetooth init\n"); terminal_print("\tbluetooth enable\n\tbluetooth start_discovery\n"); terminal_print("\tbluetooth get_profile_interface handsfree\n"); terminal_print("\thandsfree init\n\n"); return; } i = get_interface(argv[1]); if (i == NULL) { haltest_error("No such interface\n"); return; } help_print_interface(i); } /* quit/exit execution */ static void quit_p(int argc, const char **argv) { char cleanup_audio[] = "audio cleanup"; close_hw_bt_dev(); process_line(cleanup_audio); exit(0); } static int fd_stack[10]; static int fd_stack_pointer = 0; static void stdin_handler(struct pollfd *pollfd); static void process_file(const char *name) { int fd = open(name, O_RDONLY); if (fd < 0) { haltest_error("Can't open file: %s for reading\n", name); return; } if (fd_stack_pointer >= 10) { haltest_error("To many open files\n"); close(fd); return; } fd_stack[fd_stack_pointer++] = fd; poll_unregister_fd(fd_stack[fd_stack_pointer - 2], stdin_handler); poll_register_fd(fd_stack[fd_stack_pointer - 1], POLLIN, stdin_handler); } static void source_p(int argc, const char **argv) { if (argc < 2) { haltest_error("No file specified"); return; } process_file(argv[1]); } /* Commands available without interface */ static struct method commands[] = { STD_METHODCH(help, "[]"), STD_METHOD(quit), METHOD("exit", quit_p, NULL, NULL), STD_METHODH(source, ""), END_METHOD }; /* Gets comman by name */ struct method *get_command(const char *name) { return get_method(commands, name); } /* Function to enumerate interface names */ const char *interface_name(void *v, int i) { return interfaces[i] ? interfaces[i]->name : NULL; } /* Function to enumerate command and interface names */ const char *command_name(void *v, int i) { int cmd_cnt = NELEM(commands); if (i >= cmd_cnt) return interface_name(v, i - cmd_cnt); else return commands[i].name; } /* * This function changes input parameter line_buffer so it has * null termination after each token (due to strtok) * Output argv is filled with pointers to arguments * returns number of tokens parsed - argc */ static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size) { static const char *token_breaks = "\r\n\t "; char *token; int argc = 0; token = strtok(line_buffer, token_breaks); while (token != NULL && argc < (int) argv_size) { argv[argc++] = token; token = strtok(NULL, token_breaks); } return argc; } static void process_line(char *line_buffer) { char *argv[50]; int argc; int i = 0; struct method *m; argc = command_line_to_argv(line_buffer, argv, 50); if (argc < 1) return; while (interfaces[i] != NULL) { if (strcmp(interfaces[i]->name, argv[0])) { i++; continue; } if (argc < 2 || strcmp(argv[1], "?") == 0) { help_print_interface(interfaces[i]); return; } m = get_method(interfaces[i]->methods, argv[1]); if (m != NULL) { m->func(argc, (const char **) argv); return; } haltest_error("No function %s found\n", argv[1]); return; } /* No interface, try commands */ m = get_command(argv[0]); if (m == NULL) haltest_error("No such command %s\n", argv[0]); else m->func(argc, (const char **) argv); } /* called when there is something on stdin */ static void stdin_handler(struct pollfd *pollfd) { char buf[10]; if (pollfd->revents & POLLIN) { int count = read(fd_stack[fd_stack_pointer - 1], buf, 10); if (count > 0) { int i; for (i = 0; i < count; ++i) terminal_process_char(buf[i], process_line); return; } } if (fd_stack_pointer > 1) poll_register_fd(fd_stack[fd_stack_pointer - 2], POLLIN, stdin_handler); if (fd_stack_pointer > 0) { poll_unregister_fd(fd_stack[--fd_stack_pointer], stdin_handler); if (fd_stack[fd_stack_pointer]) close(fd_stack[fd_stack_pointer]); } } static void usage(void) { printf("haltest Android Bluetooth HAL testing tool\n" "Usage:\n"); printf("\thaltest [options]\n"); printf("options:\n" "\t-i --ivi Initialize only IVI interfaces\n" "\t-n, --no-init Don't initialize any interfaces\n" "\t-v --version Print version\n" "\t-h, --help Show help options\n"); } static void print_version(void) { printf("haltest version %s\n", VERSION); } static const struct option main_options[] = { { "no-init", no_argument, NULL, 'n' }, { "ivi", no_argument, NULL, 'i' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { NULL } }; static bool no_init = false; static bool ivi_only = false; static void parse_command_line(int argc, char *argv[]) { for (;;) { int opt; opt = getopt_long(argc, argv, "inhv", main_options, NULL); if (opt < 0) break; switch (opt) { case 'n': no_init = true; break; case 'i': ivi_only = true; break; case 'h': usage(); exit(0); case 'v': print_version(); exit(0); default: putchar('\n'); exit(-1); break; } } } static const char * const interface_names[] = { BT_PROFILE_HANDSFREE_ID, BT_PROFILE_ADVANCED_AUDIO_ID, BT_PROFILE_AV_RC_ID, BT_PROFILE_HEALTH_ID, BT_PROFILE_HIDHOST_ID, BT_PROFILE_PAN_ID, BT_PROFILE_GATT_ID, BT_PROFILE_SOCKETS_ID, NULL }; static const char * const ivi_interface_inames[] = { #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) BT_PROFILE_HANDSFREE_CLIENT_ID, BT_PROFILE_MAP_CLIENT_ID, BT_PROFILE_AV_RC_CTRL_ID, BT_PROFILE_ADVANCED_AUDIO_SINK_ID, #endif NULL }; static void init(const char * const *inames) { const struct method *m; const char *argv[4]; char init_audio[] = "audio init"; char init_sco[] = "sco init"; char init_bt[] = "bluetooth init"; uint32_t i; process_line(init_audio); process_line(init_sco); process_line(init_bt); m = get_interface_method("bluetooth", "get_profile_interface"); while (*inames) { argv[2] = *inames; m->func(3, argv); inames++; } /* Init what is available to init */ for (i = 3; i < NELEM(interfaces) - 1; ++i) { m = get_interface_method(interfaces[i]->name, "init"); if (m != NULL) m->func(2, argv); } } int main(int argc, char **argv) { struct stat rcstat; parse_command_line(argc, argv); terminal_setup(); if (!no_init) { if (ivi_only) init(ivi_interface_inames); else init(interface_names); } history_restore(".haltest_history"); fd_stack[fd_stack_pointer++] = 0; /* Register command line handler */ poll_register_fd(0, POLLIN, stdin_handler); if (stat(".haltestrc", &rcstat) == 0 && (rcstat.st_mode & S_IFREG) != 0) process_file(".haltestrc"); poll_dispatch_loop(); return 0; }