3      Copyright (C) Dean Camera, 2010. 
   5   dean [at] fourwalledcubicle [dot] com 
   6       www.fourwalledcubicle.com 
  10   Copyright 2010  Dean Camera (dean [at] fourwalledcubicle [dot] com) 
  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. 
  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 
  31 #define  INCLUDE_FROM_SERVICEDISCOVERYPROTOCOL_C 
  32 #include "ServiceDiscoveryProtocol.h" 
  38 } PROGMEM ServiceDiscoveryServer_Attribute_ServiceHandle 
= {(SDP_DATATYPE_UnsignedInt 
| SDP_DATASIZE_32Bit
), 0x00000000}; 
  44         ClassUUID_t UUIDList
[]; 
  45 } PROGMEM ServiceDiscoveryServer_Attribute_ServiceClassIDs 
= 
  47                 (SDP_DATATYPE_Sequence 
| SDP_DATASIZE_Variable16Bit
), (sizeof(ClassUUID_t
) * 1), 
  49                         {.Header 
= (SDP_DATATYPE_UUID 
| SDP_DATASIZE_128Bit
), .UUID 
= {BASE_96BIT_UUID
, 0x01, 0x00, 0x00, 0x00}} 
  57 } PROGMEM ServiceDiscoveryServer_Attribute_Version 
= {(SDP_DATATYPE_UnsignedInt 
| SDP_DATASIZE_16Bit
), 0x0100}; 
  59 /** Service Discovery Protocol attribute table, listing all supported attributes of the service. */ 
  60 const ServiceAttributeTable_t ServiceDiscoveryServer_Attribute_Table
[] PROGMEM 
= 
  62                 {.AttributeID 
= SDP_ATTRIBUTE_ID_SERVICERECORDHANDLE
, .Data 
= &ServiceDiscoveryServer_Attribute_ServiceHandle   
}, 
  63                 {.AttributeID 
= SDP_ATTRIBUTE_ID_SERVICECLASSIDS
,     .Data 
= &ServiceDiscoveryServer_Attribute_ServiceClassIDs 
}, 
  64                 {.AttributeID 
= SDP_ATTRIBUTE_ID_VERSION
,             .Data 
= &ServiceDiscoveryServer_Attribute_Version         
}, 
  66                 SERVICE_ATTRIBUTE_TABLE_TERMINATOR
 
  69 /** Service Discovery Protocol attribute, indicationg the service's name. */ 
  75 } PROGMEM SDP_Attribute_Name 
= {(SDP_DATATYPE_String 
| SDP_DATASIZE_Variable8Bit
), sizeof("SDP"), "SDP"}; 
  77 /** Service Discovery Protocol attribute, indicationg the service's description. */ 
  83 } PROGMEM SDP_Attribute_Description 
= {(SDP_DATATYPE_String 
| SDP_DATASIZE_Variable8Bit
), sizeof("BT Service Discovery"), "BT Service Discovery"}; 
  85 /** Service Discovery Protocol attribute, indicationg the service's availability. */ 
  90 } PROGMEM SDP_Attribute_Availability 
= {(SDP_DATATYPE_UnsignedInt 
| SDP_DATASIZE_8Bit
), 0xFF}; 
  96 } PROGMEM SDP_Attribute_LanguageOffset 
= {(SDP_DATATYPE_UnsignedInt 
| SDP_DATASIZE_16Bit
), SDP_ATTRIBUTE_LANGOFFSET
}; 
 102 } PROGMEM SDP_Attribute_ServiceHandle 
= {(SDP_DATATYPE_UnsignedInt 
| SDP_DATASIZE_32Bit
), 0x00000001}; 
 108         ClassUUID_t UUIDList
[]; 
 109 } PROGMEM SDP_Attribute_ServiceClassIDs 
