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