Merge branch 'pbap' into develop

Add Phone Book Access Client (PBAP) over General Object Exchange (GOEP)
This commit is contained in:
Matthias Ringwald 2017-03-23 22:28:29 +01:00
commit bdd624350d
13 changed files with 1821 additions and 11 deletions

View File

@ -112,6 +112,7 @@ EXAMPLES = \
hsp_hs_demo \ hsp_hs_demo \
sm_pairing_peripheral \ sm_pairing_peripheral \
sm_pairing_central \ sm_pairing_central \
pbap_client \
EXAMPLES_USING_LE = \ EXAMPLES_USING_LE = \
ancs_client_demo \ ancs_client_demo \
@ -158,6 +159,9 @@ sm_pairing_peripheral.h: sm_pairing_peripheral.gatt
sdp_rfcomm_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${PAN_OBJ} ${SDP_CLIENT} sdp_rfcomm_query.c sdp_rfcomm_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${PAN_OBJ} ${SDP_CLIENT} sdp_rfcomm_query.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
pbap_client: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} obex_iterator.c goep_client.c pbap_client.c pbap_client_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
sdp_general_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} sdp_general_query.c sdp_general_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} sdp_general_query.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@

246
example/pbap_client_demo.c Normal file
View File

@ -0,0 +1,246 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#include "btstack_config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "btstack_run_loop.h"
#include "l2cap.h"
#include "rfcomm.h"
#include "btstack_event.h"
#include "classic/goep_client.h"
#include "classic/pbap_client.h"
#ifdef HAVE_POSIX_STDIN
#include <unistd.h>
#include "stdin_support.h"
#endif
#ifndef HAVE_POSIX_STDIN
static int step = 0;
#endif
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static bd_addr_t remote_addr;
// MBP2016 "F4-0F-24-3B-1B-E1"
// Nexus 7 "30-85-A9-54-2E-78"
static const char * remote_addr_string = "30-85-A9-54-2E-78";
static btstack_packet_callback_registration_t hci_event_callback_registration;
static uint16_t pbap_cid;
#ifdef HAVE_POSIX_STDIN
// Testig User Interface
static void show_usage(void){
bd_addr_t iut_address;
gap_local_bd_addr(iut_address);
printf("\n--- Bluetooth PBAP Client (HF) Test Console %s ---\n", bd_addr_to_str(iut_address));
printf("\n");
printf("a - establish PBAP connection to %s\n", bd_addr_to_str(remote_addr));
printf("b - set phonebook '/telecom/pb'\n");
printf("c - set phonebook '/SIM1/telecom/pb'\n");
printf("d - pull phonebook\n");
printf("e - disconnnect\n");
printf("---\n");
printf("Ctrl-c - exit\n");
printf("---\n");
}
static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){
UNUSED(ds);
UNUSED(callback_type);
char cmd = btstack_stdin_read();
switch (cmd){
case 'a':
printf("[+] Connecting to %s...\n", bd_addr_to_str(remote_addr));
pbap_connect(&packet_handler, remote_addr, &pbap_cid);
break;
case 'b':
printf("[+] Set Phonebook 'telecom/pb'\n");
pbap_set_phonebook(pbap_cid, "telecom/pb");
break;
case 'c':
printf("[+] Set Phonebook 'SIM1/telecom/pb'\n");
pbap_set_phonebook(pbap_cid, "SIM1/telecom/pb");
break;
case 'd':
pbap_pull_phonebook(pbap_cid);
break;
case 'e':
pbap_disconnect(pbap_cid);
break;
default:
show_usage();
break;
}
}
// packet handler for interactive console
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
int i;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case BTSTACK_EVENT_STATE:
// BTstack activated, get started
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){
show_usage();
}
break;
case HCI_EVENT_PBAP_META:
switch (hci_event_pbap_meta_get_subevent_code(packet)){
case PBAP_SUBEVENT_CONNECTION_OPENED:
printf("[+] Connected\n");
break;
case PBAP_SUBEVENT_CONNECTION_CLOSED:
printf("[+] Connection closed\n");
break;
case PBAP_SUBEVENT_OPERATION_COMPLETED:
printf("[+] Operation complete\n");
break;
default:
break;
}
break;
default:
break;
}
break;
case PBAP_DATA_PACKET:
for (i=0;i<size;i++){
printf("%c", packet[i]);
}
break;
default:
break;
}
}
#else
// packet handler for emdded system with fixed operation sequence
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
int i;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case BTSTACK_EVENT_STATE:
// BTstack activated, get started
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){
printf("[+] Connect to Phone Book Server on %s\n", bd_addr_to_str(remote_addr));
pbap_connect(&packet_handler, remote_addr, &pbap_cid);
}
break;
case HCI_EVENT_PBAP_META:
switch (hci_event_pbap_meta_get_subevent_code(packet)){
case PBAP_SUBEVENT_CONNECTION_OPENED:
printf("[+] Connected\n");
printf("[+] Pull phonebook\n");
pbap_pull_phonebook(pbap_cid);
break;
case PBAP_SUBEVENT_CONNECTION_CLOSED:
printf("[+] Connection closed\n");
break;
case PBAP_SUBEVENT_OPERATION_COMPLETED:
printf("[+] Operation complete\n");
printf("[+] Pull Phonebook complete\n");
pbap_disconnect(pbap_cid);
break;
default:
break;
}
break;
default:
break;
}
break;
case PBAP_DATA_PACKET:
for (i=0;i<size;i++){
printf("%c", packet[i]);
}
break;
default:
break;
}
}
#endif
int btstack_main(int argc, const char * argv[]);
int btstack_main(int argc, const char * argv[]){
(void)argc;
(void)argv;
sscanf_bd_addr(remote_addr_string, remote_addr);
// register for HCI events
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
// init L2CAP
l2cap_init();
// init RFCOM
rfcomm_init();
// init GOEP Client
goep_client_init();
// init PBAP Client
pbap_client_init();
#ifdef HAVE_POSIX_STDIN
btstack_stdin_setup(stdin_process);
#endif
// turn on!
hci_power_control(HCI_POWER_ON);
return 0;
}

View File

@ -99,7 +99,7 @@ static void sm_peripheral_setup(void){
*/ */
// LE Legacy Pairing, Just Works // LE Legacy Pairing, Just Works
sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_YES_NO); sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
sm_set_authentication_requirements(0); sm_set_authentication_requirements(0);
// LE Legacy Pairing, Passkey entry initiator enter, responder (us) displays // LE Legacy Pairing, Passkey entry initiator enter, responder (us) displays