= 
 111                 (SDP_DATATYPE_Sequence 
| SDP_DATASIZE_Variable16Bit
), (sizeof(ClassUUID_t
) * 1), 
 113                         {.Header 
= (SDP_DATATYPE_UUID 
| SDP_DATASIZE_128Bit
), .UUID 
= {BASE_96BIT_UUID
, 0x00, 0x00, 0x00, 0x01}} 
 117 /** Service Discovery Protocol attribute table, listing all supported attributes of the service. */ 
 118 const ServiceAttributeTable_t SDP_Attribute_Table
[] PROGMEM 
= 
 120                 {.AttributeID 
= SDP_ATTRIBUTE_ID_SERVICERECORDHANDLE
, .Data 
= &SDP_Attribute_ServiceHandle    
}, 
 121                 {.AttributeID 
= SDP_ATTRIBUTE_ID_SERVICECLASSIDS
,     .Data 
= &SDP_Attribute_ServiceClassIDs  
}, 
 122                 {.AttributeID 
= SDP_ATTRIBUTE_ID_LANGIDOFFSET
,        .Data 
= &SDP_Attribute_LanguageOffset   
}, 
 123                 {.AttributeID 
= SDP_ATTRIBUTE_ID_NAME
,                .Data 
= &SDP_Attribute_Name             
}, 
 124                 {.AttributeID 
= SDP_ATTRIBUTE_ID_DESCRIPTION
,         .Data 
= &SDP_Attribute_Description      
}, 
 126                 SERVICE_ATTRIBUTE_TABLE_TERMINATOR
 
 129 /** Master service table, listing all supported services (and their attribute tables) of the device, including 
 130  *  each service's UUID. 
 132 const ServiceTable_t SDP_Services_Table
[] PROGMEM 
= 
 134                 {   // 128-bit UUID for the Service Discovery Server Service 
 135                         .UUID  
= {BASE_96BIT_UUID
, 0x01, 0x00, 0x00, 0x00}, 
 136                         .AttributeTable 
= ServiceDiscoveryServer_Attribute_Table
, 
 138                 {   // 128-bit UUID for the SDP service 
 139                         .UUID  
= {BASE_96BIT_UUID
, 0x00, 0x00, 0x00, 0x01}, 
 140                         .AttributeTable 
= SDP_Attribute_Table
, 
 143                 {   // 128-bit UUID for the RFCOMM service 
 144                         .UUID  
= {BASE_96BIT_UUID
, 0x03, 0x00, 0x00, 0x00}, 
 145                         .AttributeTable 
= RFCOMM_Attribute_Table
, 
 150 /** Base UUID value common to all standardized Bluetooth services */ 
 151 const uint8_t BaseUUID
