GATT服務搜索流程(一)


GATT的規范閱讀起來還是比較簡答, 但是這樣的規范在代碼上是如何實現的呢?下面就分析一下bluedroid 協議棧關於GATT的代碼流程。

BLE的設備都是在SMP之后進行ATT的流程的交互。從代碼的實現中發現也是在SMP結束之后做回調的執行過程中進行GATT的搜索流程,SMP結束之后的回調函數是bta_dm_ble_smp_cback

/*******************************************************************************
**
** Function         bta_dm_ble_smp_cback
**
** Description      Callback for BLE SMP
**
**
** Returns          void
**
*******************************************************************************/
static UINT8 bta_dm_ble_smp_cback (tBTM_LE_EVT event, BD_ADDR bda, tBTM_LE_EVT_DATA *p_data)
{
    tBTM_STATUS status = BTM_SUCCESS;
    tBTA_DM_SEC sec_event;
    char *p_name = NULL;
    UINT8 i;
    tBT_DEVICE_TYPE dev_type;

    if (!bta_dm_cb.p_sec_cback)
        return BTM_NOT_AUTHORIZED;

    memset(&sec_event, 0, sizeof(tBTA_DM_SEC));
    switch (event)
    {
...
        case BTM_LE_COMPLT_EVT:
            bdcpy(sec_event.auth_cmpl.bd_addr, bda);
#if BLE_INCLUDED == TRUE
            BTM_ReadDevInfo(bda, &sec_event.auth_cmpl.dev_type, &sec_event.auth_cmpl.addr_type);
#endif
            p_name = BTM_SecReadDevName(bda);
            if (p_name != NULL)
            {
                BCM_STRNCPY_S((char*)sec_event.auth_cmpl.bd_name,
                               sizeof(BD_NAME), p_name, (BD_NAME_LEN));
            }
            else
            {
                sec_event.auth_cmpl.bd_name[0] = 0;
            }
            if (p_data->complt.reason != 0)
            {
                sec_event.auth_cmpl.fail_reason = BTA_DM_AUTH_CONVERT_SMP_CODE(((UINT8)p_data->complt.reason));
                /* delete this device entry from Sec Dev DB */
                bta_dm_remove_sec_dev_entry (bda);
            }
            else
            {
                sec_event.auth_cmpl.success = TRUE;
                if (!p_data->complt.smp_over_br)
                    GATT_ConfigServiceChangeCCC(bda, TRUE, BT_TRANSPORT_LE);//開始GATT的相關的流程
            }

            if (bta_dm_cb.p_sec_cback)
            {
                //bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event);
                bta_dm_cb.p_sec_cback(BTA_DM_BLE_AUTH_CMPL_EVT, &sec_event);//向上匯報狀態,這里也會觸發到GATT的流程
            }

            break;

 

這里就分為兩步:

  1. GATT_ConfigServiceChangeCCC
  2. bta_dm_cb.p_sec_cback(BTA_DM_BLE_AUTH_CMPL_EVT, &sec_event); 

首先來看第一個流程

GATT_ConfigServiceChangeCCC

這個函數根據名字,應該是配置service 改變的時候用何種方式通知client,看函數的實現流程:

/*******************************************************************************
**
** Function         GATT_ConfigServiceChangeCCC
**
** Description      Configure service change indication on remote device
**
** Returns          none
**
*******************************************************************************/
void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSPORT transport)
{
    UINT16              conn_id = GATT_INVALID_CONN_ID;
    tGATT_PROFILE_CLCB   *p_clcb = gatt_profile_find_clcb_by_bd_addr (remote_bda, transport);

    if (p_clcb == NULL)
        p_clcb = gatt_profile_clcb_alloc (0, remote_bda, transport);

    if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id, transport))
    {
        p_clcb->connected = TRUE;
    }
    /* hold the link here */
    GATT_Connect(gatt_cb.gatt_if, remote_bda, TRUE, transport);//建立GATT 連接
    p_clcb->ccc_stage = GATT_SVC_CHANGED_CONNECTING;//建立連接之后,更新ccc_stage 處於connecting 的狀態

    if (!p_clcb->connected)
    {
        /* wait for connection */
        return;
    }

    p_clcb->ccc_stage ++;//如果已經連接上了,那么就處於下一個階段
    gatt_cl_start_config_ccc(p_clcb);//開始ccc
}

 

