xref: /btstack/src/classic/goep_client.c (revision 7324832b776c08ffb87e039f7b9c8dafd424821c)
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 // #define ENABLE_GOEP_L2CAP
63 
64 #ifdef ENABLE_GOEP_L2CAP
65 #ifndef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
66 #error "ENABLE_GOEP_L2CAP requires ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE. Please enable ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE or disable ENABLE_GOEP_L2CAP"
67 #endif
68 #endif
69 
70 typedef enum {
71     GOEP_INIT,
72     GOEP_W4_SDP,
73     GOEP_W4_CONNECTION,
74     GOEP_CONNECTED,
75 } goep_state_t;
76 
77 typedef struct {
78     uint16_t         cid;
79     goep_state_t     state;
80     bd_addr_t        bd_addr;
81     uint16_t         uuid;
82     hci_con_handle_t con_handle;
83     uint8_t          incoming;
84     uint8_t          rfcomm_port;
85     uint16_t         l2cap_psm;
86     uint16_t         bearer_cid;
87     uint16_t         bearer_mtu;
88 
89     uint16_t         record_index;
90 
91     // cached higher layer information PBAP + MAP
92     uint32_t         profile_supported_features;
93     uint8_t          map_mas_instance_id;
94     uint8_t          map_supported_message_types;
95 
96     // needed to select one of multiple MAS Instances
97     struct {
98         uint32_t supported_features;
99         uint16_t l2cap_psm;
100         uint8_t  instance_id;
101         uint8_t  supported_message_types;
102         uint8_t  rfcomm_port;
103     } mas_info;
104 
105     uint8_t          obex_opcode;
106     uint32_t         obex_connection_id;
107     int              obex_connection_id_set;
108 
109     btstack_packet_handler_t client_handler;
110 } goep_client_t;
111 
112 static goep_client_t   goep_client_singleton;
113 static goep_client_t * goep_client = &goep_client_singleton;
114 
115 static uint8_t            goep_client_sdp_query_attribute_value[30];
116 static const unsigned int goep_client_sdp_query_attribute_value_buffer_size = sizeof(goep_client_sdp_query_attribute_value);
117 
118 static uint8_t goep_packet_buffer[150];
119 
120 #ifdef ENABLE_GOEP_L2CAP
121 static uint8_t ertm_buffer[1000];
122 static l2cap_ertm_config_t ertm_config = {
123     1,  // ertm mandatory
124     2,  // max transmit, some tests require > 1
125     2000,
126     12000,
127     512,    // l2cap ertm mtu
128     2,
129     2,
130     1,      // 16-bit FCS
131 };
132 #endif
133 
134 static inline void goep_client_emit_connected_event(goep_client_t * context, uint8_t status){
135     uint8_t event[15];
136     int pos = 0;
137     event[pos++] = HCI_EVENT_GOEP_META;
138     pos++;  // skip len
139     event[pos++] = GOEP_SUBEVENT_CONNECTION_OPENED;
140     little_endian_store_16(event,pos,context->cid);
141     pos+=2;
142     event[pos++] = status;
143     (void)memcpy(&event[pos], context->bd_addr, 6);
144     pos += 6;
145     little_endian_store_16(event,pos,context->con_handle);
146     pos += 2;
147     event[pos++] = context->incoming;
148     event[1] = pos - 2;
149     if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos);
150     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
151 }
152 
153 static inline void goep_client_emit_connection_closed_event(goep_client_t * context){
154     uint8_t event[5];
155     int pos = 0;
156     event[pos++] = HCI_EVENT_GOEP_META;
157     pos++;  // skip len
158     event[pos++] = GOEP_SUBEVENT_CONNECTION_CLOSED;
159     little_endian_store_16(event,pos,context->cid);
160     pos+=2;
161     event[1] = pos - 2;
162     if (pos != sizeof(event)) log_error("goep_client_emit_connection_closed_event size %u", pos);
163     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
164 }
165 
166 static inline void goep_client_emit_can_send_now_event(goep_client_t * context){
167     uint8_t event[5];
168     int pos = 0;
169     event[pos++] = HCI_EVENT_GOEP_META;
170     pos++;  // skip len
171     event[pos++] = GOEP_SUBEVENT_CAN_SEND_NOW;
172     little_endian_store_16(event,pos,context->cid);
173     pos+=2;
174     event[1] = pos - 2;
175     if (pos != sizeof(event)) log_error("goep_client_emit_can_send_now_event size %u", pos);
176     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
177 }
178 
179 static void goep_client_handle_connection_opened(goep_client_t * context, uint8_t status, uint16_t mtu){
180     if (status) {
181         context->state = GOEP_INIT;
182         log_info("goep_client: open failed, status %u", status);
183     } else {
184         context->bearer_mtu = mtu;
185         context->state = GOEP_CONNECTED;
186         log_info("goep_client: connection opened. cid %u, max frame size %u", context->bearer_cid, context->bearer_mtu);
187     }
188     goep_client_emit_connected_event(context, status);
189 }
190 
191 static void goep_client_handle_connection_close(goep_client_t * context){
192     context->state = GOEP_INIT;
193     goep_client_emit_connection_closed_event(context);
194 }
195 
196 static void goep_client_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
197     UNUSED(channel);
198     UNUSED(size);
199     goep_client_t * context = goep_client;
200     switch (packet_type){
201         case HCI_EVENT_PACKET:
202             switch (hci_event_packet_get_type(packet)) {
203 #ifdef ENABLE_GOEP_L2CAP
204                 case L2CAP_EVENT_CHANNEL_OPENED:
205                     goep_client_handle_connection_opened(context, l2cap_event_channel_opened_get_status(packet),
206                         btstack_min(l2cap_event_channel_opened_get_remote_mtu(packet), l2cap_event_channel_opened_get_local_mtu(packet)));
207                     return;
208                 case L2CAP_EVENT_CAN_SEND_NOW:
209                     goep_client_emit_can_send_now_event(context);
210                     break;
211                 case L2CAP_EVENT_CHANNEL_CLOSED:
212                     goep_client_handle_connection_close(context);
213                     break;
214 #endif
215                 case RFCOMM_EVENT_CHANNEL_OPENED:
216                     goep_client_handle_connection_opened(context, rfcomm_event_channel_opened_get_status(packet), rfcomm_event_channel_opened_get_max_frame_size(packet));
217                     return;
218                 case RFCOMM_EVENT_CAN_SEND_NOW:
219                     goep_client_emit_can_send_now_event(context);
220                     break;
221                 case RFCOMM_EVENT_CHANNEL_CLOSED:
222                     goep_client_handle_connection_close(context);
223                     break;
224                 default:
225                     break;
226             }
227             break;
228         case L2CAP_DATA_PACKET:
229         case RFCOMM_DATA_PACKET:
230             context->client_handler(GOEP_DATA_PACKET, context->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 * context){
238     if (context->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER){
239         if (context->mas_info.instance_id == context->map_mas_instance_id){
240             // Requested MAS Instance found, accept info
241             log_info("MAS Instance #%u found", context->map_mas_instance_id);
242             context->rfcomm_port = context->mas_info.rfcomm_port;
243             context->profile_supported_features = context->mas_info.supported_features;
244             context->map_supported_message_types = context->mas_info.supported_message_types;
245 #ifdef ENABLE_GOEP_L2CAP
246             context->l2cap_psm = context->mas_info.l2cap_psm;
247 #endif
248         }
249     }
250 }
251 
252 static void goep_client_handle_sdp_query_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
253     goep_client_t * context = goep_client;
254 
255     UNUSED(packet_type);
256     UNUSED(channel);
257     UNUSED(size);
258 
259     des_iterator_t des_list_it;
260     des_iterator_t prot_it;
261     uint8_t status;
262     uint16_t record_index;
263 
264     switch (hci_event_packet_get_type(packet)){
265         case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
266 
267             // detect new record
268             record_index = sdp_event_query_attribute_byte_get_record_id(packet);
269             if (record_index != context->record_index){
270                 context->record_index = record_index;
271                 goep_client_handle_sdp_query_end_of_record(context);
272                 memset(&context->mas_info, 0, sizeof(context->mas_info));
273             }
274 
275             // check if relevant attribute
276             switch(sdp_event_query_attribute_byte_get_attribute_id(packet)){
277                 case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
278                 case BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES:
279                 case BLUETOOTH_ATTRIBUTE_MAS_INSTANCE_ID:
280                 case BLUETOOTH_ATTRIBUTE_SUPPORTED_MESSAGE_TYPES:
281 #ifdef ENABLE_GOEP_L2CAP
282                 case BLUETOOTH_ATTRIBUTE_GOEP_L2CAP_PSM:
283 #endif
284                     break;
285                 default:
286                     return;
287             }
288 
289             // warn if attribute too large to fit in our buffer
290             if (sdp_event_query_attribute_byte_get_attribute_length(packet) > goep_client_sdp_query_attribute_value_buffer_size) {
291                 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));
292                 break;
293             }
294 
295             // store single byte
296             goep_client_sdp_query_attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet);
297 
298             // wait until value fully received
299             if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) != sdp_event_query_attribute_byte_get_attribute_length(packet)) break;
300 
301             // process attributes
302             switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) {
303                 case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
304                     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)) {
305                         uint8_t       *des_element;
306                         uint8_t       *element;
307                         uint32_t       uuid;
308 #ifdef ENABLE_GOEP_L2CAP
309                         uint16_t       l2cap_psm;
310 #endif
311 
312                         if (des_iterator_get_type(&des_list_it) != DE_DES) continue;
313 
314                         des_element = des_iterator_get_element(&des_list_it);
315                         des_iterator_init(&prot_it, des_element);
316 
317                         // first element is UUID
318                         element = des_iterator_get_element(&prot_it);
319                         if (de_get_element_type(element) != DE_UUID) continue;
320 
321                         uuid = de_get_uuid32(element);
322                         des_iterator_next(&prot_it);
323                         if (!des_iterator_has_more(&prot_it)) continue;
324 
325                         // second element is RFCOMM server channel or L2CAP PSM
326                         element = des_iterator_get_element(&prot_it);
327                         switch (uuid){
328 #ifdef ENABLE_GOEP_L2CAP
329                             case BLUETOOTH_PROTOCOL_L2CAP:
330                                 if (de_element_get_uint16(element, &l2cap_psm)){
331                                     context->l2cap_psm = l2cap_psm;
332                                 }
333                                 break;
334 #endif
335                             case BLUETOOTH_PROTOCOL_RFCOMM:
336                                 if (context->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER) {
337                                     context->mas_info.rfcomm_port = element[de_get_header_size(element)];
338                                 } else {
339                                     context->rfcomm_port = element[de_get_header_size(element)];
340                                 }
341                                 break;
342                             default:
343                                 break;
344                         }
345                     }
346                     break;
347 #ifdef ENABLE_GOEP_L2CAP
348                 case BLUETOOTH_ATTRIBUTE_GOEP_L2CAP_PSM:
349                     if (context->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER){
350                         de_element_get_uint16(goep_client_sdp_query_attribute_value, &context->mas_info.l2cap_psm);
351                     } else {
352                         de_element_get_uint16(goep_client_sdp_query_attribute_value, &context->l2cap_psm);
353                     }
354                     break;
355 #endif
356                 // BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES == BLUETOOTH_ATTRIBUTE_MAP_SUPPORTED_FEATURES == 0x0317
357                 case BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES:
358                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
359                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_32) break;
360                     if (context->uuid == BLUETOOTH_SERVICE_CLASS_MESSAGE_ACCESS_SERVER) {
361                         context->mas_info.supported_features  = big_endian_read_32(goep_client_sdp_query_attribute_value, de_get_header_size(goep_client_sdp_query_attribute_value));
362                     } else {
363                         context->profile_supported_features  = big_endian_read_32(goep_client_sdp_query_attribute_value, de_get_header_size(goep_client_sdp_query_attribute_value));
364                     }
365                     break;
366 
367                 case BLUETOOTH_ATTRIBUTE_MAS_INSTANCE_ID:
368                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
369                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_8) break;
370                     context->mas_info.instance_id = goep_client_sdp_query_attribute_value[de_get_header_size(goep_client_sdp_query_attribute_value)];
371                     break;
372 
373                 case BLUETOOTH_ATTRIBUTE_SUPPORTED_MESSAGE_TYPES:
374                     if (de_get_element_type(goep_client_sdp_query_attribute_value) != DE_UINT) break;
375                     if (de_get_size_type(goep_client_sdp_query_attribute_value) != DE_SIZE_8) break;
376                     context->mas_info.supported_message_types = goep_client_sdp_query_attribute_value[de_get_header_size(goep_client_sdp_query_attribute_value)];
377                     break;
378 
379                 default:
380                     break;
381             }
382             break;
383 
384         case SDP_EVENT_QUERY_COMPLETE:
385             goep_client_handle_sdp_query_end_of_record(context);
386             status = sdp_event_query_complete_get_status(packet);
387             if (status != ERROR_CODE_SUCCESS){
388                 log_info("GOEP client, SDP query failed 0x%02x", status);
389                 context->state = GOEP_INIT;
390                 goep_client_emit_connected_event(goep_client, status);
391                 break;
392             }
393             if ((context->rfcomm_port == 0) && (context->l2cap_psm == 0)){
394                 log_info("No GOEP RFCOMM or L2CAP server found");
395                 context->state = GOEP_INIT;
396                 goep_client_emit_connected_event(goep_client, SDP_SERVICE_NOT_FOUND);
397                 break;
398             }
399 #ifdef ENABLE_GOEP_L2CAP
400             if (context->l2cap_psm){
401                 log_info("Remote GOEP L2CAP PSM: %u", context->l2cap_psm);
402                 l2cap_ertm_create_channel(&goep_client_packet_handler, context->bd_addr, context->l2cap_psm,
403                                           &ertm_config, ertm_buffer, sizeof(ertm_buffer), &context->bearer_cid);
404                 return;
405             }
406 #endif
407             log_info("Remote GOEP RFCOMM Server Channel: %u", context->rfcomm_port);
408             rfcomm_create_channel(&goep_client_packet_handler, context->bd_addr, context->rfcomm_port, &context->bearer_cid);
409             break;
410 
411         default:
412             break;
413     }
414 }
415 
416 static uint8_t * goep_client_get_outgoing_buffer(goep_client_t * context){
417     if (context->l2cap_psm){
418         return goep_packet_buffer;
419     } else {
420         return rfcomm_get_outgoing_buffer();
421     }
422 }
423 
424 static uint16_t goep_client_get_outgoing_buffer_len(goep_client_t * context){
425     if (context->l2cap_psm){
426         return sizeof(goep_packet_buffer);
427     } else {
428         return rfcomm_get_max_frame_size(context->bearer_cid);
429     }
430 }
431 
432 static void goep_client_packet_init(uint16_t goep_cid, uint8_t opcode){
433     UNUSED(goep_cid);
434     goep_client_t * context = goep_client;
435     if (context->l2cap_psm){
436     } else {
437         rfcomm_reserve_packet_buffer();
438     }
439     // store opcode for parsing of response
440     context->obex_opcode = opcode;
441 }
442 
443 void goep_client_init(void){
444     memset(goep_client, 0, sizeof(goep_client_t));
445     goep_client->state = GOEP_INIT;
446     goep_client->cid = 1;
447     goep_client->obex_connection_id = OBEX_CONNECTION_ID_INVALID;
448 }
449 
450 void goep_client_deinit(void){
451     memset(goep_client, 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 uint8_t goep_client_create_connection(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t uuid, uint16_t * out_cid){
457     goep_client_t * context = goep_client;
458     if (context->state != GOEP_INIT) return BTSTACK_MEMORY_ALLOC_FAILED;
459     memset(context, 0, sizeof(goep_client_t));
460     context->client_handler = handler;
461     context->state = GOEP_W4_SDP;
462     context->uuid = uuid;
463     (void)memcpy(context->bd_addr, addr, 6);
464     context->profile_supported_features = PROFILE_FEATURES_NOT_PRESENT;
465     sdp_client_query_uuid16(&goep_client_handle_sdp_query_event, context->bd_addr, uuid);
466     *out_cid = context->cid;
467     return ERROR_CODE_SUCCESS;
468 }
469 
470 uint32_t goep_client_get_pbap_supported_features(uint16_t goep_cid){
471     UNUSED(goep_cid);
472     goep_client_t * context = goep_client;
473     return context->profile_supported_features;
474 }
475 
476 uint32_t goep_client_get_map_supported_features(uint16_t goep_cid){
477     UNUSED(goep_cid);
478     goep_client_t * context = goep_client;
479     return context->profile_supported_features;
480 }
481 
482 uint8_t goep_client_get_map_mas_instance_id(uint16_t goep_cid){
483     UNUSED(goep_cid);
484     goep_client_t * context = goep_client;
485     return context->map_mas_instance_id;
486 }
487 
488 uint8_t goep_client_get_map_suported_message_types(uint16_t goep_cid){
489     UNUSED(goep_cid);
490     goep_client_t * context = goep_client;
491     return context->map_supported_message_types;
492 }
493 
494 
495 bool goep_client_version_20_or_higher(uint16_t goep_cid){
496     UNUSED(goep_cid);
497     goep_client_t * context = goep_client;
498     return context->l2cap_psm != 0;
499 }
500 
501 void goep_client_request_can_send_now(uint16_t goep_cid){
502     UNUSED(goep_cid);
503     goep_client_t * context = goep_client;
504     if (context->l2cap_psm){
505         l2cap_request_can_send_now_event(context->bearer_cid);
506     } else {
507         rfcomm_request_can_send_now_event(context->bearer_cid);
508     }
509 }
510 
511 uint8_t goep_client_disconnect(uint16_t goep_cid){
512     UNUSED(goep_cid);
513     goep_client_t * context = goep_client;
514     if (context->l2cap_psm){
515         l2cap_disconnect(context->bearer_cid);
516     } else {
517         rfcomm_disconnect(context->bearer_cid);
518     }
519     return ERROR_CODE_SUCCESS;
520 }
521 
522 void goep_client_set_connection_id(uint16_t goep_cid, uint32_t connection_id){
523     UNUSED(goep_cid);
524     goep_client_t * context = goep_client;
525     context->obex_connection_id = connection_id;
526 }
527 
528 uint8_t goep_client_get_request_opcode(uint16_t goep_cid){
529     UNUSED(goep_cid);
530     goep_client_t * context = goep_client;
531     return context->obex_opcode;
532 }
533 
534 void goep_client_request_create_connect(uint16_t goep_cid, uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){
535     UNUSED(goep_cid);
536     goep_client_t * context = goep_client;
537     goep_client_packet_init(goep_cid, OBEX_OPCODE_CONNECT);
538 
539     // workaround: limit OBEX packet len to L2CAP/RFCOMM MTU to avoid handling of fragemented packets
540     maximum_obex_packet_length = btstack_min(maximum_obex_packet_length, context->bearer_mtu);
541 
542     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
543     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
544     obex_message_builder_request_create_connect(buffer, buffer_len, obex_version_number, flags, maximum_obex_packet_length);
545 }
546 
547 void goep_client_request_create_get(uint16_t goep_cid){
548     UNUSED(goep_cid);
549     goep_client_t * context = goep_client;
550     goep_client_packet_init(goep_cid, OBEX_OPCODE_GET | OBEX_OPCODE_FINAL_BIT_MASK);
551 
552     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
553     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
554     obex_message_builder_request_create_get(buffer, buffer_len, context->obex_connection_id);
555 }
556 
557 void goep_client_request_create_put(uint16_t goep_cid){
558     UNUSED(goep_cid);
559     goep_client_t * context = goep_client;
560     goep_client_packet_init(goep_cid, OBEX_OPCODE_PUT | OBEX_OPCODE_FINAL_BIT_MASK);
561 
562     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
563     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
564     obex_message_builder_request_create_put(buffer, buffer_len, context->obex_connection_id);
565 }
566 
567 void goep_client_request_create_set_path(uint16_t goep_cid, uint8_t flags){
568     UNUSED(goep_cid);
569     goep_client_t * context = goep_client;
570     goep_client_packet_init(goep_cid, OBEX_OPCODE_SETPATH);
571 
572     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
573     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
574     obex_message_builder_request_create_set_path(buffer, buffer_len, flags, context->obex_connection_id);
575 }
576 
577 void goep_client_request_create_abort(uint16_t goep_cid){
578     UNUSED(goep_cid);
579     goep_client_t * context = goep_client;
580     goep_client_packet_init(goep_cid, OBEX_OPCODE_ABORT);
581 
582     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
583     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
584     obex_message_builder_request_create_abort(buffer, buffer_len, context->obex_connection_id);
585 }
586 
587 void goep_client_request_create_disconnect(uint16_t goep_cid){
588     UNUSED(goep_cid);
589     goep_client_t * context = goep_client;
590     goep_client_packet_init(goep_cid, OBEX_OPCODE_DISCONNECT);
591 
592     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
593     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
594     obex_message_builder_request_create_disconnect(buffer, buffer_len, context->obex_connection_id);
595 }
596 
597 void goep_client_header_add_byte(uint16_t goep_cid, uint8_t header_type, uint8_t value){
598     UNUSED(goep_cid);
599     goep_client_t * context = goep_client;
600 
601     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
602     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
603     obex_message_builder_header_add_byte(buffer, buffer_len, header_type, value);
604 }
605 
606 void goep_client_header_add_word(uint16_t goep_cid, uint8_t header_type, uint32_t value){
607     UNUSED(goep_cid);
608     goep_client_t * context = goep_client;
609 
610     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
611     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
612     obex_message_builder_header_add_word(buffer, buffer_len, header_type, value);
613 }
614 
615 void goep_client_header_add_variable(uint16_t goep_cid, uint8_t header_type, const uint8_t * header_data, uint16_t header_data_length){
616     UNUSED(goep_cid);
617     goep_client_t * context = goep_client;
618 
619     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
620     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
621     obex_message_builder_header_add_variable(buffer, buffer_len, header_type, header_data, header_data_length);
622 }
623 
624 void goep_client_header_add_srm_enable(uint16_t goep_cid){
625     UNUSED(goep_cid);
626     goep_client_t * context = goep_client;
627 
628     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
629     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
630     obex_message_builder_header_add_srm_enable(buffer, buffer_len);
631 }
632 
633 void goep_client_header_add_target(uint16_t goep_cid, const uint8_t * target, uint16_t length){
634     UNUSED(goep_cid);
635     goep_client_t * context = goep_client;
636 
637     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
638     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
639     obex_message_builder_header_add_target(buffer, buffer_len, target, length);
640 }
641 
642 void goep_client_header_add_application_parameters(uint16_t goep_cid, const uint8_t * data, uint16_t length){
643     UNUSED(goep_cid);
644     goep_client_t * context = goep_client;
645 
646     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
647     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
648     obex_message_builder_header_add_application_parameters(buffer, buffer_len, data, length);
649 }
650 
651 void goep_client_header_add_challenge_response(uint16_t goep_cid, const uint8_t * data, uint16_t length){
652     UNUSED(goep_cid);
653     goep_client_t * context = goep_client;
654 
655     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
656     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
657     obex_message_builder_header_add_challenge_response(buffer, buffer_len, data, length);
658 }
659 
660 void goep_client_body_add_static(uint16_t goep_cid, const uint8_t * data, uint32_t length){
661     UNUSED(goep_cid);
662     goep_client_t * context = goep_client;
663 
664     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
665     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
666     obex_message_builder_body_add_static(buffer, buffer_len, data, length);
667 }
668 
669 uint16_t goep_client_body_get_outgoing_buffer_len(uint16_t goep_cid) {
670     UNUSED(goep_cid);
671     goep_client_t * context = goep_client;
672 
673     return goep_client_get_outgoing_buffer_len(context);
674 };
675 
676 void goep_client_body_fillup_static(uint16_t goep_cid, const uint8_t * data, uint32_t length, uint32_t * ret_length){
677     UNUSED(goep_cid);
678     goep_client_t * context = goep_client;
679 
680     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
681     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
682     obex_message_builder_body_fillup_static(buffer, buffer_len, data, length, ret_length);
683 }
684 
685 void goep_client_header_add_name(uint16_t goep_cid, const char * name){
686     UNUSED(goep_cid);
687     goep_client_t * context = goep_client;
688 
689     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
690     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
691     obex_message_builder_header_add_name(buffer, buffer_len, name);
692 }
693 
694 void goep_client_header_add_name_prefix(uint16_t goep_cid, const char * name, uint16_t name_len){
695     UNUSED(goep_cid);
696     goep_client_t * context = goep_client;
697 
698     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
699     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
700     obex_message_builder_header_add_name_prefix(buffer, buffer_len, name, name_len);
701 }
702 
703 void goep_client_header_add_type(uint16_t goep_cid, const char * type){
704     UNUSED(goep_cid);
705     goep_client_t * context = goep_client;
706 
707     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
708     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
709     obex_message_builder_header_add_type(buffer, buffer_len, type);
710 }
711 
712 void goep_client_header_add_length(uint16_t goep_cid, uint32_t length){
713     UNUSED(goep_cid);
714     goep_client_t * context = goep_client;
715 
716     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
717     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
718     obex_message_builder_header_add_length(buffer, buffer_len, length);
719 }
720 
721 uint16_t goep_client_request_get_max_body_size(uint16_t goep_cid){
722     UNUSED(goep_cid);
723     goep_client_t * context = goep_client;
724 
725     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
726     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
727     uint16_t pos = big_endian_read_16(buffer, 1);
728     return buffer_len - pos;
729 }
730 
731 int goep_client_execute(uint16_t goep_cid){
732     return goep_client_execute_with_final_bit (goep_cid, true);
733 }
734 
735 int goep_client_execute_with_final_bit(uint16_t goep_cid, bool final){
736     UNUSED(goep_cid);
737     goep_client_t * context = goep_client;
738     uint8_t * buffer = goep_client_get_outgoing_buffer(context);
739     uint16_t buffer_len = goep_client_get_outgoing_buffer_len(context);
740 
741     obex_message_builder_set_final_bit (buffer, buffer_len, final);
742 
743     uint16_t pos = big_endian_read_16(buffer, 1);
744     if (context->l2cap_psm){
745         return l2cap_send(context->bearer_cid, buffer, pos);
746     } else {
747         return rfcomm_send_prepared(context->bearer_cid, pos);
748     }
749 }
750 
751