pbap_client.c (7dad9ff837d8c18e8c723011bb5dcf58a785baaa) | pbap_client.c (3a7447a300fd649fa06763a9ebdb11596972740a) |
---|---|
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 --- 79 unchanged lines hidden (view full) --- 88typedef enum { 89 PBAP_INIT = 0, 90 PBAP_W4_GOEP_CONNECTION, 91 PBAP_W2_SEND_CONNECT_REQUEST, 92 PBAP_W4_CONNECT_RESPONSE, 93 PBAP_CONNECT_RESPONSE_RECEIVED, 94 PBAP_CONNECTED, 95 // | 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 --- 79 unchanged lines hidden (view full) --- 88typedef enum { 89 PBAP_INIT = 0, 90 PBAP_W4_GOEP_CONNECTION, 91 PBAP_W2_SEND_CONNECT_REQUEST, 92 PBAP_W4_CONNECT_RESPONSE, 93 PBAP_CONNECT_RESPONSE_RECEIVED, 94 PBAP_CONNECTED, 95 // |
96 PBAP_W2_PULL_PHONE_BOOK, 97 PBAP_W4_PHONE_BOOK, | 96 PBAP_W2_PULL_PHONEBOOK, 97 PBAP_W4_PHONEBOOK, |
98 PBAP_W2_SET_PATH_ROOT, 99 PBAP_W4_SET_PATH_ROOT_COMPLETE, 100 PBAP_W2_SET_PATH_ELEMENT, 101 PBAP_W4_SET_PATH_ELEMENT_COMPLETE, | 98 PBAP_W2_SET_PATH_ROOT, 99 PBAP_W4_SET_PATH_ROOT_COMPLETE, 100 PBAP_W2_SET_PATH_ELEMENT, 101 PBAP_W4_SET_PATH_ELEMENT_COMPLETE, |
102 PBAP_W2_GET_PHONEBOOK_SIZE, 103 PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE, |
|
102} pbap_state_t; 103 104typedef struct pbap_client { 105 pbap_state_t state; 106 uint16_t cid; 107 bd_addr_t bd_addr; 108 hci_con_handle_t con_handle; 109 uint8_t incoming; 110 uint16_t goep_cid; 111 btstack_packet_handler_t client_handler; 112 const char * current_folder; 113 uint16_t set_path_offset; 114} pbap_client_t; 115 116static pbap_client_t _pbap_client; 117static pbap_client_t * pbap_client = &_pbap_client; 118 | 104} pbap_state_t; 105 106typedef struct pbap_client { 107 pbap_state_t state; 108 uint16_t cid; 109 bd_addr_t bd_addr; 110 hci_con_handle_t con_handle; 111 uint8_t incoming; 112 uint16_t goep_cid; 113 btstack_packet_handler_t client_handler; 114 const char * current_folder; 115 uint16_t set_path_offset; 116} pbap_client_t; 117 118static pbap_client_t _pbap_client; 119static pbap_client_t * pbap_client = &_pbap_client; 120 |
119static inline void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){ | 121static void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){ |
120 uint8_t event[15]; 121 int pos = 0; 122 event[pos++] = HCI_EVENT_PBAP_META; 123 pos++; // skip len 124 event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED; 125 little_endian_store_16(event,pos,context->cid); 126 pos+=2; 127 event[pos++] = status; 128 memcpy(&event[pos], context->bd_addr, 6); 129 pos += 6; 130 little_endian_store_16(event,pos,context->con_handle); 131 pos += 2; 132 event[pos++] = context->incoming; 133 event[1] = pos - 2; 134 if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos); 135 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 136} 137 | 122 uint8_t event[15]; 123 int pos = 0; 124 event[pos++] = HCI_EVENT_PBAP_META; 125 pos++; // skip len 126 event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED; 127 little_endian_store_16(event,pos,context->cid); 128 pos+=2; 129 event[pos++] = status; 130 memcpy(&event[pos], context->bd_addr, 6); 131 pos += 6; 132 little_endian_store_16(event,pos,context->con_handle); 133 pos += 2; 134 event[pos++] = context->incoming; 135 event[1] = pos - 2; 136 if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos); 137 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 138} 139 |
138static inline void pbap_client_emit_connection_closed_event(pbap_client_t * context){ | 140static void pbap_client_emit_connection_closed_event(pbap_client_t * context){ |
139 uint8_t event[5]; 140 int pos = 0; 141 event[pos++] = HCI_EVENT_PBAP_META; 142 pos++; // skip len 143 event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED; 144 little_endian_store_16(event,pos,context->cid); 145 pos+=2; 146 event[1] = pos - 2; 147 if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos); 148 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 149} 150 | 141 uint8_t event[5]; 142 int pos = 0; 143 event[pos++] = HCI_EVENT_PBAP_META; 144 pos++; // skip len 145 event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED; 146 little_endian_store_16(event,pos,context->cid); 147 pos+=2; 148 event[1] = pos - 2; 149 if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos); 150 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 151} 152 |
151static inline void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){ | 153static void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){ |
152 uint8_t event[6]; 153 int pos = 0; 154 event[pos++] = HCI_EVENT_PBAP_META; 155 pos++; // skip len 156 event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED; 157 little_endian_store_16(event,pos,context->cid); 158 pos+=2; 159 event[pos++]= status; 160 event[1] = pos - 2; 161 if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos); 162 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 163} 164 | 154 uint8_t event[6]; 155 int pos = 0; 156 event[pos++] = HCI_EVENT_PBAP_META; 157 pos++; // skip len 158 event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED; 159 little_endian_store_16(event,pos,context->cid); 160 pos+=2; 161 event[pos++]= status; 162 event[1] = pos - 2; 163 if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos); 164 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 165} 166 |
167static void pbap_client_emit_phonebook_size_event(pbap_client_t * context, uint8_t status, uint16_t phonebook_size){ 168 uint8_t event[8]; 169 int pos = 0; 170 event[pos++] = HCI_EVENT_PBAP_META; 171 pos++; // skip len 172 event[pos++] = PBAP_SUBEVENT_PHONEBOOK_SIZE; 173 little_endian_store_16(event,pos,context->cid); 174 pos+=2; 175 event[pos++] = status; 176 little_endian_store_16(event,pos, phonebook_size); 177 pos+=2; 178 event[1] = pos - 2; 179 if (pos != sizeof(event)) log_error("pbap_client_emit_phonebook_size_event size %u", pos); 180 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 181} 182 |
|
165static void pbap_handle_can_send_now(void){ 166 uint8_t path_element[20]; 167 uint16_t path_element_start; 168 uint16_t path_element_len; | 183static void pbap_handle_can_send_now(void){ 184 uint8_t path_element[20]; 185 uint16_t path_element_start; 186 uint16_t path_element_len; |
187 uint8_t application_parameters[20]; |
|
169 170 switch (pbap_client->state){ 171 case PBAP_W2_SEND_CONNECT_REQUEST: 172 goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT); 173 goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid); 174 // state 175 pbap_client->state = PBAP_W4_CONNECT_RESPONSE; 176 // send packet 177 goep_client_execute(pbap_client->goep_cid); 178 return; | 188 189 switch (pbap_client->state){ 190 case PBAP_W2_SEND_CONNECT_REQUEST: 191 goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT); 192 goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid); 193 // state 194 pbap_client->state = PBAP_W4_CONNECT_RESPONSE; 195 // send packet 196 goep_client_execute(pbap_client->goep_cid); 197 return; |
179 case PBAP_W2_PULL_PHONE_BOOK: | 198 case PBAP_W2_PULL_PHONEBOOK: 199 case PBAP_W2_GET_PHONEBOOK_SIZE: |
180 goep_client_create_get_request(pbap_client->goep_cid); 181 goep_client_add_header_type(pbap_client->goep_cid, pbap_type); 182 goep_client_add_header_name(pbap_client->goep_cid, pbap_name); | 200 goep_client_create_get_request(pbap_client->goep_cid); 201 goep_client_add_header_type(pbap_client->goep_cid, pbap_type); 202 goep_client_add_header_name(pbap_client->goep_cid, pbap_name); |
183 // state 184 pbap_client->state = PBAP_W4_PHONE_BOOK; | 203 if (pbap_client->state == PBAP_W2_GET_PHONEBOOK_SIZE){ 204 // Regular TLV wih 1-byte len 205 application_parameters[0] = PBAP_APPLICATION_PARAMETER_MAX_LIST_COUNT; 206 application_parameters[1] = 2; 207 big_endian_store_16(application_parameters, 2, 0); 208 goep_client_add_header_application_parameters(pbap_client->goep_cid, 4, &application_parameters[0]); 209 // state 210 pbap_client->state = PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE; 211 } else { 212 // state 213 pbap_client->state = PBAP_W4_PHONEBOOK; 214 } |
185 // send packet 186 goep_client_execute(pbap_client->goep_cid); 187 break; 188 case PBAP_W2_SET_PATH_ROOT: 189 goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory 190 // On Android 4.2 Cyanogenmod, using "" as path fails 191 // goep_client_add_header_name(pbap_client->goep_cid, ""); // empty == / 192 // state --- 106 unchanged lines hidden (view full) --- 299 } else if (packet[0] == OBEX_RESP_NOT_FOUND){ 300 pbap_client->state = PBAP_CONNECTED; 301 pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_FOUND); 302 } else { 303 pbap_client->state = PBAP_CONNECTED; 304 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 305 } 306 break; | 215 // send packet 216 goep_client_execute(pbap_client->goep_cid); 217 break; 218 case PBAP_W2_SET_PATH_ROOT: 219 goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory 220 // On Android 4.2 Cyanogenmod, using "" as path fails 221 // goep_client_add_header_name(pbap_client->goep_cid, ""); // empty == / 222 // state --- 106 unchanged lines hidden (view full) --- 329 } else if (packet[0] == OBEX_RESP_NOT_FOUND){ 330 pbap_client->state = PBAP_CONNECTED; 331 pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_FOUND); 332 } else { 333 pbap_client->state = PBAP_CONNECTED; 334 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 335 } 336 break; |
307 case PBAP_W4_PHONE_BOOK: | 337 case PBAP_W4_PHONEBOOK: |
308 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){ 309 uint8_t hi = obex_iterator_get_hi(&it); 310 if (hi == OBEX_HEADER_BODY || hi == OBEX_HEADER_END_OF_BODY){ 311 uint16_t data_len = obex_iterator_get_data_len(&it); 312 const uint8_t * data = obex_iterator_get_data(&it); 313 pbap_client->client_handler(PBAP_DATA_PACKET, pbap_client->cid, (uint8_t *) data, data_len); 314 } 315 } 316 if (packet[0] == OBEX_RESP_CONTINUE){ | 338 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){ 339 uint8_t hi = obex_iterator_get_hi(&it); 340 if (hi == OBEX_HEADER_BODY || hi == OBEX_HEADER_END_OF_BODY){ 341 uint16_t data_len = obex_iterator_get_data_len(&it); 342 const uint8_t * data = obex_iterator_get_data(&it); 343 pbap_client->client_handler(PBAP_DATA_PACKET, pbap_client->cid, (uint8_t *) data, data_len); 344 } 345 } 346 if (packet[0] == OBEX_RESP_CONTINUE){ |
317 pbap_client->state = PBAP_W2_PULL_PHONE_BOOK; | 347 pbap_client->state = PBAP_W2_PULL_PHONEBOOK; |
318 goep_client_request_can_send_now(pbap_client->goep_cid); 319 } else if (packet[0] == OBEX_RESP_SUCCESS){ 320 pbap_client->state = PBAP_CONNECTED; 321 pbap_client_emit_operation_complete_event(pbap_client, 0); 322 } else { 323 pbap_client->state = PBAP_CONNECTED; 324 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 325 } 326 break; | 348 goep_client_request_can_send_now(pbap_client->goep_cid); 349 } else if (packet[0] == OBEX_RESP_SUCCESS){ 350 pbap_client->state = PBAP_CONNECTED; 351 pbap_client_emit_operation_complete_event(pbap_client, 0); 352 } else { 353 pbap_client->state = PBAP_CONNECTED; 354 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 355 } 356 break; |
357 case PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE: 358 pbap_client->state = PBAP_CONNECTED; 359 if (packet[0] == OBEX_RESP_SUCCESS){ 360 int have_size = 0; 361 uint16_t phonebook_size; 362 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){ 363 uint8_t hi = obex_iterator_get_hi(&it); 364 if (hi == OBEX_HEADER_APPLICATION_PARAMETERS){ 365 uint16_t data_len = obex_iterator_get_data_len(&it); 366 const uint8_t * data = obex_iterator_get_data(&it); 367 // iterate over application headers (TLV with 1 bytes len) 368 unsigned int i = 0; 369 while (i<data_len){ 370 uint8_t tag = data[i++]; 371 uint8_t len = data[i++]; 372 if (tag == PBAP_APPLICATION_PARAMETER_PHONEBOOK_SIZE && len == 2){ 373 have_size = 1; 374 phonebook_size = big_endian_read_16(data, 0); 375 } 376 i+=len; 377 } 378 } 379 } 380 if (have_size){ 381 pbap_client_emit_phonebook_size_event(pbap_client, 0, phonebook_size); 382 break; 383 } 384 } 385 pbap_client_emit_phonebook_size_event(pbap_client, OBEX_UNKNOWN_ERROR, 0); 386 break; |
|
327 default: 328 break; 329 } 330 break; 331 default: 332 break; 333 } 334} --- 16 unchanged lines hidden (view full) --- 351 352uint8_t pbap_disconnect(uint16_t pbap_cid){ 353 UNUSED(pbap_cid); 354 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 355 goep_client_disconnect(pbap_client->goep_cid); 356 return 0; 357} 358 | 387 default: 388 break; 389 } 390 break; 391 default: 392 break; 393 } 394} --- 16 unchanged lines hidden (view full) --- 411 412uint8_t pbap_disconnect(uint16_t pbap_cid){ 413 UNUSED(pbap_cid); 414 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 415 goep_client_disconnect(pbap_client->goep_cid); 416 return 0; 417} 418 |
419uint8_t pbap_get_phonebook_size(uint16_t pbap_cid){ 420 UNUSED(pbap_cid); 421 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 422 pbap_client->state = PBAP_W2_GET_PHONEBOOK_SIZE; 423 goep_client_request_can_send_now(pbap_client->goep_cid); 424 return 0; 425} 426 |
|
359uint8_t pbap_pull_phonebook(uint16_t pbap_cid){ 360 UNUSED(pbap_cid); 361 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; | 427uint8_t pbap_pull_phonebook(uint16_t pbap_cid){ 428 UNUSED(pbap_cid); 429 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; |
362 pbap_client->state = PBAP_W2_PULL_PHONE_BOOK; 363 goep_client_request_can_send_now(pbap_client->goep_cid); | 430 pbap_client->state = PBAP_W2_PULL_PHONEBOOK; 431 goep_client_request_can_send_now(pbap_client->goep_cid); |
364 return 0; 365} 366 367uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){ 368 UNUSED(pbap_cid); 369 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 370 pbap_client->state = PBAP_W2_SET_PATH_ROOT; 371 pbap_client->current_folder = path; 372 pbap_client->set_path_offset = 0; | 432 return 0; 433} 434 435uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){ 436 UNUSED(pbap_cid); 437 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 438 pbap_client->state = PBAP_W2_SET_PATH_ROOT; 439 pbap_client->current_folder = path; 440 pbap_client->set_path_offset = 0; |
373 goep_client_request_can_send_now(pbap_client->goep_cid); | 441 goep_client_request_can_send_now(pbap_client->goep_cid); |
374 return 0; 375} | 442 return 0; 443} |