This adds support for NFC-DEP protocol in initiator mode for NFC-A and NFC-F technologies. When a target is detected, the process flow is as follow: For NFC-A technology: 1 - The digital stack receives a SEL_RES as the reply of the SEL_REQ command. 2 - If b7 of SEL_RES is set, the peer device is configure for NFC-DEP protocol. NFC core is notified through nfc_targets_found(). Execution continues at step 4. 3 - Otherwise, it's a tag and the NFC core is notified. Detection ends. 4 - The digital stacks sends an ATR_REQ command containing a randomly generated NFCID3 and the general bytes obtained from the LLCP layer of NFC core. For NFC-F technology: 1 - The digital stack receives a SENSF_RES as the reply of the SENSF_REQ command. 2 - If B1 and B2 of NFCID2 are 0x01 and 0xFE respectively, the peer device is configured for NFC-DEP protocol. NFC core is notified through nfc_targets_found(). Execution continues at step 4. 3 - Otherwise it's a type 3 tag. NFC core is notified. Detection ends. 4 - The digital stacks sends an ATR_REQ command containing the NFC-F NFCID2 as NFCID3 and the general bytes obtained from the LLCP layer of NFC core. For both technologies: 5 - The digital stacks receives the ATR_RES response containing the NFCID3 and the general bytes of the peer device. 6 - The digital stack notifies NFC core that the DEP link is up through nfc_dep_link_up(). 7 - The NFC core performs data exchange through tm_transceive(). 8 - The digital stack sends a DEP_REQ command containing an I PDU with the data from NFC core. 9 - The digital stack receives a DEP_RES command 10 - If the DEP_RES response contains a supervisor PDU with timeout extension request (RTOX) the digital stack sends a DEP_REQ command containing a supervisor PDU acknowledging the RTOX request. The execution continues at step 9. 11 - If the DEP_RES response contains an I PDU, the response data is passed back to NFC core through the response callback. The execution continues at step 8. Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>tirimbino
parent
8c0695e499
commit
7d0911c02f
@ -0,0 +1,381 @@ |
||||
/*
|
||||
* NFC Digital Protocol stack |
||||
* Copyright (c) 2013, Intel Corporation. |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms and conditions of the GNU General Public License, |
||||
* version 2, as published by the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope it will be useful, but WITHOUT |
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
||||
* more details. |
||||
* |
||||
*/ |
||||
|
||||
#include "digital.h" |
||||
|
||||
#define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4 |
||||
#define DIGITAL_NFC_DEP_FRAME_DIR_IN 0xD5 |
||||
|
||||
#define DIGITAL_NFC_DEP_NFCA_SOD_SB 0xF0 |
||||
|
||||
#define DIGITAL_CMD_ATR_REQ 0x00 |
||||
#define DIGITAL_CMD_ATR_RES 0x01 |
||||
#define DIGITAL_CMD_PSL_REQ 0x04 |
||||
#define DIGITAL_CMD_PSL_RES 0x05 |
||||
#define DIGITAL_CMD_DEP_REQ 0x06 |
||||
#define DIGITAL_CMD_DEP_RES 0x07 |
||||
|
||||
#define DIGITAL_ATR_REQ_MIN_SIZE 16 |
||||
#define DIGITAL_ATR_REQ_MAX_SIZE 64 |
||||
|
||||
#define DIGITAL_NFCID3_LEN ((u8)8) |
||||
#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30 |
||||
#define DIGITAL_GB_BIT 0x02 |
||||
|
||||
#define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0) |
||||
|
||||
#define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10 |
||||
|
||||
#define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \ |
||||
((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT) |
||||
#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb) ((pfb) & 0x10) |
||||
#define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08) |
||||
#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04) |
||||
#define DIGITAL_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03) |
||||
|
||||
#define DIGITAL_NFC_DEP_PFB_I_PDU 0x00 |
||||
#define DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU 0x40 |
||||
#define DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU 0x80 |
||||
|
||||
struct digital_atr_req { |
||||
u8 dir; |
||||
u8 cmd; |
||||
u8 nfcid3[10]; |
||||
u8 did; |
||||
u8 bs; |
||||
u8 br; |
||||
u8 pp; |
||||
u8 gb[0]; |
||||
} __packed; |
||||
|
||||
struct digital_atr_res { |
||||
u8 dir; |
||||
u8 cmd; |
||||
u8 nfcid3[10]; |
||||
u8 did; |
||||
u8 bs; |
||||
u8 br; |
||||
u8 to; |
||||
u8 pp; |
||||
u8 gb[0]; |
||||
} __packed; |
||||
|
||||
struct digital_psl_req { |
||||
u8 dir; |
||||
u8 cmd; |
||||
u8 did; |
||||
u8 brs; |
||||
u8 fsl; |
||||
} __packed; |
||||
|
||||
struct digital_psl_res { |
||||
u8 dir; |
||||
u8 cmd; |
||||
u8 did; |
||||
} __packed; |
||||
|
||||
struct digital_dep_req_res { |
||||
u8 dir; |
||||
u8 cmd; |
||||
u8 pfb; |
||||
} __packed; |
||||
|
||||
static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, |
||||
struct sk_buff *resp); |
||||
|
||||
static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev, |
||||
struct sk_buff *skb) |
||||
{ |
||||
skb_push(skb, sizeof(u8)); |
||||
|
||||
skb->data[0] = skb->len; |
||||
|
||||
if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A) |
||||
*skb_push(skb, sizeof(u8)) = DIGITAL_NFC_DEP_NFCA_SOD_SB; |
||||
} |
||||
|
||||
static int digital_skb_pull_dep_sod(struct nfc_digital_dev *ddev, |
||||
struct sk_buff *skb) |
||||
{ |
||||
u8 size; |
||||
|
||||
if (skb->len < 2) |
||||
return -EIO; |
||||
|
||||
if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A) |
||||
skb_pull(skb, sizeof(u8)); |
||||
|
||||
size = skb->data[0]; |
||||
if (size != skb->len) |
||||
return -EIO; |
||||
|
||||
skb_pull(skb, sizeof(u8)); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg, |
||||
struct sk_buff *resp) |
||||
{ |
||||
struct nfc_target *target = arg; |
||||
struct digital_atr_res *atr_res; |
||||
u8 gb_len; |
||||
int rc; |
||||
|
||||
if (IS_ERR(resp)) { |
||||
rc = PTR_ERR(resp); |
||||
resp = NULL; |
||||
goto exit; |
||||
} |
||||
|
||||
rc = ddev->skb_check_crc(resp); |
||||
if (rc) { |
||||
PROTOCOL_ERR("14.4.1.6"); |
||||
goto exit; |
||||
} |
||||
|
||||
rc = digital_skb_pull_dep_sod(ddev, resp); |
||||
if (rc) { |
||||
PROTOCOL_ERR("14.4.1.2"); |
||||
goto exit; |
||||
} |
||||
|
||||
if (resp->len < sizeof(struct digital_atr_res)) { |
||||
rc = -EIO; |
||||
goto exit; |
||||
} |
||||
|
||||
gb_len = resp->len - sizeof(struct digital_atr_res); |
||||
|
||||
atr_res = (struct digital_atr_res *)resp->data; |
||||
|
||||
rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len); |
||||
if (rc) |
||||
goto exit; |
||||
|
||||
rc = nfc_dep_link_is_up(ddev->nfc_dev, target->idx, NFC_COMM_ACTIVE, |
||||
NFC_RF_INITIATOR); |
||||
|
||||
ddev->curr_nfc_dep_pni = 0; |
||||
|
||||
exit: |
||||
dev_kfree_skb(resp); |
||||
|
||||
if (rc) |
||||
ddev->curr_protocol = 0; |
||||
} |
||||
|
||||
int digital_in_send_atr_req(struct nfc_digital_dev *ddev, |
||||
struct nfc_target *target, __u8 comm_mode, __u8 *gb, |
||||
size_t gb_len) |
||||
{ |
||||
struct sk_buff *skb; |
||||
struct digital_atr_req *atr_req; |
||||
uint size; |
||||
|
||||
size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len; |
||||
|
||||
if (size > DIGITAL_ATR_REQ_MAX_SIZE) { |
||||
PROTOCOL_ERR("14.6.1.1"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
skb = digital_skb_alloc(ddev, size); |
||||
if (!skb) |
||||
return -ENOMEM; |
||||
|
||||
skb_put(skb, sizeof(struct digital_atr_req)); |
||||
|
||||
atr_req = (struct digital_atr_req *)skb->data; |
||||
memset(atr_req, 0, sizeof(struct digital_atr_req)); |
||||
|
||||
atr_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; |
||||
atr_req->cmd = DIGITAL_CMD_ATR_REQ; |
||||
if (target->nfcid2_len) |
||||
memcpy(atr_req->nfcid3, target->nfcid2, |
||||
max(target->nfcid2_len, DIGITAL_NFCID3_LEN)); |
||||
else |
||||
get_random_bytes(atr_req->nfcid3, DIGITAL_NFCID3_LEN); |
||||
|
||||
atr_req->did = 0; |
||||
atr_req->bs = 0; |
||||
atr_req->br = 0; |
||||
|
||||
atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B; |
||||
|
||||
if (gb_len) { |
||||
atr_req->pp |= DIGITAL_GB_BIT; |
||||
memcpy(skb_put(skb, gb_len), gb, gb_len); |
||||
} |
||||
|
||||
digital_skb_push_dep_sod(ddev, skb); |
||||
|
||||
ddev->skb_add_crc(skb); |
||||
|
||||
digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, target); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int digital_in_send_rtox(struct nfc_digital_dev *ddev, |
||||
struct digital_data_exch *data_exch, u8 rtox) |
||||
{ |
||||
struct digital_dep_req_res *dep_req; |
||||
struct sk_buff *skb; |
||||
int rc; |
||||
|
||||
skb = digital_skb_alloc(ddev, 1); |
||||
if (!skb) |
||||
return -ENOMEM; |
||||
|
||||
*skb_put(skb, 1) = rtox; |
||||
|
||||
skb_push(skb, sizeof(struct digital_dep_req_res)); |
||||
|
||||
dep_req = (struct digital_dep_req_res *)skb->data; |
||||
|
||||
dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; |
||||
dep_req->cmd = DIGITAL_CMD_DEP_REQ; |
||||
dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU | |
||||
DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT; |
||||
|
||||
digital_skb_push_dep_sod(ddev, skb); |
||||
|
||||
ddev->skb_add_crc(skb); |
||||
|
||||
rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, |
||||
data_exch); |
||||
|
||||
return rc; |
||||
} |
||||
|
||||
static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, |
||||
struct sk_buff *resp) |
||||
{ |
||||
struct digital_data_exch *data_exch = arg; |
||||
struct digital_dep_req_res *dep_res; |
||||
u8 pfb; |
||||
uint size; |
||||
int rc; |
||||
|
||||
if (IS_ERR(resp)) { |
||||
rc = PTR_ERR(resp); |
||||
resp = NULL; |
||||
goto exit; |
||||
} |
||||
|
||||
rc = ddev->skb_check_crc(resp); |
||||
if (rc) { |
||||
PROTOCOL_ERR("14.4.1.6"); |
||||
goto error; |
||||
} |
||||
|
||||
rc = digital_skb_pull_dep_sod(ddev, resp); |
||||
if (rc) { |
||||
PROTOCOL_ERR("14.4.1.2"); |
||||
goto exit; |
||||
} |
||||
|
||||
dep_res = (struct digital_dep_req_res *)resp->data; |
||||
|
||||
if (resp->len < sizeof(struct digital_dep_req_res) || |
||||
dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN || |
||||
dep_res->cmd != DIGITAL_CMD_DEP_RES) { |
||||
rc = -EIO; |
||||
goto error; |
||||
} |
||||
|
||||
pfb = dep_res->pfb; |
||||
|
||||
switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) { |
||||
case DIGITAL_NFC_DEP_PFB_I_PDU: |
||||
if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) { |
||||
PROTOCOL_ERR("14.12.3.3"); |
||||
rc = -EIO; |
||||
goto error; |
||||
} |
||||
|
||||
ddev->curr_nfc_dep_pni = |
||||
DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1); |
||||
rc = 0; |
||||
break; |
||||
|
||||
case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: |
||||
PR_ERR("Received a ACK/NACK PDU"); |
||||
rc = -EIO; |
||||
goto error; |
||||
|
||||
case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: |
||||
if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { |
||||
rc = -EINVAL; |
||||
goto error; |
||||
} |
||||
|
||||
rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]); |
||||
if (rc) |
||||
goto error; |
||||
|
||||
kfree_skb(resp); |
||||
return; |
||||
} |
||||
|
||||
if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) { |
||||
PR_ERR("MI bit set. Chained PDU not supported."); |
||||
rc = -EIO; |
||||
goto error; |
||||
} |
||||
|
||||
size = sizeof(struct digital_dep_req_res); |
||||
|
||||
if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) |
||||
size++; |
||||
|
||||
if (size > resp->len) { |
||||
rc = -EIO; |
||||
goto error; |
||||
} |
||||
|
||||
skb_pull(resp, size); |
||||
|
||||
exit: |
||||
data_exch->cb(data_exch->cb_context, resp, rc); |
||||
|
||||
error: |
||||
kfree(data_exch); |
||||
|
||||
if (rc) |
||||
kfree_skb(resp); |
||||
} |
||||
|
||||
int digital_in_send_dep_req(struct nfc_digital_dev *ddev, |
||||
struct nfc_target *target, struct sk_buff *skb, |
||||
struct digital_data_exch *data_exch) |
||||
{ |
||||
struct digital_dep_req_res *dep_req; |
||||
|
||||
skb_push(skb, sizeof(struct digital_dep_req_res)); |
||||
|
||||
dep_req = (struct digital_dep_req_res *)skb->data; |
||||
dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; |
||||
dep_req->cmd = DIGITAL_CMD_DEP_REQ; |
||||
dep_req->pfb = ddev->curr_nfc_dep_pni; |
||||
|
||||
digital_skb_push_dep_sod(ddev, skb); |
||||
|
||||
ddev->skb_add_crc(skb); |
||||
|
||||
return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, |
||||
data_exch); |
||||
} |
Loading…
Reference in new issue