[] PROGMEM 
= {BASE_96BIT_UUID
, 0x00, 0x00, 0x00, 0x00}; 
 154 /** Main Service Discovery Protocol packet processing routine. This function processes incomming SDP packets from 
 155  *  a connected Bluetooth device, and sends back appropriate responses to allow other devices to determine the 
 156  *  services the local device exposes. 
 158  *  \param[in]  Data     Incomming packet data containing the SDP request 
 159  *  \param[in]  Channel  Channel the request was issued to by the remote device 
 161 void SDP_ProcessPacket(void* Data
, Bluetooth_Channel_t
* Channel
) 
 163         SDP_PDUHeader_t
* SDPHeader 
= (SDP_PDUHeader_t
*)Data
; 
 164         SDPHeader
->ParameterLength 
= SwapEndian_16(SDPHeader
->ParameterLength
); 
 166         BT_SDP_DEBUG(1, "SDP Packet Received"); 
 167         BT_SDP_DEBUG(2, "-- PDU ID: 0x%02X", SDPHeader
->PDU
); 
 168         BT_SDP_DEBUG(2, "-- Param Length: 0x%04X", SDPHeader
->ParameterLength
); 
 170         switch (SDPHeader
->PDU
) 
 172                 case SDP_PDU_SERVICESEARCHREQUEST
: 
 173                         SDP_ProcessServiceSearch(SDPHeader
, Channel
); 
 175                 case SDP_PDU_SERVICEATTRIBUTEREQUEST
: 
 176                         SDP_ProcessServiceAttribute(SDPHeader
, Channel
); 
 178                 case SDP_PDU_SERVICESEARCHATTRIBUTEREQUEST
: 
 179                         SDP_ProcessServiceSearchAttribute(SDPHeader
, Channel
); 
 184 /** Internal processing routine for SDP Service Search Requests. 
 186  *  \param[in] SDPHeader  Pointer to the start of the issued SDP request 
 187  *  \param[in] Channel    Pointer to the Bluetooth channel structure the request was issued to 
 189 static void SDP_ProcessServiceSearch(const SDP_PDUHeader_t
* const SDPHeader
, Bluetooth_Channel_t
* const Channel
) 
 191         BT_SDP_DEBUG(1, "<< Service Search"); 
 194 /** Internal processing routine for SDP Service Attribute Requests. 
 196  *  \param[in] SDPHeader  Pointer to the start of the issued SDP request 
 197  *  \param[in] Channel    Pointer to the Bluetooth channel structure the request was issued to 
 199 static void SDP_ProcessServiceAttribute(const SDP_PDUHeader_t
* const SDPHeader
, Bluetooth_Channel_t
* const Channel
) 
 201         BT_SDP_DEBUG(1, "<< Service Attribute"); 
 204 /** Internal processing routine for SDP Service Search Attribute Requests. 
 206  *  \param[in] SDPHeader  Pointer to the start of the issued SDP request 
 207  *  \param[in] Channel    Pointer to the Bluetooth channel structure the request was issued to 
 209 static void SDP_ProcessServiceSearchAttribute(const SDP_PDUHeader_t
* const SDPHeader
, Bluetooth_Channel_t
* const Channel
) 
 211         const void* CurrentParameter 
= ((void*)SDPHeader 
+ sizeof(SDP_PDUHeader_t
)); 
 213         BT_SDP_DEBUG(1, "<< Service Search Attribute"); 
 215         /* Retrieve the list of search UUIDs from the request */ 
 216         uint8_t UUIDList
[12][UUID_SIZE_BYTES
]; 
 217         uint8_t TotalUUIDs 
= SDP_GetUUIDList(UUIDList
, &CurrentParameter
); 
 218         BT_SDP_DEBUG(2, "-- Total UUIDs: %d", TotalUUIDs
); 
 220         /* Retrieve the maximum Attribute reponse size from the request */ 
 221         uint16_t MaxAttributeSize 
= *((uint16_t*)CurrentParameter
); 
 222         CurrentParameter 
+= sizeof(uint16_t); 
 223         BT_SDP_DEBUG(2, "-- Max Return Attribute Bytes: 0x%04X", MaxAttributeSize
); 
 225         /* Retrieve the list of Attributes from the request */ 
 226         uint16_t AttributeList
[15][2]; 
 227         uint8_t  TotalAttributes 
= SDP_GetAttributeList(AttributeList
, &CurrentParameter
); 
 228         BT_SDP_DEBUG(2, "-- Total Attributes: %d", TotalAttributes
); 
 232                 SDP_PDUHeader_t SDPHeader
; 
 233                 uint16_t        AttributeListByteCount
; 
 234                 uint8_t         ResponseData
[100]; 
 237         /* Create a pointer to the buffer to indicate the current location for response data to be added */ 
 238         void* CurrResponsePos 
= ResponsePacket
.ResponseData
; 
 240         /* Clamp the maximum attribute size to the size of the allocated buffer */ 
 241         if (MaxAttributeSize 
> sizeof(ResponsePacket
.ResponseData
)) 
 242           MaxAttributeSize 
= sizeof(ResponsePacket
.ResponseData
); 
 244         /* Add the outer Data Element Sequence header for the retrieved Attributes */ 
 245         uint16_t* TotalResponseSize 
