xref: /btstack/src/classic/hid_device.c (revision df3cc7a0037e9fbcbb0954be9f725e33b09642ed)
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"
498eb8d463SMatthias Ringwald 
50c12110e4SMilanka Ringwald typedef enum {
51c12110e4SMilanka Ringwald     HID_DEVICE_IDLE,
52c12110e4SMilanka Ringwald     HID_DEVICE_CONNECTED,
53ba9a58feSMilanka Ringwald     HID_DEVICE_W2_GET_REPORT,
54ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SET_REPORT,
55ba9a58feSMilanka Ringwald     HID_DEVICE_W2_GET_PROTOCOL,
56ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SET_PROTOCOL,
57ba9a58feSMilanka Ringwald     HID_DEVICE_W2_ANSWER_SET_PROTOCOL,
58ba9a58feSMilanka Ringwald     HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST,
59c12110e4SMilanka Ringwald } hid_device_state_t;
60c12110e4SMilanka Ringwald 
618eb8d463SMatthias Ringwald // hid device state
628eb8d463SMatthias Ringwald typedef struct hid_device {
638eb8d463SMatthias Ringwald     uint16_t  cid;
648eb8d463SMatthias Ringwald     bd_addr_t bd_addr;
658eb8d463SMatthias Ringwald     hci_con_handle_t con_handle;
668eb8d463SMatthias Ringwald     uint16_t  control_cid;
678eb8d463SMatthias Ringwald     uint16_t  interrupt_cid;
688eb8d463SMatthias Ringwald     uint8_t   incoming;
6957c643eeSMatthias Ringwald     uint8_t   connected;
70c12110e4SMilanka Ringwald     hid_device_state_t state;
71c12110e4SMilanka Ringwald     hid_report_type_t report_type;
72c12110e4SMilanka Ringwald     uint16_t  report_id;
737d26fe66SMilanka Ringwald     uint16_t  max_packet_size;
74ba9a58feSMilanka Ringwald 
75bc967e00SMilanka Ringwald     hid_handshake_param_type_t report_status;
76ba9a58feSMilanka Ringwald     hid_protocol_mode_t protocol_mode;
778eb8d463SMatthias Ringwald } hid_device_t;
788eb8d463SMatthias Ringwald 
798eb8d463SMatthias Ringwald static hid_device_t _hid_device;
80ba9a58feSMilanka Ringwald static uint8_t hid_boot_protocol_mode_supported;
817d26fe66SMilanka Ringwald static uint8_t hid_report_ids_declared;
82c12110e4SMilanka Ringwald 
83481c7cdcSMilanka Ringwald static hid_handshake_param_type_t dummy_write_report(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, uint8_t report_max_size, int * out_report_size, uint8_t * out_report){
84c12110e4SMilanka Ringwald     UNUSED(hid_cid);
85c12110e4SMilanka Ringwald     UNUSED(report_type);
86c12110e4SMilanka Ringwald     UNUSED(report_id);
87c12110e4SMilanka Ringwald     UNUSED(report_max_size);
88c12110e4SMilanka Ringwald     UNUSED(out_report_size);
89c12110e4SMilanka Ringwald     UNUSED(out_report);
90481c7cdcSMilanka Ringwald     return HID_HANDSHAKE_PARAM_TYPE_ERR_UNKNOWN;
91c12110e4SMilanka Ringwald }
92ba9a58feSMilanka Ringwald static hid_handshake_param_type_t dummy_set_report(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report){
93ba9a58feSMilanka Ringwald     UNUSED(hid_cid);
94ba9a58feSMilanka Ringwald     UNUSED(report_type);
95ba9a58feSMilanka Ringwald     UNUSED(report_size);
96ba9a58feSMilanka Ringwald     UNUSED(report);
97ba9a58feSMilanka Ringwald     return HID_HANDSHAKE_PARAM_TYPE_ERR_UNKNOWN;
98ba9a58feSMilanka Ringwald }
997d26fe66SMilanka 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){
1007d26fe66SMilanka Ringwald     UNUSED(hid_cid);
1017d26fe66SMilanka Ringwald     UNUSED(report_type);
1027d26fe66SMilanka Ringwald     UNUSED(report_id);
1037d26fe66SMilanka Ringwald     UNUSED(report_size);
1047d26fe66SMilanka Ringwald     UNUSED(report);
1057d26fe66SMilanka Ringwald }
1067d26fe66SMilanka Ringwald 
107481c7cdcSMilanka Ringwald static hid_handshake_param_type_t (*hci_device_write_report) (uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, uint8_t report_max_size, int * out_report_size, uint8_t * out_report) = dummy_write_report;
108ba9a58feSMilanka Ringwald static hid_handshake_param_type_t (*hci_device_set_report)   (uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report) = dummy_set_report;
1097d26fe66SMilanka 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;
1108eb8d463SMatthias Ringwald 
1118eb8d463SMatthias Ringwald static btstack_packet_handler_t hid_callback;
112d40c9ac6SMatthias Ringwald 
113c12110e4SMilanka Ringwald static uint16_t hid_device_cid = 0;
114c12110e4SMilanka Ringwald 
115c12110e4SMilanka Ringwald static uint16_t hid_device_get_next_cid(void){
116c12110e4SMilanka Ringwald     hid_device_cid++;
117c12110e4SMilanka Ringwald     if (!hid_device_cid){
118c12110e4SMilanka Ringwald         hid_device_cid = 1;
119c12110e4SMilanka Ringwald     }
120c12110e4SMilanka Ringwald     return hid_device_cid;
121c12110e4SMilanka Ringwald }
122c12110e4SMilanka Ringwald 
123c12110e4SMilanka Ringwald // TODO: store hid device connection into list
124c12110e4SMilanka Ringwald static hid_device_t * hid_device_get_instance_for_cid(uint16_t cid){
125*df3cc7a0SMilanka Ringwald     printf("control_cid 0x%02x, interrupt_cid 0x%02x, query_cid 0x%02x \n", _hid_device.control_cid,  _hid_device.interrupt_cid, cid);
126c12110e4SMilanka Ringwald     if (_hid_device.cid == cid || _hid_device.control_cid == cid || _hid_device.interrupt_cid == cid){
127c12110e4SMilanka Ringwald         return &_hid_device;
128c12110e4SMilanka Ringwald     }
129c12110e4SMilanka Ringwald     return NULL;
130c12110e4SMilanka Ringwald }
131c12110e4SMilanka Ringwald 
132ba9a58feSMilanka Ringwald static hid_device_t * hid_device_provide_instance_for_bt_addr(bd_addr_t bd_addr){
1336510739bSMilanka Ringwald     if (!_hid_device.cid){
134ba9a58feSMilanka Ringwald         memcpy(_hid_device.bd_addr, bd_addr, 6);
1356510739bSMilanka Ringwald         _hid_device.cid = hid_device_get_next_cid();
136ba9a58feSMilanka Ringwald         _hid_device.protocol_mode = HID_PROTOCOL_MODE_REPORT;
1376510739bSMilanka Ringwald     }
138c12110e4SMilanka Ringwald     return &_hid_device;
139c12110e4SMilanka Ringwald }
140c12110e4SMilanka Ringwald 
141c12110e4SMilanka Ringwald static hid_device_t * hid_device_get_instance_for_con_handle(uint16_t con_handle){
142c12110e4SMilanka Ringwald     UNUSED(con_handle);
143c12110e4SMilanka Ringwald     return &_hid_device;
144c12110e4SMilanka Ringwald }
145c12110e4SMilanka Ringwald 
146c12110e4SMilanka Ringwald static hid_device_t * hid_device_create_instance(void){
147ba9a58feSMilanka Ringwald 
148c12110e4SMilanka Ringwald     return &_hid_device;
149c12110e4SMilanka Ringwald }
150c12110e4SMilanka Ringwald 
151d40c9ac6SMatthias Ringwald void hid_create_sdp_record(
152d40c9ac6SMatthias Ringwald     uint8_t *service,
153d40c9ac6SMatthias Ringwald     uint32_t service_record_handle,
154d40c9ac6SMatthias Ringwald     uint16_t hid_device_subclass,
155d40c9ac6SMatthias Ringwald     uint8_t  hid_country_code,
156d40c9ac6SMatthias Ringwald     uint8_t  hid_virtual_cable,
157d40c9ac6SMatthias Ringwald     uint8_t  hid_reconnect_initiate,
158d40c9ac6SMatthias Ringwald     uint8_t  hid_boot_device,
159d40c9ac6SMatthias Ringwald     const uint8_t * hid_descriptor, uint16_t hid_descriptor_size,
160d40c9ac6SMatthias Ringwald     const char *device_name){
161d40c9ac6SMatthias Ringwald 
162d40c9ac6SMatthias Ringwald     uint8_t * attribute;
163d40c9ac6SMatthias Ringwald     de_create_sequence(service);
164d40c9ac6SMatthias Ringwald 
165d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
166d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
167d40c9ac6SMatthias Ringwald 
168d40c9ac6SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
169d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
170d40c9ac6SMatthias Ringwald     {
171d40c9ac6SMatthias Ringwald         de_add_number(attribute,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
172d40c9ac6SMatthias Ringwald     }
173d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
174d40c9ac6SMatthias Ringwald 
175d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
176d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
177d40c9ac6SMatthias Ringwald     {
178d40c9ac6SMatthias Ringwald         uint8_t * l2cpProtocol = de_push_sequence(attribute);
179d40c9ac6SMatthias Ringwald         {
180d40c9ac6SMatthias Ringwald             de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
181d40c9ac6SMatthias Ringwald             de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, PSM_HID_CONTROL);
182d40c9ac6SMatthias Ringwald         }
183d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, l2cpProtocol);
184d40c9ac6SMatthias Ringwald 
185d40c9ac6SMatthias Ringwald         uint8_t * hidProtocol = de_push_sequence(attribute);
186d40c9ac6SMatthias Ringwald         {
187d40c9ac6SMatthias Ringwald             de_add_number(hidProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
188d40c9ac6SMatthias Ringwald         }
189d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidProtocol);
190d40c9ac6SMatthias Ringwald     }
191d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
192d40c9ac6SMatthias Ringwald 
193d40c9ac6SMatthias Ringwald     // TODO?
194d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
195d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
196d40c9ac6SMatthias Ringwald     {
197d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e);
198d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a);
199d40c9ac6SMatthias Ringwald         de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100);
200d40c9ac6SMatthias Ringwald     }
201d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
202d40c9ac6SMatthias Ringwald 
203d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS);
204d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
205d40c9ac6SMatthias Ringwald     {
206d40c9ac6SMatthias Ringwald         uint8_t * additionalDescriptorAttribute = de_push_sequence(attribute);
207d40c9ac6SMatthias Ringwald         {
208d40c9ac6SMatthias Ringwald             uint8_t * l2cpProtocol = de_push_sequence(additionalDescriptorAttribute);
209d40c9ac6SMatthias Ringwald             {
210d40c9ac6SMatthias Ringwald                 de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
211d40c9ac6SMatthias Ringwald                 de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, PSM_HID_INTERRUPT);
212d40c9ac6SMatthias Ringwald             }
213d40c9ac6SMatthias Ringwald             de_pop_sequence(additionalDescriptorAttribute, l2cpProtocol);
214d40c9ac6SMatthias Ringwald 
215d40c9ac6SMatthias Ringwald             uint8_t * hidProtocol = de_push_sequence(attribute);
216d40c9ac6SMatthias Ringwald             {
217d40c9ac6SMatthias Ringwald                 de_add_number(hidProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
218d40c9ac6SMatthias Ringwald             }
219d40c9ac6SMatthias Ringwald             de_pop_sequence(attribute, hidProtocol);
220d40c9ac6SMatthias Ringwald         }
221d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, additionalDescriptorAttribute);
222d40c9ac6SMatthias Ringwald     }
223d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
224d40c9ac6SMatthias Ringwald 
225d40c9ac6SMatthias Ringwald     // 0x0100 "ServiceName"
226d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0100);
227d40c9ac6SMatthias Ringwald     de_add_data(service,  DE_STRING, strlen(device_name), (uint8_t *) device_name);
228d40c9ac6SMatthias Ringwald 
229d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
230d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
231d40c9ac6SMatthias Ringwald     {
232d40c9ac6SMatthias Ringwald         uint8_t * hidProfile = de_push_sequence(attribute);
233d40c9ac6SMatthias Ringwald         {
234d40c9ac6SMatthias Ringwald             de_add_number(hidProfile,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
235d40c9ac6SMatthias Ringwald             de_add_number(hidProfile,  DE_UINT, DE_SIZE_16, 0x0101);    // Version 1.1
236d40c9ac6SMatthias Ringwald         }
237d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidProfile);
238d40c9ac6SMatthias Ringwald     }
239d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
240d40c9ac6SMatthias Ringwald 
241d40c9ac6SMatthias Ringwald     // Deprecated in v1.1.1
242d40c9ac6SMatthias Ringwald     // de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_RELEASE_NUMBER);
243d40c9ac6SMatthias Ringwald     // de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0101);
244d40c9ac6SMatthias Ringwald 
245d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_PARSER_VERSION);
246d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0111);  // v1.1.1
247d40c9ac6SMatthias Ringwald 
248d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_SUBCLASS);
2499679ea81SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_8,  hid_device_subclass);
250d40c9ac6SMatthias Ringwald 
251d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_COUNTRY_CODE);
2529679ea81SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_8,  hid_country_code);
253d40c9ac6SMatthias Ringwald 
254d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_VIRTUAL_CABLE);
255d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  hid_virtual_cable);
256d40c9ac6SMatthias Ringwald 
257d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_RECONNECT_INITIATE);
258d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  hid_reconnect_initiate);
259d40c9ac6SMatthias Ringwald 
260d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST);
261d40c9ac6SMatthias Ringwald     attribute = de_push_sequence(service);
262d40c9ac6SMatthias Ringwald     {
263d40c9ac6SMatthias Ringwald         uint8_t* hidDescriptor = de_push_sequence(attribute);
264d40c9ac6SMatthias Ringwald         {
265d40c9ac6SMatthias Ringwald             de_add_number(hidDescriptor,  DE_UINT, DE_SIZE_8, 0x22);    // Report Descriptor
266d40c9ac6SMatthias Ringwald             de_add_data(hidDescriptor,  DE_STRING, hid_descriptor_size, (uint8_t *) hid_descriptor);
267d40c9ac6SMatthias Ringwald         }
268d40c9ac6SMatthias Ringwald         de_pop_sequence(attribute, hidDescriptor);
269d40c9ac6SMatthias Ringwald     }
270d40c9ac6SMatthias Ringwald     de_pop_sequence(service, attribute);
271d40c9ac6SMatthias Ringwald 
2729679ea81SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDLANGID_BASE_LIST);
2739679ea81SMatthias Ringwald     attribute = de_push_sequence(service);
2749679ea81SMatthias Ringwald     {
2759679ea81SMatthias Ringwald         uint8_t* hig_lang_base = de_push_sequence(attribute);
2769679ea81SMatthias Ringwald         {
2779679ea81SMatthias Ringwald             // see: http://www.usb.org/developers/docs/USB_LANGIDs.pdf
2789679ea81SMatthias Ringwald             de_add_number(hig_lang_base,  DE_UINT, DE_SIZE_16, 0x0409);    // HIDLANGID = English (US)
2799679ea81SMatthias Ringwald             de_add_number(hig_lang_base,  DE_UINT, DE_SIZE_16, 0x0100);    // HIDLanguageBase = 0x0100 default
2809679ea81SMatthias Ringwald         }
2819679ea81SMatthias Ringwald         de_pop_sequence(attribute, hig_lang_base);
2829679ea81SMatthias Ringwald     }
2839679ea81SMatthias Ringwald     de_pop_sequence(service, attribute);
2849679ea81SMatthias Ringwald 
2856510739bSMilanka Ringwald     uint8_t hid_remote_wake = 1;
2866510739bSMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_REMOTE_WAKE);
2876510739bSMilanka Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  hid_remote_wake);
2886510739bSMilanka Ringwald 
289d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_BOOT_DEVICE);
290d40c9ac6SMatthias Ringwald     de_add_number(service,  DE_BOOL, DE_SIZE_8,  hid_boot_device);
291d40c9ac6SMatthias Ringwald }
2928eb8d463SMatthias Ringwald 
2938eb8d463SMatthias Ringwald static inline void hid_device_emit_connected_event(hid_device_t * context, uint8_t status){
2948eb8d463SMatthias Ringwald     uint8_t event[15];
2958eb8d463SMatthias Ringwald     int pos = 0;
2968eb8d463SMatthias Ringwald     event[pos++] = HCI_EVENT_HID_META;
2978eb8d463SMatthias Ringwald     pos++;  // skip len
2988eb8d463SMatthias Ringwald     event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
2998eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->cid);
3008eb8d463SMatthias Ringwald     pos+=2;
3018eb8d463SMatthias Ringwald     event[pos++] = status;
3026510739bSMilanka Ringwald     reverse_bd_addr(context->bd_addr, &event[pos]);
3038eb8d463SMatthias Ringwald     pos += 6;
3048eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->con_handle);
3058eb8d463SMatthias Ringwald     pos += 2;
3068eb8d463SMatthias Ringwald     event[pos++] = context->incoming;
3078eb8d463SMatthias Ringwald     event[1] = pos - 2;
3088eb8d463SMatthias Ringwald     if (pos != sizeof(event)) log_error("hid_device_emit_connected_event size %u", pos);
3098eb8d463SMatthias Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3108eb8d463SMatthias Ringwald }
3118eb8d463SMatthias Ringwald 
3128eb8d463SMatthias Ringwald static inline void hid_device_emit_connection_closed_event(hid_device_t * context){
3138eb8d463SMatthias Ringwald     uint8_t event[5];
3148eb8d463SMatthias Ringwald     int pos = 0;
3158eb8d463SMatthias Ringwald     event[pos++] = HCI_EVENT_HID_META;
3168eb8d463SMatthias Ringwald     pos++;  // skip len
3178eb8d463SMatthias Ringwald     event[pos++] = HID_SUBEVENT_CONNECTION_CLOSED;
3188eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->cid);
3198eb8d463SMatthias Ringwald     pos+=2;
3208eb8d463SMatthias Ringwald     event[1] = pos - 2;
3218eb8d463SMatthias Ringwald     if (pos != sizeof(event)) log_error("hid_device_emit_connection_closed_event size %u", pos);
3228eb8d463SMatthias Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3238eb8d463SMatthias Ringwald }
3248eb8d463SMatthias Ringwald 
3258eb8d463SMatthias Ringwald static inline void hid_device_emit_can_send_now_event(hid_device_t * context){
3268eb8d463SMatthias Ringwald     uint8_t event[5];
3278eb8d463SMatthias Ringwald     int pos = 0;
3288eb8d463SMatthias Ringwald     event[pos++] = HCI_EVENT_HID_META;
3298eb8d463SMatthias Ringwald     pos++;  // skip len
3308eb8d463SMatthias Ringwald     event[pos++] = HID_SUBEVENT_CAN_SEND_NOW;
3318eb8d463SMatthias Ringwald     little_endian_store_16(event,pos,context->cid);
3328eb8d463SMatthias Ringwald     pos+=2;
3338eb8d463SMatthias Ringwald     event[1] = pos - 2;
3348eb8d463SMatthias Ringwald     if (pos != sizeof(event)) log_error("hid_device_emit_can_send_now_event size %u", pos);
3358eb8d463SMatthias Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3368eb8d463SMatthias Ringwald }
3378eb8d463SMatthias Ringwald 
3386510739bSMilanka Ringwald static inline void hid_device_emit_event(hid_device_t * context, uint8_t subevent_type){
3396510739bSMilanka Ringwald     uint8_t event[4];
3406510739bSMilanka Ringwald     int pos = 0;
3416510739bSMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
3426510739bSMilanka Ringwald     pos++;  // skip len
3436510739bSMilanka Ringwald     event[pos++] = subevent_type;
3446510739bSMilanka Ringwald     little_endian_store_16(event,pos,context->cid);
3456510739bSMilanka Ringwald     pos+=2;
3466510739bSMilanka Ringwald     event[1] = pos - 2;
3476510739bSMilanka Ringwald     if (pos != sizeof(event)) log_error("hid_device_emit_event size %u", pos);
3486510739bSMilanka Ringwald     hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3496510739bSMilanka Ringwald }
3506510739bSMilanka Ringwald 
3518eb8d463SMatthias Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
3528eb8d463SMatthias Ringwald     UNUSED(channel);
3538eb8d463SMatthias Ringwald     UNUSED(packet_size);
3548eb8d463SMatthias Ringwald     int connected_before;
35557c643eeSMatthias Ringwald     uint16_t psm;
35657c643eeSMatthias Ringwald     uint8_t status;
357c12110e4SMilanka Ringwald     hid_device_t * device = NULL;
3586510739bSMilanka Ringwald     uint8_t param;
3596510739bSMilanka Ringwald     bd_addr_t address;
3607d26fe66SMilanka Ringwald     uint16_t local_cid;
361ba9a58feSMilanka Ringwald 
362ba9a58feSMilanka Ringwald     int report_size;
363ba9a58feSMilanka Ringwald     uint8_t report[20];
364c12110e4SMilanka Ringwald 
3658eb8d463SMatthias Ringwald     switch (packet_type){
366c12110e4SMilanka Ringwald         case L2CAP_DATA_PACKET:
367c12110e4SMilanka Ringwald             device = hid_device_get_instance_for_cid(channel);
368c12110e4SMilanka Ringwald             if (!device) {
369c12110e4SMilanka Ringwald                 log_error("no device with cid 0x%02x", channel);
370c12110e4SMilanka Ringwald                 return;
371c12110e4SMilanka Ringwald             }
372c12110e4SMilanka Ringwald             hid_message_type_t message_type = packet[0] >> 4;
373*df3cc7a0SMilanka Ringwald             // printf("L2CAP_DATA_PACKET message_type %d, packet_size %d  \n", message_type, packet_size);
374*df3cc7a0SMilanka Ringwald             printf_hexdump(packet, packet_size);
3757d26fe66SMilanka Ringwald 
376c12110e4SMilanka Ringwald             switch (message_type){
377c12110e4SMilanka Ringwald                 case HID_MESSAGE_TYPE_GET_REPORT:
378481c7cdcSMilanka Ringwald                     device->report_type = packet[0] & 0x03;
379c12110e4SMilanka Ringwald                     device->report_id = 0;
3807d26fe66SMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
3817d26fe66SMilanka Ringwald 
3827d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
3837d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
384*df3cc7a0SMilanka Ringwald                             // printf("HID_PROTOCOL_MODE_BOOT \n");
3857d26fe66SMilanka Ringwald                             if (!hid_report_ids_declared || packet_size < 2){
386ba9a58feSMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
387ba9a58feSMilanka Ringwald                                 break;
388ba9a58feSMilanka Ringwald                             }
3897d26fe66SMilanka Ringwald                             device->report_id = packet[1];
3907d26fe66SMilanka Ringwald                             break;
3917d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
392*df3cc7a0SMilanka Ringwald                             // printf("HID_PROTOCOL_MODE_REPORT,  hid_report_ids_declared %d\n", hid_report_ids_declared);
3937d26fe66SMilanka Ringwald                             if (!hid_report_ids_declared) break;
3947d26fe66SMilanka Ringwald 
3957d26fe66SMilanka Ringwald                             if (packet_size < 2){
3967d26fe66SMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
3977d26fe66SMilanka Ringwald                                 break;
3987d26fe66SMilanka Ringwald                             }
3997d26fe66SMilanka Ringwald                             device->report_id = packet[1];
4007d26fe66SMilanka Ringwald                             break;
4017d26fe66SMilanka Ringwald                     }
4027d26fe66SMilanka Ringwald 
4037d26fe66SMilanka Ringwald                     if ((packet[0] & 0x08) && packet_size >= 4){
4047d26fe66SMilanka Ringwald                         device->max_packet_size = little_endian_read_16(packet, 2);
4057d26fe66SMilanka Ringwald                     } else {
4067d26fe66SMilanka Ringwald                         device->max_packet_size = l2cap_max_mtu();
4077d26fe66SMilanka Ringwald                     }
4087d26fe66SMilanka Ringwald                     device->state = HID_DEVICE_W2_GET_REPORT;
4097d26fe66SMilanka Ringwald                     // l2cap_request_can_send_now_event(device->control_cid);
4107d26fe66SMilanka Ringwald                     hid_device_request_can_send_now_event(channel);
4117d26fe66SMilanka Ringwald                     break;
4127d26fe66SMilanka Ringwald 
4137d26fe66SMilanka Ringwald                 case HID_MESSAGE_TYPE_SET_REPORT:
4147d26fe66SMilanka Ringwald                     device->state = HID_DEVICE_W2_SET_REPORT;
4157d26fe66SMilanka Ringwald                     device->max_packet_size = l2cap_max_mtu();
4167d26fe66SMilanka Ringwald                     if (packet_size < 1){
4177d26fe66SMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4187d26fe66SMilanka Ringwald                         break;
4197d26fe66SMilanka Ringwald                     }
4207d26fe66SMilanka Ringwald 
4217d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
4227d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
423*df3cc7a0SMilanka Ringwald                             // printf("HID_PROTOCOL_MODE_BOOT \n");
4247d26fe66SMilanka Ringwald                             if (packet_size < 3){
4257d26fe66SMilanka Ringwald                                 device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4267d26fe66SMilanka Ringwald                                 break;
4277d26fe66SMilanka Ringwald                             }
4287d26fe66SMilanka Ringwald                             device->report_id = packet[1];
4297d26fe66SMilanka Ringwald                             device->report_status = (*hci_device_set_report)(device->cid, device->report_type, device->max_packet_size-1, &packet[1]);
4307d26fe66SMilanka Ringwald                             break;
4317d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
432*df3cc7a0SMilanka Ringwald                             // printf("HID_PROTOCOL_MODE_REPORT \n");
4337d26fe66SMilanka Ringwald                             if (packet_size >= 2){
4347d26fe66SMilanka Ringwald                                 device->report_status = (*hci_device_set_report)(device->cid, device->report_type, device->max_packet_size-1, &packet[1]);
4357d26fe66SMilanka Ringwald                             } else {
4367d26fe66SMilanka Ringwald                                 uint8_t payload[] = {0};
4377d26fe66SMilanka Ringwald                                 device->report_status = (*hci_device_set_report)(device->cid, device->report_type, device->max_packet_size-1, payload);
4387d26fe66SMilanka Ringwald                             }
4397d26fe66SMilanka Ringwald                             break;
4407d26fe66SMilanka Ringwald                     }
441ba9a58feSMilanka Ringwald                     device->report_type = packet[0] & 0x03;
4427d26fe66SMilanka Ringwald                     hid_device_request_can_send_now_event(channel);
4437d26fe66SMilanka Ringwald                     // l2cap_request_can_send_now_event(device->control_cid);
444ba9a58feSMilanka Ringwald                     break;
445ba9a58feSMilanka Ringwald                 case HID_MESSAGE_TYPE_GET_PROTOCOL:
446*df3cc7a0SMilanka Ringwald                     // printf(" HID_MESSAGE_TYPE_GET_PROTOCOL\n");
447ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_W2_GET_PROTOCOL;
448ba9a58feSMilanka Ringwald                     if (packet_size != 1) {
449ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
450ba9a58feSMilanka Ringwald                         break;
451ba9a58feSMilanka Ringwald                     }
452ba9a58feSMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
453*df3cc7a0SMilanka Ringwald                     // hid_device_request_can_send_now_event(channel);
454*df3cc7a0SMilanka Ringwald                      // printf("HID_MESSAGE_TYPE_GET_PROTOCOL l2cap_request_can_send_now_event\n");
455*df3cc7a0SMilanka Ringwald                     l2cap_request_can_send_now_event(device->control_cid);
4567d26fe66SMilanka Ringwald                     break;
457ba9a58feSMilanka Ringwald 
458ba9a58feSMilanka Ringwald                 case HID_MESSAGE_TYPE_SET_PROTOCOL:
459ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_W2_SET_PROTOCOL;
460ba9a58feSMilanka Ringwald                     if (packet_size != 1) {
461ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
462ba9a58feSMilanka Ringwald                         break;
463ba9a58feSMilanka Ringwald                     }
464ba9a58feSMilanka Ringwald                     param = packet[0] & 0x01;
465ba9a58feSMilanka Ringwald                     if (param == HID_PROTOCOL_MODE_BOOT && !hid_boot_protocol_mode_supported){
466ba9a58feSMilanka Ringwald                         device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
467ba9a58feSMilanka Ringwald                         break;
468ba9a58feSMilanka Ringwald                     }
469ba9a58feSMilanka Ringwald                     device->protocol_mode = param;
4707d26fe66SMilanka Ringwald                     switch (device->protocol_mode){
4717d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
472*df3cc7a0SMilanka Ringwald                             // printf("Set protocol mode to BOOT\n");
473ba9a58feSMilanka Ringwald                             break;
4747d26fe66SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
475*df3cc7a0SMilanka Ringwald                             // printf("Set protocol mode to REPORT\n");
4767d26fe66SMilanka Ringwald                             break;
4777d26fe66SMilanka Ringwald                     }
4787d26fe66SMilanka Ringwald                     device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
4797d26fe66SMilanka Ringwald                     hid_device_request_can_send_now_event(channel);
4807d26fe66SMilanka Ringwald                     break;
4817d26fe66SMilanka Ringwald 
4826510739bSMilanka Ringwald                 case HID_MESSAGE_TYPE_HID_CONTROL:
4836510739bSMilanka Ringwald                     param = packet[0] & 0x0F;
4846510739bSMilanka Ringwald                     switch (param){
4856510739bSMilanka Ringwald                         case HID_CONTROL_PARAM_SUSPEND:
4866510739bSMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_SUSPEND);
4876510739bSMilanka Ringwald                             break;
4886510739bSMilanka Ringwald                         case HID_CONTROL_PARAM_EXIT_SUSPEND:
4896510739bSMilanka Ringwald                             hid_device_emit_event(device, HID_SUBEVENT_EXIT_SUSPEND);
4906510739bSMilanka Ringwald                             break;
4916510739bSMilanka Ringwald                         default:
4926510739bSMilanka Ringwald                             device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
4937d26fe66SMilanka Ringwald                             hid_device_request_can_send_now_event(channel);
4947d26fe66SMilanka Ringwald                             // l2cap_request_can_send_now_event(device->control_cid);
4956510739bSMilanka Ringwald                             break;
4966510739bSMilanka Ringwald                     }
4976510739bSMilanka Ringwald                     break;
4987d26fe66SMilanka Ringwald 
4997d26fe66SMilanka Ringwald                 case HID_MESSAGE_TYPE_DATA:
5007d26fe66SMilanka Ringwald                     if (packet_size < 2) {
5017d26fe66SMilanka Ringwald                         break;
5027d26fe66SMilanka Ringwald                     }
5037d26fe66SMilanka Ringwald                     device->report_type = packet[0] & 0x03;
5047d26fe66SMilanka Ringwald                     device->report_id = packet[1];
5057d26fe66SMilanka Ringwald                     (*hci_device_report_data)(device->cid, device->report_type, device->report_id, packet_size - 2, &packet[2]);
5067d26fe66SMilanka Ringwald                     break;
507c12110e4SMilanka Ringwald                 default:
508*df3cc7a0SMilanka Ringwald                     printf("HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST %d  \n", message_type);
509c12110e4SMilanka Ringwald                     device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
5107d26fe66SMilanka Ringwald                     // l2cap_request_can_send_now_event(device->control_cid);
5117d26fe66SMilanka Ringwald                     hid_device_request_can_send_now_event(channel);
512c12110e4SMilanka Ringwald                     break;
513c12110e4SMilanka Ringwald             }
514c12110e4SMilanka Ringwald             break;
5158eb8d463SMatthias Ringwald         case HCI_EVENT_PACKET:
5168eb8d463SMatthias Ringwald             switch (packet[0]){
5178eb8d463SMatthias Ringwald                 case L2CAP_EVENT_INCOMING_CONNECTION:
5188eb8d463SMatthias Ringwald                     switch (l2cap_event_incoming_connection_get_psm(packet)){
5198eb8d463SMatthias Ringwald                         case PSM_HID_CONTROL:
5208eb8d463SMatthias Ringwald                         case PSM_HID_INTERRUPT:
5216510739bSMilanka Ringwald                             l2cap_event_incoming_connection_get_address(packet, address);
522ba9a58feSMilanka Ringwald                             device = hid_device_provide_instance_for_bt_addr(address);
523c12110e4SMilanka Ringwald                             if (!device) {
524c12110e4SMilanka Ringwald                                 log_error("L2CAP_EVENT_INCOMING_CONNECTION, no hid device for con handle 0x%02x", l2cap_event_incoming_connection_get_handle(packet));
525c12110e4SMilanka Ringwald                                 l2cap_decline_connection(channel);
526c12110e4SMilanka Ringwald                                 break;
527c12110e4SMilanka Ringwald                             }
528c12110e4SMilanka Ringwald                             if (device->con_handle == 0 || l2cap_event_incoming_connection_get_handle(packet) == device->con_handle){
529c12110e4SMilanka Ringwald                                 device->con_handle = l2cap_event_incoming_connection_get_handle(packet);
530c12110e4SMilanka Ringwald                                 device->incoming = 1;
5316510739bSMilanka Ringwald                                 l2cap_event_incoming_connection_get_address(packet, device->bd_addr);
5328eb8d463SMatthias Ringwald                                 l2cap_accept_connection(channel);
5338eb8d463SMatthias Ringwald                             } else {
5348eb8d463SMatthias Ringwald                                 l2cap_decline_connection(channel);
535c12110e4SMilanka Ringwald                                 log_error("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for con handle 0x%02x", l2cap_event_incoming_connection_get_handle(packet));
5368eb8d463SMatthias Ringwald                             }
5378eb8d463SMatthias Ringwald                             break;
5388eb8d463SMatthias Ringwald                         default:
5398eb8d463SMatthias Ringwald                             l2cap_decline_connection(channel);
5408eb8d463SMatthias Ringwald                             break;
5418eb8d463SMatthias Ringwald                     }
5428eb8d463SMatthias Ringwald                     break;
5438eb8d463SMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_OPENED:
544c12110e4SMilanka Ringwald                     device = hid_device_get_instance_for_con_handle(l2cap_event_channel_opened_get_handle(packet));
545c12110e4SMilanka Ringwald                     if (!device) {
546c12110e4SMilanka Ringwald                         log_error("L2CAP_EVENT_CHANNEL_OPENED, no hid device for local cid 0x%02x", l2cap_event_channel_opened_get_local_cid(packet));
547c12110e4SMilanka Ringwald                         return;
548c12110e4SMilanka Ringwald                     }
54957c643eeSMatthias Ringwald                     status = l2cap_event_channel_opened_get_status(packet);
55057c643eeSMatthias Ringwald                     if (status) {
551c12110e4SMilanka Ringwald                         if (device->incoming == 0){
55257c643eeSMatthias Ringwald                             // report error for outgoing connection
553c12110e4SMilanka Ringwald                             hid_device_emit_connected_event(device, status);
55457c643eeSMatthias Ringwald                         }
55557c643eeSMatthias Ringwald                         return;
55657c643eeSMatthias Ringwald                     }
55757c643eeSMatthias Ringwald                     psm = l2cap_event_channel_opened_get_psm(packet);
558c12110e4SMilanka Ringwald                     connected_before = device->connected;
55957c643eeSMatthias Ringwald                     switch (psm){
5608eb8d463SMatthias Ringwald                         case PSM_HID_CONTROL:
561c12110e4SMilanka Ringwald                             device->control_cid = l2cap_event_channel_opened_get_local_cid(packet);
562*df3cc7a0SMilanka Ringwald                             // printf("HID Control opened, cid 0x%02x\n", device->control_cid);
5638eb8d463SMatthias Ringwald                             break;
5648eb8d463SMatthias Ringwald                         case PSM_HID_INTERRUPT:
565c12110e4SMilanka Ringwald                             device->interrupt_cid = l2cap_event_channel_opened_get_local_cid(packet);
566*df3cc7a0SMilanka Ringwald                             // printf("HID Interrupt opened, cid 0x%02x\n", device->interrupt_cid);
5678eb8d463SMatthias Ringwald                             break;
5688eb8d463SMatthias Ringwald                         default:
5698eb8d463SMatthias Ringwald                             break;
5708eb8d463SMatthias Ringwald                     }
5716510739bSMilanka Ringwald 
57257c643eeSMatthias Ringwald                     // connect HID Interrupt for outgoing
573c12110e4SMilanka Ringwald                     if (device->incoming == 0 && psm == PSM_HID_CONTROL){
574*df3cc7a0SMilanka Ringwald                         // printf("Create outgoing HID Interrupt\n");
575c12110e4SMilanka Ringwald                         status = l2cap_create_channel(packet_handler, device->bd_addr, PSM_HID_INTERRUPT, 48, &device->interrupt_cid);
57657c643eeSMatthias Ringwald                         break;
57757c643eeSMatthias Ringwald                     }
578c12110e4SMilanka Ringwald                     if (!connected_before && device->control_cid && device->interrupt_cid){
579c12110e4SMilanka Ringwald                         device->connected = 1;
580*df3cc7a0SMilanka Ringwald                         // printf("HID Connected\n");
581c12110e4SMilanka Ringwald                         hid_device_emit_connected_event(device, 0);
5828eb8d463SMatthias Ringwald                     }
5838eb8d463SMatthias Ringwald                     break;
5848eb8d463SMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_CLOSED:
585c12110e4SMilanka Ringwald                     device = hid_device_get_instance_for_cid(l2cap_event_channel_closed_get_local_cid(packet));
586c12110e4SMilanka Ringwald                     if (!device) return;
587c12110e4SMilanka Ringwald 
5886510739bSMilanka Ringwald                     // connected_before = device->connected;
589c12110e4SMilanka Ringwald                     device->incoming  = 0;
590c12110e4SMilanka Ringwald                     if (l2cap_event_channel_closed_get_local_cid(packet) == device->interrupt_cid){
591*df3cc7a0SMilanka Ringwald                         // printf("HID Interrupt closed\n");
592c12110e4SMilanka Ringwald                         device->interrupt_cid = 0;
59357c18996SMilanka Ringwald                     }
59457c18996SMilanka Ringwald                     if (l2cap_event_channel_closed_get_local_cid(packet) == device->control_cid){
595*df3cc7a0SMilanka Ringwald                         // printf("HID Control closed\n");
59657c18996SMilanka Ringwald                         device->control_cid = 0;
5978eb8d463SMatthias Ringwald                     }
5986510739bSMilanka Ringwald                     if (!device->interrupt_cid && !device->control_cid){
5996510739bSMilanka Ringwald                         device->connected = 0;
600c12110e4SMilanka Ringwald                         device->con_handle = 0;
6016510739bSMilanka Ringwald                         device->cid = 0;
602*df3cc7a0SMilanka Ringwald                         // printf("HID Disconnected\n");
603c12110e4SMilanka Ringwald                         hid_device_emit_connection_closed_event(device);
6048eb8d463SMatthias Ringwald                     }
6058eb8d463SMatthias Ringwald                     break;
606c12110e4SMilanka Ringwald 
60757c18996SMilanka Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
6087d26fe66SMilanka Ringwald                     local_cid = l2cap_event_can_send_now_get_local_cid(packet);
609c12110e4SMilanka Ringwald                     device = hid_device_get_instance_for_cid(l2cap_event_can_send_now_get_local_cid(packet));
6107d26fe66SMilanka Ringwald 
611c12110e4SMilanka Ringwald                     if (!device) return;
612c12110e4SMilanka Ringwald                     switch (device->state){
613ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_GET_REPORT:
614*df3cc7a0SMilanka Ringwald                             // printf("HID_DEVICE_W2_GET_REPORT \n");
6157d26fe66SMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) {
6167d26fe66SMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
6177d26fe66SMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
6187d26fe66SMilanka Ringwald                                 break;
6197d26fe66SMilanka Ringwald                             }
6207d26fe66SMilanka Ringwald                             report_size = 0;
621ba9a58feSMilanka Ringwald                             device->report_status = (*hci_device_write_report)(device->cid, device->report_type, device->report_id, (uint16_t) sizeof(report) - 1, &report_size, &report[1]);
6227d26fe66SMilanka Ringwald                             // add header size
623481c7cdcSMilanka Ringwald                             report_size += 1;
624481c7cdcSMilanka Ringwald 
625*df3cc7a0SMilanka Ringwald                             // printf(" report size %d, status %d, max_packet_size %d\n", report_size, device->report_status, device->max_packet_size);
626ba9a58feSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
627ba9a58feSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
628481c7cdcSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
629481c7cdcSMilanka Ringwald                                 break;
630481c7cdcSMilanka Ringwald                             }
631481c7cdcSMilanka Ringwald 
632481c7cdcSMilanka Ringwald                             if (report_size > l2cap_max_mtu()){
633481c7cdcSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
634481c7cdcSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
635481c7cdcSMilanka Ringwald                                 break;
636481c7cdcSMilanka Ringwald                             }
637481c7cdcSMilanka Ringwald 
638*df3cc7a0SMilanka Ringwald                             // printf("report type %d, report_size %d, max_packet_size %d \n", device->report_type, report_size, device->max_packet_size);
639481c7cdcSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_DATA << 4) | device->report_type;
6407d26fe66SMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], btstack_min(report_size, device->max_packet_size));
641481c7cdcSMilanka Ringwald                             //     device->state = HID_DEVICE_IDLE;
642c12110e4SMilanka Ringwald                             break;
643ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SET_REPORT:
644ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SET_PROTOCOL:
645ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
6466510739bSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
647ba9a58feSMilanka Ringwald                             break;
648ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_GET_PROTOCOL:
649ba9a58feSMilanka Ringwald                             if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
650*df3cc7a0SMilanka Ringwald                                 // printf("send HID_MESSAGE_TYPE_HANDSHAKE, report_status %d \n", device->report_status);
651ba9a58feSMilanka Ringwald                                 report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
652ba9a58feSMilanka Ringwald                                 hid_device_send_control_message(device->cid, &report[0], 1);
653ba9a58feSMilanka Ringwald                                 break;
654ba9a58feSMilanka Ringwald                             }
655*df3cc7a0SMilanka Ringwald                             // printf("send HID_MESSAGE_TYPE_DATA, protocol_mode %d \n", device->protocol_mode);
656ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_DATA << 4) | device->protocol_mode;
657ba9a58feSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
658ba9a58feSMilanka Ringwald                             break;
659ba9a58feSMilanka Ringwald 
660ba9a58feSMilanka Ringwald 
661ba9a58feSMilanka Ringwald                         case HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST:
662ba9a58feSMilanka Ringwald                             report[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
663ba9a58feSMilanka Ringwald                             hid_device_send_control_message(device->cid, &report[0], 1);
664c12110e4SMilanka Ringwald                             break;
665c12110e4SMilanka Ringwald                         default:
6668eb8d463SMatthias Ringwald                             log_info("HID Can send now, emit event");
667c12110e4SMilanka Ringwald                             hid_device_emit_can_send_now_event(device);
668ba9a58feSMilanka Ringwald                             // device->state = HID_DEVICE_IDLE;
669c12110e4SMilanka Ringwald                             break;
670c12110e4SMilanka Ringwald                     }
671ba9a58feSMilanka Ringwald                     device->state = HID_DEVICE_IDLE;
6728eb8d463SMatthias Ringwald                     break;
6738eb8d463SMatthias Ringwald                 default:
6748eb8d463SMatthias Ringwald                     break;
6758eb8d463SMatthias Ringwald             }
6768eb8d463SMatthias Ringwald             break;
6778eb8d463SMatthias Ringwald         default:
6788eb8d463SMatthias Ringwald             break;
6798eb8d463SMatthias Ringwald     }
6808eb8d463SMatthias Ringwald }
6818eb8d463SMatthias Ringwald 
6828eb8d463SMatthias Ringwald /**
6838eb8d463SMatthias Ringwald  * @brief Set up HID Device
6848eb8d463SMatthias Ringwald  */
6857d26fe66SMilanka Ringwald void hid_device_init(uint8_t boot_protocol_mode_supported, uint8_t report_ids_declared){
686ba9a58feSMilanka Ringwald     hid_boot_protocol_mode_supported = boot_protocol_mode_supported;
6877d26fe66SMilanka Ringwald     hid_report_ids_declared = report_ids_declared;
68845a58b30SMatthias Ringwald     l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, LEVEL_2);
68945a58b30SMatthias Ringwald     l2cap_register_service(packet_handler, PSM_HID_CONTROL,   100, LEVEL_2);
6908eb8d463SMatthias Ringwald }
6918eb8d463SMatthias Ringwald 
6928eb8d463SMatthias Ringwald /**
6938eb8d463SMatthias Ringwald  * @brief Register callback for the HID Device client.
6948eb8d463SMatthias Ringwald  * @param callback
6958eb8d463SMatthias Ringwald  */
6968eb8d463SMatthias Ringwald void hid_device_register_packet_handler(btstack_packet_handler_t callback){
6978eb8d463SMatthias Ringwald     hid_callback = callback;
6988eb8d463SMatthias Ringwald }
6998eb8d463SMatthias Ringwald 
700c12110e4SMilanka Ringwald 
701c12110e4SMilanka Ringwald 
702481c7cdcSMilanka Ringwald void hid_device_register_report_request_callback(hid_handshake_param_type_t (*callback) (uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, uint8_t report_max_size, int * out_report_size, uint8_t * out_report)){
703c12110e4SMilanka Ringwald     if (callback == NULL){
704c12110e4SMilanka Ringwald         callback = dummy_write_report;
705c12110e4SMilanka Ringwald     }
706c12110e4SMilanka Ringwald     hci_device_write_report = callback;
707c12110e4SMilanka Ringwald }
708c12110e4SMilanka Ringwald 
709ba9a58feSMilanka Ringwald void hid_device_register_set_report_callback(hid_handshake_param_type_t (*callback) (uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report)){
710ba9a58feSMilanka Ringwald     if (callback == NULL){
711ba9a58feSMilanka Ringwald         callback = dummy_set_report;
712ba9a58feSMilanka Ringwald     }
713ba9a58feSMilanka Ringwald     hci_device_set_report = callback;
714ba9a58feSMilanka Ringwald }
715c12110e4SMilanka Ringwald 
7167d26fe66SMilanka 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)){
7177d26fe66SMilanka Ringwald     if (callback == NULL){
7187d26fe66SMilanka Ringwald         callback = dummy_report_data;
7197d26fe66SMilanka Ringwald     }
7207d26fe66SMilanka Ringwald     hci_device_report_data = callback;
7217d26fe66SMilanka Ringwald }
7227d26fe66SMilanka Ringwald 
7237d26fe66SMilanka Ringwald 
7248eb8d463SMatthias Ringwald /**
7258eb8d463SMatthias Ringwald  * @brief Request can send now event to send HID Report
7268eb8d463SMatthias Ringwald  * @param hid_cid
7278eb8d463SMatthias Ringwald  */
7288eb8d463SMatthias Ringwald void hid_device_request_can_send_now_event(uint16_t hid_cid){
729c12110e4SMilanka Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_cid(hid_cid);
7307d26fe66SMilanka Ringwald     if (!hid_device || !hid_device->control_cid){
7317d26fe66SMilanka Ringwald          hid_device->state = HID_DEVICE_IDLE;
7327d26fe66SMilanka Ringwald          return;
7337d26fe66SMilanka Ringwald     }
7348eb8d463SMatthias Ringwald     l2cap_request_can_send_now_event(hid_device->control_cid);
7358eb8d463SMatthias Ringwald }
7368eb8d463SMatthias Ringwald 
7378eb8d463SMatthias Ringwald /**
7388eb8d463SMatthias Ringwald  * @brief Send HID messageon interrupt channel
7398eb8d463SMatthias Ringwald  * @param hid_cid
7408eb8d463SMatthias Ringwald  */
7418eb8d463SMatthias Ringwald void hid_device_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
742c12110e4SMilanka Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_cid(hid_cid);
743c12110e4SMilanka Ringwald     if (!hid_device || !hid_device->interrupt_cid) return;
7448eb8d463SMatthias Ringwald     l2cap_send(hid_device->interrupt_cid, (uint8_t*) message, message_len);
7458eb8d463SMatthias Ringwald }
7468eb8d463SMatthias Ringwald 
7478eb8d463SMatthias Ringwald /**
7488eb8d463SMatthias Ringwald  * @brief Send HID messageon control channel
7498eb8d463SMatthias Ringwald  * @param hid_cid
7508eb8d463SMatthias Ringwald  */
751c12110e4SMilanka Ringwald void hid_device_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
752c12110e4SMilanka Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_cid(hid_cid);
753c12110e4SMilanka Ringwald     if (!hid_device || !hid_device->control_cid) return;
7548eb8d463SMatthias Ringwald     l2cap_send(hid_device->control_cid, (uint8_t*) message, message_len);
7558eb8d463SMatthias Ringwald }
7568eb8d463SMatthias Ringwald 
75757c643eeSMatthias Ringwald /*
75857c643eeSMatthias Ringwald  * @brief Create HID connection to HID Host
75957c643eeSMatthias Ringwald  * @param addr
76057c643eeSMatthias Ringwald  * @param hid_cid to use for other commands
76157c643eeSMatthias Ringwald  * @result status
76257c643eeSMatthias Ringwald  */
76357c643eeSMatthias Ringwald uint8_t hid_device_connect(bd_addr_t addr, uint16_t * hid_cid){
764c12110e4SMilanka Ringwald     hid_device_t * hid_device = hid_device_create_instance();
765c12110e4SMilanka Ringwald     if (!hid_device){
766c12110e4SMilanka Ringwald         log_error("hid_device_connect: could not create a hid device instace");
767c12110e4SMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
768c12110e4SMilanka Ringwald     }
76957c643eeSMatthias Ringwald     // assign hic_cid
770c12110e4SMilanka Ringwald     *hid_cid = hid_device_get_next_cid();
77157c643eeSMatthias Ringwald     // store address
77257c643eeSMatthias Ringwald     memcpy(hid_device->bd_addr, addr, 6);
77357c643eeSMatthias Ringwald 
77457c643eeSMatthias Ringwald     // reset state
7756510739bSMilanka Ringwald     hid_device->cid           = *hid_cid;
77657c643eeSMatthias Ringwald     hid_device->incoming      = 0;
77757c643eeSMatthias Ringwald     hid_device->connected     = 0;
77857c643eeSMatthias Ringwald     hid_device->control_cid   = 0;
77957c643eeSMatthias Ringwald     hid_device->interrupt_cid = 0;
78057c643eeSMatthias Ringwald     // create l2cap control using fixed HID L2CAP PSM
78157c643eeSMatthias Ringwald     log_info("Create outgoing HID Control");
78257c643eeSMatthias Ringwald     uint8_t status = l2cap_create_channel(packet_handler, hid_device->bd_addr, PSM_HID_CONTROL, 48, &hid_device->control_cid);
78357c643eeSMatthias Ringwald     return status;
78457c643eeSMatthias Ringwald }
7856510739bSMilanka Ringwald 
7866510739bSMilanka Ringwald /*
7876510739bSMilanka Ringwald  * @brief Disconnect from HID Host
7886510739bSMilanka Ringwald  * @param hid_cid
7896510739bSMilanka Ringwald  * @result status
7906510739bSMilanka Ringwald  */
79157c18996SMilanka Ringwald void hid_device_disconnect_interrupt_channel(uint16_t hid_cid){
79257c18996SMilanka Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_cid(hid_cid);
79357c18996SMilanka Ringwald     if (!hid_device){
79457c18996SMilanka Ringwald         log_error("hid_device_disconnect_interrupt_channel: could not find hid device instace");
79557c18996SMilanka Ringwald         return;
79657c18996SMilanka Ringwald     }
79757c18996SMilanka Ringwald     log_info("Disconnect from interrupt channel HID Host");
79857c18996SMilanka Ringwald     if (hid_device->interrupt_cid){
79957c18996SMilanka Ringwald         l2cap_disconnect(hid_device->interrupt_cid, 0);  // reason isn't used
80057c18996SMilanka Ringwald     }
80157c18996SMilanka Ringwald }
80257c18996SMilanka Ringwald 
80357c18996SMilanka Ringwald void hid_device_disconnect_control_channel(uint16_t hid_cid){
80457c18996SMilanka Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_cid(hid_cid);
80557c18996SMilanka Ringwald     if (!hid_device){
80657c18996SMilanka Ringwald         log_error("hid_device_disconnect_control_channel: could not find hid device instace");
80757c18996SMilanka Ringwald         return;
80857c18996SMilanka Ringwald     }
80957c18996SMilanka Ringwald     log_info("Disconnect from control channel HID Host");
81057c18996SMilanka Ringwald     if (hid_device->control_cid){
81157c18996SMilanka Ringwald         l2cap_disconnect(hid_device->control_cid, 0);  // reason isn't used
81257c18996SMilanka Ringwald     }
81357c18996SMilanka Ringwald }
81457c18996SMilanka Ringwald 
8156510739bSMilanka Ringwald void hid_device_disconnect(uint16_t hid_cid){
8166510739bSMilanka Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_cid(hid_cid);
8176510739bSMilanka Ringwald     if (!hid_device){
8186510739bSMilanka Ringwald         log_error("hid_device_disconnect: could not find hid device instace");
8196510739bSMilanka Ringwald         return;
8206510739bSMilanka Ringwald     }
8216510739bSMilanka Ringwald     log_info("Disconnect from HID Host");
8226510739bSMilanka Ringwald     if (hid_device->interrupt_cid){
8236510739bSMilanka Ringwald         l2cap_disconnect(hid_device->interrupt_cid, 0);  // reason isn't used
8246510739bSMilanka Ringwald     }
82557c18996SMilanka Ringwald     if (hid_device->control_cid){
82657c18996SMilanka Ringwald         l2cap_disconnect(hid_device->control_cid, 0); // reason isn't used
82757c18996SMilanka Ringwald     }
8286510739bSMilanka Ringwald }
8297d26fe66SMilanka Ringwald 
8307d26fe66SMilanka Ringwald int hid_device_in_boot_protocol_mode(uint16_t hid_cid){
8317d26fe66SMilanka Ringwald     hid_device_t * hid_device = hid_device_get_instance_for_cid(hid_cid);
8327d26fe66SMilanka Ringwald     if (!hid_device){
8337d26fe66SMilanka Ringwald         log_error("hid_device_in_boot_protocol_mode: could not find hid device instace");
8347d26fe66SMilanka Ringwald         return 0;
8357d26fe66SMilanka Ringwald     }
8367d26fe66SMilanka Ringwald     return hid_device->protocol_mode == HID_PROTOCOL_MODE_BOOT;
8377d26fe66SMilanka Ringwald }
838