1308 lines · 26.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * NFC Digital Protocol stack * Copyright (c) 2013, Intel Corporation. */ #define pr_fmt(fmt) "digital: %s: " fmt, __func__ #include "digital.h" #define DIGITAL_CMD_SENS_REQ    0x26#define DIGITAL_CMD_ALL_REQ     0x52#define DIGITAL_CMD_SEL_REQ_CL1 0x93#define DIGITAL_CMD_SEL_REQ_CL2 0x95#define DIGITAL_CMD_SEL_REQ_CL3 0x97 #define DIGITAL_SDD_REQ_SEL_PAR 0x20 #define DIGITAL_SDD_RES_CT  0x88#define DIGITAL_SDD_RES_LEN 5#define DIGITAL_SEL_RES_LEN 1 #define DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res) (!((sel_res) & 0x04))#define DIGITAL_SEL_RES_IS_T2T(sel_res) (!((sel_res) & 0x60))#define DIGITAL_SEL_RES_IS_T4T(sel_res) ((sel_res) & 0x20)#define DIGITAL_SEL_RES_IS_NFC_DEP(sel_res) ((sel_res) & 0x40) #define DIGITAL_SENS_RES_IS_T1T(sens_res) (((sens_res) & 0x0C00) == 0x0C00)#define DIGITAL_SENS_RES_IS_VALID(sens_res) \	((!((sens_res) & 0x001F) && (((sens_res) & 0x0C00) == 0x0C00)) || \	(((sens_res) & 0x001F) && ((sens_res) & 0x0C00) != 0x0C00)) #define DIGITAL_MIFARE_READ_RES_LEN 16#define DIGITAL_MIFARE_ACK_RES	0x0A #define DIGITAL_CMD_SENSB_REQ			0x05#define DIGITAL_SENSB_ADVANCED			BIT(5)#define DIGITAL_SENSB_EXTENDED			BIT(4)#define DIGITAL_SENSB_ALLB_REQ			BIT(3)#define DIGITAL_SENSB_N(n)			((n) & 0x7) #define DIGITAL_CMD_SENSB_RES			0x50 #define DIGITAL_CMD_ATTRIB_REQ			0x1D#define DIGITAL_ATTRIB_P1_TR0_DEFAULT		(0x0 << 6)#define DIGITAL_ATTRIB_P1_TR1_DEFAULT		(0x0 << 4)#define DIGITAL_ATTRIB_P1_SUPRESS_EOS		BIT(3)#define DIGITAL_ATTRIB_P1_SUPRESS_SOS		BIT(2)#define DIGITAL_ATTRIB_P2_LISTEN_POLL_1		(0x0 << 6)#define DIGITAL_ATTRIB_P2_POLL_LISTEN_1		(0x0 << 4)#define DIGITAL_ATTRIB_P2_MAX_FRAME_256		0x8#define DIGITAL_ATTRIB_P4_DID(n)		((n) & 0xf) #define DIGITAL_CMD_SENSF_REQ	0x00#define DIGITAL_CMD_SENSF_RES	0x01 #define DIGITAL_SENSF_RES_MIN_LENGTH 17#define DIGITAL_SENSF_RES_RD_AP_B1   0x00#define DIGITAL_SENSF_RES_RD_AP_B2   0x8F #define DIGITAL_SENSF_REQ_RC_NONE 0#define DIGITAL_SENSF_REQ_RC_SC   1#define DIGITAL_SENSF_REQ_RC_AP   2 #define DIGITAL_CMD_ISO15693_INVENTORY_REQ	0x01 #define DIGITAL_ISO15693_REQ_FLAG_DATA_RATE	BIT(1)#define DIGITAL_ISO15693_REQ_FLAG_INVENTORY	BIT(2)#define DIGITAL_ISO15693_REQ_FLAG_NB_SLOTS	BIT(5)#define DIGITAL_ISO15693_RES_FLAG_ERROR		BIT(0)#define DIGITAL_ISO15693_RES_IS_VALID(flags) \	(!((flags) & DIGITAL_ISO15693_RES_FLAG_ERROR)) #define DIGITAL_ISO_DEP_I_PCB	 0x02#define DIGITAL_ISO_DEP_PNI(pni) ((pni) & 0x01) #define DIGITAL_ISO_DEP_PCB_TYPE(pcb) ((pcb) & 0xC0) #define DIGITAL_ISO_DEP_I_BLOCK 0x00 #define DIGITAL_ISO_DEP_BLOCK_HAS_DID(pcb) ((pcb) & 0x08) static const u8 digital_ats_fsc[] = {	 16,  24,  32,  40,  48,  64,  96, 128,}; #define DIGITAL_ATS_FSCI(t0) ((t0) & 0x0F)#define DIGITAL_SENSB_FSCI(pi2) (((pi2) & 0xF0) >> 4)#define DIGITAL_ATS_MAX_FSC  256 #define DIGITAL_RATS_BYTE1 0xE0#define DIGITAL_RATS_PARAM 0x80 struct digital_sdd_res {	u8 nfcid1[4];	u8 bcc;} __packed; struct digital_sel_req {	u8 sel_cmd;	u8 b2;	u8 nfcid1[4];	u8 bcc;} __packed; struct digital_sensb_req {	u8 cmd;	u8 afi;	u8 param;} __packed; struct digital_sensb_res {	u8 cmd;	u8 nfcid0[4];	u8 app_data[4];	u8 proto_info[3];} __packed; struct digital_attrib_req {	u8 cmd;	u8 nfcid0[4];	u8 param1;	u8 param2;	u8 param3;	u8 param4;} __packed; struct digital_attrib_res {	u8 mbli_did;} __packed; struct digital_sensf_req {	u8 cmd;	u8 sc1;	u8 sc2;	u8 rc;	u8 tsn;} __packed; struct digital_sensf_res {	u8 cmd;	u8 nfcid2[8];	u8 pad0[2];	u8 pad1[3];	u8 mrti_check;	u8 mrti_update;	u8 pad2;	u8 rd[2];} __packed; struct digital_iso15693_inv_req {	u8 flags;	u8 cmd;	u8 mask_len;	u64 mask;} __packed; struct digital_iso15693_inv_res {	u8 flags;	u8 dsfid;	u64 uid;} __packed; static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev,				   struct nfc_target *target); int digital_in_iso_dep_pull_sod(struct nfc_digital_dev *ddev,				struct sk_buff *skb){	u8 pcb;	u8 block_type; 	if (skb->len < 1)		return -EIO; 	pcb = *skb->data;	block_type = DIGITAL_ISO_DEP_PCB_TYPE(pcb); 	/* No support fo R-block nor S-block */	if (block_type != DIGITAL_ISO_DEP_I_BLOCK) {		pr_err("ISO_DEP R-block and S-block not supported\n");		return -EIO;	} 	if (DIGITAL_ISO_DEP_BLOCK_HAS_DID(pcb)) {		pr_err("DID field in ISO_DEP PCB not supported\n");		return -EIO;	} 	skb_pull(skb, 1); 	return 0;} int digital_in_iso_dep_push_sod(struct nfc_digital_dev *ddev,				struct sk_buff *skb){	/*	 * Chaining not supported so skb->len + 1 PCB byte + 2 CRC bytes must	 * not be greater than remote FSC	 */	if (skb->len + 3 > ddev->target_fsc)		return -EIO; 	skb_push(skb, 1); 	*skb->data = DIGITAL_ISO_DEP_I_PCB | ddev->curr_nfc_dep_pni; 	ddev->curr_nfc_dep_pni =		DIGITAL_ISO_DEP_PNI(ddev->curr_nfc_dep_pni + 1); 	return 0;} static void digital_in_recv_ats(struct nfc_digital_dev *ddev, void *arg,				struct sk_buff *resp){	struct nfc_target *target = arg;	u8 fsdi;	int rc; 	if (IS_ERR(resp)) {		rc = PTR_ERR(resp);		resp = NULL;		goto exit;	} 	if (resp->len < 2) {		rc = -EIO;		goto exit;	} 	fsdi = DIGITAL_ATS_FSCI(resp->data[1]);	if (fsdi >= 8)		ddev->target_fsc = DIGITAL_ATS_MAX_FSC;	else		ddev->target_fsc = digital_ats_fsc[fsdi]; 	ddev->curr_nfc_dep_pni = 0; 	rc = digital_target_found(ddev, target, NFC_PROTO_ISO14443); exit:	dev_kfree_skb(resp);	kfree(target); 	if (rc)		digital_poll_next_tech(ddev);} static int digital_in_send_rats(struct nfc_digital_dev *ddev,				struct nfc_target *target){	int rc;	struct sk_buff *skb; 	skb = digital_skb_alloc(ddev, 2);	if (!skb)		return -ENOMEM; 	skb_put_u8(skb, DIGITAL_RATS_BYTE1);	skb_put_u8(skb, DIGITAL_RATS_PARAM); 	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_ats,				 target);	if (rc)		kfree_skb(skb); 	return rc;} static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg,				    struct sk_buff *resp){	struct nfc_target *target = arg;	int rc;	u8 sel_res;	u8 nfc_proto; 	if (IS_ERR(resp)) {		rc = PTR_ERR(resp);		resp = NULL;		goto exit;	} 	if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) {		rc = digital_skb_check_crc_a(resp);		if (rc) {			PROTOCOL_ERR("4.4.1.3");			goto exit;		}	} 	if (resp->len != DIGITAL_SEL_RES_LEN) {		rc = -EIO;		goto exit;	} 	sel_res = resp->data[0]; 	if (!DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res)) {		rc = digital_in_send_sdd_req(ddev, target);		if (rc)			goto exit; 		goto exit_free_skb;	} 	target->sel_res = sel_res; 	if (DIGITAL_SEL_RES_IS_T2T(sel_res)) {		nfc_proto = NFC_PROTO_MIFARE;	} else if (DIGITAL_SEL_RES_IS_NFC_DEP(sel_res)) {		nfc_proto = NFC_PROTO_NFC_DEP;	} else if (DIGITAL_SEL_RES_IS_T4T(sel_res)) {		rc = digital_in_send_rats(ddev, target);		if (rc)			goto exit;		/*		 * Skip target_found and don't free it for now. This will be		 * done when receiving the ATS		 */		goto exit_free_skb;	} else {		rc = -EOPNOTSUPP;		goto exit;	} 	rc = digital_target_found(ddev, target, nfc_proto); exit:	kfree(target); exit_free_skb:	dev_kfree_skb(resp); 	if (rc)		digital_poll_next_tech(ddev);} static int digital_in_send_sel_req(struct nfc_digital_dev *ddev,				   struct nfc_target *target,				   struct digital_sdd_res *sdd_res){	struct sk_buff *skb;	struct digital_sel_req *sel_req;	u8 sel_cmd;	int rc; 	skb = digital_skb_alloc(ddev, sizeof(struct digital_sel_req));	if (!skb)		return -ENOMEM; 	skb_put(skb, sizeof(struct digital_sel_req));	sel_req = (struct digital_sel_req *)skb->data; 	if (target->nfcid1_len <= 4)		sel_cmd = DIGITAL_CMD_SEL_REQ_CL1;	else if (target->nfcid1_len < 10)		sel_cmd = DIGITAL_CMD_SEL_REQ_CL2;	else		sel_cmd = DIGITAL_CMD_SEL_REQ_CL3; 	sel_req->sel_cmd = sel_cmd;	sel_req->b2 = 0x70;	memcpy(sel_req->nfcid1, sdd_res->nfcid1, 4);	sel_req->bcc = sdd_res->bcc; 	if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) {		rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,				NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A);		if (rc)			goto exit;	} else {		digital_skb_add_crc_a(skb);	} 	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sel_res,				 target);exit:	if (rc)		kfree_skb(skb); 	return rc;} static void digital_in_recv_sdd_res(struct nfc_digital_dev *ddev, void *arg,				    struct sk_buff *resp){	struct nfc_target *target = arg;	struct digital_sdd_res *sdd_res;	int rc;	u8 offset, size;	u8 i, bcc; 	if (IS_ERR(resp)) {		rc = PTR_ERR(resp);		resp = NULL;		goto exit;	} 	if (resp->len < DIGITAL_SDD_RES_LEN) {		PROTOCOL_ERR("4.7.2.8");		rc = -EINVAL;		goto exit;	} 	sdd_res = (struct digital_sdd_res *)resp->data; 	for (i = 0, bcc = 0; i < 4; i++)		bcc ^= sdd_res->nfcid1[i]; 	if (bcc != sdd_res->bcc) {		PROTOCOL_ERR("4.7.2.6");		rc = -EINVAL;		goto exit;	} 	if (sdd_res->nfcid1[0] == DIGITAL_SDD_RES_CT) {		offset = 1;		size = 3;	} else {		offset = 0;		size = 4;	} 	if (target->nfcid1_len + size > NFC_NFCID1_MAXSIZE) {		PROTOCOL_ERR("4.7.2.1");		rc = -EPROTO;		goto exit;	} 	memcpy(target->nfcid1 + target->nfcid1_len, sdd_res->nfcid1 + offset,	       size);	target->nfcid1_len += size; 	rc = digital_in_send_sel_req(ddev, target, sdd_res); exit:	dev_kfree_skb(resp); 	if (rc) {		kfree(target);		digital_poll_next_tech(ddev);	}} static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev,				   struct nfc_target *target){	int rc;	struct sk_buff *skb;	u8 sel_cmd; 	rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,				     NFC_DIGITAL_FRAMING_NFCA_STANDARD);	if (rc)		return rc; 	skb = digital_skb_alloc(ddev, 2);	if (!skb)		return -ENOMEM; 	if (target->nfcid1_len == 0)		sel_cmd = DIGITAL_CMD_SEL_REQ_CL1;	else if (target->nfcid1_len == 3)		sel_cmd = DIGITAL_CMD_SEL_REQ_CL2;	else		sel_cmd = DIGITAL_CMD_SEL_REQ_CL3; 	skb_put_u8(skb, sel_cmd);	skb_put_u8(skb, DIGITAL_SDD_REQ_SEL_PAR); 	rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sdd_res,				 target);	if (rc)		kfree_skb(skb); 	return rc;} static void digital_in_recv_sens_res(struct nfc_digital_dev *ddev, void *arg,				     struct sk_buff *resp){	struct nfc_target *target = NULL;	int rc; 	if (IS_ERR(resp)) {		rc = PTR_ERR(resp);		resp = NULL;		goto exit;	} 	if (resp->len < sizeof(u16)) {		rc = -EIO;		goto exit;	} 	target = kzalloc_obj(struct nfc_target);	if (!target) {

Lines 1–500 of 1308.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t