mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-28 19:20:54 +00:00
new files for SDP queries
This commit is contained in:
parent
40943ff4e0
commit
d3d614de49
@ -28,6 +28,9 @@ BTdaemon_SOURCES = $(libBTstack_SOURCES) \
|
||||
$(remote_device_db_sources) \
|
||||
rfcomm.c \
|
||||
sdp.c \
|
||||
sdp_client.c \
|
||||
sdp_parser.c \
|
||||
sdp_query_rfcomm.c \
|
||||
$(springboard_access_sources)
|
||||
|
||||
# use $(CC) for Objective-C files
|
||||
|
@ -22,6 +22,9 @@ BTdaemon_FILES = $(libBTstack_FILES) \
|
||||
remote_device_db_iphone.m \
|
||||
rfcomm.c \
|
||||
sdp.c \
|
||||
sdp_client.c \
|
||||
sdp_parser.c \
|
||||
sdp_query_rfcomm.c \
|
||||
../SpringBoardAccess/SpringBoardAccess.c
|
||||
BTdaemon_CFLAGS = -I../include -I..
|
||||
BTdaemon_FRAMEWORKS = IOKit
|
||||
|
238
src/sdp_client.c
Normal file
238
src/sdp_client.c
Normal file
@ -0,0 +1,238 @@
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// minimal setup for SDP client over USB or UART
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include "sdp_client.h"
|
||||
|
||||
#include <btstack/hci_cmds.h>
|
||||
|
||||
#include "l2cap.h"
|
||||
#include "sdp_parser.h"
|
||||
#include "sdp.h"
|
||||
#include "debug.h"
|
||||
|
||||
typedef enum {
|
||||
INIT, W4_CONNECT, W2_SEND, W4_RESPONSE
|
||||
} sdp_client_state_t;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
// SDP Client Query
|
||||
static uint16_t mtu;
|
||||
static uint16_t sdp_cid = 0x40;
|
||||
static uint8_t * serviceSearchPattern;
|
||||
static uint8_t * attributeIDList;
|
||||
static uint16_t transactionID = 0;
|
||||
static uint8_t continuationState[16];
|
||||
static uint8_t continuationStateLen;
|
||||
|
||||
static sdp_client_state_t sdp_client_state;
|
||||
|
||||
void sdp_client_handle_done(uint8_t status){
|
||||
if (status == 0){
|
||||
l2cap_disconnect_internal(sdp_cid, 0);
|
||||
}
|
||||
sdp_parser_handle_done(status);
|
||||
}
|
||||
|
||||
// TODO: inline if not needed
|
||||
void parse_AttributeLists(uint8_t* packet, uint16_t length){
|
||||
sdp_parser_handle_chunk(packet, length);
|
||||
}
|
||||
|
||||
|
||||
void hexdump2(void *data, int size){
|
||||
int i;
|
||||
for (i=0; i<size;i++){
|
||||
printf("%02X ", ((uint8_t *)data)[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void sdp_client_query(bd_addr_t remote, uint8_t * des_serviceSearchPattern, uint8_t * des_attributeIDList){
|
||||
|
||||
serviceSearchPattern = des_serviceSearchPattern;
|
||||
attributeIDList = des_attributeIDList;
|
||||
continuationStateLen = 0;
|
||||
|
||||
sdp_client_state = W4_CONNECT;
|
||||
l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu());
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
printf("tryToSend channel %x, size %u\n", channel, request_len);
|
||||
|
||||
int err = l2cap_send_prepared(channel, request_len);
|
||||
switch (err){
|
||||
case 0:
|
||||
// packet is sent prepare next one
|
||||
printf("l2cap_send_internal() -> OK\n\r");
|
||||
sdp_client_state = W4_RESPONSE;
|
||||
break;
|
||||
case BTSTACK_ACL_BUFFERS_FULL:
|
||||
printf("l2cap_send_internal() ->BTSTACK_ACL_BUFFERS_FULL\n\r");
|
||||
break;
|
||||
default:
|
||||
printf("l2cap_send_internal() -> err %d\n\r", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void parse_ServiceSearchAttributeResponse(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 MTU.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// AttributeLists
|
||||
parse_AttributeLists(packet+offset, attributeListByteCount);
|
||||
offset+=attributeListByteCount;
|
||||
|
||||
continuationStateLen = packet[offset];
|
||||
offset++;
|
||||
|
||||
if (continuationStateLen > 16){
|
||||
log_error("Error parsing ServiceSearchAttributeResponse: 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 ServiceSearchAttributeResponse: wrong size of parameters, number of expected bytes%u, actual number %u.\n", parameterLength, offset);
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
if (packet_type == L2CAP_DATA_PACKET){
|
||||
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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet_type != HCI_EVENT_PACKET) return;
|
||||
|
||||
switch(packet[0]){
|
||||
|
||||
case L2CAP_EVENT_CHANNEL_OPENED:
|
||||
if (sdp_client_state != W4_CONNECT) break;
|
||||
|
||||
// data: event (8), len(8), status (8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16), local_mtu(16), remote_mtu(16)
|
||||
if (packet[2]) {
|
||||
printf("Connection failed.\n\r");
|
||||
sdp_client_handle_done(packet[2]);
|
||||
break;
|
||||
}
|
||||
sdp_cid = channel;
|
||||
mtu = READ_BT_16(packet, 17);
|
||||
handle = READ_BT_16(packet, 9);
|
||||
printf("Connected, cid %x, mtu %u.\n\r", sdp_cid, mtu);
|
||||
|
||||
sdp_client_state = W2_SEND;
|
||||
tryToSend(sdp_cid);
|
||||
break;
|
||||
case L2CAP_EVENT_CREDITS:
|
||||
case DAEMON_EVENT_HCI_PACKET_SENT:
|
||||
tryToSend(sdp_cid);
|
||||
break;
|
||||
case L2CAP_EVENT_CHANNEL_CLOSED:
|
||||
printf("Channel closed.\n\r");
|
||||
if (sdp_client_state == INIT) break;
|
||||
sdp_client_handle_done(SDP_QUERY_INCOMPLETE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t setup_sdp_request(uint8_t * data){
|
||||
|
||||
uint16_t offset = 0;
|
||||
transactionID++;
|
||||
// uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest;
|
||||
data[offset++] = SDP_ServiceSearchAttributeRequest;
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
10
src/sdp_client.h
Normal file
10
src/sdp_client.h
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// minimal setup for SDP client over USB or UART
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
#include <btstack/utils.h>
|
||||
|
||||
void sdp_client_query(bd_addr_t remote, uint8_t * des_serviceSearchPattern, uint8_t * des_attributeIDList);
|
202
src/sdp_parser.c
Normal file
202
src/sdp_parser.c
Normal file
@ -0,0 +1,202 @@
|
||||
#include "sdp_parser.h"
|
||||
#include "debug.h"
|
||||
|
||||
typedef enum {
|
||||
GET_LIST_LENGTH = 1,
|
||||
GET_RECORD_LENGTH,
|
||||
GET_ATTRIBUTE_ID_HEADER_LENGTH,
|
||||
GET_ATTRIBUTE_ID,
|
||||
GET_ATTRIBUTE_VALUE_LENGTH,
|
||||
GET_ATTRIBUTE_VALUE
|
||||
} state_t;
|
||||
|
||||
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 int record_counter = 0;
|
||||
|
||||
static void (*sdp_query_rfcomm_callback)(sdp_parser_event_t * event);
|
||||
|
||||
// Low level parser
|
||||
static de_state_t de_header_state;
|
||||
|
||||
|
||||
void de_state_init(de_state_t * state){
|
||||
state->in_state_GET_DE_HEADER_LENGTH = 1;
|
||||
state->addon_header_bytes = 0;
|
||||
state->de_size = 0;
|
||||
state->de_offset = 0;
|
||||
}
|
||||
|
||||
int de_state_size(uint8_t eventByte, de_state_t *de_state){
|
||||
if (de_state->in_state_GET_DE_HEADER_LENGTH){
|
||||
de_state->addon_header_bytes = de_get_header_size(&eventByte) - 1;
|
||||
de_state->de_size = 0;
|
||||
de_state->de_offset = 0;
|
||||
|
||||
if (de_state->addon_header_bytes == 0){
|
||||
de_state->de_size = de_get_data_size(&eventByte);
|
||||
if (de_state->de_size == 0) {
|
||||
log_error(" ERROR: ID size is zero\n\r");
|
||||
}
|
||||
// printf("Data element payload is %d bytes.\n", de_state->de_size);
|
||||
return 1;
|
||||
}
|
||||
de_state->in_state_GET_DE_HEADER_LENGTH = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (de_state->addon_header_bytes > 0){
|
||||
de_state->de_size = (de_state->de_size << 8) | eventByte;
|
||||
de_state->addon_header_bytes--;
|
||||
}
|
||||
if (de_state->addon_header_bytes > 0) return 0;
|
||||
// printf("Data element payload is %d bytes.\n", de_state->de_size);
|
||||
de_state->in_state_GET_DE_HEADER_LENGTH = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dummy_notify(sdp_parser_event_t* event){}
|
||||
|
||||
void sdp_parser_register_callback(void (*sdp_callback)(sdp_parser_event_t* event)){
|
||||
sdp_query_rfcomm_callback = dummy_notify;
|
||||
if (sdp_callback != NULL){
|
||||
sdp_query_rfcomm_callback = sdp_callback;
|
||||
}
|
||||
}
|
||||
|
||||
void parse(uint8_t eventByte){
|
||||
// count all bytes
|
||||
list_offset++;
|
||||
record_offset++;
|
||||
|
||||
// printf("BYTE_RECEIVED %02x\n", eventByte);
|
||||
switch(state){
|
||||
case GET_LIST_LENGTH:
|
||||
if (!de_state_size(eventByte, &de_header_state)) break;
|
||||
list_offset = de_header_state.de_offset;
|
||||
list_size = de_header_state.de_size;
|
||||
// printf("parser: List offset %u, list size %u\n", list_offset, list_size);
|
||||
|
||||
record_counter = 0;
|
||||
state = GET_RECORD_LENGTH;
|
||||
break;
|
||||
|
||||
case GET_RECORD_LENGTH:
|
||||
// check size
|
||||
if (!de_state_size(eventByte, &de_header_state)) break;
|
||||
// printf("parser: Record payload is %d bytes.\n", de_header_state.de_size);
|
||||
record_offset = de_header_state.de_offset;
|
||||
record_size = de_header_state.de_size;
|
||||
state = GET_ATTRIBUTE_ID_HEADER_LENGTH;
|
||||
break;
|
||||
|
||||
case GET_ATTRIBUTE_ID_HEADER_LENGTH:
|
||||
if (!de_state_size(eventByte, &de_header_state)) break;
|
||||
attribute_id = 0;
|
||||
// printf("ID data is stored in %d bytes.\n", de_header_state.de_size);
|
||||
state = GET_ATTRIBUTE_ID;
|
||||
break;
|
||||
|
||||
case GET_ATTRIBUTE_ID:
|
||||
attribute_id = (attribute_id << 8) | eventByte;
|
||||
de_header_state.de_size--;
|
||||
if (de_header_state.de_size > 0) break;
|
||||
// printf("parser: Attribute ID: %04x.\n", attribute_id);
|
||||
|
||||
state = GET_ATTRIBUTE_VALUE_LENGTH;
|
||||
attribute_bytes_received = 0;
|
||||
attribute_bytes_delivered = 0;
|
||||
attribute_value_size = 0;
|
||||
de_state_init(&de_header_state);
|
||||
break;
|
||||
|
||||
case GET_ATTRIBUTE_VALUE_LENGTH:
|
||||
attribute_bytes_received++;
|
||||
sdp_parser_attribute_value_event_t attribute_value_event = {
|
||||
.type = SDP_PARSER_ATTRIBUTE_VALUE,
|
||||
.record_id = record_counter,
|
||||
.attribute_id = attribute_id,
|
||||
.attribute_length = attribute_value_size,
|
||||
.data_offset = attribute_bytes_delivered++,
|
||||
.data = eventByte
|
||||
};
|
||||
(*sdp_query_rfcomm_callback)((sdp_parser_event_t*)&attribute_value_event);
|
||||
|
||||
if (!de_state_size(eventByte, &de_header_state)) break;
|
||||
|
||||
attribute_value_size = de_header_state.de_size + attribute_bytes_received;
|
||||
|
||||
state = GET_ATTRIBUTE_VALUE;
|
||||
break;
|
||||
|
||||
case GET_ATTRIBUTE_VALUE:
|
||||
attribute_bytes_received++;
|
||||
|
||||
sdp_parser_attribute_value_event_t attribute_value_event1 = {
|
||||
.type = SDP_PARSER_ATTRIBUTE_VALUE,
|
||||
.record_id = record_counter,
|
||||
.attribute_id = attribute_id,
|
||||
.attribute_length = attribute_value_size,
|
||||
.data_offset = attribute_bytes_delivered++,
|
||||
.data = eventByte
|
||||
};
|
||||
(*sdp_query_rfcomm_callback)((sdp_parser_event_t*)&attribute_value_event1);
|
||||
// printf("paser: attribute_bytes_received %u, attribute_value_size %u\n", attribute_bytes_received, attribute_value_size);
|
||||
|
||||
if (attribute_bytes_received < attribute_value_size) break;
|
||||
// printf("parser: Record offset %u, record size %u\n", record_offset, record_size);
|
||||
if (record_offset != record_size){
|
||||
state = GET_ATTRIBUTE_ID_HEADER_LENGTH;
|
||||
// printf("Get next attribute\n");
|
||||
break;
|
||||
}
|
||||
record_offset = 0;
|
||||
// printf("parser: List offset %u, list size %u\n", list_offset, list_size);
|
||||
if (list_offset != list_size){
|
||||
record_counter++;
|
||||
state = GET_RECORD_LENGTH;
|
||||
// printf("parser: END_OF_RECORD\n\n");
|
||||
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");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sdp_parser_init(void){
|
||||
// init
|
||||
de_state_init(&de_header_state);
|
||||
state = GET_LIST_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++){
|
||||
parse(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void sdp_parser_handle_done(uint8_t status){
|
||||
sdp_parser_complete_event_t complete_event = {
|
||||
.type = SDP_PARSER_COMPLETE,
|
||||
.status = status
|
||||
};
|
||||
(*sdp_query_rfcomm_callback)((sdp_parser_event_t*)&complete_event);
|
||||
}
|
50
src/sdp_parser.h
Normal file
50
src/sdp_parser.h
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// minimal setup for SDP client over USB or UART
|
||||
//
|
||||
//*****************************************************************************
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <btstack/sdp_util.h>
|
||||
|
||||
typedef struct de_state {
|
||||
uint8_t in_state_GET_DE_HEADER_LENGTH ;
|
||||
uint32_t addon_header_bytes;
|
||||
uint32_t de_size;
|
||||
uint32_t de_offset;
|
||||
} de_state_t;
|
||||
|
||||
typedef enum sdp_parser_event_type {
|
||||
SDP_PARSER_ATTRIBUTE_VALUE = 1,
|
||||
SDP_PARSER_COMPLETE,
|
||||
} sdp_parser_event_type_t;
|
||||
|
||||
typedef struct sdp_parser_event {
|
||||
uint8_t type;
|
||||
} sdp_parser_event_t;
|
||||
|
||||
typedef struct sdp_parser_attribute_value_event {
|
||||
uint8_t type;
|
||||
int record_id;
|
||||
uint16_t attribute_id;
|
||||
uint32_t attribute_length;
|
||||
int data_offset;
|
||||
uint8_t data;
|
||||
} sdp_parser_attribute_value_event_t;
|
||||
|
||||
typedef struct sdp_parser_complete_event {
|
||||
uint8_t type;
|
||||
uint8_t status; // 0 == OK
|
||||
} sdp_parser_complete_event_t;
|
||||
|
||||
void de_state_init(de_state_t * state);
|
||||
int de_state_size(uint8_t eventByte, de_state_t *de_state);
|
||||
|
||||
void sdp_parser_init();
|
||||
void sdp_parser_handle_chunk(uint8_t * data, uint16_t size);
|
||||
void sdp_parser_handle_done(uint8_t status);
|
||||
void sdp_parser_register_callback(void (*sdp_callback)(sdp_parser_event_t* event));
|
267
src/sdp_query_rfcomm.c
Normal file
267
src/sdp_query_rfcomm.c
Normal file
@ -0,0 +1,267 @@
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// minimal setup for SDP client over USB or UART
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
#include "sdp_query_rfcomm.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <btstack/hci_cmds.h>
|
||||
#include <btstack/sdp_util.h>
|
||||
|
||||
#include "sdp_client.h"
|
||||
|
||||
static void dummy_notify_app(sdp_query_rfcomm_event_t* event, void * context);
|
||||
|
||||
typedef enum {
|
||||
GET_PROTOCOL_LIST_LENGTH = 1,
|
||||
GET_PROTOCOL_LENGTH,
|
||||
GET_PROTOCOL_ID_HEADER_LENGTH,
|
||||
GET_PROTOCOL_ID,
|
||||
GET_PROTOCOL_VALUE_LENGTH,
|
||||
GET_PROTOCOL_VALUE
|
||||
} pdl_state_t;
|
||||
|
||||
|
||||
// higher layer query - get rfcomm channel and name
|
||||
|
||||
#define SDP_SERVICE_NAME_LEN 20
|
||||
|
||||
const uint8_t des_attributeIDList[] = { 0x35, 0x05, 0x0A, 0x00, 0x01, 0x01, 0x00}; // Arribute: 0x0001 - 0x0100
|
||||
|
||||
static uint8_t des_serviceSearchPattern[5] = {0x35, 0x03, 0x19, 0x00, 0x00};
|
||||
|
||||
static uint8_t sdp_service_name[SDP_SERVICE_NAME_LEN+1];
|
||||
static uint8_t sdp_rfcom_channel_nr = 0;
|
||||
static uint8_t sdp_service_name_header_size;
|
||||
|
||||
static pdl_state_t pdl_state = GET_PROTOCOL_LIST_LENGTH;
|
||||
static uint32_t protocol_value_bytes_received = 0;
|
||||
static uint16_t protocol_id = 0;
|
||||
static int protocol_offset;
|
||||
static int protocol_size;
|
||||
static int protocol_id_bytes_to_read;
|
||||
static int protocol_value_size;
|
||||
static de_state_t de_header_state;
|
||||
static de_state_t sn_de_header_state;
|
||||
|
||||
static void *sdp_app_context;
|
||||
static void (*sdp_app_callback)(sdp_query_rfcomm_event_t * event, void * context) = dummy_notify_app;
|
||||
|
||||
//
|
||||
|
||||
static void dummy_notify_app(sdp_query_rfcomm_event_t* event, void * context){}
|
||||
|
||||
void sdp_query_rfcomm_register_callback(void (*sdp_callback)(sdp_query_rfcomm_event_t* event, void * context), void * context){
|
||||
sdp_app_callback = dummy_notify_app;
|
||||
if (sdp_callback != NULL){
|
||||
sdp_app_callback = sdp_callback;
|
||||
}
|
||||
sdp_app_context = context;
|
||||
}
|
||||
|
||||
void handleProtocolDescriptorListData(uint32_t attribute_value_length, uint32_t data_offset, uint8_t data){
|
||||
// init state on first byte
|
||||
if (data_offset == 0){
|
||||
pdl_state = GET_PROTOCOL_LIST_LENGTH;
|
||||
}
|
||||
|
||||
// printf("handleProtocolDescriptorListData (%u,%u) %02x\n", attribute_value_length, data_offset, data);
|
||||
|
||||
switch(pdl_state){
|
||||
|
||||
case GET_PROTOCOL_LIST_LENGTH:
|
||||
if (!de_state_size(data, &de_header_state)) break;
|
||||
// printf(" query: PD List payload is %d bytes.\n", de_header_state.de_size);
|
||||
// printf(" query: PD List offset %u, list size %u\n", de_header_state.de_offset, de_header_state.de_size);
|
||||
|
||||
pdl_state = GET_PROTOCOL_LENGTH;
|
||||
break;
|
||||
|
||||
case GET_PROTOCOL_LENGTH:
|
||||
// check size
|
||||
if (!de_state_size(data, &de_header_state)) break;
|
||||
// printf(" query: PD Record payload is %d bytes.\n", de_header_state.de_size);
|
||||
|
||||
// cache protocol info
|
||||
protocol_offset = de_header_state.de_offset;
|
||||
protocol_size = de_header_state.de_size;
|
||||
|
||||
pdl_state = GET_PROTOCOL_ID_HEADER_LENGTH;
|
||||
break;
|
||||
|
||||
case GET_PROTOCOL_ID_HEADER_LENGTH:
|
||||
protocol_offset++;
|
||||
if (!de_state_size(data, &de_header_state)) break;
|
||||
|
||||
protocol_id = 0;
|
||||
protocol_id_bytes_to_read = de_header_state.de_size;
|
||||
// printf(" query: ID data is stored in %d bytes.\n", protocol_id_bytes_to_read);
|
||||
pdl_state = GET_PROTOCOL_ID;
|
||||
|
||||
break;
|
||||
|
||||
case GET_PROTOCOL_ID:
|
||||
protocol_offset++;
|
||||
|
||||
protocol_id = (protocol_id << 8) | data;
|
||||
protocol_id_bytes_to_read--;
|
||||
if (protocol_id_bytes_to_read > 0) break;
|
||||
|
||||
// printf(" query: Protocol ID: %04x.\n", protocol_id);
|
||||
|
||||
if (protocol_offset >= protocol_size){
|
||||
pdl_state = GET_PROTOCOL_LENGTH;
|
||||
// printf(" query: Get next protocol\n");
|
||||
break;
|
||||
}
|
||||
|
||||
pdl_state = GET_PROTOCOL_VALUE_LENGTH;
|
||||
protocol_value_bytes_received = 0;
|
||||
break;
|
||||
|
||||
case GET_PROTOCOL_VALUE_LENGTH:
|
||||
protocol_offset++;
|
||||
|
||||
if (!de_state_size(data, &de_header_state)) break;
|
||||
|
||||
protocol_value_size = de_header_state.de_size;
|
||||
pdl_state = GET_PROTOCOL_VALUE;
|
||||
sdp_rfcom_channel_nr = 0;
|
||||
break;
|
||||
|
||||
case GET_PROTOCOL_VALUE:
|
||||
protocol_offset++;
|
||||
protocol_value_bytes_received++;
|
||||
|
||||
// printf(" query: protocol_value_bytes_received %u, protocol_value_size %u\n", protocol_value_bytes_received, protocol_value_size);
|
||||
|
||||
if (protocol_value_bytes_received < protocol_value_size) break;
|
||||
|
||||
if (protocol_id == 0x0003){
|
||||
// printf("\n\n ******* Data ***** %02x\n\n", data);
|
||||
sdp_rfcom_channel_nr = data;
|
||||
}
|
||||
|
||||
// printf(" query: protocol done\n");
|
||||
// printf(" query: Protocol offset %u, protocol size %u\n", protocol_offset, protocol_size);
|
||||
|
||||
if (protocol_offset >= protocol_size) {
|
||||
pdl_state = GET_PROTOCOL_LENGTH;
|
||||
break;
|
||||
|
||||
}
|
||||
pdl_state = GET_PROTOCOL_ID_HEADER_LENGTH;
|
||||
// printf(" query: Get next protocol\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleServiceNameData(uint32_t attribute_value_length, uint32_t data_offset, uint8_t data){
|
||||
|
||||
// Get Header Len
|
||||
if (data_offset == 0){
|
||||
de_state_size(data, &sn_de_header_state);
|
||||
sdp_service_name_header_size = sn_de_header_state.addon_header_bytes + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Header
|
||||
if (data_offset < sdp_service_name_header_size){
|
||||
de_state_size(data, &sn_de_header_state);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process payload
|
||||
int name_len = attribute_value_length - sdp_service_name_header_size;
|
||||
int name_pos = data_offset - sdp_service_name_header_size;
|
||||
|
||||
if (name_pos < SDP_SERVICE_NAME_LEN){
|
||||
sdp_service_name[name_pos] = data;
|
||||
name_pos++;
|
||||
}
|
||||
|
||||
// sdp_rfcom_channel_nr is stored in Attribute 0x0004 which is received before this 0x0100
|
||||
if (name_pos >= SDP_SERVICE_NAME_LEN || name_pos >= name_len){
|
||||
sdp_service_name[name_pos] = 0;
|
||||
}
|
||||
// notify on last char
|
||||
if (data_offset == attribute_value_length - 1 && sdp_rfcom_channel_nr!=0){
|
||||
sdp_query_rfcomm_service_event_t value_event = {
|
||||
.type = SDP_QUERY_RFCOMM_SERVICE,
|
||||
.service_name = (uint8_t *) sdp_service_name,
|
||||
.rfcomm_channel_nr = sdp_rfcom_channel_nr
|
||||
};
|
||||
printf("Service found %s\n", sdp_service_name);
|
||||
(*sdp_app_callback)((sdp_query_rfcomm_event_t*)&value_event, sdp_app_context);
|
||||
sdp_rfcom_channel_nr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void handle_sdp_parser_event(sdp_parser_event_t * event){
|
||||
// printf(" CN - [ RID, AID, ALen, VOffset] : [%d, %04x, %u, %u, %u] BYTE %02x\n",
|
||||
// record_id, attribute_id, attribute_length, data_offset, *src);
|
||||
// return ;
|
||||
sdp_parser_attribute_value_event_t * ve;
|
||||
sdp_parser_complete_event_t * ce;
|
||||
switch (event->type){
|
||||
case SDP_PARSER_ATTRIBUTE_VALUE:
|
||||
ve = (sdp_parser_attribute_value_event_t*) event;
|
||||
|
||||
switch (ve->attribute_id){
|
||||
case SDP_ProtocolDescriptorList:
|
||||
// find rfcomm channel
|
||||
handleProtocolDescriptorListData(ve->attribute_length, ve->data_offset, ve->data);
|
||||
break;
|
||||
case 0x0100:
|
||||
// get service name
|
||||
handleServiceNameData(ve->attribute_length, ve->data_offset, ve->data);
|
||||
break;
|
||||
default:
|
||||
// give up
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SDP_PARSER_COMPLETE:
|
||||
ce = (sdp_parser_complete_event_t*) event;
|
||||
sdp_query_rfcomm_complete_event_t c_event = {
|
||||
.type = SDP_QUERY_COMPLETE,
|
||||
.status = ce->status
|
||||
};
|
||||
(*sdp_app_callback)((sdp_query_rfcomm_event_t*)&c_event, sdp_app_context);
|
||||
break;
|
||||
}
|
||||
// insert higher level code HERE
|
||||
}
|
||||
|
||||
void sdp_query_rfcomm_init(){
|
||||
// init
|
||||
de_state_init(&de_header_state);
|
||||
de_state_init(&sn_de_header_state);
|
||||
pdl_state = GET_PROTOCOL_LIST_LENGTH;
|
||||
protocol_offset = 0;
|
||||
sdp_parser_register_callback(handle_sdp_parser_event);
|
||||
}
|
||||
|
||||
|
||||
void sdp_query_rfcomm_channel_and_name_for_service_with_service_search_pattern(bd_addr_t remote, uint8_t * serviceSearchPattern){
|
||||
sdp_parser_init();
|
||||
sdp_query_rfcomm_init();
|
||||
sdp_client_query(remote, serviceSearchPattern, (uint8_t*)&des_attributeIDList[0]);
|
||||
}
|
||||
|
||||
void sdp_query_rfcomm_channel_and_name_for_service_with_uuid(bd_addr_t remote, uint16_t uuid){
|
||||
net_store_16(des_serviceSearchPattern, 3, uuid);
|
||||
sdp_query_rfcomm_channel_and_name_for_service_with_service_search_pattern(remote, (uint8_t*)des_serviceSearchPattern);
|
||||
}
|
||||
|
||||
|
31
src/sdp_query_rfcomm.h
Normal file
31
src/sdp_query_rfcomm.h
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
//*****************************************************************************
|
||||
//
|
||||
// minimal setup for SDP client over USB or UART
|
||||
//
|
||||
//*****************************************************************************
|
||||
|
||||
#include <btstack/utils.h>
|
||||
#include "sdp_parser.h"
|
||||
|
||||
|
||||
typedef struct sdp_query_rfcomm_event {
|
||||
uint8_t type;
|
||||
} sdp_query_rfcomm_event_t;
|
||||
|
||||
typedef struct sdp_query_rfcomm_service_event {
|
||||
uint8_t type;
|
||||
uint8_t rfcomm_channel_nr;
|
||||
uint8_t * service_name;
|
||||
} sdp_query_rfcomm_service_event_t;
|
||||
|
||||
typedef struct sdp_query_rfcomm_complete_event {
|
||||
uint8_t type;
|
||||
uint8_t status; // 0 == OK
|
||||
} sdp_query_rfcomm_complete_event_t;
|
||||
|
||||
|
||||
void sdp_query_rfcomm_init(void);
|
||||
void sdp_query_rfcomm_channel_and_name_for_service_with_uuid(bd_addr_t remote, uint16_t uuid);
|
||||
void sdp_query_rfcomm_channel_and_name_for_service_with_service_search_pattern(bd_addr_t remote, uint8_t * des_serviceSearchPattern);
|
||||
void sdp_query_rfcomm_register_callback(void(*sdp_app_callback)(sdp_query_rfcomm_event_t * event, void * context), void * context);
|
Loading…
x
Reference in New Issue
Block a user