View File

@ -89,7 +89,7 @@ typedef uint8_t sm_key_t[16];
// Security Manager protocol data // Security Manager protocol data
#define SM_DATA_PACKET 0x09 #define SM_DATA_PACKET 0x09
// SDP query result // SDP query result - only used by daemon
// format: type (8), record_id (16), attribute_id (16), attribute_length (16), attribute_value (max 1k) // format: type (8), record_id (16), attribute_id (16), attribute_length (16), attribute_value (max 1k)
#define SDP_CLIENT_PACKET 0x0a #define SDP_CLIENT_PACKET 0x0a
@ -98,6 +98,12 @@ typedef uint8_t sm_key_t[16];
// Unicast Connectionless Data // Unicast Connectionless Data
#define UCD_DATA_PACKET 0x0c #define UCD_DATA_PACKET 0x0c
// GOEP data
#define GOEP_DATA_PACKET 0x0d
// PBAP data
#define PBAP_DATA_PACKET 0x0e
// debug log messages // debug log messages
#define LOG_MESSAGE_PACKET 0xfc #define LOG_MESSAGE_PACKET 0xfc
@ -159,8 +165,6 @@ typedef uint8_t sm_key_t[16];
#define BNEP_CHANNEL_NOT_CONNECTED 0xA1 #define BNEP_CHANNEL_NOT_CONNECTED 0xA1
#define BNEP_DATA_LEN_EXCEEDS_MTU 0xA2 #define BNEP_DATA_LEN_EXCEEDS_MTU 0xA2
// DAEMON COMMANDS // DAEMON COMMANDS
#define OGF_BTSTACK 0x3d #define OGF_BTSTACK 0x3d
@ -266,7 +270,11 @@ typedef uint8_t sm_key_t[16];
#define GATT_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION 0X81 #define GATT_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION 0X81
#define GATT_GET_MTU 0x82 #define GATT_GET_MTU 0x82
// OBEX ERRORS
#define OBEX_UNKNOWN_ERROR 0x90
#define OBEX_CONNECT_FAILED 0x91
#define OBEX_DISCONNECTED 0x92
#define OBEX_NOT_FOUND 0x93
// EVENTS // EVENTS
@ -925,6 +933,8 @@ typedef uint8_t sm_key_t[16];
#define HCI_EVENT_ANCS_META 0xEA #define HCI_EVENT_ANCS_META 0xEA
#define HCI_EVENT_AVDTP_META 0xEB #define HCI_EVENT_AVDTP_META 0xEB
#define HCI_EVENT_AVRCP_META 0xEC #define HCI_EVENT_AVRCP_META 0xEC
#define HCI_EVENT_GOEP_META 0xED
#define HCI_EVENT_PBAP_META 0xEE
// Potential other meta groups // Potential other meta groups
// #define HCI_EVENT_BNEP_META 0xxx // #define HCI_EVENT_BNEP_META 0xxx
@ -1490,6 +1500,55 @@ typedef uint8_t sm_key_t[16];
*/ */
#define AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE 0x0F #define AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE 0x0F
/**
* @format 121BH1
* @param subevent_code
* @param goep_cid
* @param status
* @param bd_addr
* @param con_handle
* @param incoming
*/
#define GOEP_SUBEVENT_CONNECTION_OPENED 0x01
/**
* @format 12
* @param subevent_code
* @param goep_cid
*/
#define GOEP_SUBEVENT_CONNECTION_CLOSED 0x02
/**
* @format 12
* @param subevent_code
* @param goep_cid
*/
#define GOEP_SUBEVENT_CAN_SEND_NOW 0x03
/**
* @format 121BH1
* @param subevent_code
* @param pbap_cid
* @param status
* @param bd_addr
* @param con_handle
* @param incoming
*/
#define PBAP_SUBEVENT_CONNECTION_OPENED 0x01
/**
* @format 12
* @param subevent_code
* @param goep_cid
*/
#define PBAP_SUBEVENT_CONNECTION_CLOSED 0x02
/**
* @format 121
* @param subevent_code
* @param goep_cid
* @param status
*/
#define PBAP_SUBEVENT_OPERATION_COMPLETED 0x03
#endif #endif

View File

