diff --git a/src/btstack.h b/src/btstack.h index b29a50e6e..5813a08aa 100644 --- a/src/btstack.h +++ b/src/btstack.h @@ -122,6 +122,7 @@ #include "classic/hfp_hf.h" #include "classic/hid.h" #include "classic/hid_device.h" +#include "classic/hid_host.h" #include "classic/hsp_ag.h" #include "classic/hsp_hs.h" #include "classic/pan.h" diff --git a/src/classic/hid_host.c b/src/classic/hid_host.c new file mode 100644 index 000000000..81f4574e6 --- /dev/null +++ b/src/classic/hid_host.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#define BTSTACK_FILE__ "hid_host.c" + +#include + +#include "bluetooth.h" +#include "bluetooth_psm.h" +#include "bluetooth_sdp.h" +#include "btstack_debug.h" +#include "btstack_event.h" +#include "btstack_hid_parser.h" +#include "classic/hid.h" +#include "classic/hid_host.h" +#include "classic/sdp_util.h" +#include "l2cap.h" + +typedef enum { + HID_HOST_IDLE, + HID_HOST_CONTROL_CONNECTION_ESTABLISHED, + HID_HOST_W4_SET_BOOT_MODE, + HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED, + HID_HOST_CONNECTION_ESTABLISHED, + HID_HOST_W2_SEND_GET_REPORT, + HID_HOST_W4_GET_REPORT_RESPONSE, + HID_HOST_W2_SEND_SET_REPORT, + HID_HOST_W4_SET_REPORT_RESPONSE, + HID_HOST_W2_SEND_GET_PROTOCOL, + HID_HOST_W4_GET_PROTOCOL_RESPONSE, + HID_HOST_W2_SEND_SET_PROTOCOL, + HID_HOST_W4_SET_PROTOCOL_RESPONSE, + HID_HOST_W2_SEND_REPORT, + HID_HOST_W4_SEND_REPORT_RESPONSE +} hid_host_state_t; + +typedef struct { + uint16_t cid; + bd_addr_t bd_addr; + hci_con_handle_t con_handle; + uint16_t control_cid; + uint16_t interrupt_cid; + + hid_host_state_t state; + hid_protocol_mode_t protocol_mode; + + uint8_t user_request_can_send_now; + + // get report + hid_report_type_t report_type; + uint8_t report_id; + + // set report + uint8_t * report; + uint16_t report_len; +} hid_host_connection_t; + +static const uint8_t * hid_host_descriptor_storage; +static uint16_t hid_host_descriptor_storage_len; + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + uint8_t event; + // bd_addr_t address; + // uint8_t status; + // uint16_t l2cap_cid; + // hid_host_t * hid_host; + // uint8_t param; + // hid_message_type_t message_type; + // hid_handshake_param_type_t message_status; + + switch (packet_type) { + case HCI_EVENT_PACKET: + event = hci_event_packet_get_type(packet); + switch (event) { + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ + printf("BTstack up and running. \n"); + } + break; + default: + break; + } + default: + break; + } +} + +void hid_host_init(const uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){ + hid_host_descriptor_storage = hid_descriptor_storage; + hid_host_descriptor_storage_len = hid_descriptor_storage_len; + + // register L2CAP Services for reconnections + l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 0xffff, gap_get_security_level()); + l2cap_register_service(packet_handler, PSM_HID_CONTROL, 0xffff, gap_get_security_level()); +} + +void hid_host_register_packet_handler(btstack_packet_handler_t callback){ + UNUSED(callback); +} + +uint8_t hid_host_connect(bd_addr_t addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid){ + UNUSED(hid_cid); + UNUSED(protocol_mode); + return ERROR_CODE_SUCCESS; +} + + +void hid_host_disconnect(uint16_t hid_cid){ + UNUSED(hid_cid); +} + +void hid_host_request_can_send_now_event(uint16_t hid_cid){ + UNUSED(hid_cid); +} + +void hid_host_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){ + UNUSED(hid_cid); + UNUSED(message); + UNUSED(message_len); +} + +void hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){ + UNUSED(hid_cid); + UNUSED(message); + UNUSED(message_len); +} \ No newline at end of file diff --git a/src/classic/hid_host.h b/src/classic/hid_host.h new file mode 100644 index 000000000..b3610b34a --- /dev/null +++ b/src/classic/hid_host.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#ifndef HID_HOST_H +#define HID_HOST_H + +#include +#include "btstack_defines.h" +#include "bluetooth.h" +#include "btstack_hid_parser.h" +#include "classic/hid.h" + +#if defined __cplusplus +extern "C" { +#endif + +/* API_START */ +/** + * @brief Set up HID Host + * @param boot_protocol_mode_supported + * @param hid_descriptor_storage + * @param hid_descriptor_storage_len + */ +void hid_host_init(const uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len); + +/** + * @brief Register callback for the HID Device Host. + * @param callback + */ +void hid_host_register_packet_handler(btstack_packet_handler_t callback); + +/* + * @brief Create HID connection to HID Host + * @param addr + * @param hid_cid to use for other commands + * @result status + */ +uint8_t hid_host_connect(bd_addr_t addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid); + +/* + * @brief Disconnect from HID Host + * @param hid_cid + */ +void hid_host_disconnect(uint16_t hid_cid); + +/** + * @brief Request can send now event to send HID Report + * Generates an HID_SUBEVENT_CAN_SEND_NOW subevent + * @param hid_cid + */ +void hid_host_request_can_send_now_event(uint16_t hid_cid); + +/** + * @brief Send HID message on interrupt channel + * @param hid_cid + */ +void hid_host_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len); + +/** + * @brief Send HID message on control channel + * @param hid_cid + */ +void hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len); + + + +/* API_END */ + + +#if defined __cplusplus +} +#endif + +#endif diff --git a/test/pts/Makefile b/test/pts/Makefile index 912d55747..566d0b7e9 100644 --- a/test/pts/Makefile +++ b/test/pts/Makefile @@ -168,7 +168,7 @@ csc_server_test: csc_server_test.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_S csc_client_test: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} ${SM_OBJ} csc_client_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -hid_host_test: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_hid_parser.o hid_host_test.o +hid_host_test: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_hid_parser.o hid_host.o hid_host_test.o ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ hid_device_test: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_hid_parser.o btstack_ring_buffer.o hid_device.o hid_device_test.o diff --git a/test/pts/hid_host_test.c b/test/pts/hid_host_test.c index a8cb184aa..2dc1000c5 100644 --- a/test/pts/hid_host_test.c +++ b/test/pts/hid_host_test.c @@ -193,7 +193,7 @@ static uint8_t hid_host_send_get_report(uint16_t hid_cid, hid_report_type_t rep if (!hid_host || !hid_host->control_cid){ return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; } - if (hid_host->state != HID_HOST_CONTROL_CONNECTION_ESTABLISHED){ + if (hid_host->state != HID_HOST_CONNECTION_ESTABLISHED){ return ERROR_CODE_COMMAND_DISALLOWED; } @@ -222,7 +222,9 @@ static uint8_t hid_host_send_set_report(uint16_t hid_cid, hid_report_type_t repo if (!hid_host || !hid_host->control_cid){ return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; } - if (hid_host->state != HID_HOST_CONTROL_CONNECTION_ESTABLISHED){ + + if (hid_host->state != HID_HOST_CONNECTION_ESTABLISHED){ + printf("hid_host_send_set_report: unexpected state 0%02x\n", HID_HOST_CONNECTION_ESTABLISHED); return ERROR_CODE_COMMAND_DISALLOWED; } @@ -251,7 +253,7 @@ static uint8_t hid_host_send_set_input_report(uint16_t hid_cid, uint8_t report_i static uint8_t hid_host_send_get_protocol(uint16_t hid_cid){ hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); if (!hid_host || !hid_host->control_cid) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; - if (hid_host->state != HID_HOST_CONTROL_CONNECTION_ESTABLISHED) return ERROR_CODE_COMMAND_DISALLOWED; + if (hid_host->state != HID_HOST_CONNECTION_ESTABLISHED) return ERROR_CODE_COMMAND_DISALLOWED; hid_host->state = HID_HOST_W2_SEND_GET_PROTOCOL; @@ -327,7 +329,7 @@ static hid_host_t * hid_host_get_instance_for_l2cap_cid(uint16_t cid){ return NULL; } -static void hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){ +static void _hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){ hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); if (!hid_host || !hid_host->control_cid) return; l2cap_send(hid_host->control_cid, (uint8_t*) message, message_len); @@ -335,19 +337,19 @@ static void hid_host_send_control_message(uint16_t hid_cid, const uint8_t * mess static uint8_t hid_host_send_suspend(uint16_t hcid){ uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_SUSPEND }; - hid_host_send_control_message(hcid, &report[0], sizeof(report)); + _hid_host_send_control_message(hcid, &report[0], sizeof(report)); return ERROR_CODE_SUCCESS; } static uint8_t hid_host_send_exit_suspend(uint16_t hcid){ uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_EXIT_SUSPEND }; - hid_host_send_control_message(hcid, &report[0], sizeof(report)); + _hid_host_send_control_message(hcid, &report[0], sizeof(report)); return ERROR_CODE_SUCCESS; } static uint8_t hid_host_send_virtual_cable_unplug(uint16_t hcid){ uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG }; - hid_host_send_control_message(hcid, &report[0], sizeof(report)); + _hid_host_send_control_message(hcid, &report[0], sizeof(report)); return ERROR_CODE_SUCCESS; } @@ -375,7 +377,7 @@ static void hid_host_disconnect_control_channel(uint16_t hid_cid){ } } -static void hid_host_disconnect(uint16_t hid_cid){ +static void _hid_host_disconnect(uint16_t hid_cid){ hid_host_t * hid_host = hid_host_get_instance_for_hid_cid(hid_cid); if (!hid_host){ log_error("hid_host_disconnect: could not find hid device instace"); @@ -402,9 +404,9 @@ static void hid_host_setup(void){ // Initialize L2CAP l2cap_init(); - // register L2CAP Services for reconnections - l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 0xffff, gap_get_security_level()); - l2cap_register_service(packet_handler, PSM_HID_CONTROL, 0xffff, gap_get_security_level()); + + // Initialize HID Host + hid_host_init(hid_descriptor, hid_descriptor_len); // Allow sniff mode requests by HID device and support role switch gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_SNIFF_MODE | LM_LINK_POLICY_ENABLE_ROLE_SWITCH); @@ -740,7 +742,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack hid_host->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED; break; } - _hid_host.state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED; + hid_host->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; } break; case PSM_HID_INTERRUPT: @@ -905,7 +907,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack printf("HID_MESSAGE_TYPE_DATA ???\n"); break; } - hid_host->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED; + hid_host->state = HID_HOST_CONNECTION_ESTABLISHED; break; case HID_MESSAGE_TYPE_HID_CONTROL: