diff --git a/android/client/haltest.c b/android/client/haltest.c
index 6dcabbc..6b37f33 100644
--- a/android/client/haltest.c
+++ b/android/client/haltest.c
continue;
}
if (argc < 2 || strcmp(argv[1], "?") == 0) {
- j = 0;
- while (strcmp(interfaces[i]->methods[j].name, "")) {
- haltest_info("%s %s\n", argv[0],
- interfaces[i]->methods[j].name);
- ++j;
+ struct method *m = &interfaces[i]->methods[0];
+ while (strcmp(m->name, "")) {
+ haltest_info("%s %s %s\n", argv[0],
+ m->name,
+ (m->help ? m->help : ""));
+ m++;
}
return;
}
diff --git a/android/client/if-bt.c b/android/client/if-bt.c
index 90ef732..30b41cd 100644
--- a/android/client/if-bt.c
+++ b/android/client/if-bt.c
const bt_interface_t *if_bluetooth;
+#define VERIFY_PROP_TYPE_ARG(n, typ) \
+ do { \
+ if (n < argc) \
+ typ = str2btpropertytype(argv[n]); \
+ else { \
+ haltest_error("No property type specified\n"); \
+ return;\
+ } \
+ } while (0)
+
static char *bdaddr2str(const bt_bdaddr_t *bd_addr)
{
static char buf[18];
}
}
+/*
+ * Cache for remote devices, stored in sorted array
+ */
+static bt_bdaddr_t *remote_devices = NULL;
+static int remote_devices_cnt = 0;
+static int remote_devices_capacity = 0;
+
+/* Adds address to remote device set so it can be used in tab completion */
+void add_remote_device(const bt_bdaddr_t *addr)
+{
+ int i;
+
+ if (remote_devices == NULL) {
+ remote_devices = malloc(4 * sizeof(bt_bdaddr_t));
+ remote_devices_cnt = 0;
+ if (remote_devices == NULL) {
+ remote_devices_capacity = 0;
+ return;
+ }
+ remote_devices_capacity = 4;
+ }
+
+ /* Array is sorted, search for right place */
+ for (i = 0; i < remote_devices_cnt; ++i) {
+ int res = memcmp(&remote_devices[i], addr, sizeof(*addr));
+ if (res == 0)
+ return; /* Already added */
+ else if (res > 0)
+ break;
+ }
+
+ /* Realloc space if needed */
+ if (remote_devices_cnt >= remote_devices_capacity) {
+ remote_devices_capacity *= 2;
+ remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) *
+ remote_devices_capacity);
+ if (remote_devices == NULL) {
+ remote_devices_capacity = 0;
+ remote_devices_cnt = 0;
+ return;
+ }
+ }
+
+ if (i < remote_devices_cnt)
+ memmove(remote_devices + i + 1, remote_devices + i,
+ (remote_devices_cnt - i) * sizeof(bt_bdaddr_t));
+ remote_devices[i] = *addr;
+ remote_devices_cnt++;
+}
+
+const char *enum_devices(void *v, int i)
+{
+ static char buf[19];
+
+ if (i >= remote_devices_cnt)
+ return NULL;
+
+ bt_bdaddr_t2str(&remote_devices[i], buf);
+ return buf;
+}
+
+static void add_remote_device_from_props(int num_properties,
+ const bt_property_t *properties)
+{
+ int i;
+
+ for (i = 0; i < num_properties; i++) {
+ /*
+ * properities sometimes come unaligned hence memcp to
+ * aligned buffer
+ */
+ bt_property_t property;
+ memcpy(&property, properties + i, sizeof(property));
+ if (property.type == BT_PROPERTY_BDADDR)
+ add_remote_device((bt_bdaddr_t *) property.val);
+ }
+}
+
static void adapter_state_changed_cb(bt_state_t state)
{
haltest_info("%s: state=%s\n", __func__, bt_state_t2str(state));
__func__, bt_status_t2str(status), bdaddr2str(bd_addr),
num_properties);
+ add_remote_device(bd_addr);
+
dump_properties(num_properties, properties);
}
{
haltest_info("%s: num_properties=%d\n", __func__, num_properties);
+ add_remote_device_from_props(num_properties, properties);
+
dump_properties(num_properties, properties);
}
bt_discovery_state_t2str(state));
}
+/*
+ * Buffer for remote addres that came from one of bind request.
+ * It's stored for command completion.
+ */
+static char last_remote_addr[18];
+static bt_ssp_variant_t last_ssp_variant = (bt_ssp_variant_t)-1;
+
static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
uint32_t cod)
{
+ /* Store for command completion */
+ bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+
haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x\n", __func__,
- bdaddr2str(remote_bd_addr), bd_name->name, cod);
+ last_remote_addr, bd_name->name, cod);
}
static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
uint32_t cod, bt_ssp_variant_t pairing_variant,
uint32_t pass_key)
{
+ /* Store for command completion */
+ bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+ last_ssp_variant = pairing_variant;
+
haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x pairing_variant=%s pass_key=%d\n",
- __func__, bdaddr2str(remote_bd_addr), bd_name->name, cod,
+ __func__, last_remote_addr, bd_name->name, cod,
bt_ssp_variant_t2str(pairing_variant), pass_key);
}
EXEC(if_bluetooth->get_adapter_properties);
}
+static void get_adapter_property_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ if (argc == 3) {
+ *puser = TYPE_ENUM(bt_property_type_t);
+ *penum_func = enum_defines;
+ }
+}
+
static void get_adapter_property_p(int argc, const char **argv)
{
- int type = str2btpropertytype(argv[2]);
+ int type;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_PROP_TYPE_ARG(2, type);
EXEC(if_bluetooth->get_adapter_property, type);
}
+static const char * const names[] = {
+ "BT_PROPERTY_BDNAME",
+ "BT_PROPERTY_ADAPTER_SCAN_MODE",
+ "BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT", NULL
+};
+
+static void set_adapter_property_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ if (argc == 3) {
+ *puser = (void *) names;
+ *penum_func = enum_strings;
+ } else if (argc == 4) {
+ if (0 == strcmp(argv[2], "BT_PROPERTY_ADAPTER_SCAN_MODE")) {
+ *puser = TYPE_ENUM(bt_scan_mode_t);
+ *penum_func = enum_defines;
+ }
+ }
+}
+
static void set_adapter_property_p(int argc, const char **argv)
{
bt_property_t property;
int timeout;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_PROP_TYPE_ARG(2, property.type);
- property.type = str2btpropertytype(argv[2]);
-
+ if (argc <= 3) {
+ haltest_error("No property value specified\n");
+ return;
+ }
switch (property.type) {
case BT_PROPERTY_BDNAME:
property.len = strlen(argv[3]) + 1;
EXEC(if_bluetooth->set_adapter_property, &property);
}
+/*
+ * This function is to be used for completion methods that need only address
+ */
+static void complete_addr_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ if (argc == 3) {
+ *puser = NULL;
+ *penum_func = enum_devices;
+ }
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_device_properties_c complete_addr_c
+
static void get_remote_device_properties_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->get_remote_device_properties, &addr);
}
+static void get_remote_device_property_c(int argc, const char **argv,
+ enum_func *penum_func,
+ void **puser)
+{
+ if (argc == 3) {
+ *puser = NULL;
+ *penum_func = enum_devices;
+ } else if (argc == 4) {
+ *puser = TYPE_ENUM(bt_property_type_t);
+ *penum_func = enum_defines;
+ }
+}
+
static void get_remote_device_property_p(int argc, const char **argv)
{
bt_property_type_t type;
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
- type = str2btpropertytype(argv[3]);
+ VERIFY_ADDR_ARG(2, &addr);
+ VERIFY_PROP_TYPE_ARG(3, type);
EXEC(if_bluetooth->get_remote_device_property, &addr, type);
}
+/*
+ * Same completion as for get_remote_device_property_c can be used for
+ * set_remote_device_property_c. No need to create separate function.
+ */
+#define set_remote_device_property_c get_remote_device_property_c
+
static void set_remote_device_property_p(int argc, const char **argv)
{
bt_property_t property;
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
- property.type = str2btpropertytype(argv[3]);
+ VERIFY_ADDR_ARG(2, &addr);
+ VERIFY_PROP_TYPE_ARG(3, property.type);
switch (property.type) {
case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
EXEC(if_bluetooth->set_remote_device_property, &addr, &property);
}
+/*
+ * For now uuid is not autocompleted. Use routine for complete_addr_c
+ */
+#define get_remote_service_record_c complete_addr_c
+
static void get_remote_service_record_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
bt_uuid_t uuid;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
- str2bt_bdaddr_t(argv[2], &addr);
+ if (argc <= 3) {
+ haltest_error("No uuid specified\n");
+ return;
+ }
str2bt_uuid_t(argv[3], &uuid);
EXEC(if_bluetooth->get_remote_service_record, &addr, &uuid);
}
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_services_c complete_addr_c
+
static void get_remote_services_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->get_remote_services, &addr);
}
EXEC(if_bluetooth->cancel_discovery);
}
+/* Just addres to complete, use complete_addr_c */
+#define create_bond_c complete_addr_c
+
static void create_bond_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->create_bond, &addr);
}
+/* Just addres to complete, use complete_addr_c */
+#define remove_bond_c complete_addr_c
+
static void remove_bond_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->remove_bond, &addr);
}
+/* Just addres to complete, use complete_addr_c */
+#define cancel_bond_c complete_addr_c
+
static void cancel_bond_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_bluetooth);
-
- str2bt_bdaddr_t(argv[2], &addr);
+ VERIFY_ADDR_ARG(2, &addr);
EXEC(if_bluetooth->cancel_bond, &addr);
}
+static void pin_reply_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ static const char *const completions[] = { last_remote_addr, NULL };
+ if (argc == 3) {
+ *puser = (void *) completions;
+ *penum_func = enum_strings;
+ }
+}
+
static void pin_reply_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
int accept;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
- if (argc < 3) {
- haltest_error("No address specified\n");
- return;
- }
- str2bt_bdaddr_t(argv[2], &addr);
-
- if (argc >= 4) {
+ if (argc > 3) {
accept = 1;
pin_len = strlen(argv[3]);
memcpy(pin.pin, argv[3], pin_len);
EXEC(if_bluetooth->pin_reply, &addr, accept, pin_len, &pin);
}
+static void ssp_reply_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ if (argc == 3) {
+ *puser = last_remote_addr;
+ *penum_func = enum_one_string;
+ } else if (argc == 5) {
+ *puser = "1";
+ *penum_func = enum_one_string;
+ } else if (argc == 4) {
+ if (-1 != (int) last_ssp_variant) {
+ *puser = (void *) bt_ssp_variant_t2str(last_ssp_variant);
+ *penum_func = enum_one_string;
+ } else {
+ *puser = TYPE_ENUM(bt_ssp_variant_t);
+ *penum_func = enum_defines;
+ }
+ }
+}
+
static void ssp_reply_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
int passkey;
RETURN_IF_NULL(if_bluetooth);
+ VERIFY_ADDR_ARG(2, &addr);
- if (argc < 3) {
- haltest_error("No address specified\n");
- return;
- }
- str2bt_bdaddr_t(argv[2], &addr);
if (argc < 4) {
haltest_error("No ssp variant specified\n");
return;
EXEC(if_bluetooth->ssp_reply, &addr, var, accept, passkey);
}
+static void get_profile_interface_c(int argc, const char **argv,
+ enum_func *penum_func, void **puser)
+{
+ static const char *const profile_ids[] = {
+ BT_PROFILE_HANDSFREE_ID,
+ BT_PROFILE_ADVANCED_AUDIO_ID,
+ BT_PROFILE_HEALTH_ID,
+ BT_PROFILE_SOCKETS_ID,
+ BT_PROFILE_HIDHOST_ID,
+ BT_PROFILE_PAN_ID,
+#if PLATFORM_SDK_VERSION >= 18
+ BT_PROFILE_GATT_ID,
+#endif
+ BT_PROFILE_AV_RC_ID,
+ NULL
+ };
+
+ if (argc == 3) {
+ *puser = (void *) profile_ids;
+ *penum_func = enum_strings;
+ }
+}
+
static void get_profile_interface_p(int argc, const char **argv)
{
const char *id = argv[2];
RETURN_IF_NULL(if_bluetooth);
+ if (argc <= 2) {
+ haltest_error("No dut mode specified\n");
+ return;
+ }
mode = strtol(argv[2], NULL, 0);
EXEC(if_bluetooth->dut_mode_configure, mode);
STD_METHOD(enable),
STD_METHOD(disable),
STD_METHOD(get_adapter_properties),
- STD_METHOD(get_adapter_property),
- STD_METHOD(set_adapter_property),
- STD_METHOD(get_remote_device_properties),
- STD_METHOD(get_remote_device_property),
- STD_METHOD(set_remote_device_property),
- STD_METHOD(get_remote_service_record),
- STD_METHOD(get_remote_services),
+ STD_METHODCH(get_adapter_property, "<prop_type>"),
+ STD_METHODCH(set_adapter_property, "<prop_type> <prop_value>"),
+ STD_METHODCH(get_remote_device_properties, "<addr>"),
+ STD_METHODCH(get_remote_device_property, "<addr> <property_type>"),
+ STD_METHODCH(set_remote_device_property, "<addr> <property_type> <value>"),
+ STD_METHODCH(get_remote_service_record, "<addr> <uuid>"),
+ STD_METHODCH(get_remote_services, "<addr>"),
STD_METHOD(start_discovery),
STD_METHOD(cancel_discovery),
- STD_METHOD(create_bond),
- STD_METHOD(remove_bond),
- STD_METHOD(cancel_bond),
- STD_METHOD(pin_reply),
- STD_METHOD(ssp_reply),
- STD_METHOD(get_profile_interface),
- STD_METHOD(dut_mode_configure),
+ STD_METHODCH(create_bond, "<addr>"),
+ STD_METHODCH(remove_bond, "<addr>"),
+ STD_METHODCH(cancel_bond, "<addr>"),
+ STD_METHODCH(pin_reply, "<address> [<pin>]"),
+ STD_METHODCH(ssp_reply, "<address> <ssp_veriant> 1|0 [<passkey>]"),
+ STD_METHODCH(get_profile_interface, "<profile id>"),
+ STD_METHODH(dut_mode_configure, "<dut mode>"),
END_METHOD
};
diff --git a/android/client/if-main.h b/android/client/if-main.h
index 3fb3007..b6bbf05 100644
--- a/android/client/if-main.h
+++ b/android/client/if-main.h
int haltest_info(const char *format, ...);
int haltest_warn(const char *format, ...);
+/*
+ * Enumerator for discovered devices, to be used as tab completion enum_func
+ */
+const char *enum_devices(void *v, int i);
+void add_remote_device(const bt_bdaddr_t *addr);
+
/* Helper macro for executing function on interface and printing BT_STATUS */
#define EXEC(f, ...) \
{ \
#define RETURN_IF_NULL(x) \
do { if (!x) { haltest_error("%s is NULL\n", #x); return; } } while (0)
+
+#define VERIFY_ADDR_ARG(n, adr) \
+ do { \
+ if (n < argc) \
+ str2bt_bdaddr_t(argv[n], adr); \
+ else { \
+ haltest_error("No address specified\n");\
+ return;\
+ } \
+ } while (0)
diff --git a/android/client/textconv.c b/android/client/textconv.c
index eebad70..3493b1c 100644
--- a/android/client/textconv.c
+++ b/android/client/textconv.c
str += 2;
}
}
+
+const char *enum_defines(void *v, int i)
+{
+ const struct int2str *m = v;
+
+ return m[i].str != NULL ? m[i].str : NULL;
+}
+
+const char *enum_strings(void *v, int i)
+{
+ const char **m = v;
+
+ return m[i] != NULL ? m[i] : NULL;
+}
+
+const char *enum_one_string(void *v, int i)
+{
+ const char *m = v;
+
+ return (i == 0) && (m[0] != 0) ? m : NULL;
+}
diff --git a/android/client/textconv.h b/android/client/textconv.h
index 8fe976c..88da641 100644
--- a/android/client/textconv.h
+++ b/android/client/textconv.h
int int2str_findint(int v, const struct int2str m[]);
int int2str_findstr(const char *str, const struct int2str m[]);
+const char *enum_defines(void *v, int i);
+const char *enum_strings(void *v, int i);
+const char *enum_one_string(void *v, int i);
+#define TYPE_ENUM(type) ((void *)&__##type##2str[0])
#define DECINTMAP(type) \
extern struct int2str __##type##2str[]; \
const char *type##2##str(type v); \