715 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			715 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|              LUFA Library
 | |
|      Copyright (C) Dean Camera, 2010.
 | |
|               
 | |
|   dean [at] fourwalledcubicle [dot] com
 | |
|       www.fourwalledcubicle.com
 | |
| */
 | |
| 
 | |
| /*
 | |
|   Copyright 2010  Dean Camera (dean [at] fourwalledcubicle [dot] com)
 | |
| 
 | |
|   Permission to use, copy, modify, distribute, and sell this 
 | |
|   software and its documentation for any purpose is hereby granted
 | |
|   without fee, provided that the above copyright notice appear in 
 | |
|   all copies and that both that the copyright notice and this
 | |
|   permission notice and warranty disclaimer appear in supporting 
 | |
|   documentation, and that the name of the author not be used in 
 | |
|   advertising or publicity pertaining to distribution of the 
 | |
|   software without specific, written prior permission.
 | |
| 
 | |
|   The author disclaim all warranties with regard to this
 | |
|   software, including all implied warranties of merchantability
 | |
|   and fitness.  In no event shall the author be liable for any
 | |
|   special, indirect or consequential damages or any damages
 | |
|   whatsoever resulting from loss of use, data or profits, whether
 | |
|   in an action of contract, negligence or other tortious action,
 | |
|   arising out of or in connection with the use or performance of
 | |
|   this software.
 | |
| */
 | |
| 
 | |
| /** \file
 | |
|  *
 | |
|  *  SDP layer module. This module implements a simple Service Discovery
 | |
|  *  Protocol server, which can broadcast the device's supported services
 | |
|  *  to other Bluetooth devices upon request, so that they can determine
 | |
|  *  what services are available.
 | |
|  */
 | |
| 
 | |
| /*
 | |
| 	TODO: Honor remote device's buffer size constraints via continuation state
 | |
|  */
 | |
| 
 | |
| #define  INCLUDE_FROM_SERVICEDISCOVERYPROTOCOL_C
 | |
| #include "SDP.h"
 | |
| 
 | |
| /** Service attribute table list, containing a pointer to each service attribute table the device contains */
 | |
| const ServiceAttributeTable_t* SDP_Services_Table[] PROGMEM =
 | |
| 	{
 | |
| 		SerialPort_Attribute_Table,
 | |
| 	};
 | |
| 
 | |
| /** Base UUID value common to all standardized Bluetooth services */
 | |
| const UUID_t BaseUUID PROGMEM = {0x00000000, BASE_80BIT_UUID};
 | |
| 
 | |
| /** Main Service Discovery Protocol packet processing routine. This function processes incoming SDP packets from
 | |
|  *  a connected Bluetooth device, and sends back appropriate responses to allow other devices to determine the
 | |
|  *  services the local device exposes.
 | |
|  *
 | |
|  *  \param[in] Data     Incoming packet data containing the SDP request
 | |
|  *  \param[in] Channel  ACL channel the request was issued to by the remote device
 | |
|  */
 | |
| void SDP_ProcessPacket(void* Data, Bluetooth_Channel_t* const Channel)
 | |
