xref: /btstack/src/classic/hid_host.c (revision fd7ba7a6d25e1504d0db40d88bc9982ee89b87bd)
163bf37cdSMilanka Ringwald /*
263bf37cdSMilanka Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
363bf37cdSMilanka Ringwald  *
463bf37cdSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
563bf37cdSMilanka Ringwald  * modification, are permitted provided that the following conditions
663bf37cdSMilanka Ringwald  * are met:
763bf37cdSMilanka Ringwald  *
863bf37cdSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
963bf37cdSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
1063bf37cdSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
1163bf37cdSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
1263bf37cdSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
1363bf37cdSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
1463bf37cdSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
1563bf37cdSMilanka Ringwald  *    from this software without specific prior written permission.
1663bf37cdSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
1763bf37cdSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
1863bf37cdSMilanka Ringwald  *    monetary gain.
1963bf37cdSMilanka Ringwald  *
2063bf37cdSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
2163bf37cdSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2263bf37cdSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2363bf37cdSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
2463bf37cdSMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2563bf37cdSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2663bf37cdSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
2763bf37cdSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2863bf37cdSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2963bf37cdSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
3063bf37cdSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3163bf37cdSMilanka Ringwald  * SUCH DAMAGE.
3263bf37cdSMilanka Ringwald  *
3363bf37cdSMilanka Ringwald  * Please inquire about commercial licensing options at
3463bf37cdSMilanka Ringwald  * [email protected]
3563bf37cdSMilanka Ringwald  *
3663bf37cdSMilanka Ringwald  */
3763bf37cdSMilanka Ringwald 
3863bf37cdSMilanka Ringwald #define BTSTACK_FILE__ "hid_host.c"
3963bf37cdSMilanka Ringwald 
4063bf37cdSMilanka Ringwald #include <string.h>
4163bf37cdSMilanka Ringwald 
4263bf37cdSMilanka Ringwald #include "bluetooth.h"
4363bf37cdSMilanka Ringwald #include "bluetooth_psm.h"
4463bf37cdSMilanka Ringwald #include "bluetooth_sdp.h"
4563bf37cdSMilanka Ringwald #include "btstack_debug.h"
4663bf37cdSMilanka Ringwald #include "btstack_event.h"
4763bf37cdSMilanka Ringwald #include "btstack_hid_parser.h"
48*fd7ba7a6SMilanka Ringwald #include "btstack_memory.h"
4963bf37cdSMilanka Ringwald #include "classic/hid.h"
5063bf37cdSMilanka Ringwald #include "classic/hid_host.h"
5163bf37cdSMilanka Ringwald #include "classic/sdp_util.h"
52*fd7ba7a6SMilanka Ringwald #include "classic/sdp_client.h"
5363bf37cdSMilanka Ringwald #include "l2cap.h"
5463bf37cdSMilanka Ringwald 
55*fd7ba7a6SMilanka Ringwald #define MAX_ATTRIBUTE_VALUE_SIZE 300
5663bf37cdSMilanka Ringwald 
57*fd7ba7a6SMilanka Ringwald static btstack_packet_handler_t hid_callback;
5863bf37cdSMilanka Ringwald 
59*fd7ba7a6SMilanka Ringwald static uint8_t * hid_host_descriptor_storage;
6063bf37cdSMilanka Ringwald static uint16_t hid_host_descriptor_storage_len;
6163bf37cdSMilanka Ringwald 
62*fd7ba7a6SMilanka Ringwald static btstack_linked_list_t connections;
63*fd7ba7a6SMilanka Ringwald static uint16_t hid_host_cid_counter = 0;
64*fd7ba7a6SMilanka Ringwald 
65*fd7ba7a6SMilanka Ringwald // SDP
66*fd7ba7a6SMilanka Ringwald static uint8_t            attribute_value[MAX_ATTRIBUTE_VALUE_SIZE];
67*fd7ba7a6SMilanka Ringwald static const unsigned int attribute_value_buffer_size = MAX_ATTRIBUTE_VALUE_SIZE;
68*fd7ba7a6SMilanka Ringwald 
69*fd7ba7a6SMilanka Ringwald static uint16_t           sdp_query_context_hid_host_control_cid = 0;
70*fd7ba7a6SMilanka Ringwald 
71*fd7ba7a6SMilanka Ringwald static btstack_context_callback_registration_t hid_host_handle_sdp_client_query_request;
72*fd7ba7a6SMilanka Ringwald static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
73*fd7ba7a6SMilanka Ringwald 
74*fd7ba7a6SMilanka Ringwald static uint16_t hid_descriptor_storage_get_available_space(void){
75*fd7ba7a6SMilanka Ringwald     // assumes all descriptors are back to back
76*fd7ba7a6SMilanka Ringwald     uint16_t free_space = hid_host_descriptor_storage_len;
77*fd7ba7a6SMilanka Ringwald 
78*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_t it;
79*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_init(&it, &connections);
80*fd7ba7a6SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
81*fd7ba7a6SMilanka Ringwald         hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
82*fd7ba7a6SMilanka Ringwald         free_space -= connection->hid_descriptor_len;
83*fd7ba7a6SMilanka Ringwald     }
84*fd7ba7a6SMilanka Ringwald     return free_space;
85*fd7ba7a6SMilanka Ringwald }
86*fd7ba7a6SMilanka Ringwald 
87*fd7ba7a6SMilanka Ringwald static void hid_descriptor_storage_init(hid_host_connection_t * connection){
88*fd7ba7a6SMilanka Ringwald     connection->hid_descriptor_len = 0;
89*fd7ba7a6SMilanka Ringwald     connection->hid_descriptor_max_len = hid_descriptor_storage_get_available_space();
90*fd7ba7a6SMilanka Ringwald     connection->hid_descriptor_offset = hid_host_descriptor_storage_len - connection->hid_descriptor_max_len;
91*fd7ba7a6SMilanka Ringwald }
92*fd7ba7a6SMilanka Ringwald 
93*fd7ba7a6SMilanka Ringwald static bool hid_descriptor_storage_store(hid_host_connection_t * connection, uint8_t byte){
94*fd7ba7a6SMilanka Ringwald     if (connection->hid_descriptor_len >= connection->hid_descriptor_max_len) return false;
95*fd7ba7a6SMilanka Ringwald 
96*fd7ba7a6SMilanka Ringwald     hid_host_descriptor_storage[connection->hid_descriptor_offset + connection->hid_descriptor_len] = byte;
97*fd7ba7a6SMilanka Ringwald     connection->hid_descriptor_len++;
98*fd7ba7a6SMilanka Ringwald     return true;
99*fd7ba7a6SMilanka Ringwald }
100*fd7ba7a6SMilanka Ringwald 
101*fd7ba7a6SMilanka Ringwald 
102*fd7ba7a6SMilanka Ringwald static void hid_descriptor_storage_delete(hid_host_connection_t * connection){
103*fd7ba7a6SMilanka Ringwald     uint16_t next_offset = connection->hid_descriptor_offset + connection->hid_descriptor_len;
104*fd7ba7a6SMilanka Ringwald 
105*fd7ba7a6SMilanka Ringwald     memmove(&hid_host_descriptor_storage[connection->hid_descriptor_offset],
106*fd7ba7a6SMilanka Ringwald             &hid_host_descriptor_storage[next_offset],
107*fd7ba7a6SMilanka Ringwald             hid_host_descriptor_storage_len - next_offset);
108*fd7ba7a6SMilanka Ringwald 
109*fd7ba7a6SMilanka Ringwald     connection->hid_descriptor_len = 0;
110*fd7ba7a6SMilanka Ringwald     connection->hid_descriptor_offset = 0;
111*fd7ba7a6SMilanka Ringwald 
112*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_t it;
113*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_init(&it, &connections);
114*fd7ba7a6SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
115*fd7ba7a6SMilanka Ringwald         hid_host_connection_t * conn = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
116*fd7ba7a6SMilanka Ringwald         if (conn->hid_descriptor_offset >= next_offset){
117*fd7ba7a6SMilanka Ringwald             conn->hid_descriptor_offset = next_offset;
118*fd7ba7a6SMilanka Ringwald             next_offset += conn->hid_descriptor_len;
119*fd7ba7a6SMilanka Ringwald         }
120*fd7ba7a6SMilanka Ringwald     }
121*fd7ba7a6SMilanka Ringwald }
122*fd7ba7a6SMilanka Ringwald 
123*fd7ba7a6SMilanka Ringwald // HID Util
124*fd7ba7a6SMilanka Ringwald static inline void hid_emit_connected_event(hid_host_connection_t * context, uint8_t status){
125*fd7ba7a6SMilanka Ringwald     uint8_t event[15];
126*fd7ba7a6SMilanka Ringwald     int pos = 0;
127*fd7ba7a6SMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
128*fd7ba7a6SMilanka Ringwald     pos++;  // skip len
129*fd7ba7a6SMilanka Ringwald     event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
130*fd7ba7a6SMilanka Ringwald     little_endian_store_16(event, pos, context->hid_cid);
131*fd7ba7a6SMilanka Ringwald     pos+=2;
132*fd7ba7a6SMilanka Ringwald     event[pos++] = status;
133*fd7ba7a6SMilanka Ringwald     reverse_bd_addr(context->remote_addr, &event[pos]);
134*fd7ba7a6SMilanka Ringwald     pos += 6;
135*fd7ba7a6SMilanka Ringwald     little_endian_store_16(event,pos,context->con_handle);
136*fd7ba7a6SMilanka Ringwald     pos += 2;
137*fd7ba7a6SMilanka Ringwald     event[pos++] = context->incoming;
138*fd7ba7a6SMilanka Ringwald     event[1] = pos - 2;
139*fd7ba7a6SMilanka Ringwald     hid_callback(HCI_EVENT_PACKET, context->hid_cid, &event[0], pos);
140*fd7ba7a6SMilanka Ringwald }
141*fd7ba7a6SMilanka Ringwald 
142*fd7ba7a6SMilanka Ringwald // HID Host
143*fd7ba7a6SMilanka Ringwald 
144*fd7ba7a6SMilanka Ringwald static uint16_t hid_host_get_next_cid(void){
145*fd7ba7a6SMilanka Ringwald     if (hid_host_cid_counter == 0xffff) {
146*fd7ba7a6SMilanka Ringwald         hid_host_cid_counter = 1;
147*fd7ba7a6SMilanka Ringwald     } else {
148*fd7ba7a6SMilanka Ringwald         hid_host_cid_counter++;
149*fd7ba7a6SMilanka Ringwald     }
150*fd7ba7a6SMilanka Ringwald     return hid_host_cid_counter;
151*fd7ba7a6SMilanka Ringwald }
152*fd7ba7a6SMilanka Ringwald 
153*fd7ba7a6SMilanka Ringwald static hid_host_connection_t * hid_host_create_connection(bd_addr_t remote_addr){
154*fd7ba7a6SMilanka Ringwald     hid_host_connection_t * connection = btstack_memory_hid_host_connection_get();
155*fd7ba7a6SMilanka Ringwald     if (!connection){
156*fd7ba7a6SMilanka Ringwald         log_error("Not enough memory to create connection");
157*fd7ba7a6SMilanka Ringwald         return NULL;
158*fd7ba7a6SMilanka Ringwald     }
159*fd7ba7a6SMilanka Ringwald     connection->state = HID_HOST_IDLE;
160*fd7ba7a6SMilanka Ringwald     connection->hid_cid = hid_host_get_next_cid();
161*fd7ba7a6SMilanka Ringwald     (void)memcpy(connection->remote_addr, remote_addr, 6);
162*fd7ba7a6SMilanka Ringwald     printf("hid_host_create_connection, cid 0x%02x, %s \n", connection->hid_cid, bd_addr_to_str(connection->remote_addr));
163*fd7ba7a6SMilanka Ringwald 
164*fd7ba7a6SMilanka Ringwald     btstack_linked_list_add(&connections, (btstack_linked_item_t *) connection);
165*fd7ba7a6SMilanka Ringwald     return connection;
166*fd7ba7a6SMilanka Ringwald }
167*fd7ba7a6SMilanka Ringwald 
168*fd7ba7a6SMilanka Ringwald static hid_host_connection_t * hid_host_get_connection_for_bd_addr(bd_addr_t addr){
169*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_t it;
170*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_init(&it, &connections);
171*fd7ba7a6SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
172*fd7ba7a6SMilanka Ringwald         hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
173*fd7ba7a6SMilanka Ringwald         if (memcmp(addr, connection->remote_addr, 6) != 0) continue;
174*fd7ba7a6SMilanka Ringwald         return connection;
175*fd7ba7a6SMilanka Ringwald     }
176*fd7ba7a6SMilanka Ringwald     return NULL;
177*fd7ba7a6SMilanka Ringwald }
178*fd7ba7a6SMilanka Ringwald 
179*fd7ba7a6SMilanka Ringwald static hid_host_connection_t * hid_host_connection_for_hid_cid(uint16_t hid_cid){
180*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_t it;
181*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_init(&it, &connections);
182*fd7ba7a6SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
183*fd7ba7a6SMilanka Ringwald         hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
184*fd7ba7a6SMilanka Ringwald         if (connection->hid_cid != hid_cid) continue;
185*fd7ba7a6SMilanka Ringwald         return connection;
186*fd7ba7a6SMilanka Ringwald     }
187*fd7ba7a6SMilanka Ringwald     return NULL;
188*fd7ba7a6SMilanka Ringwald }
189*fd7ba7a6SMilanka Ringwald 
190*fd7ba7a6SMilanka Ringwald static void hid_host_finalize_connection(hid_host_connection_t * connection){
191*fd7ba7a6SMilanka Ringwald     btstack_linked_list_remove(&connections, (btstack_linked_item_t*) connection);
192*fd7ba7a6SMilanka Ringwald     btstack_memory_hid_host_connection_free(connection);
193*fd7ba7a6SMilanka Ringwald }
194*fd7ba7a6SMilanka Ringwald 
195*fd7ba7a6SMilanka Ringwald static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
196*fd7ba7a6SMilanka Ringwald     UNUSED(packet_type);
197*fd7ba7a6SMilanka Ringwald     UNUSED(channel);
198*fd7ba7a6SMilanka Ringwald     UNUSED(size);
199*fd7ba7a6SMilanka Ringwald 
200*fd7ba7a6SMilanka Ringwald     des_iterator_t attribute_list_it;
201*fd7ba7a6SMilanka Ringwald     des_iterator_t additional_des_it;
202*fd7ba7a6SMilanka Ringwald     des_iterator_t prot_it;
203*fd7ba7a6SMilanka Ringwald     uint8_t       *des_element;
204*fd7ba7a6SMilanka Ringwald     uint8_t       *element;
205*fd7ba7a6SMilanka Ringwald     uint32_t       uuid;
206*fd7ba7a6SMilanka Ringwald     uint8_t        status = ERROR_CODE_SUCCESS;
207*fd7ba7a6SMilanka Ringwald 
208*fd7ba7a6SMilanka Ringwald     hid_host_connection_t * connection = hid_host_connection_for_hid_cid(sdp_query_context_hid_host_control_cid);
209*fd7ba7a6SMilanka Ringwald     if (!connection) {
210*fd7ba7a6SMilanka Ringwald         log_error("SDP query, connection with 0x%02x cid not found", sdp_query_context_hid_host_control_cid);
211*fd7ba7a6SMilanka Ringwald         return;
212*fd7ba7a6SMilanka Ringwald     }
213*fd7ba7a6SMilanka Ringwald 
214*fd7ba7a6SMilanka Ringwald     switch (hci_event_packet_get_type(packet)){
215*fd7ba7a6SMilanka Ringwald         case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
216*fd7ba7a6SMilanka Ringwald 
217*fd7ba7a6SMilanka Ringwald             if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) {
218*fd7ba7a6SMilanka Ringwald 
219*fd7ba7a6SMilanka Ringwald                 attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet);
220*fd7ba7a6SMilanka Ringwald 
221*fd7ba7a6SMilanka Ringwald                 if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) {
222*fd7ba7a6SMilanka Ringwald                     switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) {
223*fd7ba7a6SMilanka Ringwald 
224*fd7ba7a6SMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
225*fd7ba7a6SMilanka Ringwald                             for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) {
226*fd7ba7a6SMilanka Ringwald                                 if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue;
227*fd7ba7a6SMilanka Ringwald                                 des_element = des_iterator_get_element(&attribute_list_it);
228*fd7ba7a6SMilanka Ringwald                                 des_iterator_init(&prot_it, des_element);
229*fd7ba7a6SMilanka Ringwald                                 element = des_iterator_get_element(&prot_it);
230*fd7ba7a6SMilanka Ringwald                                 if (de_get_element_type(element) != DE_UUID) continue;
231*fd7ba7a6SMilanka Ringwald                                 uuid = de_get_uuid32(element);
232*fd7ba7a6SMilanka Ringwald                                 switch (uuid){
233*fd7ba7a6SMilanka Ringwald                                     case BLUETOOTH_PROTOCOL_L2CAP:
234*fd7ba7a6SMilanka Ringwald                                         if (!des_iterator_has_more(&prot_it)) continue;
235*fd7ba7a6SMilanka Ringwald                                         des_iterator_next(&prot_it);
236*fd7ba7a6SMilanka Ringwald                                         de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->control_psm);
237*fd7ba7a6SMilanka Ringwald                                         log_info("HID Control PSM: 0x%04x", (int) &connection->control_psm);
238*fd7ba7a6SMilanka Ringwald                                         break;
239*fd7ba7a6SMilanka Ringwald                                     default:
240*fd7ba7a6SMilanka Ringwald                                         break;
241*fd7ba7a6SMilanka Ringwald                                 }
242*fd7ba7a6SMilanka Ringwald                             }
243*fd7ba7a6SMilanka Ringwald                             break;
244*fd7ba7a6SMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
245*fd7ba7a6SMilanka Ringwald                             for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) {
246*fd7ba7a6SMilanka Ringwald                                 if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue;
247*fd7ba7a6SMilanka Ringwald                                 des_element = des_iterator_get_element(&attribute_list_it);
248*fd7ba7a6SMilanka Ringwald                                 for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) {
249*fd7ba7a6SMilanka Ringwald                                     if (des_iterator_get_type(&additional_des_it) != DE_DES) continue;
250*fd7ba7a6SMilanka Ringwald                                     des_element = des_iterator_get_element(&additional_des_it);
251*fd7ba7a6SMilanka Ringwald                                     des_iterator_init(&prot_it, des_element);
252*fd7ba7a6SMilanka Ringwald                                     element = des_iterator_get_element(&prot_it);
253*fd7ba7a6SMilanka Ringwald                                     if (de_get_element_type(element) != DE_UUID) continue;
254*fd7ba7a6SMilanka Ringwald                                     uuid = de_get_uuid32(element);
255*fd7ba7a6SMilanka Ringwald                                     switch (uuid){
256*fd7ba7a6SMilanka Ringwald                                         case BLUETOOTH_PROTOCOL_L2CAP:
257*fd7ba7a6SMilanka Ringwald                                             if (!des_iterator_has_more(&prot_it)) continue;
258*fd7ba7a6SMilanka Ringwald                                             des_iterator_next(&prot_it);
259*fd7ba7a6SMilanka Ringwald                                             de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->interrupt_psm);
260*fd7ba7a6SMilanka Ringwald                                             log_info("HID Interrupt PSM: 0x%04x", (int) &connection->interrupt_psm);
261*fd7ba7a6SMilanka Ringwald                                             break;
262*fd7ba7a6SMilanka Ringwald                                         default:
263*fd7ba7a6SMilanka Ringwald                                             break;
264*fd7ba7a6SMilanka Ringwald                                     }
265*fd7ba7a6SMilanka Ringwald                                 }
266*fd7ba7a6SMilanka Ringwald                             }
267*fd7ba7a6SMilanka Ringwald                             break;
268*fd7ba7a6SMilanka Ringwald 
269*fd7ba7a6SMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST:
270*fd7ba7a6SMilanka Ringwald                             for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) {
271*fd7ba7a6SMilanka Ringwald                                 if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue;
272*fd7ba7a6SMilanka Ringwald                                 des_element = des_iterator_get_element(&attribute_list_it);
273*fd7ba7a6SMilanka Ringwald                                 for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) {
274*fd7ba7a6SMilanka Ringwald                                     if (des_iterator_get_type(&additional_des_it) != DE_STRING) continue;
275*fd7ba7a6SMilanka Ringwald                                     element = des_iterator_get_element(&additional_des_it);
276*fd7ba7a6SMilanka Ringwald 
277*fd7ba7a6SMilanka Ringwald                                     const uint8_t * descriptor = de_get_string(element);
278*fd7ba7a6SMilanka Ringwald                                     uint16_t descriptor_len = de_get_data_size(element);
279*fd7ba7a6SMilanka Ringwald 
280*fd7ba7a6SMilanka Ringwald                                     uint16_t i;
281*fd7ba7a6SMilanka Ringwald                                     for (i = 0; i < descriptor_len; i++){
282*fd7ba7a6SMilanka Ringwald                                         hid_descriptor_storage_store(connection, descriptor[i]);
283*fd7ba7a6SMilanka Ringwald                                     }
284*fd7ba7a6SMilanka Ringwald                                     printf("HID Descriptor:\n");
285*fd7ba7a6SMilanka Ringwald                                     printf_hexdump(descriptor, descriptor_len);
286*fd7ba7a6SMilanka Ringwald                                 }
287*fd7ba7a6SMilanka Ringwald                             }
288*fd7ba7a6SMilanka Ringwald                             break;
289*fd7ba7a6SMilanka Ringwald                         default:
290*fd7ba7a6SMilanka Ringwald                             break;
291*fd7ba7a6SMilanka Ringwald                     }
292*fd7ba7a6SMilanka Ringwald                 }
293*fd7ba7a6SMilanka Ringwald             } else {
294*fd7ba7a6SMilanka Ringwald                 fprintf(stderr, "SDP attribute value buffer size exceeded: available %d, required %d\n", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet));
295*fd7ba7a6SMilanka Ringwald             }
296*fd7ba7a6SMilanka Ringwald             break;
297*fd7ba7a6SMilanka Ringwald 
298*fd7ba7a6SMilanka Ringwald         case SDP_EVENT_QUERY_COMPLETE:
299*fd7ba7a6SMilanka Ringwald             if (!connection->control_psm) {
300*fd7ba7a6SMilanka Ringwald                 printf("HID Control PSM missing\n");
301*fd7ba7a6SMilanka Ringwald                 status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
302*fd7ba7a6SMilanka Ringwald                 break;
303*fd7ba7a6SMilanka Ringwald             }
304*fd7ba7a6SMilanka Ringwald             if (!connection->interrupt_psm) {
305*fd7ba7a6SMilanka Ringwald                 printf("HID Interrupt PSM missing\n");
306*fd7ba7a6SMilanka Ringwald                 status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
307*fd7ba7a6SMilanka Ringwald                 break;
308*fd7ba7a6SMilanka Ringwald             }
309*fd7ba7a6SMilanka Ringwald 
310*fd7ba7a6SMilanka Ringwald             printf("Setup HID\n");
311*fd7ba7a6SMilanka Ringwald             status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->control_psm, 48, &connection->control_cid);
312*fd7ba7a6SMilanka Ringwald             if (status){
313*fd7ba7a6SMilanka Ringwald                 printf("Connecting to HID Control failed: 0x%02x\n", status);
314*fd7ba7a6SMilanka Ringwald             }
315*fd7ba7a6SMilanka Ringwald             break;
316*fd7ba7a6SMilanka Ringwald 
317*fd7ba7a6SMilanka Ringwald         default:
318*fd7ba7a6SMilanka Ringwald             // bail out, we must have had an incoming connection in the meantime; just trigger next sdp query on complete
319*fd7ba7a6SMilanka Ringwald             if (hci_event_packet_get_type(packet) == SDP_EVENT_QUERY_COMPLETE){
320*fd7ba7a6SMilanka Ringwald                 (void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request);
321*fd7ba7a6SMilanka Ringwald             }
322*fd7ba7a6SMilanka Ringwald             return;
323*fd7ba7a6SMilanka Ringwald     }
324*fd7ba7a6SMilanka Ringwald 
325*fd7ba7a6SMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
326*fd7ba7a6SMilanka Ringwald         hid_emit_connected_event(connection, status);
327*fd7ba7a6SMilanka Ringwald         hid_host_finalize_connection(connection);
328*fd7ba7a6SMilanka Ringwald         sdp_query_context_hid_host_control_cid = 0;
329*fd7ba7a6SMilanka Ringwald     }
330*fd7ba7a6SMilanka Ringwald }
331*fd7ba7a6SMilanka Ringwald 
332*fd7ba7a6SMilanka Ringwald static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
333*fd7ba7a6SMilanka Ringwald     UNUSED(channel);
334*fd7ba7a6SMilanka Ringwald     UNUSED(size);
335*fd7ba7a6SMilanka Ringwald 
33663bf37cdSMilanka Ringwald     uint8_t   event;
337*fd7ba7a6SMilanka Ringwald     bd_addr_t address;
338*fd7ba7a6SMilanka Ringwald     uint8_t   status;
339*fd7ba7a6SMilanka Ringwald     uint16_t  cid;
340*fd7ba7a6SMilanka Ringwald     hid_host_connection_t * connection;
341*fd7ba7a6SMilanka Ringwald 
34263bf37cdSMilanka Ringwald     // uint8_t param;
34363bf37cdSMilanka Ringwald     // hid_message_type_t         message_type;
34463bf37cdSMilanka Ringwald     // hid_handshake_param_type_t message_status;
34563bf37cdSMilanka Ringwald 
34663bf37cdSMilanka Ringwald     switch (packet_type) {
34763bf37cdSMilanka Ringwald         case HCI_EVENT_PACKET:
34863bf37cdSMilanka Ringwald             event = hci_event_packet_get_type(packet);
34963bf37cdSMilanka Ringwald             switch (event) {
350*fd7ba7a6SMilanka Ringwald                 case L2CAP_EVENT_INCOMING_CONNECTION:
351*fd7ba7a6SMilanka Ringwald                     l2cap_event_incoming_connection_get_address(packet, address);
352*fd7ba7a6SMilanka Ringwald                     connection = hid_host_get_connection_for_bd_addr(address);
353*fd7ba7a6SMilanka Ringwald 
354*fd7ba7a6SMilanka Ringwald                     if (connection && connection->unplugged){
355*fd7ba7a6SMilanka Ringwald                         log_info("Decline connection for %s, host is unplugged", bd_addr_to_str(address));
356*fd7ba7a6SMilanka Ringwald                         l2cap_decline_connection(channel);
357*fd7ba7a6SMilanka Ringwald                         break;
358*fd7ba7a6SMilanka Ringwald                     }
359*fd7ba7a6SMilanka Ringwald 
360*fd7ba7a6SMilanka Ringwald                     switch (l2cap_event_incoming_connection_get_psm(packet)){
361*fd7ba7a6SMilanka Ringwald                         case PSM_HID_CONTROL:
362*fd7ba7a6SMilanka Ringwald                             if (connection){
363*fd7ba7a6SMilanka Ringwald                                 log_error("Connection already exists %s", bd_addr_to_str(address));
364*fd7ba7a6SMilanka Ringwald                                 l2cap_decline_connection(channel);
365*fd7ba7a6SMilanka Ringwald                                 break;
366*fd7ba7a6SMilanka Ringwald                             }
367*fd7ba7a6SMilanka Ringwald 
368*fd7ba7a6SMilanka Ringwald                             connection = hid_host_create_connection(address);
369*fd7ba7a6SMilanka Ringwald                             if (!connection) {
370*fd7ba7a6SMilanka Ringwald                                 log_error("Cannot create connection for %s", bd_addr_to_str(address));
371*fd7ba7a6SMilanka Ringwald                                 l2cap_decline_connection(channel);
372*fd7ba7a6SMilanka Ringwald                                 break;
373*fd7ba7a6SMilanka Ringwald                             }
374*fd7ba7a6SMilanka Ringwald 
375*fd7ba7a6SMilanka Ringwald                             connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED;
376*fd7ba7a6SMilanka Ringwald                             connection->con_handle = l2cap_event_incoming_connection_get_handle(packet);
377*fd7ba7a6SMilanka Ringwald                             connection->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
378*fd7ba7a6SMilanka Ringwald                             connection->incoming = true;
379*fd7ba7a6SMilanka Ringwald                             log_info("Accept connection on Control channel %s", bd_addr_to_str(address));
380*fd7ba7a6SMilanka Ringwald                             l2cap_accept_connection(channel);
381*fd7ba7a6SMilanka Ringwald                             break;
382*fd7ba7a6SMilanka Ringwald 
383*fd7ba7a6SMilanka Ringwald                         case PSM_HID_INTERRUPT:
384*fd7ba7a6SMilanka Ringwald                             if (!connection || (connection->interrupt_cid != 0) || (l2cap_event_incoming_connection_get_handle(packet) != connection->con_handle)){
385*fd7ba7a6SMilanka Ringwald                                 log_error("Decline connection for %s", bd_addr_to_str(address));
386*fd7ba7a6SMilanka Ringwald                                 l2cap_decline_connection(channel);
387*fd7ba7a6SMilanka Ringwald                                 break;
388*fd7ba7a6SMilanka Ringwald                             }
389*fd7ba7a6SMilanka Ringwald 
390*fd7ba7a6SMilanka Ringwald                             connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
391*fd7ba7a6SMilanka Ringwald                             connection->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
392*fd7ba7a6SMilanka Ringwald                             log_info("Accept connection on Interrupt channel %s", bd_addr_to_str(address));
393*fd7ba7a6SMilanka Ringwald                             l2cap_accept_connection(channel);
394*fd7ba7a6SMilanka Ringwald                             break;
395*fd7ba7a6SMilanka Ringwald 
396*fd7ba7a6SMilanka Ringwald                         default:
397*fd7ba7a6SMilanka Ringwald                             log_info("Decline connection for %s", bd_addr_to_str(address));
398*fd7ba7a6SMilanka Ringwald                             l2cap_decline_connection(channel);
399*fd7ba7a6SMilanka Ringwald                             break;
40063bf37cdSMilanka Ringwald                     }
40163bf37cdSMilanka Ringwald                     break;
402*fd7ba7a6SMilanka Ringwald 
403*fd7ba7a6SMilanka Ringwald                 case L2CAP_EVENT_CHANNEL_OPENED:
404*fd7ba7a6SMilanka Ringwald                     l2cap_event_channel_opened_get_address(packet, address);
405*fd7ba7a6SMilanka Ringwald 
406*fd7ba7a6SMilanka Ringwald                     status = l2cap_event_channel_opened_get_status(packet);
407*fd7ba7a6SMilanka Ringwald                     if (status){
408*fd7ba7a6SMilanka Ringwald                         log_info("L2CAP connection %s failed: 0x%02xn", bd_addr_to_str(address), status);
409*fd7ba7a6SMilanka Ringwald                         break;
410*fd7ba7a6SMilanka Ringwald                     }
411*fd7ba7a6SMilanka Ringwald 
412*fd7ba7a6SMilanka Ringwald                     connection = hid_host_get_connection_for_bd_addr(address);
413*fd7ba7a6SMilanka Ringwald                     if (!connection){
414*fd7ba7a6SMilanka Ringwald                         log_error("Connection does not exist %s", bd_addr_to_str(address));
415*fd7ba7a6SMilanka Ringwald                         break;
416*fd7ba7a6SMilanka Ringwald                     }
417*fd7ba7a6SMilanka Ringwald 
418*fd7ba7a6SMilanka Ringwald                     cid = l2cap_event_channel_opened_get_local_cid(packet);
419*fd7ba7a6SMilanka Ringwald 
420*fd7ba7a6SMilanka Ringwald                     switch (l2cap_event_channel_opened_get_psm(packet)){
421*fd7ba7a6SMilanka Ringwald                         case PSM_HID_CONTROL:
422*fd7ba7a6SMilanka Ringwald                             if (connection->state != HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED) break;
423*fd7ba7a6SMilanka Ringwald                             connection->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED;
424*fd7ba7a6SMilanka Ringwald 
425*fd7ba7a6SMilanka Ringwald                             if (connection->boot_mode){
426*fd7ba7a6SMilanka Ringwald                                  break;
427*fd7ba7a6SMilanka Ringwald                             }
428*fd7ba7a6SMilanka Ringwald 
429*fd7ba7a6SMilanka Ringwald                             if (!connection->incoming){
430*fd7ba7a6SMilanka Ringwald                                 status = l2cap_create_channel(hid_host_packet_handler, address, connection->interrupt_psm, 48, &connection->interrupt_cid);
431*fd7ba7a6SMilanka Ringwald                                 if (status){
432*fd7ba7a6SMilanka Ringwald                                     log_info("Connecting to HID Interrupt failed: 0x%02x", status);
433*fd7ba7a6SMilanka Ringwald                                     break;
434*fd7ba7a6SMilanka Ringwald                                 }
435*fd7ba7a6SMilanka Ringwald                                 connection->con_handle = l2cap_event_channel_opened_get_handle(packet);
436*fd7ba7a6SMilanka Ringwald                                 connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
437*fd7ba7a6SMilanka Ringwald                             }
438*fd7ba7a6SMilanka Ringwald                             break;
439*fd7ba7a6SMilanka Ringwald 
440*fd7ba7a6SMilanka Ringwald                         case PSM_HID_INTERRUPT:
441*fd7ba7a6SMilanka Ringwald                             if (connection->state != HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED) break;
442*fd7ba7a6SMilanka Ringwald                             if (connection->con_handle != l2cap_event_channel_opened_get_handle(packet)) break;
443*fd7ba7a6SMilanka Ringwald 
444*fd7ba7a6SMilanka Ringwald                             connection->state = HID_HOST_CONNECTION_ESTABLISHED;
445*fd7ba7a6SMilanka Ringwald                             hid_emit_connected_event(connection, ERROR_CODE_SUCCESS);
446*fd7ba7a6SMilanka Ringwald 
447*fd7ba7a6SMilanka Ringwald                             log_info("Connection on interrupt channel established, interrupt_cid 0x%02x", connection->interrupt_cid);
448*fd7ba7a6SMilanka Ringwald                             break;
449*fd7ba7a6SMilanka Ringwald 
450*fd7ba7a6SMilanka Ringwald                         default:
451*fd7ba7a6SMilanka Ringwald                             break;
452*fd7ba7a6SMilanka Ringwald                     }
453*fd7ba7a6SMilanka Ringwald                     // disconnect?
454*fd7ba7a6SMilanka Ringwald                     break;
455*fd7ba7a6SMilanka Ringwald 
45663bf37cdSMilanka Ringwald                 default:
45763bf37cdSMilanka Ringwald                     break;
45863bf37cdSMilanka Ringwald             }
45963bf37cdSMilanka Ringwald         default:
46063bf37cdSMilanka Ringwald             break;
46163bf37cdSMilanka Ringwald     }
46263bf37cdSMilanka Ringwald }
46363bf37cdSMilanka Ringwald 
464*fd7ba7a6SMilanka Ringwald 
465*fd7ba7a6SMilanka Ringwald void hid_host_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
46663bf37cdSMilanka Ringwald     hid_host_descriptor_storage = hid_descriptor_storage;
46763bf37cdSMilanka Ringwald     hid_host_descriptor_storage_len = hid_descriptor_storage_len;
46863bf37cdSMilanka Ringwald 
46963bf37cdSMilanka Ringwald     // register L2CAP Services for reconnections
470*fd7ba7a6SMilanka Ringwald     l2cap_register_service(hid_host_packet_handler, PSM_HID_INTERRUPT, 0xffff, gap_get_security_level());
471*fd7ba7a6SMilanka Ringwald     l2cap_register_service(hid_host_packet_handler, PSM_HID_CONTROL, 0xffff, gap_get_security_level());
47263bf37cdSMilanka Ringwald }
47363bf37cdSMilanka Ringwald 
47463bf37cdSMilanka Ringwald void hid_host_register_packet_handler(btstack_packet_handler_t callback){
475*fd7ba7a6SMilanka Ringwald     hid_callback = callback;
47663bf37cdSMilanka Ringwald }
47763bf37cdSMilanka Ringwald 
478*fd7ba7a6SMilanka Ringwald 
479*fd7ba7a6SMilanka Ringwald static void hid_host_handle_start_sdp_client_query(void * context){
480*fd7ba7a6SMilanka Ringwald     UNUSED(context);
481*fd7ba7a6SMilanka Ringwald 
482*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_t it;
483*fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_init(&it, &connections);
484*fd7ba7a6SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
485*fd7ba7a6SMilanka Ringwald         hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
486*fd7ba7a6SMilanka Ringwald 
487*fd7ba7a6SMilanka Ringwald         switch (connection->state){
488*fd7ba7a6SMilanka Ringwald              case HID_HOST_W2_SEND_SDP_QUERY:
489*fd7ba7a6SMilanka Ringwald                 connection->state = HID_HOST_W4_SDP_QUERY_RESULT;
490*fd7ba7a6SMilanka Ringwald                 break;
491*fd7ba7a6SMilanka Ringwald             default:
492*fd7ba7a6SMilanka Ringwald                 continue;
493*fd7ba7a6SMilanka Ringwald         }
494*fd7ba7a6SMilanka Ringwald         printf("hid_descriptor_storage_init, start query, cid 0x%02x, %s \n", connection->hid_cid, bd_addr_to_str(connection->remote_addr));
495*fd7ba7a6SMilanka Ringwald         hid_descriptor_storage_init(connection);
496*fd7ba7a6SMilanka Ringwald         sdp_query_context_hid_host_control_cid = connection->hid_cid;
497*fd7ba7a6SMilanka Ringwald         sdp_client_query_uuid16(&hid_host_handle_sdp_client_query_result, (uint8_t *) connection->remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
498*fd7ba7a6SMilanka Ringwald         return;
499*fd7ba7a6SMilanka Ringwald     }
500*fd7ba7a6SMilanka Ringwald }
501*fd7ba7a6SMilanka Ringwald 
502*fd7ba7a6SMilanka Ringwald uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid){
50363bf37cdSMilanka Ringwald     UNUSED(protocol_mode);
504*fd7ba7a6SMilanka Ringwald 
505*fd7ba7a6SMilanka Ringwald     if (hid_cid == NULL) {
506*fd7ba7a6SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
507*fd7ba7a6SMilanka Ringwald     }
508*fd7ba7a6SMilanka Ringwald 
509*fd7ba7a6SMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_bd_addr(remote_addr);
510*fd7ba7a6SMilanka Ringwald     if (connection){
511*fd7ba7a6SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
512*fd7ba7a6SMilanka Ringwald     }
513*fd7ba7a6SMilanka Ringwald 
514*fd7ba7a6SMilanka Ringwald     connection = hid_host_create_connection(remote_addr);
515*fd7ba7a6SMilanka Ringwald     if (!connection) return BTSTACK_MEMORY_ALLOC_FAILED;
516*fd7ba7a6SMilanka Ringwald 
517*fd7ba7a6SMilanka Ringwald 
518*fd7ba7a6SMilanka Ringwald     *hid_cid = connection->hid_cid;
519*fd7ba7a6SMilanka Ringwald 
520*fd7ba7a6SMilanka Ringwald     connection->state = HID_HOST_W2_SEND_SDP_QUERY;
521*fd7ba7a6SMilanka Ringwald     connection->incoming = false;
522*fd7ba7a6SMilanka Ringwald     connection->control_cid = 0;
523*fd7ba7a6SMilanka Ringwald     connection->control_psm = 0;
524*fd7ba7a6SMilanka Ringwald     connection->interrupt_cid = 0;
525*fd7ba7a6SMilanka Ringwald     connection->interrupt_psm = 0;
526*fd7ba7a6SMilanka Ringwald     printf("hid_host_connect, cid 0x%02x, %s \n", connection->hid_cid, bd_addr_to_str(connection->remote_addr));
527*fd7ba7a6SMilanka Ringwald 
528*fd7ba7a6SMilanka Ringwald     hid_host_handle_sdp_client_query_request.callback = &hid_host_handle_start_sdp_client_query;
529*fd7ba7a6SMilanka Ringwald     // ignore ERROR_CODE_COMMAND_DISALLOWED because in that case, we already have requested an SDP callback
530*fd7ba7a6SMilanka Ringwald     (void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request);
531*fd7ba7a6SMilanka Ringwald 
53263bf37cdSMilanka Ringwald     return ERROR_CODE_SUCCESS;
53363bf37cdSMilanka Ringwald }
53463bf37cdSMilanka Ringwald 
53563bf37cdSMilanka Ringwald 
53663bf37cdSMilanka Ringwald void hid_host_disconnect(uint16_t hid_cid){
53763bf37cdSMilanka Ringwald     UNUSED(hid_cid);
53863bf37cdSMilanka Ringwald }
53963bf37cdSMilanka Ringwald 
54063bf37cdSMilanka Ringwald void hid_host_request_can_send_now_event(uint16_t hid_cid){
54163bf37cdSMilanka Ringwald     UNUSED(hid_cid);
54263bf37cdSMilanka Ringwald }
54363bf37cdSMilanka Ringwald 
54463bf37cdSMilanka Ringwald void hid_host_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
54563bf37cdSMilanka Ringwald     UNUSED(hid_cid);
54663bf37cdSMilanka Ringwald     UNUSED(message);
54763bf37cdSMilanka Ringwald     UNUSED(message_len);
54863bf37cdSMilanka Ringwald }
54963bf37cdSMilanka Ringwald 
55063bf37cdSMilanka Ringwald void hid_host_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
55163bf37cdSMilanka Ringwald     UNUSED(hid_cid);
55263bf37cdSMilanka Ringwald     UNUSED(message);
55363bf37cdSMilanka Ringwald     UNUSED(message_len);
55463bf37cdSMilanka Ringwald }