xref: /btstack/src/classic/hid_device.c (revision e99e41f0b9aa901f6867dd04c2131ad6c06a7d25)
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
23d40c9ac6SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24d40c9ac6SMatthias Ringwald  * RINGWALD 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 
38d40c9ac6SMatthias Ringwald #define __BTSTACK_FILE__ "hid_device.c"
39d40c9ac6SMatthias Ringwald 
40d40c9ac6SMatthias Ringwald #include <string.h>
41d40c9ac6SMatthias Ringwald 
42d40c9ac6SMatthias Ringwald #include "classic/hid_device.h"
43d40c9ac6SMatthias Ringwald #include "classic/sdp_util.h"
44d40c9ac6SMatthias Ringwald #include "bluetooth.h"
45d40c9ac6SMatthias Ringwald #include "bluetooth_sdp.h"
468eb8d463SMatthias Ringwald #include "l2cap.h"
478eb8d463SMatthias Ringwald #include "btstack_event.h"
488eb8d463SMatthias Ringwald #include "btstack_debug.h"
49662cddc2SMilanka Ringwald #include "btstack_hid_parser.h"
508eb8d463SMatthias Ringwald 
51c12110e4SMilanka Ringwald typedef enum {
52c12110e4SMilanka Ringwald     HID_DEVICE_IDLE,
53c12110e4SMilanka Ringwald     HID_DEVICE_CONNECTED,
54ba9a58feSMilanka Ringwald     HID_DEVICE_W2_GET_REPORT,
55ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SET_REPORT,
56ba9a58feSMilanka Ringwald     HID_DEVICE_W2_GET_PROTOCOL,
57ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SET_PROTOCOL,
58ba9a58feSMilanka Ringwald     HID_DEVICE_W2_ANSWER_SET_PROTOCOL,
59ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST,
60c12110e4SMilanka Ringwald } hid_device_state_t;
61c12110e4SMilanka Ringwald 
628eb8d463SMatthias Ringwald // hid device state
638eb8d463SMatthias Ringwald typedef struct hid_device {
648eb8d463SMatthias Ringwald     uint16_t  cid;
658eb8d463SMatthias Ringwald     bd_addr_t bd_addr;
668eb8d463SMatthias Ringwald     hci_con_handle_t con_handle;
678eb8d463SMatthias Ringwald     uint16_t  control_cid;
688eb8d463SMatthias Ringwald     uint16_t  interrupt_cid;
698eb8d463SMatthias Ringwald     uint8_t   incoming;
7057c643eeSMatthias Ringwald     uint8_t   connected;
71c12110e4SMilanka Ringwald     hid_device_state_t state;
72c12110e4SMilanka Ringwald     hid_report_type_t report_type;
73c12110e4SMilanka Ringwald     uint16_t  report_id;
74a1118d11SMilanka Ringwald     uint16_t  expected_report_size;
75a1118d11SMilanka Ringwald     uint16_t  report_size;
76*e99e41f0SMatthias Ringwald     uint8_t   user_request_can_send_now;
77ba9a58feSMilanka Ringwald 
78bc967e00SMilanka Ringwald     hid_handshake_param_type_t report_status;
79ba9a58feSMilanka Ringwald     hid_protocol_mode_t protocol_mode;
808eb8d463SMatthias Ringwald } hid_device_t;
818eb8d463SMatthias Ringwald 
828eb8d463SMatthias Ringwald static hid_device_t _hid_device;
83ba9a58feSMilanka Ringwald static uint8_t hid_boot_protocol_mode_supported;
84662cddc2SMilanka Ringwald static const uint8_t * hid_descriptor;
85662cddc2SMilanka Ringwald static uint16_t hid_descriptor_len;
86c12110e4SMilanka Ringwald 
87a1118d11SMilanka 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){
88c12110e4SMilanka Ringwald     UNUSED(hid_cid);
89c12110e4SMilanka Ringwald     UNUSED(report_type);
90c12110e4SMilanka Ringwald     UNUSED(report_id);
91c12110e4SMilanka Ringwald     UNUSED(out_report_size);
92c12110e4SMilanka Ringwald     UNUSED(out_report);
93a1118d11SMilanka Ringwald     return -1;
94c12110e4SMilanka Ringwald }
954dfe6a8bSMilanka Ringwald 
964dfe6a8bSMilanka Ringwald static void dummy_set_report(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report){
97ba9a58feSMilanka Ringwald     UNUSED(hid_cid);
98ba9a58feSMilanka Ringwald     UNUSED(report_type);
99ba9a58feSMilanka Ringwald     UNUSED(report_size);
100ba9a58feSMilanka Ringwald     UNUSED(report);
101ba9a58feSMilanka Ringwald }
1024dfe6a8bSMilanka Ringwald 
1037d26fe66SMilanka 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){
1047d26fe66SMilanka Ringwald     UNUSED(hid_cid);
1057d26fe66SMilanka Ringwald     UNUSED(report_type);
1067d26fe66SMilanka Ringwald     UNUSED(report_id);
1077d26fe66SMilanka Ringwald     UNUSED(report_size);
1087d26fe66SMilanka Ringwald     UNUSED(report);
1097d26fe66SMilanka Ringwald }
1107d26fe66SMilanka Ringwald 
111a1118d11SMilanka 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;
1124dfe6a8bSMilanka 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;
1137d26fe66SMilanka 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;
1148eb8d463SMatthias Ringwald 
1158eb8d463SMatthias Ringwald static btstack_packet_handler_t hid_callback;
116d40c9ac6SMatthias Ringwald 
117c12110e4SMilanka Ringwald static uint16_t hid_device_cid = 0;
118c12110e4SMilanka Ringwald 
119c12110e4SMilanka Ringwald static uint16_t hid_device_get_next_cid(void){
120c12110e4SMilanka Ringwald     hid_device_cid++;
121c12110e4SMilanka Ringwald     if (!hid_device_cid){
122c12110e4SMilanka Ringwald         hid_device_cid = 1;
123c12110e4SMilanka Ringwald     }
124c12110e4SMilanka Ringwald     return hid_device_cid;
125c12110e4SMilanka Ringwald }
126c12110e4SMilanka Ringwald 
127c12110e4SMilanka Ringwald // TODO: store hid device connection into list
1284176cbc0SMatthias Ringwald static hid_device_t * hid_device_get_instance_for_l2cap_cid(uint16_t cid){
1294176cbc0SMatthias Ringwald     if (_hid_device.control_cid == cid || _hid_device.interrupt_cid == cid){
1304176cbc0SMatthias Ringwald         return &_hid_device;
1314176cbc0SMatthias Ringwald     }
1324176cbc0SMatthias Ringwald     return NULL;
1334176cbc0SMatthias Ringwald }
1344176cbc0SMatthias Ringwald 
1354176cbc0SMatthias Ringwald static hid_device_t * hid_device_get_instance_for_hid_cid(uint16_t hid_cid){
1364176cbc0SMatthias Ringwald     if (_hid_device.cid == hid_cid){
137c12110e4SMilanka Ringwald         return &_hid_device;
138c12110e4SMilanka Ringwald     }
139c12110e4SMilanka Ringwald     return NULL;
140c12110e4SMilanka Ringwald }
141c12110e4SMilanka Ringwald 
14272e3f392SMatthias Ringwald static hid_device_t * hid_device_provide_instance_for_bd_addr(bd_addr_t bd_addr){
1436510739bSMilanka Ringwald     if (!_hid_device.cid){
144ba9a58feSMilanka Ringwald         memcpy(_hid_device.bd_addr, bd_addr, 6);
1456510739bSMilanka Ringwald         _hid_device.cid = hid_device_get_next_cid();
146ba9a58feSMilanka Ringwald         _hid_device.protocol_mode = HID_PROTOCOL_MODE_REPORT;
14772e3f392SMatthias Ringwald         _hid_device.con_handle = HCI_CON_HANDLE_INVALID;
1486510739bSMilanka Ringwald     }
149c12110e4SMilanka Ringwald     return &_hid_device;
150c12110e4SMilanka Ringwald }
151c12110e4SMilanka Ringwald 
152c12110e4SMilanka Ringwald static hid_device_t * hid_device_create_instance(void){
153ba9a58feSMilanka Ringwald 
154c12110e4SMilanka Ringwald     return &_hid_device;
155c12110e4SMilanka Ringwald }
156c12110e4SMilanka Ringwald 
157d40c9ac6SMatthias Ringwald void hid_create_sdp_record(
158d40c9ac6SMatthias Ringwald     uint8_t *service,
159d40c9ac6SMatthias Ringwald     uint32_t service_record_handle,
160d40c9ac6SMatthias Ringwald     uint16_t hid_device_subclass,
161d40c9ac6SMatthias Ringwald     uint8_t  hid_country_code,
162d40c9ac6SMatthias Ringwald     uint8_t  hid_virtual_cable,
163d40c9ac6SMatthias Ringwald     uint8_t  hid_reconnect_initiate,
164d40c9ac6SMatthias Ringwald     uint8_t  hid_boot_device,
165662cddc2SMilanka Ringwald     const uint8_t * descriptor, uint16_t descriptor_size,
166d40c9ac6SMatthias Ringwald     const char *device_name){
167d40c9ac6SMatthias Ringwald 
168d40c9ac6SMatthias Ringwald     uint8_t * attribute;
169d40c9ac6SMatthias Ringwald     de_create_sequence(service);
170d40c9ac6SMatthias Ringwald 
171d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
172d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
173d40c9ac6SMatthias Ringwald 
174d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
175d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
176d40c9ac6SMatthias Ringwald     {
177d40c9ac6SMatthias Ringwald         de_add_number(attribute,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
178d40c9ac6SMatthias Ringwald     }
179d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
180d40c9ac6SMatthias Ringwald 
181d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
182d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
183d40c9ac6SMatthias Ringwald     {
184d40c9ac6SMatthias Ringwald         uint8_t * l2cpProtocol = de_push_sequence(attribute);
185d40c9ac6SMatthias Ringwald         {
186d40c9ac6SMatthias Ringwald             de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
187d40c9ac6SMatthias Ringwald             de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, PSM_HID_CONTROL);
188d40c9ac6SMatthias Ringwald         }
189d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, l2cpProtocol);
190d40c9ac6SMatthias Ringwald 
191d40c9ac6SMatthias Ringwald         uint8_t * hidProtocol = de_push_sequence(attribute);
192d40c9ac6SMatthias Ringwald         {
193d40c9ac6SMatthias Ringwald             de_add_number(hidProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
194d40c9ac6SMatthias Ringwald         }
195d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidProtocol);
196d40c9ac6SMatthias Ringwald     }
197d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
198d40c9ac6SMatthias Ringwald 
199d40c9ac6SMatthias Ringwald     // TODO?
200d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
201d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
202d40c9ac6SMatthias Ringwald     {
203d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e);
204d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a);
205d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100);
206d40c9ac6SMatthias Ringwald     }
207d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
208d40c9ac6SMatthias Ringwald 
209d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS);
210d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
211d40c9ac6SMatthias Ringwald     {
212d40c9ac6SMatthias Ringwald         uint8_t * additionalDescriptorAttribute = de_push_sequence(attribute);
213d40c9ac6SMatthias Ringwald         {
214d40c9ac6SMatthias Ringwald             uint8_t * l2cpProtocol = de_push_sequence(additionalDescriptorAttribute);
215d40c9ac6SMatthias Ringwald             {
216d40c9ac6SMatthias Ringwald                 de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
217d40c9ac6SMatthias Ringwald                 de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, PSM_HID_INTERRUPT);
218d40c9ac6SMatthias Ringwald             }
219d40c9ac6SMatthias Ringwald             de_pop_sequence(additionalDescriptorAttribute, l2cpProtocol);
220d40c9ac6SMatthias Ringwald 
2219fa0921fSMatthias Ringwald             uint8_t * hidProtocol = de_push_sequence(additionalDescriptorAttribute);
222d40c9ac6SMatthias Ringwald             {
223d40c9ac6SMatthias Ringwald                 de_add_number(hidProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
224d40c9ac6SMatthias Ringwald             }
2259fa0921fSMatthias Ringwald             de_pop_sequence(additionalDescriptorAttribute, hidProtocol);
226d40c9ac6SMatthias Ringwald         }
227d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, additionalDescriptorAttribute);
228d40c9ac6SMatthias Ringwald     }
229d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
230d40c9ac6SMatthias Ringwald 
231d40c9ac6SMatthias Ringwald     // 0x0100 "ServiceName"
232d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0100);
233d40c9ac6SMatthias Ringwald     de_add_data(service,  DE_STRING, strlen(device_name), (uint8_t *) device_name);
234d40c9ac6SMatthias Ringwald 
235d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
236d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
237d40c9ac6SMatthias Ringwald     {
238d40c9ac6SMatthias Ringwald         uint8_t * hidProfile = de_push_sequence(attribute);
239d40c9ac6SMatthias Ringwald         {
240d40c9ac6SMatthias Ringwald             de_add_number(hidProfile,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
241d40c9ac6SMatthias Ringwald             de_add_number(hidProfile,  DE_UINT, DE_SIZE_16, 0x0101);    // Version 1.1
242d40c9ac6SMatthias Ringwald         }
243d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidProfile);
244d40c9ac6SMatthias Ringwald     }
245d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
246d40c9ac6SMatthias Ringwald 
247d40c9ac6SMatthias Ringwald     // Deprecated in v1.1.1
248d40c9ac6SMatthias Ringwald     // de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_RELEASE_NUMBER);
249d40c9ac6SMatthias Ringwald     // de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0101);
250d40c9ac6SMatthias Ringwald 
251d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_PARSER_VERSION);
252d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0111);  // v1.1.1
253d40c9ac6SMatthias Ringwald 
254d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_SUBCLASS);
2559679ea81SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_8,  hid_device_subclass);
256d40c9ac6SMatthias Ringwald 
257d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_COUNTRY_CODE);
2589679ea81SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_8,  hid_country_code);
259d40c9ac6SMatthias Ringwald 
260d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_VIRTUAL_CABLE);
261d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  hid_virtual_cable);
262d40c9ac6SMatthias Ringwald 
263d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_RECONNECT_INITIATE);
264d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  hid_reconnect_initiate);
265d40c9ac6SMatthias Ringwald 
266d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST);
267d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
268d40c9ac6SMatthias Ringwald     {
269d40c9ac6SMatthias Ringwald         uint8_t* hidDescriptor = de_push_sequence(attribute);
270d40c9ac6SMatthias Ringwald         {
271d40c9ac6SMatthias Ringwald             de_add_number(hidDescriptor,  DE_UINT, DE_SIZE_8, 0x22);    // Report Descriptor
272662cddc2SMilanka Ringwald             de_add_data(hidDescriptor,  DE_STRING, descriptor_size, (uint8_t *) descriptor);
273d40c9ac6SMatthias Ringwald         }
274d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidDescriptor);
275d40c9ac6SMatthias Ringwald     }
276d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
277d40c9ac6SMatthias Ringwald 
2789679ea81SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDLANGID_BASE_LIST);
2799679ea81SMatthias Ringwald     attribute = de_push_sequence(service);
2809679ea81SMatthias Ringwald     {
2819679ea81SMatthias Ringwald         uint8_t* hig_lang_base = de_push_sequence(attribute);
2829679ea81SMatthias Ringwald         {
2839679ea81SMatthias Ringwald             // see: http://www.usb.org/developers/docs/USB_LANGIDs.pdf
2849679ea81SMatthias Ringwald             de_add_number(hig_lang_base,  DE_UINT, DE_SIZE_16, 0x0409);    // HIDLANGID = English (US)
2859679ea81SMatthias Ringwald             de_add_number(hig_lang_base,  DE_UINT, DE_SIZE_16, 0x0100);    // HIDLanguageBase = 0x0100 default
2869679ea81SMatthias Ringwald         }
2879679ea81SMatthias Ringwald         de_pop_sequence(attribute, hig_lang_base);
2889679ea81SMatthias Ringwald     }
2899679ea81SMatthias Ringwald     de_pop_sequence(service, attribute);
2909679ea81SMatthias Ringwald 
2916510739bSMilanka Ringwald     uint8_t hid_remote_wake = 1;
2926510739bSMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_REMOTE_WAKE);
2936510739bSMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  hid_remote_wake);
2946510739bSMilanka Ringwald 
295d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_BOOT_DEVICE);
296d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  hid_boot_device);
297d40c9ac6SMatthias Ringwald }
2988eb8d463SMatthias Ringwald 
2998eb8d463SMatthias Ringwald static inline void hid_device_emit_connected_event(hid_device_t * context, uint8_t status){
3008eb8d463SMatthias Ringwald     uint8_t event[15];
3018eb8d463SMatthias Ringwald     int pos = 0;
3028eb8d463SMatthias Ringwald     event[pos++] = HCI_EVENT_HID_META;
3038eb8d463SMatthias Ringwald     pos++;  // skip len
3048eb8d463SMatthias Ringwald     event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
3058eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->cid);
3068eb8d463SMatthias Ringwald     pos+=2;
3078eb8d463SMatthias Ringwald     event[pos++] = status;
3086510739bSMilanka Ringwald     reverse_bd_addr(context->bd_addr, &event[pos]);
3098eb8d463SMatthias Ringwald     pos += 6;
3108eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->con_handle);
3118eb8d463SMatthias Ringwald     pos += 2;
3128eb8d463SMatthias Ringwald     event[pos++] = context->incoming;
3138eb8d463SMatthias Ringwald     event[1] = pos - 2;
3148eb8d463SMatthias Ringwald     if (pos != sizeof(event)) log_error("hid_device_emit_connected_event size %u", pos);
3158eb8d463SMatthias Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3168eb8d463SMatthias Ringwald }
3178eb8d463SMatthias Ringwald 
3188eb8d463SMatthias Ringwald static inline void hid_device_emit_connection_closed_event(hid_device_t * context){
3198eb8d463SMatthias Ringwald     uint8_t event[5];
3208eb8d463SMatthias Ringwald     int pos = 0;
3218eb8d463SMatthias Ringwald     event[pos++] = HCI_EVENT_HID_META;
3228eb8d463SMatthias Ringwald     pos++;  // skip len
3238eb8d463SMatthias Ringwald     event[pos++] = HID_SUBEVENT_CONNECTION_CLOSED;
3248eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->cid);
3258eb8d463SMatthias Ringwald     pos+=2;
3268eb8d463SMatthias Ringwald     event[1] = pos - 2;
3278eb8d463SMatthias Ringwald     if (pos != sizeof(event)) log_error("hid_device_emit_connection_closed_event size %u", pos);
3288eb8d463SMatthias Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3298eb8d463SMatthias Ringwald }
3308eb8d463SMatthias Ringwald 
3318eb8d463SMatthias Ringwald static inline void hid_device_emit_can_send_now_event(hid_device_t * context){
3328eb8d463SMatthias Ringwald     uint8_t event[5];
3338eb8d463SMatthias Ringwald     int pos = 0;
3348eb8d463SMatthias Ringwald     event[pos++] = HCI_EVENT_HID_META;
3358eb8d463SMatthias Ringwald     pos++;  // skip len
3368eb8d463SMatthias Ringwald     event[pos++] = HID_SUBEVENT_CAN_SEND_NOW;
3378eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->cid);
3388eb8d463SMatthias Ringwald     pos+=2;
3398eb8d463SMatthias Ringwald     event[1] = pos - 2;
3408eb8d463SMatthias Ringwald     if (pos != sizeof(event)) log_error("hid_device_emit_can_send_now_event size %u", pos);
3418eb8d463SMatthias Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3428eb8d463SMatthias Ringwald }
3438eb8d463SMatthias Ringwald 
3446510739bSMilanka Ringwald static inline void hid_device_emit_event(hid_device_t * context, uint8_t subevent_type){
3456510739bSMilanka Ringwald     uint8_t event[4];
3466510739bSMilanka Ringwald     int pos = 0;
3476510739bSMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
3486510739bSMilanka Ringwald     pos++;  // skip len
3496510739bSMilanka Ringwald     event[pos++] = subevent_type;
3506510739bSMilanka Ringwald     little_endian_store_16(event,pos,context->cid);
3516510739bSMilanka Ringwald     pos+=2;
3526510739bSMilanka Ringwald     event[1] = pos - 2;
3536510739bSMilanka Ringwald     if (pos != sizeof(event)) log_error("hid_device_emit_event size %u", pos);
3546510739bSMilanka Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3556510739bSMilanka Ringwald }
3566510739bSMilanka Ringwald 
357c8cb324eSMilanka Ringwald static int hid_report_size_valid(uint16_t cid, int report_id, hid_report_type_t report_type, int report_size){
358c8cb324eSMilanka Ringwald     if (!report_size) return 0;
359c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
360c8cb324eSMilanka Ringwald         switch (report_id){
361c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
362c8cb324eSMilanka Ringwald                 if (report_size < 8) return 0;
363c8cb324eSMilanka Ringwald                 break;
364c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
365c8cb324eSMilanka Ringwald                 if (report_size < 1) return 0;
366c8cb324eSMilanka Ringwald                 break;
367c8cb324eSMilanka Ringwald             default:
368c8cb324eSMilanka Ringwald                 return 0;
369c8cb324eSMilanka Ringwald         }
370c8cb324eSMilanka Ringwald     } else {
371c8cb324eSMilanka Ringwald         int size =  btstack_hid_get_report_size_for_id(report_id, report_type, hid_descriptor_len, hid_descriptor);
372c8cb324eSMilanka Ringwald         if (size == 0 || size != report_size) return 0;
373c8cb324eSMilanka Ringwald     }
374c8cb324eSMilanka Ringwald     return 1;
375c8cb324eSMilanka Ringwald }
376c8cb324eSMilanka Ringwald 
377a796c06fSMilanka 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){
378c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
379c8cb324eSMilanka Ringwald         switch (report_id){
380c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
381c8cb324eSMilanka Ringwald                 return 8;
382c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
383c8cb324eSMilanka Ringwald                 return 3;
384c8cb324eSMilanka Ringwald             default:
385c8cb324eSMilanka Ringwald                 return 0;
386c8cb324eSMilanka Ringwald         }
387c8cb324eSMilanka Ringwald     } else {
388a796c06fSMilanka Ringwald         return btstack_hid_get_report_size_for_id(report_id, report_type, descriptor_len, descriptor);
389c8cb324eSMilanka Ringwald     }
390c8cb324eSMilanka Ringwald }
391c8cb324eSMilanka Ringwald 
392c8cb324eSMilanka Ringwald static hid_report_id_status_t hid_report_id_status(uint16_t cid, uint16_t report_id){
393c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
394c8cb324eSMilanka Ringwald         switch (report_id){
395c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
396c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
397c8cb324eSMilanka Ringwald                 return HID_REPORT_ID_VALID;
398c8cb324eSMilanka Ringwald             default:
399c8cb324eSMilanka Ringwald                 return HID_REPORT_ID_INVALID;
400c8cb324eSMilanka Ringwald         }
401c8cb324eSMilanka Ringwald     } else {
402c8cb324eSMilanka Ringwald         return btstack_hid_id_valid(report_id, hid_descriptor_len, hid_descriptor);
403c8cb324eSMilanka Ringwald     }
404c8cb324eSMilanka Ringwald }
405c8cb324eSMilanka Ringwald 
4064dfe6a8bSMilanka 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){
4074dfe6a8bSMilanka Ringwald     int pos = 0;
408c8cb324eSMilanka Ringwald     int report_id = 0;
409c8cb324eSMilanka Ringwald 
410c8cb324eSMilanka Ringwald     if (btstack_hid_report_id_declared(hid_descriptor_len, hid_descriptor)){
411c8cb324eSMilanka Ringwald         report_id = report[pos++];
4124dfe6a8bSMilanka Ringwald         hid_report_id_status_t report_id_status = hid_report_id_status(cid, report_id);
4134dfe6a8bSMilanka Ringwald         switch (report_id_status){
4144dfe6a8bSMilanka Ringwald             case HID_REPORT_ID_INVALID:
4154dfe6a8bSMilanka Ringwald                 return HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
4164dfe6a8bSMilanka Ringwald             default:
4174dfe6a8bSMilanka Ringwald                 break;
4184dfe6a8bSMilanka Ringwald         }
419c8cb324eSMilanka Ringwald         // printf("hid_device_set_report_cmd_is_valid: report_id %d, status %d \n", report_id, report_id_status);
420c8cb324eSMilanka Ringwald     }
4214dfe6a8bSMilanka Ringwald 
4224dfe6a8bSMilanka Ringwald     if (!hid_report_size_valid(cid, report_id, report_type, report_size-pos)){
423c8cb324eSMilanka Ringwald         // TODO clarify DCT/BI-03c
4244dfe6a8bSMilanka Ringwald         return HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4254dfe6a8bSMilanka Ringwald     }
4264dfe6a8bSMilanka Ringwald     return HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
4274dfe6a8bSMilanka Ringwald }
4284dfe6a8bSMilanka Ringwald 
4298eb8d463SMatthias Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
4308eb8d463SMatthias Ringwald     UNUSED(channel);
4318eb8d463SMatthias Ringwald     UNUSED(packet_size);
4328eb8d463SMatthias Ringwald     int connected_before;
43357c643eeSMatthias Ringwald     uint16_t psm;
43457c643eeSMatthias Ringwald     uint8_t status;
435c12110e4SMilanka Ringwald     hid_device_t * device = NULL;
4366510739bSMilanka Ringwald     uint8_t param;
4376510739bSMilanka Ringwald     bd_addr_t address;
4387d26fe66SMilanka Ringwald     uint16_t local_cid;
439a1118d11SMilanka Ringwald     int pos = 0;
440ba9a58feSMilanka Ringwald     int report_size;
441a1118d11SMilanka Ringwald     uint8_t report[48];
442ca3f868cSMilanka Ringwald     hid_message_type_t message_type;
443c12110e4SMilanka Ringwald 
4448eb8d463SMatthias Ringwald     switch (packet_type){
445c12110e4SMilanka Ringwald         case L2CAP_DATA_PACKET:
4464176cbc0SMatthias Ringwald             device = hid_device_get_instance_for_l2cap_cid(channel);
447c12110e4SMilanka Ringwald             if (!device) {
448c12110e4SMilanka Ringwald                 log_error("no device with cid 0x%02x", channel);
449c12110e4SMilanka Ringwald                 return;
450c12110e4SMilanka Ringwald             }
451ca3f868cSMilanka Ringwald             message_type = (hid_message_type_t)(packet[0] >> 4);
452df3cc7a0SMilanka Ringwald             // printf("L2CAP_DATA_PACKET message_type %d, packet_size %d  \n", message_type, packet_size);
453c12110e4SMilanka Ringwald             switch (message_type){
454c12110e4SMilanka Ringwald                 case HID_MESSAGE_TYPE_GET_REPORT:
45507678896SMilanka Ringwald 
456a1118d11SMilanka Ringwald                     pos = 0;
457ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[pos++] & 0x03);
458c12110e4SMilanka Ringwald                     device->report_id = 0;
4597d26fe66SMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
460a1118d11SMilanka Ringwald                     device->state = HID_DEVICE_W2_GET_REPORT;
4617d26fe66SMilanka Ringwald 
4627d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
4637d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
464c8cb324eSMilanka Ringwald                             if (packet_size < 2){
465ba9a58feSMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
466ba9a58feSMilanka Ringwald                                 break;
467ba9a58feSMilanka Ringwald                             }
468a1118d11SMilanka Ringwald                             device->report_id = packet[pos++];
4697d26fe66SMilanka Ringwald                             break;
4707d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
471a1118d11SMilanka Ringwald                             if (!btstack_hid_report_id_declared(hid_descriptor_len, hid_descriptor)) {
472c8cb324eSMilanka Ringwald                                 if (packet_size < 2) break;
473c8cb324eSMilanka Ringwald                                 if (packet[0] & 0x08){
474c8cb324eSMilanka Ringwald                                     if (packet_size > 2) {
475c8cb324eSMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
476c8cb324eSMilanka Ringwald                                     }
477c8cb324eSMilanka Ringwald                                 } else {
478c8cb324eSMilanka Ringwald                                     if (packet_size > 1) {
479c8cb324eSMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
480c8cb324eSMilanka Ringwald                                     }
481c8cb324eSMilanka Ringwald                                 }
482a1118d11SMilanka Ringwald                                 break;
483a1118d11SMilanka Ringwald                             }
4847d26fe66SMilanka Ringwald                             if (packet_size < 2){
4857d26fe66SMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4867d26fe66SMilanka Ringwald                                 break;
4877d26fe66SMilanka Ringwald                             }
488a1118d11SMilanka Ringwald                             device->report_id = packet[pos++];
489c8cb324eSMilanka Ringwald                             break;
490c8cb324eSMilanka Ringwald                     }
491c8cb324eSMilanka Ringwald                     if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
492*e99e41f0SMatthias Ringwald                         l2cap_request_can_send_now_event(device->control_cid);
493c8cb324eSMilanka Ringwald                         break;
494c8cb324eSMilanka Ringwald                     }
495ca3f868cSMilanka Ringwald                     switch (hid_report_id_status(device->cid, device->report_id)){
496a1118d11SMilanka Ringwald                         case HID_REPORT_ID_INVALID:
497a1118d11SMilanka Ringwald                             device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
498a1118d11SMilanka Ringwald                             break;
499a1118d11SMilanka Ringwald                         default:
5007d26fe66SMilanka Ringwald                             break;
5017d26fe66SMilanka Ringwald                     }
502c8cb324eSMilanka Ringwald 
503c8cb324eSMilanka Ringwald                     device->expected_report_size = hid_get_report_size_for_id(device->cid, device->report_id, device->report_type, hid_descriptor_len, hid_descriptor);
504a1118d11SMilanka Ringwald                     report_size =  device->expected_report_size + pos; // add 1 for header size and report id
505a1118d11SMilanka Ringwald 
506a1118d11SMilanka Ringwald                     if ((packet[0] & 0x08) && packet_size >= pos + 1){
507a1118d11SMilanka Ringwald                         device->report_size = btstack_min(btstack_min(little_endian_read_16(packet, pos), report_size), sizeof(report));
508a1118d11SMilanka Ringwald                     } else {
509a1118d11SMilanka Ringwald                         device->report_size = btstack_min(btstack_min(l2cap_max_mtu(), report_size), sizeof(report));
510a1118d11SMilanka Ringwald                     }
511a1118d11SMilanka Ringwald 
512*e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
5137d26fe66SMilanka Ringwald                     break;
5147d26fe66SMilanka Ringwald 
5157d26fe66SMilanka Ringwald                 case HID_MESSAGE_TYPE_SET_REPORT:
5167d26fe66SMilanka Ringwald                     device->state = HID_DEVICE_W2_SET_REPORT;
517a1118d11SMilanka Ringwald                     device->report_size = l2cap_max_mtu();
518ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[0] & 0x03);
5197d26fe66SMilanka Ringwald                     if (packet_size < 1){
5207d26fe66SMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
5217d26fe66SMilanka Ringwald                         break;
5227d26fe66SMilanka Ringwald                     }
5237d26fe66SMilanka Ringwald 
5247d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
5257d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
526df3cc7a0SMilanka Ringwald                             // printf("HID_PROTOCOL_MODE_BOOT \n");
5277d26fe66SMilanka Ringwald                             if (packet_size < 3){
5287d26fe66SMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
5297d26fe66SMilanka Ringwald                                 break;
5307d26fe66SMilanka Ringwald                             }
5317d26fe66SMilanka Ringwald                             device->report_id = packet[1];
5324dfe6a8bSMilanka Ringwald                             device->report_status = hid_device_set_report_cmd_is_valid(device->cid, device->report_type, packet_size - 1, &packet[1]);
5334dfe6a8bSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) break;
5344dfe6a8bSMilanka Ringwald                             (*hci_device_set_report)(device->cid, device->report_type, packet_size-1, &packet[1]);
5357d26fe66SMilanka Ringwald                             break;
5367d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
537df3cc7a0SMilanka Ringwald                             // printf("HID_PROTOCOL_MODE_REPORT \n");
5384dfe6a8bSMilanka Ringwald                             device->report_status = hid_device_set_report_cmd_is_valid(device->cid, device->report_type, packet_size - 1, &packet[1]);
5394dfe6a8bSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) break;
5404dfe6a8bSMilanka Ringwald 
5417d26fe66SMilanka Ringwald                             if (packet_size >= 2){
5424dfe6a8bSMilanka Ringwald                                 (*hci_device_set_report)(device->cid, device->report_type, packet_size-1, &packet[1]);
5437d26fe66SMilanka Ringwald                             } else {
5447d26fe66SMilanka Ringwald                                 uint8_t payload[] = {0};
5454dfe6a8bSMilanka Ringwald                                 (*hci_device_set_report)(device->cid, device->report_type, 1, payload);
5467d26fe66SMilanka Ringwald                             }
5477d26fe66SMilanka Ringwald                             break;
5487d26fe66SMilanka Ringwald                     }
549ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[0] & 0x03);
550*e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
551ba9a58feSMilanka Ringwald                     break;
552ba9a58feSMilanka Ringwald                 case HID_MESSAGE_TYPE_GET_PROTOCOL:
553df3cc7a0SMilanka Ringwald                     // printf(" HID_MESSAGE_TYPE_GET_PROTOCOL\n");
554ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_W2_GET_PROTOCOL;
555ba9a58feSMilanka Ringwald                     if (packet_size != 1) {
556ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
557ba9a58feSMilanka Ringwald                         break;
558ba9a58feSMilanka Ringwald                     }
559ba9a58feSMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
560df3cc7a0SMilanka Ringwald                     // hid_device_request_can_send_now_event(channel);
561df3cc7a0SMilanka Ringwald                     // printf("HID_MESSAGE_TYPE_GET_PROTOCOL l2cap_request_can_send_now_event\n");
562df3cc7a0SMilanka Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
5637d26fe66SMilanka Ringwald                     break;
564ba9a58feSMilanka Ringwald 
565ba9a58feSMilanka Ringwald                 case HID_MESSAGE_TYPE_SET_PROTOCOL:
566ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_W2_SET_PROTOCOL;
567ba9a58feSMilanka Ringwald                     if (packet_size != 1) {
568ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
569ba9a58feSMilanka Ringwald                         break;
570ba9a58feSMilanka Ringwald                     }
571ba9a58feSMilanka Ringwald                     param = packet[0] & 0x01;
572ba9a58feSMilanka Ringwald                     if (param == HID_PROTOCOL_MODE_BOOT && !hid_boot_protocol_mode_supported){
573ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
574ba9a58feSMilanka Ringwald                         break;
575ba9a58feSMilanka Ringwald                     }
576ca3f868cSMilanka Ringwald                     device->protocol_mode = (hid_protocol_mode_t) param;
5777d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
5787d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
579df3cc7a0SMilanka Ringwald                             // printf("Set protocol mode to BOOT\n");
580ba9a58feSMilanka Ringwald                             break;
5817d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
582df3cc7a0SMilanka Ringwald                             // printf("Set protocol mode to REPORT\n");
5837d26fe66SMilanka Ringwald                             break;
5847d26fe66SMilanka Ringwald                     }
5857d26fe66SMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
586*e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
5877d26fe66SMilanka Ringwald                     break;
5887d26fe66SMilanka Ringwald 
5896510739bSMilanka Ringwald                 case HID_MESSAGE_TYPE_HID_CONTROL:
5906510739bSMilanka Ringwald                     param = packet[0] & 0x0F;
5916510739bSMilanka Ringwald                     switch (param){
5926510739bSMilanka Ringwald                         case HID_CONTROL_PARAM_SUSPEND:
5936510739bSMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_SUSPEND);
5946510739bSMilanka Ringwald                             break;
5956510739bSMilanka Ringwald                         case HID_CONTROL_PARAM_EXIT_SUSPEND:
5966510739bSMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_EXIT_SUSPEND);
5976510739bSMilanka Ringwald                             break;
5986510739bSMilanka Ringwald                         default:
5996510739bSMilanka Ringwald                             device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
600*e99e41f0SMatthias Ringwald                             l2cap_request_can_send_now_event(device->control_cid);
6016510739bSMilanka Ringwald                             break;
6026510739bSMilanka Ringwald                     }
6036510739bSMilanka Ringwald                     break;
6047d26fe66SMilanka Ringwald 
6057d26fe66SMilanka Ringwald                 case HID_MESSAGE_TYPE_DATA:
6067d26fe66SMilanka Ringwald                     if (packet_size < 2) {
6077d26fe66SMilanka Ringwald                         break;
6087d26fe66SMilanka Ringwald                     }
60907678896SMilanka Ringwald                     pos = 0;
610ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[pos++] & 0x03);
61107678896SMilanka Ringwald                     device->report_id = 0;
61207678896SMilanka Ringwald                     if (btstack_hid_report_id_declared(hid_descriptor_len, hid_descriptor)){
61307678896SMilanka Ringwald                         device->report_id = packet[pos++];
614738c9391SMilanka Ringwald                     }
61507678896SMilanka Ringwald 
61607678896SMilanka Ringwald                     if (hid_report_id_status(device->cid, device->report_id) == HID_REPORT_ID_INVALID){
61707678896SMilanka Ringwald                         log_info("Ignore invalid report data packet");
61807678896SMilanka Ringwald                         break;
61907678896SMilanka Ringwald                     }
62007678896SMilanka Ringwald                     if (!hid_report_size_valid(device->cid, device->report_id, device->report_type, packet_size - pos)){
62107678896SMilanka Ringwald                         log_info("Ignore invalid report data packet, invalid size");
62207678896SMilanka Ringwald                         break;
62307678896SMilanka Ringwald                     }
62407678896SMilanka Ringwald                     (*hci_device_report_data)(device->cid, device->report_type, device->report_id, packet_size - pos, &packet[pos]);
6257d26fe66SMilanka Ringwald                     break;
626c12110e4SMilanka Ringwald                 default:
627c12110e4SMilanka Ringwald                     device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
628*e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
629c12110e4SMilanka Ringwald                     break;
630c12110e4SMilanka Ringwald             }
631c12110e4SMilanka Ringwald             break;
6328eb8d463SMatthias Ringwald         case HCI_EVENT_PACKET:
6338eb8d463SMatthias Ringwald             switch (packet[0]){
6348eb8d463SMatthias Ringwald                 case L2CAP_EVENT_INCOMING_CONNECTION:
6358eb8d463SMatthias Ringwald                     switch (l2cap_event_incoming_connection_get_psm(packet)){
6368eb8d463SMatthias Ringwald                         case PSM_HID_CONTROL:
6378eb8d463SMatthias Ringwald                         case PSM_HID_INTERRUPT:
6386510739bSMilanka Ringwald                             l2cap_event_incoming_connection_get_address(packet, address);
63972e3f392SMatthias Ringwald                             device = hid_device_provide_instance_for_bd_addr(address);
640c12110e4SMilanka Ringwald                             if (!device) {
6413da09877SMatthias Ringwald                                 log_error("L2CAP_EVENT_INCOMING_CONNECTION, cannot create instance for %s", bd_addr_to_str(address));
642c12110e4SMilanka Ringwald                                 l2cap_decline_connection(channel);
643c12110e4SMilanka Ringwald                                 break;
644c12110e4SMilanka Ringwald                             }
64572e3f392SMatthias Ringwald                             if (device->con_handle == HCI_CON_HANDLE_INVALID || l2cap_event_incoming_connection_get_handle(packet) == device->con_handle){
646c12110e4SMilanka Ringwald                                 device->con_handle = l2cap_event_incoming_connection_get_handle(packet);
647c12110e4SMilanka Ringwald                                 device->incoming = 1;
6486510739bSMilanka Ringwald                                 l2cap_event_incoming_connection_get_address(packet, device->bd_addr);
6493da09877SMatthias Ringwald                                 psm = l2cap_event_incoming_connection_get_psm(packet);
6503da09877SMatthias Ringwald                                 switch (psm){
6513da09877SMatthias Ringwald                                     case PSM_HID_CONTROL:
6523da09877SMatthias Ringwald                                         device->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
6533da09877SMatthias Ringwald                                         break;
6543da09877SMatthias Ringwald                                     case PSM_HID_INTERRUPT:
6553da09877SMatthias Ringwald                                         device->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
6563da09877SMatthias Ringwald                                     break;
6573da09877SMatthias Ringwald                                     default:
6583da09877SMatthias Ringwald                                         break;
6593da09877SMatthias Ringwald                                 }
6603da09877SMatthias Ringwald 
6618eb8d463SMatthias Ringwald                                 l2cap_accept_connection(channel);
6628eb8d463SMatthias Ringwald                             } else {
6638eb8d463SMatthias Ringwald                                 l2cap_decline_connection(channel);
6643da09877SMatthias Ringwald                                 log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s", bd_addr_to_str(address));
6658eb8d463SMatthias Ringwald                             }
6668eb8d463SMatthias Ringwald                             break;
6678eb8d463SMatthias Ringwald                         default:
6688eb8d463SMatthias Ringwald                             l2cap_decline_connection(channel);
6698eb8d463SMatthias Ringwald                             break;
6708eb8d463SMatthias Ringwald                     }
6718eb8d463SMatthias Ringwald                     break;
6728eb8d463SMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_OPENED:
6733da09877SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(l2cap_event_channel_opened_get_local_cid(packet));
674c12110e4SMilanka Ringwald                     if (!device) {
675c12110e4SMilanka Ringwald                         log_error("L2CAP_EVENT_CHANNEL_OPENED, no hid device for local cid 0x%02x", l2cap_event_channel_opened_get_local_cid(packet));
676c12110e4SMilanka Ringwald                         return;
677c12110e4SMilanka Ringwald                     }
67857c643eeSMatthias Ringwald                     status = l2cap_event_channel_opened_get_status(packet);
67957c643eeSMatthias Ringwald                     if (status) {
680c12110e4SMilanka Ringwald                         if (device->incoming == 0){
68157c643eeSMatthias Ringwald                             // report error for outgoing connection
682c12110e4SMilanka Ringwald                             hid_device_emit_connected_event(device, status);
68357c643eeSMatthias Ringwald                         }
68457c643eeSMatthias Ringwald                         return;
68557c643eeSMatthias Ringwald                     }
686641577b2SMatthias Ringwald 
687641577b2SMatthias Ringwald                     // store con_handle
688641577b2SMatthias Ringwald                     if (device->con_handle == HCI_CON_HANDLE_INVALID){
689641577b2SMatthias Ringwald                         device->con_handle  = l2cap_event_channel_opened_get_handle(packet);
690641577b2SMatthias Ringwald                     }
691641577b2SMatthias Ringwald 
692641577b2SMatthias Ringwald                     // store l2cap cid
69357c643eeSMatthias Ringwald                     psm = l2cap_event_channel_opened_get_psm(packet);
69457c643eeSMatthias Ringwald                     switch (psm){
6958eb8d463SMatthias Ringwald                         case PSM_HID_CONTROL:
696c12110e4SMilanka Ringwald                             device->control_cid = l2cap_event_channel_opened_get_local_cid(packet);
6978eb8d463SMatthias Ringwald                             break;
6988eb8d463SMatthias Ringwald                         case PSM_HID_INTERRUPT:
699c12110e4SMilanka Ringwald                             device->interrupt_cid = l2cap_event_channel_opened_get_local_cid(packet);
7008eb8d463SMatthias Ringwald                             break;
7018eb8d463SMatthias Ringwald                         default:
7028eb8d463SMatthias Ringwald                             break;
7038eb8d463SMatthias Ringwald                     }
7046510739bSMilanka Ringwald 
70557c643eeSMatthias Ringwald                     // connect HID Interrupt for outgoing
706c12110e4SMilanka Ringwald                     if (device->incoming == 0 && psm == PSM_HID_CONTROL){
707df3cc7a0SMilanka Ringwald                         // printf("Create outgoing HID Interrupt\n");
708c12110e4SMilanka Ringwald                         status = l2cap_create_channel(packet_handler, device->bd_addr, PSM_HID_INTERRUPT, 48, &device->interrupt_cid);
70957c643eeSMatthias Ringwald                         break;
71057c643eeSMatthias Ringwald                     }
711641577b2SMatthias Ringwald 
712641577b2SMatthias Ringwald                     // emit connected if both channels are open
713641577b2SMatthias Ringwald                     connected_before = device->connected;
714c12110e4SMilanka Ringwald                     if (!connected_before && device->control_cid && device->interrupt_cid){
715c12110e4SMilanka Ringwald                         device->connected = 1;
716c12110e4SMilanka Ringwald                         hid_device_emit_connected_event(device, 0);
7178eb8d463SMatthias Ringwald                     }
7188eb8d463SMatthias Ringwald                     break;
7198eb8d463SMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_CLOSED:
7204176cbc0SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(l2cap_event_channel_closed_get_local_cid(packet));
721c12110e4SMilanka Ringwald                     if (!device) return;
722c12110e4SMilanka Ringwald 
7236510739bSMilanka Ringwald                     // connected_before = device->connected;
724c12110e4SMilanka Ringwald                     device->incoming  = 0;
725c12110e4SMilanka Ringwald                     if (l2cap_event_channel_closed_get_local_cid(packet) == device->interrupt_cid){
726c12110e4SMilanka Ringwald                         device->interrupt_cid = 0;
72757c18996SMilanka Ringwald                     }
72857c18996SMilanka Ringwald                     if (l2cap_event_channel_closed_get_local_cid(packet) == device->control_cid){
72957c18996SMilanka Ringwald                         device->control_cid = 0;
7308eb8d463SMatthias Ringwald                     }
7316510739bSMilanka Ringwald                     if (!device->interrupt_cid && !device->control_cid){
7326510739bSMilanka Ringwald                         device->connected = 0;
7333da09877SMatthias Ringwald                         device->con_handle = HCI_CON_HANDLE_INVALID;
7346510739bSMilanka Ringwald                         device->cid = 0;
735c12110e4SMilanka Ringwald                         hid_device_emit_connection_closed_event(device);
7368eb8d463SMatthias Ringwald                     }
7378eb8d463SMatthias Ringwald                     break;
738c12110e4SMilanka Ringwald 
73957c18996SMilanka Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
7407d26fe66SMilanka Ringwald                     local_cid = l2cap_event_can_send_now_get_local_cid(packet);
7414176cbc0SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(local_cid);
7427d26fe66SMilanka Ringwald 
743c12110e4SMilanka Ringwald                     if (!device) return;
744c12110e4SMilanka Ringwald                     switch (device->state){
745a1118d11SMilanka Ringwald                         case HID_DEVICE_W2_GET_REPORT:{
7463da09877SMatthias Ringwald                             // printf("HID_DEVICE_W2_GET_REPORT. on entry device->report_status %d \n", device->report_status);
7477d26fe66SMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) {
7487d26fe66SMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
7497d26fe66SMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
7507d26fe66SMilanka Ringwald                                 break;
7517d26fe66SMilanka Ringwald                             }
752a1118d11SMilanka Ringwald 
753a1118d11SMilanka Ringwald                             pos = 0;
754a1118d11SMilanka Ringwald                             report[pos++] = (HID_MESSAGE_TYPE_DATA << 4) | device->report_type;
755a1118d11SMilanka Ringwald                             if (device->report_id){
756a1118d11SMilanka Ringwald                                 report[pos++] = device->report_id;
757a1118d11SMilanka Ringwald                             }
7583da09877SMatthias Ringwald                             // printf(" report size with header and id %d\n", pos);
759a1118d11SMilanka Ringwald 
7607d26fe66SMilanka Ringwald                             report_size = 0;
761a1118d11SMilanka Ringwald                             status = (*hci_device_get_report)(device->cid, device->report_type, device->report_id, &report_size, &report[pos]);
7623da09877SMatthias Ringwald                             // printf(" report size %d, status after callback %d, expected report_size %d\n", report_size + pos, status, device->report_size + pos);
763481c7cdcSMilanka Ringwald 
764a1118d11SMilanka Ringwald                             switch (status){
765a1118d11SMilanka Ringwald                                 case 0:
766a1118d11SMilanka Ringwald                                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_NOT_READY;
767a1118d11SMilanka Ringwald                                     break;
768a1118d11SMilanka Ringwald                                 case 1:
769a1118d11SMilanka Ringwald                                     if (report_size == 0){
770a1118d11SMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
771a1118d11SMilanka Ringwald                                         break;
772a1118d11SMilanka Ringwald                                     }
773a1118d11SMilanka Ringwald                                     if (device->expected_report_size != report_size){
774a1118d11SMilanka Ringwald                                         log_error("Expected report size of %d bytes, received %d", device->expected_report_size, report_size);
775a1118d11SMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
776a1118d11SMilanka Ringwald                                         break;
777a1118d11SMilanka Ringwald                                     }
778a1118d11SMilanka Ringwald                                     break;
779a1118d11SMilanka Ringwald                                 default:
780a1118d11SMilanka Ringwald                                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
781a1118d11SMilanka Ringwald                                     break;
782a1118d11SMilanka Ringwald                             }
783ba9a58feSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
784ba9a58feSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
785481c7cdcSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
786481c7cdcSMilanka Ringwald                                 break;
787481c7cdcSMilanka Ringwald                             }
788481c7cdcSMilanka Ringwald 
789a1118d11SMilanka Ringwald                             // if (report_size > l2cap_max_mtu()){
790a1118d11SMilanka Ringwald                             //     report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
791a1118d11SMilanka Ringwald                             //     hid_device_send_control_message(device->cid, &report[0], 1);
792a1118d11SMilanka Ringwald                             //     break;
793a1118d11SMilanka Ringwald                             // }
794481c7cdcSMilanka Ringwald 
795a1118d11SMilanka Ringwald                             // printf("report type %d, report_size %d, report_size %d \n", device->report_type, report_size, device->report_size);
796a1118d11SMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], device->report_size);
797481c7cdcSMilanka Ringwald                             //     device->state = HID_DEVICE_IDLE;
798c12110e4SMilanka Ringwald                             break;
799a1118d11SMilanka Ringwald                         }
800ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SET_REPORT:
801ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SET_PROTOCOL:
802ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
8036510739bSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
804ba9a58feSMilanka Ringwald                             break;
805ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_GET_PROTOCOL:
806ba9a58feSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
807df3cc7a0SMilanka Ringwald                                 // printf("send HID_MESSAGE_TYPE_HANDSHAKE, report_status %d \n", device->report_status);
808ba9a58feSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
809ba9a58feSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
810ba9a58feSMilanka Ringwald                                 break;
811ba9a58feSMilanka Ringwald                             }
8125139b81eSMilanka Ringwald 
813df3cc7a0SMilanka Ringwald                             // printf("send HID_MESSAGE_TYPE_DATA, protocol_mode %d \n", device->protocol_mode);
8145139b81eSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_DATA << 4);
8155139b81eSMilanka Ringwald                             report[1] =  device->protocol_mode;
8165139b81eSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 2);
817ba9a58feSMilanka Ringwald                             break;
818ba9a58feSMilanka Ringwald 
819ba9a58feSMilanka Ringwald 
820ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST:
821ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
822ba9a58feSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
823c12110e4SMilanka Ringwald                             break;
824c12110e4SMilanka Ringwald                         default:
825*e99e41f0SMatthias Ringwald                             if (device->user_request_can_send_now){
826*e99e41f0SMatthias Ringwald                                 device->user_request_can_send_now = 0;
827c12110e4SMilanka Ringwald                                 hid_device_emit_can_send_now_event(device);
828*e99e41f0SMatthias Ringwald                             }
829c12110e4SMilanka Ringwald                             break;
830c12110e4SMilanka Ringwald                     }
831ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_IDLE;
8328eb8d463SMatthias Ringwald                     break;
8338eb8d463SMatthias Ringwald                 default:
8348eb8d463SMatthias Ringwald                     break;
8358eb8d463SMatthias Ringwald             }
8368eb8d463SMatthias Ringwald             break;
8378eb8d463SMatthias Ringwald         default:
8388eb8d463SMatthias Ringwald             break;
8398eb8d463SMatthias Ringwald     }
8408eb8d463SMatthias Ringwald }
8418eb8d463SMatthias Ringwald 
8428eb8d463SMatthias Ringwald /**
8438eb8d463SMatthias Ringwald  * @brief Set up HID Device
8448eb8d463SMatthias Ringwald  */
845662cddc2SMilanka Ringwald void hid_device_init(uint8_t boot_protocol_mode_supported, uint16_t descriptor_len, const uint8_t * descriptor){
846ba9a58feSMilanka Ringwald     hid_boot_protocol_mode_supported = boot_protocol_mode_supported;
847662cddc2SMilanka Ringwald     hid_descriptor =  descriptor;
848662cddc2SMilanka Ringwald     hid_descriptor_len = descriptor_len;
849a1118d11SMilanka Ringwald     hci_device_get_report = dummy_write_report;
850a1118d11SMilanka Ringwald     hci_device_set_report = dummy_set_report;
851a1118d11SMilanka Ringwald     hci_device_report_data = dummy_report_data;
852a1118d11SMilanka Ringwald 
85345a58b30SMatthias Ringwald     l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, LEVEL_2);
85445a58b30SMatthias Ringwald     l2cap_register_service(packet_handler, PSM_HID_CONTROL,   100, LEVEL_2);
8558eb8d463SMatthias Ringwald }
8568eb8d463SMatthias Ringwald 
8578eb8d463SMatthias Ringwald /**
8588eb8d463SMatthias Ringwald  * @brief Register callback for the HID Device client.
8598eb8d463SMatthias Ringwald  * @param callback
8608eb8d463SMatthias Ringwald  */
8618eb8d463SMatthias Ringwald void hid_device_register_packet_handler(btstack_packet_handler_t callback){
8628eb8d463SMatthias Ringwald     hid_callback = callback;
8638eb8d463SMatthias Ringwald }
8648eb8d463SMatthias Ringwald 
865c12110e4SMilanka Ringwald 
866c12110e4SMilanka Ringwald 
867a1118d11SMilanka 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)){
868c12110e4SMilanka Ringwald     if (callback == NULL){
869c12110e4SMilanka Ringwald         callback = dummy_write_report;
870c12110e4SMilanka Ringwald     }
871a1118d11SMilanka Ringwald     hci_device_get_report = callback;
872c12110e4SMilanka Ringwald }
873c12110e4SMilanka Ringwald 
8744dfe6a8bSMilanka 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)){
875ba9a58feSMilanka Ringwald     if (callback == NULL){
876ba9a58feSMilanka Ringwald         callback = dummy_set_report;
877ba9a58feSMilanka Ringwald     }
878ba9a58feSMilanka Ringwald     hci_device_set_report = callback;
879ba9a58feSMilanka Ringwald }
880c12110e4SMilanka Ringwald 
8817d26fe66SMilanka 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)){
8827d26fe66SMilanka Ringwald     if (callback == NULL){
8837d26fe66SMilanka Ringwald         callback = dummy_report_data;
8847d26fe66SMilanka Ringwald     }
8857d26fe66SMilanka Ringwald     hci_device_report_data = callback;
8867d26fe66SMilanka Ringwald }
8877d26fe66SMilanka Ringwald 
8887d26fe66SMilanka Ringwald 
8898eb8d463SMatthias Ringwald /**
8908eb8d463SMatthias Ringwald  * @brief Request can send now event to send HID Report
8918eb8d463SMatthias Ringwald  * @param hid_cid
8928eb8d463SMatthias Ringwald  */
8938eb8d463SMatthias Ringwald void hid_device_request_can_send_now_event(uint16_t hid_cid){
8944176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
895*e99e41f0SMatthias Ringwald     if (!hid_device || !hid_device->interrupt_cid) return;
896*e99e41f0SMatthias Ringwald     hid_device->user_request_can_send_now = 1;
897*e99e41f0SMatthias Ringwald     l2cap_request_can_send_now_event(hid_device->interrupt_cid);
8988eb8d463SMatthias Ringwald }
8998eb8d463SMatthias Ringwald 
9008eb8d463SMatthias Ringwald /**
9018eb8d463SMatthias Ringwald  * @brief Send HID messageon interrupt channel
9028eb8d463SMatthias Ringwald  * @param hid_cid
9038eb8d463SMatthias Ringwald  */
9048eb8d463SMatthias Ringwald void hid_device_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
9054176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
906c12110e4SMilanka Ringwald     if (!hid_device || !hid_device->interrupt_cid) return;
9078eb8d463SMatthias Ringwald     l2cap_send(hid_device->interrupt_cid, (uint8_t*) message, message_len);
908*e99e41f0SMatthias Ringwald     // request user can send now if pending
909*e99e41f0SMatthias Ringwald     if (hid_device->interrupt_cid){
910*e99e41f0SMatthias Ringwald         l2cap_request_can_send_now_event((hid_device->interrupt_cid));
911*e99e41f0SMatthias Ringwald     }
9128eb8d463SMatthias Ringwald }
9138eb8d463SMatthias Ringwald 
9148eb8d463SMatthias Ringwald /**
9158eb8d463SMatthias Ringwald  * @brief Send HID messageon control channel
9168eb8d463SMatthias Ringwald  * @param hid_cid
9178eb8d463SMatthias Ringwald  */
918c12110e4SMilanka Ringwald void hid_device_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
9194176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
920c12110e4SMilanka Ringwald     if (!hid_device || !hid_device->control_cid) return;
9218eb8d463SMatthias Ringwald     l2cap_send(hid_device->control_cid, (uint8_t*) message, message_len);
922*e99e41f0SMatthias Ringwald     // request user can send now if pending
923*e99e41f0SMatthias Ringwald     if (hid_device->user_request_can_send_now){
924*e99e41f0SMatthias Ringwald         l2cap_request_can_send_now_event((hid_device->control_cid));
925*e99e41f0SMatthias Ringwald     }
9268eb8d463SMatthias Ringwald }
9278eb8d463SMatthias Ringwald 
92857c643eeSMatthias Ringwald /*
92957c643eeSMatthias Ringwald  * @brief Create HID connection to HID Host
93057c643eeSMatthias Ringwald  * @param addr
93157c643eeSMatthias Ringwald  * @param hid_cid to use for other commands
93257c643eeSMatthias Ringwald  * @result status
93357c643eeSMatthias Ringwald  */
93457c643eeSMatthias Ringwald uint8_t hid_device_connect(bd_addr_t addr, uint16_t * hid_cid){
935c12110e4SMilanka Ringwald     hid_device_t * hid_device = hid_device_create_instance();
936c12110e4SMilanka Ringwald     if (!hid_device){
937c12110e4SMilanka Ringwald         log_error("hid_device_connect: could not create a hid device instace");
938c12110e4SMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
939c12110e4SMilanka Ringwald     }
94057c643eeSMatthias Ringwald     // assign hic_cid
941c12110e4SMilanka Ringwald     *hid_cid = hid_device_get_next_cid();
94272e3f392SMatthias Ringwald 
94357c643eeSMatthias Ringwald     // store address
94457c643eeSMatthias Ringwald     memcpy(hid_device->bd_addr, addr, 6);
94557c643eeSMatthias Ringwald 
94657c643eeSMatthias Ringwald     // reset state
9476510739bSMilanka Ringwald     hid_device->cid           = *hid_cid;
94857c643eeSMatthias Ringwald     hid_device->incoming      = 0;
94957c643eeSMatthias Ringwald     hid_device->connected     = 0;
95057c643eeSMatthias Ringwald     hid_device->control_cid   = 0;
95157c643eeSMatthias Ringwald     hid_device->interrupt_cid = 0;
95272e3f392SMatthias Ringwald     hid_device->con_handle    = HCI_CON_HANDLE_INVALID;
95372e3f392SMatthias Ringwald 
95457c643eeSMatthias Ringwald     // create l2cap control using fixed HID L2CAP PSM
95557c643eeSMatthias Ringwald     log_info("Create outgoing HID Control");
95657c643eeSMatthias Ringwald     uint8_t status = l2cap_create_channel(packet_handler, hid_device->bd_addr, PSM_HID_CONTROL, 48, &hid_device->control_cid);
95757c643eeSMatthias Ringwald     return status;
95857c643eeSMatthias Ringwald }
9596510739bSMilanka Ringwald 
9606510739bSMilanka Ringwald /*
9616510739bSMilanka Ringwald  * @brief Disconnect from HID Host
9626510739bSMilanka Ringwald  * @param hid_cid
9636510739bSMilanka Ringwald  * @result status
9646510739bSMilanka Ringwald  */
96557c18996SMilanka Ringwald void hid_device_disconnect_interrupt_channel(uint16_t hid_cid){
9664176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
96757c18996SMilanka Ringwald     if (!hid_device){
96857c18996SMilanka Ringwald         log_error("hid_device_disconnect_interrupt_channel: could not find hid device instace");
96957c18996SMilanka Ringwald         return;
97057c18996SMilanka Ringwald     }
97157c18996SMilanka Ringwald     log_info("Disconnect from interrupt channel HID Host");
97257c18996SMilanka Ringwald     if (hid_device->interrupt_cid){
97357c18996SMilanka Ringwald         l2cap_disconnect(hid_device->interrupt_cid, 0);  // reason isn't used
97457c18996SMilanka Ringwald     }
97557c18996SMilanka Ringwald }
97657c18996SMilanka Ringwald 
97757c18996SMilanka Ringwald void hid_device_disconnect_control_channel(uint16_t hid_cid){
9784176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
97957c18996SMilanka Ringwald     if (!hid_device){
98057c18996SMilanka Ringwald         log_error("hid_device_disconnect_control_channel: could not find hid device instace");
98157c18996SMilanka Ringwald         return;
98257c18996SMilanka Ringwald     }
98357c18996SMilanka Ringwald     log_info("Disconnect from control channel HID Host");
98457c18996SMilanka Ringwald     if (hid_device->control_cid){
98557c18996SMilanka Ringwald         l2cap_disconnect(hid_device->control_cid, 0);  // reason isn't used
98657c18996SMilanka Ringwald     }
98757c18996SMilanka Ringwald }
98857c18996SMilanka Ringwald 
9896510739bSMilanka Ringwald void hid_device_disconnect(uint16_t hid_cid){
9904176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
9916510739bSMilanka Ringwald     if (!hid_device){
9926510739bSMilanka Ringwald         log_error("hid_device_disconnect: could not find hid device instace");
9936510739bSMilanka Ringwald         return;
9946510739bSMilanka Ringwald     }
9956510739bSMilanka Ringwald     log_info("Disconnect from HID Host");
9966510739bSMilanka Ringwald     if (hid_device->interrupt_cid){
9976510739bSMilanka Ringwald         l2cap_disconnect(hid_device->interrupt_cid, 0);  // reason isn't used
9986510739bSMilanka Ringwald     }
99957c18996SMilanka Ringwald     if (hid_device->control_cid){
100057c18996SMilanka Ringwald         l2cap_disconnect(hid_device->control_cid, 0); // reason isn't used
100157c18996SMilanka Ringwald     }
10026510739bSMilanka Ringwald }
10037d26fe66SMilanka Ringwald 
10047d26fe66SMilanka Ringwald int hid_device_in_boot_protocol_mode(uint16_t hid_cid){
10054176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
10067d26fe66SMilanka Ringwald     if (!hid_device){
10077d26fe66SMilanka Ringwald         log_error("hid_device_in_boot_protocol_mode: could not find hid device instace");
10087d26fe66SMilanka Ringwald         return 0;
10097d26fe66SMilanka Ringwald     }
10107d26fe66SMilanka Ringwald     return hid_device->protocol_mode == HID_PROTOCOL_MODE_BOOT;
10117d26fe66SMilanka Ringwald }
1012