@ -70,11 +70,35 @@ static inline uint8_t hci_event_packet_get_type(const uint8_t * event){
} }
/*** /***
* @brief Get subevent code for hsp event * @brief Get subevent code for ancs event
* @param event packet * @param event packet
* @return subevent_code * @return subevent_code
*/ */
static inline uint8_t hci_event_hsp_meta_get_subevent_code(const uint8_t * event){ static inline uint8_t hci_event_ancs_meta_get_subevent_code(const uint8_t * event){
return event[2];
}
/***
* @brief Get subevent code for avdtp event
* @param event packet
* @return subevent_code
*/
static inline uint8_t hci_event_avdtp_meta_get_subevent_code(const uint8_t * event){
return event[2];
}
/***
* @brief Get subevent code for avrcp event
* @param event packet
* @return subevent_code
*/
static inline uint8_t hci_event_avrcp_meta_get_subevent_code(const uint8_t * event){
return event[2];
}
/***
* @brief Get subevent code for goep event
* @param event packet
* @return subevent_code
*/
static inline uint8_t hci_event_goep_meta_get_subevent_code(const uint8_t * event){
return event[2]; return event[2];
} }
/*** /***
@ -86,11 +110,19 @@ static inline uint8_t hci_event_hfp_meta_get_subevent_code(const uint8_t * event
return event[2]; return event[2];
} }
/*** /***
* @brief Get subevent code for ancs event * @brief Get subevent code for hsp event
* @param event packet * @param event packet
* @return subevent_code * @return subevent_code
*/ */
static inline uint8_t hci_event_ancs_meta_get_subevent_code(const uint8_t * event){ static inline uint8_t hci_event_hsp_meta_get_subevent_code(const uint8_t * event){
return event[2];
}
/***
* @brief Get subevent code for pbap event
* @param event packet
* @return subevent_code
*/
static inline uint8_t hci_event_pbap_meta_get_subevent_code(const uint8_t * event){
return event[2]; return event[2];
} }
/*** /***
@ -4802,6 +4834,147 @@ static inline uint8_t avrcp_subevent_player_application_value_response_get_statu
return event[5]; return event[5];
} }
/**
* @brief Get field goep_cid from event GOEP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @return goep_cid
* @note: btstack_type 2
*/
static inline uint16_t goep_subevent_connection_opened_get_goep_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field status from event GOEP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @return status
* @note: btstack_type 1
*/
static inline uint8_t goep_subevent_connection_opened_get_status(const uint8_t * event){
return event[5];
}
/**
* @brief Get field bd_addr from event GOEP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @param Pointer to storage for bd_addr
* @note: btstack_type B
*/
static inline void goep_subevent_connection_opened_get_bd_addr(const uint8_t * event, bd_addr_t bd_addr){
reverse_bd_addr(&event[6], bd_addr);
}
/**
* @brief Get field con_handle from event GOEP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @return con_handle
* @note: btstack_type H
*/
static inline hci_con_handle_t goep_subevent_connection_opened_get_con_handle(const uint8_t * event){
return little_endian_read_16(event, 12);
}
/**
* @brief Get field incoming from event GOEP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @return incoming
* @note: btstack_type 1
*/
static inline uint8_t goep_subevent_connection_opened_get_incoming(const uint8_t * event){
return event[14];
}
/**
* @brief Get field goep_cid from event GOEP_SUBEVENT_CONNECTION_CLOSED
* @param event packet
* @return goep_cid
* @note: btstack_type 2
*/
static inline uint16_t goep_subevent_connection_closed_get_goep_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field goep_cid from event GOEP_SUBEVENT_CAN_SEND_NOW
* @param event packet
* @return goep_cid
* @note: btstack_type 2
*/
static inline uint16_t goep_subevent_can_send_now_get_goep_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field pbap_cid from event PBAP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @return pbap_cid
* @note: btstack_type 2
*/
static inline uint16_t pbap_subevent_connection_opened_get_pbap_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field status from event PBAP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @return status
* @note: btstack_type 1
*/
static inline uint8_t pbap_subevent_connection_opened_get_status(const uint8_t * event){
return event[5];
}
/**
* @brief Get field bd_addr from event PBAP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @param Pointer to storage for bd_addr
* @note: btstack_type B
*/
static inline void pbap_subevent_connection_opened_get_bd_addr(const uint8_t * event, bd_addr_t bd_addr){
reverse_bd_addr(&event[6], bd_addr);
}
/**
* @brief Get field con_handle from event PBAP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @return con_handle
* @note: btstack_type H
*/
static inline hci_con_handle_t pbap_subevent_connection_opened_get_con_handle(const uint8_t * event){
return little_endian_read_16(event, 12);
}
/**
* @brief Get field incoming from event PBAP_SUBEVENT_CONNECTION_OPENED
* @param event packet
* @return incoming
* @note: btstack_type 1
*/
static inline uint8_t pbap_subevent_connection_opened_get_incoming(const uint8_t * event){
return event[14];
}
/**
* @brief Get field goep_cid from event PBAP_SUBEVENT_CONNECTION_CLOSED
* @param event packet
* @return goep_cid
* @note: btstack_type 2
*/
static inline uint16_t pbap_subevent_connection_closed_get_goep_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field goep_cid from event PBAP_SUBEVENT_OPERATION_COMPLETED
* @param event packet
* @return goep_cid
* @note: btstack_type 2
*/
static inline uint16_t pbap_subevent_operation_completed_get_goep_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field status from event PBAP_SUBEVENT_OPERATION_COMPLETED
* @param event packet
* @return status
* @note: btstack_type 1
*/
static inline uint8_t pbap_subevent_operation_completed_get_status(const uint8_t * event){
return event[5];
}
/* API_END */ /* API_END */

328
src/classic/goep_client.c Normal file
View File

