Add comments and partial Doxygen documentation to the incomplete Bluetooth SDP handle...
[pub/USBasp.git] / Demos / Host / Incomplete / BluetoothHost / Lib / ServiceDiscoveryProtocol.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2010.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.fourwalledcubicle.com
7 */
8
9 /*
10 Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com)
11
12 Permission to use, copy, modify, distribute, and sell this
13 software and its documentation for any purpose is hereby granted
14 without fee, provided that the above copyright notice appear in
15 all copies and that both that the copyright notice and this
16 permission notice and warranty disclaimer appear in supporting
17 documentation, and that the name of the author not be used in
18 advertising or publicity pertaining to distribution of the
19 software without specific, written prior permission.
20
21 The author disclaim all warranties with regard to this
22 software, including all implied warranties of merchantability
23 and fitness. In no event shall the author be liable for any
24 special, indirect or consequential damages or any damages
25 whatsoever resulting from loss of use, data or profits, whether
26 in an action of contract, negligence or other tortious action,
27 arising out of or in connection with the use or performance of
28 this software.
29 */
30
31 #define INCLUDE_FROM_SERVICEDISCOVERYPROTOCOL_C
32 #include "ServiceDiscoveryProtocol.h"
33
34 /** Service Discovery Protocol attribute, indicationg the service's name. */
35 const struct
36 {
37 uint8_t Header;
38 uint8_t Length;
39 uint8_t Data[];
40 } PROGMEM SDP_Attribute_Name = {(SDP_DATATYPE_String | 5), sizeof("SDP"), "SDP"};
41
42 /** Service Discovery Protocol attribute, indicationg the service's description. */
43 const struct
44 {
45 uint8_t Header;
46 uint8_t Length;
47 uint8_t Data[];
48 } PROGMEM SDP_Attribute_Description = {(SDP_DATATYPE_String | 5), sizeof("BT Service Discovery"), "BT Service Discovery"};
49
50 /** Service Discovery Protocol attribute, indicationg the service's availability. */
51 const struct
52 {
53 uint8_t Header;
54 uint8_t Data;
55 } PROGMEM SDP_Attribute_Availability = {(SDP_DATATYPE_UnsignedInt | 0), 0xFF};
56
57 const struct
58 {
59 uint8_t Header;
60 uint16_t Data;
61 } PROGMEM SDP_Attribute_LanguageOffset = {(SDP_DATATYPE_UnsignedInt | 1), SDP_ATTRIBUTE_LANGOFFSET};
62
63 const struct
64 {
65 uint8_t Header;
66 uint16_t Data;
67 } PROGMEM SDP_Attribute_ServiceHandle = {(SDP_DATATYPE_UnsignedInt | 1), 0x0001};
68
69 const struct
70 {
71 uint8_t Header;
72 uint8_t Size;
73 ClassUUID_t UUIDList[];
74 } PROGMEM SDP_Attribute_ServiceClassIDs =
75 {
76 (SDP_DATATYPE_Sequence | 5), (sizeof(ClassUUID_t) * 1),
77 {
78 {.Header = (SDP_DATATYPE_UUID | 5), .Size = UUID_SIZE_BYTES, .UUID = {BASE_96BIT_UUID, 0x01, 0x00, 0x00, 0x00}}
79 }
80 };
81
82 /** Service Discovery Protocol attribute table, listing all supported attributes of the service. */
83 const ServiceAttributeTable_t SDP_Attribute_Table[] PROGMEM =
84 {
85 {.AttributeID = SDP_ATTRIBUTE_ID_SERVICERECORDHANDLE, .Data = &SDP_Attribute_ServiceHandle },
86 {.AttributeID = SDP_ATTRIBUTE_ID_SERVICECLASSIDS, .Data = &SDP_Attribute_ServiceClassIDs },
87 {.AttributeID = SDP_ATTRIBUTE_ID_LANGIDOFFSET, .Data = &SDP_Attribute_LanguageOffset },
88 {.AttributeID = SDP_ATTRIBUTE_ID_NAME, .Data = &SDP_Attribute_Name },
89 {.AttributeID = SDP_ATTRIBUTE_ID_DESCRIPTION, .Data = &SDP_Attribute_Description },
90
91 SERVICE_ATTRIBUTE_TABLE_TERMINATOR
92 };
93
94 /** Master service table, listing all supported services (and their attribute tables) of the device, including
95 * each service's UUID.
96 */
97 const ServiceTable_t SDP_Services_Table[] PROGMEM =
98 {
99 { // 128-bit UUID for the SDP service
100 .UUID = {BASE_96BIT_UUID, 0x01, 0x00, 0x00, 0x00},
101 .AttributeTable = SDP_Attribute_Table,
102 },
103 #if 0
104 { // 128-bit UUID for the RFCOMM service
105 .UUID = {BASE_96BIT_UUID, 0x03, 0x00, 0x00, 0x00},
106 .AttributeTable = RFCOMM_Attribute_Table,
107 },
108 #endif
109 };
110
111 /** Base UUID value common to all standardized Bluetooth services */
112 const uint8_t BaseUUID[] PROGMEM = {BASE_96BIT_UUID, 0x00, 0x00, 0x00, 0x00};
113
114
115 /** Main Service Discovery Protocol packet processing routine. This function processes incomming SDP packets from
116 * a connected Bluetooth device, and sends back appropriate responses to allow other devices to determine the
117 * services the local device exposes.
118 *
119 * \param[in] Data Incomming packet data containing the SDP request
120 * \param[in] Channel Channel the request was issued to by the remote device
121 */
122 void SDP_ProcessPacket(void* Data, Bluetooth_Channel_t* Channel)
123 {
124 SDP_PDUHeader_t* SDPHeader = (SDP_PDUHeader_t*)Data;
125 SDPHeader->ParameterLength = SwapEndian_16(SDPHeader->ParameterLength);
126
127 BT_SDP_DEBUG(1, "SDP Packet Received");
128 BT_SDP_DEBUG(2, "-- PDU ID: 0x%02X", SDPHeader->PDU);
129 BT_SDP_DEBUG(2, "-- Param Length: 0x%04X", SDPHeader->ParameterLength);
130
131 switch (SDPHeader->PDU)
132 {
133 case SDP_PDU_SERVICESEARCHREQUEST:
134 SDP_ProcessServiceSearch(SDPHeader, Channel);
135 break;
136 case SDP_PDU_SERVICEATTRIBUTEREQUEST:
137 SDP_ProcessServiceAttribute(SDPHeader, Channel);
138 break;
139 case SDP_PDU_SERVICESEARCHATTRIBUTEREQUEST:
140 SDP_ProcessServiceSearchAttribute(SDPHeader, Channel);
141 break;
142 }
143 }
144
145 /** Internal processing routine for SDP Service Search Requests.
146 *
147 * \param[in] SDPHeader Pointer to the start of the issued SDP request
148 * \param[in] Channel Pointer to the Bluetooth channel structure the request was issued to
149 */
150 static void SDP_ProcessServiceSearch(const SDP_PDUHeader_t* const SDPHeader, Bluetooth_Channel_t* const Channel)
151 {
152 BT_SDP_DEBUG(1, "<< Service Search");
153 }
154
155 /** Internal processing routine for SDP Service Attribute Requests.
156 *
157 * \param[in] SDPHeader Pointer to the start of the issued SDP request
158 * \param[in] Channel Pointer to the Bluetooth channel structure the request was issued to
159 */
160 static void SDP_ProcessServiceAttribute(const SDP_PDUHeader_t* const SDPHeader, Bluetooth_Channel_t* const Channel)
161 {
162 BT_SDP_DEBUG(1, "<< Service Attribute");
163 }
164
165 /** Internal processing routine for SDP Service Search Attribute Requests.
166 *
167 * \param[in] SDPHeader Pointer to the start of the issued SDP request
168 * \param[in] Channel Pointer to the Bluetooth channel structure the request was issued to
169 */
170 static void SDP_ProcessServiceSearchAttribute(const SDP_PDUHeader_t* const SDPHeader, Bluetooth_Channel_t* const Channel)
171 {
172 const void* CurrentParameter = ((void*)SDPHeader + sizeof(SDP_PDUHeader_t));
173
174 BT_SDP_DEBUG(1, "<< Service Search Attribute");
175
176 /* Retrieve the list of search UUIDs from the request */
177 uint8_t UUIDList[12][UUID_SIZE_BYTES];
178 uint8_t TotalUUIDs = SDP_GetUUIDList(UUIDList, &CurrentParameter);
179 BT_SDP_DEBUG(2, "-- Total UUIDs: %d", TotalUUIDs);
180
181 /* Retrieve the maximum Attribute reponse size from the request */
182 uint16_t MaxAttributeSize = *((uint16_t*)CurrentParameter);
183 CurrentParameter += sizeof(uint16_t);
184 BT_SDP_DEBUG(2, "-- Max Return Attribute Bytes: 0x%04X", MaxAttributeSize);
185
186 /* Retrieve the list of Attributes from the request */
187 uint16_t AttributeList[15][2];
188 uint8_t TotalAttributes = SDP_GetAttributeList(AttributeList, &CurrentParameter);
189 BT_SDP_DEBUG(2, "-- Total Attributes: %d", TotalAttributes);
190
191 struct
192 {
193 SDP_PDUHeader_t SDPHeader;
194 uint16_t AttributeListByteCount;
195 uint8_t ResponseData[100];
196 } ResponsePacket;
197
198 /* Create a pointer to the buffer to indicate the current location for response data to be added */
199 uint8_t* CurrResponsePos = ResponsePacket.ResponseData;
200
201 /* Clamp the maximum attribute size to the size of the allocated buffer */
202 if (MaxAttributeSize > sizeof(ResponsePacket.ResponseData))
203 MaxAttributeSize = sizeof(ResponsePacket.ResponseData);
204
205 /* Add the outer Data Element Sequence header for the retrieved Attributes */
206 uint16_t* TotalResponseSize = SDP_AddDataElementHeader16(&CurrResponsePos, SDP_DATATYPE_Sequence);
207
208 /* Search through the list of UUIDs one at a time looking for matching search Attributes */
209 for (uint8_t CurrUUIDItem = 0; CurrUUIDItem < TotalUUIDs; CurrUUIDItem++)
210 {
211 /* Retrieve the attribute table of the current search UUID from the global UUID table if it exists */
212 ServiceAttributeTable_t* AttributeTable = SDP_GetAttributeTable(UUIDList[CurrUUIDItem]);
213
214 /* If the UUID does not exist in the global UUID table, continue on to the next search UUID */
215 if (AttributeTable == NULL)
216 continue;
217
218 /* Add an inner Data Element Sequence header for the current UUID's found Attributes */
219 uint16_t* CurrentUUIDResponseSize = SDP_AddDataElementHeader16(&CurrResponsePos, SDP_DATATYPE_Sequence);
220
221 /* Search through the list of Attributes one at a time looking for values in the current UUID's Attribute table */
222 for (uint8_t CurrAttribute = 0; CurrAttribute < TotalAttributes; CurrAttribute++)
223 {
224 uint16_t* AttributeIDRange = AttributeList[CurrAttribute];
225
226 /* Look in the current Attribute Range for a matching Attribute ID in the UUID's Attribute table */
227 for (uint32_t CurrAttributeID = AttributeIDRange[0]; CurrAttributeID <= AttributeIDRange[1]; CurrAttributeID++)
228 {
229 /* Retrieve a PROGMEM pointer to the value of the current Attribute ID, if it exists in the UUID's Attribute table */
230 const void* AttributeValue = SDP_GetAttributeValue(AttributeTable, CurrAttributeID);
231
232 /* If the Attribute does not exist in the current UUID's Attribute table, continue to the next Attribute ID */
233 if (AttributeValue == NULL)
234 continue;
235
236 /* Retrieve the size of the attribute value from its container header */
237 uint32_t AttributeValueLength = SDP_GetLocalAttributeContainerSize(AttributeValue);
238 BT_SDP_DEBUG(2, " -- Add Attribute 0x%04X", CurrAttributeID);
239
240 /* Add a Data Element header to the response for the Attribute ID */
241 *((uint8_t*)CurrResponsePos) = (1 | SDP_DATATYPE_UnsignedInt);
242 CurrResponsePos += sizeof(uint8_t);
243
244 /* Add the Attribute ID to the created Data Element */
245 *((uint16_t*)CurrResponsePos) = CurrAttributeID;
246 CurrResponsePos += sizeof(uint16_t);
247
248 /* Copy over the Attribute value Data Element container to the response */
249 memcpy_P(CurrResponsePos, AttributeValue, AttributeValueLength);
250 CurrResponsePos += AttributeValueLength;
251
252 /* Increment the current UUID's returned Attribute container size by the number of added bytes */
253 *CurrentUUIDResponseSize += sizeof(uint8_t) + sizeof(uint16_t) + AttributeValueLength;
254 }
255
256 /* Increment the outer container size by the number of added bytes */
257 *TotalResponseSize += 3 + *CurrentUUIDResponseSize;
258 }
259 }
260
261 /* Set the total response list size to the size of the outer container plus its header size */
262 ResponsePacket.AttributeListByteCount = 3 + *TotalResponseSize;
263
264 /* Fill in the response packet's header */
265 ResponsePacket.SDPHeader.PDU = SDP_PDU_SERVICESEARCHATTRIBUTERESPONSE;
266 ResponsePacket.SDPHeader.TransactionID = SDPHeader->TransactionID;
267 ResponsePacket.SDPHeader.ParameterLength = (ResponsePacket.AttributeListByteCount + sizeof(ResponsePacket.AttributeListByteCount));
268
269 BT_SDP_DEBUG(1, ">> Service Search Attribute Response");
270 BT_SDP_DEBUG(2, "-- Total Parameter Length: 0x%04X", ResponsePacket.SDPHeader.ParameterLength);
271
272 /* Send the completed response packet to the sender */
273 Bluetooth_SendPacket(&ResponsePacket, (sizeof(ResponsePacket.SDPHeader) + ResponsePacket.SDPHeader.ParameterLength),
274 Channel);
275 }
276
277 /** Retrieves a pointer to the value of the given Attribute ID from the given Attribute table.
278 *
279 * \param[in] AttributeTable Pointer to the Attribute table to search in
280 * \param[in] AttributeID Attribute ID to search for within the table
281 *
282 * \return Pointer to the start of the Attribute's value if found within the table, NULL otherwise
283 */
284 static void* SDP_GetAttributeValue(const ServiceAttributeTable_t* AttributeTable, const uint16_t AttributeID)
285 {
286 void* CurrTableItemData;
287
288 /* Search through the current Attribute table, abort when the terminator item has been reached */
289 while ((CurrTableItemData = (void*)pgm_read_word(&AttributeTable->Data)) != NULL)
290 {
291 /* Check if the current Attribute ID matches the search ID - if so return a pointer to it */
292 if (pgm_read_word(&AttributeTable->AttributeID) == AttributeID)
293 return CurrTableItemData;
294
295 AttributeTable++;
296 }
297
298 return NULL;
299 }
300
301 /** Retrieves the Attribute table for the given UUID if it exists.
302 *
303 * \param[in] UUID UUID to search for
304 *
305 * \return Pointer to the UUID's associated Attribute table if found in the global UUID table, NULL otherwise
306 */
307 static ServiceAttributeTable_t* SDP_GetAttributeTable(const uint8_t* const UUID)
308 {
309 /* Search through the global UUID list an item at a time */
310 for (uint8_t CurrTableItem = 0; CurrTableItem < (sizeof(SDP_Services_Table) / sizeof(ServiceTable_t)); CurrTableItem++)
311 {
312 /* If the current table item's UUID matches the search UUID, return a pointer the table item's Attribute table */
313 if (!(memcmp_P(UUID, SDP_Services_Table[CurrTableItem].UUID, UUID_SIZE_BYTES)))
314 return (ServiceAttributeTable_t*)pgm_read_word(&SDP_Services_Table[CurrTableItem].AttributeTable);
315 }
316
317 return NULL;
318 }
319
320 /** Reads in the collection of Attribute ranges from the input buffer's Data Element Sequence container, into the given
321 * Attribute list for later use. Once complete, the input buffer pointer is advanced to the end of the Attribute container.
322 *
323 * \param[out] AttributeList Pointer to a buffer where the list of Attribute ranges are to be stored
324 * \param[in] CurrentParameter Pointer to a Buffer containing a Data Element Sequence of Attribute and Attribute Range elements
325 *
326 * \return Total number of Attribute ranges stored in the Data Element Sequence
327 */
328 static uint8_t SDP_GetAttributeList(uint16_t AttributeList[][2], const void** const CurrentParameter)
329 {
330 uint8_t ElementHeaderSize;
331 uint8_t TotalAttributes = 0;
332
333 /* Retrieve the total size of the Attribute container, and unwrap the outer Data Element Sequence container */
334 uint16_t AttributeIDListLength = SDP_GetDataElementSize(CurrentParameter, &ElementHeaderSize);
335 BT_SDP_DEBUG(2, "-- Total Attribute Length: 0x%04X", AttributeIDListLength);
336 while (AttributeIDListLength)
337 {
338 /* Retrieve the size of the next Attribute in the container and get a pointer to the next free Attribute element in the list */
339 uint16_t* CurrentAttributeRange = AttributeList[TotalAttributes++];
340 uint8_t AttributeLength = SDP_GetDataElementSize(CurrentParameter, &ElementHeaderSize);
341
342 /* Copy over the starting Attribute ID and (if it the current element is a range) the ending Attribute ID */
343 memcpy(&CurrentAttributeRange[0], *CurrentParameter, AttributeLength);
344
345 /* If the element is not an Attribute Range, copy over the starting ID to the ending ID to make a range of 1 */
346 if (AttributeLength == 2)
347 memcpy(&CurrentAttributeRange[1], *CurrentParameter, 2);
348
349 BT_SDP_DEBUG(2, "-- Attribute: 0x%04X-0x%04X", CurrentAttributeRange[0], CurrentAttributeRange[1]);
350
351 AttributeIDListLength -= (AttributeLength + ElementHeaderSize);
352 *CurrentParameter += AttributeLength;
353 }
354
355 return TotalAttributes;
356 }
357
358 /** Reads in the collection of UUIDs from the input buffer's Data Element Sequence container, into the given
359 * UUID list for later use. Once complete, the input buffer pointer is advanced to the end of the UUID container.
360 *
361 * \param[out] UUIDList Pointer to a buffer where the list of UUIDs are to be stored
362 * \param[in] CurrentParameter Pointer to a Buffer containing a Data Element Sequence of UUID elements
363 *
364 * \return Total number of UUIDs stored in the Data Element Sequence
365 */
366 static uint8_t SDP_GetUUIDList(uint8_t UUIDList[][UUID_SIZE_BYTES], const void** const CurrentParameter)
367 {
368 uint8_t ElementHeaderSize;
369 uint8_t TotalUUIDs = 0;
370
371 /* Retrieve the total size of the UUID container, and unwrap the outer Data Element Sequence container */
372 uint16_t ServicePatternLength = SDP_GetDataElementSize(CurrentParameter, &ElementHeaderSize);
373 BT_SDP_DEBUG(2, "-- Total UUID Length: 0x%04X", ServicePatternLength);
374 while (ServicePatternLength)
375 {
376 /* Retrieve the size of the next UUID in the container and get a pointer to the next free UUID element in the list */
377 uint8_t* CurrentUUID = UUIDList[TotalUUIDs++];
378 uint8_t UUIDLength = SDP_GetDataElementSize(CurrentParameter, &ElementHeaderSize);
379
380 /* Copy over the base UUID value to the free UUID slot in the list */
381 memcpy_P(CurrentUUID, BaseUUID, sizeof(BaseUUID));
382
383 /* Copy over UUID from the container to the free slot - if a short UUID (<= 4 bytes) it replaces the lower
384 4 bytes of the base UUID, otherwise it replaces the UUID completely */
385 memcpy(&CurrentUUID[(UUIDLength <= 4) ? (UUID_SIZE_BYTES - 4) : 0], *CurrentParameter, UUIDLength);
386
387 BT_SDP_DEBUG(2, "-- UUID (%d): 0x%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
388 UUIDLength,
389 CurrentUUID[15], CurrentUUID[14], CurrentUUID[13], CurrentUUID[12],
390 CurrentUUID[11], CurrentUUID[10], CurrentUUID[9], CurrentUUID[8],
391 CurrentUUID[7], CurrentUUID[6], CurrentUUID[5], CurrentUUID[4],
392 CurrentUUID[3], CurrentUUID[2], CurrentUUID[1], CurrentUUID[0]);
393
394 ServicePatternLength -= (UUIDLength + ElementHeaderSize);
395 *CurrentParameter += UUIDLength;
396 }
397
398 return TotalUUIDs;
399 }
400
401 /** Retrieves the total size of the given locally stored (in PROGMEM) attribute Data Element container.
402 *
403 * \param[in] AttributeData Pointer to the start of the Attribute container, located in PROGMEM
404 *
405 * \return Size in bytes of the entire attribute container, including the header
406 */
407 static uint32_t SDP_GetLocalAttributeContainerSize(const void* const AttributeData)
408 {
409 /* Fetch the size of the Data Element structure from the header */
410 uint8_t SizeIndex = (pgm_read_byte(AttributeData) & 0x07);
411
412 /* Convert the Data Element size index into a size in bytes */
413 switch (SizeIndex)
414 {
415 case 5:
416 return (1 + sizeof(uint8_t)) + pgm_read_byte(AttributeData + 1);
417 case 6:
418 return (1 + sizeof(uint16_t)) + pgm_read_word(AttributeData + 1);
419 case 7:
420 return (1 + sizeof(uint32_t)) + pgm_read_dword(AttributeData + 1);
421 default:
422 return (1 + (1 << SizeIndex));
423 }
424
425 return 0;
426 }
427
428 /** Retrieves the size of a Data Element container from the current input buffer, and advances the input buffer
429 * pointer to the start of the Data Element's contents.
430 *
431 * \param[in, out] DataElementHeader Pointer to the start of a Data Element header
432 * \param[out] ElementHeaderSize Size in bytes of the header that was skipped
433 *
434 * \return Size in bytes of the Data Element container's contents, minus the header
435 */
436 static uint32_t SDP_GetDataElementSize(const void** const DataElementHeader, uint8_t* const ElementHeaderSize)
437 {
438 /* Fetch the size of the Data Element structure from the header, increment the current buffer pos */
439 uint8_t SizeIndex = (*((uint8_t*)*DataElementHeader) & 0x07);
440 *DataElementHeader += sizeof(uint8_t);
441
442 uint32_t ElementValue;
443
444 /* Convert the Data Element size index into a size in bytes */
445 switch (SizeIndex)
446 {
447 case 5:
448 ElementValue = *((uint8_t*)*DataElementHeader);
449 *DataElementHeader += sizeof(uint8_t);
450 *ElementHeaderSize = (1 + sizeof(uint8_t));
451 break;
452 case 6:
453 ElementValue = *((uint16_t*)*DataElementHeader);
454 *DataElementHeader += sizeof(uint16_t);
455 *ElementHeaderSize = (1 + sizeof(uint16_t));
456 break;
457 case 7:
458 ElementValue = *((uint32_t*)*DataElementHeader);
459 *DataElementHeader += sizeof(uint32_t);
460 *ElementHeaderSize = (1 + sizeof(uint32_t));
461 break;
462 default:
463 ElementValue = (1 << SizeIndex);
464 *ElementHeaderSize = 1;
465 break;
466 }
467
468 return ElementValue;
469 }