From 82d6daf1818b49867b1ae48b7d309792463ec6f8 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Fri, 24 Apr 2015 16:05:54 +0200 Subject: [PATCH] manual: added SDP query examples --- docs/manual/update_listings.py | 32 +++++------ example/embedded/sdp_bnep_query.c | 84 ++++++++++++++++++++++------ example/embedded/sdp_general_query.c | 54 ++++++++++++++---- 3 files changed, 126 insertions(+), 44 deletions(-) diff --git a/docs/manual/update_listings.py b/docs/manual/update_listings.py index f8d02c29e..61804252f 100755 --- a/docs/manual/update_listings.py +++ b/docs/manual/update_listings.py @@ -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]. list_of_examples = { - "Hello World" : [["led_counter"]], - "GAP" : [["gap_inquiry"]], - "SDP Queries" :[["sdp_general_query"], + # "Hello World" : [["led_counter"]], + # "GAP" : [["gap_inquiry"]], + "SDP Queries" :[#["sdp_general_query"], ["sdp_bnep_query"] ], - "SPP Server" : [["spp_counter"], - ["spp_flowcontrol"]], - "BNEP/PAN" : [["panu_demo"]], - "Low Energy" : [["gatt_browser"], - ["le_counter"]], - "Dual Mode" : [["spp_and_le_counter"]], + # "SPP Server" : [["spp_counter"], + # ["spp_flowcontrol"]], + # "BNEP/PAN" : [["panu_demo"]], + # "Low Energy" : [["gatt_browser"], + # ["le_counter"]], + # "Dual Mode" : [["spp_and_le_counter"]], } lst_header = """ @@ -51,7 +51,7 @@ examples_header = """ """ 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 = """ \subsection{EXAMPLE_TITLE: EXAMPLE_DESC} @@ -59,7 +59,7 @@ example_section = """ """ example_subsection = """ -\subsubsection{LISTING_CAPTION} +\subsubsection{SECTION_TITLE} """ listing_start = """ @@ -162,15 +162,15 @@ def writeListings(aout, infile_name, ref_prefix): continue # detect @section - section_parts = re.match('.*(@section)\s*(.*)\s*(\*?/?)\n',line) + section_parts = re.match('.*(@section)\s*(.*)(:?\s*.?)\*?/?\n',line) 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 # detect @subsection - subsection_parts = re.match('.*(@subsection)\s*(.*)\s*(\*?/?)\n',line) + subsection_parts = re.match('.*(@section)\s*(.*)(:?\s*.?)\*?/?\n',line) 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) continue @@ -186,7 +186,7 @@ def writeListings(aout, infile_name, ref_prefix): itemize_block = None else: if isEmptyCommentLine(line): - text_block = text_block + "\n" + text_block = text_block + "\n\n" else: # finish text aout.write(text_block) diff --git a/example/embedded/sdp_bnep_query.c b/example/embedded/sdp_bnep_query.c index 224749955..5c870335c 100644 --- a/example/embedded/sdp_bnep_query.c +++ b/example/embedded/sdp_bnep_query.c @@ -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){ if (packet_type != HCI_EVENT_PACKET) return; uint8_t event = packet[0]; @@ -93,6 +129,7 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha break; } } +/* LISTING_END */ char * get_string_from_data_element(uint8_t * 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){ +/* LISTING_PAUSE */ sdp_query_attribute_value_event_t * ve; sdp_query_complete_event_t * ce; 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; if ((uint16_t)(ve->data_offset+1) == ve->attribute_length){ + + /* LISTING_RESUME */ switch(ve->attribute_id){ + // 0x0001 "Service Class ID List" case SDP_ServiceClassIDList: 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)){ @@ -155,13 +213,17 @@ static void handle_sdp_client_query_result(sdp_query_event_t * event){ } } break; +/* LISTING_PAUSE */ + // 0x0100 "Service Name" case 0x0100: + // 0x0101 "Service Description" case 0x0101: str = get_string_from_data_element(attribute_value); printf(" ** Attribute 0x%04x: %s\n", ve->attribute_id, str); free(str); break; - case 0x004:{ +/* LISTING_RESUME */ + case SDP_ProtocolDescriptorList:{ printf(" ** Attribute 0x%04x: ", ve->attribute_id); 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(" -> row data (length %d): ", ve->attribute_length); - // hexdumpf(attribute_value, ve->attribute_length); - // printf("\n"); - } break; +/* LISTING_PAUSE */ default: break; } - - } break; case SDP_QUERY_COMPLETE: ce = (sdp_query_complete_event_t*) event; printf("General query done with status %d.\n\n", ce->status); - break; } +/* LISTING_RESUME */ } +/* LISTING_END */ int btstack_main(int argc, const char * argv[]); int btstack_main(int argc, const char * argv[]){ - printf("Client HCI init done\r\n"); - // init L2CAP - l2cap_init(); - l2cap_register_packet_handler(packet_handler); - - sdp_parser_init(); - sdp_parser_register_callback(handle_sdp_client_query_result); + sdp_client_init(); // turn on! hci_power_control(HCI_POWER_ON); diff --git a/example/embedded/sdp_general_query.c b/example/embedded/sdp_general_query.c index e9eea1a0a..13c46f843 100644 --- a/example/embedded/sdp_general_query.c +++ b/example/embedded/sdp_general_query.c @@ -39,11 +39,7 @@ /* EXAMPLE_START(sdp_general_query): Dump remote SDP Records * * @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 - * 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. + * service records on a remote device. */ // ***************************************************************************** @@ -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}; +/* @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 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){ // 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) { case BTSTACK_EVENT_STATE: - // bt stack activated, get started if (packet[2] == HCI_STATE_WORKING){ 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; } } +/* LISTING_END */ static void assertBuffer(int 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){ sdp_query_attribute_value_event_t * ve; sdp_query_complete_event_t * ce; @@ -130,18 +164,14 @@ static void handle_sdp_client_query_result(sdp_query_event_t * event){ break; } } +/* LISTING_END */ int btstack_main(int argc, const char * argv[]); int btstack_main(int argc, const char * argv[]){ printf("Client HCI init done\r\n"); - // init L2CAP - l2cap_init(); - l2cap_register_packet_handler(packet_handler); - - sdp_parser_init(); - sdp_parser_register_callback(handle_sdp_client_query_result); + sdp_client_init(); // turn on! hci_power_control(HCI_POWER_ON);