Home | History | Annotate | Download | only in sdp
      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 the main SDP 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 
     35 #include "l2c_api.h"
     36 #include "l2cdefs.h"
     37 #include "osi/include/osi.h"
     38 
     39 #include "btm_api.h"
     40 #include "btu.h"
     41 
     42 #include "sdp_api.h"
     43 #include "sdpint.h"
     44 
     45 /******************************************************************************/
     46 /*                     G L O B A L      S D P       D A T A                   */
     47 /******************************************************************************/
     48 tSDP_CB sdp_cb;
     49 
     50 /******************************************************************************/
     51 /*            L O C A L    F U N C T I O N     P R O T O T Y P E S            */
     52 /******************************************************************************/
     53 static void sdp_connect_ind(const RawAddress& bd_addr, uint16_t l2cap_cid,
     54                             UNUSED_ATTR uint16_t psm, uint8_t l2cap_id);
     55 static void sdp_config_ind(uint16_t l2cap_cid, tL2CAP_CFG_INFO* p_cfg);
     56 static void sdp_config_cfm(uint16_t l2cap_cid, tL2CAP_CFG_INFO* p_cfg);
     57 static void sdp_disconnect_ind(uint16_t l2cap_cid, bool ack_needed);
     58 static void sdp_data_ind(uint16_t l2cap_cid, BT_HDR* p_msg);
     59 
     60 static void sdp_connect_cfm(uint16_t l2cap_cid, uint16_t result);
     61 static void sdp_disconnect_cfm(uint16_t l2cap_cid, uint16_t result);
     62 
     63 /*******************************************************************************
     64  *
     65  * Function         sdp_init
     66  *
     67  * Description      This function initializes the SDP unit.
     68  *
     69  * Returns          void
     70  *
     71  ******************************************************************************/
     72 void sdp_init(void) {
     73   /* Clears all structures and local SDP database (if Server is enabled) */
     74   memset(&sdp_cb, 0, sizeof(tSDP_CB));
     75 
     76   /* Initialize the L2CAP configuration. We only care about MTU and flush */
     77   sdp_cb.l2cap_my_cfg.mtu_present = true;
     78   sdp_cb.l2cap_my_cfg.mtu = SDP_MTU_SIZE;
     79   sdp_cb.l2cap_my_cfg.flush_to_present = true;
     80   sdp_cb.l2cap_my_cfg.flush_to = SDP_FLUSH_TO;
     81 
     82   sdp_cb.max_attr_list_size = SDP_MTU_SIZE - 16;
     83   sdp_cb.max_recs_per_search = SDP_MAX_DISC_SERVER_RECS;
     84 
     85 #if (SDP_SERVER_ENABLED == TRUE)
     86   /* Register with Security Manager for the specific security level */
     87   if (!BTM_SetSecurityLevel(false, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
     88                             SDP_SECURITY_LEVEL, SDP_PSM, 0, 0)) {
     89     SDP_TRACE_ERROR("Security Registration Server failed");
     90     return;
     91   }
     92 #endif
     93 
     94   /* Register with Security Manager for the specific security level */
     95   if (!BTM_SetSecurityLevel(true, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
     96                             SDP_SECURITY_LEVEL, SDP_PSM, 0, 0)) {
     97     SDP_TRACE_ERROR("Security Registration for Client failed");
     98     return;
     99   }
    100 
    101 #if defined(SDP_INITIAL_TRACE_LEVEL)
    102   sdp_cb.trace_level = SDP_INITIAL_TRACE_LEVEL;
    103 #else
    104   sdp_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */
    105 #endif
    106 
    107   sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind;
    108   sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm;
    109   sdp_cb.reg_info.pL2CA_ConnectPnd_Cb = NULL;
    110   sdp_cb.reg_info.pL2CA_ConfigInd_Cb = sdp_config_ind;
    111   sdp_cb.reg_info.pL2CA_ConfigCfm_Cb = sdp_config_cfm;
    112   sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind;
    113   sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm;
    114   sdp_cb.reg_info.pL2CA_QoSViolationInd_Cb = NULL;
    115   sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind;
    116   sdp_cb.reg_info.pL2CA_CongestionStatus_Cb = NULL;
    117   sdp_cb.reg_info.pL2CA_TxComplete_Cb = NULL;
    118 
    119   /* Now, register with L2CAP */
    120   if (!L2CA_Register(SDP_PSM, &sdp_cb.reg_info)) {
    121     SDP_TRACE_ERROR("SDP Registration failed");
    122   }
    123 }
    124 
    125 #if (SDP_DEBUG == TRUE)
    126 /*******************************************************************************
    127  *
    128  * Function         sdp_set_max_attr_list_size
    129  *
    130  * Description      This function sets the max attribute list size to use
    131  *
    132  * Returns          void
    133  *
    134  ******************************************************************************/
    135 uint16_t sdp_set_max_attr_list_size(uint16_t max_size) {
    136   if (max_size > (sdp_cb.l2cap_my_cfg.mtu - 16))
    137     max_size = sdp_cb.l2cap_my_cfg.mtu - 16;
    138 
    139   sdp_cb.max_attr_list_size = max_size;
    140 
    141   return sdp_cb.max_attr_list_size;
    142 }
    143 #endif
    144 
    145 /*******************************************************************************
    146  *
    147  * Function         sdp_connect_ind
    148  *
    149  * Description      This function handles an inbound connection indication
    150  *                  from L2CAP. This is the case where we are acting as a
    151  *                  server.
    152  *
    153  * Returns          void
    154  *
    155  ******************************************************************************/
    156 static void sdp_connect_ind(const RawAddress& bd_addr, uint16_t l2cap_cid,
    157                             UNUSED_ATTR uint16_t psm, uint8_t l2cap_id) {
    158 #if (SDP_SERVER_ENABLED == TRUE)
    159   tCONN_CB* p_ccb;
    160 
    161   /* Allocate a new CCB. Return if none available. */
    162   p_ccb = sdpu_allocate_ccb();
    163   if (p_ccb == NULL) return;
    164 
    165   /* Transition to the next appropriate state, waiting for config setup. */
    166   p_ccb->con_state = SDP_STATE_CFG_SETUP;
    167 
    168   /* Save the BD Address and Channel ID. */
    169   p_ccb->device_address = bd_addr;
    170   p_ccb->connection_id = l2cap_cid;
    171 
    172   /* Send response to the L2CAP layer. */
    173   L2CA_ConnectRsp(bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
    174   {
    175     tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg;
    176 
    177     if (cfg.fcr_present) {
    178       SDP_TRACE_DEBUG(
    179           "sdp_connect_ind:  mode %u, txwinsz %u, max_trans %u, rtrans_tout "
    180           "%u, mon_tout %u, mps %u",
    181           cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit,
    182           cfg.fcr.rtrans_tout, cfg.fcr.mon_tout, cfg.fcr.mps);
    183     }
    184 
    185     if ((!L2CA_ConfigReq(l2cap_cid, &cfg)) && cfg.fcr_present &&
    186         cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) {
    187       /* FCR not desired; try again in basic mode */
    188       cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
    189       cfg.fcr_present = false;
    190       L2CA_ConfigReq(l2cap_cid, &cfg);
    191     }
    192   }
    193 
    194   SDP_TRACE_EVENT("SDP - Rcvd L2CAP conn ind, sent config req, CID 0x%x",
    195                   p_ccb->connection_id);
    196 #else /* No server */
    197   /* Reject the connection */
    198   L2CA_ConnectRsp(bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_PSM, 0);
    199 #endif
    200 }
    201 
    202 /*******************************************************************************
    203  *
    204  * Function         sdp_connect_cfm
    205  *
    206  * Description      This function handles the connect confirm events
    207  *                  from L2CAP. This is the case when we are acting as a
    208  *                  client and have sent a connect request.
    209  *
    210  * Returns          void
    211  *
    212  ******************************************************************************/
    213 static void sdp_connect_cfm(uint16_t l2cap_cid, uint16_t result) {
    214   tCONN_CB* p_ccb;
    215   tL2CAP_CFG_INFO cfg;
    216 
    217   /* Find CCB based on CID */
    218   p_ccb = sdpu_find_ccb_by_cid(l2cap_cid);
    219   if (p_ccb == NULL) {
    220     SDP_TRACE_WARNING("SDP - Rcvd conn cnf for unknown CID 0x%x", l2cap_cid);
    221     return;
    222   }
    223 
    224   /* If the connection response contains success status, then */
    225   /* Transition to the next state and startup the timer.      */
    226   if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == SDP_STATE_CONN_SETUP)) {
    227     p_ccb->con_state = SDP_STATE_CFG_SETUP;
    228 
    229     cfg = sdp_cb.l2cap_my_cfg;
    230 
    231     if (cfg.fcr_present) {
    232       SDP_TRACE_DEBUG(
    233           "sdp_connect_cfm:  mode %u, txwinsz %u, max_trans %u, rtrans_tout "
    234           "%u, mon_tout %u, mps %u",
    235           cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit,
    236           cfg.fcr.rtrans_tout, cfg.fcr.mon_tout, cfg.fcr.mps);
    237     }
    238 
    239     if ((!L2CA_ConfigReq(l2cap_cid, &cfg)) && cfg.fcr_present &&
    240         cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) {
    241       /* FCR not desired; try again in basic mode */
    242       cfg.fcr_present = false;
    243       cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
    244       L2CA_ConfigReq(l2cap_cid, &cfg);
    245     }
    246 
    247     SDP_TRACE_EVENT("SDP - got conn cnf, sent cfg req, CID: 0x%x",
    248                     p_ccb->connection_id);
    249   } else {
    250     SDP_TRACE_WARNING("SDP - Rcvd conn cnf with error: 0x%x  CID 0x%x", result,
    251                       p_ccb->connection_id);
    252 
    253     /* Tell the user if he has a callback */
    254     if (p_ccb->p_cb || p_ccb->p_cb2) {
    255       uint16_t err = -1;
    256       if ((result == HCI_ERR_HOST_REJECT_SECURITY) ||
    257           (result == HCI_ERR_AUTH_FAILURE) ||
    258           (result == HCI_ERR_PAIRING_NOT_ALLOWED) ||
    259           (result == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) ||
    260           (result == HCI_ERR_KEY_MISSING))
    261         err = SDP_SECURITY_ERR;
    262       else if (result == HCI_ERR_HOST_REJECT_DEVICE)
    263         err = SDP_CONN_REJECTED;
    264       else
    265         err = SDP_CONN_FAILED;
    266       if (p_ccb->p_cb)
    267         (*p_ccb->p_cb)(err);
    268       else if (p_ccb->p_cb2)
    269         (*p_ccb->p_cb2)(err, p_ccb->user_data);
    270     }
    271     sdpu_release_ccb(p_ccb);
    272   }
    273 }
    274 
    275 /*******************************************************************************
    276  *
    277  * Function         sdp_config_ind
    278  *
    279  * Description      This function processes the L2CAP configuration indication
    280  *                  event.
    281  *
    282  * Returns          void
    283  *
    284  ******************************************************************************/
    285 static void sdp_config_ind(uint16_t l2cap_cid, tL2CAP_CFG_INFO* p_cfg) {
    286   tCONN_CB* p_ccb;
    287 
    288   /* Find CCB based on CID */
    289   p_ccb = sdpu_find_ccb_by_cid(l2cap_cid);
    290   if (p_ccb == NULL) {
    291     SDP_TRACE_WARNING("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
    292     return;
    293   }
    294 
    295   /* Remember the remote MTU size */
    296   if (!p_cfg->mtu_present) {
    297     /* use min(L2CAP_DEFAULT_MTU,SDP_MTU_SIZE) for GKI buffer size reasons */
    298     p_ccb->rem_mtu_size =
    299         (L2CAP_DEFAULT_MTU > SDP_MTU_SIZE) ? SDP_MTU_SIZE : L2CAP_DEFAULT_MTU;
    300   } else {
    301     if (p_cfg->mtu > SDP_MTU_SIZE)
    302       p_ccb->rem_mtu_size = SDP_MTU_SIZE;
    303     else
    304       p_ccb->rem_mtu_size = p_cfg->mtu;
    305   }
    306 
    307   /* For now, always accept configuration from the other side */
    308   p_cfg->flush_to_present = false;
    309   p_cfg->mtu_present = false;
    310   p_cfg->result = L2CAP_CFG_OK;
    311 
    312   /* Check peer config request against our rfcomm configuration */
    313   if (p_cfg->fcr_present) {
    314     /* Reject the window size if it is bigger than we want it to be */
    315     if (p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE) {
    316       if (sdp_cb.l2cap_my_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE &&
    317           p_cfg->fcr.tx_win_sz > sdp_cb.l2cap_my_cfg.fcr.tx_win_sz) {
    318         p_cfg->fcr.tx_win_sz = sdp_cb.l2cap_my_cfg.fcr.tx_win_sz;
    319         p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
    320         SDP_TRACE_DEBUG(
    321             "sdp_config_ind(CONFIG) -> Please try again with SMALLER TX "
    322             "WINDOW");
    323       }
    324 
    325       /* Reject if locally we want basic and they don't */
    326       if (sdp_cb.l2cap_my_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) {
    327         /* Ask for a new setup */
    328         p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
    329         p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
    330         SDP_TRACE_DEBUG(
    331             "sdp_config_ind(CONFIG) -> Please try again with BASIC mode");
    332       }
    333       /* Remain in configure state and give the peer our desired configuration
    334        */
    335       if (p_cfg->result != L2CAP_CFG_OK) {
    336         SDP_TRACE_WARNING(
    337             "SDP - Rcvd cfg ind, Unacceptable Parameters sent cfg cfm, CID: "
    338             "0x%x",
    339             l2cap_cid);
    340         L2CA_ConfigRsp(l2cap_cid, p_cfg);
    341         return;
    342       }
    343     } else /* We agree with peer's request */
    344       p_cfg->fcr_present = false;
    345   }
    346 
    347   L2CA_ConfigRsp(l2cap_cid, p_cfg);
    348 
    349   SDP_TRACE_EVENT("SDP - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid);
    350 
    351   p_ccb->con_flags |= SDP_FLAGS_HIS_CFG_DONE;
    352 
    353   if (p_ccb->con_flags & SDP_FLAGS_MY_CFG_DONE) {
    354     p_ccb->con_state = SDP_STATE_CONNECTED;
    355 
    356     if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) {
    357       sdp_disc_connected(p_ccb);
    358     } else {
    359       /* Start inactivity timer */
    360       alarm_set_on_mloop(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS,
    361                          sdp_conn_timer_timeout, p_ccb);
    362     }
    363   }
    364 }
    365 
    366 /*******************************************************************************
    367  *
    368  * Function         sdp_config_cfm
    369  *
    370  * Description      This function processes the L2CAP configuration confirmation
    371  *                  event.
    372  *
    373  * Returns          void
    374  *
    375  ******************************************************************************/
    376 static void sdp_config_cfm(uint16_t l2cap_cid, tL2CAP_CFG_INFO* p_cfg) {
    377   tCONN_CB* p_ccb;
    378 
    379   SDP_TRACE_EVENT("SDP - Rcvd cfg cfm, CID: 0x%x  Result: %d", l2cap_cid,
    380                   p_cfg->result);
    381 
    382   /* Find CCB based on CID */
    383   p_ccb = sdpu_find_ccb_by_cid(l2cap_cid);
    384   if (p_ccb == NULL) {
    385     SDP_TRACE_WARNING("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
    386     return;
    387   }
    388 
    389   /* For now, always accept configuration from the other side */
    390   if (p_cfg->result == L2CAP_CFG_OK) {
    391     p_ccb->con_flags |= SDP_FLAGS_MY_CFG_DONE;
    392 
    393     if (p_ccb->con_flags & SDP_FLAGS_HIS_CFG_DONE) {
    394       p_ccb->con_state = SDP_STATE_CONNECTED;
    395 
    396       if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) {
    397         sdp_disc_connected(p_ccb);
    398       } else {
    399         /* Start inactivity timer */
    400         alarm_set_on_mloop(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS,
    401                            sdp_conn_timer_timeout, p_ccb);
    402       }
    403     }
    404   } else {
    405     /* If peer has rejected FCR and suggested basic then try basic */
    406     if (p_cfg->fcr_present) {
    407       tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg;
    408       cfg.fcr_present = false;
    409       L2CA_ConfigReq(l2cap_cid, &cfg);
    410 
    411       /* Remain in configure state */
    412       return;
    413     }
    414 
    415     sdp_disconnect(p_ccb, SDP_CFG_FAILED);
    416   }
    417 }
    418 
    419 /*******************************************************************************
    420  *
    421  * Function         sdp_disconnect_ind
    422  *
    423  * Description      This function handles a disconnect event from L2CAP. If
    424  *                  requested to, we ack the disconnect before dropping the CCB
    425  *
    426  * Returns          void
    427  *
    428  ******************************************************************************/
    429 static void sdp_disconnect_ind(uint16_t l2cap_cid, bool ack_needed) {
    430   tCONN_CB* p_ccb;
    431 
    432   /* Find CCB based on CID */
    433   p_ccb = sdpu_find_ccb_by_cid(l2cap_cid);
    434   if (p_ccb == NULL) {
    435     SDP_TRACE_WARNING("SDP - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid);
    436     return;
    437   }
    438 
    439   if (ack_needed) L2CA_DisconnectRsp(l2cap_cid);
    440 
    441   SDP_TRACE_EVENT("SDP - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid);
    442   /* Tell the user if he has a callback */
    443   if (p_ccb->p_cb)
    444     (*p_ccb->p_cb)((uint16_t)((p_ccb->con_state == SDP_STATE_CONNECTED)
    445                                   ? SDP_SUCCESS
    446                                   : SDP_CONN_FAILED));
    447   else if (p_ccb->p_cb2)
    448     (*p_ccb->p_cb2)(
    449         (uint16_t)((p_ccb->con_state == SDP_STATE_CONNECTED) ? SDP_SUCCESS
    450                                                              : SDP_CONN_FAILED),
    451         p_ccb->user_data);
    452 
    453   sdpu_release_ccb(p_ccb);
    454 }
    455 
    456 /*******************************************************************************
    457  *
    458  * Function         sdp_data_ind
    459  *
    460  * Description      This function is called when data is received from L2CAP.
    461  *                  if we are the originator of the connection, we are the SDP
    462  *                  client, and the received message is queued for the client.
    463  *
    464  *                  If we are the destination of the connection, we are the SDP
    465  *                  server, so the message is passed to the server processing
    466  *                  function.
    467  *
    468  * Returns          void
    469  *
    470  ******************************************************************************/
    471 static void sdp_data_ind(uint16_t l2cap_cid, BT_HDR* p_msg) {
    472   tCONN_CB* p_ccb;
    473 
    474   /* Find CCB based on CID */
    475   p_ccb = sdpu_find_ccb_by_cid(l2cap_cid);
    476   if (p_ccb != NULL) {
    477     if (p_ccb->con_state == SDP_STATE_CONNECTED) {
    478       if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
    479         sdp_disc_server_rsp(p_ccb, p_msg);
    480       else
    481         sdp_server_handle_client_req(p_ccb, p_msg);
    482     } else {
    483       SDP_TRACE_WARNING(
    484           "SDP - Ignored L2CAP data while in state: %d, CID: 0x%x",
    485           p_ccb->con_state, l2cap_cid);
    486     }
    487   } else {
    488     SDP_TRACE_WARNING("SDP - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid);
    489   }
    490 
    491   osi_free(p_msg);
    492 }
    493 
    494 /*******************************************************************************
    495  *
    496  * Function         sdp_conn_originate
    497  *
    498  * Description      This function is called from the API to originate a
    499  *                  connection.
    500  *
    501  * Returns          void
    502  *
    503  ******************************************************************************/
    504 tCONN_CB* sdp_conn_originate(const RawAddress& p_bd_addr) {
    505   tCONN_CB* p_ccb;
    506   uint16_t cid;
    507 
    508   /* Allocate a new CCB. Return if none available. */
    509   p_ccb = sdpu_allocate_ccb();
    510   if (p_ccb == NULL) {
    511     SDP_TRACE_WARNING("SDP - no spare CCB for orig");
    512     return (NULL);
    513   }
    514 
    515   SDP_TRACE_EVENT("SDP - Originate started");
    516 
    517   /* We are the originator of this connection */
    518   p_ccb->con_flags |= SDP_FLAGS_IS_ORIG;
    519 
    520   /* Save the BD Address and Channel ID. */
    521   p_ccb->device_address = p_bd_addr;
    522   ;
    523 
    524   /* Transition to the next appropriate state, waiting for connection confirm.
    525    */
    526   p_ccb->con_state = SDP_STATE_CONN_SETUP;
    527 
    528   cid = L2CA_ConnectReq(SDP_PSM, p_bd_addr);
    529 
    530   /* Check if L2CAP started the connection process */
    531   if (cid != 0) {
    532     p_ccb->connection_id = cid;
    533 
    534     return (p_ccb);
    535   } else {
    536     SDP_TRACE_WARNING("SDP - Originate failed");
    537     sdpu_release_ccb(p_ccb);
    538     return (NULL);
    539   }
    540 }
    541 
    542 /*******************************************************************************
    543  *
    544  * Function         sdp_disconnect
    545  *
    546  * Description      This function disconnects a connection.
    547  *
    548  * Returns          void
    549  *
    550  ******************************************************************************/
    551 void sdp_disconnect(tCONN_CB* p_ccb, uint16_t reason) {
    552 #if (SDP_BROWSE_PLUS == TRUE)
    553 
    554   /* If we are browsing for multiple UUIDs ... */
    555   if ((p_ccb->con_state == SDP_STATE_CONNECTED) &&
    556       (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) &&
    557       ((reason == SDP_SUCCESS) || (reason == SDP_NO_RECS_MATCH))) {
    558     /* If the browse found something, do no more searching */
    559     if ((p_ccb->cur_uuid_idx == 0) && (p_ccb->p_db->p_first_rec))
    560       p_ccb->cur_uuid_idx = p_ccb->p_db->num_uuid_filters;
    561 
    562     while (++p_ccb->cur_uuid_idx < p_ccb->p_db->num_uuid_filters) {
    563       /* Check we have not already found the UUID (maybe through browse) */
    564       if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len == 2) &&
    565           (SDP_FindServiceInDb(
    566               p_ccb->p_db,
    567               p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].uu.uuid16, NULL)))
    568         continue;
    569 
    570       if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len > 2) &&
    571           (SDP_FindServiceUUIDInDb(
    572               p_ccb->p_db, &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx],
    573               NULL)))
    574         continue;
    575 
    576       p_ccb->cur_handle = 0;
    577 
    578       SDP_TRACE_EVENT("SDP - looking for for more,  CID: 0x%x",
    579                       p_ccb->connection_id);
    580 
    581       sdp_disc_connected(p_ccb);
    582       return;
    583     }
    584   }
    585 
    586   if ((reason == SDP_NO_RECS_MATCH) && (p_ccb->p_db->p_first_rec))
    587     reason = SDP_SUCCESS;
    588 
    589 #endif
    590 
    591   SDP_TRACE_EVENT("SDP - disconnect  CID: 0x%x", p_ccb->connection_id);
    592 
    593   /* Check if we have a connection ID */
    594   if (p_ccb->connection_id != 0) {
    595     L2CA_DisconnectReq(p_ccb->connection_id);
    596     p_ccb->disconnect_reason = reason;
    597   }
    598 
    599   /* If at setup state, we may not get callback ind from L2CAP */
    600   /* Call user callback immediately */
    601   if (p_ccb->con_state == SDP_STATE_CONN_SETUP) {
    602     /* Tell the user if he has a callback */
    603     if (p_ccb->p_cb)
    604       (*p_ccb->p_cb)(reason);
    605     else if (p_ccb->p_cb2)
    606       (*p_ccb->p_cb2)(reason, p_ccb->user_data);
    607 
    608     sdpu_release_ccb(p_ccb);
    609   }
    610 }
    611 
    612 /*******************************************************************************
    613  *
    614  * Function         sdp_disconnect_cfm
    615  *
    616  * Description      This function handles a disconnect confirm event from L2CAP.
    617  *
    618  * Returns          void
    619  *
    620  ******************************************************************************/
    621 static void sdp_disconnect_cfm(uint16_t l2cap_cid,
    622                                UNUSED_ATTR uint16_t result) {
    623   tCONN_CB* p_ccb;
    624 
    625   /* Find CCB based on CID */
    626   p_ccb = sdpu_find_ccb_by_cid(l2cap_cid);
    627   if (p_ccb == NULL) {
    628     SDP_TRACE_WARNING("SDP - Rcvd L2CAP disc cfm, unknown CID: 0x%x",
    629                       l2cap_cid);
    630     return;
    631   }
    632 
    633   SDP_TRACE_EVENT("SDP - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid);
    634 
    635   /* Tell the user if he has a callback */
    636   if (p_ccb->p_cb)
    637     (*p_ccb->p_cb)(p_ccb->disconnect_reason);
    638   else if (p_ccb->p_cb2)
    639     (*p_ccb->p_cb2)(p_ccb->disconnect_reason, p_ccb->user_data);
    640 
    641   sdpu_release_ccb(p_ccb);
    642 }
    643 
    644 
    645 /*******************************************************************************
    646  *
    647  * Function         sdp_conn_timer_timeout
    648  *
    649  * Description      This function processes a timeout. Currently, we simply send
    650  *                  a disconnect request to L2CAP.
    651  *
    652  * Returns          void
    653  *
    654  ******************************************************************************/
    655 void sdp_conn_timer_timeout(void* data) {
    656   tCONN_CB* p_ccb = (tCONN_CB*)data;
    657 
    658   SDP_TRACE_EVENT("SDP - CCB timeout in state: %d  CID: 0x%x", p_ccb->con_state,
    659                   p_ccb->connection_id);
    660 
    661   L2CA_DisconnectReq(p_ccb->connection_id);
    662   /* Tell the user if he has a callback */
    663   if (p_ccb->p_cb)
    664     (*p_ccb->p_cb)(SDP_CONN_FAILED);
    665   else if (p_ccb->p_cb2)
    666     (*p_ccb->p_cb2)(SDP_CONN_FAILED, p_ccb->user_data);
    667   sdpu_release_ccb(p_ccb);
    668 }
    669