@ -0,0 +1,328 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#include "btstack_config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "btstack_debug.h"
#include "hci_dump.h"
#include "bluetooth_sdp.h"
#include "btstack_event.h"
#include "classic/goep_client.h"
#include "classic/obex.h"
#include "classic/obex_iterator.h"
#include "classic/rfcomm.h"
#include "classic/sdp_client_rfcomm.h"
//------------------------------------------------------------------------------------------------------------
// goep_client.c
//
typedef enum {
GOEP_INIT,
GOEP_W4_SDP,
GOEP_W4_CONNECTION,
GOEP_CONNECTED,
} goep_state_t;
typedef struct {
uint16_t cid;
goep_state_t state;
bd_addr_t bd_addr;
hci_con_handle_t con_handle;
uint8_t incoming;
uint8_t bearer_l2cap;
uint16_t bearer_port; // l2cap: psm, rfcomm: channel nr
uint16_t bearer_cid;
uint16_t bearer_mtu;
uint8_t obex_opcode;
uint32_t obex_connection_id;
int obex_connection_id_set;
btstack_packet_handler_t client_handler;
} goep_client_t;
static goep_client_t _goep_client;
static goep_client_t * goep_client = &_goep_client;
static inline void goep_client_emit_connected_event(goep_client_t * context, uint8_t status){
uint8_t event[22];
int pos = 0;
event[pos++] = HCI_EVENT_GOEP_META;
pos++; // skip len
event[pos++] = GOEP_SUBEVENT_CONNECTION_OPENED;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[pos++] = status;
memcpy(&event[pos], context->bd_addr, 6);
pos += 6;
little_endian_store_16(event,pos,context->con_handle);
pos += 2;
event[pos++] = context->incoming;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos);
context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static inline void goep_client_emit_connection_closed_event(goep_client_t * context){
uint8_t event[5];
int pos = 0;
event[pos++] = HCI_EVENT_GOEP_META;
pos++; // skip len
event[pos++] = GOEP_SUBEVENT_CONNECTION_CLOSED;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("goep_client_emit_connection_closed_event size %u", pos);
context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static inline void goep_client_emit_can_send_now_event(goep_client_t * context){
uint8_t event[5];
int pos = 0;
event[pos++] = HCI_EVENT_GOEP_META;
pos++; // skip len
event[pos++] = GOEP_SUBEVENT_CAN_SEND_NOW;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("goep_client_emit_can_send_now_event size %u", pos);
context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static void goep_client_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
uint8_t status;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case RFCOMM_EVENT_CHANNEL_OPENED:
status = rfcomm_event_channel_opened_get_status(packet);
if (status) {
log_info("goep_client: RFCOMM channel open failed, status %u", rfcomm_event_channel_opened_get_status(packet));
goep_client->state = GOEP_INIT;
} else {
goep_client->bearer_mtu = rfcomm_event_channel_opened_get_max_frame_size(packet);
log_info("goep_client: RFCOMM channel open succeeded. cid %u, max frame size %u", goep_client->bearer_cid, goep_client->bearer_mtu);
goep_client->state = GOEP_CONNECTED;
}
goep_client_emit_connected_event(goep_client, status);
return;
case RFCOMM_EVENT_CAN_SEND_NOW:
goep_client_emit_can_send_now_event(goep_client);
break;
case RFCOMM_CHANNEL_CLOSED:
goep_client->state = GOEP_INIT;
goep_client_emit_connection_closed_event(goep_client);
break;
default:
break;
}
break;
case RFCOMM_DATA_PACKET:
goep_client->client_handler(GOEP_DATA_PACKET, goep_client->cid, packet, size);
break;
default:
break;
}
}
static void goep_client_handle_query_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(packet_type);
UNUSED(channel);
UNUSED(size);
switch (packet[0]){
case SDP_EVENT_QUERY_RFCOMM_SERVICE:
goep_client->bearer_port = sdp_event_query_rfcomm_service_get_rfcomm_channel(packet);
break;
case SDP_EVENT_QUERY_COMPLETE:
if (goep_client->bearer_port == 0){
log_info("Remote GOEP RFCOMM Server Channel not found");
goep_client->state = GOEP_INIT;
goep_client_emit_connected_event(goep_client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
break;
}
log_info("Remote GOEP RFCOMM Server Channel: %u", goep_client->bearer_port);
rfcomm_create_channel(&goep_client_packet_handler, goep_client->bd_addr, goep_client->bearer_port, &goep_client->bearer_cid);
break;
}
}
static void goep_client_packet_append(const uint8_t * data, uint16_t len){
uint8_t * buffer = rfcomm_get_outgoing_buffer();
uint16_t pos = big_endian_read_16(buffer, 1);
memcpy(&buffer[pos], data, len);
pos += len;
big_endian_store_16(buffer, 1, pos);
}
static void goep_client_packet_init(uint16_t goep_cid, uint8_t opcode){
UNUSED(goep_cid);
rfcomm_reserve_packet_buffer();
uint8_t * buffer = rfcomm_get_outgoing_buffer();
buffer[0] = opcode;
big_endian_store_16(buffer, 1, 3);
// store opcode for parsing of response
goep_client->obex_opcode = opcode;
}
static void goep_client_packet_add_connection_id(uint16_t goep_cid){
UNUSED(goep_cid);
// add connection_id header if set, must be first header if used
if (goep_client->obex_connection_id != OBEX_CONNECTION_ID_INVALID){
uint8_t header[5];
header[0] = OBEX_HEADER_CONNECTION_ID;
big_endian_store_32(header, 1, goep_client->obex_connection_id);
goep_client_packet_append(&header[0], sizeof(header));
}
}
void goep_client_init(void){
memset(goep_client, 0, sizeof(goep_client_t));
goep_client->state = GOEP_INIT;
goep_client->cid = 1;
goep_client->obex_connection_id = OBEX_CONNECTION_ID_INVALID;
}
uint8_t goep_client_create_connection(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t uuid, uint16_t * out_cid){
if (goep_client->state != GOEP_INIT) return BTSTACK_MEMORY_ALLOC_FAILED;
goep_client->client_handler = handler;
goep_client->state = GOEP_W4_SDP;
memcpy(goep_client->bd_addr, addr, 6);
sdp_client_query_rfcomm_channel_and_name_for_uuid(&goep_client_handle_query_rfcomm_event, goep_client->bd_addr, uuid);
*out_cid = goep_client->cid;
return 0;
}
uint8_t goep_client_disconnect(uint16_t goep_cid){
UNUSED(goep_cid);
rfcomm_disconnect(goep_client->bearer_cid);
return 0;
}
void goep_client_set_connection_id(uint16_t goep_cid, uint32_t connection_id){
UNUSED(goep_cid);
goep_client->obex_connection_id = connection_id;
}
uint8_t goep_client_get_request_opcode(uint16_t goep_cid){
UNUSED(goep_cid);
return goep_client->obex_opcode;
}
void goep_client_request_can_send_now(uint16_t goep_cid){
UNUSED(goep_cid);
rfcomm_request_can_send_now_event(goep_client->bearer_cid);
}
void goep_client_create_connect_request(uint16_t goep_cid, uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){
UNUSED(goep_cid);
goep_client_packet_init(goep_cid, OBEX_OPCODE_CONNECT);
uint8_t fields[4];
fields[0] = obex_version_number;
fields[1] = flags;
// workaround: limit OBEX packet len to RFCOMM MTU to avoid handling of fragemented packets
maximum_obex_packet_length = btstack_min(maximum_obex_packet_length, goep_client->bearer_mtu);
big_endian_store_16(fields, 2, maximum_obex_packet_length);
goep_client_packet_append(&fields[0], sizeof(fields));
}
void goep_client_create_get_request(uint16_t goep_cid){
UNUSED(goep_cid);
goep_client_packet_init(goep_cid, OBEX_OPCODE_GET | OBEX_OPCODE_FINAL_BIT_MASK);
goep_client_packet_add_connection_id(goep_cid);
}
void goep_client_create_set_path_request(uint16_t goep_cid, uint8_t flags){
UNUSED(goep_cid);
goep_client_packet_init(goep_cid, OBEX_OPCODE_SETPATH);
uint8_t fields[2];
fields[0] = flags;
fields[1] = 0; // reserved
goep_client_packet_append(&fields[0], sizeof(fields));
goep_client_packet_add_connection_id(goep_cid);
}
void goep_client_add_header_target(uint16_t goep_cid, uint16_t length, const uint8_t * target){
UNUSED(goep_cid);
uint8_t header[3];
header[0] = OBEX_HEADER_TARGET;
big_endian_store_16(header, 1, 1 + 2 + length);
goep_client_packet_append(&header[0], sizeof(header));
goep_client_packet_append(target, length);
}
void goep_client_add_header_name(uint16_t goep_cid, const char * name){
UNUSED(goep_cid);
int len_incl_zero = strlen(name) + 1;
uint8_t * buffer = rfcomm_get_outgoing_buffer();
uint16_t pos = big_endian_read_16(buffer, 1);
buffer[pos++] = OBEX_HEADER_NAME;
big_endian_store_16(buffer, pos, 1 + 2 + len_incl_zero*2);
pos += 2;
int i;
// @note name[len] == 0
for (i = 0 ; i < len_incl_zero ; i++){
buffer[pos++] = 0;
buffer[pos++] = *name++;
}
big_endian_store_16(buffer, 1, pos);
}
void goep_client_add_header_type(uint16_t goep_cid, const char * type){
UNUSED(goep_cid);
uint8_t header[3];
header[0] = OBEX_HEADER_TYPE;
int len_incl_zero = strlen(type) + 1;
big_endian_store_16(header, 1, 1 + 2 + len_incl_zero);
goep_client_packet_append(&header[0], sizeof(header));
goep_client_packet_append((const uint8_t*)type, len_incl_zero);
}
int goep_client_execute(uint16_t goep_cid){
UNUSED(goep_cid);
uint8_t * buffer = rfcomm_get_outgoing_buffer();
uint16_t pos = big_endian_read_16(buffer, 1);
return rfcomm_send_prepared(goep_client->bearer_cid, pos);
}