這里的ccc就是  Client Characteristic Configuration的縮寫,這個流程有下面的幾個stage:

#define GATT_SVC_CHANGED_CONNECTING        1   /* wait for connection */
#define GATT_SVC_CHANGED_SERVICE           2   /* GATT service discovery */
#define GATT_SVC_CHANGED_CHARACTERISTIC    3   /* service change char discovery */
#define GATT_SVC_CHANGED_DESCRIPTOR        4   /* service change CCC discoery */
#define GATT_SVC_CHANGED_CONFIGURE_CCCD    5   /* config CCC */

 

從上面的代碼得知,建立了GATT 連接之后,處於GATT_SVC_CHANGED_SERVICE 的階段:然后真正開始CCC的流程,下面看看gatt_cl_start_config_ccc 的代碼實現:

/*******************************************************************************
**
** Function         gatt_cl_start_config_ccc
**
** Description      Gatt profile start configure service change CCC
**
** Returns          void
**
*******************************************************************************/
static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb)
{
    tGATT_DISC_PARAM    srvc_disc_param;
    tGATT_VALUE         ccc_value;

    memset (&srvc_disc_param, 0 , sizeof(tGATT_DISC_PARAM));
    memset (&ccc_value, 0 , sizeof(tGATT_VALUE));

    switch(p_clcb->ccc_stage)
    {
    case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */
        srvc_disc_param.s_handle = 1;
        srvc_disc_param.e_handle = 0xffff;
        srvc_disc_param.service.len = 2;
        srvc_disc_param.service.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER;//搜索的是GATT的server
        if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, &srvc_disc_param) != GATT_SUCCESS)//根據UUID 來搜索的
        {
            GATT_TRACE_ERROR("%s() - ccc service error", __FUNCTION__);
            gatt_config_ccc_complete(p_clcb);
        }
        break;

    case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */
        srvc_disc_param.s_handle = 1;
        srvc_disc_param.e_handle = p_clcb->e_handle;//discovery階段搜索到最后一個handle
        srvc_disc_param.service.len = 2;
        srvc_disc_param.service.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD;
        if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR, &srvc_disc_param) != GATT_SUCCESS)
        {
            GATT_TRACE_ERROR("%s() - ccc char error", __FUNCTION__);
            gatt_config_ccc_complete(p_clcb);
        }
        break;

    case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */
        srvc_disc_param.s_handle = p_clcb->s_handle;
        srvc_disc_param.e_handle = p_clcb->e_handle;
        if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, &srvc_disc_param) != GATT_SUCCESS)
        {
            GATT_TRACE_ERROR("%s() - ccc char descriptor error", __FUNCTION__);
            gatt_config_ccc_complete(p_clcb);
        }
        break;

    case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */
        ccc_value.handle = p_clcb->s_handle;
        ccc_value.len = 2;
        ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION;
        if (GATTC_Write (p_clcb->conn_id, GATT_WRITE, &ccc_value) != GATT_SUCCESS) //配置
        {
            GATT_TRACE_ERROR("%s() - write ccc error", __FUNCTION__);
            gatt_config_ccc_complete(p_clcb);
        }
        break;
    }
}

 

從上面代碼結構看,其思路還是很清晰,不同stage 去做不同的事情,前三個階段都是 執行GATTC_Discover 去搜索對端的服務。最后一個階段是GATT_SVC_CHANGED_CONFIGURE_CCCD,是調用GATTC_Write 配置CCC

下面先分析一下GATTC_Discover的實現:

