mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-31 19:20:26 +00:00
manual: added SDP query examples
This commit is contained in:
parent
cf18d7621d
commit
82d6daf181
@ -8,17 +8,17 @@ list_of_groups = ["Hello World", "GAP", "SDP Queries", "SPP Server", "BNEP/PAN",
|
|||||||
|
|
||||||
# Defines which examples belong to a group. Example is defined as [example file, example title].
|
# Defines which examples belong to a group. Example is defined as [example file, example title].
|
||||||
list_of_examples = {
|
list_of_examples = {
|
||||||
"Hello World" : [["led_counter"]],
|
# "Hello World" : [["led_counter"]],
|
||||||
"GAP" : [["gap_inquiry"]],
|
# "GAP" : [["gap_inquiry"]],
|
||||||
"SDP Queries" :[["sdp_general_query"],
|
"SDP Queries" :[#["sdp_general_query"],
|
||||||
["sdp_bnep_query"]
|
["sdp_bnep_query"]
|
||||||
],
|
],
|
||||||
"SPP Server" : [["spp_counter"],
|
# "SPP Server" : [["spp_counter"],
|
||||||
["spp_flowcontrol"]],
|
# ["spp_flowcontrol"]],
|
||||||
"BNEP/PAN" : [["panu_demo"]],
|
# "BNEP/PAN" : [["panu_demo"]],
|
||||||
"Low Energy" : [["gatt_browser"],
|
# "Low Energy" : [["gatt_browser"],
|
||||||
["le_counter"]],
|
# ["le_counter"]],
|
||||||
"Dual Mode" : [["spp_and_le_counter"]],
|
# "Dual Mode" : [["spp_and_le_counter"]],
|
||||||
}
|
}
|
||||||
|
|
||||||
lst_header = """
|
lst_header = """
|
||||||
@ -51,7 +51,7 @@ examples_header = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
example_item = """
|
example_item = """
|
||||||
\item \emph{EXAMPLE_TITLE}: EXAMPLE_DESC, see Section \\ref{example:EXAMPLE_LABLE}.
|
\item \emph{EXAMPLE_TITLE}: EXAMPLE_DESC, in Section \\ref{example:EXAMPLE_LABLE}.
|
||||||
"""
|
"""
|
||||||
example_section = """
|
example_section = """
|
||||||
\subsection{EXAMPLE_TITLE: EXAMPLE_DESC}
|
\subsection{EXAMPLE_TITLE: EXAMPLE_DESC}
|
||||||
@ -59,7 +59,7 @@ example_section = """
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
example_subsection = """
|
example_subsection = """
|
||||||
\subsubsection{LISTING_CAPTION}
|
\subsubsection{SECTION_TITLE}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
listing_start = """
|
listing_start = """
|
||||||
@ -162,15 +162,15 @@ def writeListings(aout, infile_name, ref_prefix):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# detect @section
|
# detect @section
|
||||||
section_parts = re.match('.*(@section)\s*(.*)\s*(\*?/?)\n',line)
|
section_parts = re.match('.*(@section)\s*(.*)(:?\s*.?)\*?/?\n',line)
|
||||||
if section_parts:
|
if section_parts:
|
||||||
aout.write("\n" + example_subsection.replace("LISTING_CAPTION", section_parts.group(2)))
|
aout.write("\n" + example_subsection.replace("SECTION_TITLE", section_parts.group(2)))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# detect @subsection
|
# detect @subsection
|
||||||
subsection_parts = re.match('.*(@subsection)\s*(.*)\s*(\*?/?)\n',line)
|
subsection_parts = re.match('.*(@section)\s*(.*)(:?\s*.?)\*?/?\n',line)
|
||||||
if section_parts:
|
if section_parts:
|
||||||
subsubsection = example_subsection.replace("LISTING_CAPTION", section_parts.group(2)).replace('section', 'subsection')
|
subsubsection = example_subsection.replace("SECTION_TITLE", section_parts.group(2)).replace('section', 'subsection')
|
||||||
aout.write("\n" + subsubsection)
|
aout.write("\n" + subsubsection)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ def writeListings(aout, infile_name, ref_prefix):
|
|||||||
itemize_block = None
|
itemize_block = None
|
||||||
else:
|
else:
|
||||||
if isEmptyCommentLine(line):
|
if isEmptyCommentLine(line):
|
||||||
text_block = text_block + "\n"
|
text_block = text_block + "\n\n"
|
||||||
else:
|
else:
|
||||||
# finish text
|
# finish text
|
||||||
aout.write(text_block)
|
aout.write(text_block)
|
||||||
|
@ -36,8 +36,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// *****************************************************************************
|
// *****************************************************************************
|
||||||
/* EXAMPLE_START(sdp_bnep_query): Minimal setup for SDP client over USB or UART
|
/* EXAMPLE_START(sdp_bnep_query): Dump remote BNEP PAN protocol UUID and L2CAP PSM.
|
||||||
*
|
*
|
||||||
|
* @text The example shows how the SDP Client is used to get the BNEP
|
||||||
|
* service records on a remote device. It extracts the remote BNEP PAN protocol
|
||||||
|
* UUID and the L2CAP PSM, which are needed to connect to a remote BNEP service.
|
||||||
*/
|
*/
|
||||||
// *****************************************************************************
|
// *****************************************************************************
|
||||||
|
|
||||||
@ -77,6 +80,39 @@ static void assertBuffer(int size){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @section SDP Client Setup
|
||||||
|
*
|
||||||
|
* @text To receive SDP query events you must register a
|
||||||
|
* callback, i.e. query handler, with the SPD parser, as shown in
|
||||||
|
* Listing SDPClientInit. Via this handler, the SDP client will receive events:
|
||||||
|
* - SDP_QUERY_ATTRIBUTE_VALUE containing the results of the query in chunks,
|
||||||
|
* - SDP_QUERY_COMPLETE reporting the status and the end of the query.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* LISTING_START(SDPClientInit): SDP client setup */
|
||||||
|
static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||||
|
static void handle_sdp_client_query_result(sdp_query_event_t * event);
|
||||||
|
|
||||||
|
static void sdp_client_init(){
|
||||||
|
// init L2CAP
|
||||||
|
l2cap_init();
|
||||||
|
l2cap_register_packet_handler(packet_handler);
|
||||||
|
|
||||||
|
sdp_parser_init();
|
||||||
|
sdp_parser_register_callback(handle_sdp_client_query_result);
|
||||||
|
}
|
||||||
|
/* LISTING_END */
|
||||||
|
|
||||||
|
|
||||||
|
/* @section SDP Client Query
|
||||||
|
*
|
||||||
|
* @text To get the the BNEP service records on a remote device, you need to
|
||||||
|
* call sdp_general_query_for_uuid() with the remote address and the
|
||||||
|
* BNEP protocol UUID, as shown in Listing SDPQueryUUID.
|
||||||
|
* In this example we used fixed address of the remote device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* LISTING_START(SDPQueryUUID): Quering the a list of service records on a remote device. */
|
||||||
static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||||
if (packet_type != HCI_EVENT_PACKET) return;
|
if (packet_type != HCI_EVENT_PACKET) return;
|
||||||
uint8_t event = packet[0];
|
uint8_t event = packet[0];
|
||||||
@ -93,6 +129,7 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* LISTING_END */
|
||||||
|
|
||||||
char * get_string_from_data_element(uint8_t * element){
|
char * get_string_from_data_element(uint8_t * element){
|
||||||
de_size_t de_size = de_get_size_type(element);
|
de_size_t de_size = de_get_size_type(element);
|
||||||
@ -115,8 +152,26 @@ char * get_string_from_data_element(uint8_t * element){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* @section Handling SDP Client Query Result
|
||||||
|
*
|
||||||
|
* @text The SDP Client returns the result of the query in chunks. Each result
|
||||||
|
* packet contains the record ID, the Attribute ID, and a chunk of the Attribute
|
||||||
|
* value, see Listing HandleSDPQUeryResult. Here, we show how to parse the
|
||||||
|
* Service Class ID List and Protocol Descriptor List, as they contain
|
||||||
|
* the BNEP Protocol UUID and L2CAP PSM respectively.
|
||||||
|
*
|
||||||
|
* Service Class ID List is a data element sequence (DES) of 32bit UUIDs.
|
||||||
|
* One of these UUIDs is the BNEP PAN protocol UUID. The BNEP PAN protocol UUID
|
||||||
|
* is within this list.
|
||||||
|
*
|
||||||
|
* Protocol Descriptor List is a data element sequence (DES)
|
||||||
|
* which contains a DES with the L2CAP PSM, and another DES with BNEP
|
||||||
|
* version of 32bit UUIDs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* LISTING_START(HandleSDPQUeryResult): Extracting BNEP Prototocol UUID and L2CAP PSM. */
|
||||||
static void handle_sdp_client_query_result(sdp_query_event_t * event){
|
static void handle_sdp_client_query_result(sdp_query_event_t * event){
|
||||||
|
/* LISTING_PAUSE */
|
||||||
sdp_query_attribute_value_event_t * ve;
|
sdp_query_attribute_value_event_t * ve;
|
||||||
sdp_query_complete_event_t * ce;
|
sdp_query_complete_event_t * ce;
|
||||||
des_iterator_t des_list_it;
|
des_iterator_t des_list_it;
|
||||||
@ -137,7 +192,10 @@ static void handle_sdp_client_query_result(sdp_query_event_t * event){
|
|||||||
|
|
||||||
attribute_value[ve->data_offset] = ve->data;
|
attribute_value[ve->data_offset] = ve->data;
|
||||||
if ((uint16_t)(ve->data_offset+1) == ve->attribute_length){
|
if ((uint16_t)(ve->data_offset+1) == ve->attribute_length){
|
||||||
|
|
||||||
|
/* LISTING_RESUME */
|
||||||
switch(ve->attribute_id){
|
switch(ve->attribute_id){
|
||||||
|
// 0x0001 "Service Class ID List"
|
||||||
case SDP_ServiceClassIDList:
|
case SDP_ServiceClassIDList:
|
||||||
if (de_get_element_type(attribute_value) != DE_DES) break;
|
if (de_get_element_type(attribute_value) != DE_DES) break;
|
||||||
for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)){
|
for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)){
|
||||||
@ -155,13 +213,17 @@ static void handle_sdp_client_query_result(sdp_query_event_t * event){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
/* LISTING_PAUSE */
|
||||||
|
// 0x0100 "Service Name"
|
||||||
case 0x0100:
|
case 0x0100:
|
||||||
|
// 0x0101 "Service Description"
|
||||||
case 0x0101:
|
case 0x0101:
|
||||||
str = get_string_from_data_element(attribute_value);
|
str = get_string_from_data_element(attribute_value);
|
||||||
printf(" ** Attribute 0x%04x: %s\n", ve->attribute_id, str);
|
printf(" ** Attribute 0x%04x: %s\n", ve->attribute_id, str);
|
||||||
free(str);
|
free(str);
|
||||||
break;
|
break;
|
||||||
case 0x004:{
|
/* LISTING_RESUME */
|
||||||
|
case SDP_ProtocolDescriptorList:{
|
||||||
printf(" ** Attribute 0x%04x: ", ve->attribute_id);
|
printf(" ** Attribute 0x%04x: ", ve->attribute_id);
|
||||||
|
|
||||||
uint16_t l2cap_psm = 0;
|
uint16_t l2cap_psm = 0;
|
||||||
@ -190,38 +252,28 @@ static void handle_sdp_client_query_result(sdp_query_event_t * event){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("l2cap_psm 0x%04x, bnep_version 0x%04x\n", l2cap_psm, bnep_version);
|
printf("l2cap_psm 0x%04x, bnep_version 0x%04x\n", l2cap_psm, bnep_version);
|
||||||
// printf(" -> row data (length %d): ", ve->attribute_length);
|
|
||||||
// hexdumpf(attribute_value, ve->attribute_length);
|
|
||||||
// printf("\n");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
/* LISTING_PAUSE */
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDP_QUERY_COMPLETE:
|
case SDP_QUERY_COMPLETE:
|
||||||
ce = (sdp_query_complete_event_t*) event;
|
ce = (sdp_query_complete_event_t*) event;
|
||||||
printf("General query done with status %d.\n\n", ce->status);
|
printf("General query done with status %d.\n\n", ce->status);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* LISTING_RESUME */
|
||||||
}
|
}
|
||||||
|
/* LISTING_END */
|
||||||
|
|
||||||
int btstack_main(int argc, const char * argv[]);
|
int btstack_main(int argc, const char * argv[]);
|
||||||
int btstack_main(int argc, const char * argv[]){
|
int btstack_main(int argc, const char * argv[]){
|
||||||
|
|
||||||
printf("Client HCI init done\r\n");
|
printf("Client HCI init done\r\n");
|
||||||
|
|
||||||
// init L2CAP
|
sdp_client_init();
|
||||||
l2cap_init();
|
|
||||||
l2cap_register_packet_handler(packet_handler);
|
|
||||||
|
|
||||||
sdp_parser_init();
|
|
||||||
sdp_parser_register_callback(handle_sdp_client_query_result);
|
|
||||||
|
|
||||||
// turn on!
|
// turn on!
|
||||||
hci_power_control(HCI_POWER_ON);
|
hci_power_control(HCI_POWER_ON);
|
||||||
|
@ -39,11 +39,7 @@
|
|||||||
/* EXAMPLE_START(sdp_general_query): Dump remote SDP Records
|
/* EXAMPLE_START(sdp_general_query): Dump remote SDP Records
|
||||||
*
|
*
|
||||||
* @text The example shows how the SDP Client is used to get a list of
|
* @text The example shows how the SDP Client is used to get a list of
|
||||||
* service records on a remote device. The address of the remote device is
|
* service records on a remote device.
|
||||||
* fixed in the variable remote below.
|
|
||||||
* The SDP Client returns the results of the query in chunks. Each result
|
|
||||||
* packet contains the record ID, the Attribute ID, and a chunk of the Attribute
|
|
||||||
* value.
|
|
||||||
*/
|
*/
|
||||||
// *****************************************************************************
|
// *****************************************************************************
|
||||||
|
|
||||||
@ -75,8 +71,38 @@ static const int attribute_value_buffer_size = sizeof(attribute_value);
|
|||||||
|
|
||||||
static bd_addr_t remote = {0x04,0x0C,0xCE,0xE4,0x85,0xD3};
|
static bd_addr_t remote = {0x04,0x0C,0xCE,0xE4,0x85,0xD3};
|
||||||
|
|
||||||
|
/* @section SDP Client Setup
|
||||||
|
*
|
||||||
|
* @text SDP is based on L2CAP. To receive SDP query events you must register a
|
||||||
|
* callback, i.e. query handler, with the SPD parser, as shown in
|
||||||
|
* Listing SDPClientInit. Via this handler, the SDP client will receive events:
|
||||||
|
* - SDP_QUERY_ATTRIBUTE_VALUE containing the results of the query in chunks,
|
||||||
|
* - SDP_QUERY_COMPLETE reporting the status and the end of the query.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* LISTING_START(SDPClientInit): SDP client setup */
|
||||||
|
static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||||
static void handle_sdp_client_query_result(sdp_query_event_t * event);
|
static void handle_sdp_client_query_result(sdp_query_event_t * event);
|
||||||
|
|
||||||
|
static void sdp_client_init(){
|
||||||
|
// init L2CAP
|
||||||
|
l2cap_init();
|
||||||
|
l2cap_register_packet_handler(packet_handler);
|
||||||
|
|
||||||
|
sdp_parser_init();
|
||||||
|
sdp_parser_register_callback(handle_sdp_client_query_result);
|
||||||
|
}
|
||||||
|
/* LISTING_END */
|
||||||
|
|
||||||
|
/* @section SDP Client Query
|
||||||
|
*
|
||||||
|
* @text To get the a list of service records on a remote device, you need to
|
||||||
|
* call sdp_general_query_for_uuid() with the remote address and the
|
||||||
|
* UUID of the public browse group, as shown in Listing SDPQueryUUID.
|
||||||
|
* In this example we used fixed address of the remote device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* LISTING_START(SDPQueryUUID): Quering a list of service records on a remote device. */
|
||||||
static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||||
// printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
|
// printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
|
||||||
|
|
||||||
@ -85,7 +111,6 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha
|
|||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case BTSTACK_EVENT_STATE:
|
case BTSTACK_EVENT_STATE:
|
||||||
// bt stack activated, get started
|
|
||||||
if (packet[2] == HCI_STATE_WORKING){
|
if (packet[2] == HCI_STATE_WORKING){
|
||||||
sdp_general_query_for_uuid(remote, SDP_PublicBrowseGroup);
|
sdp_general_query_for_uuid(remote, SDP_PublicBrowseGroup);
|
||||||
}
|
}
|
||||||
@ -94,6 +119,7 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* LISTING_END */
|
||||||
|
|
||||||
static void assertBuffer(int size){
|
static void assertBuffer(int size){
|
||||||
if (size > attribute_value_buffer_size){
|
if (size > attribute_value_buffer_size){
|
||||||
@ -101,6 +127,14 @@ static void assertBuffer(int size){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @section Handling SDP Client Query Result
|
||||||
|
*
|
||||||
|
* @text The SDP Client returns the results of the query in chunks. Each result
|
||||||
|
* packet contains the record ID, the Attribute ID, and a chunk of the Attribute
|
||||||
|
* value, see Listing HandleSDPQUeryResult.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* LISTING_START(HandleSDPQUeryResult): Handling query result chunks. */
|
||||||
static void handle_sdp_client_query_result(sdp_query_event_t * event){
|
static void handle_sdp_client_query_result(sdp_query_event_t * event){
|
||||||
sdp_query_attribute_value_event_t * ve;
|
sdp_query_attribute_value_event_t * ve;
|
||||||
sdp_query_complete_event_t * ce;
|
sdp_query_complete_event_t * ce;
|
||||||
@ -130,18 +164,14 @@ static void handle_sdp_client_query_result(sdp_query_event_t * event){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* LISTING_END */
|
||||||
|
|
||||||
int btstack_main(int argc, const char * argv[]);
|
int btstack_main(int argc, const char * argv[]);
|
||||||
int btstack_main(int argc, const char * argv[]){
|
int btstack_main(int argc, const char * argv[]){
|
||||||
|
|
||||||
printf("Client HCI init done\r\n");
|
printf("Client HCI init done\r\n");
|
||||||
|
|
||||||
// init L2CAP
|
sdp_client_init();
|
||||||
l2cap_init();
|
|
||||||
l2cap_register_packet_handler(packet_handler);
|
|
||||||
|
|
||||||
sdp_parser_init();
|
|
||||||
sdp_parser_register_callback(handle_sdp_client_query_result);
|
|
||||||
|
|
||||||
// turn on!
|
// turn on!
|
||||||
hci_power_control(HCI_POWER_ON);
|
hci_power_control(HCI_POWER_ON);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user