Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android蓝牙源码分析——Gatt写设备

$
0
0

BluetoothGatt中的writeCharacteristic的实现在GattService中,如下:

void writeCharacteristic(int clientIf, String address, int handle, int writeType, int authReq, byte[] value) {
    gattClientWriteCharacteristicNative(connId, handle, writeType, authReq, value);
}

这个gattClientWriteCharacteristicNative的实现在com_android_bluetooth_gatt.cpp中,

static void gattClientWriteCharacteristicNative(JNIEnv* env, jobject object,
    jint conn_id, jint handle, jint write_type, jint auth_req, jbyteArray value) {
    ......
    sGattIf->client->write_characteristic(conn_id, handle, write_type, auth_req,
                                          std::move(vect_val));
}

这个sGattIf的client是定义在btif_gatt_client.c中的btgattClientInterface,这里调到了btif_gattc_write_char函数,

static bt_status_t btif_gattc_write_char(int conn_id, btgatt_srvc_id_t* srvc_id,
                                         btgatt_gatt_id_t* char_id, int write_type,
                                         int len, int auth_req, char* p_value)
{
    btif_gattc_cb_t btif_cb;
    btif_cb.conn_id = (uint16_t) conn_id;
    btif_cb.auth_req = (uint8_t) auth_req;
    btif_cb.write_type = (uint8_t) write_type;
    btif_cb.len = len > BTGATT_MAX_ATTR_LEN ? BTGATT_MAX_ATTR_LEN : len;
    memcpy(&btif_cb.srvc_id, srvc_id, sizeof(btgatt_srvc_id_t));
    memcpy(&btif_cb.char_id, char_id, sizeof(btgatt_gatt_id_t));
    memcpy(btif_cb.value, p_value, btif_cb.len);
    return btif_transfer_context(btgattc_handle_event, BTIF_GATTC_WRITE_CHAR,
                                 (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}

这里发送到butif task中,由btgattc_handle_event处理,事件为BTIF_GATTC_WRITE_CHAR,如下:

case BTIF_GATTC_WRITE_CHAR:
    btif_to_bta_srvc_id(&in_char_id.srvc_id, &p_cb->srvc_id);
    btif_to_bta_gatt_id(&in_char_id.char_id, &p_cb->char_id);

    BTA_GATTC_WriteCharValue(p_cb->conn_id, &in_char_id,
                             p_cb->write_type,
                             p_cb->len,
                             p_cb->value,
                             p_cb->auth_req);
    break;

再来看看BTA_GATTC_WriteCharValue的实现,如下:

void BTA_GATTC_WriteCharValue ( UINT16 conn_id,
                                tBTA_GATTC_CHAR_ID *p_char_id,
                                tBTA_GATTC_WRITE_TYPE  write_type,
                                UINT16 len,
                                UINT8 *p_value,
                                tBTA_GATT_AUTH_REQ auth_req)
{
    tBTA_GATTC_API_WRITE  *p_buf;

    if ((p_buf = (tBTA_GATTC_API_WRITE *) GKI_getbuf((UINT16)(sizeof(tBTA_GATTC_API_WRITE) + len))) != NULL)
    {
        memset(p_buf, 0, sizeof(tBTA_GATTC_API_WRITE) + len);

        p_buf->hdr.event = BTA_GATTC_API_WRITE_EVT;
        p_buf->hdr.layer_specific = conn_id;
        p_buf->auth_req = auth_req;

        memcpy(&p_buf->srvc_id, &p_char_id->srvc_id, sizeof(tBTA_GATT_SRVC_ID));
        memcpy(&p_buf->char_id, &p_char_id->char_id, sizeof(tBTA_GATT_ID));

        p_buf->write_type = write_type;
        p_buf->len = len;

        if (p_value && len > 0)
        {
            p_buf->p_value = (UINT8 *)(p_buf + 1);
            memcpy(p_buf->p_value, p_value, len);
        }

        bta_sys_sendmsg(p_buf);
    }
    return;
}

这里看来真正的写是在btu_task中,这里发送的事件为BTA_GATTC_API_WRITE_EVT。如下:

enum
{
    BTA_GATTC_API_OPEN_EVT   = BTA_SYS_EVT_START(BTA_ID_GATTC),
    BTA_GATTC_INT_OPEN_FAIL_EVT,
    BTA_GATTC_API_CANCEL_OPEN_EVT,
    BTA_GATTC_INT_CANCEL_OPEN_OK_EVT,

    BTA_GATTC_API_READ_EVT,
    BTA_GATTC_API_WRITE_EVT,
    ......
};

可见这些事件都属于BTA_ID_GATTC的子系统,所以在btu_task中的事件处理函数为bta_gattc_main.c中的bta_gattc_hdl_event。奇怪的是在这个函数中没找到这个事件的处理分支,而是走到了默认处理逻辑中,如下:

tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(p_msg->layer_specific);

if (p_clcb != NULL)
{
    rt = bta_gattc_sm_execute(p_clcb, p_msg->event, (tBTA_GATTC_DATA *) p_msg);
}

这里的意思是先通过layer_specific找到p_clcb,再进状态机。这个layer_specific其实就是connection id,是clientIf和address生成的一个连接id。在我们write character之前已经初始化gatt过了,所以这里肯定注册过对应的clcb,我们直接进入状态机好了,这个bta_gattc_sm_execute定义在bta_gattc_main.c中,如下:

BOOLEAN bta_gattc_sm_execute(tBTA_GATTC_CLCB *p_clcb, UINT16 event, tBTA_GATTC_DATA *p_data)
{
    tBTA_GATTC_ST_TBL     state_table;
    UINT8               action;
    int                 i;
    BOOLEAN             rt = TRUE;

    /* look up the state table for the current state */
    state_table = bta_gattc_st_tbl[p_clcb->state];

    event &= 0x00FF;

    /* set next state */
    p_clcb->state = state_table[event][BTA_GATTC_NEXT_STATE];

    /* execute action functions */
    for (i = 0; i < BTA_GATTC_ACTIONS; i++)
    {
        if ((action = state_table[event][i]) != BTA_GATTC_IGNORE)
        {
            (*bta_gattc_action[action])(p_clcb, p_data);
            if (p_clcb->p_q_cmd == p_data) {
                /* buffer is queued, don't free in the bta dispatcher.
                 * we free it ourselves when a completion event is received.
                 */
                rt = FALSE;
            }
        }
        else
        {
            break;
        }
    }
    return rt;
}

这个状态机逻辑是先根据当前状态获取到对应的状态表,再根据发过来的事件获取当前状态下该事件的处理函数,同时将状态切到对应的下一个状态。这个状态机的表bta_gattc_st_tbl如下:

/* state table */
const tBTA_GATTC_ST_TBL bta_gattc_st_tbl[] =
{
    bta_gattc_st_idle,
    bta_gattc_st_w4_conn,
    bta_gattc_st_connected,
    bta_gattc_st_discover
};

对应的状态为:

enum
{
    BTA_GATTC_IDLE_ST = 0,      /* Idle  */
    BTA_GATTC_W4_CONN_ST,       /* Wait for connection -  (optional) */
    BTA_GATTC_CONN_ST,          /* connected state */
    BTA_GATTC_DISCOVER_ST       /* discover is in progress */
};

假如当前状态是已连接,那么对应的状态表为bta_gattc_st_connected,如下:

/* state table for open state */
static const UINT8 bta_gattc_st_connected[][BTA_GATTC_NUM_COLS] =
{
/* Event                            Action 1                            Next state */
/* BTA_GATTC_API_OPEN_EVT           */   {BTA_GATTC_OPEN,               BTA_GATTC_CONN_ST},
/* BTA_GATTC_INT_OPEN_FAIL_EVT      */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_CANCEL_OPEN_EVT    */   {BTA_GATTC_CANCEL_OPEN_ERROR, BTA_GATTC_CONN_ST},
/* BTA_GATTC_INT_CANCEL_OPEN_OK_EVT */   {BTA_GATTC_IGNORE,            BTA_GATTC_CONN_ST},

/* BTA_GATTC_API_READ_EVT           */   {BTA_GATTC_READ,               BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_WRITE_EVT          */   {BTA_GATTC_WRITE,              BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_EXEC_EVT           */   {BTA_GATTC_EXEC,               BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_CFG_MTU_EVT        */   {BTA_GATTC_CFG_MTU,            BTA_GATTC_CONN_ST},

/* BTA_GATTC_API_CLOSE_EVT          */   {BTA_GATTC_CLOSE,              BTA_GATTC_IDLE_ST},

/* BTA_GATTC_API_SEARCH_EVT         */   {BTA_GATTC_SEARCH,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_CONFIRM_EVT        */   {BTA_GATTC_CONFIRM,            BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_READ_MULTI_EVT     */   {BTA_GATTC_READ_MULTI,         BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_REFRESH_EVT        */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},

/* BTA_GATTC_INT_CONN_EVT           */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_INT_DISCOVER_EVT       */   {BTA_GATTC_START_DISCOVER,     BTA_GATTC_DISCOVER_ST},
/* BTA_GATTC_DISCOVER_CMPL_EVT       */  {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_OP_CMPL_EVT            */   {BTA_GATTC_OP_CMPL,            BTA_GATTC_CONN_ST},

/* BTA_GATTC_INT_DISCONN_EVT        */   {BTA_GATTC_CLOSE,              BTA_GATTC_IDLE_ST},

/* ===> for cache loading, saving   */
/* BTA_GATTC_START_CACHE_EVT        */   {BTA_GATTC_CACHE_OPEN,         BTA_GATTC_DISCOVER_ST},
/* BTA_GATTC_CI_CACHE_OPEN_EVT      */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_CI_CACHE_LOAD_EVT      */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_CI_CACHE_SAVE_EVT      */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST}
};

我们的事件是BTA_GATTC_API_WRITE_EVT,取低8位,为5,所以对应的是BTA_GATTC_WRITE,下一个状态为BTA_GATTC_CONN_ST。我们看BTA_GATTC_WRITE对应的函数,到bta_gattc_action中查,在bta_gattc_main.c中:

/* action function list */
const tBTA_GATTC_ACTION bta_gattc_action[] =
{
    bta_gattc_open,
    bta_gattc_open_fail,
    bta_gattc_open_error,
    bta_gattc_cancel_open,
    bta_gattc_cancel_open_ok,
    bta_gattc_cancel_open_error,
    bta_gattc_conn,
    bta_gattc_start_discover,
    bta_gattc_disc_cmpl,

    bta_gattc_q_cmd,
    bta_gattc_close,
    bta_gattc_close_fail,
    bta_gattc_read,
    bta_gattc_write,

    ......
};

为bta_gattc_write函数,在bta_gattc_act.c中,如下:

void bta_gattc_write(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
{
    UINT16              handle = 0;
    tGATT_VALUE         attr = {0};
    tBTA_GATTC_OP_CMPL  op_cmpl;
    tBTA_GATT_STATUS    status = BTA_GATT_OK;

    if (bta_gattc_enqueue(p_clcb, p_data))
    {
        if ((handle = bta_gattc_id2handle(p_clcb->p_srcb,
                                          &p_data->api_write.srvc_id,
                                          &p_data->api_write.char_id,
                                          p_data->api_write.p_descr_type)) == 0)
        {
            status = BTA_GATT_ERROR;
        }
        else
        {
            attr.handle= handle;
            attr.offset = p_data->api_write.offset;
            attr.len    = p_data->api_write.len;
            attr.auth_req = p_data->api_write.auth_req;

            if (p_data->api_write.p_value)
                memcpy(attr.value, p_data->api_write.p_value, p_data->api_write.len);

            status = GATTC_Write(p_clcb->bta_conn_id, p_data->api_write.write_type, &attr);
        }

        /* write fail */
        if (status != BTA_GATT_OK)
        {
            memset(&op_cmpl, 0, sizeof(tBTA_GATTC_OP_CMPL));

            op_cmpl.status  = status;
            op_cmpl.op_code = GATTC_OPTYPE_WRITE;
            op_cmpl.p_cmpl  = NULL;

            bta_gattc_sm_execute(p_clcb, BTA_GATTC_OP_CMPL_EVT, (tBTA_GATTC_DATA *)&op_cmpl);
        }
    }
}

这里首先调用bta_gattc_enqueue将请求放入队列,如果成功就校验要写的character是否有效,如果有效,则调用GATTC_Write真正的写了。

先看这个bta_gattc_enqueue的逻辑,如下:

BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) {
    if (p_clcb->p_q_cmd == NULL) {
        p_clcb->p_q_cmd = p_data;
    } else {
        APPL_TRACE_ERROR("already has a pending command!!");
        /* skip the callback now. ----- need to send callback ? */
    }
    return (p_clcb->p_q_cmd != NULL) ? TRUE : FALSE;
}

这里逻辑很简单,不是什么队列,就是相当于一个单例,一次只能有一个命令在执行。如果之前的结果还没完又来一个命令,则这里直接返回了,连回调都收不到,所以我们所有GATT操作要串行化。

我们看GATTC_Write函数的实现,在gatt_api.c中,如下:

tGATT_STATUS GATTC_Write (UINT16 conn_id, tGATT_WRITE_TYPE type, tGATT_VALUE *p_write)
{
    tGATT_STATUS status = GATT_SUCCESS;
    tGATT_CLCB      *p_clcb;
    tGATT_VALUE     *p;
    tGATT_IF        gatt_if=GATT_GET_GATT_IF(conn_id);
    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);

    if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL )
    {
        p_clcb->operation  = GATTC_OPTYPE_WRITE;
        p_clcb->op_subtype = type;
        p_clcb->auth_req = p_write->auth_req;

        if (( p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf((UINT16)sizeof(tGATT_VALUE))) != NULL)
        {
            memcpy(p_clcb->p_attr_buf, (void *)p_write, sizeof(tGATT_VALUE));

            p =  (tGATT_VALUE *)p_clcb->p_attr_buf;
            if (type == GATT_WRITE_PREPARE)
            {
                p_clcb->start_offset = p_write->offset;
                p->offset = 0;
            }

            if (gatt_security_check_start(p_clcb) == FALSE)
            {
                status = GATT_NO_RESOURCES;
            }
        }
        else
        {
            status = GATT_NO_RESOURCES;
        }

        if (status == GATT_NO_RESOURCES)
            gatt_clcb_dealloc(p_clcb);
    }
    else
    {
        status = GATT_NO_RESOURCES;
    }
    return status;
}

这里出现了一堆错误GATT_NO_RESOURCES,值为128,平时貌似没见过,所以我们这里不关心,继续看gatt_security_check_start,这里主要是做安全检查,检查通过才会真正开始写,在gatt_auth.c中,如下:

BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb)
{
    tGATT_TCB           *p_tcb = p_clcb->p_tcb;
    tGATT_SEC_ACTION    gatt_sec_act;
    tBTM_BLE_SEC_ACT    btm_ble_sec_act;
    BOOLEAN             status = TRUE;
    tBTM_STATUS         btm_status;
    tGATT_SEC_ACTION    sec_act_old =  gatt_get_sec_act(p_tcb);

    gatt_sec_act = gatt_determine_sec_act(p_clcb);

    if (sec_act_old == GATT_SEC_NONE)
        gatt_set_sec_act(p_tcb, gatt_sec_act);

    switch (gatt_sec_act )
    {
        ......
        default:
            gatt_sec_check_complete(TRUE, p_clcb, gatt_sec_act);
            break;
    }

    if (status == FALSE)
    {
        gatt_set_sec_act(p_tcb, GATT_SEC_NONE);
        gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
    }

    return status;
}

由于我们写无需权限验证,所以这里gatt_determine_sec_act返回的就是GATT_SET_OK,所以走到了default分支的gatt_sec_check_complete,如下:

void gatt_sec_check_complete(BOOLEAN sec_check_ok, tGATT_CLCB   *p_clcb, UINT8 sec_act)
{
    if (p_clcb && p_clcb->p_tcb && GKI_queue_is_empty(&p_clcb->p_tcb->pending_enc_clcb))
        gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE);

    if (!sec_check_ok)
    {
        gatt_end_operation(p_clcb, GATT_AUTH_FAIL, NULL);
    }
    else if (p_clcb->operation == GATTC_OPTYPE_WRITE)
    {
        gatt_act_write(p_clcb, sec_act);
    }
    else if (p_clcb->operation == GATTC_OPTYPE_READ)
    {
        gatt_act_read(p_clcb, p_clcb->counter);
    }
}

这里终于准备写了,走到的是gatt_act_write,在gatt_cl.c中,如下:

void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act)
{
    tGATT_TCB           *p_tcb = p_clcb->p_tcb;
    UINT8               rt = GATT_SUCCESS, op_code = 0;
    tGATT_VALUE         *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;

    if (p_attr)
    {
        switch (p_clcb->op_subtype)
        {
            ......

            case GATT_WRITE:
                if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE))
                {
                    p_clcb->s_handle = p_attr->handle;

                    rt = gatt_send_write_msg(p_tcb,
                                             p_clcb->clcb_idx,
                                             GATT_REQ_WRITE,
                                             p_attr->handle,
                                             p_attr->len,
                                             0,
                                             p_attr->value);
                }
                else /* prepare write for long attribute */
                {
                    gatt_send_prepare_write(p_tcb, p_clcb);
                }
                break;

            ......

            default:
                rt = GATT_INTERNAL_ERROR;
                GATT_TRACE_ERROR("Unknown write type: %d", p_clcb->op_subtype);
                break;
        }
    }
    else
        rt = GATT_INTERNAL_ERROR;

    ......
}