/*******************************************************************************
**
** Function         GATTC_Discover
**
** Description      This function is called to do a discovery procedure on ATT server.
**
** Parameters       conn_id: connection identifier.
**                  disc_type:discovery type.
**                  p_param: parameters of discovery requirement.
**
** Returns          GATT_SUCCESS if command received/sent successfully.
**
*******************************************************************************/
tGATT_STATUS GATTC_Discover (UINT16 conn_id, tGATT_DISC_TYPE disc_type,
                             tGATT_DISC_PARAM *p_param)
{
    tGATT_STATUS    status = GATT_SUCCESS;
    tGATT_CLCB      *p_clcb;
    tGATT_IF        gatt_if=GATT_GET_GATT_IF(conn_id);//low 8 bits
    UINT8           tcb_idx = GATT_GET_TCB_IDX(conn_id);
    tGATT_TCB       *p_tcb = gatt_get_tcb_by_idx(tcb_idx);
    tGATT_REG       *p_reg = gatt_get_regcb(gatt_if);


    GATT_TRACE_API ("GATTC_Discover conn_id=%d disc_type=%d",conn_id, disc_type);

    if ( (p_tcb == NULL) || (p_reg==NULL) ||(p_param == NULL) ||
         (disc_type >= GATT_DISC_MAX))
    {
        GATT_TRACE_ERROR("GATTC_Discover Illegal param: disc_type %d conn_id = %d", disc_type, conn_id);
        return GATT_ILLEGAL_PARAMETER;
    }


    if (gatt_is_clcb_allocated(conn_id))
    {
        GATT_TRACE_ERROR("GATTC_Discover GATT_BUSY conn_id = %d", conn_id);
        return GATT_BUSY;
    }


    if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL )
    {
        if (!GATT_HANDLE_IS_VALID(p_param->s_handle) ||
            !GATT_HANDLE_IS_VALID(p_param->e_handle) ||
            /* search by type does not have a valid UUID param */
            (disc_type == GATT_DISC_SRVC_BY_UUID &&
             p_param->service.len == 0))
        {
            gatt_clcb_dealloc(p_clcb);
            return GATT_ILLEGAL_PARAMETER;
        }

        p_clcb->operation  = GATTC_OPTYPE_DISCOVERY;//這個opcode 並非ATT協議里面的opcode,這個只是代碼實現的一種的抽象表達,常用於在GATT rsp的處理中做邏輯判斷
        p_clcb->op_subtype = disc_type;//這個實際對應於各種ATT的各種opcode
        p_clcb->s_handle   = p_param->s_handle;
        p_clcb->e_handle   = p_param->e_handle;
        p_clcb->uuid       = p_param->service;

        gatt_act_discovery(p_clcb);//去實作discovery的行為,一層一層組包
    }
    else
    {
        status = GATT_NO_RESOURCES;
    }
    return status;
}

gatt_act_discovery 這個函數就不一層層往下分析了,最后會通過L2cap 的ATT通道下發下去。

我們發出的discovery 都會收到response的,如果沒有找到相關的屬性,就會回復一個err response。現在我們來分析一下:

在gatt_init函數中,會注冊到l2cap層相關的函數的注冊,這樣,當l2cap 有數據的時候,會調用相應的函數來通知到GATT層

