blob: 25519e5d769c3e293e5ebacf4a0226d0df23212f [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.
*
*/
#include <stdio.h>
#include <ctype.h>
#include <string.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 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, "<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
};