这里我们看普通写,如果要写的长度小,就直接走gatt_send_write_msg,否则走gatt_send_prepare_write,其实最后都是调的gatt_send_write_msg,如下:

UINT8 gatt_send_write_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code,
                           UINT16 handle, UINT16 len,
                           UINT16 offset, UINT8 *p_data)
{
    tGATT_CL_MSG     msg;

    msg.attr_value.handle = handle;
    msg.attr_value.len = len;
    msg.attr_value.offset = offset;

    memcpy (msg.attr_value.value, p_data, len);

    /* write by handle */
    return attp_send_cl_msg(p_tcb, clcb_idx, op_code, &msg);
}

这里逻辑很简单,继续往下走,在att_protocol.c中:

tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg)
{
    tGATT_STATUS     status = GATT_NO_RESOURCES;
    BT_HDR          *p_cmd = NULL;
    UINT16          offset = 0, handle;

    if (p_tcb != NULL)
    {
        switch (op_code)
        {
        ......
        case GATT_REQ_PREPARE_WRITE:
            offset = p_msg->attr_value.offset;
            /* fall through */
        case GATT_REQ_WRITE:
        case GATT_CMD_WRITE:
        case GATT_SIGN_CMD_WRITE:
            if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle))
            {
                p_cmd = attp_build_value_cmd (p_tcb->payload_size,
                                              op_code, p_msg->attr_value.handle,
                                              offset,
                                              p_msg->attr_value.len,
                                              p_msg->attr_value.value);
            }
            else
                status = GATT_ILLEGAL_PARAMETER;
            break;
        ......

        default:
            break;
        }

        if (p_cmd != NULL)
            status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd);

    }

    return status;
}

