| /* |
| * 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 <config.h> |
| #endif |
| |
| #include <stdlib.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <unistd.h> |
| #include <poll.h> |
| #include <unistd.h> |
| #include <getopt.h> |
| |
| #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 <inerface>\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, "[<interface>]"), |
| STD_METHOD(quit), |
| METHOD("exit", quit_p, NULL, NULL), |
| STD_METHODH(source, "<file>"), |
| 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; |
| } |