sdp_api.cc revision 911d1ae03efec2d54c3b1b605589d790d1745488
1/******************************************************************************
2 *
3 *  Copyright (C) 1999-2012 Broadcom Corporation
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at:
8 *
9 *  http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 ******************************************************************************/
18
19/******************************************************************************
20 *
21 *  this file contains SDP interface functions
22 *
23 ******************************************************************************/
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include "bt_common.h"
30#include "bt_target.h"
31#include "bt_utils.h"
32#include "hcidefs.h"
33#include "hcimsgs.h"
34#include "l2cdefs.h"
35
36#include "btu.h"
37#include "sdp_api.h"
38#include "sdpint.h"
39
40#include "osi/include/osi.h"
41
42/**********************************************************************
43 *   C L I E N T    F U N C T I O N    P R O T O T Y P E S            *
44 **********************************************************************/
45
46/*******************************************************************************
47 *
48 * Function         SDP_InitDiscoveryDb
49 *
50 * Description      This function is called to initialize a discovery database.
51 *
52 * Parameters:      p_db        - (input) address of an area of memory where the
53 *                                        discovery database is managed.
54 *                  len         - (input) size (in bytes) of the memory
55 *                                 NOTE: This must be larger than
56 *                                       sizeof(tSDP_DISCOVERY_DB)
57 *                  num_uuid    - (input) number of UUID filters applied
58 *                  p_uuid_list - (input) list of UUID filters
59 *                  num_attr    - (input) number of attribute filters applied
60 *                  p_attr_list - (input) list of attribute filters
61 *
62 *
63 * Returns          bool
64 *                          true if successful
65 *                          false if one or more parameters are bad
66 *
67 ******************************************************************************/
68bool SDP_InitDiscoveryDb(tSDP_DISCOVERY_DB* p_db, uint32_t len,
69                         uint16_t num_uuid, tSDP_UUID* p_uuid_list,
70                         uint16_t num_attr, uint16_t* p_attr_list) {
71#if (SDP_CLIENT_ENABLED == TRUE)
72  uint16_t xx;
73
74  /* verify the parameters */
75  if (p_db == NULL || (sizeof(tSDP_DISCOVERY_DB) > len) ||
76      num_attr > SDP_MAX_ATTR_FILTERS || num_uuid > SDP_MAX_UUID_FILTERS) {
77    SDP_TRACE_ERROR(
78        "SDP_InitDiscoveryDb Illegal param: p_db 0x%x, len %d, num_uuid %d, "
79        "num_attr %d",
80        PTR_TO_UINT(p_db), len, num_uuid, num_attr);
81
82    return (false);
83  }
84
85  memset(p_db, 0, (size_t)len);
86
87  p_db->mem_size = len - sizeof(tSDP_DISCOVERY_DB);
88  p_db->mem_free = p_db->mem_size;
89  p_db->p_first_rec = NULL;
90  p_db->p_free_mem = (uint8_t*)(p_db + 1);
91
92  for (xx = 0; xx < num_uuid; xx++) p_db->uuid_filters[xx] = *p_uuid_list++;
93
94  p_db->num_uuid_filters = num_uuid;
95
96  for (xx = 0; xx < num_attr; xx++) p_db->attr_filters[xx] = *p_attr_list++;
97
98  /* sort attributes */
99  sdpu_sort_attr_list(num_attr, p_db);
100
101  p_db->num_attr_filters = num_attr;
102#endif
103  return (true);
104}
105
106/*******************************************************************************
107 *
108 * Function         SDP_CancelServiceSearch
109 *
110 * Description      This function cancels an active query to an SDP server.
111 *
112 * Returns          true if discovery cancelled, false if a matching activity is
113 *                  not found.
114 *
115 ******************************************************************************/
116bool SDP_CancelServiceSearch(tSDP_DISCOVERY_DB* p_db) {
117#if (SDP_CLIENT_ENABLED == TRUE)
118  tCONN_CB* p_ccb = sdpu_find_ccb_by_db(p_db);
119  if (!p_ccb) return (false);
120
121  sdp_disconnect(p_ccb, SDP_CANCEL);
122  p_ccb->disc_state = SDP_DISC_WAIT_CANCEL;
123#endif
124  return (true);
125}
126
127/*******************************************************************************
128 *
129 * Function         SDP_ServiceSearchRequest
130 *
131 * Description      This function queries an SDP server for information.
132 *
133 * Returns          true if discovery started, false if failed.
134 *
135 ******************************************************************************/
136bool SDP_ServiceSearchRequest(uint8_t* p_bd_addr, tSDP_DISCOVERY_DB* p_db,
137                              tSDP_DISC_CMPL_CB* p_cb) {
138#if (SDP_CLIENT_ENABLED == TRUE)
139  tCONN_CB* p_ccb;
140
141  /* Specific BD address */
142  p_ccb = sdp_conn_originate(p_bd_addr);
143
144  if (!p_ccb) return (false);
145
146  p_ccb->disc_state = SDP_DISC_WAIT_CONN;
147  p_ccb->p_db = p_db;
148  p_ccb->p_cb = p_cb;
149
150  return (true);
151#else
152  return (false);
153#endif
154}
155
156/*******************************************************************************
157 *
158 * Function         SDP_ServiceSearchAttributeRequest
159 *
160 * Description      This function queries an SDP server for information.
161 *
162 *                  The difference between this API function and the function
163 *                  SDP_ServiceSearchRequest is that this one does a
164 *                  combined ServiceSearchAttributeRequest SDP function.
165 *                  (This is for Unplug Testing)
166 *
167 * Returns          true if discovery started, false if failed.
168 *
169 ******************************************************************************/
170bool SDP_ServiceSearchAttributeRequest(uint8_t* p_bd_addr,
171                                       tSDP_DISCOVERY_DB* p_db,
172                                       tSDP_DISC_CMPL_CB* p_cb) {
173#if (SDP_CLIENT_ENABLED == TRUE)
174  tCONN_CB* p_ccb;
175
176  /* Specific BD address */
177  p_ccb = sdp_conn_originate(p_bd_addr);
178
179  if (!p_ccb) return (false);
180
181  p_ccb->disc_state = SDP_DISC_WAIT_CONN;
182  p_ccb->p_db = p_db;
183  p_ccb->p_cb = p_cb;
184
185  p_ccb->is_attr_search = true;
186
187  return (true);
188#else
189  return (false);
190#endif
191}
192/*******************************************************************************
193 *
194 * Function         SDP_ServiceSearchAttributeRequest2
195 *
196 * Description      This function queries an SDP server for information.
197 *
198 *                  The difference between this API function and the function
199 *                  SDP_ServiceSearchRequest is that this one does a
200 *                  combined ServiceSearchAttributeRequest SDP function.
201 *                  (This is for Unplug Testing)
202 *
203 * Returns          true if discovery started, false if failed.
204 *
205 ******************************************************************************/
206bool SDP_ServiceSearchAttributeRequest2(uint8_t* p_bd_addr,
207                                        tSDP_DISCOVERY_DB* p_db,
208                                        tSDP_DISC_CMPL_CB2* p_cb2,
209                                        void* user_data) {
210#if (SDP_CLIENT_ENABLED == TRUE)
211  tCONN_CB* p_ccb;
212
213  /* Specific BD address */
214  p_ccb = sdp_conn_originate(p_bd_addr);
215
216  if (!p_ccb) return (false);
217
218  p_ccb->disc_state = SDP_DISC_WAIT_CONN;
219  p_ccb->p_db = p_db;
220  p_ccb->p_cb2 = p_cb2;
221
222  p_ccb->is_attr_search = true;
223  p_ccb->user_data = user_data;
224
225  return (true);
226#else
227  return (false);
228#endif
229}
230
231#if (SDP_CLIENT_ENABLED == TRUE)
232void SDP_SetIdleTimeout(UNUSED_ATTR BD_ADDR addr,
233                        UNUSED_ATTR uint16_t timeout) {}
234#endif
235
236/*******************************************************************************
237 *
238 * Function         SDP_FindAttributeInDb
239 *
240 * Description      This function queries an SDP database for a specific
241 *                  attribute. If the p_start_rec pointer is NULL, it looks from
242 *                  the beginning of the database, else it continues from the
243 *                  next record after p_start_rec.
244 *
245 * Returns          Pointer to matching record, or NULL
246 *
247 ******************************************************************************/
248tSDP_DISC_REC* SDP_FindAttributeInDb(tSDP_DISCOVERY_DB* p_db, uint16_t attr_id,
249                                     tSDP_DISC_REC* p_start_rec) {
250#if (SDP_CLIENT_ENABLED == TRUE)
251  tSDP_DISC_REC* p_rec;
252  tSDP_DISC_ATTR* p_attr;
253
254  /* Must have a valid database */
255  if (p_db == NULL) return (NULL);
256
257  if (!p_start_rec)
258    p_rec = p_db->p_first_rec;
259  else
260    p_rec = p_start_rec->p_next_rec;
261
262  while (p_rec) {
263    p_attr = p_rec->p_first_attr;
264    while (p_attr) {
265      if (p_attr->attr_id == attr_id) return (p_rec);
266
267      p_attr = p_attr->p_next_attr;
268    }
269
270    p_rec = p_rec->p_next_rec;
271  }
272#endif
273  /* If here, no matching attribute found */
274  return (NULL);
275}
276
277/*******************************************************************************
278 *
279 * Function         SDP_FindAttributeInRec
280 *
281 * Description      This function searches an SDP discovery record for a
282 *                  specific attribute.
283 *
284 * Returns          Pointer to matching attribute entry, or NULL
285 *
286 ******************************************************************************/
287tSDP_DISC_ATTR* SDP_FindAttributeInRec(tSDP_DISC_REC* p_rec, uint16_t attr_id) {
288#if (SDP_CLIENT_ENABLED == TRUE)
289  tSDP_DISC_ATTR* p_attr;
290
291  p_attr = p_rec->p_first_attr;
292  while (p_attr) {
293    if (p_attr->attr_id == attr_id) return (p_attr);
294
295    p_attr = p_attr->p_next_attr;
296  }
297#endif
298  /* If here, no matching attribute found */
299  return (NULL);
300}
301
302/*******************************************************************************
303 *
304 * Function         SDP_FindServiceUUIDInRec
305 *
306 * Description      This function is called to read the service UUID within a
307 *                  record if there is any.
308 *
309 * Parameters:      p_rec      - pointer to a SDP record.
310 *                  p_uuid     - output parameter to save the UUID found.
311 *
312 * Returns          true if found, otherwise false.
313 *
314 ******************************************************************************/
315bool SDP_FindServiceUUIDInRec(tSDP_DISC_REC* p_rec, tBT_UUID* p_uuid) {
316#if (SDP_CLIENT_ENABLED == TRUE)
317  tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr;
318
319  p_attr = p_rec->p_first_attr;
320
321  while (p_attr) {
322    if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
323        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
324      for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
325           p_sattr = p_sattr->p_next_attr) {
326        if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) {
327          if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == LEN_UUID_16) {
328            p_uuid->len = LEN_UUID_16;
329            p_uuid->uu.uuid16 = p_sattr->attr_value.v.u16;
330          } else if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) ==
331                     LEN_UUID_128) {
332            p_uuid->len = LEN_UUID_128;
333            for (uint8_t i = 0; i != LEN_UUID_128; ++i)
334              p_uuid->uu.uuid128[i] =
335                  p_sattr->attr_value.v.array[LEN_UUID_128 - i - 1];
336          } else if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == LEN_UUID_32) {
337            p_uuid->len = LEN_UUID_32;
338            p_uuid->uu.uuid32 = p_sattr->attr_value.v.u32;
339          }
340
341          return (true);
342        }
343
344        /* Checking for Toyota G Block Car Kit:
345        **  This car kit puts an extra data element sequence
346        **  where the UUID is suppose to be!!!
347        */
348        else {
349          if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
350              DATA_ELE_SEQ_DESC_TYPE) {
351            /* Look through data element sequence until no more UUIDs */
352            for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr;
353                 p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) {
354              /* Increment past this to see if the next attribut is UUID */
355              if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) ==
356                   UUID_DESC_TYPE)
357                  /* only support 16 bits UUID for now */
358                  && (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)) {
359                p_uuid->len = 2;
360                p_uuid->uu.uuid16 = p_extra_sattr->attr_value.v.u16;
361                return (true);
362              }
363            }
364          }
365        }
366      }
367      break;
368    } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
369      if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
370          /* only support 16 bits UUID for now */
371          && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2)) {
372        p_uuid->len = 2;
373        p_uuid->uu.uuid16 = p_attr->attr_value.v.u16;
374        return (true);
375      }
376    }
377    p_attr = p_attr->p_next_attr;
378  }
379  return false;
380#endif
381}
382
383/*******************************************************************************
384 *
385 * Function         SDP_FindServiceUUIDInRec_128bit
386 *
387 * Description      This function is called to read the 128-bit service UUID
388 *                  within a record if there is any.
389 *
390 * Parameters:      p_rec      - pointer to a SDP record.
391 *                  p_uuid     - output parameter to save the UUID found.
392 *
393 * Returns          true if found, otherwise false.
394 *
395 ******************************************************************************/
396bool SDP_FindServiceUUIDInRec_128bit(tSDP_DISC_REC* p_rec, tBT_UUID* p_uuid) {
397#if (SDP_CLIENT_ENABLED == TRUE)
398  tSDP_DISC_ATTR* p_attr = p_rec->p_first_attr;
399  while (p_attr) {
400    if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
401        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
402      tSDP_DISC_ATTR* p_sattr = p_attr->attr_value.v.p_sub_attr;
403      while (p_sattr) {
404        if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) {
405          /* only support 128 bits UUID for now */
406          if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16) {
407            p_uuid->len = LEN_UUID_128;
408            for (uint8_t i = 0; i != LEN_UUID_128; ++i)
409              p_uuid->uu.uuid128[i] =
410                  p_sattr->attr_value.v.array[LEN_UUID_128 - i - 1];
411          }
412          return (true);
413        }
414
415        p_sattr = p_sattr->p_next_attr;
416      }
417      break;
418    } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
419      if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
420          /* only support 128 bits UUID for now */
421          && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16)) {
422        p_uuid->len = LEN_UUID_128;
423        for (uint8_t i = 0; i != LEN_UUID_128; ++i)
424          p_uuid->uu.uuid128[i] =
425              p_attr->attr_value.v.array[LEN_UUID_128 - i - 1];
426        return (true);
427      }
428    }
429    p_attr = p_attr->p_next_attr;
430  }
431  return false;
432#endif
433}
434
435/*******************************************************************************
436 *
437 * Function         SDP_FindServiceInDb
438 *
439 * Description      This function queries an SDP database for a specific
440 *                  service. If the p_start_rec pointer is NULL, it looks from
441 *                  the beginning of the database, else it continues from the
442 *                  next record after p_start_rec.
443 *
444 * Returns          Pointer to record containing service class, or NULL
445 *
446 ******************************************************************************/
447tSDP_DISC_REC* SDP_FindServiceInDb(tSDP_DISCOVERY_DB* p_db,
448                                   uint16_t service_uuid,
449                                   tSDP_DISC_REC* p_start_rec) {
450#if (SDP_CLIENT_ENABLED == TRUE)
451  tSDP_DISC_REC* p_rec;
452  tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr;
453
454  /* Must have a valid database */
455  if (p_db == NULL) return (NULL);
456
457  if (!p_start_rec)
458    p_rec = p_db->p_first_rec;
459  else
460    p_rec = p_start_rec->p_next_rec;
461
462  while (p_rec) {
463    p_attr = p_rec->p_first_attr;
464    while (p_attr) {
465      if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
466          (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) ==
467           DATA_ELE_SEQ_DESC_TYPE)) {
468        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
469             p_sattr = p_sattr->p_next_attr) {
470          if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
471              (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)) {
472            SDP_TRACE_DEBUG(
473                "SDP_FindServiceInDb - p_sattr value = 0x%x serviceuuid = 0x%x",
474                p_sattr->attr_value.v.u16, service_uuid);
475            if (service_uuid == UUID_SERVCLASS_HDP_PROFILE) {
476              if ((p_sattr->attr_value.v.u16 == UUID_SERVCLASS_HDP_SOURCE) ||
477                  (p_sattr->attr_value.v.u16 == UUID_SERVCLASS_HDP_SINK)) {
478                SDP_TRACE_DEBUG(
479                    "SDP_FindServiceInDb found HDP source or sink\n");
480                return (p_rec);
481              }
482            }
483          }
484
485          if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE &&
486              (service_uuid == 0 ||
487               (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2 &&
488                p_sattr->attr_value.v.u16 == service_uuid)))
489          /* for a specific uuid, or any one */
490          {
491            return (p_rec);
492          }
493
494          /* Checking for Toyota G Block Car Kit:
495          **  This car kit puts an extra data element sequence
496          **  where the UUID is suppose to be!!!
497          */
498          else {
499            if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
500                DATA_ELE_SEQ_DESC_TYPE) {
501              /* Look through data element sequence until no more UUIDs */
502              for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr;
503                   p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) {
504                /* Increment past this to see if the next attribut is UUID */
505                if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) ==
506                     UUID_DESC_TYPE) &&
507                    (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)
508                    /* for a specific uuid, or any one */
509                    && ((p_extra_sattr->attr_value.v.u16 == service_uuid) ||
510                        (service_uuid == 0))) {
511                  return (p_rec);
512                }
513              }
514            }
515          }
516        }
517        break;
518      } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
519        if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) &&
520            (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2)
521            /* find a specific UUID or anyone */
522            &&
523            ((p_attr->attr_value.v.u16 == service_uuid) || service_uuid == 0))
524          return (p_rec);
525      }
526
527      p_attr = p_attr->p_next_attr;
528    }
529
530    p_rec = p_rec->p_next_rec;
531  }
532#endif
533  /* If here, no matching UUID found */
534  return (NULL);
535}
536
537/*******************************************************************************
538 *
539 * Function         SDP_FindServiceInDb_128bit
540 *
541 * Description      Query an SDP database for a specific service. If the
542 *                  p_start_rec pointer is NULL, it looks from the beginning of
543 *                  the database, else it continues from the next record after
544 *                  p_start_rec.
545 *
546 *                  This function is kept separate from SDP_FindServiceInDb
547 *                  since that API is expected to return only 16-bit UUIDs
548 *
549 * Returns          Pointer to record containing service class, or NULL
550 *
551 ******************************************************************************/
552tSDP_DISC_REC* SDP_FindServiceInDb_128bit(tSDP_DISCOVERY_DB* p_db,
553                                          tSDP_DISC_REC* p_start_rec) {
554#if (SDP_CLIENT_ENABLED == TRUE)
555  tSDP_DISC_REC* p_rec;
556  tSDP_DISC_ATTR *p_attr, *p_sattr;
557
558  /* Must have a valid database */
559  if (p_db == NULL) return (NULL);
560
561  if (!p_start_rec)
562    p_rec = p_db->p_first_rec;
563  else
564    p_rec = p_start_rec->p_next_rec;
565
566  while (p_rec) {
567    p_attr = p_rec->p_first_attr;
568    while (p_attr) {
569      if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
570          (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) ==
571           DATA_ELE_SEQ_DESC_TYPE)) {
572        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
573             p_sattr = p_sattr->p_next_attr) {
574          if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
575              (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16)) {
576            return (p_rec);
577          }
578        }
579        break;
580      } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
581        if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) &&
582            (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16))
583          return (p_rec);
584      }
585
586      p_attr = p_attr->p_next_attr;
587    }
588
589    p_rec = p_rec->p_next_rec;
590  }
591#endif
592  /* If here, no matching UUID found */
593  return (NULL);
594}
595
596/*******************************************************************************
597 *
598 * Function         SDP_FindServiceUUIDInDb
599 *
600 * Description      Query an SDP database for a specific service. If the
601 *                  p_start_rec pointer is NULL, it looks from the beginning of
602 *                  the database, else it continues from the next record after
603 *                  p_start_rec.
604 *
605 * NOTE             the only difference between this function and the previous
606 *                  function "SDP_FindServiceInDb()" is that this function takes
607 *                  a tBT_UUID input
608 *
609 * Returns          Pointer to record containing service class, or NULL
610 *
611 ******************************************************************************/
612tSDP_DISC_REC* SDP_FindServiceUUIDInDb(tSDP_DISCOVERY_DB* p_db,
613                                       tBT_UUID* p_uuid,
614                                       tSDP_DISC_REC* p_start_rec) {
615#if (SDP_CLIENT_ENABLED == TRUE)
616  tSDP_DISC_REC* p_rec;
617  tSDP_DISC_ATTR *p_attr, *p_sattr;
618
619  /* Must have a valid database */
620  if (p_db == NULL) return (NULL);
621
622  if (!p_start_rec)
623    p_rec = p_db->p_first_rec;
624  else
625    p_rec = p_start_rec->p_next_rec;
626
627  while (p_rec) {
628    p_attr = p_rec->p_first_attr;
629    while (p_attr) {
630      if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
631          (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) ==
632           DATA_ELE_SEQ_DESC_TYPE)) {
633        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
634             p_sattr = p_sattr->p_next_attr) {
635          if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) {
636            if (sdpu_compare_uuid_with_attr(p_uuid, p_sattr)) return (p_rec);
637          }
638        }
639        break;
640      } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
641        if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) {
642          if (sdpu_compare_uuid_with_attr(p_uuid, p_attr)) return (p_rec);
643        }
644      }
645
646      p_attr = p_attr->p_next_attr;
647    }
648
649    p_rec = p_rec->p_next_rec;
650  }
651#endif /* CLIENT_ENABLED == TRUE */
652  /* If here, no matching UUID found */
653  return (NULL);
654}
655
656#if (SDP_CLIENT_ENABLED == TRUE)
657/*******************************************************************************
658 *
659 * Function         sdp_fill_proto_elem
660 *
661 * Description      This function retrieves the protocol element.
662 *
663 * Returns          true if found, false if not
664 *                  If found, the passed protocol list element is filled in.
665 *
666 ******************************************************************************/
667static bool sdp_fill_proto_elem(tSDP_DISC_ATTR* p_attr, uint16_t layer_uuid,
668                                tSDP_PROTOCOL_ELEM* p_elem) {
669  tSDP_DISC_ATTR* p_sattr;
670
671  /* Walk through the protocol descriptor list */
672  for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr;
673       p_attr = p_attr->p_next_attr) {
674    /* Safety check - each entry should itself be a sequence */
675    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
676      return (false);
677
678    /* Now, see if the entry contains the layer we are interested in */
679    for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
680         p_sattr = p_sattr->p_next_attr) {
681      /* SDP_TRACE_DEBUG ("SDP - p_sattr 0x%x, layer_uuid:0x%x, u16:0x%x####",
682          p_sattr, layer_uuid, p_sattr->attr_value.v.u16); */
683
684      if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
685          (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) &&
686          (p_sattr->attr_value.v.u16 == layer_uuid)) {
687        /* Bingo. Now fill in the passed element */
688        p_elem->protocol_uuid = layer_uuid;
689        p_elem->num_params = 0;
690
691        /* Store the parameters, if any */
692        for (p_sattr = p_sattr->p_next_attr; p_sattr;
693             p_sattr = p_sattr->p_next_attr) {
694          if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) != UINT_DESC_TYPE)
695            break;
696
697          if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)
698            p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u16;
699          else
700            p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u8;
701
702          if (p_elem->num_params >= SDP_MAX_PROTOCOL_PARAMS) break;
703        }
704        return (true);
705      }
706    }
707  }
708
709  return (false);
710}
711#endif /* CLIENT_ENABLED == TRUE */
712
713/*******************************************************************************
714 *
715 * Function         SDP_FindProtocolListElemInRec
716 *
717 * Description      This function looks at a specific discovery record for a
718 *                  protocol list element.
719 *
720 * Returns          true if found, false if not
721 *                  If found, the passed protocol list element is filled in.
722 *
723 ******************************************************************************/
724bool SDP_FindProtocolListElemInRec(tSDP_DISC_REC* p_rec, uint16_t layer_uuid,
725                                   tSDP_PROTOCOL_ELEM* p_elem) {
726#if (SDP_CLIENT_ENABLED == TRUE)
727  tSDP_DISC_ATTR* p_attr;
728
729  p_attr = p_rec->p_first_attr;
730  while (p_attr) {
731    /* Find the protocol descriptor list */
732    if ((p_attr->attr_id == ATTR_ID_PROTOCOL_DESC_LIST) &&
733        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
734      return sdp_fill_proto_elem(p_attr, layer_uuid, p_elem);
735    }
736    p_attr = p_attr->p_next_attr;
737  }
738#endif
739  /* If here, no match found */
740  return (false);
741}
742
743/*******************************************************************************
744 *
745 * Function         SDP_FindAddProtoListsElemInRec
746 *
747 * Description      This function looks at a specific discovery record for a
748 *                  protocol list element.
749 *
750 * Returns          true if found, false if not
751 *                  If found, the passed protocol list element is filled in.
752 *
753 ******************************************************************************/
754bool SDP_FindAddProtoListsElemInRec(tSDP_DISC_REC* p_rec, uint16_t layer_uuid,
755                                    tSDP_PROTOCOL_ELEM* p_elem) {
756#if (SDP_CLIENT_ENABLED == TRUE)
757  tSDP_DISC_ATTR *p_attr, *p_sattr;
758  bool ret = false;
759
760  p_attr = p_rec->p_first_attr;
761  while (p_attr) {
762    /* Find the additional protocol descriptor list attribute */
763    if ((p_attr->attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS) &&
764        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
765      for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
766           p_sattr = p_sattr->p_next_attr) {
767        /* Safety check - each entry should itself be a sequence */
768        if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
769            DATA_ELE_SEQ_DESC_TYPE) {
770          ret = sdp_fill_proto_elem(p_sattr, layer_uuid, p_elem);
771          if (ret == true) break;
772        }
773      }
774      return ret;
775    }
776    p_attr = p_attr->p_next_attr;
777  }
778#endif
779  /* If here, no match found */
780  return (false);
781}
782
783/*******************************************************************************
784 *
785 * Function         SDP_FindProfileVersionInRec
786 *
787 * Description      This function looks at a specific discovery record for the
788 *                  Profile list descriptor, and pulls out the version number.
789 *                  The version number consists of an 8-bit major version and
790 *                  an 8-bit minor version.
791 *
792 * Returns          true if found, false if not
793 *                  If found, the major and minor version numbers that were
794 *                  passed in are filled in.
795 *
796 ******************************************************************************/
797bool SDP_FindProfileVersionInRec(tSDP_DISC_REC* p_rec, uint16_t profile_uuid,
798                                 uint16_t* p_version) {
799#if (SDP_CLIENT_ENABLED == TRUE)
800  tSDP_DISC_ATTR *p_attr, *p_sattr;
801
802  p_attr = p_rec->p_first_attr;
803  while (p_attr) {
804    /* Find the profile descriptor list */
805    if ((p_attr->attr_id == ATTR_ID_BT_PROFILE_DESC_LIST) &&
806        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
807      /* Walk through the protocol descriptor list */
808      for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr;
809           p_attr = p_attr->p_next_attr) {
810        /* Safety check - each entry should itself be a sequence */
811        if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
812          return (false);
813
814        /* Now, see if the entry contains the profile UUID we are interested in
815         */
816        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
817             p_sattr = p_sattr->p_next_attr) {
818          if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
819              (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) ==
820               2) /* <- This is bytes, not size code! */
821              && (p_sattr->attr_value.v.u16 == profile_uuid)) {
822            /* Now fill in the major and minor numbers */
823            /* if the attribute matches the description for version (type UINT,
824             * size 2 bytes) */
825            p_sattr = p_sattr->p_next_attr;
826
827            if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
828                 UINT_DESC_TYPE) &&
829                (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)) {
830              /* The high order 8 bits is the major number, low order is the
831               * minor number (big endian) */
832              *p_version = p_sattr->attr_value.v.u16;
833
834              return (true);
835            } else
836              return (false); /* The type and/or size was not valid for the
837                                 profile list version */
838          }
839        }
840      }
841
842      return (false);
843    }
844    p_attr = p_attr->p_next_attr;
845  }
846#endif /* CLIENT_ENABLED == TRUE */
847
848  /* If here, no match found */
849  return (false);
850}
851
852/*******************************************************************************
853 *                   Device Identification (DI) Client Functions
854 ******************************************************************************/
855
856/*******************************************************************************
857 *
858 * Function         SDP_DiDiscover
859 *
860 * Description      This function queries a remote device for DI information.
861 *
862 * Returns          SDP_SUCCESS if query started successfully, else error
863 *
864 ******************************************************************************/
865uint16_t SDP_DiDiscover(BD_ADDR remote_device, tSDP_DISCOVERY_DB* p_db,
866                        uint32_t len, tSDP_DISC_CMPL_CB* p_cb) {
867#if (SDP_CLIENT_ENABLED == TRUE)
868  uint16_t result = SDP_DI_DISC_FAILED;
869  uint16_t num_uuids = 1;
870  uint16_t di_uuid = UUID_SERVCLASS_PNP_INFORMATION;
871
872  /* build uuid for db init */
873  tSDP_UUID init_uuid;
874  init_uuid.len = 2;
875  init_uuid.uu.uuid16 = di_uuid;
876
877  if (SDP_InitDiscoveryDb(p_db, len, num_uuids, &init_uuid, 0, NULL))
878    if (SDP_ServiceSearchRequest(remote_device, p_db, p_cb))
879      result = SDP_SUCCESS;
880
881  return result;
882#else
883  return SDP_DI_DISC_FAILED;
884#endif
885}
886
887/*******************************************************************************
888 *
889 * Function         SDP_GetNumDiRecords
890 *
891 * Description      Searches specified database for DI records
892 *
893 * Returns          number of DI records found
894 *
895 ******************************************************************************/
896uint8_t SDP_GetNumDiRecords(tSDP_DISCOVERY_DB* p_db) {
897#if (SDP_CLIENT_ENABLED == TRUE)
898  uint8_t num_records = 0;
899  tSDP_DISC_REC* p_curr_record = NULL;
900
901  do {
902    p_curr_record = SDP_FindServiceInDb(p_db, UUID_SERVCLASS_PNP_INFORMATION,
903                                        p_curr_record);
904    if (p_curr_record) num_records++;
905  } while (p_curr_record);
906
907  return num_records;
908#else
909  return 0;
910#endif
911}
912
913/*******************************************************************************
914 *
915 * Function         SDP_AttrStringCopy
916 *
917 * Description      This function copy given attribute to specified buffer as a
918 *                  string
919 *
920 * Returns          none
921 *
922 ******************************************************************************/
923static void SDP_AttrStringCopy(char* dst, tSDP_DISC_ATTR* p_attr,
924                               uint16_t dst_size) {
925  if (dst == NULL) return;
926  if (p_attr) {
927    uint16_t len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
928    if (len > dst_size - 1) {
929      len = dst_size - 1;
930    }
931    memcpy(dst, (char*)p_attr->attr_value.v.array, len);
932    dst[len] = '\0';
933  } else {
934    dst[0] = '\0';
935  }
936}
937
938/*******************************************************************************
939 *
940 * Function         SDP_GetDiRecord
941 *
942 * Description      This function retrieves a remote device's DI record from
943 *                  the specified database.
944 *
945 * Returns          SDP_SUCCESS if record retrieved, else error
946 *
947 ******************************************************************************/
948uint16_t SDP_GetDiRecord(uint8_t get_record_index,
949                         tSDP_DI_GET_RECORD* p_device_info,
950                         tSDP_DISCOVERY_DB* p_db) {
951#if (SDP_CLIENT_ENABLED == TRUE)
952  uint16_t result = SDP_NO_DI_RECORD_FOUND;
953  uint8_t curr_record_index = 1;
954
955  tSDP_DISC_REC* p_curr_record = NULL;
956
957  /* find the requested SDP record in the discovery database */
958  do {
959    p_curr_record = SDP_FindServiceInDb(p_db, UUID_SERVCLASS_PNP_INFORMATION,
960                                        p_curr_record);
961    if (p_curr_record) {
962      if (curr_record_index++ == get_record_index) {
963        result = SDP_SUCCESS;
964        break;
965      }
966    }
967  } while (p_curr_record);
968
969  if (result == SDP_SUCCESS) {
970    /* copy the information from the SDP record to the DI record */
971    tSDP_DISC_ATTR* p_curr_attr = NULL;
972
973    /* ClientExecutableURL is optional */
974    p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_CLIENT_EXE_URL);
975    SDP_AttrStringCopy(p_device_info->rec.client_executable_url, p_curr_attr,
976                       SDP_MAX_ATTR_LEN);
977
978    /* Service Description is optional */
979    p_curr_attr =
980        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_SERVICE_DESCRIPTION);
981    SDP_AttrStringCopy(p_device_info->rec.service_description, p_curr_attr,
982                       SDP_MAX_ATTR_LEN);
983
984    /* DocumentationURL is optional */
985    p_curr_attr =
986        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_DOCUMENTATION_URL);
987    SDP_AttrStringCopy(p_device_info->rec.documentation_url, p_curr_attr,
988                       SDP_MAX_ATTR_LEN);
989
990    p_curr_attr =
991        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_SPECIFICATION_ID);
992    if (p_curr_attr)
993      p_device_info->spec_id = p_curr_attr->attr_value.v.u16;
994    else
995      result = SDP_ERR_ATTR_NOT_PRESENT;
996
997    p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_VENDOR_ID);
998    if (p_curr_attr)
999      p_device_info->rec.vendor = p_curr_attr->attr_value.v.u16;
1000    else
1001      result = SDP_ERR_ATTR_NOT_PRESENT;
1002
1003    p_curr_attr =
1004        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_VENDOR_ID_SOURCE);
1005    if (p_curr_attr)
1006      p_device_info->rec.vendor_id_source = p_curr_attr->attr_value.v.u16;
1007    else
1008      result = SDP_ERR_ATTR_NOT_PRESENT;
1009
1010    p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRODUCT_ID);
1011    if (p_curr_attr)
1012      p_device_info->rec.product = p_curr_attr->attr_value.v.u16;
1013    else
1014      result = SDP_ERR_ATTR_NOT_PRESENT;
1015
1016    p_curr_attr =
1017        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRODUCT_VERSION);
1018    if (p_curr_attr)
1019      p_device_info->rec.version = p_curr_attr->attr_value.v.u16;
1020    else
1021      result = SDP_ERR_ATTR_NOT_PRESENT;
1022
1023    p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRIMARY_RECORD);
1024    if (p_curr_attr)
1025      p_device_info->rec.primary_record = (bool)p_curr_attr->attr_value.v.u8;
1026    else
1027      result = SDP_ERR_ATTR_NOT_PRESENT;
1028  }
1029
1030  return result;
1031#else /* SDP_CLIENT_ENABLED is FALSE */
1032  return SDP_NO_DI_RECORD_FOUND;
1033#endif
1034}
1035
1036/*******************************************************************************
1037 *                   Device Identification (DI) Server Functions
1038 ******************************************************************************/
1039
1040/*******************************************************************************
1041 *
1042 * Function         SDP_SetLocalDiRecord
1043 *
1044 * Description      This function adds a DI record to the local SDP database.
1045 *
1046 *
1047 *
1048 * Returns          Returns SDP_SUCCESS if record added successfully, else error
1049 *
1050 ******************************************************************************/
1051uint16_t SDP_SetLocalDiRecord(tSDP_DI_RECORD* p_device_info,
1052                              uint32_t* p_handle) {
1053#if (SDP_SERVER_ENABLED == TRUE)
1054  uint16_t result = SDP_SUCCESS;
1055  uint32_t handle;
1056  uint16_t di_uuid = UUID_SERVCLASS_PNP_INFORMATION;
1057  uint16_t di_specid = BLUETOOTH_DI_SPECIFICATION;
1058  uint8_t temp_u16[2];
1059  uint8_t* p_temp;
1060  uint8_t u8;
1061
1062  *p_handle = 0;
1063  if (p_device_info == NULL) return SDP_ILLEGAL_PARAMETER;
1064
1065  /* if record is to be primary record, get handle to replace old primary */
1066  if (p_device_info->primary_record == true &&
1067      sdp_cb.server_db.di_primary_handle)
1068    handle = sdp_cb.server_db.di_primary_handle;
1069  else {
1070    handle = SDP_CreateRecord();
1071    if (handle == 0) return SDP_NO_RESOURCES;
1072  }
1073
1074  *p_handle = handle;
1075
1076  /* build the SDP entry */
1077  /* Add the UUID to the Service Class ID List */
1078  if ((SDP_AddServiceClassIdList(handle, 1, &di_uuid)) == false)
1079    result = SDP_DI_REG_FAILED;
1080
1081  /* mandatory */
1082  if (result == SDP_SUCCESS) {
1083    p_temp = temp_u16;
1084    UINT16_TO_BE_STREAM(p_temp, di_specid);
1085    if (!(SDP_AddAttribute(handle, ATTR_ID_SPECIFICATION_ID, UINT_DESC_TYPE,
1086                           sizeof(di_specid), temp_u16)))
1087      result = SDP_DI_REG_FAILED;
1088  }
1089
1090  /* optional - if string is null, do not add attribute */
1091  if (result == SDP_SUCCESS) {
1092    if (p_device_info->client_executable_url[0] != '\0') {
1093      if (!((strlen(p_device_info->client_executable_url) + 1 <=
1094             SDP_MAX_ATTR_LEN) &&
1095            SDP_AddAttribute(
1096                handle, ATTR_ID_CLIENT_EXE_URL, URL_DESC_TYPE,
1097                (uint32_t)(strlen(p_device_info->client_executable_url) + 1),
1098                (uint8_t*)p_device_info->client_executable_url)))
1099        result = SDP_DI_REG_FAILED;
1100    }
1101  }
1102
1103  /* optional - if string is null, do not add attribute */
1104  if (result == SDP_SUCCESS) {
1105    if (p_device_info->service_description[0] != '\0') {
1106      if (!((strlen(p_device_info->service_description) + 1 <=
1107             SDP_MAX_ATTR_LEN) &&
1108            SDP_AddAttribute(
1109                handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE,
1110                (uint32_t)(strlen(p_device_info->service_description) + 1),
1111                (uint8_t*)p_device_info->service_description)))
1112        result = SDP_DI_REG_FAILED;
1113    }
1114  }
1115
1116  /* optional - if string is null, do not add attribute */
1117  if (result == SDP_SUCCESS) {
1118    if (p_device_info->documentation_url[0] != '\0') {
1119      if (!((strlen(p_device_info->documentation_url) + 1 <=
1120             SDP_MAX_ATTR_LEN) &&
1121            SDP_AddAttribute(
1122                handle, ATTR_ID_DOCUMENTATION_URL, URL_DESC_TYPE,
1123                (uint32_t)(strlen(p_device_info->documentation_url) + 1),
1124                (uint8_t*)p_device_info->documentation_url)))
1125        result = SDP_DI_REG_FAILED;
1126    }
1127  }
1128
1129  /* mandatory */
1130  if (result == SDP_SUCCESS) {
1131    p_temp = temp_u16;
1132    UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor);
1133    if (!(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID, UINT_DESC_TYPE,
1134                           sizeof(p_device_info->vendor), temp_u16)))
1135      result = SDP_DI_REG_FAILED;
1136  }
1137
1138  /* mandatory */
1139  if (result == SDP_SUCCESS) {
1140    p_temp = temp_u16;
1141    UINT16_TO_BE_STREAM(p_temp, p_device_info->product);
1142    if (!(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_ID, UINT_DESC_TYPE,
1143                           sizeof(p_device_info->product), temp_u16)))
1144      result = SDP_DI_REG_FAILED;
1145  }
1146
1147  /* mandatory */
1148  if (result == SDP_SUCCESS) {
1149    p_temp = temp_u16;
1150    UINT16_TO_BE_STREAM(p_temp, p_device_info->version);
1151    if (!(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_VERSION, UINT_DESC_TYPE,
1152                           sizeof(p_device_info->version), temp_u16)))
1153      result = SDP_DI_REG_FAILED;
1154  }
1155
1156  /* mandatory */
1157  if (result == SDP_SUCCESS) {
1158    u8 = (uint8_t)p_device_info->primary_record;
1159    if (!(SDP_AddAttribute(handle, ATTR_ID_PRIMARY_RECORD, BOOLEAN_DESC_TYPE, 1,
1160                           &u8)))
1161      result = SDP_DI_REG_FAILED;
1162  }
1163
1164  /* mandatory */
1165  if (result == SDP_SUCCESS) {
1166    p_temp = temp_u16;
1167    UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor_id_source);
1168    if (!(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID_SOURCE, UINT_DESC_TYPE,
1169                           sizeof(p_device_info->vendor_id_source), temp_u16)))
1170      result = SDP_DI_REG_FAILED;
1171  }
1172
1173  if (result != SDP_SUCCESS)
1174    SDP_DeleteRecord(handle);
1175  else if (p_device_info->primary_record == true)
1176    sdp_cb.server_db.di_primary_handle = handle;
1177
1178  return result;
1179#else  /* SDP_SERVER_ENABLED is FALSE */
1180  return SDP_DI_REG_FAILED;
1181#endif /* if SDP_SERVER_ENABLED */
1182}
1183
1184/*******************************************************************************
1185 *
1186 * Function         SDP_SetTraceLevel
1187 *
1188 * Description      This function sets the trace level for SDP. If called with
1189 *                  a value of 0xFF, it simply reads the current trace level.
1190 *
1191 * Returns          the new (current) trace level
1192 *
1193 ******************************************************************************/
1194uint8_t SDP_SetTraceLevel(uint8_t new_level) {
1195  if (new_level != 0xFF) sdp_cb.trace_level = new_level;
1196
1197  return (sdp_cb.trace_level);
1198}
1199