// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2013 Intel Corporation * */ #define _GNU_SOURCE #include #include #include #include #include #include "if-main.h" #include "pollhandler.h" #include "../hal-utils.h" const bthh_interface_t *if_hh = NULL; SINTMAP(bthh_protocol_mode_t, -1, "(unknown)") DELEMENT(BTHH_REPORT_MODE), DELEMENT(BTHH_BOOT_MODE), DELEMENT(BTHH_UNSUPPORTED_MODE), ENDMAP SINTMAP(bthh_report_type_t, -1, "(unknown)") DELEMENT(BTHH_INPUT_REPORT), DELEMENT(BTHH_OUTPUT_REPORT), DELEMENT(BTHH_FEATURE_REPORT), ENDMAP SINTMAP(bthh_connection_state_t, -1, "(unknown)") DELEMENT(BTHH_CONN_STATE_CONNECTED), DELEMENT(BTHH_CONN_STATE_CONNECTING), DELEMENT(BTHH_CONN_STATE_DISCONNECTED), DELEMENT(BTHH_CONN_STATE_DISCONNECTING), DELEMENT(BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST), DELEMENT(BTHH_CONN_STATE_FAILED_KBD_FROM_HOST), DELEMENT(BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES), DELEMENT(BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER), DELEMENT(BTHH_CONN_STATE_FAILED_GENERIC), DELEMENT(BTHH_CONN_STATE_UNKNOWN), ENDMAP SINTMAP(bthh_status_t, -1, "(unknown)") DELEMENT(BTHH_OK), DELEMENT(BTHH_HS_HID_NOT_READY), DELEMENT(BTHH_HS_INVALID_RPT_ID), DELEMENT(BTHH_HS_TRANS_NOT_SPT), DELEMENT(BTHH_HS_INVALID_PARAM), DELEMENT(BTHH_HS_ERROR), DELEMENT(BTHH_ERR), DELEMENT(BTHH_ERR_SDP), DELEMENT(BTHH_ERR_PROTO), DELEMENT(BTHH_ERR_DB_FULL), DELEMENT(BTHH_ERR_TOD_UNSPT), DELEMENT(BTHH_ERR_NO_RES), DELEMENT(BTHH_ERR_AUTH_FAILED), DELEMENT(BTHH_ERR_HDL), ENDMAP static char connected_device_addr[MAX_ADDR_STR_LEN]; /* * Callback for connection state change. * state will have one of the values from bthh_connection_state_t */ static void connection_state_cb(bt_bdaddr_t *bd_addr, bthh_connection_state_t state) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s connection_state=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_connection_state_t2str(state)); if (state == BTHH_CONN_STATE_CONNECTED) strcpy(connected_device_addr, addr); } /* * Callback for virtual unplug api. * the status of the virtual unplug */ static void virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status)); } /* Callback for Android 5.0 handshake api. */ #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) static void handshake_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status)); } #endif /* * Callback for get hid info * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, * version, ctry_code, len */ static void hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info) { char addr[MAX_ADDR_STR_LEN]; /* TODO: bluedroid does not seem to ever call this callback */ haltest_info("%s: bd_addr=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr)); } /* * Callback for get/set protocol api. * the protocol mode is one of the value from bthh_protocol_mode_t */ static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, bthh_protocol_mode_t mode) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s hh_status=%s mode=%s\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status), bthh_protocol_mode_t2str(mode)); } /* Callback for get/set_idle_time api. */ static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate) { char addr[MAX_ADDR_STR_LEN]; haltest_info("%s: bd_addr=%s hh_status=%s idle_rate=%d\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status), idle_rate); } /* * Callback for get report api. * if status is ok rpt_data contains the report data */ static void get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t *rpt_data, int rpt_size) { char addr[MAX_ADDR_STR_LEN]; /* TODO: print actual report */ haltest_info("%s: bd_addr=%s hh_status=%s rpt_size=%d\n", __func__, bt_bdaddr_t2str(bd_addr, addr), bthh_status_t2str(hh_status), rpt_size); } static bthh_callbacks_t bthh_callbacks = { .size = sizeof(bthh_callbacks), .connection_state_cb = connection_state_cb, .hid_info_cb = hid_info_cb, .protocol_mode_cb = protocol_mode_cb, .idle_time_cb = idle_time_cb, .get_report_cb = get_report_cb, .virtual_unplug_cb = virtual_unplug_cb, #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) .handshake_cb = handshake_cb #endif }; /* init */ static void init_p(int argc, const char **argv) { RETURN_IF_NULL(if_hh); EXEC(if_hh->init, &bthh_callbacks); } /* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = (void *) connected_device_addr; *enum_func = enum_one_string; } } static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hh->connect, &addr); } /* disconnect */ /* Same completion as connect_c */ #define disconnect_c connect_c static void disconnect_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hh->disconnect, &addr); } /* virtual_unplug */ /* Same completion as connect_c */ #define virtual_unplug_c connect_c static void virtual_unplug_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); EXEC(if_hh->virtual_unplug, &addr); } /* set_info */ /* Same completion as connect_c */ #define set_info_c connect_c static void set_info_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_hid_info_t hid_info; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); memset(&hid_info, 0, sizeof(hid_info)); /* * This command is intentionally not supported. See comment from * bt_hid_info() in android/hidhost.c */ EXEC(if_hh->set_info, &addr, hid_info); } /* get_protocol */ static void get_protocol_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = connected_device_addr; *enum_func = enum_one_string; } else if (argc == 4) { *user = TYPE_ENUM(bthh_protocol_mode_t); *enum_func = enum_defines; } } static void get_protocol_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_protocol_mode_t protocolMode; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc < 4) { haltest_error("No protocol mode specified\n"); return; } protocolMode = str2bthh_protocol_mode_t(argv[3]); EXEC(if_hh->get_protocol, &addr, protocolMode); } /* set_protocol */ /* Same completion as get_protocol_c */ #define set_protocol_c get_protocol_c static void set_protocol_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_protocol_mode_t protocolMode; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc < 4) { haltest_error("No protocol mode specified\n"); return; } protocolMode = str2bthh_protocol_mode_t(argv[3]); EXEC(if_hh->set_protocol, &addr, protocolMode); } /* get_report */ static void get_report_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = connected_device_addr; *enum_func = enum_one_string; } else if (argc == 4) { *user = TYPE_ENUM(bthh_report_type_t); *enum_func = enum_defines; } } static void get_report_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_report_type_t reportType; uint8_t reportId; int bufferSize; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc < 4) { haltest_error("No report type specified\n"); return; } reportType = str2bthh_report_type_t(argv[3]); if (argc < 5) { haltest_error("No reportId specified\n"); return; } reportId = (uint8_t) atoi(argv[4]); if (argc < 6) { haltest_error("No bufferSize specified\n"); return; } bufferSize = atoi(argv[5]); EXEC(if_hh->get_report, &addr, reportType, reportId, bufferSize); } /* set_report */ static void set_report_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = connected_device_addr; *enum_func = enum_one_string; } else if (argc == 4) { *user = TYPE_ENUM(bthh_report_type_t); *enum_func = enum_defines; } } static void set_report_p(int argc, const char **argv) { bt_bdaddr_t addr; bthh_report_type_t reportType; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc <= 3) { haltest_error("No report type specified\n"); return; } reportType = str2bthh_report_type_t(argv[3]); if (argc <= 4) { haltest_error("No report specified\n"); return; } EXEC(if_hh->set_report, &addr, reportType, (char *) argv[4]); } /* send_data */ static void send_data_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { *user = connected_device_addr; *enum_func = enum_one_string; } } static void send_data_p(int argc, const char **argv) { bt_bdaddr_t addr; RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); if (argc <= 3) { haltest_error("No data to send specified\n"); return; } EXEC(if_hh->send_data, &addr, (char *) argv[3]); } /* cleanup */ static void cleanup_p(int argc, const char **argv) { RETURN_IF_NULL(if_hh); EXECV(if_hh->cleanup); } /* Methods available in bthh_interface_t */ static struct method methods[] = { STD_METHOD(init), STD_METHODCH(connect, ""), STD_METHODCH(disconnect, ""), STD_METHODCH(virtual_unplug, ""), STD_METHODCH(set_info, ""), STD_METHODCH(get_protocol, " "), STD_METHODCH(set_protocol, " "), STD_METHODCH(get_report, " "), STD_METHODCH(set_report, " "), STD_METHODCH(send_data, " "), STD_METHOD(cleanup), END_METHOD }; const struct interface hh_if = { .name = "hidhost", .methods = methods };