177
src/classic/goep_client.h Normal file
View File

@ -0,0 +1,177 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#ifndef __GOEP_CLIENT_H
#if defined __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "btstack_defines.h"
//------------------------------------------------------------------------------------------------------------
// goep_client.h
//
// Communicate with remote OBEX server - General Object Exchange
//
/* API_START */
/**
* Setup GOEP Client
*/
void goep_client_init(void);
/*
* @brief Create GOEP connection to a GEOP server with specified UUID on a remote deivce.
* @param handler
* @param addr
* @param uuid
* @param out_cid to use for further commands
* @result status
*/
uint8_t goep_client_create_connection(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t uuid, uint16_t * out_cid);
/**
* @brief Disconnects GOEP connection with given identifier.
* @param gope_cid
* @return status
*/
uint8_t goep_client_disconnect(uint16_t goep_cid);
/**
* @brief Request emission of GOEP_SUBEVENT_CAN_SEND_NOW as soon as possible
* @note GOEP_SUBEVENT_CAN_SEND_NOW might be emitted during call to this function
* so packet handler should be ready to handle it
* @param goep_cid
*/
void goep_client_request_can_send_now(uint16_t goep_cid);
/**
* @brief Get Opcode from last created request, needed for parsing of OBEX response packet
* @param gope_cid
* @return opcode
*/
uint8_t goep_client_get_request_opcode(uint16_t goep_cid);
/**
* @brief Set Connection ID used for newly created requests
* @param gope_cid
*/
void goep_client_set_connection_id(uint16_t goep_cid, uint32_t connection_id);
/**
* @brief Start Connect request
* @param gope_cid
* @param obex_version_number
* @param flags
* @param maximum_obex_packet_length
*/
void goep_client_create_connect_request(uint16_t goep_cid, uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length);
/**
* @brief Start Get request
* @param gope_cid
*/
void goep_client_create_get_request(uint16_t goep_cid);
/**
* @brief Start Set Path request
* @param gope_cid
*/
void goep_client_create_set_path_request(uint16_t goep_cid, uint8_t flags);
// not implemented yet
// void goep_client_create_put(uint16_t goep_cid);
/**
* @brief Add name header to current request
* @param goep_cid
* @param name
*/
void goep_client_add_header_name(uint16_t goep_cid, const char * name);
/**
* @brief Add target header to current request
* @param goep_cid
* @param target
*/
void goep_client_add_header_target(uint16_t goep_cid, uint16_t length, const uint8_t * target);
/**
* @brief Add type header to current request
* @param goep_cid
* @param type
*/
void goep_client_add_header_type(uint16_t goep_cid, const char * type);
/**
* @brief Add count header to current request
* @param goep_cid
* @param count
*/
void goep_client_add_header_count(uint16_t goep_cid, uint32_t count);
/**
* @brief Add application parameters header to current request
* @param goep_cid
* @param lenght of application parameters
* @param daa
*/
void goep_client_add_header_application_parameters(uint16_t goep_cid, uint16_t length, uint8_t * data);
// int goep_client_add_body_static(uint16_t goep_cid, uint32_t length, uint8_t * data);
// int goep_client_add_body_dynamic(uint16_t goep_cid, uint32_t length, void (*data_callback)(uint32_t offset, uint8_t * buffer, uint32_t len));
/**
* @brief Execute prepared request
* @param goep_cid
* @param daa
*/
int goep_client_execute(uint16_t goep_cid);
/* API_END */
#if defined __cplusplus
}
#endif
#endif

82
src/classic/obex.h Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#ifndef __OBEX_H
#define OBEX_OPCODE_CONNECT 0x80
#define OBEX_OPCODE_DISCONNECT 0x81
#define OBEX_OPCODE_PUT 0x02
#define OBEX_OPCODE_CLOSE 0x82
#define OBEX_OPCODE_GET 0x03
#define OBEX_OPCODE_SETPATH 0x85
#define OBEX_OPCODE_SESSION 0x87
#define OBEX_OPCODE_ABORT 0xFF
#define OBEX_RESP_SUCCESS 0xA0
#define OBEX_RESP_CONTINUE 0x90
#define OBEX_RESP_CANCELED 0xC1
#define OBEX_RESP_NOT_FOUND 0xC4
#define OBEX_RESP_REFUSED 0xC6
#define OBEX_HEADER_BODY 0x48
#define OBEX_HEADER_END_OF_BODY 0x49
#define OBEX_HEADER_COUNT 0xC0
#define OBEX_HEADER_NAME 0x01
#define OBEX_HEADER_TYPE 0x42
#define OBEX_HEADER_LENGTH 0xC3
#define OBEX_HEADER_TIME_ISO_8601 0x44
#define OBEX_HEADER_TIME_4_BYTE 0xC4
#define OBEX_HEADER_DESCRIPTION 0x05
#define OBEX_HEADER_TARGET 0x46
#define OBEX_HEADER_HTTP 0x47
#define OBEX_HEADER_WHO 0x4A
#define OBEX_HEADER_OBJECT_CLASS 0x4F
#define OBEX_HEADER_APPLICATION_PARAMETERS 0x4C
#define OBEX_HEADER_CONNECTION_ID 0xCb
#define OBEX_OPCODE_FINAL_BIT_MASK 0x80
#define OBEX_VERSION 0x14
#define OBEX_PACKET_HEADER_SIZE 3
#define OBEX_PACKET_OPCODE_OFFSET 0
#define OBEX_PACKET_LENGTH_OFFSET 1
#define OBEX_MAX_PACKETLEN_DEFAULT 0xffff
#define OBEX_CONNECTION_ID_INVALID 0xFFFFFFFF
#endif

181
src/classic/obex_iterator.c Normal file
View File

