xref: /btstack/src/classic/goep_client.c (revision ced70f9bfeafe291ec597a3a9cc862e39e0da3ce)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "goep_client.c"
39 
40 #include "btstack_config.h"
41 
42 #include <stdint.h>
43 #include <string.h>
44 
45 #include "btstack_debug.h"
46 #include "hci_dump.h"
47 #include "bluetooth_sdp.h"
48 #include "btstack_event.h"
49 #include "classic/goep_client.h"
50 #include "classic/obex_message_builder.h"
51 #include "classic/obex.h"
52 #include "classic/obex_iterator.h"
53 #include "classic/rfcomm.h"
54 #include "classic/sdp_client.h"
55 #include "classic/sdp_util.h"
56 #include "l2cap.h"
57 
58 //------------------------------------------------------------------------------------------------------------
59 // goep_client.c
60 //
61 
62 #ifdef ENABLE_GOEP_L2CAP
63 #ifndef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
64 #error "ENABLE_GOEP_L2CAP requires ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE. Please enable ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE or disable ENABLE_GOEP_L2CAP"
65 #endif
66 #endif
67 
68 static uint16_t goep_client_cid;
69 static btstack_linked_list_t goep_clients;
70 
71 static goep_client_t *    goep_client_sdp_active;
72 static uint8_t            goep_client_sdp_query_attribute_value[30];
73 static const unsigned int goep_client_sdp_query_attribute_value_buffer_size = sizeof(goep_client_sdp_query_attribute_value);
74 static uint8_t goep_packet_buffer[150];
75 
76 // singleton instance
77 static goep_client_t   goep_client_singleton;
78 
79 #ifdef ENABLE_GOEP_L2CAP
80 // singleton instance
81 static uint8_t goep_client_singleton_ertm_buffer[1000];
82 static l2cap_ertm_config_t goep_client_singleton_ertm_config = {
83     1,  // ertm mandatory
84     2,  // max transmit, some tests require > 1
85     2000,
86     12000,
87     512,    // l2cap ertm mtu
88     2,
89     2,
90     1,      // 16-bit FCS
91 };
92 #endif
93 
94 static inline void goep_client_emit_connected_event(goep_client_t * goep_client, uint8_t status){
95     uint8_t event[15];
96     int pos = 0;
97     event[pos++] = HCI_EVENT_GOEP_META;
98     pos++;  // skip len
99     event[pos++] = GOEP_SUBEVENT_CONNECTION_OPENED;
100     little_endian_store_16(event, pos, goep_client->cid);
101     pos+=2;
102     event[pos++] = status;
103     (void)memcpy(&event[pos], goep_client->bd_addr, 6);
104     pos += 6;
105     little_endian_store_16(event, pos, goep_client->con_handle);
106     pos += 2;
107     event[pos++] = goep_client->incoming;
108     event[1] = pos - 2;
109     if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos);
110     goep_client->client_handler(HCI_EVENT_PACKET, goep_client->cid, &event[0], pos);
111 }
112 
113 static inline void goep_client_emit_connection_closed_event(goep_client_t * goep_client){
114     uint8_t event[5];
115     int pos = 0;
116     event[pos++] = HCI_EVENT_GOEP_META;
117     pos++;  // skip len
118     event[pos++] = GOEP_SUBEVENT_CONNECTION_CLOSED;
119     little_endian_store_16(event, pos, goep_client->cid);
120     pos+=2;
121     event[1] = pos - 2;
122     if (pos != sizeof(event)) log_error("goep_client_emit_connection_closed_event size %u", pos);
123     goep_client->client_handler(HCI_EVENT_PACKET, goep_client->cid, &event[0], pos);
124 }
125 
126 static inline void goep_client_emit_can_send_now_event(goep_client_t * goep_client){
127     uint8_t event[5];
128     int pos = 0;
129     event[pos++] = HCI_EVENT_GOEP_META;
130     pos++;  // skip len
131     event[pos++] = GOEP_SUBEVENT_CAN_SEND_NOW;
132     little_endian_store_16(event, pos, goep_client->cid);
133     pos+=2;
134     event[1] = pos - 2;
135     if (pos != sizeof(event)) log_error("goep_client_emit_can_send_now_event size %u", pos);
136     goep_client->client_handler(HCI_EVENT_PACKET, goep_client->cid, &event[0], pos);
137 }
138 
139 static void goep_client_handle_connection_opened(goep_client_t * goep_client, uint8_t status, uint16_t mtu){
140     if (status) {
141         goep_client->state = GOEP_CLIENT_INIT;
142         log_info("goep_client: open failed, status %u", status);
143     } else {
144         goep_client->bearer_mtu = mtu;
145         goep_client->state = GOEP_CLIENT_CONNECTED;
146         log_info("goep_client: connection opened. cid %u, max frame size %u", goep_client->bearer_cid, goep_client->bearer_mtu);
147     }
148     goep_client_emit_connected_event(goep_client, status);
149 }
150 
151 static void goep_client_handle_connection_close(goep_client_t * goep_client){
152     goep_client->state = GOEP_CLIENT_INIT;
153     btstack_linked_list_remove(&goep_clients, (btstack_linked_item_t *) goep_client);
154     goep_client_emit_connection_closed_event(goep_client);
155 }
156 
157 static goep_client_t * goep_client_for_cid(uint16_t cid){
158     btstack_linked_list_iterator_t it;
159     btstack_linked_list_iterator_init(&it, &goep_clients);
160     while (btstack_linked_list_iterator_has_next(&it)){
161         goep_client_t * goep_client = (goep_client_t *) btstack_linked_list_iterator_next(&it);
162         if (goep_client->cid == cid){
163             return goep_client;
164         }
165     }
166     return NULL;
167 }
168 
169 static goep_client_t * goep_client_for_bearer_cid(uint16_t bearer_cid){
170     btstack_linked_list_iterator_t it;
171     btstack_linked_list_iterator_init(&it, &goep_clients);
172     while (btstack_linked_list_iterator_has_next(&it)){
173         goep_client_t * goep_client = (goep_client_t *) btstack_linked_list_iterator_next(&it);
174         if (goep_client->bearer_cid == bearer_cid){
175             return goep_client;
176         }
177     }
178     return NULL;
179 }
180 
181 static void goep_client_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
182     UNUSED(channel);
183     UNUSED(size);
184     goep_client_t * goep_client;
185     switch (packet_type){
186         case HCI_EVENT_PACKET:
187             switch (hci_event_packet_get_type(packet)) {
188 #ifdef ENABLE_GOEP_L2CAP
189                 case L2CAP_EVENT_CHANNEL_OPENED:
190                     goep_client = goep_client_for_bearer_cid(l2cap_event_channel_opened_get_local_cid(packet));
191                     btstack_assert(goep_client != NULL);
192                     goep_client_handle_connection_opened(goep_client, l2cap_event_channel_opened_get_status(packet),
193                                                          btstack_min(l2cap_event_channel_opened_get_remote_mtu(packet), l2cap_event_channel_opened_get_local_mtu(packet)));
194                     break;
195                 case L2CAP_EVENT_CAN_SEND_NOW:
196                     goep_client = goep_client_for_bearer_cid(l2cap_event_can_send_now_get_local_cid(packet));
197                     btstack_assert(goep_client != NULL);
198                     goep_client_emit_can_send_now_event(goep_client);
199                     break;
200                 case L2CAP_EVENT_CHANNEL_CLOSED:
201                     goep_client = goep_client_for_bearer_cid(l2cap_event_channel_closed_get_local_cid(packet));
202                     btstack_assert(goep_client != NULL);
203                     goep_client_handle_connection_close(goep_client);
204                     break;
205 #endif
206                 case RFCOMM_EVENT_CHANNEL_OPENED:
207                     goep_client = goep_client_for_bearer_cid(rfcomm_event_channel_opened_get_rfcomm_cid(packet));
208                     btstack_assert(goep_client != NULL);
209                     goep_client_handle_connection_opened(goep_client, rfcomm_event_channel_opened_get_status(packet), rfcomm_event_channel_opened_get_max_frame_size(packet));
210                     return;
211                 case RFCOMM_EVENT_CAN_SEND_NOW:
212                     goep_client = goep_client_for_bearer_cid(rfcomm_event_can_send_now_get_rfcomm_cid(packet));
213                     btstack_assert(goep_client != NULL);
214                     goep_client_emit_can_send_now_event(goep_client);
215                     break;
216                 case RFCOMM_EVENT_CHANNEL_CLOSED:
217                     goep_client = goep_client_for_bearer_cid(rfcomm_event_channel_closed_get_rfcomm_cid(packet));
218                     btstack_assert(goep_client != NULL);
219                     goep_client_handle_connection_close(goep_client);
220                     break;
221                 default:
222                     break;
223             }
224             break;
225         case L2CAP_DATA_PACKET:
226         case RFCOMM_DATA_PACKET:
227             goep_client = goep_client_for_bearer_cid(channel);
228             btstack_assert(goep_client != NULL);
229             goep_client->client_handler(GOEP_DATA_PACKET, goep_client->cid, packet, size);
230             break;
231         default:
232             break;
233     }
234 }
235 
236 static void goep_client_handle_sdp_query_end_of_record(goep_client_t * goep_client){
237     if (goep_client->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER){
238         if (goep_client->mas_info.instance_id == goep_client->map_mas_instance_id){
239             // Requested MAS Instance found, accept info
240             goep_client->rfcomm_port = goep_client->mas_info.rfcomm_port;
241             goep_client->profile_supported_features = goep_client->mas_info.supported_features;
242             goep_client->map_supported_message_types = goep_client->mas_info.supported_message_types;
243 #ifdef ENABLE_GOEP_L2CAP
244             goep_client->l2cap_psm = goep_client->mas_info.l2cap_psm;
245             log_info("MAS Instance #%u found, rfcomm #%u, l2cap 0x%04x", goep_client->map_mas_instance_id,
246                      goep_client->rfcomm_port, goep_client->l2cap_psm);
247 #else
248             log_info("MAS Instance #%u found, rfcomm #%u", goep_client->map_mas_instance_id,
249                      goep_client->rfcomm_port);
250 #endif
251         }
252     }
253 }
254 static uint8_t goep_client_start_connect(goep_client_t * goep_client){
255 #ifdef ENABLE_GOEP_L2CAP
256     if (goep_client->l2cap_psm && (goep_client->ertm_buffer != NULL)){
257         log_info("Remote GOEP L2CAP PSM: %u", goep_client->l2cap_psm);
258         return l2cap_ertm_create_channel(&goep_client_packet_handler, goep_client->bd_addr, goep_client->l2cap_psm,
259         &goep_client->ertm_config, goep_client->ertm_buffer,
260         goep_client->ertm_buffer_size, &goep_client->bearer_cid);
261     }
262 #endif
263     log_info("Remote GOEP RFCOMM Server Channel: %u", goep_client->rfcomm_port);
264     return rfcomm_create_channel(&goep_client_packet_handler, goep_client->bd_addr, goep_client->rfcomm_port, &goep_client->bearer_cid);
265 }
266 
267 static void goep_client_handle_sdp_query_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
268     goep_client_t * goep_client = goep_client_sdp_active;
269     btstack_assert(goep_client != NULL);
270 
271     UNUSED(packet_type);
272     UNUSED(channel);
273     UNUSED(size);
274 
275     des_iterator_t des_list_it;
276     des_iterator_t prot_it;
277     uint8_t status;
278     uint16_t record_index;
279     bool goep_server_found;
280 
281     switch (hci_event_packet_get_type(packet)){
282         case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
283 
284             // detect new record
285             record_index = sdp_event_query_attribute_byte_get_record_id(packet);
286             if (record_index != goep_client->record_index){
287                 goep_client->record_index = record_index;
288                 goep_client_handle_sdp_query_end_of_record(goep_client);
289                 memset(&goep_client->mas_info, 0, sizeof(goep_client->mas_info));
290             }
291 
292             // check if relevant attribute
293             switch(sdp_event_query_attribute_byte_get_attribute_id(packet)){
294                 case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
295                 case BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES:
296                 case BLUETOOTH_ATTRIBUTE_MAS_INSTANCE_ID:
297                 case BLUETOOTH_ATTRIBUTE_SUPPORTED_MESSAGE_TYPES:
298 #ifdef ENABLE_GOEP_L2CAP
299                 case BLUETOOTH_ATTRIBUTE_GOEP_L2CAP_PSM:
300 #endif
301                     break;
302                 default:
303                     return;
304             }
305 
306             // warn if attribute too large to fit in our buffer
307             if (sdp_event_query_attribute_byte_get_attribute_length(packet) > goep_client_sdp_query_attribute_value_buffer_size) {
308                 log_error("SDP attribute value size exceeded for attribute %x: available %d, required %d", sdp_event_query_attribute_byte_get_attribute_id(packet), goep_client_sdp_query_attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet));
309                 break;
310             }
311 
312             // store single byte
313             goep_client_sdp_query_attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet);
314 
315             // wait until value fully received
316             if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) != sdp_event_query_attribute_byte_get_attribute_length(packet)) break;
317 
318             // process attributes
319             switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) {
320                 case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
321                     for (des_iterator_init(&des_list_it, goep_client_sdp_query_attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) {
322                         uint8_t       *des_element;
323                         uint8_t       *element;
324                         uint32_t       uuid;
325 
326                         if (des_iterator_get_type(&des_list_it) != DE_DES) continue;
327 
328                         des_element = des_iterator_get_element(&des_list_it);
329                         des_iterator_init(&prot_it, des_element);
330 
331                         // first element is UUID
332                         element = des_iterator_get_element(&prot_it);
333                         if (de_get_element_type(element) != DE_UUID) continue;
334 
335                         uuid = de_get_uuid32(element);
336                         des_iterator_next(&prot_it);
337                         if (!des_iterator_has_more(&prot_it)) continue;
338 
339                         // second element is RFCOMM server channel or L2CAP PSM
340                         element = des_iterator_get_element(&prot_it);
341                         if (uuid == BLUETOOTH_PROTOCOL_RFCOMM){
342                             if (goep_client->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER) {
343                                 goep_client->mas_info.rfcomm_port = element[de_get_header_size(element)];
344                             } else {
345                                 goep_client->rfcomm_port = element[de_get_header_size(element)];
346                             }
347                         }
348                     }
349                     break;
350 #ifdef ENABLE_GOEP_L2CAP
351                 case BLUETOOTH_ATTRIBUTE_GOEP_L2CAP_PSM:
352                     if (goep_client->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER){
353                         de_element_get_uint16(goep_client_sdp_query_attribute_value, &goep_client->mas_info.l2cap_psm);
354                     } else {
355                         de_element_get_uint16(goep_client_sdp_query_attribute_value, &goep_client->l2cap_psm);
356                     }
357                     break;
358 #endif
359                 // BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES == BLUETOOTH_ATTRIBUTE_MAP_SUPPORTED_FEATURES == 0x0317
360                 case BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES:
361                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
362                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_32) break;
363                     if (goep_client->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER) {
364                         goep_client->mas_info.supported_features  = big_endian_read_32(goep_client_sdp_query_attribute_value, de_get_header_size(goep_client_sdp_query_attribute_value));
365                     } else {
366                         goep_client->profile_supported_features  = big_endian_read_32(goep_client_sdp_query_attribute_value, de_get_header_size(goep_client_sdp_query_attribute_value));
367                     }
368                     break;
369 
370                 case BLUETOOTH_ATTRIBUTE_MAS_INSTANCE_ID:
371                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
372                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_8) break;
373                     goep_client->mas_info.instance_id = goep_client_sdp_query_attribute_value[de_get_header_size(goep_client_sdp_query_attribute_value)];
374                     break;
375 
376                 case BLUETOOTH_ATTRIBUTE_SUPPORTED_MESSAGE_TYPES:
377                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
378                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_8) break;
379                     goep_client->mas_info.supported_message_types = goep_client_sdp_query_attribute_value[de_get_header_size(goep_client_sdp_query_attribute_value)];
380                     break;
381 
382                 default:
383                     break;
384             }
385             break;
386 
387         case SDP_EVENT_QUERY_COMPLETE:
388             goep_client_sdp_active = NULL;
389             goep_client_handle_sdp_query_end_of_record(goep_client);
390             status = sdp_event_query_complete_get_status(packet);
391             if (status != ERROR_CODE_SUCCESS){
392                 log_info("GOEP client, SDP query failed 0x%02x", status);
393                 goep_client->state = GOEP_CLIENT_INIT;
394                 goep_client_emit_connected_event(goep_client, status);
395                 break;
396             }
397             goep_server_found = false;
398             if (goep_client->rfcomm_port != 0){
399                 goep_server_found = true;
400             }
401 #ifdef ENABLE_GOEP_L2CAP
402             if (goep_client->l2cap_psm != 0){
403                 goep_server_found = true;
404             }
405 #endif
406             if (goep_server_found == false){
407                 log_info("No GOEP RFCOMM or L2CAP server found");
408                 goep_client->state = GOEP_CLIENT_INIT;
409                 goep_client_emit_connected_event(goep_client, SDP_SERVICE_NOT_FOUND);
410                 break;
411             }
412             (void) goep_client_start_connect(goep_client);
413             break;
414 
415         default:
416             break;
417     }
418 }
419 
420 static uint8_t * goep_client_get_outgoing_buffer(goep_client_t * goep_client){
421     if (goep_client->l2cap_psm){
422         return goep_packet_buffer;
423     } else {
424         return rfcomm_get_outgoing_buffer();
425     }
426 }
427 
428 static uint16_t goep_client_get_outgoing_buffer_len(goep_client_t * goep_client){
429     if (goep_client->l2cap_psm){
430         return sizeof(goep_packet_buffer);
431     } else {
432         return rfcomm_get_max_frame_size(goep_client->bearer_cid);
433     }
434 }
435 
436 static void goep_client_packet_init(goep_client_t *goep_client, uint8_t opcode) {
437     if (goep_client->l2cap_psm != 0){
438     } else {
439         rfcomm_reserve_packet_buffer();
440     }
441     // store opcode for parsing of response
442     goep_client->obex_opcode = opcode;
443 }
444 
445 void goep_client_init(void){
446     goep_client_singleton.state = GOEP_CLIENT_INIT;
447 }
448 
449 void goep_client_deinit(void){
450     goep_clients = NULL;
451     goep_client_cid = 0;
452     memset(&goep_client_singleton, 0, sizeof(goep_client_t));
453     memset(goep_client_sdp_query_attribute_value, 0, sizeof(goep_client_sdp_query_attribute_value));
454     memset(goep_packet_buffer, 0, sizeof(goep_packet_buffer));
455 }
456 
457 static void geop_client_sdp_query_start(void * context){
458     uint16_t goep_cid = (uint16_t)(uintptr_t) context;
459     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
460     if (context != NULL){
461         goep_client_sdp_active = goep_client;
462         sdp_client_query_uuid16(&goep_client_handle_sdp_query_event, goep_client->bd_addr,
463                                 goep_client->uuid);
464     }
465 }
466 
467 uint8_t
468 goep_client_connect(goep_client_t *goep_client, l2cap_ertm_config_t *l2cap_ertm_config, uint8_t *l2cap_ertm_buffer,
469                     uint16_t l2cap_ertm_buffer_size, btstack_packet_handler_t handler, bd_addr_t addr, uint16_t uuid,
470                     uint8_t instance_id, uint16_t *out_cid) {
471     btstack_assert(goep_client != NULL);
472 
473     // get new goep cid, skip 0x0000
474     goep_client_cid++;
475     if (goep_client_cid == 0) {
476         goep_client_cid = 1;
477     }
478 
479     // add to list
480     memset(goep_client, 0, sizeof(goep_client_t));
481     goep_client->state = GOEP_CLIENT_W4_SDP;
482     goep_client->cid = goep_client_cid;
483     goep_client->client_handler = handler;
484     goep_client->uuid = uuid;
485     goep_client->map_mas_instance_id = instance_id;
486     goep_client->profile_supported_features = PROFILE_FEATURES_NOT_PRESENT;
487     (void)memcpy(goep_client->bd_addr, addr, 6);
488     goep_client->sdp_query_request.callback = geop_client_sdp_query_start;
489     goep_client->sdp_query_request.context = (void *)(uintptr_t) goep_client->cid;
490     goep_client->obex_connection_id = OBEX_CONNECTION_ID_INVALID;
491 #ifdef ENABLE_GOEP_L2CAP
492     if (l2cap_ertm_config != NULL){
493         memcpy(&goep_client->ertm_config, l2cap_ertm_config, sizeof(l2cap_ertm_config_t));
494     }
495     goep_client->ertm_buffer_size = l2cap_ertm_buffer_size;
496     goep_client->ertm_buffer = l2cap_ertm_buffer;
497 #endif
498     btstack_linked_list_add(&goep_clients, (btstack_linked_item_t *) goep_client);
499 
500     // request sdp query
501     sdp_client_register_query_callback(&goep_client->sdp_query_request);
502 
503     *out_cid = goep_client->cid;
504     return ERROR_CODE_SUCCESS;
505 }
506 
507 #ifdef ENABLE_GOEP_L2CAP
508 uint8_t goep_client_connect_l2cap(goep_client_t *goep_client, l2cap_ertm_config_t *l2cap_ertm_config, uint8_t *l2cap_ertm_buffer,
509                           uint16_t l2cap_ertm_buffer_size, btstack_packet_handler_t handler, bd_addr_t addr, uint16_t l2cap_psm,
510                           uint16_t *out_cid){
511     btstack_assert(goep_client != NULL);
512     btstack_assert(l2cap_ertm_config != NULL);
513     btstack_assert(l2cap_ertm_buffer != NULL);
514 
515     // get new goep cid, skip 0x0000
516     goep_client_cid++;
517     if (goep_client_cid == 0) {
518         goep_client_cid = 1;
519     }
520 
521     // add to list
522     memset(goep_client, 0, sizeof(goep_client_t));
523     goep_client->state = GOEP_CLIENT_W4_SDP;
524     goep_client->cid = goep_client_cid;
525     goep_client->client_handler = handler;
526     goep_client->profile_supported_features = PROFILE_FEATURES_NOT_PRESENT;
527     (void)memcpy(goep_client->bd_addr, addr, 6);
528     goep_client->obex_connection_id = OBEX_CONNECTION_ID_INVALID;
529     memcpy(&goep_client->ertm_config, l2cap_ertm_config, sizeof(l2cap_ertm_config_t));
530     goep_client->ertm_buffer_size = l2cap_ertm_buffer_size;
531     goep_client->ertm_buffer = l2cap_ertm_buffer;
532     btstack_linked_list_add(&goep_clients, (btstack_linked_item_t *) goep_client);
533 
534     goep_client->l2cap_psm = l2cap_psm;
535 
536     *out_cid = goep_client->cid;
537 
538     return goep_client_start_connect(goep_client);
539 }
540 #endif
541 
542 uint8_t goep_client_create_connection(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t uuid, uint16_t * out_cid){
543     goep_client_t * goep_client = &goep_client_singleton;
544     if (goep_client->state != GOEP_CLIENT_INIT) {
545         return BTSTACK_MEMORY_ALLOC_FAILED;
546     }
547 #ifdef ENABLE_GOEP_L2CAP
548     return goep_client_connect(goep_client, &goep_client_singleton_ertm_config, goep_client_singleton_ertm_buffer,
549                                sizeof(goep_client_singleton_ertm_buffer), handler, addr, uuid, 0, out_cid);
550 #else
551     return goep_client_connect(goep_client,NULL, NULL, 0, handler, addr, uuid, 0, out_cid);
552 #endif
553 }
554 
555 uint32_t goep_client_get_pbap_supported_features(uint16_t goep_cid){
556     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
557     if (goep_client == NULL){
558         return PBAP_FEATURES_NOT_PRESENT;
559     }
560     return goep_client->profile_supported_features;
561 }
562 
563 uint32_t goep_client_get_map_supported_features(uint16_t goep_cid){
564     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
565     if (goep_client == NULL){
566         return PROFILE_FEATURES_NOT_PRESENT;
567     }
568     return goep_client->profile_supported_features;
569 }
570 
571 uint8_t goep_client_get_map_mas_instance_id(uint16_t goep_cid){
572     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
573     if (goep_client == NULL){
574         return 0;
575     }
576     return goep_client->map_mas_instance_id;
577 }
578 
579 uint8_t goep_client_get_map_supported_message_types(uint16_t goep_cid){
580     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
581     if (goep_client == NULL){
582         return 0;
583     }
584     return goep_client->map_supported_message_types;
585 }
586 
587 
588 bool goep_client_version_20_or_higher(uint16_t goep_cid){
589     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
590     if (goep_client == NULL){
591         return false;
592     }
593     return goep_client->l2cap_psm != 0;
594 }
595 
596 void goep_client_request_can_send_now(uint16_t goep_cid){
597     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
598     if (goep_client == NULL){
599         return;
600     }
601     if (goep_client->l2cap_psm){
602         l2cap_request_can_send_now_event(goep_client->bearer_cid);
603     } else {
604         rfcomm_request_can_send_now_event(goep_client->bearer_cid);
605     }
606 }
607 
608 uint8_t goep_client_disconnect(uint16_t goep_cid){
609     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
610     if (goep_client == NULL){
611         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
612     }
613     if (goep_client->l2cap_psm){
614         l2cap_disconnect(goep_client->bearer_cid);
615     } else {
616         rfcomm_disconnect(goep_client->bearer_cid);
617     }
618     return ERROR_CODE_SUCCESS;
619 }
620 
621 void goep_client_set_connection_id(uint16_t goep_cid, uint32_t connection_id){
622     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
623     if (goep_client == NULL){
624         return;
625     }
626     goep_client->obex_connection_id = connection_id;
627 }
628 
629 uint8_t goep_client_get_request_opcode(uint16_t goep_cid){
630     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
631     if (goep_client == NULL){
632         return 0;
633     }
634     return goep_client->obex_opcode;
635 }
636 
637 void goep_client_request_create_connect(uint16_t goep_cid, uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){
638     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
639     if (goep_client == NULL){
640         return;
641     }
642     goep_client_packet_init(goep_client, OBEX_OPCODE_CONNECT);
643     // workaround: limit OBEX packet len to L2CAP/RFCOMM MTU to avoid handling of fragemented packets
644     maximum_obex_packet_length = btstack_min(maximum_obex_packet_length, goep_client->bearer_mtu);
645 
646     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
647     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
648     obex_message_builder_request_create_connect(buffer, buffer_len, obex_version_number, flags, maximum_obex_packet_length);
649 }
650 
651 void goep_client_request_create_get(uint16_t goep_cid){
652     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
653     if (goep_client == NULL){
654         return;
655     }
656     goep_client_packet_init(goep_client, OBEX_OPCODE_GET | OBEX_OPCODE_FINAL_BIT_MASK);
657     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
658     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
659     obex_message_builder_request_create_get(buffer, buffer_len, goep_client->obex_connection_id);
660 }
661 
662 void goep_client_request_create_put(uint16_t goep_cid){
663     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
664     if (goep_client == NULL){
665         return;
666     }
667     goep_client_packet_init(goep_client, OBEX_OPCODE_PUT | OBEX_OPCODE_FINAL_BIT_MASK);
668     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
669     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
670     obex_message_builder_request_create_put(buffer, buffer_len, goep_client->obex_connection_id);
671 }
672 
673 void goep_client_request_create_set_path(uint16_t goep_cid, uint8_t flags){
674     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
675     if (goep_client == NULL){
676         return;
677     }
678     goep_client_packet_init(goep_client, OBEX_OPCODE_SETPATH);
679     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
680     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
681     obex_message_builder_request_create_set_path(buffer, buffer_len, flags, goep_client->obex_connection_id);
682 }
683 
684 void goep_client_request_create_abort(uint16_t goep_cid){
685     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
686     if (goep_client == NULL){
687         return;
688     }
689     goep_client_packet_init(goep_client, OBEX_OPCODE_ABORT);
690 
691     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
692     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
693     obex_message_builder_request_create_abort(buffer, buffer_len, goep_client->obex_connection_id);
694 }
695 
696 void goep_client_request_create_disconnect(uint16_t goep_cid){
697     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
698     if (goep_client == NULL){
699         return;
700     }
701     goep_client_packet_init(goep_client, OBEX_OPCODE_DISCONNECT);
702     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
703     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
704     obex_message_builder_request_create_disconnect(buffer, buffer_len, goep_client->obex_connection_id);
705 }
706 
707 void goep_client_header_add_byte(uint16_t goep_cid, uint8_t header_type, uint8_t value){
708     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
709     if (goep_client == NULL){
710         return;
711     }
712     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
713     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
714     obex_message_builder_header_add_byte(buffer, buffer_len, header_type, value);
715 }
716 
717 void goep_client_header_add_word(uint16_t goep_cid, uint8_t header_type, uint32_t value){
718     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
719     if (goep_client == NULL){
720         return;
721     }
722     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
723     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
724     obex_message_builder_header_add_word(buffer, buffer_len, header_type, value);
725 }
726 
727 void goep_client_header_add_variable(uint16_t goep_cid, uint8_t header_type, const uint8_t * header_data, uint16_t header_data_length){
728     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
729     if (goep_client == NULL){
730         return;
731     }
732     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
733     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
734     obex_message_builder_header_add_variable(buffer, buffer_len, header_type, header_data, header_data_length);
735 }
736 
737 void goep_client_header_add_srm_enable(uint16_t goep_cid){
738     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
739     if (goep_client == NULL){
740         return;
741     }
742     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
743     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
744     obex_message_builder_header_add_srm_enable(buffer, buffer_len);
745 }
746 
747 void goep_client_header_add_target(uint16_t goep_cid, const uint8_t * target, uint16_t length){
748     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
749     if (goep_client == NULL){
750         return;
751     }
752     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
753     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
754     obex_message_builder_header_add_target(buffer, buffer_len, target, length);
755 }
756 
757 void goep_client_header_add_application_parameters(uint16_t goep_cid, const uint8_t * data, uint16_t length){
758     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
759     if (goep_client == NULL){
760         return;
761     }
762     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
763     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
764     obex_message_builder_header_add_application_parameters(buffer, buffer_len, data, length);
765 }
766 
767 void goep_client_header_add_challenge_response(uint16_t goep_cid, const uint8_t * data, uint16_t length){
768     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
769     if (goep_client == NULL){
770         return;
771     }
772     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
773     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
774     obex_message_builder_header_add_challenge_response(buffer, buffer_len, data, length);
775 }
776 
777 void goep_client_body_add_static(uint16_t goep_cid, const uint8_t * data, uint32_t length){
778     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
779     if (goep_client == NULL){
780         return;
781     }
782     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
783     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
784     obex_message_builder_body_add_static(buffer, buffer_len, data, length);
785 }
786 
787 uint16_t goep_client_body_get_outgoing_buffer_len(uint16_t goep_cid) {
788     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
789     if (goep_client == NULL){
790         return 0;
791     }
792     return goep_client_get_outgoing_buffer_len(goep_client);
793 };
794 
795 void goep_client_body_fillup_static(uint16_t goep_cid, const uint8_t * data, uint32_t length, uint32_t * ret_length){
796     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
797     if (goep_client == NULL){
798         return;
799     }
800     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
801     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
802     obex_message_builder_body_fillup_static(buffer, buffer_len, data, length, ret_length);
803 }
804 
805 void goep_client_header_add_name(uint16_t goep_cid, const char * name){
806     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
807     if (goep_client == NULL){
808         return;
809     }
810     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
811     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
812     obex_message_builder_header_add_name(buffer, buffer_len, name);
813 }
814 
815 void goep_client_header_add_name_prefix(uint16_t goep_cid, const char * name, uint16_t name_len){
816     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
817     if (goep_client == NULL){
818         return;
819     }
820     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
821     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
822     obex_message_builder_header_add_name_prefix(buffer, buffer_len, name, name_len);
823 }
824 
825 void goep_client_header_add_unicode_prefix(uint16_t goep_cid, uint8_t header_id, const char * name, uint16_t name_len){
826     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
827     if (goep_client == NULL){
828         return;
829     }
830     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
831     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
832     obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, header_id, name, name_len);
833 }
834 
835 void goep_client_header_add_type(uint16_t goep_cid, const char * type){
836     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
837     if (goep_client == NULL){
838         return;
839     }
840     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
841     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
842     obex_message_builder_header_add_type(buffer, buffer_len, type);
843 }
844 
845 void goep_client_header_add_length(uint16_t goep_cid, uint32_t length){
846     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
847     if (goep_client == NULL){
848         return;
849     }
850     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
851     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
852     obex_message_builder_header_add_length(buffer, buffer_len, length);
853 }
854 
855 uint16_t goep_client_request_get_max_body_size(uint16_t goep_cid){
856     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
857     if (goep_client == NULL){
858         return 0;
859     }
860     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
861     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
862     uint16_t pos = big_endian_read_16(buffer, 1);
863     return buffer_len - pos;
864 }
865 
866 int goep_client_execute(uint16_t goep_cid){
867     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
868     if (goep_client == NULL){
869         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
870     }
871     return goep_client_execute_with_final_bit (goep_cid, true);
872 }
873 
874 int goep_client_execute_with_final_bit(uint16_t goep_cid, bool final){
875     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
876     if (goep_client == NULL){
877         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
878     }
879     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
880     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
881 
882     obex_message_builder_set_final_bit (buffer, buffer_len, final);
883 
884     uint16_t pos = big_endian_read_16(buffer, 1);
885     if (goep_client->l2cap_psm){
886         return l2cap_send(goep_client->bearer_cid, buffer, pos);
887     } else {
888         return rfcomm_send_prepared(goep_client->bearer_cid, pos);
889     }
890 }
891 
892