sdp: added service search and service attribute search queries

This commit is contained in:
matthias.ringwald@gmail.com 2013-11-01 13:09:54 +00:00
parent 46a10724a8
commit bace66242a
10 changed files with 313 additions and 43 deletions

View File

@ -51,6 +51,9 @@ sdp_general_query: ${CORE_OBJ} ${COMMON_OBJ} sdp_general_query.c
spp_counter: ${CORE_OBJ} ${COMMON_OBJ} spp_counter.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
spp_counter_ssp: ${CORE_OBJ} ${COMMON_OBJ} spp_counter_ssp.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
# compile .ble description
profile.h: profile.gatt
python ${BTSTACK_ROOT}/ble/compile-gatt.py $< $@

View File

@ -58,7 +58,7 @@ static void btstack_setup(){
btstack_memory_init();
run_loop_init(RUN_LOOP_POSIX);
// hci_dump_open("/tmp/hci_dump_sdp_client.pklg", HCI_DUMP_PACKETLOGGER);
hci_dump_open("/tmp/hci_dump_sdp_general_query.pklg", HCI_DUMP_PACKETLOGGER);
hci_transport_t * transport = hci_transport_usb_instance();
hci_uart_config_t * config = NULL;

View File

@ -52,7 +52,7 @@ static void btstack_setup(){
btstack_memory_init();
run_loop_init(RUN_LOOP_POSIX);
// hci_dump_open("/tmp/hci_dump_sdp_client.pklg", HCI_DUMP_PACKETLOGGER);
hci_dump_open("/tmp/hci_dump_sdp_rfcomm_query.pklg", HCI_DUMP_PACKETLOGGER);
hci_transport_t * transport = hci_transport_usb_instance();
hci_uart_config_t * config = NULL;

View File

@ -209,6 +209,9 @@ extern "C" {
// data: event(8), len(8), record nr(16), attribute id(16), attribute value(var)
#define SDP_QUERY_ATTRIBUTE_VALUE 0x93
// data: event(8), total nr(16), current nr(16), service record handle(32)
#define SDP_QUERY_SERVICE_RECORD_HANDLE 0x94
//#define SDP_PARSER_ATTRIBUTE_VALUE 0x94
//#define SDP_PARSER_COMPLETE 0x95

View File

@ -45,6 +45,7 @@ extern "C" {
#endif
typedef enum {
SDP_Invalid = 0,
SDP_ErrorResponse = 1,
SDP_ServiceSearchRequest,
SDP_ServiceSearchResponse,

View File

@ -55,11 +55,15 @@ typedef enum {
void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static uint16_t setup_sdp_request(uint8_t * data);
static uint16_t setup_ServiceSearchRequest(uint8_t * data);
static uint16_t setup_ServiceAttributeRequest(uint8_t * data);
static uint16_t setup_ServiceSearchAttributeRequest(uint8_t * data);
// SDP Client Query
static uint16_t mtu;
static uint16_t sdp_cid = 0x40;
static uint32_t serviceRecordHandle;
static uint8_t * serviceSearchPattern;
static uint8_t * attributeIDList;
static uint16_t transactionID = 0;
@ -67,6 +71,7 @@ static uint8_t continuationState[16];
static uint8_t continuationStateLen;
static sdp_client_state_t sdp_client_state;
static SDP_PDU_ID_t PDU_ID = SDP_Invalid;
void sdp_client_handle_done(uint8_t status){
if (status == 0){
@ -75,7 +80,11 @@ void sdp_client_handle_done(uint8_t status){
sdp_parser_handle_done(status);
}
// TODO: inline if not needed
void parse_ServiceRecordHandleList(uint8_t* packet, uint16_t total_count, uint16_t current_count){
sdp_parser_handle_service_search(packet, total_count, current_count);
}
// TODO: inline if not needed (des(des))
void parse_AttributeLists(uint8_t* packet, uint16_t length){
sdp_parser_handle_chunk(packet, length);
}
@ -94,25 +103,58 @@ and a list of attribute IDs. The remote data is handled by the SDP parser. The
SDP parser delivers attribute values and done event via a registered callback. */
void sdp_client_query(bd_addr_t remote, uint8_t * des_serviceSearchPattern, uint8_t * des_attributeIDList){
serviceSearchPattern = des_serviceSearchPattern;
attributeIDList = des_attributeIDList;
continuationStateLen = 0;
PDU_ID = SDP_ServiceSearchAttributeResponse;
sdp_client_state = W4_CONNECT;
l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu());
}
void sdp_client_service_attribute_search(bd_addr_t remote, uint32_t search_serviceRecordHandle, uint8_t * des_attributeIDList){
serviceRecordHandle = search_serviceRecordHandle;
attributeIDList = des_attributeIDList;
continuationStateLen = 0;
PDU_ID = SDP_ServiceAttributeResponse;
sdp_client_state = W4_CONNECT;
l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu());
}
void sdp_client_service_search(bd_addr_t remote, uint8_t * des_serviceSearchPattern){
serviceSearchPattern = des_serviceSearchPattern;
continuationStateLen = 0;
PDU_ID = SDP_ServiceSearchResponse;
sdp_client_state = W4_CONNECT;
l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu());
}
void tryToSend(uint16_t channel){
static void tryToSend(uint16_t channel){
if (sdp_client_state != W2_SEND) return;
if (!l2cap_can_send_packet_now(channel)) return;
uint8_t * data = l2cap_get_outgoing_buffer();
uint16_t request_len = setup_sdp_request(data);
uint16_t request_len = 0;
switch (PDU_ID){
case SDP_ServiceSearchResponse:
request_len = setup_ServiceSearchRequest(data);
break;
case SDP_ServiceAttributeResponse:
request_len = setup_ServiceAttributeRequest(data);
break;
case SDP_ServiceSearchAttributeResponse:
request_len = setup_ServiceSearchAttributeRequest(data);
break;
default:
log_info("SDP Client tryToSend :: PDU ID invalid. %u", PDU_ID);
return;
}
printf("tryToSend channel %x, size %u\n", channel, request_len);
@ -121,6 +163,7 @@ void tryToSend(uint16_t channel){
case 0:
// packet is sent prepare next one
printf("l2cap_send_internal() -> OK\n\r");
PDU_ID = SDP_Invalid;
sdp_client_state = W4_RESPONSE;
break;
case BTSTACK_ACL_BUFFERS_FULL:
@ -132,7 +175,76 @@ void tryToSend(uint16_t channel){
}
}
void parse_ServiceSearchAttributeResponse(uint8_t* packet){
static void parse_ServiceSearchResponse(uint8_t* packet){
uint16_t offset = 3;
uint16_t parameterLength = READ_NET_16(packet,offset);
offset+=2;
uint16_t totalServiceRecordCount = READ_NET_16(packet,offset);
offset+=2;
uint16_t currentServiceRecordCount = READ_NET_16(packet,offset);
offset+=2;
if (currentServiceRecordCount > totalServiceRecordCount){
log_error("CurrentServiceRecordCount is larger then TotalServiceRecordCount.\n");
return;
}
parse_ServiceRecordHandleList(packet+offset, totalServiceRecordCount, currentServiceRecordCount);
offset+=(currentServiceRecordCount * 4);
continuationStateLen = packet[offset];
offset++;
if (continuationStateLen > 16){
log_error("Error parsing ServiceSearchResponse: Number of bytes in continuation state exceedes 16.\n");
return;
}
memcpy(continuationState, packet+offset, continuationStateLen);
offset+=continuationStateLen;
if (parameterLength != offset - 5){
log_error("Error parsing ServiceSearchResponse: wrong size of parameters, number of expected bytes%u, actual number %u.\n", parameterLength, offset);
}
}
static void parse_ServiceAttributeResponse(uint8_t* packet){
uint16_t offset = 3;
uint16_t parameterLength = READ_NET_16(packet,offset);
offset+=2;
// AttributeListByteCount <= mtu
uint16_t attributeListByteCount = READ_NET_16(packet,offset);
offset+=2;
if (attributeListByteCount > mtu){
log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in found attribute list is larger then the MaximumAttributeByteCount.\n");
return;
}
// AttributeLists
parse_AttributeLists(packet+offset, attributeListByteCount);
offset+=attributeListByteCount;
continuationStateLen = packet[offset];
offset++;
if (continuationStateLen > 16){
log_error("Error parsing ServiceAttributeResponse: Number of bytes in continuation state exceedes 16.\n");
return;
}
memcpy(continuationState, packet+offset, continuationStateLen);
offset+=continuationStateLen;
if (parameterLength != offset - 5){
log_error("Error parsing ServiceAttributeResponse: wrong size of parameters, number of expected bytes%u, actual number %u.\n", parameterLength, offset);
}
}
static void parse_ServiceSearchAttributeResponse(uint8_t* packet){
uint16_t offset = 3;
uint16_t parameterLength = READ_NET_16(packet,offset);
offset+=2;
@ -141,7 +253,7 @@ void parse_ServiceSearchAttributeResponse(uint8_t* packet){
offset+=2;
if (attributeListByteCount > mtu){
log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in found attribute list is larger then MTU.\n");
log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in found attribute list is larger then the MaximumAttributeByteCount.\n");
return;
}
@ -164,34 +276,57 @@ void parse_ServiceSearchAttributeResponse(uint8_t* packet){
}
}
void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
uint16_t handle;
printf("l2cap_packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
printf("SDP Client :: l2cap_packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
if (packet_type == L2CAP_DATA_PACKET){
printf("SDP Client :: L2CAP_DATA_PACKET received\n");
uint16_t responseTransactionID = READ_NET_16(packet,1);
if ( responseTransactionID != transactionID){
printf("Missmatching transaction ID, expected %u, found %u.\n", transactionID, responseTransactionID);
return;
}
if (packet[0] == SDP_ServiceSearchAttributeResponse){
parse_ServiceSearchAttributeResponse(packet);
// continuation set or DONE?
if (continuationStateLen == 0){
printf("DONE! All clients already notified.\n");
sdp_client_handle_done(0);
sdp_client_state = INIT;
return;
}
// prepare next request and send
sdp_client_state = W2_SEND;
tryToSend(sdp_cid);
if (packet[0] != SDP_ServiceSearchAttributeResponse
&& packet[0] != SDP_ServiceSearchResponse
&& packet[0] != SDP_ServiceAttributeResponse){
printf("Not a valid PDU ID, expected %u, %u or %u, found %u.\n", SDP_ServiceSearchResponse,
SDP_ServiceAttributeResponse, SDP_ServiceSearchAttributeResponse, packet[0]);
return;
}
PDU_ID = packet[0];
log_info("SDP Client :: PDU ID. %u ,%u", PDU_ID, packet[0]);
switch (PDU_ID){
case SDP_ServiceSearchResponse:
parse_ServiceSearchResponse(packet);
break;
case SDP_ServiceAttributeResponse:
parse_ServiceAttributeResponse(packet);
break;
case SDP_ServiceSearchAttributeResponse:
parse_ServiceSearchAttributeResponse(packet);
break;
default:
log_error("SDP Client :: PDU ID invalid. %u ,%u", PDU_ID, packet[0]);
return;
}
// continuation set or DONE?
if (continuationStateLen == 0){
printf("DONE! All clients already notified.\n");
sdp_client_handle_done(0);
sdp_client_state = INIT;
return;
}
// prepare next request and send
sdp_client_state = W2_SEND;
tryToSend(sdp_cid);
return;
}
@ -230,7 +365,82 @@ void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet,
}
}
static uint16_t setup_sdp_request(uint8_t * data){
static uint16_t setup_ServiceSearchRequest(uint8_t * data){
uint16_t offset = 0;
transactionID++;
// uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest;
data[offset++] = SDP_ServiceSearchRequest;
// uint16_t transactionID
net_store_16(data, offset, transactionID);
offset += 2;
// param legnth
offset += 2;
// parameters:
// ServiceSearchPattern - DES (min 1 UUID, max 12)
uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
memcpy(data + offset, serviceSearchPattern, serviceSearchPatternLen);
offset += serviceSearchPatternLen;
// MaximumAttributeByteCount - uint16_t 0x0007 - 0xffff -> mtu
net_store_16(data, offset, mtu);
offset += 2;
// ContinuationState - uint8_t number of cont. bytes N<=16
data[offset++] = continuationStateLen;
// - N-bytes previous response from server
memcpy(data + offset, continuationState, continuationStateLen);
offset += continuationStateLen;
// uint16_t paramLength
net_store_16(data, 3, offset - 5);
return offset;
}
static uint16_t setup_ServiceAttributeRequest(uint8_t * data){
uint16_t offset = 0;
transactionID++;
// uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest;
data[offset++] = SDP_ServiceAttributeRequest;
// uint16_t transactionID
net_store_16(data, offset, transactionID);
offset += 2;
// param legnth
offset += 2;
// parameters:
// ServiceRecordHandle
net_store_32(data, offset, serviceRecordHandle);
offset += 4;
// MaximumAttributeByteCount - uint16_t 0x0007 - 0xffff -> mtu
net_store_16(data, offset, mtu);
offset += 2;
// AttibuteIDList
uint16_t attributeIDListLen = de_get_len(attributeIDList);
memcpy(data + offset, attributeIDList, attributeIDListLen);
offset += attributeIDListLen;
// ContinuationState - uint8_t number of cont. bytes N<=16
data[offset++] = continuationStateLen;
// - N-bytes previous response from server
memcpy(data + offset, continuationState, continuationStateLen);
offset += continuationStateLen;
// uint16_t paramLength
net_store_16(data, 3, offset - 5);
return offset;
}
static uint16_t setup_ServiceSearchAttributeRequest(uint8_t * data){
uint16_t offset = 0;
transactionID++;
@ -271,4 +481,3 @@ static uint16_t setup_sdp_request(uint8_t * data){
}

View File

@ -53,6 +53,8 @@ and a list of attribute IDs. The remote data is handled by the SDP parser. The
SDP parser delivers attribute values and done event via a registered callback. */
void sdp_client_query(bd_addr_t remote, uint8_t * des_serviceSearchPattern, uint8_t * des_attributeIDList);
void sdp_client_service_attribute_search(bd_addr_t remote, uint32_t search_serviceRecordHandle, uint8_t * des_attributeIDList);
void sdp_client_service_search(bd_addr_t remote, uint8_t * des_serviceSearchPattern);
#if defined __cplusplus
}

View File

@ -52,14 +52,14 @@ typedef enum {
static state_t state = GET_LIST_LENGTH;
static uint16_t attribute_id = 0;
static uint32_t attribute_bytes_received = 0;
static uint32_t attribute_bytes_delivered = 0;
static int list_offset = 0;
static int list_size;
static int record_offset = 0;
static int record_size;
static int attribute_value_size;
static uint16_t attribute_bytes_received = 0;
static uint16_t attribute_bytes_delivered = 0;
static uint16_t list_offset = 0;
static uint16_t list_size;
static uint16_t record_offset = 0;
static uint16_t record_size;
static uint16_t attribute_value_size;
static uint32_t record_handle;
static int record_counter = 0;
static void (*sdp_query_rfcomm_callback)(sdp_query_event_t * event);
@ -117,7 +117,11 @@ void parse(uint8_t eventByte){
list_offset++;
record_offset++;
// printf("BYTE_RECEIVED %02x\n", eventByte);
static int b = 0;
printf("0x%02x, ", eventByte);
if (++b==16) { printf("\n"); b = 0; }
// printf(" parse BYTE_RECEIVED %02x\n", eventByte);
switch(state){
case GET_LIST_LENGTH:
if (!de_state_size(eventByte, &de_header_state)) break;
@ -127,6 +131,8 @@ void parse(uint8_t eventByte){
record_counter = 0;
state = GET_RECORD_LENGTH;
printf("\n -- new record starts --\n"); b = 0;
break;
case GET_RECORD_LENGTH:
@ -203,17 +209,19 @@ void parse(uint8_t eventByte){
}
record_offset = 0;
// printf("parser: List offset %u, list size %u\n", list_offset, list_size);
if (list_offset != list_size){
if (list_size > 0 && list_offset != list_size){
record_counter++;
state = GET_RECORD_LENGTH;
// printf("parser: END_OF_RECORD\n\n");
printf("\n -- new record starts --\n %u %u %u", list_offset, list_size, record_offset); b = 0;
break;
}
list_offset = 0;
de_state_init(&de_header_state);
state = GET_LIST_LENGTH;
record_counter = 0;
// printf("parser: END_OF_RECORD & DONE\n\n\n");
printf("parser: END_OF_RECORD & DONE\n\n\n");
break;
default:
break;
@ -229,6 +237,15 @@ void sdp_parser_init(void){
record_counter = 0;
}
void sdp_parser_init_service_attribute_search(void){
// init
de_state_init(&de_header_state);
state = GET_RECORD_LENGTH;
list_offset = 0;
record_offset = 0;
record_counter = 0;
}
void sdp_parser_handle_chunk(uint8_t * data, uint16_t size){
int i;
for (i=0;i<size;i++){
@ -236,6 +253,27 @@ void sdp_parser_handle_chunk(uint8_t * data, uint16_t size){
}
}
void sdp_parser_init_service_search(void){
record_offset = 0;
}
void sdp_parser_handle_service_search(uint8_t * data, uint16_t total_count, uint16_t record_handle_count){
int i;
for (i=0;i<record_handle_count;i++){
record_handle = READ_NET_32(data, i*4);
record_counter++;
printf("handle[%d/%d] = %d\n", i, record_handle_count, record_handle);
sdp_query_service_record_handle_event_t service_record_handle_event = {
SDP_QUERY_SERVICE_RECORD_HANDLE,
total_count,
record_counter,
record_handle
};
(*sdp_query_rfcomm_callback)((sdp_query_event_t*)&service_record_handle_event);
}
}
void sdp_parser_handle_done(uint8_t status){
sdp_query_complete_event_t complete_event = {
SDP_QUERY_COMPLETE,

View File

@ -46,6 +46,7 @@
#include <string.h>
#include <btstack/sdp_util.h>
#include <btstack/utils.h>
#if defined __cplusplus
extern "C" {
@ -86,8 +87,21 @@ typedef struct sdp_query_attribute_value_event {
} sdp_query_attribute_value_event_t;
typedef struct sdp_query_service_record_handle_event {
uint8_t type;
uint16_t total_count;
uint16_t current_count;
uint32_t record_handle;
} sdp_query_service_record_handle_event_t;
void sdp_parser_init(void);
void sdp_parser_init_service_attribute_search(void);
void sdp_parser_handle_chunk(uint8_t * data, uint16_t size);
void sdp_parser_init_service_search(void);
void sdp_parser_handle_service_search(uint8_t * data, uint16_t total_count, uint16_t record_handle_count);
void sdp_parser_handle_done(uint8_t status);
// Registers a callback to receive attribute value data and parse complete event.

View File

@ -142,7 +142,7 @@ static void test_attribute_value_event(sdp_query_attribute_value_event_t* event)
}
static void handle_general_sdp_parser_event(sdp_query_event_t * event){
static void handle_sdp_parser_event(sdp_query_event_t * event){
sdp_query_attribute_value_event_t * ve;
sdp_query_complete_event_t * ce;
@ -176,12 +176,12 @@ TEST_GROUP(SDPClient){
attribute_value = (uint8_t*) malloc(attribute_value_buffer_size);
record_id = -1;
sdp_parser_init();
sdp_parser_register_callback(handle_general_sdp_parser_event);
sdp_parser_register_callback(handle_sdp_parser_event);
}
};
TEST(SDPClient, QueryRFCOMMWithMacOSXData){
TEST(SDPClient, QueryWithMacOSXData){
uint16_t expected_last_attribute_id = 0xffff;
uint16_t expected_last_record_id = 8;
uint8_t expected_attribute_value[3] = {0x09, 0x00, 0x05};