由于我们的op_code是GATT_REQ_WRITE,所以这里先通过attp_build_value_cmd封装好了cmd,然后调用attp_cl_send_cmd发送出去。之所以这么做是因为这个任务也许不能马上执行,所以需要丢到一个队列中。这个cmd就相当于队列中的一个task。我们看这个cmd是怎么丢到队列中的:

tGATT_STATUS attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd)
{
    tGATT_STATUS att_ret = GATT_SUCCESS;

    if (p_tcb != NULL)
    {
        cmd_code &= ~GATT_AUTH_SIGN_MASK;

        /* no pending request or value confirmation */
        if (p_tcb->pending_cl_req == p_tcb->next_slot_inq ||
            cmd_code == GATT_HANDLE_VALUE_CONF)
        {
            att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd);
            if (att_ret == GATT_CONGESTED || att_ret == GATT_SUCCESS)
            {
                /* do not enq cmd if handle value confirmation or set request */
                if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE)
                {
                    gatt_start_rsp_timer (clcb_idx);
                    gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL);
                }
            }
            else
                att_ret = GATT_INTERNAL_ERROR;
        }
        else
        {
            att_ret = GATT_CMD_STARTED;
            gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd);
        }
    }
    else
        att_ret = GATT_ERROR;

    return att_ret;
}

这里的意思是如果当前没有别的待处理的请求的话就直接调用attp_send_msg_to_l2cap给请求丢到L2CAP层处理了,否则加到等待队列中。如果是丢到L2CAP层的请求则还要通过gatt_start_rsp_timer 启动一个定时器来检查回调。

到这里还有两个问题没有讨论到,一个是L2CAP层怎么处理写设备请求的,另一个是GKI层定时器的处理逻辑,这些会留给下文。

作者:dingjikerbo 发表于2016/9/3 18:36:43 原文链接
阅读:99 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>