/*******************************************************************************
**
** Function         gatt_init
**
** Description      This function is enable the GATT profile on the device.
**                  It clears out the control blocks, and registers with L2CAP.
**
** Returns          void
**
*******************************************************************************/
void gatt_init (void)
{
    tL2CAP_FIXED_CHNL_REG  fixed_reg;
...
    fixed_reg.pL2CA_FixedConn_Cb = gatt_le_connect_cback;
    fixed_reg.pL2CA_FixedData_Cb = gatt_le_data_ind;//數據傳輸函數
    fixed_reg.pL2CA_FixedCong_Cb = gatt_le_cong_cback;      /* congestion callback */
    fixed_reg.default_idle_tout  = 0xffff;                  /* 0xffff default idle timeout */

    L2CA_RegisterFixedChannel (L2CAP_ATT_CID, &fixed_reg);//注冊到l2cap
...

 從這邊我們知道,當l2cap 有數據的時候就會調用gatt_le_data_ind  來通知gatt層,其執行的核心代碼是gatt_data_process,並通過判斷我們是server端還是client 端來路由到不同的處理函數。

 當我們是client 端:gatt_client_handle_server_rsp  當我們是server 端,執行gatt_server_handle_client_req

下面我們簡單分析一下gatt_client_handle_server_rsp :

 

/*******************************************************************************
**
** Function         gatt_client_handle_server_rsp
**
** Description      This function is called to handle the server response to
**                  client.
**
**
** Returns          void
**
*******************************************************************************/
void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
                                    UINT16 len, UINT8 *p_data)
{
    tGATT_CLCB   *p_clcb = NULL;
    UINT8        rsp_code;

    if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF)
    {
        p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code);//取出發送的cmd

        rsp_code = gatt_cmd_to_rsp_code(rsp_code);

        if (p_clcb == NULL || (rsp_code != op_code && op_code != GATT_RSP_ERROR))
        {
            GATT_TRACE_WARNING ("ATT - Ignore wrong response. Receives (%02x) \
                                Request(%02x) Ignored", op_code, rsp_code);

            return;
        }
        else
        {
            btu_stop_timer (&p_clcb->rsp_timer_ent);//停掉rsp timer
            p_clcb->retry_count = 0;
        }
    }
    /* the size of the message may not be bigger than the local max PDU size*/
    /* The message has to be smaller than the agreed MTU, len does not count op_code */
    if (len >= p_tcb->payload_size)
    {
        GATT_TRACE_ERROR("invalid response/indicate pkt size: %d, PDU size: %d", len + 1, p_tcb->payload_size);
        if (op_code != GATT_HANDLE_VALUE_NOTIF &&
            op_code != GATT_HANDLE_VALUE_IND)
            gatt_end_operation(p_clcb, GATT_ERROR, NULL);
    }
    else
    {
        switch (op_code)
        {
            case GATT_RSP_ERROR://出錯的時候執行的語句
                gatt_process_error_rsp(p_tcb, p_clcb, op_code, len, p_data);
                break;

            case GATT_RSP_MTU:       /* 2 bytes mtu */
                gatt_process_mtu_rsp(p_tcb, p_clcb, len ,p_data);
                break;

            case GATT_RSP_FIND_INFO:
                gatt_process_read_info_rsp(p_tcb, p_clcb, op_code, len, p_data);
                break;

            case GATT_RSP_READ_BY_TYPE:
            case GATT_RSP_READ_BY_GRP_TYPE:
                gatt_process_read_by_type_rsp(p_tcb, p_clcb, op_code, len, p_data);
                break;

            case GATT_RSP_READ:
            case GATT_RSP_READ_BLOB:
            case GATT_RSP_READ_MULTI:
                gatt_process_read_rsp(p_tcb, p_clcb, op_code, len, p_data);
                break;

            case GATT_RSP_FIND_TYPE_VALUE: /* disc service with UUID */
                gatt_process_find_type_value_rsp(p_tcb, p_clcb, len, p_data);
                break;

            case GATT_RSP_WRITE:
                gatt_process_handle_rsp(p_clcb);
                break;

            case GATT_RSP_PREPARE_WRITE:
                gatt_process_prep_write_rsp(p_tcb, p_clcb, op_code, len, p_data);
                break;

            case GATT_RSP_EXEC_WRITE:
                gatt_end_operation(p_clcb, p_clcb->status, NULL);
                break;

            case GATT_HANDLE_VALUE_NOTIF:
            case GATT_HANDLE_VALUE_IND:
                gatt_process_notification(p_tcb, op_code, len, p_data);
                break;

            default:
                GATT_TRACE_ERROR("Unknown opcode = %d", op_code);
                break;
        }
    }

    if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF)
    {
        gatt_cl_send_next_cmd_inq(p_tcb);//如果還有數據pending,繼續發送
    }

    return;
}

 這邊我們可以預想,GATT開始進行搜索的時候是進行 primary service的搜索,然后搜索完了之后,再去搜索,服務器會有error response數據返回,那這里猜測應該是在error response中會完成stage 的轉換。如果對端發過來的數據是相應的response,那么就調用相應的處理函數,處理完了之后應該還是會發起一次新的服務搜索,直至結束。

下面我們依次分析這幾個函數:先看看錯誤處理:

