/*******************************************************************************
**
** 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;

    GATT_TRACE_DEBUG2("gatt_proc_disc_error_rsp reason: %02x cmd_code %04x", reason, opcode);

    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;
                GATT_TRACE_DEBUG0("Discovery completed");
            }
            break;
        default:
            GATT_TRACE_ERROR1("Incorrect discovery opcode %04x",   opcode);
            break;
    }

    gatt_end_operation(p_clcb, status, NULL);
}
/*******************************************************************************
**
** Function         gatt_act_connect
**
** Description      GATT connection initiation.
**
** Returns          void.
**
*******************************************************************************/
BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr)
{
    BOOLEAN     ret = FALSE;
    tGATT_TCB   *p_tcb;
    UINT8       st;

    GATT_TRACE_DEBUG0("gatt_act_connect");

    if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) != NULL)
    {
        ret = TRUE;
        st = gatt_get_ch_state(p_tcb);

        /* before link down, another app try to open a GATT connection */
        if(st == GATT_CH_OPEN &&  gatt_num_apps_hold_link(p_tcb) == 0 &&
            /* only connection on fix channel when the l2cap channel is already open */
            p_tcb->att_lcid == L2CAP_ATT_CID )
        {
            if (!gatt_connect(bd_addr,  p_tcb))
                ret = FALSE;
        }
        else if(st == GATT_CH_CLOSING)
        {
            /* need to complete the closing first */
            ret = FALSE;
        }
    }
    else
    {
        if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL)
        {
            if (!gatt_connect(bd_addr,  p_tcb))
            {
                GATT_TRACE_ERROR0("gatt_connect failed");
                memset(p_tcb, 0, sizeof(tGATT_TCB));
            }
            else
                ret = TRUE;
        }
        else
        {
            ret = 0;
            GATT_TRACE_ERROR1("Max TCB for gatt_if [%d] reached.", p_reg->gatt_if);
        }
    }

    if (ret)
    {
        gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, FALSE);
    }

    return ret;
}
Пример #3
0
/*******************************************************************************
**
** Function         gatt_le_data_ind
**
** Description      This function is called when data is received from L2CAP.
**                  if we are the originator of the connection, we are the ATT
**                  client, and the received message is queued up for the client.
**
**                  If we are the destination of the connection, we are the ATT
**                  server, so the message is passed to the server processing
**                  function.
**
** Returns          void
**
*******************************************************************************/
void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf)
{
    GATT_TRACE_DEBUG0("gatt_data_process");
    UINT8   *p = (UINT8 *)(p_buf + 1) + p_buf->offset;
    UINT8   op_code, pseudo_op_code;
    UINT16  msg_len;


    if (p_buf->len > 0)
    {
        msg_len = p_buf->len - 1;
        STREAM_TO_UINT8(op_code, p);
        GATT_TRACE_DEBUG1("op_code = %d", op_code);

        /* remove the two MSBs associated with sign write and write cmd */
        pseudo_op_code = op_code & (~GATT_WRITE_CMD_MASK);

        if (pseudo_op_code < GATT_OP_CODE_MAX)
        {
            if (op_code == GATT_SIGN_CMD_WRITE)
            {
                GATT_TRACE_DEBUG0("op_code == GATT_SIGN_CMD_WRITE");
                gatt_verify_signature(p_tcb, p_buf);
                return;
            }
            else
            {
                /* message from client */
                if ((op_code % 2) == 0)
                {
                    GATT_TRACE_DEBUG0("gatt_server_handle_client_req");
                    gatt_server_handle_client_req (p_tcb, op_code, msg_len, p);
                }
                else
                {
                    GATT_TRACE_DEBUG0("gatt_client_handle_server_rsp");
                    gatt_client_handle_server_rsp (p_tcb, op_code, msg_len, p);
                }
            }
        }
        else
        {
            GATT_TRACE_ERROR1 ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x", op_code);
        }
    }
    else
    {
        GATT_TRACE_ERROR0 ("invalid data length, ignore");
    }

    GKI_freebuf (p_buf);
}
Пример #4
0
/*******************************************************************************
**
** Function         gatt_l2cif_connect_ind
**
** Description      This function handles an inbound connection indication
**                  from L2CAP. This is the case where we are acting as a
**                  server.
**
** Returns          void
**
*******************************************************************************/
static void gatt_l2cif_connect_ind_cback (BD_ADDR  bd_addr, UINT16 lcid, UINT16 psm, UINT8 id)
{
    /* do we already have a control channel for this peer? */
    UINT8       result = L2CAP_CONN_OK;
    tL2CAP_CFG_INFO cfg;
    tGATT_TCB       *p_tcb = gatt_find_tcb_by_addr(bd_addr);

    GATT_TRACE_ERROR1("Connection indication cid = %d", lcid);
    /* new connection ? */
    if (p_tcb == NULL)
    {
        /* allocate tcb */
        if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) == NULL)
        {
            /* no tcb available, reject L2CAP connection */
            result = L2CAP_CONN_NO_RESOURCES;
        }
        else
            p_tcb->att_lcid = lcid;

    }
    else /* existing connection , reject it */
    {
        result = L2CAP_CONN_NO_RESOURCES;
    }

    /* Send L2CAP connect rsp */
    L2CA_ConnectRsp(bd_addr, id, lcid, result, 0);

    /* if result ok, proceed with connection */
    if (result == L2CAP_CONN_OK)
    {
        /* transition to configuration state */
        gatt_set_ch_state(p_tcb, GATT_CH_CFG);

        /* Send L2CAP config req */
        memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
        cfg.mtu_present = TRUE;
        cfg.mtu = GATT_MAX_MTU_SIZE;

        L2CA_ConfigReq(lcid, &cfg);
    }
}
Пример #5
0
/*******************************************************************************
**
** Function         gatt_act_connect
**
** Description      GATT connection initiation.
**
** Returns          void.
**
*******************************************************************************/
BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr)
{
    BOOLEAN     ret = FALSE;
    tGATT_TCB   *p_tcb;

    GATT_TRACE_DEBUG0("gatt_act_connect");

    if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) != NULL)
    {
        ret = TRUE;
        if(gatt_get_ch_state(p_tcb) == GATT_CH_CLOSING )
        {
            /* need to complete the closing first */
            ret = FALSE;
        }
    }
    else
    {
        if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL)
        {
            if (!gatt_connect(bd_addr,  p_tcb))
            {
                GATT_TRACE_ERROR0("gatt_connect failed");
                memset(p_tcb, 0, sizeof(tGATT_TCB));
            }
            else
                ret = TRUE;
        }
        else
        {
            ret = 0;
            GATT_TRACE_ERROR1("Max TCB for gatt_if [%d] reached.", p_reg->gatt_if);
        }
    }

    if (ret)
    {
        gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, FALSE);
    }

    return ret;
}
/*******************************************************************************
**
** Function         gatt_process_read_by_type_rsp
**
** Description      This function is called to handle the read by type response.
**                  read by type can be used for discovery, or read by type or
**                  read characteristic value.
**
** Returns          void
**
*******************************************************************************/
void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
                                    UINT16 len, UINT8 *p_data)
{
    tGATT_DISC_RES      result;
    tGATT_DISC_VALUE    record_value;
    UINT8               *p = p_data, value_len, handle_len = 2;
    UINT16              handle = 0;

    /* discovery procedure and no callback function registered */
    if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && (p_clcb->operation == GATTC_OPTYPE_DISCOVERY))
        return;

    STREAM_TO_UINT8(value_len, p);

    if ((value_len > (p_tcb->payload_size - 2)) || (value_len > (len-1))  )
    {
        /* this is an error case that server's response containing a value length which is larger than MTU-2
           or value_len > message total length -1 */
        GATT_TRACE_ERROR4("gatt_process_read_by_type_rsp: Discard response op_code=%d vale_len=%d > (MTU-2=%d or msg_len-1=%d)",
                          op_code, value_len, (p_tcb->payload_size - 2), (len-1));
        gatt_end_operation(p_clcb, GATT_ERROR, NULL);
        return;
    }

    if (op_code == GATT_RSP_READ_BY_GRP_TYPE)
        handle_len = 4;

    value_len -= handle_len; /* substract the handle pairs bytes */
    len -= 1;

    while (len >= (handle_len + value_len))
    {
        STREAM_TO_UINT16(handle, p);

        if (!GATT_HANDLE_IS_VALID(handle))
        {
            gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
            return;
        }

        memset(&result, 0, sizeof(tGATT_DISC_RES));
        memset(&record_value, 0, sizeof(tGATT_DISC_VALUE));

        result.handle = handle;
        result.type.len = 2;
        result.type.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype];

        /* discover all services */
        if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY &&
            p_clcb->op_subtype == GATT_DISC_SRVC_ALL &&
            op_code == GATT_RSP_READ_BY_GRP_TYPE)
        {
            STREAM_TO_UINT16(handle, p);

            if (!GATT_HANDLE_IS_VALID(handle))
            {
                gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
                return;
            }
            else
            {
                record_value.group_value.e_handle = handle;
                if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type, value_len, &p))
                {
                    GATT_TRACE_ERROR0("discover all service response parsing failure");
                    break;
                }
            }
        }
        /* discover included service */
        else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC)
        {
            STREAM_TO_UINT16(record_value.incl_service.s_handle, p);
            STREAM_TO_UINT16(record_value.incl_service.e_handle, p);

            if (!GATT_HANDLE_IS_VALID(record_value.incl_service.s_handle) ||
                !GATT_HANDLE_IS_VALID(record_value.incl_service.e_handle))
            {
                gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
                return;
            }

            if(value_len == 6)
            {
                STREAM_TO_UINT16(record_value.incl_service.service_type.uu.uuid16, p);
                record_value.incl_service.service_type.len = LEN_UUID_16;
            }
            else if (value_len == 4)
            {
                p_clcb->s_handle = record_value.incl_service.s_handle;
                p_clcb->read_uuid128.wait_for_read_rsp = TRUE;
                p_clcb->read_uuid128.next_disc_start_hdl = handle + 1;
                memcpy(&p_clcb->read_uuid128.result, &result, sizeof(result));
                memcpy(&p_clcb->read_uuid128.result.value, &record_value, sizeof (result.value));
                p_clcb->op_subtype |= 0x90;
                gatt_act_read(p_clcb, 0);
                return;
            }
            else
            {
               GATT_TRACE_ERROR1("gatt_process_read_by_type_rsp INCL_SRVC failed with invalid data value_len=%d", value_len);
               gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p);
               return;
            }
        }
        /* read by type */
        else if (p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE)
        {
            p_clcb->counter = len - 2;
            p_clcb->s_handle = handle;
            if ( p_clcb->counter == (p_clcb->p_tcb->payload_size -4))
            {
                p_clcb->op_subtype = GATT_READ_BY_HANDLE;
                if (!p_clcb->p_attr_buf)
                    p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf(GATT_MAX_ATTR_LEN);
                if (p_clcb->p_attr_buf && p_clcb->counter <= GATT_MAX_ATTR_LEN)
                {
                    memcpy(p_clcb->p_attr_buf, p, p_clcb->counter);
                    gatt_act_read(p_clcb, p_clcb->counter);
                }
                else
                   gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, (void *)p);
            }
            else
            {
                 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p);
            }
            return;
        }
        else /* discover characterisitic or read characteristic value */
        {
            STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p);
            STREAM_TO_UINT16(record_value.dclr_value.val_handle, p);
            if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle))
            {
                gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
                return;
            }
            if (!gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid, (UINT16)(value_len - 3), &p))
            {
                gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
                /* invalid format, and skip the result */
                return;
            }

            /* UUID not matching */
            if (!gatt_uuid_compare(record_value.dclr_value.char_uuid, p_clcb->uuid))
            {
                len -= (value_len + 2);
                continue; /* skip the result, and look for next one */
            }
            else if (p_clcb->operation == GATTC_OPTYPE_READ)
            /* UUID match for read characteristic value */
            {
                /* only read the first matching UUID characteristic value, and
                  discard the rest results */
                p_clcb->s_handle = record_value.dclr_value.val_handle;
                p_clcb->op_subtype |= 0x80;
                gatt_act_read(p_clcb, 0);
                return;
            }
        }
        len -= (value_len + handle_len);

        /* result is (handle, 16bits UUID) pairs */
        memcpy (&result.value, &record_value, sizeof (result.value));

        /* send callback if is discover procedure */
        if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && 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);
    }

    p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1);

    if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)
    {
        /* initiate another request */
        gatt_act_discovery(p_clcb) ;
    }
    else /* read characteristic value */
    {
        gatt_act_read(p_clcb, 0);
    }
}
/*******************************************************************************
**
** Function         gatt_process_notification
**
** Description      This function is called to handle the handle value indication
**                  or handle value notification.
**
**
** Returns          void
**
*******************************************************************************/
void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
                               UINT16 len, UINT8 *p_data)
{
    tGATT_VALUE     value = {0};
    tGATT_REG       *p_reg;
    UINT16          conn_id;
    tGATT_STATUS    encrypt_status;
    UINT8           *p= p_data, i,
    event = (op_code == GATT_HANDLE_VALUE_NOTIF) ? GATTC_OPTYPE_NOTIFICATION : GATTC_OPTYPE_INDICATION;

    GATT_TRACE_DEBUG0("gatt_process_notification ");

    STREAM_TO_UINT16 (value.handle, p);
    value.len = len - 2;
    memcpy (value.value, p, value.len);

    if (!GATT_HANDLE_IS_VALID(value.handle))
    {
        /* illegal handle, send ack now */
        if (op_code == GATT_HANDLE_VALUE_IND)
            attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL);
        return;
    }

    if (event == GATTC_OPTYPE_INDICATION)
    {
        if (p_tcb->ind_count)
        {
            /* this is an error case that receiving an indication but we
               still has an indication not being acked yet.
               For now, just log the error reset the counter.
               Later we need to disconnect the link unconditionally.
            */
            GATT_TRACE_ERROR1("gatt_process_notification rcv Ind. but ind_count=%d (will reset ind_count)",  p_tcb->ind_count);
        }
        p_tcb->ind_count = 0;
    }

    /* should notify all registered client with the handle value notificaion/indication
       Note: need to do the indication count and start timer first then do callback
     */

    for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++)
    {
        if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb && (event == GATTC_OPTYPE_INDICATION))
            p_tcb->ind_count++;
    }

    if (event == GATTC_OPTYPE_INDICATION)
    {
        /* start a timer for app confirmation */
        if (p_tcb->ind_count > 0)
            gatt_start_ind_ack_timer(p_tcb);
        else /* no app to indicate, or invalid handle */
            attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL);
    }

    encrypt_status = gatt_get_link_encrypt_status(p_tcb);
    for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++)
    {
        if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb)
        {
            conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if);
            (*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value);
        }
    }

}
/*******************************************************************************
**
** Function         gatt_act_write
**
** Description      GATT write operation.
**
** Returns          void.
**
*******************************************************************************/
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;
    tGATT_VALUE         *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;

    if (p_attr)
    {
        switch (p_clcb->op_subtype)
        {
            case GATT_WRITE_NO_RSP:
                p_clcb->s_handle = p_attr->handle;
                op_code = (sec_act == GATT_SEC_SIGN_DATA) ? GATT_SIGN_CMD_WRITE : GATT_CMD_WRITE;
                rt = gatt_send_write_msg(p_tcb,
                                         p_clcb->clcb_idx,
                                         op_code,
                                         p_attr->handle,
                                         p_attr->len,
                                         0,
                                         p_attr->value);
                break;

            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;

            case GATT_WRITE_PREPARE:
                gatt_send_prepare_write(p_tcb, p_clcb);
                break;

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

    if ((rt != GATT_SUCCESS  && rt != GATT_CMD_STARTED)
        || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP))
    {
        if (rt != GATT_SUCCESS)
        {
            GATT_TRACE_ERROR1("gatt_act_write() failed op_code=0x%x", op_code);
        }
        gatt_end_operation(p_clcb, rt, NULL);
    }
}
/*******************************************************************************
**
** Function         gatt_act_read
**
** Description      GATT read operation.
**
** Returns          void.
**
*******************************************************************************/
void gatt_act_read (tGATT_CLCB *p_clcb, UINT16 offset)
{
    tGATT_TCB  *p_tcb = p_clcb->p_tcb;
    UINT8   rt = GATT_INTERNAL_ERROR;
    tGATT_CL_MSG  msg;
    UINT8        op_code = 0;

    memset (&msg, 0, sizeof(tGATT_CL_MSG));

    switch (p_clcb->op_subtype)
    {
        case GATT_READ_CHAR_VALUE:
        case GATT_READ_BY_TYPE:
            op_code = GATT_REQ_READ_BY_TYPE;
            msg.browse.s_handle = p_clcb->s_handle;
            msg.browse.e_handle = p_clcb->e_handle;
            if (p_clcb->op_subtype == GATT_READ_BY_TYPE)
                memcpy(&msg.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID));
            else
            {
                msg.browse.uuid.len = LEN_UUID_16;
                msg.browse.uuid.uu.uuid16 = GATT_UUID_CHAR_DECLARE;
            }
            break;

        case GATT_READ_CHAR_VALUE_HDL:
        case GATT_READ_BY_HANDLE:
            if (!p_clcb->counter)
            {
                op_code = GATT_REQ_READ;
                msg.handle = p_clcb->s_handle;
            }
            else
            {
                if (!p_clcb->first_read_blob_after_read)
                    p_clcb->first_read_blob_after_read = TRUE;
                else
                    p_clcb->first_read_blob_after_read = FALSE;

                GATT_TRACE_DEBUG1("gatt_act_read first_read_blob_after_read=%d",
                                  p_clcb->first_read_blob_after_read);
                op_code = GATT_REQ_READ_BLOB;
                msg.read_blob.offset = offset;
                msg.read_blob.handle = p_clcb->s_handle;
            }
            p_clcb->op_subtype &= ~ 0x80;
            break;

        case GATT_READ_PARTIAL:
            op_code = GATT_REQ_READ_BLOB;
            msg.read_blob.handle = p_clcb->s_handle;
            msg.read_blob.offset = offset;
            break;

        case GATT_READ_MULTIPLE:
            op_code = GATT_REQ_READ_MULTI;
            memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
            break;

        case GATT_READ_INC_SRV_UUID128:
            op_code = GATT_REQ_READ;
            msg.handle = p_clcb->s_handle;
            p_clcb->op_subtype &= ~ 0x90;
            break;

        default:
            GATT_TRACE_ERROR1("Unknown read type: %d", p_clcb->op_subtype);
            break;
    }

    if ( op_code == 0 ||
         (rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, op_code, &msg)) != GATT_SUCCESS)
    {
        gatt_end_operation(p_clcb, rt, NULL);
    }
}