/* * 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 #include #include #include #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_idle_time(void *buf, uint16_t len) { struct hal_ev_hidhost_idle_time *ev = buf; if (cbacks->idle_time_cb) cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status, ev->idle_rate); } 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_IDLE_TIME */ .handler = handle_idle_time, .var_len = false, .data_len = sizeof(struct hal_ev_hidhost_idle_time), }, { /* 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; }