= SDP_AddDataElementHeader16(&CurrResponsePos
, SDP_DATATYPE_Sequence
); 
 247         /* Search through the list of UUIDs one at a time looking for matching search Attributes */ 
 248         for (uint8_t CurrUUIDItem 
= 0; CurrUUIDItem 
< TotalUUIDs
; CurrUUIDItem
++) 
 250                 /* Retrieve the attribute table of the current search UUID from the global UUID table if it exists */ 
 251                 ServiceAttributeTable_t
* AttributeTable 
= SDP_GetAttributeTable(UUIDList
[CurrUUIDItem
]); 
 253                 /* If the UUID does not exist in the global UUID table, continue on to the next search UUID */ 
 254                 if (AttributeTable 
== NULL
) 
 257                 BT_SDP_DEBUG(2, " -- Found UUID %d in table", CurrUUIDItem
); 
 259                 /* Add an inner Data Element Sequence header for the current UUID's found Attributes */ 
 260                 uint16_t* CurrentUUIDResponseSize 
= SDP_AddDataElementHeader16(&CurrResponsePos
, SDP_DATATYPE_Sequence
); 
 262                 /* Search through the list of Attributes one at a time looking for values in the current UUID's Attribute table */ 
 263                 for (uint8_t CurrAttribute 
= 0; CurrAttribute 
< TotalAttributes
; CurrAttribute
++) 
 265                         uint16_t* AttributeIDRange 
= AttributeList
[CurrAttribute
]; 
 267                         /* Look in the current Attribute Range for a matching Attribute ID in the UUID's Attribute table */ 
 268                         for (uint32_t CurrAttributeID 
= AttributeIDRange
[0]; CurrAttributeID 
<= AttributeIDRange
[1]; CurrAttributeID
++) 
 270                                 /* Retrieve a PROGMEM pointer to the value of the current Attribute ID, if it exists in the UUID's Attribute table */ 
 271                                 const void* AttributeValue 
= SDP_GetAttributeValue(AttributeTable
, CurrAttributeID
); 
 273                                 /* If the Attribute does not exist in the current UUID's Attribute table, continue to the next Attribute ID */ 
 274                                 if (AttributeValue 
== NULL
) 
 277                                 BT_SDP_DEBUG(2, " -- Add Attribute 0x%04X", CurrAttributeID
); 
 279                                 /* Increment the current UUID's returned Attribute container size by the number of added bytes */ 
 280                                 *CurrentUUIDResponseSize 
+= SDP_AddAttributeToResponse(CurrAttributeID
, AttributeValue
, &CurrResponsePos
); 
 283                         /* Increment the outer container size by the number of added bytes */ 
 284                         *TotalResponseSize 
+= 3 + *CurrentUUIDResponseSize
; 
 288         /* Set the total response list size to the size of the outer container plus its header size */ 
 289         ResponsePacket
.AttributeListByteCount    
= 3 + *TotalResponseSize
; 
 291         /* Fill in the response packet's header */ 
 292         ResponsePacket
.SDPHeader
.PDU             
= SDP_PDU_SERVICESEARCHATTRIBUTERESPONSE
; 
 293         ResponsePacket
.SDPHeader
.TransactionID   
= SDPHeader
->TransactionID
; 
 294         ResponsePacket
