xref: /btstack/src/classic/goep_client.c (revision 511c7f3dfac14d3dabcc99fbb2cb5b1507dca670)
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         btstack_linked_list_remove(&goep_clients, (btstack_linked_item_t *) goep_client);
143         log_info("goep_client: open failed, status %u", status);
144     } else {
145         goep_client->bearer_mtu = mtu;
146         goep_client->state = GOEP_CLIENT_CONNECTED;
147         log_info("goep_client: connection opened. cid %u, max frame size %u", goep_client->bearer_cid, goep_client->bearer_mtu);
148     }
149     goep_client_emit_connected_event(goep_client, status);
150 }
151 
152 static void goep_client_handle_connection_close(goep_client_t * goep_client){
153     goep_client->state = GOEP_CLIENT_INIT;
154     btstack_linked_list_remove(&goep_clients, (btstack_linked_item_t *) goep_client);
155     goep_client_emit_connection_closed_event(goep_client);
156 }
157 
158 static goep_client_t * goep_client_for_cid(uint16_t cid){
159     btstack_linked_list_iterator_t it;
160     btstack_linked_list_iterator_init(&it, &goep_clients);
161     while (btstack_linked_list_iterator_has_next(&it)){
162         goep_client_t * goep_client = (goep_client_t *) btstack_linked_list_iterator_next(&it);
163         if (goep_client->cid == cid){
164             return goep_client;
165         }
166     }
167     return NULL;
168 }
169 
170 static goep_client_t * goep_client_for_bearer_cid(uint16_t bearer_cid){
171     btstack_linked_list_iterator_t it;
172     btstack_linked_list_iterator_init(&it, &goep_clients);
173     while (btstack_linked_list_iterator_has_next(&it)){
174         goep_client_t * goep_client = (goep_client_t *) btstack_linked_list_iterator_next(&it);
175         if (goep_client->bearer_cid == bearer_cid){
176             return goep_client;
177         }
178     }
179     return NULL;
180 }
181 
182 static void goep_client_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
183     UNUSED(channel);
184     UNUSED(size);
185     goep_client_t * goep_client;
186     switch (packet_type){
187         case HCI_EVENT_PACKET:
188             switch (hci_event_packet_get_type(packet)) {
189 #ifdef ENABLE_GOEP_L2CAP
190                 case L2CAP_EVENT_CHANNEL_OPENED:
191                     goep_client = goep_client_for_bearer_cid(l2cap_event_channel_opened_get_local_cid(packet));
192                     btstack_assert(goep_client != NULL);
193                     goep_client_handle_connection_opened(goep_client, l2cap_event_channel_opened_get_status(packet),
194                                                          btstack_min(l2cap_event_channel_opened_get_remote_mtu(packet), l2cap_event_channel_opened_get_local_mtu(packet)));
195                     break;
196                 case L2CAP_EVENT_CAN_SEND_NOW:
197                     goep_client = goep_client_for_bearer_cid(l2cap_event_can_send_now_get_local_cid(packet));
198                     btstack_assert(goep_client != NULL);
199                     goep_client_emit_can_send_now_event(goep_client);
200                     break;
201                 case L2CAP_EVENT_CHANNEL_CLOSED:
202                     goep_client = goep_client_for_bearer_cid(l2cap_event_channel_closed_get_local_cid(packet));
203                     btstack_assert(goep_client != NULL);
204                     goep_client_handle_connection_close(goep_client);
205                     break;
206 #endif
207                 case RFCOMM_EVENT_CHANNEL_OPENED:
208                     goep_client = goep_client_for_bearer_cid(rfcomm_event_channel_opened_get_rfcomm_cid(packet));
209                     btstack_assert(goep_client != NULL);
210                     goep_client_handle_connection_opened(goep_client, rfcomm_event_channel_opened_get_status(packet), rfcomm_event_channel_opened_get_max_frame_size(packet));
211                     return;
212                 case RFCOMM_EVENT_CAN_SEND_NOW:
213                     goep_client = goep_client_for_bearer_cid(rfcomm_event_can_send_now_get_rfcomm_cid(packet));
214                     btstack_assert(goep_client != NULL);
215                     goep_client_emit_can_send_now_event(goep_client);
216                     break;
217                 case RFCOMM_EVENT_CHANNEL_CLOSED:
218                     goep_client = goep_client_for_bearer_cid(rfcomm_event_channel_closed_get_rfcomm_cid(packet));
219                     btstack_assert(goep_client != NULL);
220                     goep_client_handle_connection_close(goep_client);
221                     break;
222                 default:
223                     break;
224             }
225             break;
226         case L2CAP_DATA_PACKET:
227         case RFCOMM_DATA_PACKET:
228             goep_client = goep_client_for_bearer_cid(channel);
229             btstack_assert(goep_client != NULL);
230             goep_client->client_handler(GOEP_DATA_PACKET, goep_client->cid, packet, size);
231             break;
232         default:
233             break;
234     }
235 }
236 
237 static void goep_client_handle_sdp_query_end_of_record(goep_client_t * goep_client){
238     if (goep_client->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER){
239         if (goep_client->mas_info.instance_id == goep_client->map_mas_instance_id){
240             // Requested MAS Instance found, accept info
241             goep_client->rfcomm_port = goep_client->mas_info.rfcomm_port;
242             goep_client->profile_supported_features = goep_client->mas_info.supported_features;
243             goep_client->map_supported_message_types = goep_client->mas_info.supported_message_types;
244 #ifdef ENABLE_GOEP_L2CAP
245             goep_client->l2cap_psm = goep_client->mas_info.l2cap_psm;
246             log_info("MAS Instance #%u found, rfcomm #%u, l2cap 0x%04x", goep_client->map_mas_instance_id,
247                      goep_client->rfcomm_port, goep_client->l2cap_psm);
248 #else
249             log_info("MAS Instance #%u found, rfcomm #%u", goep_client->map_mas_instance_id,
250                      goep_client->rfcomm_port);
251 #endif
252         }
253     }
254 }
255 static uint8_t goep_client_start_connect(goep_client_t * goep_client){
256 #ifdef ENABLE_GOEP_L2CAP
257     if (goep_client->l2cap_psm && (goep_client->ertm_buffer != NULL)){
258         log_info("Remote GOEP L2CAP PSM: %u", goep_client->l2cap_psm);
259         return l2cap_ertm_create_channel(&goep_client_packet_handler, goep_client->bd_addr, goep_client->l2cap_psm,
260         &goep_client->ertm_config, goep_client->ertm_buffer,
261         goep_client->ertm_buffer_size, &goep_client->bearer_cid);
262     }
263 #endif
264     log_info("Remote GOEP RFCOMM Server Channel: %u", goep_client->rfcomm_port);
265     return rfcomm_create_channel(&goep_client_packet_handler, goep_client->bd_addr, goep_client->rfcomm_port, &goep_client->bearer_cid);
266 }
267 
268 static void goep_client_handle_sdp_query_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
269     goep_client_t * goep_client = goep_client_sdp_active;
270     btstack_assert(goep_client != NULL);
271 
272     UNUSED(packet_type);
273     UNUSED(channel);
274     UNUSED(size);
275 
276     des_iterator_t des_list_it;
277     des_iterator_t prot_it;
278     uint8_t status;
279     uint16_t record_index;
280     bool goep_server_found;
281 
282     switch (hci_event_packet_get_type(packet)){
283         case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
284 
285             // detect new record
286             record_index = sdp_event_query_attribute_byte_get_record_id(packet);
287             if (record_index != goep_client->record_index){
288                 goep_client->record_index = record_index;
289                 goep_client_handle_sdp_query_end_of_record(goep_client);
290                 memset(&goep_client->mas_info, 0, sizeof(goep_client->mas_info));
291             }
292 
293             // check if relevant attribute
294             switch(sdp_event_query_attribute_byte_get_attribute_id(packet)){
295                 case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
296                 case BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES:
297                 case BLUETOOTH_ATTRIBUTE_MAS_INSTANCE_ID:
298                 case BLUETOOTH_ATTRIBUTE_SUPPORTED_MESSAGE_TYPES:
299 #ifdef ENABLE_GOEP_L2CAP
300                 case BLUETOOTH_ATTRIBUTE_GOEP_L2CAP_PSM:
301 #endif
302                     break;
303                 default:
304                     return;
305             }
306 
307             // warn if attribute too large to fit in our buffer
308             if (sdp_event_query_attribute_byte_get_attribute_length(packet) > goep_client_sdp_query_attribute_value_buffer_size) {
309                 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));
310                 break;
311             }
312 
313             // store single byte
314             goep_client_sdp_query_attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet);
315 
316             // wait until value fully received
317             if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) != sdp_event_query_attribute_byte_get_attribute_length(packet)) break;
318 
319             // process attributes
320             switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) {
321                 case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
322                     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)) {
323                         uint8_t       *des_element;
324                         uint8_t       *element;
325                         uint32_t       uuid;
326 
327                         if (des_iterator_get_type(&des_list_it) != DE_DES) continue;
328 
329                         des_element = des_iterator_get_element(&des_list_it);
330                         des_iterator_init(&prot_it, des_element);
331 
332                         // first element is UUID
333                         element = des_iterator_get_element(&prot_it);
334                         if (de_get_element_type(element) != DE_UUID) continue;
335 
336                         uuid = de_get_uuid32(element);
337                         des_iterator_next(&prot_it);
338                         if (!des_iterator_has_more(&prot_it)) continue;
339 
340                         // second element is RFCOMM server channel or L2CAP PSM
341                         element = des_iterator_get_element(&prot_it);
342                         if (uuid == BLUETOOTH_PROTOCOL_RFCOMM){
343                             if (goep_client->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER) {
344                                 goep_client->mas_info.rfcomm_port = element[de_get_header_size(element)];
345                             } else {
346                                 goep_client->rfcomm_port = element[de_get_header_size(element)];
347                             }
348                         }
349                     }
350                     break;
351 #ifdef ENABLE_GOEP_L2CAP
352                 case BLUETOOTH_ATTRIBUTE_GOEP_L2CAP_PSM:
353                     if (goep_client->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER){
354                         de_element_get_uint16(goep_client_sdp_query_attribute_value, &goep_client->mas_info.l2cap_psm);
355                     } else {
356                         de_element_get_uint16(goep_client_sdp_query_attribute_value, &goep_client->l2cap_psm);
357                     }
358                     break;
359 #endif
360                 // BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES == BLUETOOTH_ATTRIBUTE_MAP_SUPPORTED_FEATURES == 0x0317
361                 case BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES:
362                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
363                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_32) break;
364                     if (goep_client->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER) {
365                         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));
366                     } else {
367                         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));
368                     }
369                     break;
370 
371                 case BLUETOOTH_ATTRIBUTE_MAS_INSTANCE_ID:
372                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
373                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_8) break;
374                     goep_client->mas_info.instance_id = goep_client_sdp_query_attribute_value[de_get_header_size(goep_client_sdp_query_attribute_value)];
375                     break;
376 
377                 case BLUETOOTH_ATTRIBUTE_SUPPORTED_MESSAGE_TYPES:
378                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
379                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_8) break;
380                     goep_client->mas_info.supported_message_types = goep_client_sdp_query_attribute_value[de_get_header_size(goep_client_sdp_query_attribute_value)];
381                     break;
382 
383                 default:
384                     break;
385             }
386             break;
387 
388         case SDP_EVENT_QUERY_COMPLETE:
389             goep_client_sdp_active = NULL;
390             goep_client_handle_sdp_query_end_of_record(goep_client);
391             status = sdp_event_query_complete_get_status(packet);
392             if (status != ERROR_CODE_SUCCESS){
393                 log_info("GOEP client, SDP query failed 0x%02x", status);
394                 goep_client_handle_connection_opened(goep_client, status, 0);
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_handle_connection_opened(goep_client, SDP_SERVICE_NOT_FOUND, 0);
409                 break;
410             }
411             (void) goep_client_start_connect(goep_client);
412             break;
413 
414         default:
415             break;
416     }
417 }
418 
419 static uint8_t * goep_client_get_outgoing_buffer(goep_client_t * goep_client){
420     if (goep_client->l2cap_psm){
421         return goep_packet_buffer;
422     } else {
423         return rfcomm_get_outgoing_buffer();
424     }
425 }
426 
427 static uint16_t goep_client_get_outgoing_buffer_len(goep_client_t * goep_client){
428     if (goep_client->l2cap_psm){
429         return sizeof(goep_packet_buffer);
430     } else {
431         return rfcomm_get_max_frame_size(goep_client->bearer_cid);
432     }
433 }
434 
435 static void goep_client_packet_init(goep_client_t *goep_client, uint8_t opcode) {
436     if (goep_client->l2cap_psm != 0){
437     } else {
438         rfcomm_reserve_packet_buffer();
439     }
440     // store opcode for parsing of response
441     goep_client->obex_opcode = opcode;
442 }
443 
444 void goep_client_init(void){
445     goep_client_singleton.state = GOEP_CLIENT_INIT;
446 }
447 
448 void goep_client_deinit(void){
449     goep_clients = NULL;
450     goep_client_cid = 0;
451     memset(&goep_client_singleton, 0, sizeof(goep_client_t));
452     memset(goep_client_sdp_query_attribute_value, 0, sizeof(goep_client_sdp_query_attribute_value));
453     memset(goep_packet_buffer, 0, sizeof(goep_packet_buffer));
454 }
455 
456 static void geop_client_sdp_query_start(void * context){
457     uint16_t goep_cid = (uint16_t)(uintptr_t) context;
458     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
459     if (context != NULL){
460         goep_client_sdp_active = goep_client;
461         sdp_client_query_uuid16(&goep_client_handle_sdp_query_event, goep_client->bd_addr,
462                                 goep_client->uuid);
463     }
464 }
465 
466 uint8_t
467 goep_client_connect(goep_client_t *goep_client, l2cap_ertm_config_t *l2cap_ertm_config, uint8_t *l2cap_ertm_buffer,
468                     uint16_t l2cap_ertm_buffer_size, btstack_packet_handler_t handler, bd_addr_t addr, uint16_t uuid,
469                     uint8_t instance_id, uint16_t *out_cid) {
470     btstack_assert(goep_client != NULL);
471 
472     // get new goep cid, skip 0x0000
473     goep_client_cid++;
474     if (goep_client_cid == 0) {
475         goep_client_cid = 1;
476     }
477 
478     // add to list
479     memset(goep_client, 0, sizeof(goep_client_t));
480     goep_client->state = GOEP_CLIENT_W4_SDP;
481     goep_client->cid = goep_client_cid;
482     goep_client->client_handler = handler;
483     goep_client->uuid = uuid;
484     goep_client->map_mas_instance_id = instance_id;
485     goep_client->profile_supported_features = PROFILE_FEATURES_NOT_PRESENT;
486     (void)memcpy(goep_client->bd_addr, addr, 6);
487     goep_client->sdp_query_request.callback = geop_client_sdp_query_start;
488     goep_client->sdp_query_request.context = (void *)(uintptr_t) goep_client->cid;
489     goep_client->obex_connection_id = OBEX_CONNECTION_ID_INVALID;
490 #ifdef ENABLE_GOEP_L2CAP
491     if (l2cap_ertm_config != NULL){
492         memcpy(&goep_client->ertm_config, l2cap_ertm_config, sizeof(l2cap_ertm_config_t));
493     }
494     goep_client->ertm_buffer_size = l2cap_ertm_buffer_size;
495     goep_client->ertm_buffer = l2cap_ertm_buffer;
496 #endif
497     btstack_linked_list_add(&goep_clients, (btstack_linked_item_t *) goep_client);
498 
499     // request sdp query
500     sdp_client_register_query_callback(&goep_client->sdp_query_request);
501 
502     *out_cid = goep_client->cid;
503     return ERROR_CODE_SUCCESS;
504 }
505 
506 #ifdef ENABLE_GOEP_L2CAP
507 uint8_t goep_client_connect_l2cap(goep_client_t *goep_client, l2cap_ertm_config_t *l2cap_ertm_config, uint8_t *l2cap_ertm_buffer,
508                           uint16_t l2cap_ertm_buffer_size, btstack_packet_handler_t handler, bd_addr_t addr, uint16_t l2cap_psm,
509                           uint16_t *out_cid){
510     btstack_assert(goep_client != NULL);
511     btstack_assert(l2cap_ertm_config != NULL);
512     btstack_assert(l2cap_ertm_buffer != NULL);
513 
514     // get new goep cid, skip 0x0000
515     goep_client_cid++;
516     if (goep_client_cid == 0) {
517         goep_client_cid = 1;
518     }
519 
520     // add to list
521     memset(goep_client, 0, sizeof(goep_client_t));
522     goep_client->state = GOEP_CLIENT_W4_SDP;
523     goep_client->cid = goep_client_cid;
524     goep_client->client_handler = handler;
525     goep_client->profile_supported_features = PROFILE_FEATURES_NOT_PRESENT;
526     (void)memcpy(goep_client->bd_addr, addr, 6);
527     goep_client->obex_connection_id = OBEX_CONNECTION_ID_INVALID;
528     memcpy(&goep_client->ertm_config, l2cap_ertm_config, sizeof(l2cap_ertm_config_t));
529     goep_client->ertm_buffer_size = l2cap_ertm_buffer_size;
530     goep_client->ertm_buffer = l2cap_ertm_buffer;
531     btstack_linked_list_add_tail(&goep_clients, (btstack_linked_item_t *) goep_client);
532 
533     goep_client->l2cap_psm = l2cap_psm;
534 
535     *out_cid = goep_client->cid;
536 
537     return goep_client_start_connect(goep_client);
538 }
539 #endif
540 
541 uint8_t goep_client_create_connection(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t uuid, uint16_t * out_cid){
542     goep_client_t * goep_client = &goep_client_singleton;
543     if (goep_client->state != GOEP_CLIENT_INIT) {
544         return BTSTACK_MEMORY_ALLOC_FAILED;
545     }
546 #ifdef ENABLE_GOEP_L2CAP
547     return goep_client_connect(goep_client, &goep_client_singleton_ertm_config, goep_client_singleton_ertm_buffer,
548                                sizeof(goep_client_singleton_ertm_buffer), handler, addr, uuid, 0, out_cid);
549 #else
550     return goep_client_connect(goep_client,NULL, NULL, 0, handler, addr, uuid, 0, out_cid);
551 #endif
552 }
553 
554 uint32_t goep_client_get_pbap_supported_features(uint16_t goep_cid){
555     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
556     if (goep_client == NULL){
557         return PBAP_FEATURES_NOT_PRESENT;
558     }
559     return goep_client->profile_supported_features;
560 }
561 
562 uint32_t goep_client_get_map_supported_features(uint16_t goep_cid){
563     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
564     if (goep_client == NULL){
565         return PROFILE_FEATURES_NOT_PRESENT;
566     }
567     return goep_client->profile_supported_features;
568 }
569 
570 uint8_t goep_client_get_map_mas_instance_id(uint16_t goep_cid){
571     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
572     if (goep_client == NULL){
573         return 0;
574     }
575     return goep_client->map_mas_instance_id;
576 }
577 
578 uint8_t goep_client_get_map_supported_message_types(uint16_t goep_cid){
579     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
580     if (goep_client == NULL){
581         return 0;
582     }
583     return goep_client->map_supported_message_types;
584 }
585 
586 
587 bool goep_client_version_20_or_higher(uint16_t goep_cid){
588     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
589     if (goep_client == NULL){
590         return false;
591     }
592     return goep_client->l2cap_psm != 0;
593 }
594 
595 void goep_client_request_can_send_now(uint16_t goep_cid){
596     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
597     if (goep_client == NULL){
598         return;
599     }
600     if (goep_client->l2cap_psm){
601         l2cap_request_can_send_now_event(goep_client->bearer_cid);
602     } else {
603         rfcomm_request_can_send_now_event(goep_client->bearer_cid);
604     }
605 }
606 
607 uint8_t goep_client_disconnect(uint16_t goep_cid){
608     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
609     if (goep_client == NULL){
610         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
611     }
612     if (goep_client->l2cap_psm){
613         l2cap_disconnect(goep_client->bearer_cid);
614     } else {
615         rfcomm_disconnect(goep_client->bearer_cid);
616     }
617     return ERROR_CODE_SUCCESS;
618 }
619 
620 void goep_client_set_connection_id(uint16_t goep_cid, uint32_t connection_id){
621     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
622     if (goep_client == NULL){
623         return;
624     }
625     goep_client->obex_connection_id = connection_id;
626 }
627 
628 uint8_t goep_client_get_request_opcode(uint16_t goep_cid){
629     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
630     if (goep_client == NULL){
631         return 0;
632     }
633     return goep_client->obex_opcode;
634 }
635 
636 void goep_client_request_create_connect(uint16_t goep_cid, uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){
637     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
638     if (goep_client == NULL){
639         return;
640     }
641     goep_client_packet_init(goep_client, OBEX_OPCODE_CONNECT);
642     // workaround: limit OBEX packet len to L2CAP/RFCOMM MTU to avoid handling of fragemented packets
643     maximum_obex_packet_length = btstack_min(maximum_obex_packet_length, goep_client->bearer_mtu);
644 
645     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
646     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
647     obex_message_builder_request_create_connect(buffer, buffer_len, obex_version_number, flags, maximum_obex_packet_length);
648 }
649 
650 void goep_client_request_create_get(uint16_t goep_cid){
651     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
652     if (goep_client == NULL){
653         return;
654     }
655     goep_client_packet_init(goep_client, OBEX_OPCODE_GET | OBEX_OPCODE_FINAL_BIT_MASK);
656     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
657     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
658     obex_message_builder_request_create_get(buffer, buffer_len, goep_client->obex_connection_id);
659 }
660 
661 void goep_client_request_create_put(uint16_t goep_cid){
662     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
663     if (goep_client == NULL){
664         return;
665     }
666     goep_client_packet_init(goep_client, OBEX_OPCODE_PUT | OBEX_OPCODE_FINAL_BIT_MASK);
667     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
668     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
669     obex_message_builder_request_create_put(buffer, buffer_len, goep_client->obex_connection_id);
670 }
671 
672 void goep_client_request_create_set_path(uint16_t goep_cid, uint8_t flags){
673     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
674     if (goep_client == NULL){
675         return;
676     }
677     goep_client_packet_init(goep_client, OBEX_OPCODE_SETPATH);
678     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
679     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
680     obex_message_builder_request_create_set_path(buffer, buffer_len, flags, goep_client->obex_connection_id);
681 }
682 
683 void goep_client_request_create_abort(uint16_t goep_cid){
684     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
685     if (goep_client == NULL){
686         return;
687     }
688     goep_client_packet_init(goep_client, OBEX_OPCODE_ABORT);
689 
690     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
691     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
692     obex_message_builder_request_create_abort(buffer, buffer_len, goep_client->obex_connection_id);
693 }
694 
695 void goep_client_request_create_disconnect(uint16_t goep_cid){
696     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
697     if (goep_client == NULL){
698         return;
699     }
700     goep_client_packet_init(goep_client, OBEX_OPCODE_DISCONNECT);
701     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
702     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
703     obex_message_builder_request_create_disconnect(buffer, buffer_len, goep_client->obex_connection_id);
704 }
705 
706 void goep_client_header_add_byte(uint16_t goep_cid, uint8_t header_type, uint8_t value){
707     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
708     if (goep_client == NULL){
709         return;
710     }
711     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
712     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
713     obex_message_builder_header_add_byte(buffer, buffer_len, header_type, value);
714 }
715 
716 void goep_client_header_add_word(uint16_t goep_cid, uint8_t header_type, uint32_t value){
717     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
718     if (goep_client == NULL){
719         return;
720     }
721     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
722     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
723     obex_message_builder_header_add_word(buffer, buffer_len, header_type, value);
724 }
725 
726 void goep_client_header_add_variable(uint16_t goep_cid, uint8_t header_type, const uint8_t * header_data, uint16_t header_data_length){
727     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
728     if (goep_client == NULL){
729         return;
730     }
731     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
732     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
733     obex_message_builder_header_add_variable(buffer, buffer_len, header_type, header_data, header_data_length);
734 }
735 
736 void goep_client_header_add_srm_enable(uint16_t goep_cid){
737     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
738     if (goep_client == NULL){
739         return;
740     }
741     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
742     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
743     obex_message_builder_header_add_srm_enable(buffer, buffer_len);
744 }
745 
746 void goep_client_header_add_target(uint16_t goep_cid, const uint8_t * target, uint16_t length){
747     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
748     if (goep_client == NULL){
749         return;
750     }
751     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
752     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
753     obex_message_builder_header_add_target(buffer, buffer_len, target, length);
754 }
755 
756 void goep_client_header_add_application_parameters(uint16_t goep_cid, const uint8_t * data, uint16_t length){
757     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
758     if (goep_client == NULL){
759         return;
760     }
761     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
762     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
763     obex_message_builder_header_add_application_parameters(buffer, buffer_len, data, length);
764 }
765 
766 void goep_client_header_add_challenge_response(uint16_t goep_cid, const uint8_t * data, uint16_t length){
767     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
768     if (goep_client == NULL){
769         return;
770     }
771     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
772     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
773     obex_message_builder_header_add_challenge_response(buffer, buffer_len, data, length);
774 }
775 
776 void goep_client_body_add_static(uint16_t goep_cid, const uint8_t * data, uint32_t length){
777     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
778     if (goep_client == NULL){
779         return;
780     }
781     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
782     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
783     obex_message_builder_body_add_static(buffer, buffer_len, data, length);
784 }
785 
786 uint16_t goep_client_body_get_outgoing_buffer_len(uint16_t goep_cid) {
787     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
788     if (goep_client == NULL){
789         return 0;
790     }
791     return goep_client_get_outgoing_buffer_len(goep_client);
792 };
793 
794 void goep_client_body_fillup_static(uint16_t goep_cid, const uint8_t * data, uint32_t length, uint32_t * ret_length){
795     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
796     if (goep_client == NULL){
797         return;
798     }
799     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
800     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
801     obex_message_builder_body_fillup_static(buffer, buffer_len, data, length, ret_length);
802 }
803 
804 void goep_client_header_add_name(uint16_t goep_cid, const char * name){
805     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
806     if (goep_client == NULL){
807         return;
808     }
809     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
810     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
811     obex_message_builder_header_add_name(buffer, buffer_len, name);
812 }
813 
814 void goep_client_header_add_name_prefix(uint16_t goep_cid, const char * name, uint16_t name_len){
815     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
816     if (goep_client == NULL){
817         return;
818     }
819     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
820     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
821     obex_message_builder_header_add_name_prefix(buffer, buffer_len, name, name_len);
822 }
823 
824 void goep_client_header_add_unicode_prefix(uint16_t goep_cid, uint8_t header_id, const char * name, uint16_t name_len){
825     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
826     if (goep_client == NULL){
827         return;
828     }
829     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
830     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
831     obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, header_id, name, name_len);
832 }
833 
834 void goep_client_header_add_type(uint16_t goep_cid, const char * type){
835     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
836     if (goep_client == NULL){
837         return;
838     }
839     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
840     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
841     obex_message_builder_header_add_type(buffer, buffer_len, type);
842 }
843 
844 void goep_client_header_add_length(uint16_t goep_cid, uint32_t length){
845     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
846     if (goep_client == NULL){
847         return;
848     }
849     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
850     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
851     obex_message_builder_header_add_length(buffer, buffer_len, length);
852 }
853 
854 uint16_t goep_client_request_get_max_body_size(uint16_t goep_cid){
855     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
856     if (goep_client == NULL){
857         return 0;
858     }
859     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
860     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
861     uint16_t pos = big_endian_read_16(buffer, 1);
862     return buffer_len - pos;
863 }
864 
865 int goep_client_execute(uint16_t goep_cid){
866     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
867     if (goep_client == NULL){
868         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
869     }
870     return goep_client_execute_with_final_bit (goep_cid, true);
871 }
872 
873 int goep_client_execute_with_final_bit(uint16_t goep_cid, bool final){
874     goep_client_t * goep_client = goep_client_for_cid(goep_cid);
875     if (goep_client == NULL){
876         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
877     }
878     uint8_t * buffer = goep_client_get_outgoing_buffer(goep_client);
879     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(goep_client);
880 
881     obex_message_builder_set_final_bit (buffer, buffer_len, final);
882 
883     uint16_t pos = big_endian_read_16(buffer, 1);
884     if (goep_client->l2cap_psm){
885         return l2cap_send(goep_client->bearer_cid, buffer, pos);
886     } else {
887         return rfcomm_send_prepared(goep_client->bearer_cid, pos);
888     }
889 }
890 
891