• 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 服务搜索流程二中描述。


  • 相关阅读:
    Flask-SQLAlchemy
    with 与 上下文管理器
    使用@property
    C++:如何把一个int转成4个字节?
    尝试理解Flask源码 之 搞懂WSGI协议
    qt setData()和data()
    我使用过的Linux命令之sftp
    linux下如何使用sftp命令
    Linux环境下安装JDK
    CentOS 6.5 配置IP地址的三种方法
  • 原文地址:https://www.cnblogs.com/libs-liu/p/9334908.html
Copyright © 2020-2023  润新知