xref: /btstack/src/classic/hid_device.c (revision 80d9d5d49ade6b79ca044fefc9ed01965398a2f7)
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 
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 
52c12110e4SMilanka Ringwald typedef enum {
53c12110e4SMilanka Ringwald     HID_DEVICE_IDLE,
54c12110e4SMilanka Ringwald     HID_DEVICE_CONNECTED,
55ba9a58feSMilanka Ringwald     HID_DEVICE_W2_GET_REPORT,
56ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SET_REPORT,
57ba9a58feSMilanka Ringwald     HID_DEVICE_W2_GET_PROTOCOL,
58ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SET_PROTOCOL,
59ba9a58feSMilanka Ringwald     HID_DEVICE_W2_ANSWER_SET_PROTOCOL,
60ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST,
61c12110e4SMilanka Ringwald } hid_device_state_t;
62c12110e4SMilanka Ringwald 
638eb8d463SMatthias Ringwald // hid device state
648eb8d463SMatthias Ringwald typedef struct hid_device {
658eb8d463SMatthias Ringwald     uint16_t  cid;
668eb8d463SMatthias Ringwald     bd_addr_t bd_addr;
678eb8d463SMatthias Ringwald     hci_con_handle_t con_handle;
688eb8d463SMatthias Ringwald     uint16_t  control_cid;
698eb8d463SMatthias Ringwald     uint16_t  interrupt_cid;
708eb8d463SMatthias Ringwald     uint8_t   incoming;
7157c643eeSMatthias Ringwald     uint8_t   connected;
72c12110e4SMilanka Ringwald     hid_device_state_t state;
73c12110e4SMilanka Ringwald     hid_report_type_t report_type;
74c12110e4SMilanka Ringwald     uint16_t  report_id;
75a1118d11SMilanka Ringwald     uint16_t  expected_report_size;
76a1118d11SMilanka Ringwald     uint16_t  report_size;
77e99e41f0SMatthias Ringwald     uint8_t   user_request_can_send_now;
78ba9a58feSMilanka Ringwald 
79bc967e00SMilanka Ringwald     hid_handshake_param_type_t report_status;
80ba9a58feSMilanka Ringwald     hid_protocol_mode_t protocol_mode;
818eb8d463SMatthias Ringwald } hid_device_t;
828eb8d463SMatthias Ringwald 
838eb8d463SMatthias Ringwald static hid_device_t _hid_device;
8451b27067SMilanka Ringwald static bool hid_boot_protocol_mode_supported;
85662cddc2SMilanka Ringwald static const uint8_t * hid_descriptor;
86662cddc2SMilanka Ringwald static uint16_t hid_descriptor_len;
87c12110e4SMilanka Ringwald 
88a1118d11SMilanka 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){
89c12110e4SMilanka Ringwald     UNUSED(hid_cid);
90c12110e4SMilanka Ringwald     UNUSED(report_type);
91c12110e4SMilanka Ringwald     UNUSED(report_id);
92c12110e4SMilanka Ringwald     UNUSED(out_report_size);
93c12110e4SMilanka Ringwald     UNUSED(out_report);
94a1118d11SMilanka Ringwald     return -1;
95c12110e4SMilanka Ringwald }
964dfe6a8bSMilanka Ringwald 
974dfe6a8bSMilanka Ringwald static void dummy_set_report(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report){
98ba9a58feSMilanka Ringwald     UNUSED(hid_cid);
99ba9a58feSMilanka Ringwald     UNUSED(report_type);
100ba9a58feSMilanka Ringwald     UNUSED(report_size);
101ba9a58feSMilanka Ringwald     UNUSED(report);
102ba9a58feSMilanka Ringwald }
1034dfe6a8bSMilanka Ringwald 
1047d26fe66SMilanka 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){
1057d26fe66SMilanka Ringwald     UNUSED(hid_cid);
1067d26fe66SMilanka Ringwald     UNUSED(report_type);
1077d26fe66SMilanka Ringwald     UNUSED(report_id);
1087d26fe66SMilanka Ringwald     UNUSED(report_size);
1097d26fe66SMilanka Ringwald     UNUSED(report);
1107d26fe66SMilanka Ringwald }
1117d26fe66SMilanka Ringwald 
112a1118d11SMilanka 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;
1134dfe6a8bSMilanka 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;
1147d26fe66SMilanka 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;
1158eb8d463SMatthias Ringwald 
1168eb8d463SMatthias Ringwald static btstack_packet_handler_t hid_callback;
117d40c9ac6SMatthias Ringwald 
118c12110e4SMilanka Ringwald static uint16_t hid_device_cid = 0;
119c12110e4SMilanka Ringwald 
120c12110e4SMilanka Ringwald static uint16_t hid_device_get_next_cid(void){
121c12110e4SMilanka Ringwald     hid_device_cid++;
122c12110e4SMilanka Ringwald     if (!hid_device_cid){
123c12110e4SMilanka Ringwald         hid_device_cid = 1;
124c12110e4SMilanka Ringwald     }
125c12110e4SMilanka Ringwald     return hid_device_cid;
126c12110e4SMilanka Ringwald }
127c12110e4SMilanka Ringwald 
128c12110e4SMilanka Ringwald // TODO: store hid device connection into list
1294176cbc0SMatthias Ringwald static hid_device_t * hid_device_get_instance_for_l2cap_cid(uint16_t cid){
130c1ab6cc1SMatthias Ringwald     if ((_hid_device.control_cid == cid) || (_hid_device.interrupt_cid == cid)){
1314176cbc0SMatthias Ringwald         return &_hid_device;
1324176cbc0SMatthias Ringwald     }
1334176cbc0SMatthias Ringwald     return NULL;
1344176cbc0SMatthias Ringwald }
1354176cbc0SMatthias Ringwald 
1364176cbc0SMatthias Ringwald static hid_device_t * hid_device_get_instance_for_hid_cid(uint16_t hid_cid){
1374176cbc0SMatthias Ringwald     if (_hid_device.cid == hid_cid){
138c12110e4SMilanka Ringwald         return &_hid_device;
139c12110e4SMilanka Ringwald     }
140c12110e4SMilanka Ringwald     return NULL;
141c12110e4SMilanka Ringwald }
142c12110e4SMilanka Ringwald 
14372e3f392SMatthias Ringwald static hid_device_t * hid_device_provide_instance_for_bd_addr(bd_addr_t bd_addr){
1446510739bSMilanka Ringwald     if (!_hid_device.cid){
1456535961aSMatthias Ringwald         (void)memcpy(_hid_device.bd_addr, bd_addr, 6);
1466510739bSMilanka Ringwald         _hid_device.cid = hid_device_get_next_cid();
147ba9a58feSMilanka Ringwald         _hid_device.protocol_mode = HID_PROTOCOL_MODE_REPORT;
14872e3f392SMatthias Ringwald         _hid_device.con_handle = HCI_CON_HANDLE_INVALID;
1496510739bSMilanka Ringwald     }
150c12110e4SMilanka Ringwald     return &_hid_device;
151c12110e4SMilanka Ringwald }
152c12110e4SMilanka Ringwald 
153c12110e4SMilanka Ringwald static hid_device_t * hid_device_create_instance(void){
154ba9a58feSMilanka Ringwald 
155c12110e4SMilanka Ringwald     return &_hid_device;
156c12110e4SMilanka Ringwald }
157c12110e4SMilanka Ringwald 
158*80d9d5d4SMilanka Ringwald void hid_create_sdp_record(uint8_t *service, uint32_t service_record_handle, const hid_sdp_record_t * params){
159d40c9ac6SMatthias Ringwald     uint8_t * attribute;
160d40c9ac6SMatthias Ringwald     de_create_sequence(service);
161d40c9ac6SMatthias Ringwald 
162d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
163d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
164d40c9ac6SMatthias Ringwald 
165d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
166d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
167d40c9ac6SMatthias Ringwald     {
168d40c9ac6SMatthias Ringwald         de_add_number(attribute,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
169d40c9ac6SMatthias Ringwald     }
170d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
171d40c9ac6SMatthias Ringwald 
172d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
173d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
174d40c9ac6SMatthias Ringwald     {
175d40c9ac6SMatthias Ringwald         uint8_t * l2cpProtocol = de_push_sequence(attribute);
176d40c9ac6SMatthias Ringwald         {
177d40c9ac6SMatthias Ringwald             de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
17884e3541eSMilanka Ringwald             de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_HID_CONTROL);
179d40c9ac6SMatthias Ringwald         }
180d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, l2cpProtocol);
181d40c9ac6SMatthias Ringwald 
182d40c9ac6SMatthias Ringwald         uint8_t * hidProtocol = de_push_sequence(attribute);
183d40c9ac6SMatthias Ringwald         {
184d40c9ac6SMatthias Ringwald             de_add_number(hidProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
185d40c9ac6SMatthias Ringwald         }
186d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidProtocol);
187d40c9ac6SMatthias Ringwald     }
188d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
189d40c9ac6SMatthias Ringwald 
190d40c9ac6SMatthias Ringwald     // TODO?
191d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
192d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
193d40c9ac6SMatthias Ringwald     {
194d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e);
195d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a);
196d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100);
197d40c9ac6SMatthias Ringwald     }
198d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
199d40c9ac6SMatthias Ringwald 
200d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS);
201d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
202d40c9ac6SMatthias Ringwald     {
203d40c9ac6SMatthias Ringwald         uint8_t * additionalDescriptorAttribute = de_push_sequence(attribute);
204d40c9ac6SMatthias Ringwald         {
205d40c9ac6SMatthias Ringwald             uint8_t * l2cpProtocol = de_push_sequence(additionalDescriptorAttribute);
206d40c9ac6SMatthias Ringwald             {
207d40c9ac6SMatthias Ringwald                 de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
20884e3541eSMilanka Ringwald                 de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_HID_INTERRUPT);
209d40c9ac6SMatthias Ringwald             }
210d40c9ac6SMatthias Ringwald             de_pop_sequence(additionalDescriptorAttribute, l2cpProtocol);
211d40c9ac6SMatthias Ringwald 
2129fa0921fSMatthias Ringwald             uint8_t * hidProtocol = de_push_sequence(additionalDescriptorAttribute);
213d40c9ac6SMatthias Ringwald             {
214d40c9ac6SMatthias Ringwald                 de_add_number(hidProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
215d40c9ac6SMatthias Ringwald             }
2169fa0921fSMatthias Ringwald             de_pop_sequence(additionalDescriptorAttribute, hidProtocol);
217d40c9ac6SMatthias Ringwald         }
218d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, additionalDescriptorAttribute);
219d40c9ac6SMatthias Ringwald     }
220d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
221d40c9ac6SMatthias Ringwald 
222d40c9ac6SMatthias Ringwald     // 0x0100 "ServiceName"
223d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0100);
224*80d9d5d4SMilanka Ringwald     de_add_data(service,  DE_STRING, strlen(params->device_name), (uint8_t *) params->device_name);
225d40c9ac6SMatthias Ringwald 
226d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
227d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
228d40c9ac6SMatthias Ringwald     {
229d40c9ac6SMatthias Ringwald         uint8_t * hidProfile = de_push_sequence(attribute);
230d40c9ac6SMatthias Ringwald         {
231d40c9ac6SMatthias Ringwald             de_add_number(hidProfile,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
232d40c9ac6SMatthias Ringwald             de_add_number(hidProfile,  DE_UINT, DE_SIZE_16, 0x0101);    // Version 1.1
233d40c9ac6SMatthias Ringwald         }
234d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidProfile);
235d40c9ac6SMatthias Ringwald     }
236d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
237d40c9ac6SMatthias Ringwald 
238d40c9ac6SMatthias Ringwald     // Deprecated in v1.1.1
239d40c9ac6SMatthias Ringwald     // de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_RELEASE_NUMBER);
240d40c9ac6SMatthias Ringwald     // de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0101);
241d40c9ac6SMatthias Ringwald 
242d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_PARSER_VERSION);
243d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0111);  // v1.1.1
244d40c9ac6SMatthias Ringwald 
245d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_SUBCLASS);
246*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_8,  params->hid_device_subclass);
247d40c9ac6SMatthias Ringwald 
248d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_COUNTRY_CODE);
249*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_8,  params->hid_country_code);
250d40c9ac6SMatthias Ringwald 
251d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_VIRTUAL_CABLE);
252*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_virtual_cable);
253d40c9ac6SMatthias Ringwald 
254d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_RECONNECT_INITIATE);
255*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_reconnect_initiate);
256ffe3f1a1SMilanka Ringwald 
257d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST);
258d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
259d40c9ac6SMatthias Ringwald     {
260d40c9ac6SMatthias Ringwald         uint8_t* hidDescriptor = de_push_sequence(attribute);
261d40c9ac6SMatthias Ringwald         {
262d40c9ac6SMatthias Ringwald             de_add_number(hidDescriptor,  DE_UINT, DE_SIZE_8, 0x22);    // Report Descriptor
263*80d9d5d4SMilanka Ringwald             de_add_data(hidDescriptor,  DE_STRING, params->hid_descriptor_size, (uint8_t *) params->hid_descriptor);
264d40c9ac6SMatthias Ringwald         }
265d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidDescriptor);
266d40c9ac6SMatthias Ringwald     }
267d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
268d40c9ac6SMatthias Ringwald 
2699679ea81SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDLANGID_BASE_LIST);
2709679ea81SMatthias Ringwald     attribute = de_push_sequence(service);
2719679ea81SMatthias Ringwald     {
2729679ea81SMatthias Ringwald         uint8_t* hig_lang_base = de_push_sequence(attribute);
2739679ea81SMatthias Ringwald         {
2749679ea81SMatthias Ringwald             // see: http://www.usb.org/developers/docs/USB_LANGIDs.pdf
2759679ea81SMatthias Ringwald             de_add_number(hig_lang_base,  DE_UINT, DE_SIZE_16, 0x0409);    // HIDLANGID = English (US)
2769679ea81SMatthias Ringwald             de_add_number(hig_lang_base,  DE_UINT, DE_SIZE_16, 0x0100);    // HIDLanguageBase = 0x0100 default
2779679ea81SMatthias Ringwald         }
2789679ea81SMatthias Ringwald         de_pop_sequence(attribute, hig_lang_base);
2799679ea81SMatthias Ringwald     }
2809679ea81SMatthias Ringwald     de_pop_sequence(service, attribute);
2819679ea81SMatthias Ringwald 
282*80d9d5d4SMilanka Ringwald     // battery power
283*80d9d5d4SMilanka Ringwald 
2846510739bSMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_REMOTE_WAKE);
285*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_remote_wake ? 1 : 0);
286*80d9d5d4SMilanka Ringwald 
287*80d9d5d4SMilanka Ringwald     // supervision timeout
288*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_SUPERVISION_TIMEOUT);
289*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, params->hid_supervision_timeout);
290*80d9d5d4SMilanka Ringwald 
291*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_NORMALLY_CONNECTABLE);
292*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_normally_connectable);
2936510739bSMilanka Ringwald 
294d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_BOOT_DEVICE);
295*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  params->hid_boot_device ? 1 : 0);
296*80d9d5d4SMilanka Ringwald 
297*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MAX_LATENCY);
298*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, params->hid_ssr_host_max_latency);
299*80d9d5d4SMilanka Ringwald 
300*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MIN_TIMEOUT);
301*80d9d5d4SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, params->hid_ssr_host_min_timeout);
302d40c9ac6SMatthias Ringwald }
3038eb8d463SMatthias Ringwald 
3048eb8d463SMatthias Ringwald static inline void hid_device_emit_connected_event(hid_device_t * context, uint8_t status){
3058eb8d463SMatthias Ringwald     uint8_t event[15];
3068eb8d463SMatthias Ringwald     int pos = 0;
3078eb8d463SMatthias Ringwald     event[pos++] = HCI_EVENT_HID_META;
3088eb8d463SMatthias Ringwald     pos++;  // skip len
3098eb8d463SMatthias Ringwald     event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
3108eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->cid);
3118eb8d463SMatthias Ringwald     pos+=2;
3128eb8d463SMatthias Ringwald     event[pos++] = status;
3136510739bSMilanka Ringwald     reverse_bd_addr(context->bd_addr, &event[pos]);
3148eb8d463SMatthias Ringwald     pos += 6;
3158eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->con_handle);
3168eb8d463SMatthias Ringwald     pos += 2;
3178eb8d463SMatthias Ringwald     event[pos++] = context->incoming;
3188eb8d463SMatthias Ringwald     event[1] = pos - 2;
3198eb8d463SMatthias Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3208eb8d463SMatthias Ringwald }
3218eb8d463SMatthias Ringwald 
3226510739bSMilanka Ringwald static inline void hid_device_emit_event(hid_device_t * context, uint8_t subevent_type){
323517089d1SMilanka Ringwald     uint8_t event[5];
3246510739bSMilanka Ringwald     int pos = 0;
3256510739bSMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
3266510739bSMilanka Ringwald     pos++;  // skip len
3276510739bSMilanka Ringwald     event[pos++] = subevent_type;
3286510739bSMilanka Ringwald     little_endian_store_16(event,pos,context->cid);
3296510739bSMilanka Ringwald     pos+=2;
3306510739bSMilanka Ringwald     event[1] = pos - 2;
3316510739bSMilanka Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3326510739bSMilanka Ringwald }
3336510739bSMilanka Ringwald 
334c8cb324eSMilanka Ringwald static int hid_report_size_valid(uint16_t cid, int report_id, hid_report_type_t report_type, int report_size){
335c8cb324eSMilanka Ringwald     if (!report_size) return 0;
336c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
337c8cb324eSMilanka Ringwald         switch (report_id){
338c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
339c8cb324eSMilanka Ringwald                 if (report_size < 8) return 0;
340c8cb324eSMilanka Ringwald                 break;
341c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
342c8cb324eSMilanka Ringwald                 if (report_size < 1) return 0;
343c8cb324eSMilanka Ringwald                 break;
344c8cb324eSMilanka Ringwald             default:
345c8cb324eSMilanka Ringwald                 return 0;
346c8cb324eSMilanka Ringwald         }
347c8cb324eSMilanka Ringwald     } else {
348c8cb324eSMilanka Ringwald         int size =  btstack_hid_get_report_size_for_id(report_id, report_type, hid_descriptor_len, hid_descriptor);
349c1ab6cc1SMatthias Ringwald         if ((size == 0) || (size != report_size)) return 0;
350c8cb324eSMilanka Ringwald     }
351c8cb324eSMilanka Ringwald     return 1;
352c8cb324eSMilanka Ringwald }
353c8cb324eSMilanka Ringwald 
354a796c06fSMilanka 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){
355c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
356c8cb324eSMilanka Ringwald         switch (report_id){
357c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
358c8cb324eSMilanka Ringwald                 return 8;
359c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
360c8cb324eSMilanka Ringwald                 return 3;
361c8cb324eSMilanka Ringwald             default:
362c8cb324eSMilanka Ringwald                 return 0;
363c8cb324eSMilanka Ringwald         }
364c8cb324eSMilanka Ringwald     } else {
365a796c06fSMilanka Ringwald         return btstack_hid_get_report_size_for_id(report_id, report_type, descriptor_len, descriptor);
366c8cb324eSMilanka Ringwald     }
367c8cb324eSMilanka Ringwald }
368c8cb324eSMilanka Ringwald 
369c8cb324eSMilanka Ringwald static hid_report_id_status_t hid_report_id_status(uint16_t cid, uint16_t report_id){
370c8cb324eSMilanka Ringwald     if (hid_device_in_boot_protocol_mode(cid)){
371c8cb324eSMilanka Ringwald         switch (report_id){
372c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_KEYBOARD_ID:
373c8cb324eSMilanka Ringwald             case HID_BOOT_MODE_MOUSE_ID:
374c8cb324eSMilanka Ringwald                 return HID_REPORT_ID_VALID;
375c8cb324eSMilanka Ringwald             default:
376c8cb324eSMilanka Ringwald                 return HID_REPORT_ID_INVALID;
377c8cb324eSMilanka Ringwald         }
378c8cb324eSMilanka Ringwald     } else {
379c8cb324eSMilanka Ringwald         return btstack_hid_id_valid(report_id, hid_descriptor_len, hid_descriptor);
380c8cb324eSMilanka Ringwald     }
381c8cb324eSMilanka Ringwald }
382c8cb324eSMilanka Ringwald 
3834dfe6a8bSMilanka 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){
3844dfe6a8bSMilanka Ringwald     int pos = 0;
385c8cb324eSMilanka Ringwald     int report_id = 0;
386c8cb324eSMilanka Ringwald 
387c8cb324eSMilanka Ringwald     if (btstack_hid_report_id_declared(hid_descriptor_len, hid_descriptor)){
388c8cb324eSMilanka Ringwald         report_id = report[pos++];
3894dfe6a8bSMilanka Ringwald         hid_report_id_status_t report_id_status = hid_report_id_status(cid, report_id);
3904dfe6a8bSMilanka Ringwald         switch (report_id_status){
3914dfe6a8bSMilanka Ringwald             case HID_REPORT_ID_INVALID:
3924dfe6a8bSMilanka Ringwald                 return HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
3934dfe6a8bSMilanka Ringwald             default:
3944dfe6a8bSMilanka Ringwald                 break;
3954dfe6a8bSMilanka Ringwald         }
396c8cb324eSMilanka Ringwald     }
3974dfe6a8bSMilanka Ringwald 
3984dfe6a8bSMilanka Ringwald     if (!hid_report_size_valid(cid, report_id, report_type, report_size-pos)){
399c8cb324eSMilanka Ringwald         // TODO clarify DCT/BI-03c
4004dfe6a8bSMilanka Ringwald         return HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4014dfe6a8bSMilanka Ringwald     }
4024dfe6a8bSMilanka Ringwald     return HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
4034dfe6a8bSMilanka Ringwald }
4044dfe6a8bSMilanka Ringwald 
4058eb8d463SMatthias Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
4068eb8d463SMatthias Ringwald     UNUSED(channel);
4078eb8d463SMatthias Ringwald     UNUSED(packet_size);
4088eb8d463SMatthias Ringwald     int connected_before;
40957c643eeSMatthias Ringwald     uint16_t psm;
41057c643eeSMatthias Ringwald     uint8_t status;
411c12110e4SMilanka Ringwald     hid_device_t * device = NULL;
4126510739bSMilanka Ringwald     uint8_t param;
4136510739bSMilanka Ringwald     bd_addr_t address;
4147d26fe66SMilanka Ringwald     uint16_t local_cid;
415a1118d11SMilanka Ringwald     int pos = 0;
416ba9a58feSMilanka Ringwald     int report_size;
417a1118d11SMilanka Ringwald     uint8_t report[48];
418ca3f868cSMilanka Ringwald     hid_message_type_t message_type;
419c12110e4SMilanka Ringwald 
4208eb8d463SMatthias Ringwald     switch (packet_type){
421c12110e4SMilanka Ringwald         case L2CAP_DATA_PACKET:
4224176cbc0SMatthias Ringwald             device = hid_device_get_instance_for_l2cap_cid(channel);
423c12110e4SMilanka Ringwald             if (!device) {
424c12110e4SMilanka Ringwald                 log_error("no device with cid 0x%02x", channel);
425c12110e4SMilanka Ringwald                 return;
426c12110e4SMilanka Ringwald             }
427ca3f868cSMilanka Ringwald             message_type = (hid_message_type_t)(packet[0] >> 4);
428c12110e4SMilanka Ringwald             switch (message_type){
429c12110e4SMilanka Ringwald                 case HID_MESSAGE_TYPE_GET_REPORT:
43007678896SMilanka Ringwald 
431a1118d11SMilanka Ringwald                     pos = 0;
432ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[pos++] & 0x03);
433c12110e4SMilanka Ringwald                     device->report_id = 0;
4347d26fe66SMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
435a1118d11SMilanka Ringwald                     device->state = HID_DEVICE_W2_GET_REPORT;
4367d26fe66SMilanka Ringwald 
4377d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
4387d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
439c8cb324eSMilanka Ringwald                             if (packet_size < 2){
440ba9a58feSMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
441ba9a58feSMilanka Ringwald                                 break;
442ba9a58feSMilanka Ringwald                             }
443a1118d11SMilanka Ringwald                             device->report_id = packet[pos++];
4447d26fe66SMilanka Ringwald                             break;
4457d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
446a1118d11SMilanka Ringwald                             if (!btstack_hid_report_id_declared(hid_descriptor_len, hid_descriptor)) {
447c8cb324eSMilanka Ringwald                                 if (packet_size < 2) break;
448c8cb324eSMilanka Ringwald                                 if (packet[0] & 0x08){
449c8cb324eSMilanka Ringwald                                     if (packet_size > 2) {
450c8cb324eSMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
451c8cb324eSMilanka Ringwald                                     }
452c8cb324eSMilanka Ringwald                                 } else {
453c8cb324eSMilanka Ringwald                                     if (packet_size > 1) {
454c8cb324eSMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
455c8cb324eSMilanka Ringwald                                     }
456c8cb324eSMilanka Ringwald                                 }
457a1118d11SMilanka Ringwald                                 break;
458a1118d11SMilanka Ringwald                             }
4597d26fe66SMilanka Ringwald                             if (packet_size < 2){
4607d26fe66SMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4617d26fe66SMilanka Ringwald                                 break;
4627d26fe66SMilanka Ringwald                             }
463a1118d11SMilanka Ringwald                             device->report_id = packet[pos++];
464c8cb324eSMilanka Ringwald                             break;
4657bbeb3adSMilanka Ringwald                         default:
4667bbeb3adSMilanka Ringwald                             btstack_assert(false);
4677bbeb3adSMilanka Ringwald                             break;
468c8cb324eSMilanka Ringwald                     }
469c8cb324eSMilanka Ringwald                     if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
470e99e41f0SMatthias Ringwald                         l2cap_request_can_send_now_event(device->control_cid);
471c8cb324eSMilanka Ringwald                         break;
472c8cb324eSMilanka Ringwald                     }
473ca3f868cSMilanka Ringwald                     switch (hid_report_id_status(device->cid, device->report_id)){
474a1118d11SMilanka Ringwald                         case HID_REPORT_ID_INVALID:
475a1118d11SMilanka Ringwald                             device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
476a1118d11SMilanka Ringwald                             break;
477a1118d11SMilanka Ringwald                         default:
4787d26fe66SMilanka Ringwald                             break;
4797d26fe66SMilanka Ringwald                     }
480c8cb324eSMilanka Ringwald 
481c8cb324eSMilanka Ringwald                     device->expected_report_size = hid_get_report_size_for_id(device->cid, device->report_id, device->report_type, hid_descriptor_len, hid_descriptor);
482a1118d11SMilanka Ringwald                     report_size =  device->expected_report_size + pos; // add 1 for header size and report id
483a1118d11SMilanka Ringwald 
484a1df452eSMatthias Ringwald                     if ((packet[0] & 0x08) && (packet_size >= (pos + 1))){
485a1118d11SMilanka Ringwald                         device->report_size = btstack_min(btstack_min(little_endian_read_16(packet, pos), report_size), sizeof(report));
486a1118d11SMilanka Ringwald                     } else {
487a1118d11SMilanka Ringwald                         device->report_size = btstack_min(btstack_min(l2cap_max_mtu(), report_size), sizeof(report));
488a1118d11SMilanka Ringwald                     }
489a1118d11SMilanka Ringwald 
490e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
4917d26fe66SMilanka Ringwald                     break;
4927d26fe66SMilanka Ringwald 
4937d26fe66SMilanka Ringwald                 case HID_MESSAGE_TYPE_SET_REPORT:
4947d26fe66SMilanka Ringwald                     device->state = HID_DEVICE_W2_SET_REPORT;
495a1118d11SMilanka Ringwald                     device->report_size = l2cap_max_mtu();
496ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[0] & 0x03);
4977d26fe66SMilanka Ringwald                     if (packet_size < 1){
4987d26fe66SMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4997d26fe66SMilanka Ringwald                         break;
5007d26fe66SMilanka Ringwald                     }
5017d26fe66SMilanka Ringwald 
5027d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
5037d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
5047d26fe66SMilanka Ringwald                             if (packet_size < 3){
5057d26fe66SMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
5067d26fe66SMilanka Ringwald                                 break;
5077d26fe66SMilanka Ringwald                             }
5087d26fe66SMilanka Ringwald                             device->report_id = packet[1];
5094dfe6a8bSMilanka Ringwald                             device->report_status = hid_device_set_report_cmd_is_valid(device->cid, device->report_type, packet_size - 1, &packet[1]);
5104dfe6a8bSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) break;
5114dfe6a8bSMilanka Ringwald                             (*hci_device_set_report)(device->cid, device->report_type, packet_size-1, &packet[1]);
5127d26fe66SMilanka Ringwald                             break;
5137d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
5144dfe6a8bSMilanka Ringwald                             device->report_status = hid_device_set_report_cmd_is_valid(device->cid, device->report_type, packet_size - 1, &packet[1]);
5154dfe6a8bSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) break;
5164dfe6a8bSMilanka Ringwald 
5177d26fe66SMilanka Ringwald                             if (packet_size >= 2){
5184dfe6a8bSMilanka Ringwald                                 (*hci_device_set_report)(device->cid, device->report_type, packet_size-1, &packet[1]);
5197d26fe66SMilanka Ringwald                             } else {
5207d26fe66SMilanka Ringwald                                 uint8_t payload[] = {0};
5214dfe6a8bSMilanka Ringwald                                 (*hci_device_set_report)(device->cid, device->report_type, 1, payload);
5227d26fe66SMilanka Ringwald                             }
5237d26fe66SMilanka Ringwald                             break;
5247bbeb3adSMilanka Ringwald                         default:
5257bbeb3adSMilanka Ringwald                             btstack_assert(false);
5267bbeb3adSMilanka Ringwald                             break;
5277d26fe66SMilanka Ringwald                     }
528ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[0] & 0x03);
529e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
530ba9a58feSMilanka Ringwald                     break;
531ba9a58feSMilanka Ringwald                 case HID_MESSAGE_TYPE_GET_PROTOCOL:
532ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_W2_GET_PROTOCOL;
533ba9a58feSMilanka Ringwald                     if (packet_size != 1) {
534ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
535ba9a58feSMilanka Ringwald                         break;
536ba9a58feSMilanka Ringwald                     }
537ba9a58feSMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
538df3cc7a0SMilanka Ringwald                     // hid_device_request_can_send_now_event(channel);
539df3cc7a0SMilanka Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
5407d26fe66SMilanka Ringwald                     break;
541ba9a58feSMilanka Ringwald 
542ba9a58feSMilanka Ringwald                 case HID_MESSAGE_TYPE_SET_PROTOCOL:
543ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_W2_SET_PROTOCOL;
544ba9a58feSMilanka Ringwald                     if (packet_size != 1) {
545ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
546ba9a58feSMilanka Ringwald                         break;
547ba9a58feSMilanka Ringwald                     }
548ba9a58feSMilanka Ringwald                     param = packet[0] & 0x01;
549ffe3f1a1SMilanka Ringwald                     if (((hid_protocol_mode_t)param == HID_PROTOCOL_MODE_BOOT) && !hid_boot_protocol_mode_supported){
550ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
551ba9a58feSMilanka Ringwald                         break;
552ba9a58feSMilanka Ringwald                     }
553ca3f868cSMilanka Ringwald                     device->protocol_mode = (hid_protocol_mode_t) param;
5547d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
5557d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
556ba9a58feSMilanka Ringwald                             break;
5577d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
5587d26fe66SMilanka Ringwald                             break;
5597bbeb3adSMilanka Ringwald                         default:
5607bbeb3adSMilanka Ringwald                             btstack_assert(false);
5617bbeb3adSMilanka Ringwald                             break;
5627d26fe66SMilanka Ringwald                     }
5637d26fe66SMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
564e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
5657d26fe66SMilanka Ringwald                     break;
5667d26fe66SMilanka Ringwald 
5676510739bSMilanka Ringwald                 case HID_MESSAGE_TYPE_HID_CONTROL:
5686510739bSMilanka Ringwald                     param = packet[0] & 0x0F;
569ffe3f1a1SMilanka Ringwald 
570ffe3f1a1SMilanka Ringwald                     switch ((hid_control_param_t)param){
5716510739bSMilanka Ringwald                         case HID_CONTROL_PARAM_SUSPEND:
5726510739bSMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_SUSPEND);
5736510739bSMilanka Ringwald                             break;
5746510739bSMilanka Ringwald                         case HID_CONTROL_PARAM_EXIT_SUSPEND:
5756510739bSMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_EXIT_SUSPEND);
5766510739bSMilanka Ringwald                             break;
577acfd7ed2SMilanka Ringwald                         case HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG:
578acfd7ed2SMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG);
579acfd7ed2SMilanka Ringwald                             break;
5806510739bSMilanka Ringwald                         default:
5816510739bSMilanka Ringwald                             device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
582e99e41f0SMatthias Ringwald                             l2cap_request_can_send_now_event(device->control_cid);
5836510739bSMilanka Ringwald                             break;
5846510739bSMilanka Ringwald                     }
5856510739bSMilanka Ringwald                     break;
5867d26fe66SMilanka Ringwald 
5877d26fe66SMilanka Ringwald                 case HID_MESSAGE_TYPE_DATA:
5887d26fe66SMilanka Ringwald                     if (packet_size < 2) {
5897d26fe66SMilanka Ringwald                         break;
5907d26fe66SMilanka Ringwald                     }
59107678896SMilanka Ringwald                     pos = 0;
592ca3f868cSMilanka Ringwald                     device->report_type = (hid_report_type_t)(packet[pos++] & 0x03);
59307678896SMilanka Ringwald                     device->report_id = 0;
59407678896SMilanka Ringwald                     if (btstack_hid_report_id_declared(hid_descriptor_len, hid_descriptor)){
59507678896SMilanka Ringwald                         device->report_id = packet[pos++];
596738c9391SMilanka Ringwald                     }
59707678896SMilanka Ringwald 
59807678896SMilanka Ringwald                     if (hid_report_id_status(device->cid, device->report_id) == HID_REPORT_ID_INVALID){
59907678896SMilanka Ringwald                         log_info("Ignore invalid report data packet");
60007678896SMilanka Ringwald                         break;
60107678896SMilanka Ringwald                     }
60207678896SMilanka Ringwald                     if (!hid_report_size_valid(device->cid, device->report_id, device->report_type, packet_size - pos)){
60307678896SMilanka Ringwald                         log_info("Ignore invalid report data packet, invalid size");
60407678896SMilanka Ringwald                         break;
60507678896SMilanka Ringwald                     }
60607678896SMilanka Ringwald                     (*hci_device_report_data)(device->cid, device->report_type, device->report_id, packet_size - pos, &packet[pos]);
6077d26fe66SMilanka Ringwald                     break;
608c12110e4SMilanka Ringwald                 default:
609c12110e4SMilanka Ringwald                     device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
610e99e41f0SMatthias Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
611c12110e4SMilanka Ringwald                     break;
612c12110e4SMilanka Ringwald             }
613c12110e4SMilanka Ringwald             break;
6148eb8d463SMatthias Ringwald         case HCI_EVENT_PACKET:
6158eb8d463SMatthias Ringwald             switch (packet[0]){
6168eb8d463SMatthias Ringwald                 case L2CAP_EVENT_INCOMING_CONNECTION:
6178eb8d463SMatthias Ringwald                     switch (l2cap_event_incoming_connection_get_psm(packet)){
6188eb8d463SMatthias Ringwald                         case PSM_HID_CONTROL:
6198eb8d463SMatthias Ringwald                         case PSM_HID_INTERRUPT:
6206510739bSMilanka Ringwald                             l2cap_event_incoming_connection_get_address(packet, address);
62172e3f392SMatthias Ringwald                             device = hid_device_provide_instance_for_bd_addr(address);
622c12110e4SMilanka Ringwald                             if (!device) {
6233da09877SMatthias Ringwald                                 log_error("L2CAP_EVENT_INCOMING_CONNECTION, cannot create instance for %s", bd_addr_to_str(address));
624c12110e4SMilanka Ringwald                                 l2cap_decline_connection(channel);
625c12110e4SMilanka Ringwald                                 break;
626c12110e4SMilanka Ringwald                             }
627c1ab6cc1SMatthias Ringwald                             if ((device->con_handle == HCI_CON_HANDLE_INVALID) || (l2cap_event_incoming_connection_get_handle(packet) == device->con_handle)){
628c12110e4SMilanka Ringwald                                 device->con_handle = l2cap_event_incoming_connection_get_handle(packet);
629c12110e4SMilanka Ringwald                                 device->incoming = 1;
6306510739bSMilanka Ringwald                                 l2cap_event_incoming_connection_get_address(packet, device->bd_addr);
6313da09877SMatthias Ringwald                                 psm = l2cap_event_incoming_connection_get_psm(packet);
6323da09877SMatthias Ringwald                                 switch (psm){
6333da09877SMatthias Ringwald                                     case PSM_HID_CONTROL:
6343da09877SMatthias Ringwald                                         device->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
6353da09877SMatthias Ringwald                                         break;
6363da09877SMatthias Ringwald                                     case PSM_HID_INTERRUPT:
6373da09877SMatthias Ringwald                                         device->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
6383da09877SMatthias Ringwald                                     break;
6393da09877SMatthias Ringwald                                     default:
6403da09877SMatthias Ringwald                                         break;
6413da09877SMatthias Ringwald                                 }
6423da09877SMatthias Ringwald 
6438eb8d463SMatthias Ringwald                                 l2cap_accept_connection(channel);
6448eb8d463SMatthias Ringwald                             } else {
6458eb8d463SMatthias Ringwald                                 l2cap_decline_connection(channel);
6463da09877SMatthias Ringwald                                 log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s", bd_addr_to_str(address));
6478eb8d463SMatthias Ringwald                             }
6488eb8d463SMatthias Ringwald                             break;
6498eb8d463SMatthias Ringwald                         default:
6508eb8d463SMatthias Ringwald                             l2cap_decline_connection(channel);
6518eb8d463SMatthias Ringwald                             break;
6528eb8d463SMatthias Ringwald                     }
6538eb8d463SMatthias Ringwald                     break;
6548eb8d463SMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_OPENED:
6553da09877SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(l2cap_event_channel_opened_get_local_cid(packet));
656c12110e4SMilanka Ringwald                     if (!device) {
657c12110e4SMilanka Ringwald                         log_error("L2CAP_EVENT_CHANNEL_OPENED, no hid device for local cid 0x%02x", l2cap_event_channel_opened_get_local_cid(packet));
658c12110e4SMilanka Ringwald                         return;
659c12110e4SMilanka Ringwald                     }
66057c643eeSMatthias Ringwald                     status = l2cap_event_channel_opened_get_status(packet);
66157c643eeSMatthias Ringwald                     if (status) {
662c12110e4SMilanka Ringwald                         if (device->incoming == 0){
66357c643eeSMatthias Ringwald                             // report error for outgoing connection
664c12110e4SMilanka Ringwald                             hid_device_emit_connected_event(device, status);
66557c643eeSMatthias Ringwald                         }
66657c643eeSMatthias Ringwald                         return;
66757c643eeSMatthias Ringwald                     }
668641577b2SMatthias Ringwald 
669641577b2SMatthias Ringwald                     // store con_handle
670641577b2SMatthias Ringwald                     if (device->con_handle == HCI_CON_HANDLE_INVALID){
671641577b2SMatthias Ringwald                         device->con_handle  = l2cap_event_channel_opened_get_handle(packet);
672641577b2SMatthias Ringwald                     }
673641577b2SMatthias Ringwald 
674641577b2SMatthias Ringwald                     // store l2cap cid
67557c643eeSMatthias Ringwald                     psm = l2cap_event_channel_opened_get_psm(packet);
67657c643eeSMatthias Ringwald                     switch (psm){
6778eb8d463SMatthias Ringwald                         case PSM_HID_CONTROL:
678c12110e4SMilanka Ringwald                             device->control_cid = l2cap_event_channel_opened_get_local_cid(packet);
6798eb8d463SMatthias Ringwald                             break;
6808eb8d463SMatthias Ringwald                         case PSM_HID_INTERRUPT:
681c12110e4SMilanka Ringwald                             device->interrupt_cid = l2cap_event_channel_opened_get_local_cid(packet);
6828eb8d463SMatthias Ringwald                             break;
6838eb8d463SMatthias Ringwald                         default:
6848eb8d463SMatthias Ringwald                             break;
6858eb8d463SMatthias Ringwald                     }
6866510739bSMilanka Ringwald 
68757c643eeSMatthias Ringwald                     // connect HID Interrupt for outgoing
688c1ab6cc1SMatthias Ringwald                     if ((device->incoming == 0) && (psm == PSM_HID_CONTROL)){
689c12110e4SMilanka Ringwald                         status = l2cap_create_channel(packet_handler, device->bd_addr, PSM_HID_INTERRUPT, 48, &device->interrupt_cid);
69057c643eeSMatthias Ringwald                         break;
69157c643eeSMatthias Ringwald                     }
692641577b2SMatthias Ringwald 
693641577b2SMatthias Ringwald                     // emit connected if both channels are open
694641577b2SMatthias Ringwald                     connected_before = device->connected;
695c12110e4SMilanka Ringwald                     if (!connected_before && device->control_cid && device->interrupt_cid){
696c12110e4SMilanka Ringwald                         device->connected = 1;
697c12110e4SMilanka Ringwald                         hid_device_emit_connected_event(device, 0);
6988eb8d463SMatthias Ringwald                     }
6998eb8d463SMatthias Ringwald                     break;
7008eb8d463SMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_CLOSED:
7014176cbc0SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(l2cap_event_channel_closed_get_local_cid(packet));
702c12110e4SMilanka Ringwald                     if (!device) return;
703c12110e4SMilanka Ringwald 
7046510739bSMilanka Ringwald                     // connected_before = device->connected;
705c12110e4SMilanka Ringwald                     device->incoming  = 0;
706c12110e4SMilanka Ringwald                     if (l2cap_event_channel_closed_get_local_cid(packet) == device->interrupt_cid){
707c12110e4SMilanka Ringwald                         device->interrupt_cid = 0;
70857c18996SMilanka Ringwald                     }
70957c18996SMilanka Ringwald                     if (l2cap_event_channel_closed_get_local_cid(packet) == device->control_cid){
71057c18996SMilanka Ringwald                         device->control_cid = 0;
7118eb8d463SMatthias Ringwald                     }
7126510739bSMilanka Ringwald                     if (!device->interrupt_cid && !device->control_cid){
7136510739bSMilanka Ringwald                         device->connected = 0;
7143da09877SMatthias Ringwald                         device->con_handle = HCI_CON_HANDLE_INVALID;
7156510739bSMilanka Ringwald                         device->cid = 0;
716caf9a547SMilanka Ringwald                         hid_device_emit_event(device, HID_SUBEVENT_CONNECTION_CLOSED);
7178eb8d463SMatthias Ringwald                     }
7188eb8d463SMatthias Ringwald                     break;
719c12110e4SMilanka Ringwald 
72057c18996SMilanka Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
7217d26fe66SMilanka Ringwald                     local_cid = l2cap_event_can_send_now_get_local_cid(packet);
7224176cbc0SMatthias Ringwald                     device = hid_device_get_instance_for_l2cap_cid(local_cid);
7237d26fe66SMilanka Ringwald 
724c12110e4SMilanka Ringwald                     if (!device) return;
725c12110e4SMilanka Ringwald                     switch (device->state){
726a1118d11SMilanka Ringwald                         case HID_DEVICE_W2_GET_REPORT:{
7277d26fe66SMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) {
7287d26fe66SMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
7297d26fe66SMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
7307d26fe66SMilanka Ringwald                                 break;
7317d26fe66SMilanka Ringwald                             }
732a1118d11SMilanka Ringwald 
733a1118d11SMilanka Ringwald                             pos = 0;
734a1118d11SMilanka Ringwald                             report[pos++] = (HID_MESSAGE_TYPE_DATA << 4) | device->report_type;
735a1118d11SMilanka Ringwald                             if (device->report_id){
736a1118d11SMilanka Ringwald                                 report[pos++] = device->report_id;
737a1118d11SMilanka Ringwald                             }
738a1118d11SMilanka Ringwald 
7397d26fe66SMilanka Ringwald                             report_size = 0;
740a1118d11SMilanka Ringwald                             status = (*hci_device_get_report)(device->cid, device->report_type, device->report_id, &report_size, &report[pos]);
741481c7cdcSMilanka Ringwald 
742a1118d11SMilanka Ringwald                             switch (status){
743a1118d11SMilanka Ringwald                                 case 0:
744a1118d11SMilanka Ringwald                                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_NOT_READY;
745a1118d11SMilanka Ringwald                                     break;
746a1118d11SMilanka Ringwald                                 case 1:
747a1118d11SMilanka Ringwald                                     if (report_size == 0){
748a1118d11SMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
749a1118d11SMilanka Ringwald                                         break;
750a1118d11SMilanka Ringwald                                     }
751a1118d11SMilanka Ringwald                                     if (device->expected_report_size != report_size){
752a1118d11SMilanka Ringwald                                         log_error("Expected report size of %d bytes, received %d", device->expected_report_size, report_size);
753a1118d11SMilanka Ringwald                                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
754a1118d11SMilanka Ringwald                                         break;
755a1118d11SMilanka Ringwald                                     }
756a1118d11SMilanka Ringwald                                     break;
757a1118d11SMilanka Ringwald                                 default:
758a1118d11SMilanka Ringwald                                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
759a1118d11SMilanka Ringwald                                     break;
760a1118d11SMilanka Ringwald                             }
761ba9a58feSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
762ba9a58feSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
763481c7cdcSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
764481c7cdcSMilanka Ringwald                                 break;
765481c7cdcSMilanka Ringwald                             }
766481c7cdcSMilanka Ringwald 
767a1118d11SMilanka Ringwald                             // if (report_size > l2cap_max_mtu()){
768a1118d11SMilanka Ringwald                             //     report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
769a1118d11SMilanka Ringwald                             //     hid_device_send_control_message(device->cid, &report[0], 1);
770a1118d11SMilanka Ringwald                             //     break;
771a1118d11SMilanka Ringwald                             // }
772481c7cdcSMilanka Ringwald 
773a1118d11SMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], device->report_size);
774481c7cdcSMilanka Ringwald                             //     device->state = HID_DEVICE_IDLE;
775c12110e4SMilanka Ringwald                             break;
776a1118d11SMilanka Ringwald                         }
777ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SET_REPORT:
778ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SET_PROTOCOL:
779ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
7806510739bSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
781ba9a58feSMilanka Ringwald                             break;
782ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_GET_PROTOCOL:
783ba9a58feSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
784ba9a58feSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
785ba9a58feSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
786ba9a58feSMilanka Ringwald                                 break;
787ba9a58feSMilanka Ringwald                             }
7885139b81eSMilanka Ringwald 
7895139b81eSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_DATA << 4);
7905139b81eSMilanka Ringwald                             report[1] =  device->protocol_mode;
7915139b81eSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 2);
792ba9a58feSMilanka Ringwald                             break;
793ba9a58feSMilanka Ringwald 
794ba9a58feSMilanka Ringwald 
795ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST:
796ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
797ba9a58feSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
798c12110e4SMilanka Ringwald                             break;
799c12110e4SMilanka Ringwald                         default:
800e99e41f0SMatthias Ringwald                             if (device->user_request_can_send_now){
801e99e41f0SMatthias Ringwald                                 device->user_request_can_send_now = 0;
802caf9a547SMilanka Ringwald                                 hid_device_emit_event(device, HID_SUBEVENT_CAN_SEND_NOW);
803e99e41f0SMatthias Ringwald                             }
804c12110e4SMilanka Ringwald                             break;
805c12110e4SMilanka Ringwald                     }
806ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_IDLE;
8078eb8d463SMatthias Ringwald                     break;
8088eb8d463SMatthias Ringwald                 default:
8098eb8d463SMatthias Ringwald                     break;
8108eb8d463SMatthias Ringwald             }
8118eb8d463SMatthias Ringwald             break;
8128eb8d463SMatthias Ringwald         default:
8138eb8d463SMatthias Ringwald             break;
8148eb8d463SMatthias Ringwald     }
8158eb8d463SMatthias Ringwald }
8168eb8d463SMatthias Ringwald 
8178eb8d463SMatthias Ringwald /**
8188eb8d463SMatthias Ringwald  * @brief Set up HID Device
8198eb8d463SMatthias Ringwald  */
82051b27067SMilanka Ringwald void hid_device_init(bool boot_protocol_mode_supported, uint16_t descriptor_len, const uint8_t * descriptor){
821ba9a58feSMilanka Ringwald     hid_boot_protocol_mode_supported = boot_protocol_mode_supported;
822662cddc2SMilanka Ringwald     hid_descriptor =  descriptor;
823662cddc2SMilanka Ringwald     hid_descriptor_len = descriptor_len;
824a1118d11SMilanka Ringwald     hci_device_get_report = dummy_write_report;
825a1118d11SMilanka Ringwald     hci_device_set_report = dummy_set_report;
826a1118d11SMilanka Ringwald     hci_device_report_data = dummy_report_data;
827a1118d11SMilanka Ringwald 
82878315a58SMatthias Ringwald     l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, gap_get_security_level());
82978315a58SMatthias Ringwald     l2cap_register_service(packet_handler, PSM_HID_CONTROL,   100, gap_get_security_level());
8308eb8d463SMatthias Ringwald }
8318eb8d463SMatthias Ringwald 
8322cc1b993SMatthias Ringwald void hid_device_deinit(void){
8332cc1b993SMatthias Ringwald     (void) memset(&_hid_device, 0, sizeof(hid_device_t));
8342cc1b993SMatthias Ringwald }
8352cc1b993SMatthias Ringwald 
8368eb8d463SMatthias Ringwald /**
8378eb8d463SMatthias Ringwald  * @brief Register callback for the HID Device client.
8388eb8d463SMatthias Ringwald  * @param callback
8398eb8d463SMatthias Ringwald  */
8408eb8d463SMatthias Ringwald void hid_device_register_packet_handler(btstack_packet_handler_t callback){
8418eb8d463SMatthias Ringwald     hid_callback = callback;
8428eb8d463SMatthias Ringwald }
8438eb8d463SMatthias Ringwald 
844c12110e4SMilanka Ringwald 
845c12110e4SMilanka Ringwald 
846a1118d11SMilanka 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)){
847c12110e4SMilanka Ringwald     if (callback == NULL){
848c12110e4SMilanka Ringwald         callback = dummy_write_report;
849c12110e4SMilanka Ringwald     }
850a1118d11SMilanka Ringwald     hci_device_get_report = callback;
851c12110e4SMilanka Ringwald }
852c12110e4SMilanka Ringwald 
8534dfe6a8bSMilanka 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)){
854ba9a58feSMilanka Ringwald     if (callback == NULL){
855ba9a58feSMilanka Ringwald         callback = dummy_set_report;
856ba9a58feSMilanka Ringwald     }
857ba9a58feSMilanka Ringwald     hci_device_set_report = callback;
858ba9a58feSMilanka Ringwald }
859c12110e4SMilanka Ringwald 
8607d26fe66SMilanka 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)){
8617d26fe66SMilanka Ringwald     if (callback == NULL){
8627d26fe66SMilanka Ringwald         callback = dummy_report_data;
8637d26fe66SMilanka Ringwald     }
8647d26fe66SMilanka Ringwald     hci_device_report_data = callback;
8657d26fe66SMilanka Ringwald }
8667d26fe66SMilanka Ringwald 
8677d26fe66SMilanka Ringwald 
8688eb8d463SMatthias Ringwald /**
8698eb8d463SMatthias Ringwald  * @brief Request can send now event to send HID Report
8708eb8d463SMatthias Ringwald  * @param hid_cid
8718eb8d463SMatthias Ringwald  */
8728eb8d463SMatthias Ringwald void hid_device_request_can_send_now_event(uint16_t hid_cid){
8734176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
874e99e41f0SMatthias Ringwald     if (!hid_device || !hid_device->interrupt_cid) return;
875e99e41f0SMatthias Ringwald     hid_device->user_request_can_send_now = 1;
876e99e41f0SMatthias Ringwald     l2cap_request_can_send_now_event(hid_device->interrupt_cid);
8778eb8d463SMatthias Ringwald }
8788eb8d463SMatthias Ringwald 
8798eb8d463SMatthias Ringwald /**
8808eb8d463SMatthias Ringwald  * @brief Send HID message on interrupt channel
8818eb8d463SMatthias Ringwald  * @param hid_cid
8828eb8d463SMatthias Ringwald  */
8838eb8d463SMatthias Ringwald void hid_device_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
8844176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
885c12110e4SMilanka Ringwald     if (!hid_device || !hid_device->interrupt_cid) return;
8868eb8d463SMatthias Ringwald     l2cap_send(hid_device->interrupt_cid, (uint8_t*) message, message_len);
887e9392950SMilanka Ringwald     if (hid_device->user_request_can_send_now){
888517089d1SMilanka Ringwald         printf("request user can send now because pending\n");
889e99e41f0SMatthias Ringwald         l2cap_request_can_send_now_event((hid_device->interrupt_cid));
890e99e41f0SMatthias Ringwald     }
8918eb8d463SMatthias Ringwald }
8928eb8d463SMatthias Ringwald 
8938eb8d463SMatthias Ringwald /**
8948eb8d463SMatthias Ringwald  * @brief Send HID message on control channel
8958eb8d463SMatthias Ringwald  * @param hid_cid
8968eb8d463SMatthias Ringwald  */
897c12110e4SMilanka Ringwald void hid_device_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
8984176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
899c12110e4SMilanka Ringwald     if (!hid_device || !hid_device->control_cid) return;
9008eb8d463SMatthias Ringwald     l2cap_send(hid_device->control_cid, (uint8_t*) message, message_len);
901e99e41f0SMatthias Ringwald     // request user can send now if pending
902e99e41f0SMatthias Ringwald     if (hid_device->user_request_can_send_now){
903e99e41f0SMatthias Ringwald         l2cap_request_can_send_now_event((hid_device->control_cid));
904e99e41f0SMatthias Ringwald     }
9058eb8d463SMatthias Ringwald }
9068eb8d463SMatthias Ringwald 
90757c643eeSMatthias Ringwald /*
90857c643eeSMatthias Ringwald  * @brief Create HID connection to HID Host
90957c643eeSMatthias Ringwald  * @param addr
91057c643eeSMatthias Ringwald  * @param hid_cid to use for other commands
91157c643eeSMatthias Ringwald  * @result status
91257c643eeSMatthias Ringwald  */
91357c643eeSMatthias Ringwald uint8_t hid_device_connect(bd_addr_t addr, uint16_t * hid_cid){
914c12110e4SMilanka Ringwald     hid_device_t * hid_device = hid_device_create_instance();
915c12110e4SMilanka Ringwald     if (!hid_device){
916c12110e4SMilanka Ringwald         log_error("hid_device_connect: could not create a hid device instace");
917c12110e4SMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
918c12110e4SMilanka Ringwald     }
91957c643eeSMatthias Ringwald     // assign hic_cid
920c12110e4SMilanka Ringwald     *hid_cid = hid_device_get_next_cid();
92172e3f392SMatthias Ringwald 
92257c643eeSMatthias Ringwald     // store address
9236535961aSMatthias Ringwald     (void)memcpy(hid_device->bd_addr, addr, 6);
92457c643eeSMatthias Ringwald 
92557c643eeSMatthias Ringwald     // reset state
9266510739bSMilanka Ringwald     hid_device->cid           = *hid_cid;
92757c643eeSMatthias Ringwald     hid_device->incoming      = 0;
92857c643eeSMatthias Ringwald     hid_device->connected     = 0;
92957c643eeSMatthias Ringwald     hid_device->control_cid   = 0;
93057c643eeSMatthias Ringwald     hid_device->interrupt_cid = 0;
93172e3f392SMatthias Ringwald     hid_device->con_handle    = HCI_CON_HANDLE_INVALID;
93272e3f392SMatthias Ringwald 
93357c643eeSMatthias Ringwald     // create l2cap control using fixed HID L2CAP PSM
93457c643eeSMatthias Ringwald     log_info("Create outgoing HID Control");
93557c643eeSMatthias Ringwald     uint8_t status = l2cap_create_channel(packet_handler, hid_device->bd_addr, PSM_HID_CONTROL, 48, &hid_device->control_cid);
93657c643eeSMatthias Ringwald     return status;
93757c643eeSMatthias Ringwald }
9386510739bSMilanka Ringwald 
9396510739bSMilanka Ringwald /*
9406510739bSMilanka Ringwald  * @brief Disconnect from HID Host
9416510739bSMilanka Ringwald  * @param hid_cid
9426510739bSMilanka Ringwald  * @result status
9436510739bSMilanka Ringwald  */
94457c18996SMilanka Ringwald void hid_device_disconnect_interrupt_channel(uint16_t hid_cid){
9454176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
94657c18996SMilanka Ringwald     if (!hid_device){
94757c18996SMilanka Ringwald         log_error("hid_device_disconnect_interrupt_channel: could not find hid device instace");
94857c18996SMilanka Ringwald         return;
94957c18996SMilanka Ringwald     }
95057c18996SMilanka Ringwald     log_info("Disconnect from interrupt channel HID Host");
95157c18996SMilanka Ringwald     if (hid_device->interrupt_cid){
95257c18996SMilanka Ringwald         l2cap_disconnect(hid_device->interrupt_cid, 0);  // reason isn't used
95357c18996SMilanka Ringwald     }
95457c18996SMilanka Ringwald }
95557c18996SMilanka Ringwald 
95657c18996SMilanka Ringwald void hid_device_disconnect_control_channel(uint16_t hid_cid){
9574176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
95857c18996SMilanka Ringwald     if (!hid_device){
95957c18996SMilanka Ringwald         log_error("hid_device_disconnect_control_channel: could not find hid device instace");
96057c18996SMilanka Ringwald         return;
96157c18996SMilanka Ringwald     }
96257c18996SMilanka Ringwald     log_info("Disconnect from control channel HID Host");
96357c18996SMilanka Ringwald     if (hid_device->control_cid){
96457c18996SMilanka Ringwald         l2cap_disconnect(hid_device->control_cid, 0);  // reason isn't used
96557c18996SMilanka Ringwald     }
96657c18996SMilanka Ringwald }
96757c18996SMilanka Ringwald 
9686510739bSMilanka Ringwald void hid_device_disconnect(uint16_t hid_cid){
9694176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
9706510739bSMilanka Ringwald     if (!hid_device){
9716510739bSMilanka Ringwald         log_error("hid_device_disconnect: could not find hid device instace");
9726510739bSMilanka Ringwald         return;
9736510739bSMilanka Ringwald     }
9746510739bSMilanka Ringwald     log_info("Disconnect from HID Host");
9756510739bSMilanka Ringwald     if (hid_device->interrupt_cid){
9766510739bSMilanka Ringwald         l2cap_disconnect(hid_device->interrupt_cid, 0);  // reason isn't used
9776510739bSMilanka Ringwald     }
97857c18996SMilanka Ringwald     if (hid_device->control_cid){
97957c18996SMilanka Ringwald         l2cap_disconnect(hid_device->control_cid, 0); // reason isn't used
98057c18996SMilanka Ringwald     }
9816510739bSMilanka Ringwald }
9827d26fe66SMilanka Ringwald 
9837d26fe66SMilanka Ringwald int hid_device_in_boot_protocol_mode(uint16_t hid_cid){
9844176cbc0SMatthias Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
9857d26fe66SMilanka Ringwald     if (!hid_device){
9867d26fe66SMilanka Ringwald         log_error("hid_device_in_boot_protocol_mode: could not find hid device instace");
9877d26fe66SMilanka Ringwald         return 0;
9887d26fe66SMilanka Ringwald     }
9897d26fe66SMilanka Ringwald     return hid_device->protocol_mode == HID_PROTOCOL_MODE_BOOT;
9907d26fe66SMilanka Ringwald }
991