As a result of effort in previous post we now armed with knowdledge what has to be done in order to connect to Bluetooth SDP (Service Discovery Protocol). All we need to get from SDP is a RFComm channel number to connect to, the rest information provided by SDP service while it is a subject of certain interest, not compulsory for our goal. Make the same preparations and run two consoles with logging hcpdump applications and it was described in the previous post. Let’s analyze packet exchange a bit further, immediately after establishing connection to SDP. Here is what the first console would typically report:
< ACL data: handle 43 flags 0x02 dlen 24 L2CAP(d): cid 0x0040 len 20 [psm 1] SDP SSA Req: tid 0x0 len 0xf //Service search attribute request is sent to remote endpoint pat uuid-16 0x1101 (SP) max 65535 aid(s) 0x0000 - 0xffff cont 00 > ACL data: handle 43 flags 0x02 dlen 50 L2CAP(d): cid 0x0040 len 46 [psm 1] SDP SSA Rsp: tid 0x0 len 0x29 //Service search attribute response is received but it doesn't fit in one packet count 36 cont 02 00 24 //Indication that there is still some data pending < ACL data: handle 43 flags 0x02 dlen 26 L2CAP(d): cid 0x0040 len 22 [psm 1] SDP SSA Req: tid 0x1 len 0x11 //Service search attribute request is sent again to fetch what didn't fit in the first packet pat uuid-16 0x1101 (SP) max 65535 aid(s) 0x0000 - 0xffff cont 02 00 24 //Indication that we need to get the pending part of data > ACL data: handle 43 flags 0x02 dlen 28 L2CAP(d): cid 0x0040 len 24 [psm 1] SDP SSA Rsp: tid 0x1 len 0x13 //Second service search attribute response with remaining data count 16 record #0 aid 0x0000 (SrvRecHndl) uint 0x10007 aid 0x0001 (SrvClassIDList) < uuid-16 0x1101 (SP) > aid 0x0004 (ProtocolDescList) < < uuid-16 0x0100 (L2CAP) > < uuid-16 0x0003 (RFCOMM) uint 0x10 > > aid 0x0009 (BTProfileDescList) < < uuid-16 0x1101 (SP) uint 0x100 > > cont 00 //Indication that there is no pending data anymore
Let’s have a look at the same exchange in raw representation logged by the second console and try to decode it:
< 0000: 02 2a 20 18 00 14 00 40 00 06 00 00 00 0f 35 03 .* ....@......5. //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU (0x06 at address 0x0009) 0010: 19 11 01 ff ff 35 05 0a 00 00 ff ff 00 .....5....... > 0000: 02 2a 20 32 00 2e 00 40 00 07 00 00 00 29 00 24 .* 2...@.....).$ //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU (0x07 at address 0x0009) 0010: 36 00 31 36 00 2e 09 00 00 0a 00 01 00 07 09 00 6.16............ 0020: 01 35 03 19 11 01 09 00 04 35 0c 35 03 19 01 00 .5.......5.5.... 0030: 35 05 19 00 02 00 24 5.....$ < 0000: 02 2a 20 1a 00 16 00 40 00 06 00 01 00 11 35 03 .* ....@......5. //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU (0x06 at address 0x0009) 0010: 19 11 01 ff ff 35 05 0a 00 00 ff ff 02 00 24 .....5........$ > 0000: 02 2a 20 1c 00 18 00 40 00 07 00 01 00 13 00 10 .* ....@........ //2-nd SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU (0x07 at address 0x0009) 0010: 03 08 10 09 00 09 35 08 35 06 19 11 01 09 01 00 ......5.5....... 0020: 00 .
Again, it would be worth getting familiar with internal packet structure and payload. An SDP service is basically a hierarchical list of attributes, each attribute has a list of field-value pairs and fields can have different types (byte, short, int – just like modern programming language). Attributes can be grouped and can nest more elements or groups inside. This is pretty much what we need to know about SDP services. Let’s have a closer look at the binary data:
=== HCI header === 02 2a HCI hanle with PB and BC flags 20 18 HCI ACL total data length (it actually specifies total length 0x0018) 00 14 L2CAP payload length 00 40 SDP channel ID (please note that channel number is not 0x0001 anymore that is why the packet is interpreted as SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU and not as L2CAP_CMD_DISCONNECT_REQUEST (although they both have the same code) 00 ??? Unknown byte, it is present neither in spec nor in packets when sent from Arduino/Luminardo. Probably a bug in hcidump as the byte is not even accounted by HCI ACL total data length, just ignore it === Actual L2CAP command === 06 SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU 00 00 Transaction identifier 00 0f Parameter length { 35 Data Element Sequence, data size in next 8 bits 03 Data size { 19 Field type UUID, 2 bytes 11 01 Field value 0x0100 - L2CAP_UUID } ff ff Maximum attribute byte count 35 Data Element Sequence, data size in next 8 bits 05 Data size { 0a Field type Unsigned int, 4 bytes - Attribute ID range 00 00 Attribute range from 0x0000... ff ff ... to 0xFFFF } } 00 No more data
In essence, the packet above is requesting all attributes with identifiers from 0 to 0xFFFF from service with UUID = 0x0100.
=== HCI header === 02 2a 20 32 00 2e 00 40 00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure === Actual L2CAP command === 07 SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 00 00 Transaction identifier 00 29 Parameter length 00 24 Attribute lists byte count { 36 Data Element Sequence, size in next 16 bits 00 31 Size { 36 Data Element Sequence, size in next 16 bits 00 2e Size { 09 Unsigned integer in next 16 bits 00 00 Attr ID 0000 - "Service Record Handle" 0a Unsigned integer in next 32 bits 00 01 00 07 Attr Value - handle 09 Unsigned integer in next 16 bits 00 01 Attr ID 0001 - "Service Class ID" 35 Data Element Sequence, size in next 8 bits 03 Size { 19 UUID, 2 bytes 11 01 "SERIALPORT UUID" } 09 Unsigned integer in next 16 bits 00 04 Attr ID 0004 - "Protocol Descriptor List" 35 Data Element Sequence, size in next 8 bits 0c Size { 35 Data Element Sequence, size in next 8 bits 03 Size { 19 UUID, 2 bytes 01 00 "L2CAP UUID" } 35 Data Element Sequence, size in next 8 bits 05 Size { 19 UUID, 2 bytes 00 ... end of data, 3 bytes are still missing, will be continued in next package ... } } 02 Size in next two bytes 00 24 More data to come
=== HCI header === 02 2a 20 1a 00 16 00 40 00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure === Actual L2CAP command === 06 SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU 00 01 Transaction Identifier 00 11 Parameter length { 35 Data Element Sequence, data size in next 8 bits 03 Data Size { 19 Field type UUID, 2 bytes 11 01 Field value 0x0100 - L2CAP_UUID } ff ff Maximum attribute byte count 35 Data Element Sequence, data size in next 8 bits 05 Data Size { 0a Field type Unsigned int, 4 bytes - Attribute ID range 00 00 Attribute range from 0x0000... ff ff ... to 0xFFFF } } 02 Copied from last response, 00 24 in essence it is a request to get the remaining data
=== HCI header === 02 2a 20 1c 00 18 00 40 00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure === Actual L2CAP command === 07 SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 00 01 Transaction identifier 00 13 Parameter length 00 10 Attribute lists byte count { 03 Continuation of 0x00 from previous packet makes "00 03" - "RFCOMM UUID" 08 Unsigned integer in next 8 bits 10 Here it is, a RFComm channel number, that is what we are after! 09 Unsigned integer in next 16 bits 00 09 Attr ID 0009 - "Bluetooth Profile Descriptor List" 35 Data Element Sequence, data size in next 8 bits 08 Data Size { 35 Data Element Sequence, data size in next 8 bits 06 Data Size { 19 UUID, 2 bytes 11 01 "SERIALPORT UUID" 09 Unsigned int in next 16 bits 01 00 Profile version number } } 00 No more data
As an attentive reader has probably already noted, in the last package we received a value that we were after, it is RFComm channel number which is 0x10 for this particular case. At this point we know what has to be sent and how in order to get this magic number. Enough theories, time to put our knowledge in practice. As a prototype to validate our ideas with SDP service a limited in functionality class called SPPi has been designed, please refer to the Download section of this post. Copy SPPi.cpp and SPPi.h to USB Host 2.0 folder then compile and run Luminardo_USBHost_Bluetooth_SPPi.ino. The output should be like this (which proves that now we can talk SDP):
Luminardo USB Host Bluetooth SPPi Test SPP Bluetooth Library Started Enabling VBus... enabled Bluetooth Dongle Initialized HCI Reset complete Write class of device Local Bluetooth Address: 00:19:0E:12:65:6A The name is set to: Luminardo Pairing to 'Other' device with predefined address Device: 40:22:11:00:69:58 has been found Connecting to 'Other' device... Connected to 'Other' device Received Key Request Bluetooth pin is set too: 1234 Pairing successful with 'Other' device SDP Connection Request L2CAP Connection Response SDP Connection Response SDP Configuration Request Received SDP Configuration Response Sent SDP Configuration Request Sent SDP Configuration Response Received SDP Successfully Configured SDP Service Search Attribute Request 1 Sent SDP data... - SDP Service Search Attribute Response 1: 0B 20 34 00 30 00 41 00 07 00 48 00 2B 00 26 36 00 31 36 00 2E 09 00 00 0A 00 01 00 07 09 00 01 35 03 19 11 01 09 00 04 35 0C 35 03 19 01 00 35 05 19 00 03 08 02 00 26 SDP Service Search Attribute Request 2 Sent SDP data... - SDP Service Search Attribute Response 2: 0B 20 1A 00 16 00 41 00 07 00 49 00 11 00 0E 10 09 00 09 35 08 35 06 19 11 01 09 01 00 00
Now you can move to the next, final step – to start RFComm transport layer which will be described in detail in next post. Stay tuned and don’t hesitate to ask questions!
Downloads:
1. USB Host 2.0 Add-on Allowing to Initiate Communication with SDP service.
Leave a Reply
You must be logged in to post a comment.