xref: /btstack/src/classic/hid_device.c (revision 6c8af51cd8ad0a0d53fc3934daf633474c3bec06)
1d40c9ac6SMatthias Ringwald /*
2d40c9ac6SMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
3d40c9ac6SMatthias Ringwald  *
4d40c9ac6SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
5d40c9ac6SMatthias Ringwald  * modification, are permitted provided that the following conditions
6d40c9ac6SMatthias Ringwald  * are met:
7d40c9ac6SMatthias Ringwald  *
8d40c9ac6SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
9d40c9ac6SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
10d40c9ac6SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11d40c9ac6SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
12d40c9ac6SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
13d40c9ac6SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
14d40c9ac6SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
15d40c9ac6SMatthias Ringwald  *    from this software without specific prior written permission.
16d40c9ac6SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
17d40c9ac6SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
18d40c9ac6SMatthias Ringwald  *    monetary gain.
19d40c9ac6SMatthias Ringwald  *
20d40c9ac6SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21d40c9ac6SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22d40c9ac6SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25d40c9ac6SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26d40c9ac6SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27d40c9ac6SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28d40c9ac6SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29d40c9ac6SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30d40c9ac6SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31d40c9ac6SMatthias Ringwald  * SUCH DAMAGE.
32d40c9ac6SMatthias Ringwald  *
33d40c9ac6SMatthias Ringwald  * Please inquire about commercial licensing options at
34d40c9ac6SMatthias Ringwald  * [email protected]
35d40c9ac6SMatthias Ringwald  *
36d40c9ac6SMatthias Ringwald  */
37d40c9ac6SMatthias Ringwald 
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "hid_device.c"
39d40c9ac6SMatthias Ringwald 
40d40c9ac6SMatthias Ringwald #include <string.h>
41d40c9ac6SMatthias Ringwald 
4284e3541eSMilanka Ringwald #include "bluetooth.h"
4384e3541eSMilanka Ringwald #include "bluetooth_psm.h"
4484e3541eSMilanka Ringwald #include "bluetooth_sdp.h"
4584e3541eSMilanka Ringwald #include "btstack_debug.h"
4684e3541eSMilanka Ringwald #include "btstack_event.h"
4784e3541eSMilanka Ringwald #include "btstack_hid_parser.h"
48d40c9ac6SMatthias Ringwald #include "classic/hid_device.h"
49d40c9ac6SMatthias Ringwald #include "classic/sdp_util.h"
508eb8d463SMatthias Ringwald #include "l2cap.h"
518eb8d463SMatthias Ringwald 
52de81b46aSMatthias Ringwald // prototypes
53de81b46aSMatthias Ringwald static int dummy_write_report(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int * out_report_size, uint8_t * out_report);
54de81b46aSMatthias Ringwald static void dummy_set_report(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report);
55de81b46aSMatthias Ringwald static void dummy_report_data(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t * report);
56de81b46aSMatthias Ringwald 
57c12110e4SMilanka Ringwald typedef enum {
58c12110e4SMilanka Ringwald     HID_DEVICE_IDLE,
59c12110e4SMilanka Ringwald     HID_DEVICE_CONNECTED,
60ba9a58feSMilanka Ringwald     HID_DEVICE_W2_GET_REPORT,
61ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SET_REPORT,
62ba9a58feSMilanka Ringwald     HID_DEVICE_W2_GET_PROTOCOL,
63ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SET_PROTOCOL,
64ba9a58feSMilanka Ringwald     HID_DEVICE_W2_ANSWER_SET_PROTOCOL,
65ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST,
66c12110e4SMilanka Ringwald } hid_device_state_t;
67c12110e4SMilanka Ringwald 
688eb8d463SMatthias Ringwald // hid device state
698eb8d463SMatthias Ringwald typedef struct hid_device {
708eb8d463SMatthias Ringwald     uint16_t  cid;
718eb8d463SMatthias Ringwald     bd_addr_t bd_addr;
728eb8d463SMatthias Ringwald     hci_con_handle_t con_handle;
738eb8d463SMatthias Ringwald     uint16_t  control_cid;
748eb8d463SMatthias Ringwald     uint16_t  interrupt_cid;
758eb8d463SMatthias Ringwald     uint8_t   incoming;
7657c643eeSMatthias Ringwald     uint8_t   connected;
77c12110e4SMilanka Ringwald     hid_device_state_t state;
78c12110e4SMilanka Ringwald     hid_report_type_t report_type;
79b56e8b56SMatthias Ringwald     uint8_t   report_id;
80a1118d11SMilanka Ringwald     uint16_t  expected_report_size;
81a1118d11SMilanka Ringwald     uint16_t  report_size;
82e99e41f0SMatthias Ringwald     uint8_t   user_request_can_send_now;
83ba9a58feSMilanka Ringwald 
84bc967e00SMilanka Ringwald     hid_handshake_param_type_t report_status;
85ba9a58feSMilanka Ringwald     hid_protocol_mode_t protocol_mode;
868eb8d463SMatthias Ringwald } hid_device_t;
878eb8d463SMatthias Ringwald 
88de81b46aSMatthias Ringwald // higher layer callbacks
89de81b46aSMatthias Ringwald static btstack_packet_handler_t hid_device_callback;
90de81b46aSMatthias Ringwald static int  (*hci_device_get_report)   (uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int * out_report_size, uint8_t * out_report) = dummy_write_report;
91de81b46aSMatthias Ringwald static void (*hci_device_set_report)   (uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report) = dummy_set_report;
92de81b46aSMatthias Ringwald static void (*hci_device_report_data)  (uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t * report) = dummy_report_data;
93de81b46aSMatthias Ringwald 
94de81b46aSMatthias Ringwald static hid_device_t    hid_device_singleton;
95de81b46aSMatthias Ringwald 
96de81b46aSMatthias Ringwald static bool            hid_device_boot_protocol_mode_supported;
97de81b46aSMatthias Ringwald static const uint8_t * hid_device_descriptor;
98de81b46aSMatthias Ringwald static uint16_t        hid_device_descriptor_len;
99de81b46aSMatthias Ringwald 
100de81b46aSMatthias Ringwald 
101de81b46aSMatthias Ringwald static uint16_t hid_device_cid = 0;
102de81b46aSMatthias Ringwald 
103c12110e4SMilanka Ringwald 
104a1118d11SMilanka Ringwald static int dummy_write_report(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int * out_report_size, uint8_t * out_report){
105c12110e4SMilanka Ringwald     UNUSED(hid_cid);
106c12110e4SMilanka Ringwald     UNUSED(report_type);
107c12110e4SMilanka Ringwald     UNUSED(report_id);
108c12110e4SMilanka Ringwald     UNUSED(out_report_size);
109c12110e4SMilanka Ringwald     UNUSED(out_report);
110a1118d11SMilanka Ringwald     return -1;
111c12110e4SMilanka Ringwald }
1124dfe6a8bSMilanka Ringwald 
1134dfe6a8bSMilanka Ringwald static void dummy_set_report(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report){
114ba9a58feSMilanka Ringwald     UNUSED(hid_cid);
115ba9a58feSMilanka Ringwald     UNUSED(report_type);
116ba9a58feSMilanka Ringwald     UNUSED(report_size);
117ba9a58feSMilanka Ringwald     UNUSED(report);
118ba9a58feSMilanka Ringwald }
1194dfe6a8bSMilanka Ringwald 
1207d26fe66SMilanka Ringwald static void dummy_report_data(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t * report){
1217d26fe66SMilanka Ringwald     UNUSED(hid_cid);
1227d26fe66SMilanka Ringwald     UNUSED(report_type);
1237d26fe66SMilanka Ringwald     UNUSED(report_id);
1247d26fe66SMilanka Ringwald     UNUSED(report_size);
1257d26fe66SMilanka Ringwald     UNUSED(report);
1267d26fe66SMilanka Ringwald }
127c12110e4SMilanka Ringwald static uint16_t hid_device_get_next_cid(void){
128c12110e4SMilanka Ringwald     hid_device_cid++;
129c12110e4SMilanka Ringwald     if (!hid_device_cid){
130c12110e4SMilanka Ringwald         hid_device_cid = 1;
131c12110e4SMilanka Ringwald     }
132c12110e4SMilanka Ringwald     return hid_device_cid;
133c12110e4SMilanka Ringwald }
134c12110e4SMilanka Ringwald 
135c12110e4SMilanka Ringwald // TODO: store hid device connection into list
1364176cbc0SMatthias Ringwald static hid_device_t * hid_device_get_instance_for_l2cap_cid(uint16_t cid){
137de81b46aSMatthias Ringwald     if ((hid_device_singleton.control_cid == cid) || (hid_device_singleton.interrupt_cid == cid)){
138de81b46aSMatthias Ringwald         return &hid_device_singleton;
1394176cbc0SMatthias Ringwald     }
1404176cbc0SMatthias Ringwald     return NULL;
1414176cbc0SMatthias Ringwald }
1424176cbc0SMatthias Ringwald 
1434176cbc0SMatthias Ringwald static hid_device_t * hid_device_get_instance_for_hid_cid(uint16_t hid_cid){
144de81b46aSMatthias Ringwald     if (hid_device_singleton.cid == hid_cid){
145de81b46aSMatthias Ringwald         return &hid_device_singleton;
146c12110e4SMilanka Ringwald     }
147c12110e4SMilanka Ringwald     return NULL;
148c12110e4SMilanka Ringwald }
149c12110e4SMilanka Ringwald 
150*6c8af51cSMatthias Ringwald static void hid_device_setup_instance(hid_device_t *hid_device, const uint8_t *bd_addr) {
151*6c8af51cSMatthias Ringwald     (void)memcpy(hid_device->bd_addr, bd_addr, 6);
152*6c8af51cSMatthias Ringwald     hid_device->cid = hid_device_get_next_cid();
153*6c8af51cSMatthias Ringwald     // reset state
154*6c8af51cSMatthias Ringwald     hid_device->protocol_mode = HID_PROTOCOL_MODE_REPORT;
155*6c8af51cSMatthias Ringwald     hid_device->con_handle    = HCI_CON_HANDLE_INVALID;
156*6c8af51cSMatthias Ringwald     hid_device->incoming      = 0;
157*6c8af51cSMatthias Ringwald     hid_device->connected     = 0;
158*6c8af51cSMatthias Ringwald     hid_device->control_cid   = 0;
159*6c8af51cSMatthias Ringwald     hid_device->interrupt_cid = 0;
160*6c8af51cSMatthias Ringwald }
161*6c8af51cSMatthias Ringwald 
16272e3f392SMatthias Ringwald static hid_device_t * hid_device_provide_instance_for_bd_addr(bd_addr_t bd_addr){
163de81b46aSMatthias Ringwald     if (!hid_device_singleton.cid){
164*6c8af51cSMatthias Ringwald         hid_device_setup_instance(&hid_device_singleton, bd_addr);
1656510739bSMilanka Ringwald     }
166de81b46aSMatthias Ringwald     return &hid_device_singleton;
167c12110e4SMilanka Ringwald }
168c12110e4SMilanka Ringwald 
169c12110e4SMilanka Ringwald static hid_device_t * hid_device_create_instance(void){
170ba9a58feSMilanka Ringwald 
171de81b46aSMatthias Ringwald     return &hid_device_singleton;
172c12110e4SMilanka Ringwald }
173c12110e4SMilanka Ringwald 
17480d9d5d4SMilanka Ringwald void hid_create_sdp_record(uint8_t *service, uint32_t service_record_handle, const hid_sdp_record_t * params){
175d40c9ac6SMatthias Ringwald     uint8_t * attribute;
176d40c9ac6SMatthias Ringwald     de_create_sequence(service);
177d40c9ac6SMatthias Ringwald 
178d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
179d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
180d40c9ac6SMatthias Ringwald 
181d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
182d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
183d40c9ac6SMatthias Ringwald     {
184d40c9ac6SMatthias Ringwald         de_add_number(attribute,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
185d40c9ac6SMatthias Ringwald     }
186d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
187d40c9ac6SMatthias Ringwald 
188d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
189d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
190d40c9ac6SMatthias Ringwald     {
191d40c9ac6SMatthias Ringwald         uint8_t * l2cpProtocol = de_push_sequence(attribute);
192d40c9ac6SMatthias Ringwald         {
193d40c9ac6SMatthias Ringwald             de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
19484e3541eSMilanka Ringwald             de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_HID_CONTROL);
195d40c9ac6SMatthias Ringwald         }
196d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, l2cpProtocol);
197d40c9ac6SMatthias Ringwald 
198d40c9ac6SMatthias Ringwald         uint8_t * hidProtocol = de_push_sequence(attribute);
199d40c9ac6SMatthias Ringwald         {
200d40c9ac6SMatthias Ringwald             de_add_number(hidProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
201d40c9ac6SMatthias Ringwald         }
202d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidProtocol);
203d40c9ac6SMatthias Ringwald     }
204d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
205d40c9ac6SMatthias Ringwald 
206d40c9ac6SMatthias Ringwald     // TODO?
207d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
208d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
209d40c9ac6SMatthias Ringwald     {
210d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e);
211d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a);
212d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100);
213d40c9ac6SMatthias Ringwald     }
214d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
215d40c9ac6SMatthias Ringwald 
216d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS);
217d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
218d40c9ac6SMatthias Ringwald     {
219d40c9ac6SMatthias Ringwald         uint8_t * additionalDescriptorAttribute = de_push_sequence(attribute);
220d40c9ac6SMatthias Ringwald         {
221d40c9ac6SMatthias Ringwald             uint8_t * l2cpProtocol = de_push_sequence(additionalDescriptorAttribute);
222d40c9ac6SMatthias Ringwald             {
223d40c9ac6SMatthias Ringwald                 de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
22484e3541eSMilanka Ringwald                 de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_HID_INTERRUPT);
225d40c9ac6SMatthias Ringwald             }
226d40c9ac6SMatthias Ringwald             de_pop_sequence(additionalDescriptorAttribute, l2cpProtocol);
227d40c9ac6SMatthias Ringwald 
2289fa0921fSMatthias Ringwald             uint8_t * hidProtocol = de_push_sequence(additionalDescriptorAttribute);
229d40c9ac6SMatthias Ringwald             {
230d40c9ac6SMatthias Ringwald                 de_add_number(hidProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
231d40c9ac6SMatthias Ringwald             }
2329fa0921fSMatthias Ringwald             de_pop_sequence(additionalDescriptorAttribute, hidProtocol);
233d40c9ac6SMatthias Ringwald         }
234d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, additionalDescriptorAttribute);
235d40c9ac6SMatthias Ringwald     }
236d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
237d40c9ac6SMatthias Ringwald 
238d40c9ac6SMatthias Ringwald     // 0x0100 "ServiceName"
239d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0100);
240b56e8b56SMatthias Ringwald     de_add_data(service,  DE_STRING, (uint16_t) strlen(params->device_name), (uint8_t *) params->device_name);
241d40c9ac6SMatthias Ringwald 
242d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
243d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
244d40c9ac6SMatthias Ringwald     {
245d40c9ac6SMatthias Ringwald         uint8_t * hidProfile = de_push_sequence(attribute);
246d40c9ac6SMatthias Ringwald         {
247d40c9ac6SMatthias Ringwald             de_add_number(hidProfile,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
248d40c9ac6SMatthias Ringwald             de_add_number(hidProfile,  DE_UINT, DE_SIZE_16, 0x0101);    // Version 1.1
249d40c9ac6SMatthias Ringwald         }
250d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidProfile);
251d40c9ac6SMatthias Ringwald     }
252d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
253d40c9ac6SMatthias Ringwald 
254d40c9ac6SMatthias Ringwald     // Deprecated in v1.1.1
255d40c9ac6SMatthias Ringwald     // de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_RELEASE_NUMBER);
256d40c9ac6SMatthias Ringwald     // de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0101);
257d40c9ac6SMatthias Ringwald 
258d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_PARSER_VERSION);
259d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0111);  // v1.1.1
260d40c9ac6SMatthias Ringwald 
261d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_SUBCLASS);
26280d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_8,  params->hid_device_subclass);
263d40c9ac6SMatthias Ringwald 
264d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_COUNTRY_CODE);
26580d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_8,  params->hid_country_code);
266d40c9ac6SMatthias Ringwald 
267d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_VIRTUAL_CABLE);
26880d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_virtual_cable);
269d40c9ac6SMatthias Ringwald 
270d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_RECONNECT_INITIATE);
27180d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_reconnect_initiate);
272ffe3f1a1SMilanka Ringwald 
273d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST);
274d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
275d40c9ac6SMatthias Ringwald     {
276d40c9ac6SMatthias Ringwald         uint8_t* hidDescriptor = de_push_sequence(attribute);
277d40c9ac6SMatthias Ringwald         {
278d40c9ac6SMatthias Ringwald             de_add_number(hidDescriptor,  DE_UINT, DE_SIZE_8, 0x22);    // Report Descriptor
27980d9d5d4SMilanka Ringwald             de_add_data(hidDescriptor,  DE_STRING, params->hid_descriptor_size, (uint8_t *) params->hid_descriptor);
280d40c9ac6SMatthias Ringwald         }
281d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidDescriptor);
282d40c9ac6SMatthias Ringwald     }
283d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
284d40c9ac6SMatthias Ringwald 
2859679ea81SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDLANGID_BASE_LIST);
2869679ea81SMatthias Ringwald     attribute = de_push_sequence(service);
2879679ea81SMatthias Ringwald     {
2889679ea81SMatthias Ringwald         uint8_t* hig_lang_base = de_push_sequence(attribute);
2899679ea81SMatthias Ringwald         {
2909679ea81SMatthias Ringwald             // see: http://www.usb.org/developers/docs/USB_LANGIDs.pdf
2919679ea81SMatthias Ringwald             de_add_number(hig_lang_base,  DE_UINT, DE_SIZE_16, 0x0409);    // HIDLANGID = English (US)
2929679ea81SMatthias Ringwald             de_add_number(hig_lang_base,  DE_UINT, DE_SIZE_16, 0x0100);    // HIDLanguageBase = 0x0100 default
2939679ea81SMatthias Ringwald         }
2949679ea81SMatthias Ringwald         de_pop_sequence(attribute, hig_lang_base);
2959679ea81SMatthias Ringwald     }
2969679ea81SMatthias Ringwald     de_pop_sequence(service, attribute);
2979679ea81SMatthias Ringwald 
29880d9d5d4SMilanka Ringwald     // battery power
29980d9d5d4SMilanka Ringwald 
3006510739bSMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_REMOTE_WAKE);
30180d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_remote_wake ? 1 : 0);
30280d9d5d4SMilanka Ringwald 
30380d9d5d4SMilanka Ringwald     // supervision timeout
30480d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_SUPERVISION_TIMEOUT);
30580d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, params->hid_supervision_timeout);
30680d9d5d4SMilanka Ringwald 
30780d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_NORMALLY_CONNECTABLE);
30880d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_normally_connectable);
3096510739bSMilanka Ringwald 
310d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_BOOT_DEVICE);
31180d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_boot_device ? 1 : 0);
31280d9d5d4SMilanka Ringwald 
31380d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MAX_LATENCY);
31480d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, params->hid_ssr_host_max_latency);
31580d9d5d4SMilanka Ringwald 
31680d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MIN_TIMEOUT);
31780d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, params->hid_ssr_host_min_timeout);
318d40c9ac6SMatthias Ringwald }
3198eb8d463SMatthias Ringwald 
3208eb8d463SMatthias Ringwald static inline void hid_device_emit_connected_event(hid_device_t * context, uint8_t status){
3218eb8d463SMatthias Ringwald     uint8_t event[15];
3228eb8d463SMatthias Ringwald     int pos = 0;
3238eb8d463SMatthias Ringwald     event[pos++] = HCI_EVENT_HID_META;
3248eb8d463SMatthias Ringwald     pos++;  // skip len
3258eb8d463SMatthias Ringwald     event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
3268eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->cid);
3278eb8d463SMatthias Ringwald     pos+=2;
3288eb8d463SMatthias Ringwald     event[pos++] = status;
3296510739bSMilanka Ringwald     reverse_bd_addr(context->bd_addr, &event[pos]);
3308eb8d463SMatthias Ringwald     pos += 6;
3318eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->con_handle);
3328eb8d463SMatthias Ringwald     pos += 2;
3338eb8d463SMatthias Ringwald     event[pos++] = context->incoming;
3348eb8d463SMatthias Ringwald     event[1] = pos - 2;
335de81b46aSMatthias Ringwald     hid_device_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3368eb8d463SMatthias Ringwald }
3378eb8d463SMatthias Ringwald 
3386510739bSMilanka Ringwald static inline void hid_device_emit_event(hid_device_t * context, uint8_t subevent_type){
339517089d1SMilanka Ringwald     uint8_t event[5];
3406510739bSMilanka Ringwald     int pos = 0;
3416510739bSMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
3426510739bSMilanka Ringwald     pos++;  // skip len
3436510739bSMilanka Ringwald     event[pos++] = subevent_type;
3446510739bSMilanka Ringwald     little_endian_store_16(event,pos,context->cid);
3456510739bSMilanka Ringwald     pos+=2;
3466510739bSMilanka Ringwald     event[1] = pos - 2;
347de81b46aSMatthias Ringwald     hid_device_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3486510739bSMilanka Ringwald }
3496510739bSMilanka Ringwald 
350c8cb324eSMilanka Ringwald static int hid_report_size_valid(uint16_t cid, int report_id, hid_report_type_t report_type, int report_size){
351c8cb324eSMilanka Ringwald     if (!report_size) return 0;
352c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
353c8cb324eSMilanka Ringwald         switch (report_id){
354c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
355c8cb324eSMilanka Ringwald                 if (report_size < 8) return 0;
356c8cb324eSMilanka Ringwald                 break;
357c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
358c8cb324eSMilanka Ringwald                 if (report_size < 1) return 0;
359c8cb324eSMilanka Ringwald                 break;
360c8cb324eSMilanka Ringwald             default:
361c8cb324eSMilanka Ringwald                 return 0;
362c8cb324eSMilanka Ringwald         }
363c8cb324eSMilanka Ringwald     } else {
364de81b46aSMatthias Ringwald         int size =  btstack_hid_get_report_size_for_id(report_id, report_type, hid_device_descriptor_len, hid_device_descriptor);
365c1ab6cc1SMatthias Ringwald         if ((size == 0) || (size != report_size)) return 0;
366c8cb324eSMilanka Ringwald     }
367c8cb324eSMilanka Ringwald     return 1;
368c8cb324eSMilanka Ringwald }
369c8cb324eSMilanka Ringwald 
370a796c06fSMilanka Ringwald static int hid_get_report_size_for_id(uint16_t cid, int report_id, hid_report_type_t report_type, uint16_t descriptor_len, const uint8_t * descriptor){
371c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
372c8cb324eSMilanka Ringwald         switch (report_id){
373c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
374c8cb324eSMilanka Ringwald                 return 8;
375c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
376c8cb324eSMilanka Ringwald                 return 3;
377c8cb324eSMilanka Ringwald             default:
378c8cb324eSMilanka Ringwald                 return 0;
379c8cb324eSMilanka Ringwald         }
380c8cb324eSMilanka Ringwald     } else {
381a796c06fSMilanka Ringwald         return btstack_hid_get_report_size_for_id(report_id, report_type, descriptor_len, descriptor);
382c8cb324eSMilanka Ringwald     }
383c8cb324eSMilanka Ringwald }
384c8cb324eSMilanka Ringwald 
385c8cb324eSMilanka Ringwald static hid_report_id_status_t hid_report_id_status(uint16_t cid, uint16_t report_id){
386c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
387c8cb324eSMilanka Ringwald         switch (report_id){
388c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
389c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
390c8cb324eSMilanka Ringwald                 return HID_REPORT_ID_VALID;
391c8cb324eSMilanka Ringwald             default:
392c8cb324eSMilanka Ringwald                 return HID_REPORT_ID_INVALID;
393c8cb324eSMilanka Ringwald         }
394c8cb324eSMilanka Ringwald     } else {
395de81b46aSMatthias Ringwald         return btstack_hid_id_valid(report_id, hid_device_descriptor_len, hid_device_descriptor);
396c8cb324eSMilanka Ringwald     }
397c8cb324eSMilanka Ringwald }
398c8cb324eSMilanka Ringwald 
3994dfe6a8bSMilanka Ringwald static hid_handshake_param_type_t hid_device_set_report_cmd_is_valid(uint16_t cid, hid_report_type_t report_type, int report_size, uint8_t * report){
4004dfe6a8bSMilanka Ringwald     int pos = 0;
401c8cb324eSMilanka Ringwald     int report_id = 0;
402c8cb324eSMilanka Ringwald 
403de81b46aSMatthias Ringwald     if (btstack_hid_report_id_declared(hid_device_descriptor_len, hid_device_descriptor)){
404c8cb324eSMilanka Ringwald         report_id = report[pos++];
4054dfe6a8bSMilanka Ringwald         hid_report_id_status_t report_id_status = hid_report_id_status(cid, report_id);
4064dfe6a8bSMilanka Ringwald         switch (report_id_status){
4074dfe6a8bSMilanka Ringwald             case HID_REPORT_ID_INVALID:
4084dfe6a8bSMilanka Ringwald                 return HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
4094dfe6a8bSMilanka Ringwald             default:
4104dfe6a8bSMilanka Ringwald                 break;
4114dfe6a8bSMilanka Ringwald         }
412c8cb324eSMilanka Ringwald     }
4134dfe6a8bSMilanka Ringwald 
4144dfe6a8bSMilanka Ringwald     if (!hid_report_size_valid(cid, report_id, report_type, report_size-pos)){
415c8cb324eSMilanka Ringwald         // TODO clarify DCT/BI-03c
4164dfe6a8bSMilanka Ringwald         return HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4174dfe6a8bSMilanka Ringwald     }
4184dfe6a8bSMilanka Ringwald     return HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
4194dfe6a8bSMilanka Ringwald }
4204dfe6a8bSMilanka Ringwald 
4218eb8d463SMatthias Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
4228eb8d463SMatthias Ringwald     UNUSED(channel);
4238eb8d463SMatthias Ringwald     UNUSED(packet_size);
4248eb8d463SMatthias Ringwald     int connected_before;
42557c643eeSMatthias Ringwald     uint16_t psm;
42657c643eeSMatthias Ringwald     uint8_t status;
427c12110e4SMilanka Ringwald     hid_device_t * device = NULL;
4286510739bSMilanka Ringwald     uint8_t param;
4296510739bSMilanka Ringwald     bd_addr_t address;
4307d26fe66SMilanka Ringwald     uint16_t local_cid;
431a1118d11SMilanka Ringwald     int pos = 0;
432ba9a58feSMilanka Ringwald     int report_size;
433a1118d11SMilanka Ringwald     uint8_t report[48];
434ca3f868cSMilanka Ringwald     hid_message_type_t message_type;
435c12110e4SMilanka Ringwald 
4368eb8d463SMatthias Ringwald     switch (packet_type){
437c12110e4SMilanka Ringwald         case L2CAP_DATA_PACKET:
4384176cbc0SMatthias Ringwald             device = hid_device_get_instance_for_l2cap_cid(channel);
439c12110e4SMilanka Ringwald             if (!device) {
440c12110e4SMilanka Ringwald                 log_error("no device with cid 0x%02x", channel);
441c12110e4SMilanka Ringwald                 return;
442c12110e4SMilanka Ringwald             }
443ca3f868cSMilanka Ringwald             message_type = (hid_message_type_t)(packet[0] >> 4);
444c12110e4SMilanka Ringwald             switch (message_type){
445c12110e4SMilanka Ringwald                 case HID_MESSAGE_TYPE_GET_REPORT:
44607678896SMilanka Ringwald 
447a1118d11SMilanka Ringwald                     pos = 0;
448ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[pos++] & 0x03);
449c12110e4SMilanka Ringwald                     device->report_id = 0;
4507d26fe66SMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
451a1118d11SMilanka Ringwald                     device->state = HID_DEVICE_W2_GET_REPORT;
4527d26fe66SMilanka Ringwald 
4537d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
4547d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
455c8cb324eSMilanka Ringwald                             if (packet_size < 2){
456ba9a58feSMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
457ba9a58feSMilanka Ringwald                                 break;
458ba9a58feSMilanka Ringwald                             }
459a1118d11SMilanka Ringwald                             device->report_id = packet[pos++];
4607d26fe66SMilanka Ringwald                             break;
4617d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
462de81b46aSMatthias Ringwald                             if (!btstack_hid_report_id_declared(hid_device_descriptor_len, hid_device_descriptor)) {
463c8cb324eSMilanka Ringwald                                 if (packet_size < 2) break;
464c8cb324eSMilanka Ringwald                                 if (packet[0] & 0x08){
465c8cb324eSMilanka Ringwald                                     if (packet_size > 2) {
466c8cb324eSMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
467c8cb324eSMilanka Ringwald                                     }
468c8cb324eSMilanka Ringwald                                 } else {
469c8cb324eSMilanka Ringwald                                     if (packet_size > 1) {
470c8cb324eSMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
471c8cb324eSMilanka Ringwald                                     }
472c8cb324eSMilanka Ringwald                                 }
473a1118d11SMilanka Ringwald                                 break;
474a1118d11SMilanka Ringwald                             }
4757d26fe66SMilanka Ringwald                             if (packet_size < 2){
4767d26fe66SMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4777d26fe66SMilanka Ringwald                                 break;
4787d26fe66SMilanka Ringwald                             }
479a1118d11SMilanka Ringwald                             device->report_id = packet[pos++];
480c8cb324eSMilanka Ringwald                             break;
4817bbeb3adSMilanka Ringwald                         default:
4827bbeb3adSMilanka Ringwald                             btstack_assert(false);
4837bbeb3adSMilanka Ringwald                             break;
484c8cb324eSMilanka Ringwald                     }
485c8cb324eSMilanka Ringwald                     if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
486e99e41f0SMatthias Ringwald                         l2cap_request_can_send_now_event(device->control_cid);
487c8cb324eSMilanka Ringwald                         break;
488c8cb324eSMilanka Ringwald                     }
489ca3f868cSMilanka Ringwald                     switch (hid_report_id_status(device->cid, device->report_id)){
490a1118d11SMilanka Ringwald                         case HID_REPORT_ID_INVALID:
491a1118d11SMilanka Ringwald                             device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
492a1118d11SMilanka Ringwald                             break;
493a1118d11SMilanka Ringwald                         default:
4947d26fe66SMilanka Ringwald                             break;
4957d26fe66SMilanka Ringwald                     }
496c8cb324eSMilanka Ringwald 
497de81b46aSMatthias Ringwald                     device->expected_report_size = hid_get_report_size_for_id(device->cid, device->report_id, device->report_type, hid_device_descriptor_len, hid_device_descriptor);
498a1118d11SMilanka Ringwald                     report_size =  device->expected_report_size + pos; // add 1 for header size and report id
499a1118d11SMilanka Ringwald 
500a1df452eSMatthias Ringwald                     if ((packet[0] & 0x08) && (packet_size >= (pos + 1))){
501a1118d11SMilanka Ringwald                         device->report_size = btstack_min(btstack_min(little_endian_read_16(packet, pos), report_size), sizeof(report));
502a1118d11SMilanka Ringwald                     } else {
503a1118d11SMilanka Ringwald                         device->report_size = btstack_min(btstack_min(l2cap_max_mtu(), report_size), sizeof(report));
504a1118d11SMilanka Ringwald                     }
505a1118d11SMilanka Ringwald 
506e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
5077d26fe66SMilanka Ringwald                     break;
5087d26fe66SMilanka Ringwald 
5097d26fe66SMilanka Ringwald                 case HID_MESSAGE_TYPE_SET_REPORT:
5107d26fe66SMilanka Ringwald                     device->state = HID_DEVICE_W2_SET_REPORT;
511a1118d11SMilanka Ringwald                     device->report_size = l2cap_max_mtu();
512ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[0] & 0x03);
5137d26fe66SMilanka Ringwald                     if (packet_size < 1){
5147d26fe66SMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
5157d26fe66SMilanka Ringwald                         break;
5167d26fe66SMilanka Ringwald                     }
5177d26fe66SMilanka Ringwald 
5187d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
5197d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
5207d26fe66SMilanka Ringwald                             if (packet_size < 3){
5217d26fe66SMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
5227d26fe66SMilanka Ringwald                                 break;
5237d26fe66SMilanka Ringwald                             }
5247d26fe66SMilanka Ringwald                             device->report_id = packet[1];
5254dfe6a8bSMilanka Ringwald                             device->report_status = hid_device_set_report_cmd_is_valid(device->cid, device->report_type, packet_size - 1, &packet[1]);
5264dfe6a8bSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) break;
5274dfe6a8bSMilanka Ringwald                             (*hci_device_set_report)(device->cid, device->report_type, packet_size-1, &packet[1]);
5287d26fe66SMilanka Ringwald                             break;
5297d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
5304dfe6a8bSMilanka Ringwald                             device->report_status = hid_device_set_report_cmd_is_valid(device->cid, device->report_type, packet_size - 1, &packet[1]);
5314dfe6a8bSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) break;
5324dfe6a8bSMilanka Ringwald 
5337d26fe66SMilanka Ringwald                             if (packet_size >= 2){
5344dfe6a8bSMilanka Ringwald                                 (*hci_device_set_report)(device->cid, device->report_type, packet_size-1, &packet[1]);
5357d26fe66SMilanka Ringwald                             } else {
5367d26fe66SMilanka Ringwald                                 uint8_t payload[] = {0};
5374dfe6a8bSMilanka Ringwald                                 (*hci_device_set_report)(device->cid, device->report_type, 1, payload);
5387d26fe66SMilanka Ringwald                             }
5397d26fe66SMilanka Ringwald                             break;
5407bbeb3adSMilanka Ringwald                         default:
5417bbeb3adSMilanka Ringwald                             btstack_assert(false);
5427bbeb3adSMilanka Ringwald                             break;
5437d26fe66SMilanka Ringwald                     }
544ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[0] & 0x03);
545e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
546ba9a58feSMilanka Ringwald                     break;
547ba9a58feSMilanka Ringwald                 case HID_MESSAGE_TYPE_GET_PROTOCOL:
548ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_W2_GET_PROTOCOL;
549ba9a58feSMilanka Ringwald                     if (packet_size != 1) {
550ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
551ba9a58feSMilanka Ringwald                         break;
552ba9a58feSMilanka Ringwald                     }
553ba9a58feSMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
554df3cc7a0SMilanka Ringwald                     // hid_device_request_can_send_now_event(channel);
555df3cc7a0SMilanka Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
5567d26fe66SMilanka Ringwald                     break;
557ba9a58feSMilanka Ringwald 
558ba9a58feSMilanka Ringwald                 case HID_MESSAGE_TYPE_SET_PROTOCOL:
559ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_W2_SET_PROTOCOL;
560ba9a58feSMilanka Ringwald                     if (packet_size != 1) {
561ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
562ba9a58feSMilanka Ringwald                         break;
563ba9a58feSMilanka Ringwald                     }
564ba9a58feSMilanka Ringwald                     param = packet[0] & 0x01;
565de81b46aSMatthias Ringwald                     if (((hid_protocol_mode_t)param == HID_PROTOCOL_MODE_BOOT) && !hid_device_boot_protocol_mode_supported){
566ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
567ba9a58feSMilanka Ringwald                         break;
568ba9a58feSMilanka Ringwald                     }
569ca3f868cSMilanka Ringwald                     device->protocol_mode = (hid_protocol_mode_t) param;
5707d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
5717d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
572ba9a58feSMilanka Ringwald                             break;
5737d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
5747d26fe66SMilanka Ringwald                             break;
5757bbeb3adSMilanka Ringwald                         default:
5767bbeb3adSMilanka Ringwald                             btstack_assert(false);
5777bbeb3adSMilanka Ringwald                             break;
5787d26fe66SMilanka Ringwald                     }
5797d26fe66SMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
580e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
5817d26fe66SMilanka Ringwald                     break;
5827d26fe66SMilanka Ringwald 
5836510739bSMilanka Ringwald                 case HID_MESSAGE_TYPE_HID_CONTROL:
5846510739bSMilanka Ringwald                     param = packet[0] & 0x0F;
585ffe3f1a1SMilanka Ringwald 
586ffe3f1a1SMilanka Ringwald                     switch ((hid_control_param_t)param){
5876510739bSMilanka Ringwald                         case HID_CONTROL_PARAM_SUSPEND:
5886510739bSMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_SUSPEND);
5896510739bSMilanka Ringwald                             break;
5906510739bSMilanka Ringwald                         case HID_CONTROL_PARAM_EXIT_SUSPEND:
5916510739bSMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_EXIT_SUSPEND);
5926510739bSMilanka Ringwald                             break;
593acfd7ed2SMilanka Ringwald                         case HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG:
594acfd7ed2SMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG);
595acfd7ed2SMilanka Ringwald                             break;
5966510739bSMilanka Ringwald                         default:
5976510739bSMilanka Ringwald                             device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
598e99e41f0SMatthias Ringwald                             l2cap_request_can_send_now_event(device->control_cid);
5996510739bSMilanka Ringwald                             break;
6006510739bSMilanka Ringwald                     }
6016510739bSMilanka Ringwald                     break;
6027d26fe66SMilanka Ringwald 
6037d26fe66SMilanka Ringwald                 case HID_MESSAGE_TYPE_DATA:
6047d26fe66SMilanka Ringwald                     if (packet_size < 2) {
6057d26fe66SMilanka Ringwald                         break;
6067d26fe66SMilanka Ringwald                     }
60707678896SMilanka Ringwald                     pos = 0;
608ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[pos++] & 0x03);
60907678896SMilanka Ringwald                     device->report_id = 0;
610de81b46aSMatthias Ringwald                     if (btstack_hid_report_id_declared(hid_device_descriptor_len, hid_device_descriptor)){
61107678896SMilanka Ringwald                         device->report_id = packet[pos++];
612738c9391SMilanka Ringwald                     }
61307678896SMilanka Ringwald 
61407678896SMilanka Ringwald                     if (hid_report_id_status(device->cid, device->report_id) == HID_REPORT_ID_INVALID){
61507678896SMilanka Ringwald                         log_info("Ignore invalid report data packet");
61607678896SMilanka Ringwald                         break;
61707678896SMilanka Ringwald                     }
61807678896SMilanka Ringwald                     if (!hid_report_size_valid(device->cid, device->report_id, device->report_type, packet_size - pos)){
61907678896SMilanka Ringwald                         log_info("Ignore invalid report data packet, invalid size");
62007678896SMilanka Ringwald                         break;
62107678896SMilanka Ringwald                     }
62207678896SMilanka Ringwald                     (*hci_device_report_data)(device->cid, device->report_type, device->report_id, packet_size - pos, &packet[pos]);
6237d26fe66SMilanka Ringwald                     break;
624c12110e4SMilanka Ringwald                 default:
625c12110e4SMilanka Ringwald                     device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
626e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
627c12110e4SMilanka Ringwald                     break;
628c12110e4SMilanka Ringwald             }
629c12110e4SMilanka Ringwald             break;
6308eb8d463SMatthias Ringwald         case HCI_EVENT_PACKET:
6318eb8d463SMatthias Ringwald             switch (packet[0]){
6328eb8d463SMatthias Ringwald                 case L2CAP_EVENT_INCOMING_CONNECTION:
6338eb8d463SMatthias Ringwald                     switch (l2cap_event_incoming_connection_get_psm(packet)){
6348eb8d463SMatthias Ringwald                         case PSM_HID_CONTROL:
6358eb8d463SMatthias Ringwald                         case PSM_HID_INTERRUPT:
6366510739bSMilanka Ringwald                             l2cap_event_incoming_connection_get_address(packet, address);
63772e3f392SMatthias Ringwald                             device = hid_device_provide_instance_for_bd_addr(address);
638c12110e4SMilanka Ringwald                             if (!device) {
6393da09877SMatthias Ringwald                                 log_error("L2CAP_EVENT_INCOMING_CONNECTION, cannot create instance for %s", bd_addr_to_str(address));
640c12110e4SMilanka Ringwald                                 l2cap_decline_connection(channel);
641c12110e4SMilanka Ringwald                                 break;
642c12110e4SMilanka Ringwald                             }
643c1ab6cc1SMatthias Ringwald                             if ((device->con_handle == HCI_CON_HANDLE_INVALID) || (l2cap_event_incoming_connection_get_handle(packet) == device->con_handle)){
644c12110e4SMilanka Ringwald                                 device->con_handle = l2cap_event_incoming_connection_get_handle(packet);
645c12110e4SMilanka Ringwald                                 device->incoming = 1;
6466510739bSMilanka Ringwald                                 l2cap_event_incoming_connection_get_address(packet, device->bd_addr);
6473da09877SMatthias Ringwald                                 psm = l2cap_event_incoming_connection_get_psm(packet);
6483da09877SMatthias Ringwald                                 switch (psm){
6493da09877SMatthias Ringwald                                     case PSM_HID_CONTROL:
6503da09877SMatthias Ringwald                                         device->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
6513da09877SMatthias Ringwald                                         break;
6523da09877SMatthias Ringwald                                     case PSM_HID_INTERRUPT:
6533da09877SMatthias Ringwald                                         device->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
6543da09877SMatthias Ringwald                                     break;
6553da09877SMatthias Ringwald                                     default:
6563da09877SMatthias Ringwald                                         break;
6573da09877SMatthias Ringwald                                 }
6583da09877SMatthias Ringwald 
6598eb8d463SMatthias Ringwald                                 l2cap_accept_connection(channel);
6608eb8d463SMatthias Ringwald                             } else {
6618eb8d463SMatthias Ringwald                                 l2cap_decline_connection(channel);
6623da09877SMatthias Ringwald                                 log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s", bd_addr_to_str(address));
6638eb8d463SMatthias Ringwald                             }
6648eb8d463SMatthias Ringwald                             break;
6658eb8d463SMatthias Ringwald                         default:
6668eb8d463SMatthias Ringwald                             l2cap_decline_connection(channel);
6678eb8d463SMatthias Ringwald                             break;
6688eb8d463SMatthias Ringwald                     }
6698eb8d463SMatthias Ringwald                     break;
6708eb8d463SMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_OPENED:
6713da09877SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(l2cap_event_channel_opened_get_local_cid(packet));
672c12110e4SMilanka Ringwald                     if (!device) {
673c12110e4SMilanka Ringwald                         log_error("L2CAP_EVENT_CHANNEL_OPENED, no hid device for local cid 0x%02x", l2cap_event_channel_opened_get_local_cid(packet));
674c12110e4SMilanka Ringwald                         return;
675c12110e4SMilanka Ringwald                     }
67657c643eeSMatthias Ringwald                     status = l2cap_event_channel_opened_get_status(packet);
67757c643eeSMatthias Ringwald                     if (status) {
678c12110e4SMilanka Ringwald                         if (device->incoming == 0){
67957c643eeSMatthias Ringwald                             // report error for outgoing connection
680c12110e4SMilanka Ringwald                             hid_device_emit_connected_event(device, status);
68157c643eeSMatthias Ringwald                         }
68257c643eeSMatthias Ringwald                         return;
68357c643eeSMatthias Ringwald                     }
684641577b2SMatthias Ringwald 
685641577b2SMatthias Ringwald                     // store con_handle
686641577b2SMatthias Ringwald                     if (device->con_handle == HCI_CON_HANDLE_INVALID){
687641577b2SMatthias Ringwald                         device->con_handle  = l2cap_event_channel_opened_get_handle(packet);
688641577b2SMatthias Ringwald                     }
689641577b2SMatthias Ringwald 
690641577b2SMatthias Ringwald                     // store l2cap cid
69157c643eeSMatthias Ringwald                     psm = l2cap_event_channel_opened_get_psm(packet);
69257c643eeSMatthias Ringwald                     switch (psm){
6938eb8d463SMatthias Ringwald                         case PSM_HID_CONTROL:
694c12110e4SMilanka Ringwald                             device->control_cid = l2cap_event_channel_opened_get_local_cid(packet);
6958eb8d463SMatthias Ringwald                             break;
6968eb8d463SMatthias Ringwald                         case PSM_HID_INTERRUPT:
697c12110e4SMilanka Ringwald                             device->interrupt_cid = l2cap_event_channel_opened_get_local_cid(packet);
6988eb8d463SMatthias Ringwald                             break;
6998eb8d463SMatthias Ringwald                         default:
7008eb8d463SMatthias Ringwald                             break;
7018eb8d463SMatthias Ringwald                     }
7026510739bSMilanka Ringwald 
70357c643eeSMatthias Ringwald                     // connect HID Interrupt for outgoing
704c1ab6cc1SMatthias Ringwald                     if ((device->incoming == 0) && (psm == PSM_HID_CONTROL)){
705c12110e4SMilanka Ringwald                         status = l2cap_create_channel(packet_handler, device->bd_addr, PSM_HID_INTERRUPT, 48, &device->interrupt_cid);
70657c643eeSMatthias Ringwald                         break;
70757c643eeSMatthias Ringwald                     }
708641577b2SMatthias Ringwald 
709641577b2SMatthias Ringwald                     // emit connected if both channels are open
710641577b2SMatthias Ringwald                     connected_before = device->connected;
711c12110e4SMilanka Ringwald                     if (!connected_before && device->control_cid && device->interrupt_cid){
712c12110e4SMilanka Ringwald                         device->connected = 1;
713c12110e4SMilanka Ringwald                         hid_device_emit_connected_event(device, 0);
7148eb8d463SMatthias Ringwald                     }
7158eb8d463SMatthias Ringwald                     break;
7168eb8d463SMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_CLOSED:
7174176cbc0SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(l2cap_event_channel_closed_get_local_cid(packet));
718c12110e4SMilanka Ringwald                     if (!device) return;
719c12110e4SMilanka Ringwald 
7206510739bSMilanka Ringwald                     // connected_before = device->connected;
721c12110e4SMilanka Ringwald                     device->incoming  = 0;
722c12110e4SMilanka Ringwald                     if (l2cap_event_channel_closed_get_local_cid(packet) == device->interrupt_cid){
723c12110e4SMilanka Ringwald                         device->interrupt_cid = 0;
72457c18996SMilanka Ringwald                     }
72557c18996SMilanka Ringwald                     if (l2cap_event_channel_closed_get_local_cid(packet) == device->control_cid){
72657c18996SMilanka Ringwald                         device->control_cid = 0;
7278eb8d463SMatthias Ringwald                     }
7286510739bSMilanka Ringwald                     if (!device->interrupt_cid && !device->control_cid){
7296510739bSMilanka Ringwald                         device->connected = 0;
7303da09877SMatthias Ringwald                         device->con_handle = HCI_CON_HANDLE_INVALID;
7316510739bSMilanka Ringwald                         device->cid = 0;
732caf9a547SMilanka Ringwald                         hid_device_emit_event(device, HID_SUBEVENT_CONNECTION_CLOSED);
7338eb8d463SMatthias Ringwald                     }
7348eb8d463SMatthias Ringwald                     break;
735c12110e4SMilanka Ringwald 
73657c18996SMilanka Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
7377d26fe66SMilanka Ringwald                     local_cid = l2cap_event_can_send_now_get_local_cid(packet);
7384176cbc0SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(local_cid);
7397d26fe66SMilanka Ringwald 
740c12110e4SMilanka Ringwald                     if (!device) return;
741c12110e4SMilanka Ringwald                     switch (device->state){
742a1118d11SMilanka Ringwald                         case HID_DEVICE_W2_GET_REPORT:{
7437d26fe66SMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) {
7447d26fe66SMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
7457d26fe66SMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
7467d26fe66SMilanka Ringwald                                 break;
7477d26fe66SMilanka Ringwald                             }
748a1118d11SMilanka Ringwald 
749a1118d11SMilanka Ringwald                             pos = 0;
750a1118d11SMilanka Ringwald                             report[pos++] = (HID_MESSAGE_TYPE_DATA << 4) | device->report_type;
751a1118d11SMilanka Ringwald                             if (device->report_id){
752a1118d11SMilanka Ringwald                                 report[pos++] = device->report_id;
753a1118d11SMilanka Ringwald                             }
754a1118d11SMilanka Ringwald 
7557d26fe66SMilanka Ringwald                             report_size = 0;
756a1118d11SMilanka Ringwald                             status = (*hci_device_get_report)(device->cid, device->report_type, device->report_id, &report_size, &report[pos]);
757481c7cdcSMilanka Ringwald 
758a1118d11SMilanka Ringwald                             switch (status){
759a1118d11SMilanka Ringwald                                 case 0:
760a1118d11SMilanka Ringwald                                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_NOT_READY;
761a1118d11SMilanka Ringwald                                     break;
762a1118d11SMilanka Ringwald                                 case 1:
763a1118d11SMilanka Ringwald                                     if (report_size == 0){
764a1118d11SMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
765a1118d11SMilanka Ringwald                                         break;
766a1118d11SMilanka Ringwald                                     }
767a1118d11SMilanka Ringwald                                     if (device->expected_report_size != report_size){
768a1118d11SMilanka Ringwald                                         log_error("Expected report size of %d bytes, received %d", device->expected_report_size, report_size);
769a1118d11SMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
770a1118d11SMilanka Ringwald                                         break;
771a1118d11SMilanka Ringwald                                     }
772a1118d11SMilanka Ringwald                                     break;
773a1118d11SMilanka Ringwald                                 default:
774a1118d11SMilanka Ringwald                                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
775a1118d11SMilanka Ringwald                                     break;
776a1118d11SMilanka Ringwald                             }
777ba9a58feSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
778ba9a58feSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
779481c7cdcSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
780481c7cdcSMilanka Ringwald                                 break;
781481c7cdcSMilanka Ringwald                             }
782481c7cdcSMilanka Ringwald 
783a1118d11SMilanka Ringwald                             // if (report_size > l2cap_max_mtu()){
784a1118d11SMilanka Ringwald                             //     report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
785a1118d11SMilanka Ringwald                             //     hid_device_send_control_message(device->cid, &report[0], 1);
786a1118d11SMilanka Ringwald                             //     break;
787a1118d11SMilanka Ringwald                             // }
788481c7cdcSMilanka Ringwald 
789a1118d11SMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], device->report_size);
790481c7cdcSMilanka Ringwald                             //     device->state = HID_DEVICE_IDLE;
791c12110e4SMilanka Ringwald                             break;
792a1118d11SMilanka Ringwald                         }
793ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SET_REPORT:
794ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SET_PROTOCOL:
795ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
7966510739bSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
797ba9a58feSMilanka Ringwald                             break;
798ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_GET_PROTOCOL:
799ba9a58feSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
800ba9a58feSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
801ba9a58feSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
802ba9a58feSMilanka Ringwald                                 break;
803ba9a58feSMilanka Ringwald                             }
8045139b81eSMilanka Ringwald 
8055139b81eSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_DATA << 4);
8065139b81eSMilanka Ringwald                             report[1] =  device->protocol_mode;
8075139b81eSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 2);
808ba9a58feSMilanka Ringwald                             break;
809ba9a58feSMilanka Ringwald 
810ba9a58feSMilanka Ringwald 
811ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST:
812ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
813ba9a58feSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
814c12110e4SMilanka Ringwald                             break;
815c12110e4SMilanka Ringwald                         default:
816e99e41f0SMatthias Ringwald                             if (device->user_request_can_send_now){
817e99e41f0SMatthias Ringwald                                 device->user_request_can_send_now = 0;
818caf9a547SMilanka Ringwald                                 hid_device_emit_event(device, HID_SUBEVENT_CAN_SEND_NOW);
819e99e41f0SMatthias Ringwald                             }
820c12110e4SMilanka Ringwald                             break;
821c12110e4SMilanka Ringwald                     }
822ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_IDLE;
8238eb8d463SMatthias Ringwald                     break;
8248eb8d463SMatthias Ringwald                 default:
8258eb8d463SMatthias Ringwald                     break;
8268eb8d463SMatthias Ringwald             }
8278eb8d463SMatthias Ringwald             break;
8288eb8d463SMatthias Ringwald         default:
8298eb8d463SMatthias Ringwald             break;
8308eb8d463SMatthias Ringwald     }
8318eb8d463SMatthias Ringwald }
8328eb8d463SMatthias Ringwald 
8338eb8d463SMatthias Ringwald /**
8348eb8d463SMatthias Ringwald  * @brief Set up HID Device
8358eb8d463SMatthias Ringwald  */
83651b27067SMilanka Ringwald void hid_device_init(bool boot_protocol_mode_supported, uint16_t descriptor_len, const uint8_t * descriptor){
837de81b46aSMatthias Ringwald     hid_device_boot_protocol_mode_supported = boot_protocol_mode_supported;
838de81b46aSMatthias Ringwald     hid_device_descriptor =  descriptor;
839de81b46aSMatthias Ringwald     hid_device_descriptor_len = descriptor_len;
840a1118d11SMilanka Ringwald     hci_device_get_report = dummy_write_report;
841a1118d11SMilanka Ringwald     hci_device_set_report = dummy_set_report;
842a1118d11SMilanka Ringwald     hci_device_report_data = dummy_report_data;
843a1118d11SMilanka Ringwald 
84478315a58SMatthias Ringwald     l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, gap_get_security_level());
84578315a58SMatthias Ringwald     l2cap_register_service(packet_handler, PSM_HID_CONTROL,   100, gap_get_security_level());
8468eb8d463SMatthias Ringwald }
8478eb8d463SMatthias Ringwald 
8482cc1b993SMatthias Ringwald void hid_device_deinit(void){
849de81b46aSMatthias Ringwald     hid_device_callback = NULL;
850de81b46aSMatthias Ringwald     hci_device_get_report = NULL;
851de81b46aSMatthias Ringwald     hci_device_set_report = NULL;
852de81b46aSMatthias Ringwald     hci_device_report_data = NULL;
853de81b46aSMatthias Ringwald 
854de81b46aSMatthias Ringwald     (void) memset(&hid_device_singleton, 0, sizeof(hid_device_t));
855de81b46aSMatthias Ringwald 
856de81b46aSMatthias Ringwald     hid_device_boot_protocol_mode_supported = false;
857de81b46aSMatthias Ringwald     hid_device_descriptor = NULL;
858de81b46aSMatthias Ringwald     hid_device_descriptor_len = 0;
859de81b46aSMatthias Ringwald     hid_device_cid = 0;
8602cc1b993SMatthias Ringwald }
8612cc1b993SMatthias Ringwald 
8628eb8d463SMatthias Ringwald /**
8638eb8d463SMatthias Ringwald  * @brief Register callback for the HID Device client.
8648eb8d463SMatthias Ringwald  * @param callback
8658eb8d463SMatthias Ringwald  */
8668eb8d463SMatthias Ringwald void hid_device_register_packet_handler(btstack_packet_handler_t callback){
867de81b46aSMatthias Ringwald     hid_device_callback = callback;
8688eb8d463SMatthias Ringwald }
8698eb8d463SMatthias Ringwald 
870a1118d11SMilanka Ringwald void hid_device_register_report_request_callback(int (*callback)(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int * out_report_size, uint8_t * out_report)){
871c12110e4SMilanka Ringwald     if (callback == NULL){
872c12110e4SMilanka Ringwald         callback = dummy_write_report;
873c12110e4SMilanka Ringwald     }
874a1118d11SMilanka Ringwald     hci_device_get_report = callback;
875c12110e4SMilanka Ringwald }
876c12110e4SMilanka Ringwald 
8774dfe6a8bSMilanka Ringwald void hid_device_register_set_report_callback(void (*callback)(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report)){
878ba9a58feSMilanka Ringwald     if (callback == NULL){
879ba9a58feSMilanka Ringwald         callback = dummy_set_report;
880ba9a58feSMilanka Ringwald     }
881ba9a58feSMilanka Ringwald     hci_device_set_report = callback;
882ba9a58feSMilanka Ringwald }
883c12110e4SMilanka Ringwald 
8847d26fe66SMilanka Ringwald void hid_device_register_report_data_callback(void (*callback)(uint16_t cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t * report)){
8857d26fe66SMilanka Ringwald     if (callback == NULL){
8867d26fe66SMilanka Ringwald         callback = dummy_report_data;
8877d26fe66SMilanka Ringwald     }
8887d26fe66SMilanka Ringwald     hci_device_report_data = callback;
8897d26fe66SMilanka Ringwald }
8907d26fe66SMilanka Ringwald 
8917d26fe66SMilanka Ringwald 
8928eb8d463SMatthias Ringwald /**
8938eb8d463SMatthias Ringwald  * @brief Request can send now event to send HID Report
8948eb8d463SMatthias Ringwald  * @param hid_cid
8958eb8d463SMatthias Ringwald  */
8968eb8d463SMatthias Ringwald void hid_device_request_can_send_now_event(uint16_t hid_cid){
8974176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
898e99e41f0SMatthias Ringwald     if (!hid_device || !hid_device->interrupt_cid) return;
899e99e41f0SMatthias Ringwald     hid_device->user_request_can_send_now = 1;
900e99e41f0SMatthias Ringwald     l2cap_request_can_send_now_event(hid_device->interrupt_cid);
9018eb8d463SMatthias Ringwald }
9028eb8d463SMatthias Ringwald 
9038eb8d463SMatthias Ringwald /**
9048eb8d463SMatthias Ringwald  * @brief Send HID message on interrupt channel
9058eb8d463SMatthias Ringwald  * @param hid_cid
9068eb8d463SMatthias Ringwald  */
9078eb8d463SMatthias Ringwald void hid_device_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
9084176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
909c12110e4SMilanka Ringwald     if (!hid_device || !hid_device->interrupt_cid) return;
9108eb8d463SMatthias Ringwald     l2cap_send(hid_device->interrupt_cid, (uint8_t*) message, message_len);
911e9392950SMilanka Ringwald     if (hid_device->user_request_can_send_now){
912e99e41f0SMatthias Ringwald         l2cap_request_can_send_now_event((hid_device->interrupt_cid));
913e99e41f0SMatthias Ringwald     }
9148eb8d463SMatthias Ringwald }
9158eb8d463SMatthias Ringwald 
9168eb8d463SMatthias Ringwald /**
9178eb8d463SMatthias Ringwald  * @brief Send HID message on control channel
9188eb8d463SMatthias Ringwald  * @param hid_cid
9198eb8d463SMatthias Ringwald  */
920c12110e4SMilanka Ringwald void hid_device_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
9214176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
922c12110e4SMilanka Ringwald     if (!hid_device || !hid_device->control_cid) return;
9238eb8d463SMatthias Ringwald     l2cap_send(hid_device->control_cid, (uint8_t*) message, message_len);
924e99e41f0SMatthias Ringwald     // request user can send now if pending
925e99e41f0SMatthias Ringwald     if (hid_device->user_request_can_send_now){
926e99e41f0SMatthias Ringwald         l2cap_request_can_send_now_event((hid_device->control_cid));
927e99e41f0SMatthias Ringwald     }
9288eb8d463SMatthias Ringwald }
9298eb8d463SMatthias Ringwald 
93057c643eeSMatthias Ringwald /*
93157c643eeSMatthias Ringwald  * @brief Create HID connection to HID Host
93257c643eeSMatthias Ringwald  * @param addr
93357c643eeSMatthias Ringwald  * @param hid_cid to use for other commands
93457c643eeSMatthias Ringwald  * @result status
93557c643eeSMatthias Ringwald  */
93657c643eeSMatthias Ringwald uint8_t hid_device_connect(bd_addr_t addr, uint16_t * hid_cid){
937c12110e4SMilanka Ringwald     hid_device_t * hid_device = hid_device_create_instance();
938c12110e4SMilanka Ringwald     if (!hid_device){
939c12110e4SMilanka Ringwald         log_error("hid_device_connect: could not create a hid device instace");
940c12110e4SMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
941c12110e4SMilanka Ringwald     }
94272e3f392SMatthias Ringwald 
943*6c8af51cSMatthias Ringwald     // setup instance
944*6c8af51cSMatthias Ringwald     hid_device_setup_instance(hid_device, addr);
945*6c8af51cSMatthias Ringwald     *hid_cid = hid_device->cid;
94672e3f392SMatthias Ringwald 
94757c643eeSMatthias Ringwald     // create l2cap control using fixed HID L2CAP PSM
94857c643eeSMatthias Ringwald     log_info("Create outgoing HID Control");
94957c643eeSMatthias Ringwald     uint8_t status = l2cap_create_channel(packet_handler, hid_device->bd_addr, PSM_HID_CONTROL, 48, &hid_device->control_cid);
95057c643eeSMatthias Ringwald     return status;
95157c643eeSMatthias Ringwald }
9526510739bSMilanka Ringwald 
9536510739bSMilanka Ringwald /*
9546510739bSMilanka Ringwald  * @brief Disconnect from HID Host
9556510739bSMilanka Ringwald  * @param hid_cid
9566510739bSMilanka Ringwald  * @result status
9576510739bSMilanka Ringwald  */
95857c18996SMilanka Ringwald void hid_device_disconnect_interrupt_channel(uint16_t hid_cid){
9594176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
96057c18996SMilanka Ringwald     if (!hid_device){
96157c18996SMilanka Ringwald         log_error("hid_device_disconnect_interrupt_channel: could not find hid device instace");
96257c18996SMilanka Ringwald         return;
96357c18996SMilanka Ringwald     }
96457c18996SMilanka Ringwald     log_info("Disconnect from interrupt channel HID Host");
96557c18996SMilanka Ringwald     if (hid_device->interrupt_cid){
966b93f8966SMatthias Ringwald         l2cap_disconnect(hid_device->interrupt_cid);
96757c18996SMilanka Ringwald     }
96857c18996SMilanka Ringwald }
96957c18996SMilanka Ringwald 
97057c18996SMilanka Ringwald void hid_device_disconnect_control_channel(uint16_t hid_cid){
9714176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
97257c18996SMilanka Ringwald     if (!hid_device){
97357c18996SMilanka Ringwald         log_error("hid_device_disconnect_control_channel: could not find hid device instace");
97457c18996SMilanka Ringwald         return;
97557c18996SMilanka Ringwald     }
97657c18996SMilanka Ringwald     log_info("Disconnect from control channel HID Host");
97757c18996SMilanka Ringwald     if (hid_device->control_cid){
978b93f8966SMatthias Ringwald         l2cap_disconnect(hid_device->control_cid);
97957c18996SMilanka Ringwald     }
98057c18996SMilanka Ringwald }
98157c18996SMilanka Ringwald 
9826510739bSMilanka Ringwald void hid_device_disconnect(uint16_t hid_cid){
9834176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
9846510739bSMilanka Ringwald     if (!hid_device){
9856510739bSMilanka Ringwald         log_error("hid_device_disconnect: could not find hid device instace");
9866510739bSMilanka Ringwald         return;
9876510739bSMilanka Ringwald     }
9886510739bSMilanka Ringwald     log_info("Disconnect from HID Host");
9896510739bSMilanka Ringwald     if (hid_device->interrupt_cid){
990b93f8966SMatthias Ringwald         l2cap_disconnect(hid_device->interrupt_cid);
9916510739bSMilanka Ringwald     }
99257c18996SMilanka Ringwald     if (hid_device->control_cid){
993b93f8966SMatthias Ringwald         l2cap_disconnect(hid_device->control_cid);
99457c18996SMilanka Ringwald         }
9956510739bSMilanka Ringwald }
9967d26fe66SMilanka Ringwald 
9977d26fe66SMilanka Ringwald int hid_device_in_boot_protocol_mode(uint16_t hid_cid){
9984176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
9997d26fe66SMilanka Ringwald     if (!hid_device){
10007d26fe66SMilanka Ringwald         log_error("hid_device_in_boot_protocol_mode: could not find hid device instace");
10017d26fe66SMilanka Ringwald         return 0;
10027d26fe66SMilanka Ringwald     }
10037d26fe66SMilanka Ringwald     return hid_device->protocol_mode == HID_PROTOCOL_MODE_BOOT;
10047d26fe66SMilanka Ringwald }
1005