.SDPHeader
.ParameterLength 
= (ResponsePacket
.AttributeListByteCount 
+ sizeof(ResponsePacket
.AttributeListByteCount
)); 
 296         BT_SDP_DEBUG(1, ">> Service Search Attribute Response"); 
 297         BT_SDP_DEBUG(2, "-- Total Parameter Length: 0x%04X", ResponsePacket
.SDPHeader
.ParameterLength
); 
 299         /* Send the completed response packet to the sender */ 
 300         Bluetooth_SendPacket(&ResponsePacket
, (sizeof(ResponsePacket
.SDPHeader
) + ResponsePacket
.SDPHeader
.ParameterLength
), 
 304 /** Adds the given attribute ID and value to the reponse buffer, and advances the response buffer pointer past the added data. 
 306  *  \param[in] AttributeID          Attribute ID to add to the response buffer 
 307  *  \param[in] AttributeValue       Pointer to the start of the Attribute's value, located in PROGMEM 
 308  *  \param[in, out] ResponseBuffer  Pointer to a buffer where the Attribute and Attribute Value is to be added 
 310  *  \return Number of bytes added to the response buffer 
 312 static uint16_t SDP_AddAttributeToResponse(const uint16_t AttributeID
, const void* AttributeValue
, void** ResponseBuffer
) 
 314         /* Retrieve the size of the attribute value from its container header */ 
 315         uint32_t AttributeValueLength 
= SDP_GetLocalAttributeContainerSize(AttributeValue
); 
 317         /* Add a Data Element header to the response for the Attribute ID */ 
 318         *((uint8_t*)*ResponseBuffer
) = (SDP_DATATYPE_UnsignedInt 
| SDP_DATASIZE_16Bit
); 
 319         *ResponseBuffer 
+= sizeof(uint8_t); 
 321         /* Add the Attribute ID to the created Data Element */ 
 322         *((uint16_t*)*ResponseBuffer
) = AttributeID
; 
 323         *ResponseBuffer 
+= sizeof(uint16_t); 
 325         /* Copy over the Attribute value Data Element container to the response */ 
 326         memcpy_P(*ResponseBuffer
, AttributeValue
, AttributeValueLength
); 
 327         *ResponseBuffer 
+= AttributeValueLength
; 
 329         return (sizeof(uint8_t) + sizeof(uint16_t) + AttributeValueLength
); 
 332 /** Retrieves a pointer to the value of the given Attribute ID from the given Attribute table. 
 334  *  \param[in] AttributeTable  Pointer to the Attribute table to search in 
 335  *  \param[in] AttributeID     Attribute ID to search for within the table 
 337  *  \return Pointer to the start of the Attribute's value if found within the table, NULL otherwise 
 339 static void* SDP_GetAttributeValue(const ServiceAttributeTable_t
* AttributeTable
, const uint16_t AttributeID
) 
 341         void* CurrTableItemData
; 
 343         /* Search through the current Attribute table, abort when the terminator item has been reached */ 
 344         while ((CurrTableItemData 
= (void*)pgm_read_word(&AttributeTable
->Data
)) != NULL
) 
 346                 /* Check if the current Attribute ID matches the search ID - if so return a pointer to it */ 
 347                 if (pgm_read_word(&AttributeTable
->AttributeID
) == AttributeID
) 
 348                   return CurrTableItemData
; 
 356 /** Retrieves the Attribute table for the given UUID if it exists. 
 358  *  \param[in] UUID  UUID to search for 
 360  *  \return Pointer to the UUID's associated Attribute table if found in the global UUID table, NULL otherwise 
 362 static ServiceAttributeTable_t
* SDP_GetAttributeTable(const uint8_t* const UUID
) 
 364         /* Search through the global UUID list an item at a time */ 
 365         for (uint8_t CurrTableItem 
= 0; CurrTableItem 
< (sizeof(SDP_Services_Table
) / sizeof(ServiceTable_t
)); CurrTableItem
++) 
 367                 /* If the current table item's UUID matches the search UUID, return a pointer the table item's Attribute table */ 
 368                 if (!(memcmp_P(UUID
, SDP_Services_Table
[CurrTableItem
].UUID
, UUID_SIZE_BYTES
))) 
 369                   return (ServiceAttributeTable_t
*)pgm_read_word(&SDP_Services_Table
[CurrTableItem
].AttributeTable
); 
 375 /** Reads in the collection of Attribute ranges from the input buffer's Data Element Sequence container, into the given  
 376  *  Attribute list for later use. Once complete, the input buffer pointer is advanced to the end of the Attribute container. 
 378  *  \param[out] AttributeList     Pointer to a buffer where the list of Attribute ranges are to be stored 
 379  *  \param[in]  CurrentParameter  Pointer to a Buffer containing a Data Element Sequence of Attribute and Attribute Range elements 
 381  *  \return Total number of Attribute ranges stored in the Data Element Sequence 
 383 static uint8_t SDP_GetAttributeList(uint16_t AttributeList
[][2], const void** const CurrentParameter
) 
 385         uint8_t ElementHeaderSize
; 
 386         uint8_t TotalAttributes 
= 0; 
 388         /* Retrieve the total size of the Attribute container, and unwrap the outer Data Element Sequence container */ 
 389         uint16_t AttributeIDListLength 
= SDP_GetDataElementSize(CurrentParameter
, &ElementHeaderSize
); 
 390         BT_SDP_DEBUG(2, "-- Total Attribute Length: 0x%04X", AttributeIDListLength
); 
 391         while (AttributeIDListLength
) 
 393                 /* Retrieve the size of the next Attribute in the container and get a pointer to the next free Attribute element in the list */ 
 394                 uint16_t* CurrentAttributeRange 
= AttributeList
[TotalAttributes
++]; 
 395                 uint8_t   AttributeLength       
= SDP_GetDataElementSize(CurrentParameter
, &ElementHeaderSize
); 
 397                 /* Copy over the starting Attribute ID and (if it the current element is a range) the ending Attribute ID */ 
 398                 memcpy(&CurrentAttributeRange
[0], *CurrentParameter
, AttributeLength
); 
 400                 /* If the element is not an Attribute Range, copy over the starting ID to the ending ID to make a range of 1 */ 
 401                 if (AttributeLength 
== 2) 
 402                   memcpy(&CurrentAttributeRange
[1], *CurrentParameter
, 2); 
 404                 BT_SDP_DEBUG(2, "-- Attribute: 0x%04X-0x%04X", CurrentAttributeRange
[0], CurrentAttributeRange
[1]); 
 406                 AttributeIDListLength 
-= (AttributeLength 
+ ElementHeaderSize
); 
 407                 *CurrentParameter     
+= AttributeLength
; 
 410         return TotalAttributes
; 
 413 /** Reads in the collection of UUIDs from the input buffer's Data Element Sequence container, into the given  
 414  *  UUID list for later use. Once complete, the input buffer pointer is advanced to the end of the UUID container. 
 416  *  \param[out] UUIDList          Pointer to a buffer where the list of UUIDs are to be stored 
 417  *  \param[in]  CurrentParameter  Pointer to a Buffer containing a Data Element Sequence of UUID elements 
 419  *  \return Total number of UUIDs stored in the Data Element Sequence 
 421 static uint8_t SDP_GetUUIDList(uint8_t UUIDList
[][UUID_SIZE_BYTES
], const void** const CurrentParameter
) 
 423         uint8_t ElementHeaderSize
; 
 424         uint8_t TotalUUIDs 
= 0; 
 426         /* Retrieve the total size of the UUID container, and unwrap the outer Data Element Sequence container */ 
 427         uint16_t ServicePatternLength 
= SDP_GetDataElementSize(CurrentParameter
, &ElementHeaderSize
); 
 428         BT_SDP_DEBUG(2, "-- Total UUID Length: 0x%04X", ServicePatternLength
); 
 429         while (ServicePatternLength
) 
 431                 /* Retrieve the size of the next UUID in the container and get a pointer to the next free UUID element in the list */ 
 432                 uint8_t* CurrentUUID 
= UUIDList
[TotalUUIDs
++]; 
 433                 uint8_t  UUIDLength  
= SDP_GetDataElementSize(CurrentParameter
, &ElementHeaderSize
); 
 435                 /* Copy over the base UUID value to the free UUID slot in the list */ 
 436                 memcpy_P(CurrentUUID
, BaseUUID
, sizeof(BaseUUID
)); 
 438                 /* Copy over UUID from the container to the free slot - if a short UUID (<= 4 bytes) it replaces the lower 
 439                    4 bytes of the base UUID, otherwise it replaces the UUID completely */ 
 440                 memcpy(&CurrentUUID
[(UUIDLength 
<= 4) ? 
(UUID_SIZE_BYTES 
- 4) : 0], *CurrentParameter
, UUIDLength
); 
 442                 BT_SDP_DEBUG(2, "-- UUID (%d): 0x%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", 
 444                                 CurrentUUID
[15], CurrentUUID
[14], CurrentUUID
[13], CurrentUUID
[12], 
 445                                 CurrentUUID
[11], CurrentUUID
[10], CurrentUUID
[9],  CurrentUUID
[8], 
 446                                 CurrentUUID
[7],  CurrentUUID
[6],  CurrentUUID
[5],  CurrentUUID
[4], 
 447                                 CurrentUUID
[3],  CurrentUUID
[2],  CurrentUUID
[1],  CurrentUUID
[0]); 
 449                 ServicePatternLength 
-= (UUIDLength 
+ ElementHeaderSize
); 
 450                 *CurrentParameter    
+= UUIDLength
; 
 456 /** Retrieves the total size of the given locally stored (in PROGMEM) attribute Data Element container. 
 458  *  \param[in] AttributeData  Pointer to the start of the Attribute container, located in PROGMEM 
 460  *  \return Size in bytes of the entire attribute container, including the header 
 462 static uint32_t SDP_GetLocalAttributeContainerSize(const void* const AttributeData
) 
 464         /* Fetch the size of the Data Element structure from the header */ 
 465         uint8_t SizeIndex 
= (pgm_read_byte(AttributeData
) & 0x07); 
 467         /* Convert the Data Element size index into a size in bytes */ 
 470                 case SDP_DATASIZE_Variable8Bit
: 
 471                         return (1 + sizeof(uint8_t))  + pgm_read_byte(AttributeData 
+ 1); 
 472                 case SDP_DATASIZE_Variable16Bit
: 
 473                         return (1 + sizeof(uint16_t)) + pgm_read_word(AttributeData 
+ 1); 
 474                 case SDP_DATASIZE_Variable32Bit
: 
 475                         return (1 + sizeof(uint32_t)) + pgm_read_dword(AttributeData 
+ 1); 
 477                         return (1 + (1 << SizeIndex
)); 
 483 /** Retrieves the size of a Data Element container from the current input buffer, and advances the input buffer 
 484  *  pointer to the start of the Data Element's contents. 
 486  *  \param[in, out] DataElementHeader  Pointer to the start of a Data Element header 
 487  *  \param[out]     ElementHeaderSize  Size in bytes of the header that was skipped 
 489  *  \return Size in bytes of the Data Element container's contents, minus the header 
 491 static uint32_t SDP_GetDataElementSize(const void** const DataElementHeader
, uint8_t* const ElementHeaderSize
) 
 493         /* Fetch the size of the Data Element structure from the header, increment the current buffer pos */ 
 494         uint8_t SizeIndex 
= (*((uint8_t*)*DataElementHeader
) & 0x07); 
 495         *DataElementHeader 
+= sizeof(uint8_t); 
 497         uint32_t ElementValue
; 
 499         /* Convert the Data Element size index into a size in bytes */ 
 502                 case SDP_DATASIZE_Variable8Bit
: 
 503                         ElementValue 
= *((uint8_t*)*DataElementHeader
); 
 504                         *DataElementHeader 
+= sizeof(uint8_t); 
 505                         *ElementHeaderSize  
= (1 + sizeof(uint8_t)); 
 507                 case SDP_DATASIZE_Variable16Bit
: 
 508                         ElementValue 
= *((uint16_t*)*DataElementHeader
); 
 509                         *DataElementHeader 
+= sizeof(uint16_t); 
 510                         *ElementHeaderSize  
= (1 + sizeof(uint16_t)); 
 512                 case SDP_DATASIZE_Variable32Bit
: 
 513                         ElementValue 
= *((uint32_t*)*DataElementHeader
); 
 514                         *DataElementHeader 
+= sizeof(uint32_t); 
 515                         *ElementHeaderSize  
= (1 + sizeof(uint32_t)); 
 518                         ElementValue 
= (1 << SizeIndex
); 
 519                         *ElementHeaderSize 
= 1;