blob: 92cd8114a3fe04cdc60b754865553a2762f1d931 [file] [log] [blame]
/*
* 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;
}