mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-02-04 06:39:53 +00:00
Merge branch 'pbap' into develop
Add Phone Book Access Client (PBAP) over General Object Exchange (GOEP)
This commit is contained in:
commit
bdd624350d
@ -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
246
example/pbap_client_demo.c
Normal 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;
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
328
src/classic/goep_client.c
Normal 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
177
src/classic/goep_client.h
Normal 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
82
src/classic/obex.h
Normal 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
181
src/classic/obex_iterator.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
76
src/classic/obex_iterator.h
Normal file
76
src/classic/obex_iterator.h
Normal 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
371
src/classic/pbap_client.c
Normal 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); // Don’t 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); // Don’t 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
90
src/classic/pbap_client.h
Normal 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
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user