@ -0,0 +1,181 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#include "btstack_config.h"
#include <stdint.h>
#include <stdlib.h>
#include "hci_cmd.h"
#include "btstack_debug.h"
#include "hci.h"
#include "bluetooth_sdp.h"
#include "classic/sdp_client_rfcomm.h"
#include "btstack_event.h"
#include "classic/obex.h"
#include "classic/obex_iterator.h"
static int obex_packet_header_offset_for_opcode(uint8_t opcode){
switch (opcode){
case OBEX_OPCODE_SETPATH:
return 5;
case OBEX_OPCODE_CONNECT:
return 7;
default:
return 3;
}
}
static void obex_iterator_init(obex_iterator_t *context, int header_offset, const uint8_t * packet_data, uint16_t packet_len){
memset(context, 0, sizeof(obex_iterator_t));
context->data = packet_data + header_offset;
context->length = packet_len - header_offset;
}
void obex_iterator_init_with_request_packet(obex_iterator_t *context, const uint8_t * packet_data, uint16_t packet_len){
int header_offset = obex_packet_header_offset_for_opcode(packet_data[0]);
obex_iterator_init(context, header_offset, packet_data, packet_len);
}
void obex_iterator_init_with_response_packet(obex_iterator_t *context, uint8_t request_opcode, const uint8_t * packet_data, uint16_t packet_len){
int header_offset = request_opcode == OBEX_OPCODE_CONNECT ? 7 : 3;
obex_iterator_init(context, header_offset, packet_data, packet_len);
}
int obex_iterator_has_more(const obex_iterator_t * context){
return context->offset < context->length;
}
void obex_iterator_next(obex_iterator_t * context){
int len = 0;
const uint8_t * data = context->data + context->offset;
int encoding = data[0] >> 6;
switch (encoding){
case 0:
case 1:
// 16-bit length info prefixed
len = 2 + big_endian_read_16(data, 1);
break;
case 2:
// 8-bit value
len = 1;
break;
case 3:
// 32-bit value
len = 4;
break;
// avoid compiler warning about unused cases (by unclever compilers)
default:
break;
}
context->offset += 1 + len;
}
// OBEX packet header access functions
// @note BODY/END-OF-BODY headers might be incomplete
uint8_t obex_iterator_get_hi(const obex_iterator_t * context){
return context->data[context->offset];
}
uint8_t obex_iterator_get_data_8(const obex_iterator_t * context){
return context->data[context->offset+1];
}
uint32_t obex_iterator_get_data_32(const obex_iterator_t * context){
return big_endian_read_32(context->data, context->offset + 1);
}
uint32_t obex_iterator_get_data_len(const obex_iterator_t * context){
const uint8_t * data = context->data + context->offset;
int encoding = data[0] >> 6;
switch (encoding){
case 0:
case 1:
// 16-bit length info prefixed
return big_endian_read_16(data, 1) - 3;
case 2:
// 8-bit value
return 1;
case 3:
// 32-bit value
return 4;
// avoid compiler warning about unused cases (by unclever compilers)
default:
return 0;
}
}
const uint8_t * obex_iterator_get_data(const obex_iterator_t * context){
const uint8_t * data = context->data + context->offset;
int encoding = data[0] >> 6;
switch (encoding){
case 0:
case 1:
// 16-bit length info prefixed
return &data[3];
default:
// 8-bit value
// 32-bit value
return &data[1];
}
}
void obex_dump_packet(uint8_t request_opcode, uint8_t * packet, uint16_t size){
// printf("RCV: '");
// printf_hexdump(packet, size);
obex_iterator_t it;
printf("Opcode: 0x%02x\n", packet[0]);
for (obex_iterator_init_with_response_packet(&it, request_opcode, packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
uint8_t hi = obex_iterator_get_hi(&it);
printf("HI: %x - ", hi);
uint8_t encoding = hi >> 6;
uint16_t len;
switch (encoding){
case 0:
case 1:
len = obex_iterator_get_data_len(&it);
printf_hexdump(obex_iterator_get_data(&it), len);
break;
case 2:
printf("%02x\n", obex_iterator_get_data_8(&it));
break;
case 3:
printf("%08x\n", (int) obex_iterator_get_data_32(&it));
break;
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#ifndef __OBEX_ITERATOR_H
#if defined __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* API_START */
typedef struct obex_iterator {
const uint8_t * data;
uint8_t offset;
uint8_t length;
} obex_iterator_t;
// OBEX packet header iterator
void obex_iterator_init_with_request_packet(obex_iterator_t *context, const uint8_t * packet_data, uint16_t packet_len);
void obex_iterator_init_with_response_packet(obex_iterator_t *context, uint8_t request_opcode, const uint8_t * packet_data, uint16_t packet_len);
int obex_iterator_has_more(const obex_iterator_t * context);
void obex_iterator_next(obex_iterator_t * context);
// OBEX packet header access functions
// @note BODY/END-OF-BODY headers might be incomplete
uint8_t obex_iterator_get_hi(const obex_iterator_t * context);
uint8_t obex_iterator_get_data_8(const obex_iterator_t * context);
uint32_t obex_iterator_get_data_32(const obex_iterator_t * context);
uint32_t obex_iterator_get_data_len(const obex_iterator_t * context);
const uint8_t * obex_iterator_get_data(const obex_iterator_t * context);
/* API_END */
// debug
void obex_dump_packet(uint8_t request_opcode, uint8_t * packet, uint16_t size);
#if defined __cplusplus
}
#endif
#endif

371
src/classic/pbap_client.c Normal file
View File

@ -0,0 +1,371 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
// *****************************************************************************
//
#if 0
0x0000 = uint32(65542),
// BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE
0x0001 = { uuid16(11 2f) },
// BLUETOOTH_PROTOCOL_L2CAP, BLUETOOTH_PROTOCOL_RFCOMM, BLUETOOTH_PROTOCOL_OBEX
0x0004 = { { uuid16(01 00) }, { uuid16(00 03), uint8(19) }, { uuid16(00 08) } }
0x0005 = { uuid16(10 02) },
// BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS, v1.01 = 0x101
0x0009 = { { uuid16(11 30), uint16(257) } },
0x0100 = string(OBEX Phonebook Access Server
// BLUETOOTH_ATTRIBUTE_SUPPORTED_FEATURES -- should be 0x317 BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES?
0x0311 = uint8(3),
// BLUETOOTH_ATTRIBUTE_SUPPORTED_REPOSITORIES
0x0314 = uint8(1),
#endif
//
// *****************************************************************************
#include "btstack_config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hci_cmd.h"
#include "btstack_run_loop.h"
#include "btstack_debug.h"
#include "hci.h"
#include "btstack_memory.h"
#include "hci_dump.h"
#include "l2cap.h"
#include "bluetooth_sdp.h"
#include "classic/sdp_client_rfcomm.h"
#include "btstack_event.h"
#include "classic/obex.h"
#include "classic/obex_iterator.h"
#include "classic/goep_client.h"
#include "classic/pbap_client.h"
// 796135f0-f0c5-11d8-0966- 0800200c9a66
uint8_t pbap_uuid[] = { 0x79, 0x61, 0x35, 0xf0, 0xf0, 0xc5, 0x11, 0xd8, 0x09, 0x66, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66};
const char * pbap_type = "x-bt/phonebook";
const char * pbap_name = "pb.vcf";
typedef enum {
PBAP_INIT = 0,
PBAP_W4_GOEP_CONNECTION,
PBAP_W2_SEND_CONNECT_REQUEST,
PBAP_W4_CONNECT_RESPONSE,
PBAP_CONNECT_RESPONSE_RECEIVED,
PBAP_CONNECTED,
//
PBAP_W2_PULL_PHONE_BOOK,
PBAP_W4_PHONE_BOOK,
PBAP_W2_SET_PATH_ROOT,
PBAP_W4_SET_PATH_ROOT_COMPLETE,
PBAP_W2_SET_PATH_ELEMENT,
PBAP_W4_SET_PATH_ELEMENT_COMPLETE,
} pbap_state_t;
typedef struct pbap_client {
pbap_state_t state;
uint16_t cid;
bd_addr_t bd_addr;
hci_con_handle_t con_handle;
uint8_t incoming;
uint16_t goep_cid;
btstack_packet_handler_t client_handler;
const char * current_folder;
uint16_t set_path_offset;
} pbap_client_t;
static pbap_client_t _pbap_client;
static pbap_client_t * pbap_client = &_pbap_client;
static inline void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){
uint8_t event[15];
int pos = 0;
event[pos++] = HCI_EVENT_PBAP_META;
pos++; // skip len
event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[pos++] = status;
memcpy(&event[pos], context->bd_addr, 6);
pos += 6;
little_endian_store_16(event,pos,context->con_handle);
pos += 2;
event[pos++] = context->incoming;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos);
context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static inline void pbap_client_emit_connection_closed_event(pbap_client_t * context){
uint8_t event[5];
int pos = 0;
event[pos++] = HCI_EVENT_PBAP_META;
pos++; // skip len
event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos);
context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static inline void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){
uint8_t event[6];
int pos = 0;
event[pos++] = HCI_EVENT_PBAP_META;
pos++; // skip len
event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[pos++]= status;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos);
context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static void pbap_handle_can_send_now(void){
uint8_t path_element[20];
uint16_t path_element_start;
uint16_t path_element_len;
switch (pbap_client->state){
case PBAP_W2_SEND_CONNECT_REQUEST:
goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid);
// state
pbap_client->state = PBAP_W4_CONNECT_RESPONSE;
// send packet
goep_client_execute(pbap_client->goep_cid);
return;
case PBAP_W2_PULL_PHONE_BOOK:
goep_client_create_get_request(pbap_client->goep_cid);
goep_client_add_header_type(pbap_client->goep_cid, pbap_type);
goep_client_add_header_name(pbap_client->goep_cid, pbap_name);
// state
pbap_client->state = PBAP_W4_PHONE_BOOK;
// send packet
goep_client_execute(pbap_client->goep_cid);
break;
case PBAP_W2_SET_PATH_ROOT:
goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Dont create directory
// On Android 4.2 Cyanogenmod, using "" as path fails
// goep_client_add_header_name(pbap_client->goep_cid, ""); // empty == /
// state
pbap_client->state = PBAP_W4_SET_PATH_ROOT_COMPLETE;
// send packet
goep_client_execute(pbap_client->goep_cid);
break;
case PBAP_W2_SET_PATH_ELEMENT:
// find '/' or '\0'
path_element_start = pbap_client->set_path_offset;
while (pbap_client->current_folder[pbap_client->set_path_offset] != '\0' &&
pbap_client->current_folder[pbap_client->set_path_offset] != '/'){
pbap_client->set_path_offset++;
}
// skip /
if (pbap_client->current_folder[pbap_client->set_path_offset] == '/'){
pbap_client->set_path_offset++;
}
path_element_len = pbap_client->set_path_offset-path_element_start;
memcpy(path_element, &pbap_client->current_folder[path_element_start], path_element_len);
path_element[path_element_len] = 0;
goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Dont create directory
goep_client_add_header_name(pbap_client->goep_cid, (const char *) path_element); // next element
// state
pbap_client->state = PBAP_W4_SET_PATH_ELEMENT_COMPLETE;
// send packet
goep_client_execute(pbap_client->goep_cid);
break;
default:
break;
}
}
static void pbap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
obex_iterator_t it;
uint8_t status;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_GOEP_META:
switch (hci_event_goep_meta_get_subevent_code(packet)){
case GOEP_SUBEVENT_CONNECTION_OPENED:
status = goep_subevent_connection_opened_get_status(packet);
pbap_client->con_handle = goep_subevent_connection_opened_get_con_handle(packet);
pbap_client->incoming = goep_subevent_connection_opened_get_incoming(packet);
goep_subevent_connection_opened_get_bd_addr(packet, pbap_client->bd_addr);
if (status){
log_info("pbap: connection failed %u", status);
pbap_client->state = PBAP_INIT;
pbap_client_emit_connected_event(pbap_client, status);
} else {
log_info("pbap: connection established");
pbap_client->goep_cid = goep_subevent_connection_opened_get_goep_cid(packet);
pbap_client->state = PBAP_W2_SEND_CONNECT_REQUEST;
goep_client_request_can_send_now(pbap_client->goep_cid);
}
break;
case GOEP_SUBEVENT_CONNECTION_CLOSED:
if (pbap_client->state != PBAP_CONNECTED){
pbap_client_emit_operation_complete_event(pbap_client, OBEX_DISCONNECTED);
}
pbap_client->state = PBAP_INIT;
pbap_client_emit_connection_closed_event(pbap_client);
break;
case GOEP_SUBEVENT_CAN_SEND_NOW:
pbap_handle_can_send_now();
break;
}
break;
default:
break;
}
break;
case GOEP_DATA_PACKET:
// TODO: handle chunked data
#if 0
obex_dump_packet(goep_client_get_request_opcode(pbap_client->goep_cid), packet, size);
#endif
switch (pbap_client->state){
case PBAP_W4_CONNECT_RESPONSE:
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)){
uint8_t hi = obex_iterator_get_hi(&it);
if (hi == OBEX_HEADER_CONNECTION_ID){
goep_client_set_connection_id(pbap_client->goep_cid, obex_iterator_get_data_32(&it));
}
}
if (packet[0] == OBEX_RESP_SUCCESS){
pbap_client->state = PBAP_CONNECTED;
pbap_client_emit_connected_event(pbap_client, 0);
} else {
log_info("pbap: obex connect failed, result 0x%02x", packet[0]);
pbap_client->state = PBAP_INIT;
pbap_client_emit_connected_event(pbap_client, OBEX_CONNECT_FAILED);
}
break;
case PBAP_W4_SET_PATH_ROOT_COMPLETE:
case PBAP_W4_SET_PATH_ELEMENT_COMPLETE:
if (packet[0] == OBEX_RESP_SUCCESS){
if (pbap_client->current_folder){
pbap_client->state = PBAP_W2_SET_PATH_ELEMENT;
goep_client_request_can_send_now(pbap_client->goep_cid);
} else {
pbap_client_emit_operation_complete_event(pbap_client, 0);
}
} else if (packet[0] == OBEX_RESP_NOT_FOUND){
pbap_client->state = PBAP_CONNECTED;
pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_FOUND);
} else {
pbap_client->state = PBAP_CONNECTED;
pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
}
break;
case PBAP_W4_PHONE_BOOK:
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)){
uint8_t hi = obex_iterator_get_hi(&it);
if (hi == OBEX_HEADER_BODY || hi == OBEX_HEADER_END_OF_BODY){
uint16_t data_len = obex_iterator_get_data_len(&it);
const uint8_t * data = obex_iterator_get_data(&it);
pbap_client->client_handler(PBAP_DATA_PACKET, pbap_client->cid, (uint8_t *) data, data_len);
}
}
if (packet[0] == OBEX_RESP_CONTINUE){
pbap_client->state = PBAP_W2_PULL_PHONE_BOOK;
goep_client_request_can_send_now(pbap_client->goep_cid);
} else if (packet[0] == OBEX_RESP_SUCCESS){
pbap_client->state = PBAP_CONNECTED;
pbap_client_emit_operation_complete_event(pbap_client, 0);
} else {
pbap_client->state = PBAP_CONNECTED;
pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
}
break;
default:
break;
}
break;
default:
break;
}
}
void pbap_client_init(void){
memset(pbap_client, 0, sizeof(pbap_client_t));
pbap_client->state = PBAP_INIT;
pbap_client->cid = 1;
}
uint8_t pbap_connect(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid){
if (pbap_client->state != PBAP_INIT) return BTSTACK_MEMORY_ALLOC_FAILED;
pbap_client->state = PBAP_W4_GOEP_CONNECTION;
pbap_client->client_handler = handler;
uint8_t err = goep_client_create_connection(&pbap_packet_handler, addr, BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE, &pbap_client->goep_cid);
*out_cid = pbap_client->cid;
if (err) return err;
return 0;
}
uint8_t pbap_disconnect(uint16_t pbap_cid){
UNUSED(pbap_cid);
if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
goep_client_disconnect(pbap_client->goep_cid);
return 0;
}
uint8_t pbap_pull_phonebook(uint16_t pbap_cid){
UNUSED(pbap_cid);
if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
pbap_client->state = PBAP_W2_PULL_PHONE_BOOK;
goep_client_request_can_send_now(pbap_client->goep_cid);
return 0;
}
uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){
UNUSED(pbap_cid);
if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
pbap_client->state = PBAP_W2_SET_PATH_ROOT;
pbap_client->current_folder = path;
pbap_client->set_path_offset = 0;
goep_client_request_can_send_now(pbap_client->goep_cid);
return 0;
}

