bluez/android/hal-hidhost.c
Szymon Janc 47e30d43c5 android/hidhost: Move set_report parameter check to daemon
HAL library is to be as simple as possible and parameters values should
be verified by daemon for robustness anyway. Move this check to daemon.
2013-12-31 11:34:32 +02:00

392 lines
8.9 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 <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include "hal-log.h"
#include "hal.h"
#include "hal-msg.h"
#include "hal-ipc.h"
static const bthh_callbacks_t *cbacks;
static bool interface_ready(void)
{
return cbacks != NULL;
}
static void handle_conn_state(void *buf, uint16_t len)
{
struct hal_ev_hidhost_conn_state *ev = buf;
if (cbacks->connection_state_cb)
cbacks->connection_state_cb((bt_bdaddr_t *) ev->bdaddr,
ev->state);
}
static void handle_info(void *buf, uint16_t len)
{
struct hal_ev_hidhost_info *ev = buf;
bthh_hid_info_t info;
info.attr_mask = ev->attr;
info.sub_class = ev->subclass;
info.app_id = ev->app_id;
info.vendor_id = ev->vendor;
info.product_id = ev->product;
info.version = ev->version;
info.ctry_code = ev->country;
info.dl_len = ev->descr_len;
memcpy(info.dsc_list, ev->descr, info.dl_len);
if (cbacks->hid_info_cb)
cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info);
}
static void handle_proto_mode(void *buf, uint16_t len)
{
struct hal_ev_hidhost_proto_mode *ev = buf;
if (cbacks->protocol_mode_cb)
cbacks->protocol_mode_cb((bt_bdaddr_t *) ev->bdaddr,
ev->status, ev->mode);
}
static void handle_get_report(void *buf, uint16_t len)
{
struct hal_ev_hidhost_get_report *ev = buf;
if (len != sizeof(*ev) + ev->len) {
error("invalid get report event, aborting");
exit(EXIT_FAILURE);
}
if (cbacks->get_report_cb)
cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
ev->data, ev->len);
}
static void handle_virtual_unplug(void *buf, uint16_t len)
{
struct hal_ev_hidhost_virtual_unplug *ev = buf;
if (cbacks->virtual_unplug_cb)
cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr,
ev->status);
}
/* handlers will be called from notification thread context,
* index in table equals to 'opcode - HAL_MINIMUM_EVENT' */
static const struct hal_ipc_handler ev_handlers[] = {
{ /* HAL_EV_HIDHOST_CONN_STATE */
.handler = handle_conn_state,
.var_len = false,
.data_len = sizeof(struct hal_ev_hidhost_conn_state)
},
{ /* HAL_EV_HIDHOST_INFO */
.handler = handle_info,
.var_len = false,
.data_len = sizeof(struct hal_ev_hidhost_info),
},
{ /* HAL_EV_HIDHOST_PROTO_MODE */
.handler = handle_proto_mode,
.var_len = false,
.data_len = sizeof(struct hal_ev_hidhost_proto_mode),
},
{ /* HAL_EV_HIDHOST_GET_REPORT */
.handler = handle_get_report,
.var_len = true,
.data_len = sizeof(struct hal_ev_hidhost_get_report),
},
{ /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
.handler = handle_virtual_unplug,
.var_len = false,
.data_len = sizeof(struct hal_ev_hidhost_virtual_unplug),
},
};
static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr)
{
struct hal_cmd_hidhost_connect cmd;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr)
return BT_STATUS_PARM_INVALID;
memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
sizeof(cmd), &cmd, 0, NULL, NULL);
}
static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
{
struct hal_cmd_hidhost_disconnect cmd;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr)
return BT_STATUS_PARM_INVALID;
memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
sizeof(cmd), &cmd, 0, NULL, NULL);
}
static bt_status_t virtual_unplug(bt_bdaddr_t *bd_addr)
{
struct hal_cmd_hidhost_virtual_unplug cmd;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr)
return BT_STATUS_PARM_INVALID;
memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
sizeof(cmd), &cmd, 0, NULL, NULL);
}
static bt_status_t set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
{
struct hal_cmd_hidhost_set_info cmd;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr)
return BT_STATUS_PARM_INVALID;
memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
cmd.attr = hid_info.attr_mask;
cmd.subclass = hid_info.sub_class;
cmd.app_id = hid_info.app_id;
cmd.vendor = hid_info.vendor_id;
cmd.product = hid_info.product_id;
cmd.country = hid_info.ctry_code;
cmd.descr_len = hid_info.dl_len;
memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len);
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
sizeof(cmd), &cmd, 0, NULL, NULL);
}
static bt_status_t get_protocol(bt_bdaddr_t *bd_addr,
bthh_protocol_mode_t protocol_mode)
{
struct hal_cmd_hidhost_get_protocol cmd;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr)
return BT_STATUS_PARM_INVALID;
memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
/* type match IPC type */
cmd.mode = protocol_mode;
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
HAL_OP_HIDHOST_GET_PROTOCOL,
sizeof(cmd), &cmd, 0, NULL, NULL);
}
static bt_status_t set_protocol(bt_bdaddr_t *bd_addr,
bthh_protocol_mode_t protocol_mode)
{
struct hal_cmd_hidhost_set_protocol cmd;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr)
return BT_STATUS_PARM_INVALID;
memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
/* type match IPC type */
cmd.mode = protocol_mode;
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
HAL_OP_HIDHOST_SET_PROTOCOL,
sizeof(cmd), &cmd, 0, NULL, NULL);
}
static bt_status_t get_report(bt_bdaddr_t *bd_addr,
bthh_report_type_t report_type,
uint8_t report_id,
int buffer_size)
{
struct hal_cmd_hidhost_get_report cmd;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr)
return BT_STATUS_PARM_INVALID;
memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
cmd.id = report_id;
cmd.buf_size = buffer_size;
/* type match IPC type */
cmd.type = report_type;
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
sizeof(cmd), &cmd, 0, NULL, NULL);
}
static bt_status_t set_report(bt_bdaddr_t *bd_addr,
bthh_report_type_t report_type,
char *report)
{
uint8_t buf[BLUEZ_HAL_MTU];
struct hal_cmd_hidhost_set_report *cmd = (void *) buf;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr || !report)
return BT_STATUS_PARM_INVALID;
memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
cmd->len = strlen(report);
memcpy(cmd->data, report, cmd->len);
/* type match IPC type */
cmd->type = report_type;
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
sizeof(*cmd) + cmd->len, buf, 0, NULL, NULL);
}
static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data)
{
uint8_t buf[BLUEZ_HAL_MTU];
struct hal_cmd_hidhost_send_data *cmd = (void *) buf;
DBG("");
if (!interface_ready())
return BT_STATUS_NOT_READY;
if (!bd_addr || !data)
return BT_STATUS_PARM_INVALID;
memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
cmd->len = strlen(data);
memcpy(cmd->data, data, cmd->len);
return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
sizeof(*cmd) + cmd->len, buf, 0, NULL, NULL);
}
static bt_status_t init(bthh_callbacks_t *callbacks)
{
struct hal_cmd_register_module cmd;
int ret;
DBG("");
if (interface_ready())
return BT_STATUS_DONE;
/* store reference to user callbacks */
cbacks = callbacks;
hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers,
sizeof(ev_handlers)/sizeof(ev_handlers[0]));
cmd.service_id = HAL_SERVICE_ID_HIDHOST;
ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
sizeof(cmd), &cmd, 0, NULL, NULL);
if (ret != BT_STATUS_SUCCESS) {
cbacks = NULL;
hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
}
return ret;
}
static void cleanup(void)
{
struct hal_cmd_unregister_module cmd;
DBG("");
if (!interface_ready())
return;
cbacks = NULL;
cmd.service_id = HAL_SERVICE_ID_HIDHOST;
hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
sizeof(cmd), &cmd, 0, NULL, NULL);
hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
}
static bthh_interface_t hidhost_if = {
.size = sizeof(hidhost_if),
.init = init,
.connect = hidhost_connect,
.disconnect = disconnect,
.virtual_unplug = virtual_unplug,
.set_info = set_info,
.get_protocol = get_protocol,
.set_protocol = set_protocol,
.get_report = get_report,
.set_report = set_report,
.send_data = send_data,
.cleanup = cleanup
};
bthh_interface_t *bt_get_hidhost_interface(void)
{
return &hidhost_if;
}