/*******************************************************************************
**
** Function         gatt_proc_disc_error_rsp
**
** Description      This function process the read by type response and send another
**                  request if needed.
**
** Returns          void.
**
*******************************************************************************/
void gatt_proc_disc_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 opcode,
                              UINT16 handle, UINT8 reason)
{
    tGATT_STATUS    status = (tGATT_STATUS) reason;

    UNUSED(p_tcb);
    UNUSED(handle);
    switch (opcode)
    {
        case GATT_REQ_READ_BY_GRP_TYPE:
        case GATT_REQ_FIND_TYPE_VALUE:
        case GATT_REQ_READ_BY_TYPE:
        case GATT_REQ_FIND_INFO:
            if (reason == GATT_NOT_FOUND)
            {
                status = GATT_SUCCESS;
            }
            break;
        default:
            GATT_TRACE_ERROR("Incorrect discovery opcode %04x",   opcode);
            break;
    }
    gatt_end_operation(p_clcb, status, NULL);//搜索結束 執行該函數
}
/*******************************************************************************
**
** Function         gatt_end_operation
**
** Description      This function ends a discovery, send callback and finalize
**                  some control value.
**
** Returns          16 bits uuid.
**
*******************************************************************************/
void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data)
{
    tGATT_CL_COMPLETE   cb_data;
    tGATT_CMPL_CBACK    *p_cmpl_cb = (p_clcb->p_reg) ? p_clcb->p_reg->app_cb.p_cmpl_cb : NULL;
    UINT8               op = p_clcb->operation, disc_type=GATT_DISC_MAX;
    tGATT_DISC_CMPL_CB  *p_disc_cmpl_cb = (p_clcb->p_reg) ? p_clcb->p_reg->app_cb.p_disc_cmpl_cb : NULL;
    UINT16              conn_id;
    UINT8               operation;

    memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE));

    if (p_cmpl_cb != NULL && p_clcb->operation != 0)
    {
        if (p_clcb->operation == GATTC_OPTYPE_READ)
        {
            cb_data.att_value.handle   = p_clcb->s_handle;
            cb_data.att_value.len      = p_clcb->counter;

            if (p_data && p_clcb->counter)
                memcpy (cb_data.att_value.value, p_data, cb_data.att_value.len);
        }

        if (p_clcb->operation == GATTC_OPTYPE_WRITE)
        {
            memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE));
            cb_data.handle           =
            cb_data.att_value.handle = p_clcb->s_handle;
            if (p_clcb->op_subtype == GATT_WRITE_PREPARE)
            {
                if (p_data)
                {
                    cb_data.att_value = *((tGATT_VALUE *) p_data);
                }
                else
                {
                    GATT_TRACE_DEBUG("Rcv Prepare write rsp but no data");
                }
            }
        }

        if (p_clcb->operation == GATTC_OPTYPE_CONFIG)
            cb_data.mtu = p_clcb->p_tcb->payload_size;

        if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)
        {
            disc_type = p_clcb->op_subtype;
        }
    }

    if (p_clcb->p_attr_buf)  //free
    {
        GKI_freebuf(p_clcb->p_attr_buf);
    }

    operation =  p_clcb->operation;
    conn_id = p_clcb->conn_id;
    btu_stop_timer(&p_clcb->rsp_timer_ent);//停掉timer,是在發送的時候開啟的

    gatt_clcb_dealloc(p_clcb);//free 結構

    if (p_disc_cmpl_cb && (op == GATTC_OPTYPE_DISCOVERY))
        (*p_disc_cmpl_cb)(conn_id, disc_type, status);//調用回調
    else if (p_cmpl_cb && op)
        (*p_cmpl_cb)(conn_id, op, status, &cb_data);
    else
        GATT_TRACE_WARNING ("gatt_end_operation not sent out op=%d p_disc_cmpl_cb:%p p_cmpl_cb:%p",
                             operation, p_disc_cmpl_cb, p_cmpl_cb);
}

 

這里重點是 p_disc_cmpl_cb,看其實現:

/*******************************************************************************
**
** Function         gatt_disc_cmpl_cback
**
** Description      Gatt profile discovery complete callback
**
** Returns          void
**
*******************************************************************************/
static void gatt_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status)
{
    tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);

    if (p_clcb == NULL)
        return;

    if (status == GATT_SUCCESS && p_clcb->ccc_result > 0)
    {
        p_clcb->ccc_result = 0;
        p_clcb->ccc_stage ++;//轉換到下一個stage
        gatt_cl_start_config_ccc(p_clcb);//並且繼續進行CCC
    } else {
        GATT_TRACE_ERROR("%s() - Register for service changed indication failure", __FUNCTION__);
        /* free the connection */
        gatt_config_ccc_complete (p_clcb);
    }
}

 

從上面的代碼 ,我們發現,轉到了下一個stage 之后,又回到了gatt_cl_start_config_ccc ,進行下一輪的服務搜索。

下面我們繼續分析:gatt_process_find_type_value_rsp 這是對應於GATT_RSP_FIND_TYPE_VALUE 的case:

