Exemplo n.º 1
0
/*******************************************************************************
**
** Function         avrc_pars_vendor_rsp
**
** Description      This function parses the vendor specific commands defined by
**                  Bluetooth SIG
**
** Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully.
**                  Otherwise, the error code defined by AVRCP 1.4
**
*******************************************************************************/
static tAVRC_STS avrc_pars_vendor_rsp(tAVRC_MSG_VENDOR *p_msg, tAVRC_RESPONSE *p_result)
{
    tAVRC_STS  status = AVRC_STS_NO_ERROR;
    UINT8   *p;
    UINT16  len;
    UINT8 eventid = 0;

    /* Check the vendor data */
    if (p_msg->vendor_len == 0) {
        return AVRC_STS_NO_ERROR;
    }
    if (p_msg->p_vendor_data == NULL) {
        return AVRC_STS_INTERNAL_ERR;
    }

    p = p_msg->p_vendor_data;
    BE_STREAM_TO_UINT8 (p_result->pdu, p);
    p++; /* skip the reserved/packe_type byte */
    BE_STREAM_TO_UINT16 (len, p);
    AVRC_TRACE_DEBUG("avrc_pars_vendor_rsp() ctype:0x%x pdu:0x%x, len:%d/0x%x", p_msg->hdr.ctype, p_result->pdu, len, len);
    if (p_msg->hdr.ctype == AVRC_RSP_REJ) {
        p_result->rsp.status = *p;
        return p_result->rsp.status;
    }

    switch (p_result->pdu) {
        /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */
        /* case AVRC_PDU_ABORT_CONTINUATION_RSP:   0x41 */

#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
    case AVRC_PDU_SET_ABSOLUTE_VOLUME:      /* 0x50 */
        if (len != 1) {
            status = AVRC_STS_INTERNAL_ERR;
        } else {
            BE_STREAM_TO_UINT8 (p_result->volume.volume, p);
        }
        break;
#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */

    case AVRC_PDU_REGISTER_NOTIFICATION:    /* 0x31 */
#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
        BE_STREAM_TO_UINT8 (eventid, p);
        if (AVRC_EVT_VOLUME_CHANGE == eventid
                && (AVRC_RSP_CHANGED == p_msg->hdr.ctype || AVRC_RSP_INTERIM == p_msg->hdr.ctype
                    || AVRC_RSP_REJ == p_msg->hdr.ctype || AVRC_RSP_NOT_IMPL == p_msg->hdr.ctype)) {
            p_result->reg_notif.status = p_msg->hdr.ctype;
            p_result->reg_notif.event_id = eventid;
            BE_STREAM_TO_UINT8 (p_result->reg_notif.param.volume, p);
        }
        AVRC_TRACE_DEBUG("avrc_pars_vendor_rsp PDU reg notif response:event %x, volume %x", eventid,
                         p_result->reg_notif.param.volume);
#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */
        break;
    default:
        status = AVRC_STS_BAD_CMD;
        break;
    }

    return status;
}
Exemplo n.º 2
0
/*******************************************************************************
**
** Function         llcp_util_parse_cc
**
** Description      Parse CC PDU
**
** Returns          tLLCP_STATUS
**
*******************************************************************************/
tLLCP_STATUS llcp_util_parse_cc (UINT8 *p_bytes, UINT16 length, UINT16 *p_miu, UINT8 *p_rw)
{
    UINT8 param_type, param_len, *p = p_bytes;

    *p_miu = LLCP_DEFAULT_MIU;
    *p_rw  = LLCP_DEFAULT_RW;

    while (length)
    {
        BE_STREAM_TO_UINT8 (param_type, p);
        length--;

        switch (param_type)
        {
        case LLCP_MIUX_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT16 ((*p_miu), p);
            (*p_miu) &= LLCP_MIUX_MASK;
            (*p_miu) += LLCP_DEFAULT_MIU;

            LLCP_TRACE_DEBUG1 ("llcp_util_parse_cc (): LLCP_MIUX_TYPE:%d", *p_miu);
            break;

        case LLCP_RW_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT8 ((*p_rw), p);
            (*p_rw) &= 0x0F;

            LLCP_TRACE_DEBUG1 ("llcp_util_parse_cc (): LLCP_RW_TYPE:%d", *p_rw);
            break;

        default:
            LLCP_TRACE_ERROR1 ("llcp_util_parse_cc (): Unexpected type 0x%x", param_type);
            BE_STREAM_TO_UINT8 (param_len, p);
            p += param_len;
            break;
        }

        if (length >= param_len + 1)
            length -= param_len + 1;
        else
        {
            LLCP_TRACE_ERROR0 ("llcp_util_parse_cc (): Bad LTV's");
            return LLCP_STATUS_FAIL;
        }
    }
    return LLCP_STATUS_SUCCESS;
}
Exemplo n.º 3
0
/*******************************************************************************
**
** Function         ce_t4t_process_select_file_cmd
**
** Description      This function processes Select Command by file ID.
**
** Returns          TRUE if success
**
*******************************************************************************/
static BOOLEAN ce_t4t_process_select_file_cmd (UINT8 *p_cmd)
{
    UINT8  data_len;
    UINT16 file_id, status_words;

    CE_TRACE_DEBUG0 ("ce_t4t_process_select_file_cmd ()");

    p_cmd++; /* skip P2 */

    /* Lc Byte */
    BE_STREAM_TO_UINT8 (data_len, p_cmd);

    if (data_len == T4T_FILE_ID_SIZE)
    {
        /* File ID */
        BE_STREAM_TO_UINT16 (file_id, p_cmd);

        if (ce_t4t_select_file (file_id))
        {
            status_words = T4T_RSP_CMD_CMPLTED;
        }
        else
        {
            status_words = T4T_RSP_NOT_FOUND;
        }
    }
    else
    {
        status_words = T4T_RSP_WRONG_LENGTH;
    }

    if (!ce_t4t_send_status (status_words))
    {
        return FALSE;
    }

    if (status_words == T4T_RSP_CMD_CMPLTED)
    {
        return TRUE;
    }
    return FALSE;
}
Exemplo n.º 4
0
/*******************************************************************************
**
** Function         llcp_sdp_proc_snl
**
** Description      Process SDREQ and SDRES in SNL
**
**
** Returns          LLCP_STATUS
**
*******************************************************************************/
tLLCP_STATUS llcp_sdp_proc_snl (UINT16 sdu_length, UINT8 *p)
{
    UINT8  type, length, tid, sap, *p_value;

    LLCP_TRACE_DEBUG0 ("llcp_sdp_proc_snl ()");

    if ((llcp_cb.lcb.agreed_major_version < LLCP_MIN_SNL_MAJOR_VERSION)||
       ((llcp_cb.lcb.agreed_major_version == LLCP_MIN_SNL_MAJOR_VERSION)&&(llcp_cb.lcb.agreed_minor_version < LLCP_MIN_SNL_MINOR_VERSION)))
    {
        LLCP_TRACE_DEBUG0 ("llcp_sdp_proc_snl(): version number less than 1.1, SNL not supported.");
        return LLCP_STATUS_FAIL;
    }
    while (sdu_length >= 2) /* at least type and length */
    {
        BE_STREAM_TO_UINT8 (type, p);
        BE_STREAM_TO_UINT8 (length, p);

        switch (type)
        {
        case LLCP_SDREQ_TYPE:
            if (  (length > 1)                /* TID and sevice name */
                &&(sdu_length >= 2 + length)  ) /* type, length, TID and service name */
            {
                p_value = p;
                BE_STREAM_TO_UINT8 (tid, p_value);
                sap = llcp_sdp_get_sap_by_name ((char*) p_value, (UINT8) (length - 1));
                llcp_sdp_send_sdres (tid, sap);
            }
            else
            {
                LLCP_TRACE_ERROR1 ("llcp_sdp_proc_snl (): bad length (%d) in LLCP_SDREQ_TYPE", length);
            }
            break;

        case LLCP_SDRES_TYPE:
            if (  (length == LLCP_SDRES_LEN)  /* TID and SAP */
                &&(sdu_length >= 2 + length)  ) /* type, length, TID and SAP */
            {
                p_value = p;
                BE_STREAM_TO_UINT8 (tid, p_value);
                BE_STREAM_TO_UINT8 (sap, p_value);
                llcp_sdp_return_sap (tid, sap);
            }
            else
            {
                LLCP_TRACE_ERROR1 ("llcp_sdp_proc_snl (): bad length (%d) in LLCP_SDRES_TYPE", length);
            }
            break;

        default:
            LLCP_TRACE_WARNING1 ("llcp_sdp_proc_snl (): Unknown type (0x%x) is ignored", type);
            break;
        }

        if (sdu_length >= 2 + length)   /* type, length, value */
        {
            sdu_length -= 2 + length;
            p += length;
        }
        else
        {
            break;
        }
    }

    if (sdu_length)
    {
        LLCP_TRACE_ERROR0 ("llcp_sdp_proc_snl (): Bad format of SNL");
        return LLCP_STATUS_FAIL;
    }
    else
    {
        return LLCP_STATUS_SUCCESS;
    }
}
/*******************************************************************************
**
** Function         avrc_pars_vendor_rsp
**
** Description      This function parses the vendor specific commands defined by
**                  Bluetooth SIG
**
** Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully.
**                  Otherwise, the error code defined by AVRCP 1.4
**
*******************************************************************************/
static tAVRC_STS avrc_pars_vendor_rsp(tAVRC_MSG_VENDOR *p_msg, tAVRC_RESPONSE *p_result)
{
    tAVRC_STS  status = AVRC_STS_NO_ERROR;
    UINT8   *p = p_msg->p_vendor_data;
    UINT16  len;
    UINT8   xx, yy;
    tAVRC_NOTIF_RSP_PARAM   *p_param;
    tAVRC_APP_SETTING       *p_app_set;
    tAVRC_APP_SETTING_TEXT  *p_app_txt;
    tAVRC_ATTR_ENTRY        *p_entry;
    UINT32  *p_u32;
    UINT8   *p_u8;
    UINT16  size_needed;
    UINT8 eventid=0;

    BE_STREAM_TO_UINT8 (p_result->pdu, p);
    p++; /* skip the reserved/packe_type byte */
    BE_STREAM_TO_UINT16 (len, p);
    AVRC_TRACE_DEBUG("avrc_pars_vendor_rsp() ctype:0x%x pdu:0x%x, len:%d/0x%x", p_msg->hdr.ctype, p_result->pdu, len, len);
    if (p_msg->hdr.ctype == AVRC_RSP_REJ)
    {
        p_result->rsp.status = *p;
        return p_result->rsp.status;
    }

    switch (p_result->pdu)
    {
    /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */
    /* case AVRC_PDU_ABORT_CONTINUATION_RSP:   0x41 */

#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
    case AVRC_PDU_SET_ABSOLUTE_VOLUME:      /* 0x50 */
        if (len != 1)
            status = AVRC_STS_INTERNAL_ERR;
        else
        {
            BE_STREAM_TO_UINT8 (p_result->volume.volume, p);
        }
        break;

    case AVRC_PDU_GET_ELEMENT_ATTR:      /* 0x20 */
        BE_STREAM_TO_UINT8 (p_result->get_elem_attrs.num_attr, p);

        AVRC_TRACE_API("num_attr: %d", p_result->get_elem_attrs.num_attr);

        if (len == 0)
            status = AVRC_STS_INTERNAL_ERR;
        else
        {
            status = avrc_prs_get_elem_attrs_rsp(&p_result->get_elem_attrs, p);
        }
        break;
#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */

    case AVRC_PDU_REGISTER_NOTIFICATION:    /* 0x31 */
#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
        BE_STREAM_TO_UINT8 (eventid, p);
        if(AVRC_EVT_VOLUME_CHANGE==eventid
            && (AVRC_RSP_CHANGED==p_msg->hdr.ctype || AVRC_RSP_INTERIM==p_msg->hdr.ctype
            || AVRC_RSP_REJ==p_msg->hdr.ctype || AVRC_RSP_NOT_IMPL==p_msg->hdr.ctype))
        {
            p_result->reg_notif.status=p_msg->hdr.ctype;
            p_result->reg_notif.event_id=eventid;
            BE_STREAM_TO_UINT8 (p_result->reg_notif.param.volume, p);
        }
        AVRC_TRACE_DEBUG("avrc_pars_vendor_rsp PDU reg notif response:event %x, volume %x",eventid,
            p_result->reg_notif.param.volume);
#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */
        break;
    default:
        status = AVRC_STS_BAD_CMD;
        break;
    }

    return status;
}
Exemplo n.º 6
0
/*******************************************************************************
**
** Function         ce_t4t_data_cback
**
** Description      This callback function receives the data from NFCC.
**
** Returns          none
**
*******************************************************************************/
static void ce_t4t_data_cback (UINT8 conn_id, tNFC_CONN_EVT event, tNFC_CONN *p_data)
{
    BT_HDR  *p_c_apdu;
    UINT8   *p_cmd;
    UINT8    cla, instruct, select_type = 0, length;
    UINT16   offset, max_file_size;
    tCE_DATA ce_data;

    if (event == NFC_DEACTIVATE_CEVT)
    {
        NFC_SetStaticRfCback (NULL);
        return;
    }
    if (event != NFC_DATA_CEVT)
    {
        return;
    }

    p_c_apdu = (BT_HDR *) p_data->data.p_data;

#if (BT_TRACE_PROTOCOL == TRUE)
    DispCET4Tags (p_c_apdu, TRUE);
#endif

    CE_TRACE_DEBUG1 ("ce_t4t_data_cback (): conn_id = 0x%02X", conn_id);

    p_cmd = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;

    /* Class Byte */
    BE_STREAM_TO_UINT8 (cla, p_cmd);

    /* Don't check class if registered AID has been selected */
    if (  (cla != T4T_CMD_CLASS)
        &&((ce_cb.mem.t4t.status & CE_T4T_STATUS_REG_AID_SELECTED) == 0)
        &&((ce_cb.mem.t4t.status & CE_T4T_STATUS_WILDCARD_AID_SELECTED) == 0)  )
    {
        GKI_freebuf (p_c_apdu);
        ce_t4t_send_status (T4T_RSP_CLASS_NOT_SUPPORTED);
        return;
    }

    /* Instruction Byte */
    BE_STREAM_TO_UINT8 (instruct, p_cmd);

    if ((cla == T4T_CMD_CLASS) && (instruct == T4T_CMD_INS_SELECT))
    {
        /* P1 Byte */
        BE_STREAM_TO_UINT8 (select_type, p_cmd);

        if (select_type == T4T_CMD_P1_SELECT_BY_NAME)
        {
            ce_t4t_process_select_app_cmd (p_cmd, p_c_apdu);
            return;
        }
    }

    /* if registered AID is selected */
    if (ce_cb.mem.t4t.status & CE_T4T_STATUS_REG_AID_SELECTED)
    {
        CE_TRACE_DEBUG0 ("CET4T: Forward raw frame to registered AID");

        /* forward raw frame to upper layer */
        if (ce_cb.mem.t4t.selected_aid_idx < CE_T4T_MAX_REG_AID)
        {
            ce_data.raw_frame.status = p_data->data.status;
            ce_data.raw_frame.p_data = p_c_apdu;
            ce_data.raw_frame.aid_handle = ce_cb.mem.t4t.selected_aid_idx;
            p_c_apdu = NULL;

            (*(ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].p_cback)) (CE_T4T_RAW_FRAME_EVT, &ce_data);
        }
        else
        {
            GKI_freebuf (p_c_apdu);
            ce_t4t_send_status (T4T_RSP_NOT_FOUND);
        }
    }
    else if (ce_cb.mem.t4t.status & CE_T4T_STATUS_WILDCARD_AID_SELECTED)
    {
        CE_TRACE_DEBUG0 ("CET4T: Forward raw frame to wildcard AID handler");

        /* forward raw frame to upper layer */
        ce_data.raw_frame.status = p_data->data.status;
        ce_data.raw_frame.p_data = p_c_apdu;
        ce_data.raw_frame.aid_handle = CE_T4T_WILDCARD_AID_HANDLE;
        p_c_apdu = NULL;

        (*(ce_cb.mem.t4t.p_wildcard_aid_cback)) (CE_T4T_RAW_FRAME_EVT, &ce_data);
    }
    else if (ce_cb.mem.t4t.status & CE_T4T_STATUS_T4T_APP_SELECTED)
    {
        if (instruct == T4T_CMD_INS_SELECT)
        {
            /* P1 Byte is already parsed */
            if (select_type == T4T_CMD_P1_SELECT_BY_FILE_ID)
            {
                ce_t4t_process_select_file_cmd (p_cmd);
            }
            else
            {
                CE_TRACE_ERROR1 ("CET4T: Bad P1 byte (0x%02X)", select_type);
                ce_t4t_send_status (T4T_RSP_WRONG_PARAMS);
            }
        }
        else if (instruct == T4T_CMD_INS_READ_BINARY)
        {
            if (  (ce_cb.mem.t4t.status & CE_T4T_STATUS_CC_FILE_SELECTED)
                ||(ce_cb.mem.t4t.status & CE_T4T_STATUS_NDEF_SELECTED)  )
            {
                if (ce_cb.mem.t4t.status & CE_T4T_STATUS_CC_FILE_SELECTED)
                {
                    max_file_size = T4T_FC_TLV_OFFSET_IN_CC + T4T_FILE_CONTROL_TLV_SIZE;
                }
                else
                {
                    max_file_size = ce_cb.mem.t4t.max_file_size;
                }

                BE_STREAM_TO_UINT16 (offset, p_cmd); /* Offset */
                BE_STREAM_TO_UINT8 (length, p_cmd); /* Le     */

                /* check if valid parameters */
                if (length <= CE_T4T_MAX_LE)
                {
                    /* CE allows to read more than current file size but not max file size */
                    if (length + offset > max_file_size)
                    {
                        if (offset < max_file_size)
                        {
                            length = (UINT8) (max_file_size - offset);

                            CE_TRACE_DEBUG2 ("CET4T: length is reduced to %d by max_file_size (%d)",
                                              length, max_file_size);
                        }
                        else
                        {
                            CE_TRACE_ERROR2 ("CET4T: offset (%d) must be less than max_file_size (%d)",
                                              offset, max_file_size);
                            length = 0;
                        }
                    }
                }
                else
                {
                    CE_TRACE_ERROR2 ("CET4T: length (%d) must be less than MLe (%d)",
                                      length, CE_T4T_MAX_LE);
                    length = 0;
                }

                if (length > 0)
                    ce_t4t_read_binary (offset, length);
                else
                    ce_t4t_send_status (T4T_RSP_WRONG_PARAMS);
            }
            else
            {
                CE_TRACE_ERROR0 ("CET4T: File has not been selected");
                ce_t4t_send_status (T4T_RSP_CMD_NOT_ALLOWED);
            }
        }
        else if (instruct == T4T_CMD_INS_UPDATE_BINARY)
        {
            if (ce_cb.mem.t4t.status & CE_T4T_STATUS_NDEF_FILE_READ_ONLY)
            {
                CE_TRACE_ERROR0 ("CET4T: No access right");
                ce_t4t_send_status (T4T_RSP_CMD_NOT_ALLOWED);
            }
            else if (ce_cb.mem.t4t.status & CE_T4T_STATUS_NDEF_SELECTED)
            {
                BE_STREAM_TO_UINT16 (offset, p_cmd); /* Offset */
                BE_STREAM_TO_UINT8 (length, p_cmd); /* Lc     */

                /* check if valid parameters */
                if (length <= CE_T4T_MAX_LC)
                {
                    if (length + offset > ce_cb.mem.t4t.max_file_size)
                    {
                        CE_TRACE_ERROR3 ("CET4T: length (%d) + offset (%d) must be less than max_file_size (%d)",
                                          length, offset, ce_cb.mem.t4t.max_file_size);
                        length = 0;
                    }
                }
                else
                {
                    CE_TRACE_ERROR2 ("CET4T: length (%d) must be less than MLc (%d)",
                                      length, CE_T4T_MAX_LC);
                    length = 0;
                }

                if (length > 0)
                    ce_t4t_update_binary (offset, length, p_cmd);
                else
                    ce_t4t_send_status (T4T_RSP_WRONG_PARAMS);
            }
            else
            {
                CE_TRACE_ERROR0 ("CET4T: NDEF File has not been selected");
                ce_t4t_send_status (T4T_RSP_CMD_NOT_ALLOWED);
            }
        }
        else
        {
            CE_TRACE_ERROR1 ("CET4T: Unsupported Instruction byte (0x%02X)", instruct);
            ce_t4t_send_status (T4T_RSP_INSTR_NOT_SUPPORTED);
        }
    }
    else
    {
        CE_TRACE_ERROR0 ("CET4T: Application has not been selected");
        ce_t4t_send_status (T4T_RSP_CMD_NOT_ALLOWED);
    }

    if (p_c_apdu)
        GKI_freebuf (p_c_apdu);
}
Exemplo n.º 7
0
/*******************************************************************************
**
** Function         ce_t4t_process_select_app_cmd
**
** Description      This function processes Select Command by AID.
**
** Returns          none
**
*******************************************************************************/
static void ce_t4t_process_select_app_cmd (UINT8 *p_cmd, BT_HDR *p_c_apdu)
{
    UINT8    data_len;
    UINT16   status_words = 0x0000; /* invalid status words */
    tCE_DATA ce_data;
    UINT8    xx;

    CE_TRACE_DEBUG0 ("ce_t4t_process_select_app_cmd ()");

    p_cmd++; /* skip P2 */

    /* Lc Byte */
    BE_STREAM_TO_UINT8 (data_len, p_cmd);

#if (CE_TEST_INCLUDED == TRUE)
    if (mapping_aid_test_enabled)
    {
        if (  (data_len == T4T_V20_NDEF_TAG_AID_LEN)
            &&(!memcmp(p_cmd, ce_test_tag_app_id, data_len))
            &&(ce_cb.mem.t4t.p_ndef_msg)  )
        {
            GKI_freebuf (p_c_apdu);
            ce_t4t_send_status ((UINT16) T4T_RSP_CMD_CMPLTED);
            return;
        }
    }
#endif

    /*
    ** Compare AIDs registered by applications
    ** if found, use callback of the application
    ** otherwise, return error and maintain the same status
    */
    ce_cb.mem.t4t.selected_aid_idx = CE_T4T_MAX_REG_AID;
    for (xx = 0; xx < CE_T4T_MAX_REG_AID; xx++)
    {
        if (  (ce_cb.mem.t4t.reg_aid[xx].aid_len > 0)
            &&(ce_cb.mem.t4t.reg_aid[xx].aid_len == data_len)
            &&(!(memcmp(ce_cb.mem.t4t.reg_aid[xx].aid, p_cmd, data_len)))  )
        {
            ce_cb.mem.t4t.selected_aid_idx = xx;
            break;
        }
    }

    /* if found matched AID */
    if (ce_cb.mem.t4t.selected_aid_idx < CE_T4T_MAX_REG_AID)
    {
        ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_CC_FILE_SELECTED);
        ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_NDEF_SELECTED);
        ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_T4T_APP_SELECTED);
        ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_WILDCARD_AID_SELECTED);
        ce_cb.mem.t4t.status |= CE_T4T_STATUS_REG_AID_SELECTED;

        CE_TRACE_DEBUG4 ("ce_t4t_process_select_app_cmd (): Registered AID[%02X%02X%02X%02X...] is selected",
                         ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].aid[0],
                         ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].aid[1],
                         ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].aid[2],
                         ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].aid[3]);

        ce_data.raw_frame.status = NFC_STATUS_OK;
        ce_data.raw_frame.p_data = p_c_apdu;
        ce_data.raw_frame.aid_handle = ce_cb.mem.t4t.selected_aid_idx;

        p_c_apdu = NULL;

        (*(ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].p_cback)) (CE_T4T_RAW_FRAME_EVT, &ce_data);
    }
    else if (  (data_len == T4T_V20_NDEF_TAG_AID_LEN)
             &&(!memcmp(p_cmd, t4t_v20_ndef_tag_aid, data_len - 1))
             &&(ce_cb.mem.t4t.p_ndef_msg)  )
    {
        p_cmd += data_len - 1;

        /* adjust version if possible */
        if ((*p_cmd) == 0x00)
        {
            ce_t4t_set_version_in_cc (T4T_VERSION_1_0);
            status_words = T4T_RSP_CMD_CMPLTED;
        }
        else if ((*p_cmd) == 0x01)
        {
            ce_t4t_set_version_in_cc (T4T_VERSION_2_0);
            status_words = T4T_RSP_CMD_CMPLTED;
        }
        else
        {
            CE_TRACE_DEBUG0 ("ce_t4t_process_select_app_cmd (): Not found matched AID");
            status_words = T4T_RSP_NOT_FOUND;
        }
    }
    else if (ce_cb.mem.t4t.p_wildcard_aid_cback)
    {
        ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_CC_FILE_SELECTED);
        ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_NDEF_SELECTED);
        ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_T4T_APP_SELECTED);
        ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_REG_AID_SELECTED);
        ce_cb.mem.t4t.status |= CE_T4T_STATUS_WILDCARD_AID_SELECTED;

        ce_data.raw_frame.status = NFC_STATUS_OK;
        ce_data.raw_frame.p_data = p_c_apdu;
        ce_data.raw_frame.aid_handle = CE_T4T_WILDCARD_AID_HANDLE;
        p_c_apdu = NULL;

        CE_TRACE_DEBUG0 ("CET4T: Forward raw frame (SELECT APP) to wildcard AID handler");
        (*(ce_cb.mem.t4t.p_wildcard_aid_cback)) (CE_T4T_RAW_FRAME_EVT, &ce_data);
    }
    else
    {
        CE_TRACE_DEBUG0 ("ce_t4t_process_select_app_cmd (): Not found matched AID or not listening T4T NDEF");
        status_words = T4T_RSP_NOT_FOUND;
    }

    if (status_words)
    {
        /* if T4T CE can support */
        if (status_words == T4T_RSP_CMD_CMPLTED)
        {
            ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_CC_FILE_SELECTED);
            ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_NDEF_SELECTED);
            ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_REG_AID_SELECTED);
            ce_cb.mem.t4t.status &= ~ (CE_T4T_STATUS_WILDCARD_AID_SELECTED);
            ce_cb.mem.t4t.status |= CE_T4T_STATUS_T4T_APP_SELECTED;

            CE_TRACE_DEBUG0 ("ce_t4t_process_select_app_cmd (): T4T CE App selected");
        }

        ce_t4t_send_status (status_words);
        GKI_freebuf (p_c_apdu);
    }
    /* if status_words is not set then upper layer will send R-APDU */

    return;
}
Exemplo n.º 8
0
/*******************************************************************************
**
** Function         avrc_pars_vendor_cmd
**
** Description      This function parses the vendor specific commands defined by
**                  Bluetooth SIG
**
** Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully.
**                  Otherwise, the error code defined by AVRCP 1.4
**
*******************************************************************************/
static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR *p_msg, tAVRC_COMMAND *p_result,
                                      UINT8 *p_buf, UINT16 buf_len)
{
    tAVRC_STS  status = AVRC_STS_NO_ERROR;
    UINT8   *p;
    UINT16  len;
    UINT8   xx, yy;
    UINT8   *p_u8;
    UINT16  *p_u16;
    UINT32  u32, u32_2, *p_u32;
    tAVRC_APP_SETTING       *p_app_set;
    UINT16  size_needed;

    /* Check the vendor data */
    if (p_msg->vendor_len == 0) {
        return AVRC_STS_NO_ERROR;
    }
    if (p_msg->p_vendor_data == NULL) {
        return AVRC_STS_INTERNAL_ERR;
    }

    p = p_msg->p_vendor_data;
    p_result->pdu = *p++;
    AVRC_TRACE_DEBUG("avrc_pars_vendor_cmd() pdu:0x%x", p_result->pdu);
    if (!AVRC_IsValidAvcType (p_result->pdu, p_msg->hdr.ctype)) {
        AVRC_TRACE_DEBUG("avrc_pars_vendor_cmd() detects wrong AV/C type!");
        status = AVRC_STS_BAD_CMD;
    }

    p++; /* skip the reserved byte */
    BE_STREAM_TO_UINT16 (len, p);
    if ((len + 4) != (p_msg->vendor_len)) {
        status = AVRC_STS_INTERNAL_ERR;
    }

    if (status != AVRC_STS_NO_ERROR) {
        return status;
    }

    switch (p_result->pdu) {
    case AVRC_PDU_GET_CAPABILITIES:         /* 0x10 */
        p_result->get_caps.capability_id = *p++;
        if (!AVRC_IS_VALID_CAP_ID(p_result->get_caps.capability_id)) {
            status = AVRC_STS_BAD_PARAM;
        } else if (len != 1) {
            status = AVRC_STS_INTERNAL_ERR;
        }
        break;

    case AVRC_PDU_LIST_PLAYER_APP_ATTR:     /* 0x11 */
        /* no additional parameters */
        if (len != 0) {
            status = AVRC_STS_INTERNAL_ERR;
        }
        break;

    case AVRC_PDU_LIST_PLAYER_APP_VALUES:   /* 0x12 */
        p_result->list_app_values.attr_id = *p++;
        if (!AVRC_IS_VALID_ATTRIBUTE(p_result->list_app_values.attr_id)) {
            status = AVRC_STS_BAD_PARAM;
        } else if (len != 1) {
            status = AVRC_STS_INTERNAL_ERR;
        }
        break;

    case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: /* 0x13 */
    case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: /* 0x15 */
        BE_STREAM_TO_UINT8 (p_result->get_cur_app_val.num_attr, p);
        if (len != (p_result->get_cur_app_val.num_attr + 1)) {
            status = AVRC_STS_INTERNAL_ERR;
            break;
        }
        p_u8 = p_result->get_cur_app_val.attrs;
        for (xx = 0, yy = 0; xx < p_result->get_cur_app_val.num_attr; xx++) {
            /* only report the valid player app attributes */
            if (AVRC_IsValidPlayerAttr(*p)) {
                p_u8[yy++] = *p;
            }
            p++;
        }
        p_result->get_cur_app_val.num_attr = yy;
        if (yy == 0) {
            status = AVRC_STS_BAD_PARAM;
        }
        break;

    case AVRC_PDU_SET_PLAYER_APP_VALUE:     /* 0x14 */
        BE_STREAM_TO_UINT8 (p_result->set_app_val.num_val, p);
        size_needed = sizeof(tAVRC_APP_SETTING);
        if (p_buf && (len == ((p_result->set_app_val.num_val << 1) + 1))) {
            p_result->set_app_val.p_vals = (tAVRC_APP_SETTING *)p_buf;
            p_app_set = p_result->set_app_val.p_vals;
            for (xx = 0; ((xx < p_result->set_app_val.num_val) && (buf_len > size_needed)); xx++) {
                p_app_set[xx].attr_id = *p++;
                p_app_set[xx].attr_val = *p++;
                if (!avrc_is_valid_player_attrib_value(p_app_set[xx].attr_id, p_app_set[xx].attr_val)) {
                    status = AVRC_STS_BAD_PARAM;
                }
            }
            if (xx != p_result->set_app_val.num_val) {
                AVRC_TRACE_ERROR("AVRC_PDU_SET_PLAYER_APP_VALUE not enough room:%d orig num_val:%d",
                                 xx, p_result->set_app_val.num_val);
                p_result->set_app_val.num_val = xx;
            }
        } else {
            AVRC_TRACE_ERROR("AVRC_PDU_SET_PLAYER_APP_VALUE NULL decode buffer or bad len");
            status = AVRC_STS_INTERNAL_ERR;
        }
        break;

    case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT:/* 0x16 */
        if (len < 3) {
            status = AVRC_STS_INTERNAL_ERR;
        } else {
            BE_STREAM_TO_UINT8 (p_result->get_app_val_txt.attr_id, p);
            if (!AVRC_IS_VALID_ATTRIBUTE(p_result->get_app_val_txt.attr_id)) {
                status = AVRC_STS_BAD_PARAM;
            } else {
                BE_STREAM_TO_UINT8 (p_result->get_app_val_txt.num_val, p);
                if ( (len - 2/* attr_id & num_val */) != p_result->get_app_val_txt.num_val) {
                    status = AVRC_STS_INTERNAL_ERR;
                } else {
                    p_u8 = p_result->get_app_val_txt.vals;
                    for (xx = 0; xx < p_result->get_app_val_txt.num_val; xx++) {
                        p_u8[xx] = *p++;
                        if (!avrc_is_valid_player_attrib_value(p_result->get_app_val_txt.attr_id,
                                                               p_u8[xx])) {
                            status = AVRC_STS_BAD_PARAM;
                            break;
                        }
                    }
                }
            }
        }
        break;

    case AVRC_PDU_INFORM_DISPLAY_CHARSET:  /* 0x17 */
        if (len < 3) {
            status = AVRC_STS_INTERNAL_ERR;
        } else {
            BE_STREAM_TO_UINT8 (p_result->inform_charset.num_id, p);
            if ( (len - 1/* num_id */) != p_result->inform_charset.num_id * 2) {
                status = AVRC_STS_INTERNAL_ERR;
            } else {
                p_u16 = p_result->inform_charset.charsets;
                if (p_result->inform_charset.num_id > AVRC_MAX_CHARSET_SIZE) {
                    p_result->inform_charset.num_id = AVRC_MAX_CHARSET_SIZE;
                }
                for (xx = 0; xx < p_result->inform_charset.num_id; xx++) {
                    BE_STREAM_TO_UINT16 (p_u16[xx], p);
                }
            }
        }
        break;

    case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT:/* 0x18 */
        if (len != 1) {
            status = AVRC_STS_INTERNAL_ERR;
        } else {
            p_result->inform_battery_status.battery_status = *p++;
            if (!AVRC_IS_VALID_BATTERY_STATUS(p_result->inform_battery_status.battery_status)) {
                status = AVRC_STS_BAD_PARAM;
            }
        }
        break;

    case AVRC_PDU_GET_ELEMENT_ATTR:         /* 0x20 */
        if (len < 9) { /* UID/8 and num_attr/1 */
            status = AVRC_STS_INTERNAL_ERR;
        } else {
            BE_STREAM_TO_UINT32 (u32, p);
            BE_STREAM_TO_UINT32 (u32_2, p);
            if (u32 == 0 && u32_2 == 0) {
                BE_STREAM_TO_UINT8 (p_result->get_elem_attrs.num_attr, p);
                if ( (len - 9/* UID/8 and num_attr/1 */) != (p_result->get_elem_attrs.num_attr * 4)) {
                    status = AVRC_STS_INTERNAL_ERR;
                } else {
                    p_u32 = p_result->get_elem_attrs.attrs;
                    if (p_result->get_elem_attrs.num_attr > AVRC_MAX_ELEM_ATTR_SIZE) {
                        p_result->get_elem_attrs.num_attr = AVRC_MAX_ELEM_ATTR_SIZE;
                    }
                    for (xx = 0; xx < p_result->get_elem_attrs.num_attr; xx++) {
                        BE_STREAM_TO_UINT32 (p_u32[xx], p);
                    }
                }
            } else {
                status = AVRC_STS_NOT_FOUND;
            }
        }
        break;

    case AVRC_PDU_GET_PLAY_STATUS:          /* 0x30 */
        /* no additional parameters */
        if (len != 0) {
            status = AVRC_STS_INTERNAL_ERR;
        }
        break;

    case AVRC_PDU_REGISTER_NOTIFICATION:    /* 0x31 */
        if (len != 5) {
            status = AVRC_STS_INTERNAL_ERR;
        } else {
            BE_STREAM_TO_UINT8 (p_result->reg_notif.event_id, p);
            BE_STREAM_TO_UINT32 (p_result->reg_notif.param, p);
        }
        break;

    case AVRC_PDU_SET_ABSOLUTE_VOLUME: {
        if (len != 1) {
            status = AVRC_STS_INTERNAL_ERR;
        }
        break;
    }

    /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */
    /* case AVRC_PDU_ABORT_CONTINUATION_RSP:   0x41 */

    default:
        status = AVRC_STS_BAD_CMD;
        break;
    }

    return status;
}
Exemplo n.º 9
0
/*******************************************************************************
**
** Function         llcp_util_parse_connect
**
** Description      Parse CONNECT PDU
**
** Returns          tLLCP_STATUS
**
*******************************************************************************/
tLLCP_STATUS llcp_util_parse_connect (UINT8  *p_bytes, UINT16 length, tLLCP_CONNECTION_PARAMS *p_params)
{
    UINT8 param_type, param_len, *p = p_bytes;

    p_params->miu = LLCP_DEFAULT_MIU;
    p_params->rw  = LLCP_DEFAULT_RW;
    p_params->sn[0] = 0;

    while (length)
    {
        BE_STREAM_TO_UINT8 (param_type, p);
        length--;

        switch (param_type)
        {
        case LLCP_MIUX_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT16 (p_params->miu, p);
            p_params->miu &= LLCP_MIUX_MASK;
            p_params->miu += LLCP_DEFAULT_MIU;

            LLCP_TRACE_DEBUG1 ("llcp_util_parse_connect (): LLCP_MIUX_TYPE:%d", p_params->miu);
            break;

        case LLCP_RW_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT8 (p_params->rw, p);
            p_params->rw &= 0x0F;

            LLCP_TRACE_DEBUG1 ("llcp_util_parse_connect (): LLCP_RW_TYPE:%d", p_params->rw);
            break;

        case LLCP_SN_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);

            if (param_len <= LLCP_MAX_SN_LEN)
            {
                memcpy (p_params->sn, p, param_len);
                p_params->sn[param_len] = 0;
            }
            else
            {
                memcpy (p_params->sn, p, LLCP_MAX_SN_LEN);
                p_params->sn[LLCP_MAX_SN_LEN] = 0;
            }
            p += param_len;

            LLCP_TRACE_DEBUG1 ("llcp_util_parse_connect (): LLCP_SN_TYPE:<%s>", p_params->sn);
            break;

        default:
            LLCP_TRACE_ERROR1 ("llcp_util_parse_connect (): Unexpected type 0x%x", param_type);
            BE_STREAM_TO_UINT8 (param_len, p);
            p += param_len;
            break;
        }

        /* check remaining lengh */
        if (length >= param_len + 1)
        {
            length -= param_len + 1;
        }
        else
        {
            LLCP_TRACE_ERROR0 ("llcp_util_parse_connect (): Bad LTV's");
            return LLCP_STATUS_FAIL;
        }
    }
    return LLCP_STATUS_SUCCESS;
}
Exemplo n.º 10
0
/*******************************************************************************
**
** Function         llcp_util_parse_link_params
**
** Description      Parse LLCP Link parameters
**
** Returns          TRUE if success
**
*******************************************************************************/
BOOLEAN llcp_util_parse_link_params (UINT16 length, UINT8 *p_bytes)
{
    UINT8 param_type, param_len, *p = p_bytes;

    while (length)
    {
        BE_STREAM_TO_UINT8 (param_type, p);
        length--;

        switch (param_type)
        {
        case LLCP_VERSION_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT8 (llcp_cb.lcb.peer_version, p);
            LLCP_TRACE_DEBUG1 ("Peer Version - 0x%02X", llcp_cb.lcb.peer_version);
            break;

        case LLCP_MIUX_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT16 (llcp_cb.lcb.peer_miu, p);
            llcp_cb.lcb.peer_miu &= LLCP_MIUX_MASK;
            llcp_cb.lcb.peer_miu += LLCP_DEFAULT_MIU;
            LLCP_TRACE_DEBUG1 ("Peer MIU - %d bytes", llcp_cb.lcb.peer_miu);
            break;

        case LLCP_WKS_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT16 (llcp_cb.lcb.peer_wks, p);
            LLCP_TRACE_DEBUG1 ("Peer WKS - 0x%04X", llcp_cb.lcb.peer_wks);
            break;

        case LLCP_LTO_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT8 (llcp_cb.lcb.peer_lto, p);
            llcp_cb.lcb.peer_lto *= LLCP_LTO_UNIT;  /* 10ms unit */
            LLCP_TRACE_DEBUG1 ("Peer LTO - %d ms", llcp_cb.lcb.peer_lto);
            break;

        case LLCP_OPT_TYPE:
            BE_STREAM_TO_UINT8 (param_len, p);
            BE_STREAM_TO_UINT8 (llcp_cb.lcb.peer_opt, p);
            LLCP_TRACE_DEBUG1 ("Peer OPT - 0x%02X", llcp_cb.lcb.peer_opt);
            break;

        default:
            LLCP_TRACE_ERROR1 ("llcp_util_parse_link_params (): Unexpected type 0x%x", param_type);
            BE_STREAM_TO_UINT8 (param_len, p);
            p += param_len;
            break;
        }

        if (length >= param_len + 1)
            length -= param_len + 1;
        else
        {
            LLCP_TRACE_ERROR0 ("llcp_util_parse_link_params (): Bad LTV's");
            return (FALSE);
        }
    }
    return (TRUE);
}
Exemplo n.º 11
0
/*******************************************************************************
**
** Function         rw_t4t_sm_detect_ndef
**
** Description      State machine for NDEF detection procedure
**
** Returns          none
**
*******************************************************************************/
static void rw_t4t_sm_detect_ndef (BT_HDR *p_r_apdu)
{
    tRW_T4T_CB  *p_t4t = &rw_cb.tcb.t4t;
    UINT8       *p, type, length;
    UINT16      status_words, nlen;
    tRW_DATA    rw_data;

#if (BT_TRACE_VERBOSE == TRUE)
    RW_TRACE_DEBUG2 ("rw_t4t_sm_detect_ndef (): sub_state:%s (%d)",
                      rw_t4t_get_sub_state_name (p_t4t->sub_state), p_t4t->sub_state);
#else
    RW_TRACE_DEBUG1 ("rw_t4t_sm_detect_ndef (): sub_state=%d", p_t4t->sub_state);
#endif

    /* get status words */
    p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
    p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
    BE_STREAM_TO_UINT16 (status_words, p);

    if (status_words != T4T_RSP_CMD_CMPLTED)
    {
        /* try V1.0 after failing of V2.0 */
        if (  (p_t4t->sub_state == RW_T4T_SUBSTATE_WAIT_SELECT_APP)
            &&(p_t4t->version   == T4T_VERSION_2_0)  )
        {
            p_t4t->version = T4T_VERSION_1_0;

            RW_TRACE_DEBUG1 ("rw_t4t_sm_detect_ndef (): retry with version=0x%02X",
                              p_t4t->version);

            if (!rw_t4t_select_application (T4T_VERSION_1_0))
            {
                rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
            }
            return;
        }

        p_t4t->ndef_status &= ~ (RW_T4T_NDEF_STATUS_NDEF_DETECTED);
        rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p-2), *(p-1));
        return;
    }

    switch (p_t4t->sub_state)
    {
    case RW_T4T_SUBSTATE_WAIT_SELECT_APP:

        /* NDEF Tag application has been selected then select CC file */
        if (!rw_t4t_select_file (T4T_CC_FILE_ID))
        {
            rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
        }
        else
        {
            p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_CC;
        }
        break;

    case RW_T4T_SUBSTATE_WAIT_SELECT_CC:

        /* CC file has been selected then read mandatory part of CC file */
        if (!rw_t4t_read_file (0x00, T4T_CC_FILE_MIN_LEN, FALSE))
        {
            rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
        }
        else
        {
            p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_CC_FILE;
        }
        break;

    case RW_T4T_SUBSTATE_WAIT_CC_FILE:

        /* CC file has been read then validate and select mandatory NDEF file */
        if (p_r_apdu->len >= T4T_CC_FILE_MIN_LEN + T4T_RSP_STATUS_WORDS_SIZE)
        {
            p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;

            BE_STREAM_TO_UINT16 (p_t4t->cc_file.cclen, p);
            BE_STREAM_TO_UINT8 (p_t4t->cc_file.version, p);
            BE_STREAM_TO_UINT16 (p_t4t->cc_file.max_le, p);
            BE_STREAM_TO_UINT16 (p_t4t->cc_file.max_lc, p);

            BE_STREAM_TO_UINT8 (type, p);
            BE_STREAM_TO_UINT8 (length, p);

            if (  (type == T4T_NDEF_FILE_CONTROL_TYPE)
                &&(length == T4T_FILE_CONTROL_LENGTH)  )
            {
                BE_STREAM_TO_UINT16 (p_t4t->cc_file.ndef_fc.file_id, p);
                BE_STREAM_TO_UINT16 (p_t4t->cc_file.ndef_fc.max_file_size, p);
                BE_STREAM_TO_UINT8 (p_t4t->cc_file.ndef_fc.read_access, p);
                BE_STREAM_TO_UINT8 (p_t4t->cc_file.ndef_fc.write_access, p);

#if (BT_TRACE_VERBOSE == TRUE)
                RW_TRACE_DEBUG0 ("Capability Container (CC) file");
                RW_TRACE_DEBUG1 ("  CCLEN:  0x%04X",    p_t4t->cc_file.cclen);
                RW_TRACE_DEBUG1 ("  Version:0x%02X",    p_t4t->cc_file.version);
                RW_TRACE_DEBUG1 ("  MaxLe:  0x%04X",    p_t4t->cc_file.max_le);
                RW_TRACE_DEBUG1 ("  MaxLc:  0x%04X",    p_t4t->cc_file.max_lc);
                RW_TRACE_DEBUG0 ("  NDEF File Control TLV");
                RW_TRACE_DEBUG1 ("    FileID:      0x%04X", p_t4t->cc_file.ndef_fc.file_id);
                RW_TRACE_DEBUG1 ("    MaxFileSize: 0x%04X", p_t4t->cc_file.ndef_fc.max_file_size);
                RW_TRACE_DEBUG1 ("    ReadAccess:  0x%02X", p_t4t->cc_file.ndef_fc.read_access);
                RW_TRACE_DEBUG1 ("    WriteAccess: 0x%02X", p_t4t->cc_file.ndef_fc.write_access);
#endif

                if (rw_t4t_validate_cc_file ())
                {
                    if (!rw_t4t_select_file (p_t4t->cc_file.ndef_fc.file_id))
                    {
                        rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
                    }
                    else
                    {
                        p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE;
                    }
                    break;
                }
            }
        }

        /* invalid response or CC file */
        p_t4t->ndef_status &= ~ (RW_T4T_NDEF_STATUS_NDEF_DETECTED);
        rw_t4t_handle_error (NFC_STATUS_BAD_RESP, 0, 0);
        break;

    case RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE:

        /* NDEF file has been selected then read the first 2 bytes (NLEN) */
        if (!rw_t4t_read_file (0, T4T_FILE_LENGTH_SIZE, FALSE))
        {
            rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
        }
        else
        {
            p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_READ_NLEN;
        }
        break;

    case RW_T4T_SUBSTATE_WAIT_READ_NLEN:

        /* NLEN has been read then report upper layer */
        if (p_r_apdu->len == T4T_FILE_LENGTH_SIZE + T4T_RSP_STATUS_WORDS_SIZE)
        {
            /* get length of NDEF */
            p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
            BE_STREAM_TO_UINT16 (nlen, p);

            if (nlen <= p_t4t->cc_file.ndef_fc.max_file_size - T4T_FILE_LENGTH_SIZE)
            {
                p_t4t->ndef_status = RW_T4T_NDEF_STATUS_NDEF_DETECTED;

                if (p_t4t->cc_file.ndef_fc.write_access != T4T_FC_WRITE_ACCESS)
                {
                    p_t4t->ndef_status |= RW_T4T_NDEF_STATUS_NDEF_READ_ONLY;
                }

                /* Get max bytes to read per command */
                if (p_t4t->cc_file.max_le >= RW_T4T_MAX_DATA_PER_READ)
                {
                    p_t4t->max_read_size = RW_T4T_MAX_DATA_PER_READ;
                }
                else
                {
                    p_t4t->max_read_size = p_t4t->cc_file.max_le;
                }

                /* Le: valid range is 0x01 to 0xFF */
                if (p_t4t->max_read_size >= T4T_MAX_LENGTH_LE)
                {
                    p_t4t->max_read_size = T4T_MAX_LENGTH_LE;
                }

                /* Get max bytes to update per command */
                if (p_t4t->cc_file.max_lc >= RW_T4T_MAX_DATA_PER_WRITE)
                {
                    p_t4t->max_update_size = RW_T4T_MAX_DATA_PER_WRITE;
                }
                else
                {
                    p_t4t->max_update_size = p_t4t->cc_file.max_lc;
                }

                /* Lc: valid range is 0x01 to 0xFF */
                if (p_t4t->max_update_size >= T4T_MAX_LENGTH_LC)
                {
                    p_t4t->max_update_size = T4T_MAX_LENGTH_LC;
                }

                p_t4t->ndef_length = nlen;
                p_t4t->state       = RW_T4T_STATE_IDLE;

                if (rw_cb.p_cback)
                {
                    rw_data.ndef.status   = NFC_STATUS_OK;
                    rw_data.ndef.protocol = NFC_PROTOCOL_ISO_DEP;
                    rw_data.ndef.max_size = (UINT32) (p_t4t->cc_file.ndef_fc.max_file_size - (UINT16) T4T_FILE_LENGTH_SIZE);
                    rw_data.ndef.cur_size = nlen;
                    rw_data.ndef.flags    = RW_NDEF_FL_SUPPORTED | RW_NDEF_FL_FORMATED;
                    if (p_t4t->cc_file.ndef_fc.write_access != T4T_FC_WRITE_ACCESS)
                    {
                        rw_data.ndef.flags    |= RW_NDEF_FL_READ_ONLY;
                    }

                    (*(rw_cb.p_cback)) (RW_T4T_NDEF_DETECT_EVT, &rw_data);

                    RW_TRACE_DEBUG0 ("rw_t4t_sm_detect_ndef (): Sent RW_T4T_NDEF_DETECT_EVT");
                }
            }
            else
            {
                /* NLEN should be less than max file size */
                RW_TRACE_ERROR2 ("rw_t4t_sm_detect_ndef (): NLEN (%d) + 2 must be <= max file size (%d)",
                                 nlen, p_t4t->cc_file.ndef_fc.max_file_size);

                p_t4t->ndef_status &= ~ (RW_T4T_NDEF_STATUS_NDEF_DETECTED);
                rw_t4t_handle_error (NFC_STATUS_BAD_RESP, 0, 0);
            }
        }
        else
        {
            /* response payload size should be T4T_FILE_LENGTH_SIZE */
            RW_TRACE_ERROR2 ("rw_t4t_sm_detect_ndef (): Length (%d) of R-APDU must be %d",
                             p_r_apdu->len, T4T_FILE_LENGTH_SIZE + T4T_RSP_STATUS_WORDS_SIZE);

            p_t4t->ndef_status &= ~ (RW_T4T_NDEF_STATUS_NDEF_DETECTED);
            rw_t4t_handle_error (NFC_STATUS_BAD_RESP, 0, 0);
        }
        break;

    default:
        RW_TRACE_ERROR1 ("rw_t4t_sm_detect_ndef (): unknown sub_state=%d", p_t4t->sub_state);
        rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
        break;
    }
}