90
src/classic/pbap_client.h Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#ifndef __GOEP_CLIENT_H
#if defined __cplusplus
extern "C" {
#endif
#include "btstack_config.h"
#include <stdint.h>
/* API_START */
/**
* Setup PhoneBook Access Client
*/
void pbap_client_init(void);
/**
* @brief Create PBAP connection to a Phone Book Server (PSE) server on a remote deivce.
* @param handler
* @param addr
* @param out_cid to use for further commands
* @result status
*/
uint8_t pbap_connect(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid);
/**
* @brief Disconnects PBAP connection with given identifier.
* @param pbap_cid
* @return status
*/
uint8_t pbap_disconnect(uint16_t pbap_cid);
/**
* @brief Set current folder on PSE
* @param pbap_cid
* @param path - note: path is not copied
* @return status
*/
uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path);
/**
* @brief Pull phone book from PSE
* @param pbap_cid
* @return status
*/
uint8_t pbap_pull_phonebook(uint16_t pbap_cid);
/* API_END */
#if defined __cplusplus
}
#endif
#endif

View File

@ -8,6 +8,30 @@ import os
import btstack_parser as parser import btstack_parser as parser
meta_events = [
'ANCS',
'AVDTP',
'AVRCP',
'GOEP',
'HFP',
'HSP',
'PBAP',
'LE'
]
supported_event_groups = meta_events + [
'BTSTACK',
'GAP',
'HCI',
'SDP',
'SM',
'L2CAP',
'RFCOMM',
'GATT',
'BNEP',
'ATT',
]
program_info = ''' program_info = '''
BTstack Event Getter Generator for BTstack BTstack Event Getter Generator for BTstack
Copyright 2016, BlueKitchen GmbH Copyright 2016, BlueKitchen GmbH
@ -232,14 +256,13 @@ def create_events(events):
fout.write(copyright) fout.write(copyright)
fout.write(hfile_header_begin) fout.write(hfile_header_begin)
meta_events = ['HSP', 'HFP', 'ANCS', 'LE'];
for meta_event in meta_events: for meta_event in meta_events:
fout.write(meta_event_template.format(meta_event=meta_event.lower())) fout.write(meta_event_template.format(meta_event=meta_event.lower()))
for event_type, event_name, format, args in events: for event_type, event_name, format, args in events:
parts = event_name.split("_") parts = event_name.split("_")
event_group = parts[0] event_group = parts[0]
if not event_group in [ 'BTSTACK', 'GAP', 'HCI', 'HSP', 'HFP', 'SDP', 'ANCS', 'SM', 'L2CAP', 'RFCOMM', 'GATT', 'BNEP', 'ATT', 'AVDTP', 'AVRCP']: if not event_group in supported_event_groups:
print("// %s " % event_name) print("// %s " % event_name)
continue continue
# print(event_name) # print(event_name)