| {
 | |
| 	SDP_PDUHeader_t* SDPHeader = (SDP_PDUHeader_t*)Data;
 | |
| 	SDPHeader->ParameterLength = SwapEndian_16(SDPHeader->ParameterLength);
 | |
| 
 | |
| 	BT_SDP_DEBUG(1, "SDP Packet Received");
 | |
| 	BT_SDP_DEBUG(2, "-- PDU ID: 0x%02X", SDPHeader->PDU);
 | |
| 	BT_SDP_DEBUG(2, "-- Param Length: 0x%04X", SDPHeader->ParameterLength);
 | |
| 
 | |
| 	/* Dispatch to the correct processing routine for the given SDP packet type */
 | |
| 	switch (SDPHeader->PDU)
 | |
| 	{
 | |
| 		case SDP_PDU_SERVICESEARCHREQUEST:
 | |
| 			SDP_ProcessServiceSearch(SDPHeader, Channel);
 | |
| 			break;		
 | |
| 		case SDP_PDU_SERVICEATTRIBUTEREQUEST:
 | |
| 			SDP_ProcessServiceAttribute(SDPHeader, Channel);
 | |
| 			break;
 | |
| 		case SDP_PDU_SERVICESEARCHATTRIBUTEREQUEST:
 | |
| 			SDP_ProcessServiceSearchAttribute(SDPHeader, Channel);
 | |
| 			break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /** Internal processing routine for SDP Service Search Requests.
 | |
|  *
 | |
|  *  \param[in] SDPHeader  Pointer to the start of the issued SDP request
 | |
|  *  \param[in] Channel    Pointer to the Bluetooth channel structure the request was issued to
 | |
|  */
 | |
| static void SDP_ProcessServiceSearch(const SDP_PDUHeader_t* const SDPHeader,
 | |
|                                      Bluetooth_Channel_t* const Channel)
 | |
| {
 | |
| 	const void* CurrentParameter = ((const void*)SDPHeader + sizeof(SDP_PDUHeader_t));
 | |
| 
 | |
| 	BT_SDP_DEBUG(1, "<< Service Search");
 | |
| 
 | |
| 	/* Retrieve the list of search UUIDs from the request */
 | |
| 	uint8_t UUIDList[12][UUID_SIZE_BYTES];
 | |
| 	uint8_t TotalUUIDs = SDP_GetUUIDList(UUIDList, &CurrentParameter);
 | |
| 	BT_SDP_DEBUG(2, "-- Total UUIDs: %d", TotalUUIDs);
 | |
| 	
 | |
| 	/* Retrieve the maximum service record response count from the request */
 | |
| 	uint16_t MaxServiceRecordCount = SDP_ReadData16(&CurrentParameter);
 | |
| 	BT_SDP_DEBUG(2, "-- Max Return Service Count: 0x%04X", MaxServiceRecordCount);
 | |
| 	
 | |
| 	struct
 | |
| 	{
 | |
| 		SDP_PDUHeader_t SDPHeader;
 | |
| 		uint16_t        TotalServiceRecordCount;
 | |
| 		uint16_t        CurrentServiceRecordCount;
 | |
| 		uint8_t         ResponseData[100];
 | |
| 	} ResponsePacket;
 | |
| 	
 | |
| 	uint8_t AddedServiceHandles = 0;
 | |
| 
 | |
| 	/* Create a pointer to the buffer to indicate the current location for response data to be added */
 | |
| 	void* CurrResponsePos = ResponsePacket.ResponseData;
 | |
| 
 | |
| 	/* Search through the global service list an item at a time */
 | |
| 	for (uint8_t CurrTableItem = 0; CurrTableItem < (sizeof(SDP_Services_Table) / sizeof(void*)); CurrTableItem++)
 | |
| 	{
 | |
| 		/* Read in a pointer to the current UUID table entry's Attribute table */
 | |
| 		ServiceAttributeTable_t* CurrAttributeTable = pgm_read_ptr(&SDP_Services_Table[CurrTableItem]);
 | |
| 
 | |
| 		if (!(SDP_SearchServiceTable(UUIDList, TotalUUIDs, CurrAttributeTable)))
 | |
| 		  continue;
 | |
| 
 | |
| 		BT_SDP_DEBUG(2, " -- Found search match in table");
 | |
| 
 | |
| 		/* Retrieve a PROGMEM pointer to the value of the service's record handle */
 | |
| 		const void* AttributeValue = SDP_GetAttributeValue(CurrAttributeTable, SDP_ATTRIBUTE_ID_SERVICERECORDHANDLE);
 | |
| 
 | |
| 		/* Copy over the service record handle to the response list */
 | |
| 		uint8_t AttrHeaderSize;
 | |
| 		uint8_t AttrSize = SDP_GetLocalAttributeContainerSize(AttributeValue, &AttrHeaderSize);
 | |
| 		memcpy_P(CurrResponsePos, AttributeValue + AttrHeaderSize, AttrSize);
 | |
| 		CurrResponsePos += AttrHeaderSize + AttrSize;
 | |
| 		
 | |
| 		AddedServiceHandles++;
 | |
| 	}
 | |
| 
 | |
| 	/* Continuation state - always zero */
 | |
| 	SDP_WriteData8(&CurrResponsePos, 0);
 | |
| 
 | |
| 	/* Fill out the service record count values in the returned packet */
 | |
| 	ResponsePacket.TotalServiceRecordCount   = SwapEndian_16(AddedServiceHandles);
 | |
| 	ResponsePacket.CurrentServiceRecordCount = ResponsePacket.TotalServiceRecordCount;
 | |
| 
 | |
| 	/* Calculate the total parameter length that is to be sent, including the fixed return parameters, the created service
 | |
| 	   handle list and the SDP continuation state */
 | |
| 	uint16_t ParamLength = (ResponsePacket.CurrentServiceRecordCount << 2) +
 | |
| 	                        sizeof(ResponsePacket.CurrentServiceRecordCount) +
 | |
| 	                        sizeof(ResponsePacket.TotalServiceRecordCount) +
 | |
| 	                        sizeof(uint8_t);
 | |
| 
 | |
| 	/* Fill in the response packet's header */
 | |
| 	ResponsePacket.SDPHeader.PDU             = SDP_PDU_SERVICESEARCHRESPONSE;
 | |
| 	ResponsePacket.SDPHeader.TransactionID   = SDPHeader->TransactionID;
 | |
| 	ResponsePacket.SDPHeader.ParameterLength = SwapEndian_16(ParamLength);
 | |
| 
 | |
| 	BT_SDP_DEBUG(1, ">> Service Search Response");
 | |
| 
 | |
| 	/* Send the completed response packet to the sender */
 | |
| 	Bluetooth_SendPacket(&ResponsePacket, (sizeof(ResponsePacket.SDPHeader) + ParamLength), Channel);
 | |
| }
 | |
| 
 | |
| /** Internal processing routine for SDP Service Attribute Requests.
 | |
|  *
 | |
|  *  \param[in] SDPHeader  Pointer to the start of the issued SDP request
 | |
|  *  \param[in] Channel    Pointer to the Bluetooth channel structure the request was issued to
 | |
|  */
 | |
| static void SDP_ProcessServiceAttribute(const SDP_PDUHeader_t* const SDPHeader,
 | |
|                                         Bluetooth_Channel_t* const Channel)
 | |
| {
 | |
| 	const void* CurrentParameter = ((const void*)SDPHeader + sizeof(SDP_PDUHeader_t));
 | |
| 
 | |
| 	BT_SDP_DEBUG(1, "<< Service Attribute");
 | |
| 
 | |
| 	/* Retrieve the service handle whose attributes are to be examined */
 | |
| 	uint32_t ServiceHandle = SDP_ReadData32(&CurrentParameter);
 | |
| 	BT_SDP_DEBUG(2, "-- Service Handle: 0x%08lX", ServiceHandle);
 | |
| 	
 | |
| 	/* Retrieve the maximum Attribute response size from the request */
 | |
| 	uint16_t MaxAttributeSize = SDP_ReadData16(&CurrentParameter);
 | |
| 	BT_SDP_DEBUG(2, "-- Max Return Attribute Bytes: 0x%04X", MaxAttributeSize);
 | |
| 	
 | |
| 	/* Retrieve the list of Attributes from the request */
 | |
| 	uint16_t AttributeList[8][2];
 | |
| 	uint8_t  TotalAttributes = SDP_GetAttributeList(AttributeList, &CurrentParameter);
 | |
| 	BT_SDP_DEBUG(2, "-- Total Attributes: %d", TotalAttributes);
 | |
| 
 | |
| 	struct
 | |
| 	{
 | |
| 		SDP_PDUHeader_t SDPHeader;
 | |
| 		uint16_t        AttributeListByteCount;
 | |
| 		uint8_t         ResponseData[100];
 | |
| 	} ResponsePacket;
 | |
| 
 | |
| 	/* Create a pointer to the buffer to indicate the current location for response data to be added */
 | |
| 	void* CurrResponsePos = ResponsePacket.ResponseData;
 | |
| 
 | |
| 	/* Clamp the maximum attribute size to the size of the allocated buffer */
 | |
| 	if (MaxAttributeSize > sizeof(ResponsePacket.ResponseData))
 | |
| 	  MaxAttributeSize = sizeof(ResponsePacket.ResponseData);
 | |
| 
 | |
| 	uint16_t TotalResponseSize = 0;
 | |
| 
 | |
| 	/* Search through the global UUID list an item at a time */
 | |
| 	for (uint8_t CurrTableItem = 0; CurrTableItem < (sizeof(SDP_Services_Table) / sizeof(void*)); CurrTableItem++)
 | |
| 	{
 | |
| 		/* Read in a pointer to the current UUID table entry's Attribute table */
 | |
| 		ServiceAttributeTable_t* CurrAttributeTable = pgm_read_ptr(&SDP_Services_Table[CurrTableItem]);
 | |
| 		
 | |
| 		/* Retrieve a PROGMEM pointer to the value of the Service Record Handle */
 | |
| 		const void* ServiceRecord = SDP_GetAttributeValue(CurrAttributeTable, SDP_ATTRIBUTE_ID_SERVICERECORDHANDLE);
 | |
| 		
 | |
| 		/* Get the size of the header for the Service Record Handle */
 | |
| 		uint8_t AttrHeaderSize;
 | |
| 		SDP_GetLocalAttributeContainerSize(ServiceRecord, &AttrHeaderSize);
 | |
| 		
 | |
| 		/* Retrieve the endian-swapped service handle of the current service being examined */
 | |
| 		uint32_t CurrServiceHandle = SwapEndian_32(pgm_read_dword(ServiceRecord + AttrHeaderSize));
 | |
| 		
 | |
| 		/* Check if the current service in the service table has the requested service handle */
 | |
| 		if (ServiceHandle == CurrServiceHandle)
 | |
| 		{
 | |
| 			/* Add the listed attributes for the found UUID to the response */
 | |
| 			TotalResponseSize = SDP_AddListedAttributesToResponse(CurrAttributeTable, AttributeList, TotalAttributes,
 | |
| 		                                                          &CurrResponsePos);
 | |
| 			
 | |
| 			/* Requested service found, abort the search through the service table */
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Continuation state - always zero */
 | |
| 	SDP_WriteData8(&CurrResponsePos, 0);
 | |
| 
 | |
| 	/* Set the total response list size to the size of the outer container plus its header size and continuation state */
 | |
| 	ResponsePacket.AttributeListByteCount    = SwapEndian_16(TotalResponseSize);
 | |
| 
 | |
| 	/* Calculate the total parameter length that is to be sent, including the fixed return parameters, the created attribute
 | |
| 	   value list and the SDP continuation state */
 | |
| 	uint16_t ParamLength = (sizeof(ResponsePacket.AttributeListByteCount) + TotalResponseSize + sizeof(uint8_t));
 | |
| 	
 | |
| 	/* Fill in the response packet's header */
 | |
| 	ResponsePacket.SDPHeader.PDU             = SDP_PDU_SERVICEATTRIBUTERESPONSE;
 | |
| 	ResponsePacket.SDPHeader.TransactionID   = SDPHeader->TransactionID;
 | |
| 	ResponsePacket.SDPHeader.ParameterLength = SwapEndian_16(ParamLength);
 | |
| 
 | |
| 	BT_SDP_DEBUG(1, ">> Service Attribute Response");
 | |
| 	BT_SDP_DEBUG(2, "-- Param Len 0x%04X", ParamLength);
 | |
| 
 | |
| 	/* Send the completed response packet to the sender */
 | |
| 	Bluetooth_SendPacket(&ResponsePacket, (sizeof(ResponsePacket.SDPHeader) + ParamLength), Channel);
 | |
| }
 | |
| 
 | |
| /** Internal processing routine for SDP Service Search Attribute Requests.
 | |
|  *
 | |
|  *  \param[in] SDPHeader  Pointer to the start of the issued SDP request
 | |
|  *  \param[in] Channel    Pointer to the Bluetooth channel structure the request was issued to
 | |
|  */
 | |
| static void SDP_ProcessServiceSearchAttribute(const SDP_PDUHeader_t* const SDPHeader,
 | |
|                                               Bluetooth_Channel_t* const Channel)
 | |
| {
 | |
| 	const void* CurrentParameter = ((const void*)SDPHeader + sizeof(SDP_PDUHeader_t));
 | |
| 	
 | |
| 	BT_SDP_DEBUG(1, "<< Service Search Attribute");
 | |
| 
 | |
| 	/* Retrieve the list of search UUIDs from the request */
 | |
| 	uint8_t UUIDList[12][UUID_SIZE_BYTES];
 | |
| 	uint8_t TotalUUIDs = SDP_GetUUIDList(UUIDList, &CurrentParameter);
 | |
| 	BT_SDP_DEBUG(2, "-- Total UUIDs: %d", TotalUUIDs);
 | |
| 	
 | |
| 	/* Retrieve the maximum Attribute response size from the request */
 | |
| 	uint16_t MaxAttributeSize = SDP_ReadData16(&CurrentParameter);
 | |
| 	BT_SDP_DEBUG(2, "-- Max Return Attribute Bytes: 0x%04X", MaxAttributeSize);
 | |
| 	
 | |
| 	/* Retrieve the list of Attributes from the request */
 | |
| 	uint16_t AttributeList[8][2];
 | |
| 	uint8_t  TotalAttributes = SDP_GetAttributeList(AttributeList, &CurrentParameter);
 | |
| 	BT_SDP_DEBUG(2, "-- Total Attributes: %d", TotalAttributes);
 | |
| 	
 | |
| 	struct
 | |
| 	{
 | |
| 		SDP_PDUHeader_t SDPHeader;
 | |
| 		uint16_t        AttributeListByteCount;
 | |
| 		uint8_t         ResponseData[100];
 | |
| 	} ResponsePacket;
 | |
| 	
 | |
| 	/* Create a pointer to the buffer to indicate the current location for response data to be added */
 | |
| 	void* CurrResponsePos = ResponsePacket.ResponseData;
 | |
| 
 | |
| 	/* Clamp the maximum attribute size to the size of the allocated buffer */
 | |
| 	if (MaxAttributeSize > sizeof(ResponsePacket.ResponseData))
 | |
| 	  MaxAttributeSize = sizeof(ResponsePacket.ResponseData);
 | |
| 
 | |
| 	/* Add the outer Data Element Sequence header for all of the retrieved Attributes */
 | |
| 	uint16_t* TotalResponseSize = SDP_AddSequence16(&CurrResponsePos);
 | |
| 	
 | |
| 	/* Search through the global service list an item at a time */
 | |
| 	for (uint8_t CurrTableItem = 0; CurrTableItem < (sizeof(SDP_Services_Table) / sizeof(void*)); CurrTableItem++)
 | |
| 	{
 | |
| 		/* Read in a pointer to the current UUID table entry's Attribute table */
 | |
| 		ServiceAttributeTable_t* CurrAttributeTable = pgm_read_ptr(&SDP_Services_Table[CurrTableItem]);
 | |
| 
 | |
| 		if (!(SDP_SearchServiceTable(UUIDList, TotalUUIDs, CurrAttributeTable)))
 | |
| 		  continue;
 | |
| 		  
 | |
| 		BT_SDP_DEBUG(2, " -- Found search match in table");
 | |
| 
 | |
| 		/* Add the listed attributes for the found UUID to the response */
 | |
| 		*TotalResponseSize += SDP_AddListedAttributesToResponse(CurrAttributeTable, AttributeList, TotalAttributes, 
 | |
| 		                                                        &CurrResponsePos);
 | |
| 	}
 | |
| 	
 | |
| 	/* Continuation state - always zero */
 | |
| 	SDP_WriteData8(&CurrResponsePos, 0);
 | |
| 
 | |
| 	/* Set the total response list size to the size of the outer container plus its header size and continuation state */
 | |
| 	ResponsePacket.AttributeListByteCount    = SwapEndian_16(3 + *TotalResponseSize);
 | |
| 
 | |
| 	/* Calculate the total parameter length that is to be sent, including the fixed return parameters, the created attribute
 | |
| 	   value list and the SDP continuation state */
 | |
| 	uint16_t ParamLength = (sizeof(ResponsePacket.AttributeListByteCount) + 
 | |
| 	                        (3 + *TotalResponseSize) +
 | |
| 	                        sizeof(uint8_t));
 | |
| 
 | |
| 	/* Flip the endianness of the container's size */
 | |
| 	*TotalResponseSize = SwapEndian_16(*TotalResponseSize);
 | |
| 
 | |
| 	/* Fill in the response packet's header */
 | |
| 	ResponsePacket.SDPHeader.PDU             = SDP_PDU_SERVICESEARCHATTRIBUTERESPONSE;
 | |
| 	ResponsePacket.SDPHeader.TransactionID   = SDPHeader->TransactionID;
 | |
| 	ResponsePacket.SDPHeader.ParameterLength = SwapEndian_16(ParamLength);
 | |
| 
 | |
| 	BT_SDP_DEBUG(1, ">> Service Search Attribute Response");
 | |
| 	BT_SDP_DEBUG(2, "-- Param Len 0x%04X", ParamLength);
 | |
| 
 | |
| 	/* Send the completed response packet to the sender */
 | |
| 	Bluetooth_SendPacket(&ResponsePacket, (sizeof(ResponsePacket.SDPHeader) + ParamLength), Channel);
 | |
| }
 | |
| 
 | |
| /** Adds all the Attributes in the given service table to the response that appear in the Attribute table.
 | |
|  *
 | |
|  *  \param[in]  AttributeTable   Pointer to an Attribute table for the service to examine
 | |
|  *  \param[in]  AttributeList    Pointer to a list of Attribute ranges
 | |
|  *  \param[in]  TotalAttributes  Number of Attributes stored in the Attribute list
 | |
|  *  \param[out] BufferPos       Pointer to the output buffer position where the retrieved attributes are to be stored
 | |
|  *
 | |
|  *  \return Number of bytes added to the output buffer
 | |
|  */
 | |
| static uint16_t SDP_AddListedAttributesToResponse(const ServiceAttributeTable_t* AttributeTable,
 | |
|                                                   uint16_t AttributeList[][2],
 | |
|                                                   const uint8_t TotalAttributes,
 | |
|                                                   void** const BufferPos)
 | |
| {
 | |
| 	uint16_t TotalResponseSize;
 | |
| 
 | |
| 	/* Add an inner Data Element Sequence header for the current services's found Attributes */
 | |
| 	uint16_t* AttributeListSize = SDP_AddSequence16(BufferPos);
 | |
| 
 | |
| 	/* Search through the list of Attributes one at a time looking for values in the current UUID's Attribute table */
 | |
| 	for (uint8_t CurrAttribute = 0; CurrAttribute < TotalAttributes; CurrAttribute++)
 | |
| 	{
 | |
| 		uint16_t* AttributeIDRange = AttributeList[CurrAttribute];
 | |
| 		void*     AttributeValue;
 | |
| 		
 | |
| 		/* Look through the current service's attribute list, examining all the attributes */
 | |
| 		while ((AttributeValue = pgm_read_ptr(&AttributeTable->Data)) != NULL)
 | |
| 		{
 | |
| 			/* Get the current Attribute's ID from the current attribute table entry */
 | |
| 			uint16_t CurrAttributeID = pgm_read_word(&AttributeTable->AttributeID);
 | |
| 
 | |
| 			/* Check if the current Attribute's ID is within the current Attribute range */
 | |
| 			if ((CurrAttributeID >= AttributeIDRange[0]) && (CurrAttributeID <= AttributeIDRange[1]))
 | |
| 			{
 | |
| 				/* Increment the current UUID's returned Attribute container size by the number of added bytes */
 | |
| 				*AttributeListSize += SDP_AddAttributeToResponse(CurrAttributeID, AttributeValue, BufferPos);			
 | |
| 			}
 | |
| 			
 | |
| 			AttributeTable++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Record the total number of added bytes to the buffer */
 | |
| 	TotalResponseSize = 3 + *AttributeListSize;
 | |
| 
 | |
| 	/* Fix endianness of the added attribute data element sequence */
 | |
| 	*AttributeListSize = SwapEndian_16(*AttributeListSize);
 | |
| 
 | |
| 	return TotalResponseSize;
 | |
| }
 | |
| 
 | |
| /** Adds the given attribute ID and value to the response buffer, and advances the response buffer pointer past the added data.
 | |
|  *
 | |
|  *  \param[in] AttributeID          Attribute ID to add to the response buffer
 | |
|  *  \param[in] AttributeValue       Pointer to the start of the Attribute's value, located in PROGMEM
 | |
|  *  \param[in, out] ResponseBuffer  Pointer to a buffer where the Attribute and Attribute Value is to be added
 | |
|  *
 | |
|  *  \return Number of bytes added to the response buffer
 | |
|  */
 | |
| static uint16_t SDP_AddAttributeToResponse(const uint16_t AttributeID,
 | |
|                                            const void* AttributeValue,
 | |
|                                            void** ResponseBuffer)
 | |
| {
 | |
| 	/* Retrieve the size of the attribute value from its container header */
 | |
| 	uint8_t  AttributeHeaderLength;
 | |
| 	uint16_t AttributeValueLength = SDP_GetLocalAttributeContainerSize(AttributeValue, &AttributeHeaderLength);
 | |
| 	
 | |
| 	BT_SDP_DEBUG(2, " -- Add Attribute (0x%04X) 0x%04X", (AttributeHeaderLength + AttributeValueLength), AttributeID);
 | |
| 
 | |
| 	/* Add a Data Element header to the response for the Attribute ID */
 | |
| 	SDP_WriteData8(ResponseBuffer, (SDP_DATATYPE_UnsignedInt | SDP_DATASIZE_16Bit));
 | |
| 	
 | |
| 	/* Add the Attribute ID to the created Data Element */
 | |
| 	SDP_WriteData16(ResponseBuffer, AttributeID);
 | |
| 	
 | |
| 	/* Copy over the Attribute value Data Element container to the response */
 | |
| 	memcpy_P(*ResponseBuffer, AttributeValue, AttributeHeaderLength + AttributeValueLength);
 | |
| 	*ResponseBuffer += AttributeHeaderLength + AttributeValueLength;
 | |
| 	
 | |
| 	return (sizeof(uint8_t) + sizeof(uint16_t) + AttributeHeaderLength + AttributeValueLength);
 | |
| }
 | |
| 
 | |
| /** Retrieves a pointer to the value of the given Attribute ID from the given Attribute table.
 | |
|  *
 | |
|  *  \param[in] AttributeTable  Pointer to the Attribute table to search in
 | |
|  *  \param[in] AttributeID     Attribute ID to search for within the table
 | |
|  *
 | |
|  *  \return Pointer to the start of the Attribute's value if found within the table, NULL otherwise
 | |
|  */
 | |
| static void* SDP_GetAttributeValue(const ServiceAttributeTable_t* AttributeTable,
 | |
|                                    const uint16_t AttributeID)
 | |
| {
 | |
| 	void* CurrTableItemData;
 | |
| 	
 | |
| 	/* Search through the current Attribute table, abort when the terminator item has been reached */
 | |
| 	while ((CurrTableItemData = pgm_read_ptr(&AttributeTable->Data)) != NULL)
 | |
| 	{
 | |
| 		/* Check if the current Attribute ID matches the search ID - if so return a pointer to it */
 | |
| 		if (pgm_read_word(&AttributeTable->AttributeID) == AttributeID)
 | |
| 		  return CurrTableItemData;
 | |
| 		
 | |
| 		AttributeTable++;
 | |
| 	}
 | |
| 			
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /** Retrieves the Attribute table for the given UUID list if it exists.
 | |
|  *
 | |
|  *  \param[in] UUIDList            List of UUIDs which must be matched within the service attribute table
 | |
|  *  \param[in] TotalUUIDs          Total number of UUIDs stored in the UUID list
 | |
|  *  \param[in] CurrAttributeTable  Pointer to the service attribute table to search through
 | |
|  *
 | |
|  *  \return True if all the UUIDs given in the UUID list appear in the given attribute table, false otherwise
 | |
|  */
 | |
| static bool SDP_SearchServiceTable(uint8_t UUIDList[][UUID_SIZE_BYTES],
 | |
|                                    const uint8_t TotalUUIDs,
 | |
| 			                       const ServiceAttributeTable_t* CurrAttributeTable)
 | |
| {
 | |
| 	const void* CurrAttribute;
 | |
| 	uint16_t    UUIDMatchFlags = 0;
 | |
| 	
 | |
| 	/* Search through the current attribute table, checking each attribute value for UUID matches */
 | |
| 	while ((CurrAttribute = pgm_read_ptr(&CurrAttributeTable->Data)) != NULL)
 | |
| 	{
 | |
| 		SDP_CheckUUIDMatch(UUIDList, TotalUUIDs, &UUIDMatchFlags, CurrAttribute);
 | |
| 		CurrAttributeTable++;
 | |
| 	}
 | |
| 
 | |
| 	/* Determine how many UUID matches in the list we have found */
 | |
| 	uint8_t UUIDMatches;
 | |
| 	for (UUIDMatches = 0; UUIDMatchFlags; UUIDMatches++)
 | |
| 	  UUIDMatchFlags &= (UUIDMatchFlags - 1);
 | |
| 	
 | |
| 	/* If all UUIDs have been matched to the current service, return true */
 | |
| 	return (UUIDMatches == TotalUUIDs);
 | |
| }
 | |
| 
 | |
| /** Recursively unwraps the given locally stored attribute (in PROGMEM space), searching for UUIDs to match against
 | |
|  *  the given UUID list. As matches are found, they are indicated in the UUIDMatch flag list.
 | |
|  *
 | |
|  *  \param[in]      UUIDList        List of UUIDs which must be matched within the service attribute table
 | |
|  *  \param[in]      TotalUUIDs      Total number of UUIDs stored in the UUID list
 | |
|  *  \param[in, out] UUIDMatchFlags  Array of flags indicating which UUIDs in the list have already been matched
 | |
|  *  \param[in]      CurrAttribute   Pointer to the current attribute to search through
 | |
|  *
 | |
|  *  \return True if all the UUIDs given in the UUID list appear in the given attribute table, false otherwise
 | |
|  */
 | |
| static void SDP_CheckUUIDMatch(uint8_t UUIDList[][UUID_SIZE_BYTES],
 | |
|                                const uint8_t TotalUUIDs,
 | |
|                                uint16_t* const UUIDMatchFlags,
 | |
|                                const void* CurrAttribute)
 | |
| {
 | |
| 	uint8_t CurrAttributeType = (pgm_read_byte(CurrAttribute) & ~0x07);
 | |
| 
 | |
| 	/* Check the data type of the current attribute value - if UUID, compare, if Sequence, unwrap and recurse */
 | |
| 	if (CurrAttributeType == SDP_DATATYPE_UUID)
 | |
| 	{
 | |
| 		uint16_t CurrUUIDMatchMask = (1 << 0);
 | |
| 	
 | |
| 		/* Look for matches in the UUID list against the current attribute UUID value */
 | |
| 		for (uint8_t i = 0; i < TotalUUIDs; i++)
 | |
| 		{
 | |
| 			/* Check if the current unmatched UUID is identical to the search UUID */
 | |
| 			if (!(*UUIDMatchFlags & CurrUUIDMatchMask) && !(memcmp_P(UUIDList[i], (CurrAttribute + 1), UUID_SIZE_BYTES)))
 | |
| 			{
 | |
| 				/* Indicate match found for the current attribute UUID and early-abort */
 | |
| 				*UUIDMatchFlags |= CurrUUIDMatchMask;
 | |
| 				break;
 | |
| 			}
 | |
| 			
 | |
| 			CurrUUIDMatchMask <<= 1;
 | |
| 		}
 | |
| 	}
 | |
| 	else if (CurrAttributeType == SDP_DATATYPE_Sequence)
 | |
| 	{
 | |
| 		uint8_t  SequenceHeaderSize;
 | |
| 		uint16_t SequenceSize = SDP_GetLocalAttributeContainerSize(CurrAttribute, &SequenceHeaderSize);
 | |
| 		
 | |
| 		CurrAttribute += SequenceHeaderSize;
 | |
| 		
 | |
| 		/* Recursively unwrap the sequence container, and re-search its contents for UUIDs */
 | |
| 		while (SequenceSize)
 | |
| 		{
 | |
| 			uint8_t  InnerHeaderSize;
 | |
| 			uint16_t InnerSize = SDP_GetLocalAttributeContainerSize(CurrAttribute, &InnerHeaderSize);
 | |
| 			
 | |
| 			/* Recursively search of the next element in the sequence, trying to match UUIDs with the UUID list */
 | |
| 			SDP_CheckUUIDMatch(UUIDList, TotalUUIDs, UUIDMatchFlags, CurrAttribute);
 | |
| 
 | |
| 			/* Skip to the next element in the sequence */
 | |
| 			SequenceSize  -= InnerHeaderSize + InnerSize;
 | |
| 			CurrAttribute += InnerHeaderSize + InnerSize;
 | |
| 		}
 | |
| 	}	
 | |
| }
 | |
| 
 | |
| /** Reads in the collection of Attribute ranges from the input buffer's Data Element Sequence container, into the given 
 | |
|  *  Attribute list for later use. Once complete, the input buffer pointer is advanced to the end of the Attribute container.
 | |
|  *
 | |
|  *  \param[out] AttributeList     Pointer to a buffer where the list of Attribute ranges are to be stored
 | |
|  *  \param[in]  CurrentParameter  Pointer to a Buffer containing a Data Element Sequence of Attribute and Attribute Range elements
 | |
|  *
 | |
|  *  \return Total number of Attribute ranges stored in the Data Element Sequence
 | |
|  */
 | |
| static uint8_t SDP_GetAttributeList(uint16_t AttributeList[][2],
 | |
|                                     const void** const CurrentParameter)
 | |
| {
 | |
| 	uint8_t ElementHeaderSize;
 | |
| 	uint8_t TotalAttributes = 0;
 | |
| 
 | |
| 	/* Retrieve the total size of the Attribute container, and unwrap the outer Data Element Sequence container */
 | |
| 	uint16_t AttributeIDListLength = SDP_GetDataElementSize(CurrentParameter, &ElementHeaderSize);
 | |
| 	BT_SDP_DEBUG(2, "-- Total Attribute Length: 0x%04X", AttributeIDListLength);
 | |
| 	while (AttributeIDListLength)
 | |
| 	{
 | |
| 		/* Retrieve the size of the next Attribute in the container and get a pointer to the next free Attribute element in the list */
 | |
| 		uint16_t* CurrentAttributeRange = AttributeList[TotalAttributes++];
 | |
| 		uint8_t   AttributeLength       = SDP_GetDataElementSize(CurrentParameter, &ElementHeaderSize);
 | |
| 		
 | |
| 		/* Copy over the starting Attribute ID and (if it the current element is a range) the ending Attribute ID */
 | |
| 		memcpy(&CurrentAttributeRange[0], *CurrentParameter, AttributeLength);
 | |
| 		
 | |
| 		/* If the element is not an Attribute Range, copy over the starting ID to the ending ID to make a range of 1 */
 | |
| 		if (AttributeLength == 2)
 | |
| 		  CurrentAttributeRange[1] = CurrentAttributeRange[0];
 | |
| 
 | |
| 		/* Swap the endianness of the attribute range values */
 | |
| 		CurrentAttributeRange[0] = SwapEndian_16(CurrentAttributeRange[0]);
 | |
| 		CurrentAttributeRange[1] = SwapEndian_16(CurrentAttributeRange[1]);
 | |
| 
 | |
| 		BT_SDP_DEBUG(2, "-- Attribute: 0x%04X-0x%04X", CurrentAttributeRange[0], CurrentAttributeRange[1]);
 | |
| 		
 | |
| 		AttributeIDListLength -= (AttributeLength + ElementHeaderSize);
 | |
| 		*CurrentParameter     += AttributeLength;
 | |
| 	}
 | |
| 	
 | |
| 	return TotalAttributes;
 | |
| }
 | |
| 
 | |
| /** Reads in the collection of UUIDs from the input buffer's Data Element Sequence container, into the given 
 | |
|  *  UUID list for later use. Once complete, the input buffer pointer is advanced to the end of the UUID container.
 | |
|  *
 | |
|  *  \param[out] UUIDList          Pointer to a buffer where the list of UUIDs are to be stored
 | |
|  *  \param[in]  CurrentParameter  Pointer to a Buffer containing a Data Element Sequence of UUID elements
 | |
|  *
 | |
|  *  \return Total number of UUIDs stored in the Data Element Sequence
 | |
|  */
 | |
| static uint8_t SDP_GetUUIDList(uint8_t UUIDList[][UUID_SIZE_BYTES],
 | |
|                                const void** const CurrentParameter)
 | |
| {
 | |
| 	uint8_t ElementHeaderSize;
 | |
| 	uint8_t TotalUUIDs = 0;
 | |
| 
 | |
| 	/* Retrieve the total size of the UUID container, and unwrap the outer Data Element Sequence container */
 | |
| 	uint16_t ServicePatternLength = SDP_GetDataElementSize(CurrentParameter, &ElementHeaderSize);
 | |
| 	BT_SDP_DEBUG(2, "-- Total UUID Length: 0x%04X", ServicePatternLength);
 | |
| 	while (ServicePatternLength)
 | |
| 	{
 | |
| 		/* Retrieve the size of the next UUID in the container and get a pointer to the next free UUID element in the list */
 | |
| 		uint8_t* CurrentUUID = UUIDList[TotalUUIDs++];
 | |
| 		uint8_t  UUIDLength  = SDP_GetDataElementSize(CurrentParameter, &ElementHeaderSize);
 | |
| 		
 | |
| 		/* Copy over UUID from the container to the free slot */
 | |
| 		if (UUIDLength <= 4)
 | |
| 		{
 | |
| 			/* Copy over the base UUID value to the free UUID slot in the list */
 | |
| 			memcpy_P(CurrentUUID, &BaseUUID, sizeof(BaseUUID));
 | |
| 
 | |
| 			/* Copy over short UUID */
 | |
| 			memcpy(CurrentUUID + (4 - UUIDLength), *CurrentParameter, UUIDLength);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* Copy over full UUID */
 | |
| 			memcpy(CurrentUUID, *CurrentParameter, UUIDLength);		
 | |
| 		}
 | |
| 		
 | |
| 		BT_SDP_DEBUG(2, "-- UUID (%d): %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
 | |
| 		                UUIDLength,
 | |
| 		                CurrentUUID[0], CurrentUUID[1], CurrentUUID[2], CurrentUUID[3],
 | |
| 		                CurrentUUID[4], CurrentUUID[5],
 | |
| 						CurrentUUID[6], CurrentUUID[7],
 | |
| 		                CurrentUUID[8], CurrentUUID[9],
 | |
| 						CurrentUUID[10], CurrentUUID[11], CurrentUUID[12],  CurrentUUID[13],  CurrentUUID[14],  CurrentUUID[15]);
 | |
| 
 | |
| 		ServicePatternLength -= (UUIDLength + ElementHeaderSize);
 | |
| 		*CurrentParameter    += UUIDLength;
 | |
| 	}
 | |
| 	
 | |
| 	return TotalUUIDs;
 | |
| }
 | |
| 
 | |
| /** Retrieves the total size of the given locally stored (in PROGMEM) attribute Data Element container.
 | |
|  *
 | |
|  *  \param[in]  AttributeData  Pointer to the start of the Attribute container, located in PROGMEM
 | |
|  *  \param[out] HeaderSize     Pointer to a location where the header size of the data element is to be stored
 | |
|  *
 | |
|  *  \return Size in bytes of the entire attribute container, including the header
 | |
|  */
 | |
| static uint32_t SDP_GetLocalAttributeContainerSize(const void* const AttributeData,
 | |
|                                                    uint8_t* const HeaderSize)
 | |
| {
 | |
| 	/* Fetch the size of the Data Element structure from the header */
 | |
| 	uint8_t SizeIndex = (pgm_read_byte(AttributeData) & 0x07);
 | |
| 	
 | |
| 	uint32_t ElementValueSize;
 | |
| 
 | |
| 	/* Convert the Data Element size index into a size in bytes */
 | |
| 	switch (SizeIndex)
 | |
| 	{
 | |
| 		case SDP_DATASIZE_Variable8Bit:
 | |
| 			*HeaderSize = (1 + sizeof(uint8_t));
 | |
| 			ElementValueSize = pgm_read_byte(AttributeData + 1);
 | |
| 			break;
 | |
| 		case SDP_DATASIZE_Variable16Bit:
 | |
| 			*HeaderSize = (1 + sizeof(uint16_t));
 | |
| 			ElementValueSize = SwapEndian_16(pgm_read_word(AttributeData + 1));
 | |
| 			break;
 | |
| 		case SDP_DATASIZE_Variable32Bit:
 | |
| 			*HeaderSize = (1 + sizeof(uint32_t));
 | |
| 			ElementValueSize = SwapEndian_32(pgm_read_dword(AttributeData + 1));
 | |
| 			break;
 | |
| 		default:
 | |
| 			*HeaderSize = 1;
 | |
| 			ElementValueSize = (1 << SizeIndex);
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	return ElementValueSize;
 | |
| }
 | |
| 
 | |
| /** Retrieves the size of a Data Element container from the current input buffer, and advances the input buffer
 | |
|  *  pointer to the start of the Data Element's contents.
 | |
|  *
 | |
|  *  \param[in, out] DataElementHeader  Pointer to the start of a Data Element header
 | |
|  *  \param[out]     ElementHeaderSize  Size in bytes of the header that was skipped
 | |
|  *
 | |
|  *  \return Size in bytes of the Data Element container's contents, minus the header
 | |
|  */
 | |
| static uint32_t SDP_GetDataElementSize(const void** const DataElementHeader,
 | |
|                                        uint8_t* const ElementHeaderSize)
 | |
| {
 | |
| 	/* Fetch the size of the Data Element structure from the header, increment the current buffer pos */
 | |
| 	uint8_t  SizeIndex = (SDP_ReadData8(DataElementHeader) & 0x07);	
 | |
| 
 | |
| 	uint32_t ElementValueSize;
 | |
| 
 | |
| 	/* Convert the Data Element size index into a size in bytes */
 | |
| 	switch (SizeIndex)
 | |
| 	{
 | |
| 		case SDP_DATASIZE_Variable8Bit:
 | |
| 			*ElementHeaderSize  = (1 + sizeof(uint8_t));
 | |
| 			ElementValueSize    = SDP_ReadData8(DataElementHeader);
 | |
| 			break;
 | |
| 		case SDP_DATASIZE_Variable16Bit:
 | |
| 			*ElementHeaderSize  = (1 + sizeof(uint16_t));
 | |
| 			ElementValueSize    = SDP_ReadData16(DataElementHeader);
 | |
| 			break;
 | |
| 		case SDP_DATASIZE_Variable32Bit:
 | |
| 			*ElementHeaderSize  = (1 + sizeof(uint32_t));
 | |
| 			ElementValueSize    = SDP_ReadData32(DataElementHeader);
 | |
| 			break;
 | |
| 		default:
 | |
| 			*ElementHeaderSize  = 1;
 | |
| 			ElementValueSize    = (1 << SizeIndex);
 | |
| 			break;
 | |
| 	}
 | |
| 	
 | |
| 	return ElementValueSize;
 | |
| }
 | 
