/*************************************************************************
Copyright (C) Cambridge Silicon Radio Limited 2011-2014
Part of ADK 3.0
FILE NAME
gatt_primary_discovery.c
DESCRIPTION
Functions to handle GATT Primary Service Discovery sub-procedures.
This module implements support for the following GATT sub-procedures:
- Discover All Primary Services
- Discover Primary Service by Service UUID
NOTES
*/
#include "gatt_private.h"
#include <service.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <vm.h>
#if (GATT_FEATURES & GATT_PRIMARY_DISCOVERY)
static const uint32 uuid_primary_service = ATT_UUID_PRI_SERVICE;
/* search pattern for sdp search */
static const uint8 sdp_search[] = {
0x35, 0x03, /* DataElSeq, 3 bytes */
0x19, 0x00, 0x07 /* UUID16, ATT 0x0007 */
};
/* search pattern for sdp attributes */
static const uint8 sdp_search_attr[] = {
0x35, 0x06, /* DataElSeq, 6 bytes */
0x09, 0x00, 0x04, /* UINT16, ProtocolDescriptorList 0x0004 */
0x09, 0x00, 0x01 /* UINT16, ServiceClassIdList 0x0001 */
};
#define SDC_MAX_ATTRIBUTES 20
#define SDP_TYPE_UUID16 0x19
#define SDP_TYPE_UUID128 0x1c
#define SDP_SEARCH_SIZE_OFFS 1 /* offset for sequence length */
/*************************************************************************
NAME
gattHandleClSdpServiceSearchAttributeCfm
DESCRIPTION
This function handles Connection library response for SDP Service
Search Attribute request.
RETURNS
*/
void gattHandleClSdpServiceSearchAttributeCfm(
CL_SDP_SERVICE_SEARCH_ATTRIBUTE_CFM_T *m)
{
uint16 id = 0;
cid_map_t *conn = PanicNull(gattFindConnAddr(&m->bd_addr));/* never NULL */
/* Use GATT_DISCOVER_ALL_BREDR_SERVICES_CFM to populate the response.
* The message is mangled later to it's correct form based on what
* procedure we were running. */
MAKE_GATT_MESSAGE(GATT_DISCOVER_ALL_BREDR_SERVICES_CFM);
memmove(&message->bd_addr, &m->bd_addr, sizeof(bdaddr));
message->more_to_come = m->more_to_come;
message->status = gatt_status_attr_not_found;
if (m->status == sdp_response_success)
{
ServiceDataType type;
Region sdp;
Region val;
/* If we have correctly formatted response it is:
*
* 0 UINT16 0x0004 (ProtocolDescriptorList)
* 1 elSeq
* 2 elSeq
* 3 UUID 0x0100 (L2CAP)
* 4 UINT16 0x001f (ATT PSM)
* 5 elSeq
* 6 UUID 0x0007 (ATT)
* 7 UINT16 start_handle
* 8 UINT16 end_handle
* 9 UINT16 0x0001 (ServiceClassIDList)
* 10 elSeq
* 11 UUID service_uuid
*/
const ServiceDataType expect[] = {
sdtUnsignedInteger, sdtSequence,
sdtSequence, sdtUUID, sdtUnsignedInteger,
sdtSequence, sdtUUID, sdtUnsignedInteger, sdtUnsignedInteger,
sdtUnsignedInteger, sdtSequence, sdtUUID };
uint16 i;
sdp.begin = m->attributes;
sdp.end = m->attributes + m->size_attributes;
/* assume the response is valid and do only minimal verify */
for (i = 0; i < sizeof(expect) / sizeof(ServiceDataType); i++)
{
if (!ServiceGetValue(&sdp, &type, &val) || type != expect[i])
{
break;
}
/* take values we are interested in */
switch (i)
{
case 7:
message->handle = RegionReadUnsigned(&val);
break;
case 8:
message->end = RegionReadUnsigned(&val);
break;
case 11:
gatt_get_sdp_uuid(message->uuid,
&message->uuid_type,
&val);
message->status = gatt_status_success;
}
/* go inside the sequence */
if (type == sdtSequence) sdp.begin = val.begin;
}
}
/* in case of error reset values */
else
{
message->handle = 0;
message->end = 0;
message->uuid_type = gatt_uuid_none;
memset(message->uuid, 0, GATT_UUID_SIZE);
}
switch (conn->data.scenario)
{
#if (GATT_FEATURES & GATT_DISC_ALL_PRIMARY_SERVICES)
case gatt_ms_discover_all_primary_services:
id =
GATT_DISCOVER_ALL_PRIMARY_SERVICES_CFM -
GATT_DISCOVER_PRIMARY_SERVICE_CFM;
/* fall through to move the message offset */
#endif
case gatt_ms_discover_primary_service:
/* add to the id */
id += GATT_DISCOVER_PRIMARY_SERVICE_CFM;
/* move the message to correct offset */
{
GATT_DISCOVER_PRIMARY_SERVICE_CFM_T *msg =
(GATT_DISCOVER_PRIMARY_SERVICE_CFM_T*)message;
msg->cid = conn->cid;
memmove(&msg->handle,
&message->handle,
sizeof(GATT_DISCOVER_PRIMARY_SERVICE_CFM_T) -
offsetof(GATT_DISCOVER_PRIMARY_SERVICE_CFM_T, handle));
}
break;
#if (GATT_FEATURES & GATT_DISC_ALL_PRIMARY_SERVICES)
case gatt_ms_discover_all_bredr_services:
id = GATT_DISCOVER_ALL_BREDR_SERVICES_CFM;
break;
#endif
#if (GATT_FEATURES & GATT_DISC_PRIMARY_SERVICE)
case gatt_ms_discover_bredr_service:
id = GATT_DISCOVER_BREDR_SERVICE_CFM;
break;
#endif
default:
/* horror error which can't happen, ever. */
id = 0;
Panic();
}
MessageSend(conn->data.app, id, message);
if (!m->more_to_come)
{
/* delete temporary connection instance */
if (conn->data.scenario == gatt_ms_discover_all_bredr_services ||
conn->data.scenario == gatt_ms_discover_bredr_service)
gattDeleteCid(conn->cid);
gattSetConnState(conn, NULL, gatt_ms_none);
}
}
/*************************************************************************
NAME
get_temp_conn
DESCRIPTION
This function is used to create a temporary connection instance for
SDP searches when there is no existing connection.
RETURNS
connection pointer or NULL
*/
static cid_map_t *get_temp_conn(Task task)
{
gattState *gatt = (gattState*)gattGetTask();
uint16 cid = 0x003f; /* maximum fixed cid */
uint16 i;
/* create special connection instance for the SDP search */
for (i = 1; i < MAX_ATT_CONNECTIONS; i++)
if (gatt->u.cid_map[i].cid < cid)
cid = gatt->u.cid_map[i].cid - 1;
return gattAddCid(cid, task);
}
#endif
#if (GATT_FEATURES & GATT_DISC_ALL_PRIMARY_SERVICES)
void GattDiscoverAllPrimaryServicesRequest(Task theAppTask, uint16 cid)
{
cid_map_t *conn;
if ((conn = gattFindConn(cid)) && !conn->closing)
{
MAKE_GATT_MESSAGE(GATT_INTERNAL_DISCOVER_ALL_PRIMARY_SERVICES_REQ);
message->common.task = theAppTask;
message->common.cid = cid;
MessageSendConditionallyOnTask(
gattGetTask(),
GATT_INTERNAL_DISCOVER_ALL_PRIMARY_SERVICES_REQ,
message,
&conn->data.app);
}
else
{
MAKE_GATT_MESSAGE(GATT_DISCOVER_ALL_PRIMARY_SERVICES_CFM);
memset(message, 0, sizeof(GATT_DISCOVER_ALL_PRIMARY_SERVICES_CFM_T));
message->cid = cid;
评论0