439 lines
10 KiB
C
439 lines
10 KiB
C
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include <hardware/bluetooth.h>
|
|
#include <hardware/bt_hh.h>
|
|
|
|
#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 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
|
|
};
|
|
|
|
/* 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, "<addr>"),
|
|
STD_METHODCH(disconnect, "<addr>"),
|
|
STD_METHODCH(virtual_unplug, "<addr>"),
|
|
STD_METHODCH(set_info, "<addr>"),
|
|
STD_METHODCH(get_protocol, "<addr> <mode>"),
|
|
STD_METHODCH(set_protocol, "<addr> <mode>"),
|
|
STD_METHODCH(get_report, "<addr> <type> <report_id> <size>"),
|
|
STD_METHODCH(set_report, "<addr> <type> <hex_encoded_report>"),
|
|
STD_METHODCH(send_data, "<addr> <hex_encoded_data>"),
|
|
STD_METHOD(cleanup),
|
|
END_METHOD
|
|
};
|
|
|
|
const struct interface hh_if = {
|
|
.name = "hidhost",
|
|
.methods = methods
|
|
};
|