/*******************************************************************************
**
** Function         gatt_process_find_type_value_rsp
**
** Description      This function is called to handle find by type value response.
**
**
** Returns          void
**
*******************************************************************************/
void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data)
{
    tGATT_DISC_RES      result;
    UINT8               *p = p_data;

    /* unexpected response */
    if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_SRVC_BY_UUID) //這個函數處理的都是GATTC_OPTYPE_DISCOVERY且subtype是 GATT_DISC_SRVC_BY_UUID
        return;

    memset (&result, 0, sizeof(tGATT_DISC_RES));
    result.type.len = 2;
    result.type.uu.uuid16 = GATT_UUID_PRI_SERVICE;

    /* returns a series of handle ranges */
    while (len >= 4)
    {
        STREAM_TO_UINT16 (result.handle, p);
        STREAM_TO_UINT16 (result.value.group_value.e_handle, p);
        memcpy (&result.value.group_value.service_type,  &p_clcb->uuid, sizeof(tBT_UUID));

        len -= 4;

        if (p_clcb->p_reg->app_cb.p_disc_res_cb)
            (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result);//回調結果
    }

    /* last handle  + 1 */
    p_clcb->s_handle = (result.value.group_value.e_handle == 0) ? 0 : (result.value.group_value.e_handle + 1);//更新start handle,並繼續進行搜索
    /* initiate another request */
    gatt_act_discovery(p_clcb) ;
}

 這里又涉及到GATT的回調函數,我們需要看看其 來龍去脈:

發現在gatt_init 中會進行gatt_profile_db_init :

/*******************************************************************************
**
** Function         gatt_profile_db_init
**
** Description      Initializa the GATT profile attribute database.
**
*******************************************************************************/
void gatt_profile_db_init (void)
{
    tBT_UUID          app_uuid = {LEN_UUID_128, {0}};
    tBT_UUID          uuid = {LEN_UUID_16, {UUID_SERVCLASS_GATT_SERVER}};
    UINT16            service_handle = 0;
    tGATT_STATUS      status;

    /* Fill our internal UUID with a fixed pattern 0x81 */
    memset (&app_uuid.uu.uuid128, 0x81, LEN_UUID_128);

    /* Create a GATT profile service */
    gatt_cb.gatt_if = GATT_Register(&app_uuid, &gatt_profile_cback);//gatt_if是index of the client registered with GATT,gatt_if 是從1開始的
    GATT_StartIf(gatt_cb.gatt_if);

 

這里是將gatt_profile_cback 注冊到GATT:看看gatt_profile_cback 的實現:

static tGATT_CBACK gatt_profile_cback =
{
    gatt_connect_cback,
    gatt_cl_op_cmpl_cback,
    gatt_disc_res_cback,
    gatt_disc_cmpl_cback,
    gatt_request_cback,
    NULL,
    NULL
} ;

 

 那我們這里執行的函數就是gatt_disc_res_cback:

/*******************************************************************************
**
** Function         gatt_disc_res_cback
**
** Description      Gatt profile discovery result callback
**
** Returns          void
**
*******************************************************************************/
static void gatt_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data)
{
    tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);

    if (p_clcb == NULL)
        return;

    switch (disc_type)
    {
    case GATT_DISC_SRVC_BY_UUID:/* stage 1 */
        p_clcb->e_handle = p_data->value.group_value.e_handle;//獲得服務器返回的最后一個handle,繼續搜索是s = e+1
        p_clcb->ccc_result ++;//這個標志在discovery complete函數中復位
        break;

    case GATT_DISC_CHAR:/* stage 2 */
        p_clcb->s_handle = p_data->value.dclr_value.val_handle;
        p_clcb->ccc_result ++;
        break;

    case GATT_DISC_CHAR_DSCPT: /* stage 3 */
        if (p_data->type.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG)
        {
            p_clcb->s_handle = p_data->handle;
            p_clcb->ccc_result ++;
        }
        break;
    }
}

 

我們看到   gatt_process_find_type_value_rsp   最后會更新 下一輪開始搜索的hanle 值。

其他的函數 的實現都是 類似的,就不一一分析了,那 Client Characteristic Configuration的分析就到此結束。

SMP 結束之后,還會向上面匯報SMP 結束的event,限於篇幅的原因,關於

bta_dm_cb.p_sec_cback(BTA_DM_BLE_AUTH_CMPL_EVT, &sec_event); 

 的部分,將在GATT 服務搜索流程二中描述。

 

 

 


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM