diff options
Diffstat (limited to 'drivers/nfc/st21nfca')
-rw-r--r-- | drivers/nfc/st21nfca/Kconfig | 18 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/Makefile | 9 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/core.c | 1050 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/dep.c | 691 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/i2c.c | 635 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/se.c | 456 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/st21nfca.h | 212 | ||||
-rw-r--r-- | drivers/nfc/st21nfca/vendor_cmds.c | 375 |
8 files changed, 3446 insertions, 0 deletions
diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig new file mode 100644 index 000000000..cc3bd5658 --- /dev/null +++ b/drivers/nfc/st21nfca/Kconfig @@ -0,0 +1,18 @@ +config NFC_ST21NFCA + tristate + select CRC_CCITT + ---help--- + STMicroelectronics ST21NFCA core driver. It implements the chipset + HCI logic and hooks into the NFC kernel APIs. Physical layers will + register against it. + +config NFC_ST21NFCA_I2C + tristate "STMicroelectronics ST21NFCA NFC driver (I2C)" + depends on NFC_HCI && I2C && NFC_SHDLC + select NFC_ST21NFCA + ---help--- + This module adds support for the STMicroelectronics st21nfca i2c interface. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called st21nfca_i2c. + Say N if unsure. diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile new file mode 100644 index 000000000..ded6489c3 --- /dev/null +++ b/drivers/nfc/st21nfca/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for ST21NFCA HCI based NFC driver +# + +st21nfca_hci-objs = core.o dep.o se.o vendor_cmds.o +obj-$(CONFIG_NFC_ST21NFCA) += st21nfca_hci.o + +st21nfca_i2c-objs = i2c.o +obj-$(CONFIG_NFC_ST21NFCA_I2C) += st21nfca_i2c.o diff --git a/drivers/nfc/st21nfca/core.c b/drivers/nfc/st21nfca/core.c new file mode 100644 index 000000000..f37069b53 --- /dev/null +++ b/drivers/nfc/st21nfca/core.c @@ -0,0 +1,1050 @@ +/* + * HCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * 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 that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "st21nfca.h" + +#define DRIVER_DESC "HCI NFC driver for ST21NFCA" + +#define FULL_VERSION_LEN 3 + +/* Proprietary gates, events, commands and registers */ + +/* Commands that apply to all RF readers */ +#define ST21NFCA_RF_READER_CMD_PRESENCE_CHECK 0x30 + +#define ST21NFCA_RF_READER_ISO15693_GATE 0x12 +#define ST21NFCA_RF_READER_ISO15693_INVENTORY 0x01 + +/* + * Reader gate for communication with contact-less cards using Type A + * protocol ISO14443-3 but not compliant with ISO14443-4 + */ +#define ST21NFCA_RF_READER_14443_3_A_GATE 0x15 +#define ST21NFCA_RF_READER_14443_3_A_UID 0x02 +#define ST21NFCA_RF_READER_14443_3_A_ATQA 0x03 +#define ST21NFCA_RF_READER_14443_3_A_SAK 0x04 + +#define ST21NFCA_RF_READER_F_DATARATE 0x01 +#define ST21NFCA_RF_READER_F_DATARATE_106 0x01 +#define ST21NFCA_RF_READER_F_DATARATE_212 0x02 +#define ST21NFCA_RF_READER_F_DATARATE_424 0x04 +#define ST21NFCA_RF_READER_F_POL_REQ 0x02 +#define ST21NFCA_RF_READER_F_POL_REQ_DEFAULT 0xffff0000 +#define ST21NFCA_RF_READER_F_NFCID2 0x03 +#define ST21NFCA_RF_READER_F_NFCID1 0x04 + +#define ST21NFCA_RF_CARD_F_MODE 0x01 +#define ST21NFCA_RF_CARD_F_NFCID2_LIST 0x04 +#define ST21NFCA_RF_CARD_F_NFCID1 0x05 +#define ST21NFCA_RF_CARD_F_SENS_RES 0x06 +#define ST21NFCA_RF_CARD_F_SEL_RES 0x07 +#define ST21NFCA_RF_CARD_F_DATARATE 0x08 +#define ST21NFCA_RF_CARD_F_DATARATE_212_424 0x01 + +#define ST21NFCA_DEVICE_MGNT_PIPE 0x02 + +#define ST21NFCA_DM_GETINFO 0x13 +#define ST21NFCA_DM_GETINFO_PIPE_LIST 0x02 +#define ST21NFCA_DM_GETINFO_PIPE_INFO 0x01 +#define ST21NFCA_DM_PIPE_CREATED 0x02 +#define ST21NFCA_DM_PIPE_OPEN 0x04 +#define ST21NFCA_DM_RF_ACTIVE 0x80 +#define ST21NFCA_DM_DISCONNECT 0x30 + +#define ST21NFCA_DM_IS_PIPE_OPEN(p) \ + ((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN)) + +#define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/ + +#define ST21NFCA_EVT_HOT_PLUG 0x03 +#define ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80) + +#define ST21NFCA_SE_TO_PIPES 2000 + +static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES); + +static struct nfc_hci_gate st21nfca_gates[] = { + {NFC_HCI_ADMIN_GATE, NFC_HCI_ADMIN_PIPE}, + {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE}, + {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE}, + + {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_RF_CARD_F_GATE, NFC_HCI_INVALID_PIPE}, + + /* Secure element pipes are created by secure element host */ + {ST21NFCA_CONNECTIVITY_GATE, NFC_HCI_DO_NOT_CREATE_PIPE}, + {ST21NFCA_APDU_READER_GATE, NFC_HCI_DO_NOT_CREATE_PIPE}, +}; + +struct st21nfca_pipe_info { + u8 pipe_state; + u8 src_host_id; + u8 src_gate_id; + u8 dst_host_id; + u8 dst_gate_id; +} __packed; + +/* Largest headroom needed for outgoing custom commands */ +#define ST21NFCA_CMDS_HEADROOM 7 + +static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) +{ + int i, j, r; + struct sk_buff *skb_pipe_list, *skb_pipe_info; + struct st21nfca_pipe_info *info; + + u8 pipe_list[] = { ST21NFCA_DM_GETINFO_PIPE_LIST, + NFC_HCI_TERMINAL_HOST_ID + }; + u8 pipe_info[] = { ST21NFCA_DM_GETINFO_PIPE_INFO, + NFC_HCI_TERMINAL_HOST_ID, 0 + }; + + /* On ST21NFCA device pipes number are dynamics + * A maximum of 16 pipes can be created at the same time + * If pipes are already created, hci_dev_up will fail. + * Doing a clear all pipe is a bad idea because: + * - It does useless EEPROM cycling + * - It might cause issue for secure elements support + * (such as removing connectivity or APDU reader pipe) + * A better approach on ST21NFCA is to: + * - get a pipe list for each host. + * (eg: NFC_HCI_HOST_CONTROLLER_ID for now). + * (TODO Later on UICC HOST and eSE HOST) + * - get pipe information + * - match retrieved pipe list in st21nfca_gates + * ST21NFCA_DEVICE_MGNT_GATE is a proprietary gate + * with ST21NFCA_DEVICE_MGNT_PIPE. + * Pipe can be closed and need to be open. + */ + r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DEVICE_MGNT_PIPE); + if (r < 0) + return r; + + /* Get pipe list */ + r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DM_GETINFO, pipe_list, sizeof(pipe_list), + &skb_pipe_list); + if (r < 0) + return r; + + /* Complete the existing gate_pipe table */ + for (i = 0; i < skb_pipe_list->len; i++) { + pipe_info[2] = skb_pipe_list->data[i]; + r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DM_GETINFO, pipe_info, + sizeof(pipe_info), &skb_pipe_info); + if (r) + continue; + + /* + * Match pipe ID and gate ID + * Output format from ST21NFC_DM_GETINFO is: + * - pipe state (1byte) + * - source hid (1byte) + * - source gid (1byte) + * - destination hid (1byte) + * - destination gid (1byte) + */ + info = (struct st21nfca_pipe_info *) skb_pipe_info->data; + if (info->dst_gate_id == ST21NFCA_APDU_READER_GATE && + info->src_host_id == NFC_HCI_UICC_HOST_ID) { + pr_err("Unexpected apdu_reader pipe on host %x\n", + info->src_host_id); + kfree_skb(skb_pipe_info); + continue; + } + + for (j = 3; (j < ARRAY_SIZE(st21nfca_gates)) && + (st21nfca_gates[j].gate != info->dst_gate_id) ; j++) + ; + + if (j < ARRAY_SIZE(st21nfca_gates) && + st21nfca_gates[j].gate == info->dst_gate_id && + ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) { + hdev->init_data.gates[j].pipe = pipe_info[2]; + + hdev->gate2pipe[st21nfca_gates[j].gate] = + pipe_info[2]; + hdev->pipes[pipe_info[2]].gate = + st21nfca_gates[j].gate; + hdev->pipes[pipe_info[2]].dest_host = + info->src_host_id; + } + kfree_skb(skb_pipe_info); + } + + /* + * 3 gates have a well known pipe ID. Only NFC_HCI_LINK_MGMT_GATE + * is not yet open at this stage. + */ + r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, + NFC_HCI_LINK_MGMT_GATE, + NFC_HCI_LINK_MGMT_PIPE); + + kfree_skb(skb_pipe_list); + return r; +} + +static int st21nfca_hci_open(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + int r; + + mutex_lock(&info->info_lock); + + if (info->state != ST21NFCA_ST_COLD) { + r = -EBUSY; + goto out; + } + + r = info->phy_ops->enable(info->phy_id); + + if (r == 0) + info->state = ST21NFCA_ST_READY; + +out: + mutex_unlock(&info->info_lock); + return r; +} + +static void st21nfca_hci_close(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + mutex_lock(&info->info_lock); + + if (info->state == ST21NFCA_ST_COLD) + goto out; + + info->phy_ops->disable(info->phy_id); + info->state = ST21NFCA_ST_COLD; + +out: + mutex_unlock(&info->info_lock); +} + +static int st21nfca_hci_ready(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + struct sk_buff *skb; + + u8 param; + u8 white_list[2]; + int wl_size = 0; + int r; + + if (info->se_status->is_uicc_present) + white_list[wl_size++] = NFC_HCI_UICC_HOST_ID; + if (info->se_status->is_ese_present) + white_list[wl_size++] = ST21NFCA_ESE_HOST_ID; + + if (wl_size) { + r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_WHITELIST, + (u8 *) &white_list, wl_size); + if (r < 0) + return r; + } + + /* Set NFC_MODE in device management gate to enable */ + r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_NFC_MODE, &skb); + if (r < 0) + return r; + + param = skb->data[0]; + kfree_skb(skb); + if (param == 0) { + param = 1; + + r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_NFC_MODE, ¶m, 1); + if (r < 0) + return r; + } + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + + r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, + NFC_HCI_ID_MGMT_VERSION_SW, &skb); + if (r < 0) + return r; + + if (skb->len != FULL_VERSION_LEN) { + kfree_skb(skb); + return -EINVAL; + } + + print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ", + DUMP_PREFIX_NONE, 16, 1, + skb->data, FULL_VERSION_LEN, false); + + kfree_skb(skb); + + return 0; +} + +static int st21nfca_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + return info->phy_ops->write(info->phy_id, skb); +} + +static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols) +{ + int r; + u32 pol_req; + u8 param[19]; + struct sk_buff *datarate_skb; + + pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n", + __func__, im_protocols, tm_protocols); + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + if (im_protocols) { + /* + * enable polling according to im_protocols & tm_protocols + * - CLOSE pipe according to im_protocols & tm_protocols + */ + if ((NFC_HCI_RF_READER_B_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + NFC_HCI_RF_READER_B_GATE); + if (r < 0) + return r; + } + + if ((NFC_HCI_RF_READER_A_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + NFC_HCI_RF_READER_A_GATE); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_F_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_F_GATE); + if (r < 0) + return r; + } else { + hdev->gb = nfc_get_local_general_bytes(hdev->ndev, + &hdev->gb_len); + + if (hdev->gb == NULL || hdev->gb_len == 0) { + im_protocols &= ~NFC_PROTO_NFC_DEP_MASK; + tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK; + } + + param[0] = ST21NFCA_RF_READER_F_DATARATE_106 | + ST21NFCA_RF_READER_F_DATARATE_212 | + ST21NFCA_RF_READER_F_DATARATE_424; + r = nfc_hci_set_param(hdev, ST21NFCA_RF_READER_F_GATE, + ST21NFCA_RF_READER_F_DATARATE, + param, 1); + if (r < 0) + return r; + + pol_req = be32_to_cpu((__force __be32) + ST21NFCA_RF_READER_F_POL_REQ_DEFAULT); + r = nfc_hci_set_param(hdev, ST21NFCA_RF_READER_F_GATE, + ST21NFCA_RF_READER_F_POL_REQ, + (u8 *) &pol_req, 4); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_14443_3_A_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_14443_3_A_GATE); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_ISO15693_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_ISO15693_GATE); + if (r < 0) + return r; + } + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + if (r < 0) + nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + } + + if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_get_param(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_RF_CARD_F_DATARATE, + &datarate_skb); + if (r < 0) + return r; + + /* Configure the maximum supported datarate to 424Kbps */ + if (datarate_skb->len > 0 && + datarate_skb->data[0] != + ST21NFCA_RF_CARD_F_DATARATE_212_424) { + param[0] = ST21NFCA_RF_CARD_F_DATARATE_212_424; + r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_RF_CARD_F_DATARATE, + param, 1); + if (r < 0) { + kfree_skb(datarate_skb); + return r; + } + } + kfree_skb(datarate_skb); + + /* + * Configure sens_res + * + * NFC Forum Digital Spec Table 7: + * NFCID1 size: triple (10 bytes) + */ + param[0] = 0x00; + param[1] = 0x08; + r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_RF_CARD_F_SENS_RES, param, 2); + if (r < 0) + return r; + + /* + * Configure sel_res + * + * NFC Forum Digistal Spec Table 17: + * b3 set to 0b (value b7-b6): + * - 10b: Configured for NFC-DEP Protocol + */ + param[0] = 0x40; + r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_RF_CARD_F_SEL_RES, param, 1); + if (r < 0) + return r; + + /* Configure NFCID1 Random uid */ + r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_RF_CARD_F_NFCID1, NULL, 0); + if (r < 0) + return r; + + /* Configure NFCID2_LIST */ + /* System Code */ + param[0] = 0x00; + param[1] = 0x00; + /* NFCID2 */ + param[2] = 0x01; + param[3] = 0xfe; + param[4] = 'S'; + param[5] = 'T'; + param[6] = 'M'; + param[7] = 'i'; + param[8] = 'c'; + param[9] = 'r'; + /* 8 byte Pad bytes used for polling respone frame */ + + /* + * Configuration byte: + * - bit 0: define the default NFCID2 entry used when the + * system code is equal to 'FFFF' + * - bit 1: use a random value for lowest 6 bytes of + * NFCID2 value + * - bit 2: ignore polling request frame if request code + * is equal to '01' + * - Other bits are RFU + */ + param[18] = 0x01; + r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_RF_CARD_F_NFCID2_LIST, param, + 19); + if (r < 0) + return r; + + param[0] = 0x02; + r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_RF_CARD_F_MODE, param, 1); + } + + return r; +} + +static void st21nfca_hci_stop_poll(struct nfc_hci_dev *hdev) +{ + nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DM_DISCONNECT, NULL, 0, NULL); +} + +static int st21nfca_get_iso14443_3_atqa(struct nfc_hci_dev *hdev, u16 *atqa) +{ + int r; + struct sk_buff *atqa_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_ATQA, &atqa_skb); + if (r < 0) + goto exit; + + if (atqa_skb->len != 2) { + r = -EPROTO; + goto exit; + } + + *atqa = be16_to_cpu(*(__be16 *) atqa_skb->data); + +exit: + kfree_skb(atqa_skb); + return r; +} + +static int st21nfca_get_iso14443_3_sak(struct nfc_hci_dev *hdev, u8 *sak) +{ + int r; + struct sk_buff *sak_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_SAK, &sak_skb); + if (r < 0) + goto exit; + + if (sak_skb->len != 1) { + r = -EPROTO; + goto exit; + } + + *sak = sak_skb->data[0]; + +exit: + kfree_skb(sak_skb); + return r; +} + +static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *uid, + int *len) +{ + int r; + struct sk_buff *uid_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_UID, &uid_skb); + if (r < 0) + goto exit; + + if (uid_skb->len == 0 || uid_skb->len > NFC_NFCID1_MAXSIZE) { + r = -EPROTO; + goto exit; + } + + memcpy(uid, uid_skb->data, uid_skb->len); + *len = uid_skb->len; +exit: + kfree_skb(uid_skb); + return r; +} + +static int st21nfca_get_iso15693_inventory(struct nfc_hci_dev *hdev, + struct nfc_target *target) +{ + int r; + struct sk_buff *inventory_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_ISO15693_GATE, + ST21NFCA_RF_READER_ISO15693_INVENTORY, + &inventory_skb); + if (r < 0) + goto exit; + + skb_pull(inventory_skb, 2); + + if (inventory_skb->len == 0 || + inventory_skb->len > NFC_ISO15693_UID_MAXSIZE) { + r = -EPROTO; + goto exit; + } + + memcpy(target->iso15693_uid, inventory_skb->data, inventory_skb->len); + target->iso15693_dsfid = inventory_skb->data[1]; + target->is_iso15693 = 1; +exit: + kfree_skb(inventory_skb); + return r; +} + +static int st21nfca_hci_dep_link_up(struct nfc_hci_dev *hdev, + struct nfc_target *target, u8 comm_mode, + u8 *gb, size_t gb_len) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + info->dep_info.idx = target->idx; + return st21nfca_im_send_atr_req(hdev, gb, gb_len); +} + +static int st21nfca_hci_dep_link_down(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + info->state = ST21NFCA_ST_READY; + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DM_DISCONNECT, NULL, 0, NULL); +} + +static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, + struct nfc_target *target) +{ + int r, len; + u16 atqa; + u8 sak; + u8 uid[NFC_NFCID1_MAXSIZE]; + + switch (gate) { + case ST21NFCA_RF_READER_F_GATE: + target->supported_protocols = NFC_PROTO_FELICA_MASK; + break; + case ST21NFCA_RF_READER_14443_3_A_GATE: + /* ISO14443-3 type 1 or 2 tags */ + r = st21nfca_get_iso14443_3_atqa(hdev, &atqa); + if (r < 0) + return r; + if (atqa == 0x000c) { + target->supported_protocols = NFC_PROTO_JEWEL_MASK; + target->sens_res = 0x0c00; + } else { + r = st21nfca_get_iso14443_3_sak(hdev, &sak); + if (r < 0) + return r; + + r = st21nfca_get_iso14443_3_uid(hdev, uid, &len); + if (r < 0) + return r; + + target->supported_protocols = + nfc_hci_sak_to_protocol(sak); + if (target->supported_protocols == 0xffffffff) + return -EPROTO; + + target->sens_res = atqa; + target->sel_res = sak; + memcpy(target->nfcid1, uid, len); + target->nfcid1_len = len; + } + + break; + case ST21NFCA_RF_READER_ISO15693_GATE: + target->supported_protocols = NFC_PROTO_ISO15693_MASK; + r = st21nfca_get_iso15693_inventory(hdev, target); + if (r < 0) + return r; + break; + default: + return -EPROTO; + } + + return 0; +} + +static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev, + u8 gate, + struct nfc_target *target) +{ + int r; + struct sk_buff *nfcid_skb = NULL; + + if (gate == ST21NFCA_RF_READER_F_GATE) { + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE, + ST21NFCA_RF_READER_F_NFCID2, &nfcid_skb); + if (r < 0) + goto exit; + + if (nfcid_skb->len > NFC_SENSF_RES_MAXSIZE) { + r = -EPROTO; + goto exit; + } + + /* + * - After the recepton of polling response for type F frame + * at 212 or 424 Kbit/s, NFCID2 registry parameters will be + * updated. + * - After the reception of SEL_RES with NFCIP-1 compliant bit + * set for type A frame NFCID1 will be updated + */ + if (nfcid_skb->len > 0) { + /* P2P in type F */ + memcpy(target->sensf_res, nfcid_skb->data, + nfcid_skb->len); + target->sensf_res_len = nfcid_skb->len; + /* NFC Forum Digital Protocol Table 44 */ + if (target->sensf_res[0] == 0x01 && + target->sensf_res[1] == 0xfe) + target->supported_protocols = + NFC_PROTO_NFC_DEP_MASK; + else + target->supported_protocols = + NFC_PROTO_FELICA_MASK; + } else { + kfree_skb(nfcid_skb); + nfcid_skb = NULL; + /* P2P in type A */ + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE, + ST21NFCA_RF_READER_F_NFCID1, + &nfcid_skb); + if (r < 0) + goto exit; + + if (nfcid_skb->len > NFC_NFCID1_MAXSIZE) { + r = -EPROTO; + goto exit; + } + memcpy(target->sensf_res, nfcid_skb->data, + nfcid_skb->len); + target->sensf_res_len = nfcid_skb->len; + target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + } + target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE; + } + r = 1; +exit: + kfree_skb(nfcid_skb); + return r; +} + +#define ST21NFCA_CB_TYPE_READER_ISO15693 1 +static void st21nfca_hci_data_exchange_cb(void *context, struct sk_buff *skb, + int err) +{ + struct st21nfca_hci_info *info = context; + + switch (info->async_cb_type) { + case ST21NFCA_CB_TYPE_READER_ISO15693: + if (err == 0) + skb_trim(skb, skb->len - 1); + info->async_cb(info->async_cb_context, skb, err); + break; + default: + if (err == 0) + kfree_skb(skb); + break; + } +} + +/* + * Returns: + * <= 0: driver handled the data exchange + * 1: driver doesn't especially handle, please do standard processing + */ +static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev, + struct nfc_target *target, + struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_info(DRIVER_DESC ": %s for gate=%d len=%d\n", __func__, + target->hci_reader_gate, skb->len); + + switch (target->hci_reader_gate) { + case ST21NFCA_RF_READER_F_GATE: + if (target->supported_protocols == NFC_PROTO_NFC_DEP_MASK) + return st21nfca_im_send_dep_req(hdev, skb); + + *(u8 *)skb_push(skb, 1) = 0x1a; + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, cb, cb_context); + case ST21NFCA_RF_READER_14443_3_A_GATE: + *(u8 *)skb_push(skb, 1) = 0x1a; /* CTR, see spec:10.2.2.1 */ + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, cb, cb_context); + case ST21NFCA_RF_READER_ISO15693_GATE: + info->async_cb_type = ST21NFCA_CB_TYPE_READER_ISO15693; + info->async_cb = cb; + info->async_cb_context = cb_context; + + *(u8 *)skb_push(skb, 1) = 0x17; + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, + st21nfca_hci_data_exchange_cb, + info); + break; + default: + return 1; + } +} + +static int st21nfca_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + return st21nfca_tm_send_dep_res(hdev, skb); +} + +static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev, + struct nfc_target *target) +{ + u8 fwi = 0x11; + + switch (target->hci_reader_gate) { + case NFC_HCI_RF_READER_A_GATE: + case NFC_HCI_RF_READER_B_GATE: + /* + * PRESENCE_CHECK on those gates is available + * However, the answer to this command is taking 3 * fwi + * if the card is no present. + * Instead, we send an empty I-Frame with a very short + * configurable fwi ~604µs. + */ + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, &fwi, 1, NULL); + case ST21NFCA_RF_READER_14443_3_A_GATE: + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + ST21NFCA_RF_READER_CMD_PRESENCE_CHECK, + NULL, 0, NULL); + default: + return -EOPNOTSUPP; + } +} + +static void st21nfca_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, + struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + u8 gate = hdev->pipes[pipe].gate; + + pr_debug("cmd: %x\n", cmd); + + switch (cmd) { + case NFC_HCI_ANY_OPEN_PIPE: + if (gate != ST21NFCA_APDU_READER_GATE && + hdev->pipes[pipe].dest_host != NFC_HCI_UICC_HOST_ID) + info->se_info.count_pipes++; + + if (info->se_info.count_pipes == info->se_info.expected_pipes) { + del_timer_sync(&info->se_info.se_active_timer); + info->se_info.se_active = false; + info->se_info.count_pipes = 0; + complete(&info->se_info.req_completion); + } + break; + } +} + +static int st21nfca_admin_event_received(struct nfc_hci_dev *hdev, u8 event, + struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_debug("admin event: %x\n", event); + + switch (event) { + case ST21NFCA_EVT_HOT_PLUG: + if (info->se_info.se_active) { + if (!ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(skb)) { + del_timer_sync(&info->se_info.se_active_timer); + info->se_info.se_active = false; + complete(&info->se_info.req_completion); + } else { + mod_timer(&info->se_info.se_active_timer, + jiffies + + msecs_to_jiffies(ST21NFCA_SE_TO_PIPES)); + } + } + break; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on admin gate\n"); + } + kfree_skb(skb); + return 0; +} + +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ +static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, + u8 event, struct sk_buff *skb) +{ + u8 gate = hdev->pipes[pipe].gate; + u8 host = hdev->pipes[pipe].dest_host; + + pr_debug("hci event: %d gate: %x\n", event, gate); + + switch (gate) { + case NFC_HCI_ADMIN_GATE: + return st21nfca_admin_event_received(hdev, event, skb); + case ST21NFCA_RF_CARD_F_GATE: + return st21nfca_dep_event_received(hdev, event, skb); + case ST21NFCA_CONNECTIVITY_GATE: + return st21nfca_connectivity_event_received(hdev, host, + event, skb); + case ST21NFCA_APDU_READER_GATE: + return st21nfca_apdu_reader_event_received(hdev, event, skb); + case NFC_HCI_LOOPBACK_GATE: + return st21nfca_hci_loopback_event_received(hdev, event, skb); + default: + return 1; + } +} + +static struct nfc_hci_ops st21nfca_hci_ops = { + .open = st21nfca_hci_open, + .close = st21nfca_hci_close, + .load_session = st21nfca_hci_load_session, + .hci_ready = st21nfca_hci_ready, + .xmit = st21nfca_hci_xmit, + .start_poll = st21nfca_hci_start_poll, + .stop_poll = st21nfca_hci_stop_poll, + .dep_link_up = st21nfca_hci_dep_link_up, + .dep_link_down = st21nfca_hci_dep_link_down, + .target_from_gate = st21nfca_hci_target_from_gate, + .complete_target_discovered = st21nfca_hci_complete_target_discovered, + .im_transceive = st21nfca_hci_im_transceive, + .tm_send = st21nfca_hci_tm_send, + .check_presence = st21nfca_hci_check_presence, + .event_received = st21nfca_hci_event_received, + .cmd_received = st21nfca_hci_cmd_received, + .discover_se = st21nfca_hci_discover_se, + .enable_se = st21nfca_hci_enable_se, + .disable_se = st21nfca_hci_disable_se, + .se_io = st21nfca_hci_se_io, +}; + +int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, + char *llc_name, int phy_headroom, int phy_tailroom, + int phy_payload, struct nfc_hci_dev **hdev, + struct st21nfca_se_status *se_status) +{ + struct st21nfca_hci_info *info; + int r = 0; + int dev_num; + u32 protocols; + struct nfc_hci_init_data init_data; + unsigned long quirks = 0; + + info = kzalloc(sizeof(struct st21nfca_hci_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->phy_ops = phy_ops; + info->phy_id = phy_id; + info->state = ST21NFCA_ST_COLD; + mutex_init(&info->info_lock); + + init_data.gate_count = ARRAY_SIZE(st21nfca_gates); + + memcpy(init_data.gates, st21nfca_gates, sizeof(st21nfca_gates)); + + /* + * Session id must include the driver name + i2c bus addr + * persistent info to discriminate 2 identical chips + */ + dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES); + if (dev_num >= ST21NFCA_NUM_DEVICES) { + r = -ENODEV; + goto err_alloc_hdev; + } + + set_bit(dev_num, dev_mask); + + scnprintf(init_data.session_id, sizeof(init_data.session_id), "%s%2x", + "ST21AH", dev_num); + + protocols = NFC_PROTO_JEWEL_MASK | + NFC_PROTO_MIFARE_MASK | + NFC_PROTO_FELICA_MASK | + NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK | + NFC_PROTO_ISO15693_MASK | + NFC_PROTO_NFC_DEP_MASK; + + set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks); + + info->hdev = + nfc_hci_allocate_device(&st21nfca_hci_ops, &init_data, quirks, + protocols, llc_name, + phy_headroom + ST21NFCA_CMDS_HEADROOM, + phy_tailroom, phy_payload); + + if (!info->hdev) { + pr_err("Cannot allocate nfc hdev.\n"); + r = -ENOMEM; + goto err_alloc_hdev; + } + + info->se_status = se_status; + + nfc_hci_set_clientdata(info->hdev, info); + + r = nfc_hci_register_device(info->hdev); + if (r) + goto err_regdev; + + *hdev = info->hdev; + st21nfca_dep_init(info->hdev); + st21nfca_se_init(info->hdev); + st21nfca_vendor_cmds_init(info->hdev); + + return 0; + +err_regdev: + nfc_hci_free_device(info->hdev); + +err_alloc_hdev: + kfree(info); + + return r; +} +EXPORT_SYMBOL(st21nfca_hci_probe); + +void st21nfca_hci_remove(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + st21nfca_dep_deinit(hdev); + st21nfca_se_deinit(hdev); + nfc_hci_unregister_device(hdev); + nfc_hci_free_device(hdev); + kfree(info); +} +EXPORT_SYMBOL(st21nfca_hci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfca/dep.c b/drivers/nfc/st21nfca/dep.c new file mode 100644 index 000000000..7399eb2c5 --- /dev/null +++ b/drivers/nfc/st21nfca/dep.c @@ -0,0 +1,691 @@ +/* + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * 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 that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <net/nfc/hci.h> + +#include "st21nfca.h" + +#define ST21NFCA_NFCIP1_INITIATOR 0x00 +#define ST21NFCA_NFCIP1_REQ 0xd4 +#define ST21NFCA_NFCIP1_RES 0xd5 +#define ST21NFCA_NFCIP1_ATR_REQ 0x00 +#define ST21NFCA_NFCIP1_ATR_RES 0x01 +#define ST21NFCA_NFCIP1_PSL_REQ 0x04 +#define ST21NFCA_NFCIP1_PSL_RES 0x05 +#define ST21NFCA_NFCIP1_DEP_REQ 0x06 +#define ST21NFCA_NFCIP1_DEP_RES 0x07 + +#define ST21NFCA_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03) +#define ST21NFCA_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0) +#define ST21NFCA_NFC_DEP_PFB_IS_TIMEOUT(pfb) \ + ((pfb) & ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT) +#define ST21NFCA_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04) +#define ST21NFCA_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08) +#define ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT 0x10 + +#define ST21NFCA_NFC_DEP_PFB_IS_TIMEOUT(pfb) \ + ((pfb) & ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT) + +#define ST21NFCA_NFC_DEP_PFB_I_PDU 0x00 +#define ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU 0x40 +#define ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU 0x80 + +#define ST21NFCA_ATR_REQ_MIN_SIZE 17 +#define ST21NFCA_ATR_REQ_MAX_SIZE 65 +#define ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B 0x30 +#define ST21NFCA_GB_BIT 0x02 + +#define ST21NFCA_EVT_SEND_DATA 0x10 +#define ST21NFCA_EVT_FIELD_ON 0x11 +#define ST21NFCA_EVT_CARD_DEACTIVATED 0x12 +#define ST21NFCA_EVT_CARD_ACTIVATED 0x13 +#define ST21NFCA_EVT_FIELD_OFF 0x14 + +#define ST21NFCA_EVT_CARD_F_BITRATE 0x16 +#define ST21NFCA_EVT_READER_F_BITRATE 0x13 +#define ST21NFCA_PSL_REQ_SEND_SPEED(brs) (brs & 0x38) +#define ST21NFCA_PSL_REQ_RECV_SPEED(brs) (brs & 0x07) +#define ST21NFCA_PP2LRI(pp) ((pp & 0x30) >> 4) +#define ST21NFCA_CARD_BITRATE_212 0x01 +#define ST21NFCA_CARD_BITRATE_424 0x02 + +#define ST21NFCA_DEFAULT_TIMEOUT 0x0a + + +#define PROTOCOL_ERR(req) pr_err("%d: ST21NFCA Protocol error: %s\n", \ + __LINE__, req) + +struct st21nfca_atr_req { + u8 length; + u8 cmd0; + u8 cmd1; + u8 nfcid3[NFC_NFCID3_MAXSIZE]; + u8 did; + u8 bsi; + u8 bri; + u8 ppi; + u8 gbi[0]; +} __packed; + +struct st21nfca_atr_res { + u8 length; + u8 cmd0; + u8 cmd1; + u8 nfcid3[NFC_NFCID3_MAXSIZE]; + u8 did; + u8 bsi; + u8 bri; + u8 to; + u8 ppi; + u8 gbi[0]; +} __packed; + +struct st21nfca_psl_req { + u8 length; + u8 cmd0; + u8 cmd1; + u8 did; + u8 brs; + u8 fsl; +} __packed; + +struct st21nfca_psl_res { + u8 length; + u8 cmd0; + u8 cmd1; + u8 did; +} __packed; + +struct st21nfca_dep_req_res { + u8 length; + u8 cmd0; + u8 cmd1; + u8 pfb; + u8 did; + u8 nad; +} __packed; + +static void st21nfca_tx_work(struct work_struct *work) +{ + struct st21nfca_hci_info *info = container_of(work, + struct st21nfca_hci_info, + dep_info.tx_work); + + struct nfc_dev *dev; + struct sk_buff *skb; + + if (info) { + dev = info->hdev->ndev; + skb = info->dep_info.tx_pending; + + device_lock(&dev->dev); + + nfc_hci_send_cmd_async(info->hdev, ST21NFCA_RF_READER_F_GATE, + ST21NFCA_WR_XCHG_DATA, skb->data, skb->len, + info->async_cb, info); + device_unlock(&dev->dev); + kfree_skb(skb); + } +} + +static void st21nfca_im_send_pdu(struct st21nfca_hci_info *info, + struct sk_buff *skb) +{ + info->dep_info.tx_pending = skb; + schedule_work(&info->dep_info.tx_work); +} + +static int st21nfca_tm_send_atr_res(struct nfc_hci_dev *hdev, + struct st21nfca_atr_req *atr_req) +{ + struct st21nfca_atr_res *atr_res; + struct sk_buff *skb; + size_t gb_len; + int r; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + gb_len = atr_req->length - sizeof(struct st21nfca_atr_req); + skb = alloc_skb(atr_req->length + 1, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(struct st21nfca_atr_res)); + + atr_res = (struct st21nfca_atr_res *)skb->data; + memset(atr_res, 0, sizeof(struct st21nfca_atr_res)); + + atr_res->length = atr_req->length + 1; + atr_res->cmd0 = ST21NFCA_NFCIP1_RES; + atr_res->cmd1 = ST21NFCA_NFCIP1_ATR_RES; + + memcpy(atr_res->nfcid3, atr_req->nfcid3, 6); + atr_res->bsi = 0x00; + atr_res->bri = 0x00; + atr_res->to = ST21NFCA_DEFAULT_TIMEOUT; + atr_res->ppi = ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B; + + if (gb_len) { + skb_put(skb, gb_len); + + atr_res->ppi |= ST21NFCA_GB_BIT; + memcpy(atr_res->gbi, atr_req->gbi, gb_len); + r = nfc_set_remote_general_bytes(hdev->ndev, atr_res->gbi, + gb_len); + if (r < 0) { + kfree_skb(skb); + return r; + } + } + + info->dep_info.curr_nfc_dep_pni = 0; + + r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_EVT_SEND_DATA, skb->data, skb->len); + kfree_skb(skb); + return r; +} + +static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev, + struct sk_buff *skb) +{ + struct st21nfca_atr_req *atr_req; + size_t gb_len; + int r; + + skb_trim(skb, skb->len - 1); + + if (!skb->len) { + r = -EIO; + goto exit; + } + + if (skb->len < ST21NFCA_ATR_REQ_MIN_SIZE) { + r = -EPROTO; + goto exit; + } + + atr_req = (struct st21nfca_atr_req *)skb->data; + + if (atr_req->length < sizeof(struct st21nfca_atr_req)) { + r = -EPROTO; + goto exit; + } + + r = st21nfca_tm_send_atr_res(hdev, atr_req); + if (r) + goto exit; + + gb_len = skb->len - sizeof(struct st21nfca_atr_req); + + r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, atr_req->gbi, gb_len); + if (r) + goto exit; + + r = 0; + +exit: + return r; +} + +static int st21nfca_tm_send_psl_res(struct nfc_hci_dev *hdev, + struct st21nfca_psl_req *psl_req) +{ + struct st21nfca_psl_res *psl_res; + struct sk_buff *skb; + u8 bitrate[2] = {0, 0}; + int r; + + skb = alloc_skb(sizeof(struct st21nfca_psl_res), GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_put(skb, sizeof(struct st21nfca_psl_res)); + + psl_res = (struct st21nfca_psl_res *)skb->data; + + psl_res->length = sizeof(struct st21nfca_psl_res); + psl_res->cmd0 = ST21NFCA_NFCIP1_RES; + psl_res->cmd1 = ST21NFCA_NFCIP1_PSL_RES; + psl_res->did = psl_req->did; + + r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_EVT_SEND_DATA, skb->data, skb->len); + if (r < 0) + goto error; + + /* + * ST21NFCA only support P2P passive. + * PSL_REQ BRS value != 0 has only a meaning to + * change technology to type F. + * We change to BITRATE 424Kbits. + * In other case switch to BITRATE 106Kbits. + */ + if (ST21NFCA_PSL_REQ_SEND_SPEED(psl_req->brs) && + ST21NFCA_PSL_REQ_RECV_SPEED(psl_req->brs)) { + bitrate[0] = ST21NFCA_CARD_BITRATE_424; + bitrate[1] = ST21NFCA_CARD_BITRATE_424; + } + + /* Send an event to change bitrate change event to card f */ + r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_EVT_CARD_F_BITRATE, bitrate, 2); +error: + kfree_skb(skb); + return r; +} + +static int st21nfca_tm_recv_psl_req(struct nfc_hci_dev *hdev, + struct sk_buff *skb) +{ + struct st21nfca_psl_req *psl_req; + int r; + + skb_trim(skb, skb->len - 1); + + if (!skb->len) { + r = -EIO; + goto exit; + } + + psl_req = (struct st21nfca_psl_req *)skb->data; + + if (skb->len < sizeof(struct st21nfca_psl_req)) { + r = -EIO; + goto exit; + } + + r = st21nfca_tm_send_psl_res(hdev, psl_req); +exit: + return r; +} + +int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + int r; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + *(u8 *)skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni; + *(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_RES; + *(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_RES; + *(u8 *)skb_push(skb, 1) = skb->len; + + r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE, + ST21NFCA_EVT_SEND_DATA, skb->data, skb->len); + kfree_skb(skb); + + return r; +} +EXPORT_SYMBOL(st21nfca_tm_send_dep_res); + +static int st21nfca_tm_recv_dep_req(struct nfc_hci_dev *hdev, + struct sk_buff *skb) +{ + struct st21nfca_dep_req_res *dep_req; + u8 size; + int r; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + skb_trim(skb, skb->len - 1); + + size = 4; + + dep_req = (struct st21nfca_dep_req_res *)skb->data; + if (skb->len < size) { + r = -EIO; + goto exit; + } + + if (ST21NFCA_NFC_DEP_DID_BIT_SET(dep_req->pfb)) + size++; + if (ST21NFCA_NFC_DEP_NAD_BIT_SET(dep_req->pfb)) + size++; + + if (skb->len < size) { + r = -EIO; + goto exit; + } + + /* Receiving DEP_REQ - Decoding */ + switch (ST21NFCA_NFC_DEP_PFB_TYPE(dep_req->pfb)) { + case ST21NFCA_NFC_DEP_PFB_I_PDU: + info->dep_info.curr_nfc_dep_pni = + ST21NFCA_NFC_DEP_PFB_PNI(dep_req->pfb); + break; + case ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU: + pr_err("Received a ACK/NACK PDU\n"); + break; + case ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU: + pr_err("Received a SUPERVISOR PDU\n"); + break; + } + + skb_pull(skb, size); + + return nfc_tm_data_received(hdev->ndev, skb); +exit: + return r; +} + +static int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, + struct sk_buff *skb) +{ + u8 cmd0, cmd1; + int r; + + cmd0 = skb->data[1]; + switch (cmd0) { + case ST21NFCA_NFCIP1_REQ: + cmd1 = skb->data[2]; + switch (cmd1) { + case ST21NFCA_NFCIP1_ATR_REQ: + r = st21nfca_tm_recv_atr_req(hdev, skb); + break; + case ST21NFCA_NFCIP1_PSL_REQ: + r = st21nfca_tm_recv_psl_req(hdev, skb); + break; + case ST21NFCA_NFCIP1_DEP_REQ: + r = st21nfca_tm_recv_dep_req(hdev, skb); + break; + default: + return 1; + } + default: + return 1; + } + return r; +} + +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ +int st21nfca_dep_event_received(struct nfc_hci_dev *hdev, + u8 event, struct sk_buff *skb) +{ + int r = 0; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_debug("dep event: %d\n", event); + + switch (event) { + case ST21NFCA_EVT_CARD_ACTIVATED: + info->dep_info.curr_nfc_dep_pni = 0; + break; + case ST21NFCA_EVT_CARD_DEACTIVATED: + break; + case ST21NFCA_EVT_FIELD_ON: + break; + case ST21NFCA_EVT_FIELD_OFF: + break; + case ST21NFCA_EVT_SEND_DATA: + r = st21nfca_tm_event_send_data(hdev, skb); + if (r < 0) + return r; + return 0; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on card f gate\n"); + return 1; + } + kfree_skb(skb); + return r; +} +EXPORT_SYMBOL(st21nfca_dep_event_received); + +static void st21nfca_im_send_psl_req(struct nfc_hci_dev *hdev, u8 did, u8 bsi, + u8 bri, u8 lri) +{ + struct sk_buff *skb; + struct st21nfca_psl_req *psl_req; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + skb = + alloc_skb(sizeof(struct st21nfca_psl_req) + 1, GFP_KERNEL); + if (!skb) + return; + skb_reserve(skb, 1); + + skb_put(skb, sizeof(struct st21nfca_psl_req)); + psl_req = (struct st21nfca_psl_req *) skb->data; + + psl_req->length = sizeof(struct st21nfca_psl_req); + psl_req->cmd0 = ST21NFCA_NFCIP1_REQ; + psl_req->cmd1 = ST21NFCA_NFCIP1_PSL_REQ; + psl_req->did = did; + psl_req->brs = (0x30 & bsi << 4) | (bri & 0x03); + psl_req->fsl = lri; + + *(u8 *)skb_push(skb, 1) = info->dep_info.to | 0x10; + + st21nfca_im_send_pdu(info, skb); +} + +#define ST21NFCA_CB_TYPE_READER_F 1 +static void st21nfca_im_recv_atr_res_cb(void *context, struct sk_buff *skb, + int err) +{ + struct st21nfca_hci_info *info = context; + struct st21nfca_atr_res *atr_res; + int r; + + if (err != 0) + return; + + if (!skb) + return; + + switch (info->async_cb_type) { + case ST21NFCA_CB_TYPE_READER_F: + skb_trim(skb, skb->len - 1); + atr_res = (struct st21nfca_atr_res *)skb->data; + r = nfc_set_remote_general_bytes(info->hdev->ndev, + atr_res->gbi, + skb->len - sizeof(struct st21nfca_atr_res)); + if (r < 0) + return; + + if (atr_res->to >= 0x0e) + info->dep_info.to = 0x0e; + else + info->dep_info.to = atr_res->to + 1; + + info->dep_info.to |= 0x10; + + r = nfc_dep_link_is_up(info->hdev->ndev, info->dep_info.idx, + NFC_COMM_PASSIVE, NFC_RF_INITIATOR); + if (r < 0) + return; + + info->dep_info.curr_nfc_dep_pni = 0; + if (ST21NFCA_PP2LRI(atr_res->ppi) != info->dep_info.lri) + st21nfca_im_send_psl_req(info->hdev, atr_res->did, + atr_res->bsi, atr_res->bri, + ST21NFCA_PP2LRI(atr_res->ppi)); + break; + default: + kfree_skb(skb); + break; + } +} + +int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len) +{ + struct sk_buff *skb; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + struct st21nfca_atr_req *atr_req; + struct nfc_target *target; + uint size; + + info->dep_info.to = ST21NFCA_DEFAULT_TIMEOUT; + size = ST21NFCA_ATR_REQ_MIN_SIZE + gb_len; + if (size > ST21NFCA_ATR_REQ_MAX_SIZE) { + PROTOCOL_ERR("14.6.1.1"); + return -EINVAL; + } + + skb = + alloc_skb(sizeof(struct st21nfca_atr_req) + gb_len + 1, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, 1); + + skb_put(skb, sizeof(struct st21nfca_atr_req)); + + atr_req = (struct st21nfca_atr_req *)skb->data; + memset(atr_req, 0, sizeof(struct st21nfca_atr_req)); + + atr_req->cmd0 = ST21NFCA_NFCIP1_REQ; + atr_req->cmd1 = ST21NFCA_NFCIP1_ATR_REQ; + memset(atr_req->nfcid3, 0, NFC_NFCID3_MAXSIZE); + target = hdev->ndev->targets; + + if (target->sensf_res_len > 0) + memcpy(atr_req->nfcid3, target->sensf_res, + target->sensf_res_len); + else + get_random_bytes(atr_req->nfcid3, NFC_NFCID3_MAXSIZE); + + atr_req->did = 0x0; + + atr_req->bsi = 0x00; + atr_req->bri = 0x00; + atr_req->ppi = ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B; + if (gb_len) { + atr_req->ppi |= ST21NFCA_GB_BIT; + skb_put_data(skb, gb, gb_len); + } + atr_req->length = sizeof(struct st21nfca_atr_req) + hdev->gb_len; + + *(u8 *)skb_push(skb, 1) = info->dep_info.to | 0x10; /* timeout */ + + info->async_cb_type = ST21NFCA_CB_TYPE_READER_F; + info->async_cb_context = info; + info->async_cb = st21nfca_im_recv_atr_res_cb; + info->dep_info.bri = atr_req->bri; + info->dep_info.bsi = atr_req->bsi; + info->dep_info.lri = ST21NFCA_PP2LRI(atr_req->ppi); + + return nfc_hci_send_cmd_async(hdev, ST21NFCA_RF_READER_F_GATE, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, info->async_cb, info); +} +EXPORT_SYMBOL(st21nfca_im_send_atr_req); + +static void st21nfca_im_recv_dep_res_cb(void *context, struct sk_buff *skb, + int err) +{ + struct st21nfca_hci_info *info = context; + struct st21nfca_dep_req_res *dep_res; + + int size; + + if (err != 0) + return; + + if (!skb) + return; + + switch (info->async_cb_type) { + case ST21NFCA_CB_TYPE_READER_F: + dep_res = (struct st21nfca_dep_req_res *)skb->data; + + size = 3; + if (skb->len < size) + goto exit; + + if (ST21NFCA_NFC_DEP_DID_BIT_SET(dep_res->pfb)) + size++; + if (ST21NFCA_NFC_DEP_NAD_BIT_SET(dep_res->pfb)) + size++; + + if (skb->len < size) + goto exit; + + skb_trim(skb, skb->len - 1); + + /* Receiving DEP_REQ - Decoding */ + switch (ST21NFCA_NFC_DEP_PFB_TYPE(dep_res->pfb)) { + case ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU: + pr_err("Received a ACK/NACK PDU\n"); + case ST21NFCA_NFC_DEP_PFB_I_PDU: + info->dep_info.curr_nfc_dep_pni = + ST21NFCA_NFC_DEP_PFB_PNI(dep_res->pfb + 1); + size++; + skb_pull(skb, size); + nfc_tm_data_received(info->hdev->ndev, skb); + break; + case ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU: + pr_err("Received a SUPERVISOR PDU\n"); + skb_pull(skb, size); + *(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ; + *(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ; + *(u8 *)skb_push(skb, 1) = skb->len; + *(u8 *)skb_push(skb, 1) = info->dep_info.to | 0x10; + + st21nfca_im_send_pdu(info, skb); + break; + } + + return; + default: + break; + } + +exit: + kfree_skb(skb); +} + +int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + info->async_cb_type = ST21NFCA_CB_TYPE_READER_F; + info->async_cb_context = info; + info->async_cb = st21nfca_im_recv_dep_res_cb; + + *(u8 *)skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni; + *(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ; + *(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ; + *(u8 *)skb_push(skb, 1) = skb->len; + + *(u8 *)skb_push(skb, 1) = info->dep_info.to | 0x10; + + return nfc_hci_send_cmd_async(hdev, ST21NFCA_RF_READER_F_GATE, + ST21NFCA_WR_XCHG_DATA, + skb->data, skb->len, + info->async_cb, info); +} +EXPORT_SYMBOL(st21nfca_im_send_dep_req); + +void st21nfca_dep_init(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + INIT_WORK(&info->dep_info.tx_work, st21nfca_tx_work); + info->dep_info.curr_nfc_dep_pni = 0; + info->dep_info.idx = 0; + info->dep_info.to = ST21NFCA_DEFAULT_TIMEOUT; +} +EXPORT_SYMBOL(st21nfca_dep_init); + +void st21nfca_dep_deinit(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + cancel_work_sync(&info->dep_info.tx_work); +} +EXPORT_SYMBOL(st21nfca_dep_deinit); diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c new file mode 100644 index 000000000..ebea3b4dd --- /dev/null +++ b/drivers/nfc/st21nfca/i2c.c @@ -0,0 +1,635 @@ +/* + * I2C Link Layer for ST21NFCA HCI based Driver + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * 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 that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/crc-ccitt.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/gpio/consumer.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/acpi.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/nfc.h> +#include <linux/firmware.h> + +#include <asm/unaligned.h> + +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> +#include <net/nfc/nfc.h> + +#include "st21nfca.h" + +/* + * Every frame starts with ST21NFCA_SOF_EOF and ends with ST21NFCA_SOF_EOF. + * Because ST21NFCA_SOF_EOF is a possible data value, there is a mecanism + * called byte stuffing has been introduced. + * + * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING + * - insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte) + * - xor byte with ST21NFCA_BYTE_STUFFING_MASK + */ +#define ST21NFCA_SOF_EOF 0x7e +#define ST21NFCA_BYTE_STUFFING_MASK 0x20 +#define ST21NFCA_ESCAPE_BYTE_STUFFING 0x7d + +/* SOF + 00 */ +#define ST21NFCA_FRAME_HEADROOM 2 + +/* 2 bytes crc + EOF */ +#define ST21NFCA_FRAME_TAILROOM 3 +#define IS_START_OF_FRAME(buf) (buf[0] == ST21NFCA_SOF_EOF && \ + buf[1] == 0) + +#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci" +#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c" + +struct st21nfca_i2c_phy { + struct i2c_client *i2c_dev; + struct nfc_hci_dev *hdev; + + struct gpio_desc *gpiod_ena; + struct st21nfca_se_status se_status; + + struct sk_buff *pending_skb; + int current_read_len; + /* + * crc might have fail because i2c macro + * is disable due to other interface activity + */ + int crc_trials; + + int powered; + int run_mode; + + /* + * < 0 if hardware error occured (e.g. i2c err) + * and prevents normal operation. + */ + int hard_fault; + struct mutex phy_lock; +}; + +static u8 len_seq[] = { 16, 24, 12, 29 }; +static u16 wait_tab[] = { 2, 3, 5, 15, 20, 40}; + +#define I2C_DUMP_SKB(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \ + 16, 1, (skb)->data, (skb)->len, 0); \ +} while (0) + +/* + * In order to get the CLF in a known state we generate an internal reboot + * using a proprietary command. + * Once the reboot is completed, we expect to receive a ST21NFCA_SOF_EOF + * fill buffer. + */ +static int st21nfca_hci_platform_init(struct st21nfca_i2c_phy *phy) +{ + u16 wait_reboot[] = { 50, 300, 1000 }; + char reboot_cmd[] = { 0x7E, 0x66, 0x48, 0xF6, 0x7E }; + u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE]; + int i, r = -1; + + for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) { + r = i2c_master_send(phy->i2c_dev, reboot_cmd, + sizeof(reboot_cmd)); + if (r < 0) + msleep(wait_reboot[i]); + } + if (r < 0) + return r; + + /* CLF is spending about 20ms to do an internal reboot */ + msleep(20); + r = -1; + for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) { + r = i2c_master_recv(phy->i2c_dev, tmp, + ST21NFCA_HCI_LLC_MAX_SIZE); + if (r < 0) + msleep(wait_reboot[i]); + } + if (r < 0) + return r; + + for (i = 0; i < ST21NFCA_HCI_LLC_MAX_SIZE && + tmp[i] == ST21NFCA_SOF_EOF; i++) + ; + + if (r != ST21NFCA_HCI_LLC_MAX_SIZE) + return -ENODEV; + + usleep_range(1000, 1500); + return 0; +} + +static int st21nfca_hci_i2c_enable(void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + + gpiod_set_value(phy->gpiod_ena, 1); + phy->powered = 1; + phy->run_mode = ST21NFCA_HCI_MODE; + + usleep_range(10000, 15000); + + return 0; +} + +static void st21nfca_hci_i2c_disable(void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + + gpiod_set_value(phy->gpiod_ena, 0); + + phy->powered = 0; +} + +static void st21nfca_hci_add_len_crc(struct sk_buff *skb) +{ + u16 crc; + u8 tmp; + + *(u8 *)skb_push(skb, 1) = 0; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + + tmp = crc & 0x00ff; + skb_put_u8(skb, tmp); + + tmp = (crc >> 8) & 0x00ff; + skb_put_u8(skb, tmp); +} + +static void st21nfca_hci_remove_len_crc(struct sk_buff *skb) +{ + skb_pull(skb, ST21NFCA_FRAME_HEADROOM); + skb_trim(skb, skb->len - ST21NFCA_FRAME_TAILROOM); +} + +/* + * Writing a frame must not return the number of written bytes. + * It must return either zero for success, or <0 for error. + * In addition, it must not alter the skb + */ +static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r = -1, i, j; + struct st21nfca_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE * 2]; + + I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb); + + if (phy->hard_fault != 0) + return phy->hard_fault; + + /* + * Compute CRC before byte stuffing computation on frame + * Note st21nfca_hci_add_len_crc is doing a byte stuffing + * on its own value + */ + st21nfca_hci_add_len_crc(skb); + + /* add ST21NFCA_SOF_EOF on tail */ + skb_put_u8(skb, ST21NFCA_SOF_EOF); + /* add ST21NFCA_SOF_EOF on head */ + *(u8 *)skb_push(skb, 1) = ST21NFCA_SOF_EOF; + + /* + * Compute byte stuffing + * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING + * insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte) + * xor byte with ST21NFCA_BYTE_STUFFING_MASK + */ + tmp[0] = skb->data[0]; + for (i = 1, j = 1; i < skb->len - 1; i++, j++) { + if (skb->data[i] == ST21NFCA_SOF_EOF + || skb->data[i] == ST21NFCA_ESCAPE_BYTE_STUFFING) { + tmp[j] = ST21NFCA_ESCAPE_BYTE_STUFFING; + j++; + tmp[j] = skb->data[i] ^ ST21NFCA_BYTE_STUFFING_MASK; + } else { + tmp[j] = skb->data[i]; + } + } + tmp[j] = skb->data[i]; + j++; + + /* + * Manage sleep mode + * Try 3 times to send data with delay between each + */ + mutex_lock(&phy->phy_lock); + for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) { + r = i2c_master_send(client, tmp, j); + if (r < 0) + msleep(wait_tab[i]); + } + mutex_unlock(&phy->phy_lock); + + if (r >= 0) { + if (r != j) + r = -EREMOTEIO; + else + r = 0; + } + + st21nfca_hci_remove_len_crc(skb); + + return r; +} + +static int get_frame_size(u8 *buf, int buflen) +{ + int len = 0; + + if (buf[len + 1] == ST21NFCA_SOF_EOF) + return 0; + + for (len = 1; len < buflen && buf[len] != ST21NFCA_SOF_EOF; len++) + ; + + return len; +} + +static int check_crc(u8 *buf, int buflen) +{ + u16 crc; + + crc = crc_ccitt(0xffff, buf, buflen - 2); + crc = ~crc; + + if (buf[buflen - 2] != (crc & 0xff) || buf[buflen - 1] != (crc >> 8)) { + pr_err(ST21NFCA_HCI_DRIVER_NAME + ": CRC error 0x%x != 0x%x 0x%x\n", crc, buf[buflen - 1], + buf[buflen - 2]); + + pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__); + print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, + 16, 2, buf, buflen, false); + return -EPERM; + } + return 0; +} + +/* + * Prepare received data for upper layer. + * Received data include byte stuffing, crc and sof/eof + * which is not usable by hci part. + * returns: + * frame size without sof/eof, header and byte stuffing + * -EBADMSG : frame was incorrect and discarded + */ +static int st21nfca_hci_i2c_repack(struct sk_buff *skb) +{ + int i, j, r, size; + + if (skb->len < 1 || (skb->len > 1 && skb->data[1] != 0)) + return -EBADMSG; + + size = get_frame_size(skb->data, skb->len); + if (size > 0) { + skb_trim(skb, size); + /* remove ST21NFCA byte stuffing for upper layer */ + for (i = 1, j = 0; i < skb->len; i++) { + if (skb->data[i + j] == + (u8) ST21NFCA_ESCAPE_BYTE_STUFFING) { + skb->data[i] = skb->data[i + j + 1] + | ST21NFCA_BYTE_STUFFING_MASK; + i++; + j++; + } + skb->data[i] = skb->data[i + j]; + } + /* remove byte stuffing useless byte */ + skb_trim(skb, i - j); + /* remove ST21NFCA_SOF_EOF from head */ + skb_pull(skb, 1); + + r = check_crc(skb->data, skb->len); + if (r != 0) { + i = 0; + return -EBADMSG; + } + + /* remove headbyte */ + skb_pull(skb, 1); + /* remove crc. Byte Stuffing is already removed here */ + skb_trim(skb, skb->len - 2); + return skb->len; + } + return 0; +} + +/* + * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees + * that i2c bus will be flushed and that next read will start on a new frame. + * returned skb contains only LLC header and payload. + * returns: + * frame size : if received frame is complete (find ST21NFCA_SOF_EOF at + * end of read) + * -EAGAIN : if received frame is incomplete (not find ST21NFCA_SOF_EOF + * at end of read) + * -EREMOTEIO : i2c read error (fatal) + * -EBADMSG : frame was incorrect and discarded + * (value returned from st21nfca_hci_i2c_repack) + * -EIO : if no ST21NFCA_SOF_EOF is found after reaching + * the read length end sequence + */ +static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy, + struct sk_buff *skb) +{ + int r, i; + u8 len; + u8 buf[ST21NFCA_HCI_LLC_MAX_PAYLOAD]; + struct i2c_client *client = phy->i2c_dev; + + if (phy->current_read_len < ARRAY_SIZE(len_seq)) { + len = len_seq[phy->current_read_len]; + + /* + * Add retry mecanism + * Operation on I2C interface may fail in case of operation on + * RF or SWP interface + */ + r = 0; + mutex_lock(&phy->phy_lock); + for (i = 0; i < ARRAY_SIZE(wait_tab) && r <= 0; i++) { + r = i2c_master_recv(client, buf, len); + if (r < 0) + msleep(wait_tab[i]); + } + mutex_unlock(&phy->phy_lock); + + if (r != len) { + phy->current_read_len = 0; + return -EREMOTEIO; + } + + /* + * The first read sequence does not start with SOF. + * Data is corrupeted so we drop it. + */ + if (!phy->current_read_len && !IS_START_OF_FRAME(buf)) { + skb_trim(skb, 0); + phy->current_read_len = 0; + return -EIO; + } else if (phy->current_read_len && IS_START_OF_FRAME(buf)) { + /* + * Previous frame transmission was interrupted and + * the frame got repeated. + * Received frame start with ST21NFCA_SOF_EOF + 00. + */ + skb_trim(skb, 0); + phy->current_read_len = 0; + } + + skb_put_data(skb, buf, len); + + if (skb->data[skb->len - 1] == ST21NFCA_SOF_EOF) { + phy->current_read_len = 0; + return st21nfca_hci_i2c_repack(skb); + } + phy->current_read_len++; + return -EAGAIN; + } + return -EIO; +} + +/* + * Reads an shdlc frame from the chip. This is not as straightforward as it + * seems. The frame format is data-crc, and corruption can occur anywhere + * while transiting on i2c bus, such that we could read an invalid data. + * The tricky case is when we read a corrupted data or crc. We must detect + * this here in order to determine that data can be transmitted to the hci + * core. This is the reason why we check the crc here. + * The CLF will repeat a frame until we send a RR on that frame. + * + * On ST21NFCA, IRQ goes in idle when read starts. As no size information are + * available in the incoming data, other IRQ might come. Every IRQ will trigger + * a read sequence with different length and will fill the current frame. + * The reception is complete once we reach a ST21NFCA_SOF_EOF. + */ +static irqreturn_t st21nfca_hci_irq_thread_fn(int irq, void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + struct i2c_client *client; + + int r; + + if (!phy || irq != phy->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + + client = phy->i2c_dev; + dev_dbg(&client->dev, "IRQ\n"); + + if (phy->hard_fault != 0) + return IRQ_HANDLED; + + r = st21nfca_hci_i2c_read(phy, phy->pending_skb); + if (r == -EREMOTEIO) { + phy->hard_fault = r; + + nfc_hci_recv_frame(phy->hdev, NULL); + + return IRQ_HANDLED; + } else if (r == -EAGAIN || r == -EIO) { + return IRQ_HANDLED; + } else if (r == -EBADMSG && phy->crc_trials < ARRAY_SIZE(wait_tab)) { + /* + * With ST21NFCA, only one interface (I2C, RF or SWP) + * may be active at a time. + * Having incorrect crc is usually due to i2c macrocell + * deactivation in the middle of a transmission. + * It may generate corrupted data on i2c. + * We give sometime to get i2c back. + * The complete frame will be repeated. + */ + msleep(wait_tab[phy->crc_trials]); + phy->crc_trials++; + phy->current_read_len = 0; + kfree_skb(phy->pending_skb); + } else if (r > 0) { + /* + * We succeeded to read data from the CLF and + * data is valid. + * Reset counter. + */ + nfc_hci_recv_frame(phy->hdev, phy->pending_skb); + phy->crc_trials = 0; + } else { + kfree_skb(phy->pending_skb); + } + + phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL); + if (phy->pending_skb == NULL) { + phy->hard_fault = -ENOMEM; + nfc_hci_recv_frame(phy->hdev, NULL); + } + + return IRQ_HANDLED; +} + +static struct nfc_phy_ops i2c_phy_ops = { + .write = st21nfca_hci_i2c_write, + .enable = st21nfca_hci_i2c_enable, + .disable = st21nfca_hci_i2c_disable, +}; + +static const struct acpi_gpio_params enable_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_st21nfca_gpios[] = { + { "enable-gpios", &enable_gpios, 1 }, + {}, +}; + +static int st21nfca_hci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct st21nfca_i2c_phy *phy; + int r; + + dev_dbg(&client->dev, "%s\n", __func__); + dev_dbg(&client->dev, "IRQ: %d\n", client->irq); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); + return -ENODEV; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->i2c_dev = client; + phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL); + if (phy->pending_skb == NULL) + return -ENOMEM; + + phy->current_read_len = 0; + phy->crc_trials = 0; + mutex_init(&phy->phy_lock); + i2c_set_clientdata(client, phy); + + r = devm_acpi_dev_add_driver_gpios(dev, acpi_st21nfca_gpios); + if (r) + dev_dbg(dev, "Unable to add GPIO mapping table\n"); + + /* Get EN GPIO from resource provider */ + phy->gpiod_ena = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_ena)) { + nfc_err(dev, "Unable to get ENABLE GPIO\n"); + r = PTR_ERR(phy->gpiod_ena); + goto out_free; + } + + phy->se_status.is_ese_present = + device_property_read_bool(&client->dev, "ese-present"); + phy->se_status.is_uicc_present = + device_property_read_bool(&client->dev, "uicc-present"); + + r = st21nfca_hci_platform_init(phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to reboot st21nfca\n"); + goto out_free; + } + + r = devm_request_threaded_irq(&client->dev, client->irq, NULL, + st21nfca_hci_irq_thread_fn, + IRQF_ONESHOT, + ST21NFCA_HCI_DRIVER_NAME, phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + goto out_free; + } + + r = st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, + ST21NFCA_FRAME_HEADROOM, + ST21NFCA_FRAME_TAILROOM, + ST21NFCA_HCI_LLC_MAX_PAYLOAD, + &phy->hdev, + &phy->se_status); + if (r) + goto out_free; + + return 0; + +out_free: + kfree_skb(phy->pending_skb); + return r; +} + +static int st21nfca_hci_i2c_remove(struct i2c_client *client) +{ + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __func__); + + st21nfca_hci_remove(phy->hdev); + + if (phy->powered) + st21nfca_hci_i2c_disable(phy); + if (phy->pending_skb) + kfree_skb(phy->pending_skb); + + return 0; +} + +static const struct i2c_device_id st21nfca_hci_i2c_id_table[] = { + {ST21NFCA_HCI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); + +static const struct acpi_device_id st21nfca_hci_i2c_acpi_match[] = { + {"SMO2100", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st21nfca_hci_i2c_acpi_match); + +static const struct of_device_id of_st21nfca_i2c_match[] = { + { .compatible = "st,st21nfca-i2c", }, + { .compatible = "st,st21nfca_i2c", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match); + +static struct i2c_driver st21nfca_hci_i2c_driver = { + .driver = { + .name = ST21NFCA_HCI_I2C_DRIVER_NAME, + .of_match_table = of_match_ptr(of_st21nfca_i2c_match), + .acpi_match_table = ACPI_PTR(st21nfca_hci_i2c_acpi_match), + }, + .probe = st21nfca_hci_i2c_probe, + .id_table = st21nfca_hci_i2c_id_table, + .remove = st21nfca_hci_i2c_remove, +}; +module_i2c_driver(st21nfca_hci_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c new file mode 100644 index 000000000..52e209950 --- /dev/null +++ b/drivers/nfc/st21nfca/se.c @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * 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 that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <net/nfc/hci.h> + +#include "st21nfca.h" + +#define ST21NFCA_EVT_UICC_ACTIVATE 0x10 +#define ST21NFCA_EVT_UICC_DEACTIVATE 0x13 +#define ST21NFCA_EVT_SE_HARD_RESET 0x20 +#define ST21NFCA_EVT_SE_SOFT_RESET 0x11 +#define ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER 0x21 +#define ST21NFCA_EVT_SE_ACTIVATE 0x22 +#define ST21NFCA_EVT_SE_DEACTIVATE 0x23 + +#define ST21NFCA_EVT_TRANSMIT_DATA 0x10 +#define ST21NFCA_EVT_WTX_REQUEST 0x11 + +#define ST21NFCA_EVT_CONNECTIVITY 0x10 +#define ST21NFCA_EVT_TRANSACTION 0x12 + +#define ST21NFCA_SE_TO_HOT_PLUG 1000 +/* Connectivity pipe only */ +#define ST21NFCA_SE_COUNT_PIPE_UICC 0x01 +/* Connectivity + APDU Reader pipe */ +#define ST21NFCA_SE_COUNT_PIPE_EMBEDDED 0x02 + +#define ST21NFCA_SE_MODE_OFF 0x00 +#define ST21NFCA_SE_MODE_ON 0x01 + +#define ST21NFCA_PARAM_ATR 0x01 +#define ST21NFCA_ATR_DEFAULT_BWI 0x04 + +/* + * WT = 2^BWI/10[s], convert into msecs and add a secure + * room by increasing by 2 this timeout + */ +#define ST21NFCA_BWI_TO_TIMEOUT(x) ((1 << x) * 200) +#define ST21NFCA_ATR_GET_Y_FROM_TD(x) (x >> 4) + +/* If TA is present bit 0 is set */ +#define ST21NFCA_ATR_TA_PRESENT(x) (x & 0x01) +/* If TB is present bit 1 is set */ +#define ST21NFCA_ATR_TB_PRESENT(x) (x & 0x02) + +static u8 st21nfca_se_get_bwi(struct nfc_hci_dev *hdev) +{ + int i; + u8 td; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + /* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */ + for (i = 1; i < ST21NFCA_ESE_MAX_LENGTH; i++) { + td = ST21NFCA_ATR_GET_Y_FROM_TD(info->se_info.atr[i]); + if (ST21NFCA_ATR_TA_PRESENT(td)) + i++; + if (ST21NFCA_ATR_TB_PRESENT(td)) { + i++; + return info->se_info.atr[i] >> 4; + } + } + return ST21NFCA_ATR_DEFAULT_BWI; +} + +static void st21nfca_se_get_atr(struct nfc_hci_dev *hdev) +{ + int r; + struct sk_buff *skb; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + r = nfc_hci_get_param(hdev, ST21NFCA_APDU_READER_GATE, + ST21NFCA_PARAM_ATR, &skb); + if (r < 0) + return; + + if (skb->len <= ST21NFCA_ESE_MAX_LENGTH) { + memcpy(info->se_info.atr, skb->data, skb->len); + info->se_info.wt_timeout = + ST21NFCA_BWI_TO_TIMEOUT(st21nfca_se_get_bwi(hdev)); + } + kfree_skb(skb); +} + +static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx, + u8 state) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + int r, i; + struct sk_buff *sk_host_list; + u8 se_event, host_id; + + switch (se_idx) { + case NFC_HCI_UICC_HOST_ID: + se_event = (state == ST21NFCA_SE_MODE_ON ? + ST21NFCA_EVT_UICC_ACTIVATE : + ST21NFCA_EVT_UICC_DEACTIVATE); + + info->se_info.count_pipes = 0; + info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_UICC; + break; + case ST21NFCA_ESE_HOST_ID: + se_event = (state == ST21NFCA_SE_MODE_ON ? + ST21NFCA_EVT_SE_ACTIVATE : + ST21NFCA_EVT_SE_DEACTIVATE); + + info->se_info.count_pipes = 0; + info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_EMBEDDED; + break; + default: + return -EINVAL; + } + + /* + * Wait for an EVT_HOT_PLUG in order to + * retrieve a relevant host list. + */ + reinit_completion(&info->se_info.req_completion); + r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE, se_event, + NULL, 0); + if (r < 0) + return r; + + mod_timer(&info->se_info.se_active_timer, jiffies + + msecs_to_jiffies(ST21NFCA_SE_TO_HOT_PLUG)); + info->se_info.se_active = true; + + /* Ignore return value and check in any case the host_list */ + wait_for_completion_interruptible(&info->se_info.req_completion); + + r = nfc_hci_get_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_HOST_LIST, + &sk_host_list); + if (r < 0) + return r; + + for (i = 0; i < sk_host_list->len && + sk_host_list->data[i] != se_idx; i++) + ; + host_id = sk_host_list->data[i]; + kfree_skb(sk_host_list); + + if (state == ST21NFCA_SE_MODE_ON && host_id == se_idx) + return se_idx; + else if (state == ST21NFCA_SE_MODE_OFF && host_id != se_idx) + return se_idx; + + return -1; +} + +int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + int se_count = 0; + + if (test_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks)) + return 0; + + if (info->se_status->is_uicc_present) { + nfc_add_se(hdev->ndev, NFC_HCI_UICC_HOST_ID, NFC_SE_UICC); + se_count++; + } + + if (info->se_status->is_ese_present) { + nfc_add_se(hdev->ndev, ST21NFCA_ESE_HOST_ID, NFC_SE_EMBEDDED); + se_count++; + } + + return !se_count; +} +EXPORT_SYMBOL(st21nfca_hci_discover_se); + +int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + int r; + + /* + * According to upper layer, se_idx == NFC_SE_UICC when + * info->se_status->is_uicc_enable is true should never happen. + * Same for eSE. + */ + r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_ON); + if (r == ST21NFCA_ESE_HOST_ID) { + st21nfca_se_get_atr(hdev); + r = nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE, + ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0); + if (r < 0) + return r; + } else if (r < 0) { + /* + * The activation tentative failed, the secure element + * is not connected. Remove from the list. + */ + nfc_remove_se(hdev->ndev, se_idx); + return r; + } + + return 0; +} +EXPORT_SYMBOL(st21nfca_hci_enable_se); + +int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + int r; + + /* + * According to upper layer, se_idx == NFC_SE_UICC when + * info->se_status->is_uicc_enable is true should never happen + * Same for eSE. + */ + r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_OFF); + if (r < 0) + return r; + + return 0; +} +EXPORT_SYMBOL(st21nfca_hci_disable_se); + +int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_debug("se_io %x\n", se_idx); + + switch (se_idx) { + case ST21NFCA_ESE_HOST_ID: + info->se_info.cb = cb; + info->se_info.cb_context = cb_context; + mod_timer(&info->se_info.bwi_timer, jiffies + + msecs_to_jiffies(info->se_info.wt_timeout)); + info->se_info.bwi_active = true; + return nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE, + ST21NFCA_EVT_TRANSMIT_DATA, + apdu, apdu_length); + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(st21nfca_hci_se_io); + +static void st21nfca_se_wt_work(struct work_struct *work) +{ + /* + * No answer from the secure element + * within the defined timeout. + * Let's send a reset request as recovery procedure. + * According to the situation, we first try to send a software reset + * to the secure element. If the next command is still not + * answering in time, we send to the CLF a secure element hardware + * reset request. + */ + /* hardware reset managed through VCC_UICC_OUT power supply */ + u8 param = 0x01; + struct st21nfca_hci_info *info = container_of(work, + struct st21nfca_hci_info, + se_info.timeout_work); + + pr_debug("\n"); + + info->se_info.bwi_active = false; + + if (!info->se_info.xch_error) { + info->se_info.xch_error = true; + nfc_hci_send_event(info->hdev, ST21NFCA_APDU_READER_GATE, + ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0); + } else { + info->se_info.xch_error = false; + nfc_hci_send_event(info->hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_EVT_SE_HARD_RESET, ¶m, 1); + } + info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME); +} + +static void st21nfca_se_wt_timeout(struct timer_list *t) +{ + struct st21nfca_hci_info *info = from_timer(info, t, se_info.bwi_timer); + + schedule_work(&info->se_info.timeout_work); +} + +static void st21nfca_se_activation_timeout(struct timer_list *t) +{ + struct st21nfca_hci_info *info = from_timer(info, t, + se_info.se_active_timer); + + pr_debug("\n"); + + info->se_info.se_active = false; + + complete(&info->se_info.req_completion); +} + +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ +int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, + u8 event, struct sk_buff *skb) +{ + int r = 0; + struct device *dev = &hdev->ndev->dev; + struct nfc_evt_transaction *transaction; + + pr_debug("connectivity gate event: %x\n", event); + + switch (event) { + case ST21NFCA_EVT_CONNECTIVITY: + r = nfc_se_connectivity(hdev->ndev, host); + break; + case ST21NFCA_EVT_TRANSACTION: + /* + * According to specification etsi 102 622 + * 11.2.2.4 EVT_TRANSACTION Table 52 + * Description Tag Length + * AID 81 5 to 16 + * PARAMETERS 82 0 to 255 + */ + if (skb->len < NFC_MIN_AID_LENGTH + 2 || + skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) + return -EPROTO; + + transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev, + skb->len - 2, GFP_KERNEL); + if (!transaction) + return -ENOMEM; + + transaction->aid_len = skb->data[1]; + + /* Checking if the length of the AID is valid */ + if (transaction->aid_len > sizeof(transaction->aid)) { + devm_kfree(dev, transaction); + return -EINVAL; + } + + memcpy(transaction->aid, &skb->data[2], + transaction->aid_len); + + /* Check next byte is PARAMETERS tag (82) */ + if (skb->data[transaction->aid_len + 2] != + NFC_EVT_TRANSACTION_PARAMS_TAG) { + devm_kfree(dev, transaction); + return -EPROTO; + } + + transaction->params_len = skb->data[transaction->aid_len + 3]; + + /* Total size is allocated (skb->len - 2) minus fixed array members */ + if (transaction->params_len > ((skb->len - 2) - + sizeof(struct nfc_evt_transaction))) { + devm_kfree(dev, transaction); + return -EINVAL; + } + + memcpy(transaction->params, skb->data + + transaction->aid_len + 4, transaction->params_len); + + r = nfc_se_transaction(hdev->ndev, host, transaction); + break; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n"); + return 1; + } + kfree_skb(skb); + return r; +} +EXPORT_SYMBOL(st21nfca_connectivity_event_received); + +int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev, + u8 event, struct sk_buff *skb) +{ + int r = 0; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_debug("apdu reader gate event: %x\n", event); + + switch (event) { + case ST21NFCA_EVT_TRANSMIT_DATA: + del_timer_sync(&info->se_info.bwi_timer); + cancel_work_sync(&info->se_info.timeout_work); + info->se_info.bwi_active = false; + r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0); + if (r < 0) + goto exit; + + info->se_info.cb(info->se_info.cb_context, + skb->data, skb->len, 0); + break; + case ST21NFCA_EVT_WTX_REQUEST: + mod_timer(&info->se_info.bwi_timer, jiffies + + msecs_to_jiffies(info->se_info.wt_timeout)); + break; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on apdu reader gate\n"); + return 1; + } + +exit: + kfree_skb(skb); + return r; +} +EXPORT_SYMBOL(st21nfca_apdu_reader_event_received); + +void st21nfca_se_init(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + init_completion(&info->se_info.req_completion); + INIT_WORK(&info->se_info.timeout_work, st21nfca_se_wt_work); + /* initialize timers */ + timer_setup(&info->se_info.bwi_timer, st21nfca_se_wt_timeout, 0); + info->se_info.bwi_active = false; + + timer_setup(&info->se_info.se_active_timer, + st21nfca_se_activation_timeout, 0); + info->se_info.se_active = false; + + info->se_info.count_pipes = 0; + info->se_info.expected_pipes = 0; + + info->se_info.xch_error = false; + + info->se_info.wt_timeout = + ST21NFCA_BWI_TO_TIMEOUT(ST21NFCA_ATR_DEFAULT_BWI); +} +EXPORT_SYMBOL(st21nfca_se_init); + +void st21nfca_se_deinit(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + if (info->se_info.bwi_active) + del_timer_sync(&info->se_info.bwi_timer); + if (info->se_info.se_active) + del_timer_sync(&info->se_info.se_active_timer); + + cancel_work_sync(&info->se_info.timeout_work); + info->se_info.bwi_active = false; + info->se_info.se_active = false; +} +EXPORT_SYMBOL(st21nfca_se_deinit); diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h new file mode 100644 index 000000000..7e2923ac9 --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * 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 that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LOCAL_ST21NFCA_H_ +#define __LOCAL_ST21NFCA_H_ + +#include <net/nfc/hci.h> +#include <linux/skbuff.h> +#include <linux/workqueue.h> + +#define HCI_MODE 0 + +/* framing in HCI mode */ +#define ST21NFCA_SOF_EOF_LEN 2 + +/* Almost every time value is 0 */ +#define ST21NFCA_HCI_LLC_LEN 1 + +/* Size in worst case : + * In normal case CRC len = 2 but byte stuffing + * may appear in case one CRC byte = ST21NFCA_SOF_EOF + */ +#define ST21NFCA_HCI_LLC_CRC 4 + +#define ST21NFCA_HCI_LLC_LEN_CRC (ST21NFCA_SOF_EOF_LEN + \ + ST21NFCA_HCI_LLC_LEN + \ + ST21NFCA_HCI_LLC_CRC) +#define ST21NFCA_HCI_LLC_MIN_SIZE (1 + ST21NFCA_HCI_LLC_LEN_CRC) + +/* Worst case when adding byte stuffing between each byte */ +#define ST21NFCA_HCI_LLC_MAX_PAYLOAD 29 +#define ST21NFCA_HCI_LLC_MAX_SIZE (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \ + ST21NFCA_HCI_LLC_MAX_PAYLOAD) + +/* Reader RF commands */ +#define ST21NFCA_WR_XCHG_DATA 0x10 + +#define ST21NFCA_DEVICE_MGNT_GATE 0x01 +#define ST21NFCA_RF_READER_F_GATE 0x14 +#define ST21NFCA_RF_CARD_F_GATE 0x24 +#define ST21NFCA_APDU_READER_GATE 0xf0 +#define ST21NFCA_CONNECTIVITY_GATE 0x41 + +/* + * ref ISO7816-3 chap 8.1. the initial character TS is followed by a + * sequence of at most 32 characters. + */ +#define ST21NFCA_ESE_MAX_LENGTH 33 +#define ST21NFCA_ESE_HOST_ID 0xc0 + +#define DRIVER_DESC "HCI NFC driver for ST21NFCA" + +#define ST21NFCA_HCI_MODE 0 +#define ST21NFCA_NUM_DEVICES 256 + +#define ST21NFCA_VENDOR_OUI 0x0080E1 /* STMicroelectronics */ +#define ST21NFCA_FACTORY_MODE 2 + +struct st21nfca_se_status { + bool is_ese_present; + bool is_uicc_present; +}; + +enum st21nfca_state { + ST21NFCA_ST_COLD, + ST21NFCA_ST_READY, +}; + +/** + * enum nfc_vendor_cmds - supported nfc vendor commands + * + * @FACTORY_MODE: Allow to set the driver into a mode where no secure element + * are activated. It does not consider any NFC_ATTR_VENDOR_DATA. + * @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command. + * It does not consider any NFC_ATTR_VENDOR_DATA. + * @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example + * RF trimmings or low level drivers configurations (I2C, SPI, SWP). + * @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing + * table following RF technology, CLF mode or protocol. + * @HCI_DM_GET_INFO: Allow to retrieve CLF information. + * @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low + * level drivers configurations or RF trimmings. + * @HCI_DM_LOAD: Allow to load a firmware into the CLF. A complete + * packet can be more than 8KB. + * @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF + * configuration changes without CLF power off. + * @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the + * white list). + * @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF + * technology. When using this command to anti-collision is done. + * @HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF + * connectivity. + */ +enum nfc_vendor_cmds { + FACTORY_MODE, + HCI_CLEAR_ALL_PIPES, + HCI_DM_PUT_DATA, + HCI_DM_UPDATE_AID, + HCI_DM_GET_INFO, + HCI_DM_GET_DATA, + HCI_DM_LOAD, + HCI_DM_RESET, + HCI_GET_PARAM, + HCI_DM_FIELD_GENERATOR, + HCI_LOOPBACK, +}; + +struct st21nfca_vendor_info { + struct completion req_completion; + struct sk_buff *rx_skb; +}; + +struct st21nfca_dep_info { + struct sk_buff *tx_pending; + struct work_struct tx_work; + u8 curr_nfc_dep_pni; + u32 idx; + u8 to; + u8 did; + u8 bsi; + u8 bri; + u8 lri; +} __packed; + +struct st21nfca_se_info { + u8 atr[ST21NFCA_ESE_MAX_LENGTH]; + struct completion req_completion; + + struct timer_list bwi_timer; + int wt_timeout; /* in msecs */ + bool bwi_active; + + struct timer_list se_active_timer; + bool se_active; + int expected_pipes; + int count_pipes; + + bool xch_error; + + se_io_cb_t cb; + void *cb_context; + struct work_struct timeout_work; +}; + +struct st21nfca_hci_info { + struct nfc_phy_ops *phy_ops; + void *phy_id; + + struct nfc_hci_dev *hdev; + struct st21nfca_se_status *se_status; + + enum st21nfca_state state; + + struct mutex info_lock; + + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; + + struct st21nfca_dep_info dep_info; + struct st21nfca_se_info se_info; + struct st21nfca_vendor_info vendor_info; +}; + +int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, + char *llc_name, int phy_headroom, int phy_tailroom, + int phy_payload, struct nfc_hci_dev **hdev, + struct st21nfca_se_status *se_status); +void st21nfca_hci_remove(struct nfc_hci_dev *hdev); + +int st21nfca_dep_event_received(struct nfc_hci_dev *hdev, + u8 event, struct sk_buff *skb); +int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb); + +int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len); +int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb); +void st21nfca_dep_init(struct nfc_hci_dev *hdev); +void st21nfca_dep_deinit(struct nfc_hci_dev *hdev); + +int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, + u8 event, struct sk_buff *skb); +int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev, + u8 event, struct sk_buff *skb); + +int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev); +int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx); +int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx); +int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context); + +void st21nfca_se_init(struct nfc_hci_dev *hdev); +void st21nfca_se_deinit(struct nfc_hci_dev *hdev); + +int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *ndev, u8 event, + struct sk_buff *skb); +int st21nfca_vendor_cmds_init(struct nfc_hci_dev *ndev); + +#endif /* __LOCAL_ST21NFCA_H_ */ diff --git a/drivers/nfc/st21nfca/vendor_cmds.c b/drivers/nfc/st21nfca/vendor_cmds.c new file mode 100644 index 000000000..ab765e547 --- /dev/null +++ b/drivers/nfc/st21nfca/vendor_cmds.c @@ -0,0 +1,375 @@ +/* + * Proprietary commands extension for STMicroelectronics NFC Chip + * + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. + * + * 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 that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <net/genetlink.h> +#include <linux/module.h> +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "st21nfca.h" + +#define ST21NFCA_HCI_DM_GETDATA 0x10 +#define ST21NFCA_HCI_DM_PUTDATA 0x11 +#define ST21NFCA_HCI_DM_LOAD 0x12 +#define ST21NFCA_HCI_DM_GETINFO 0x13 +#define ST21NFCA_HCI_DM_UPDATE_AID 0x20 +#define ST21NFCA_HCI_DM_RESET 0x3e + +#define ST21NFCA_HCI_DM_FIELD_GENERATOR 0x32 + +#define ST21NFCA_FACTORY_MODE_ON 1 +#define ST21NFCA_FACTORY_MODE_OFF 0 + +#define ST21NFCA_EVT_POST_DATA 0x02 + +struct get_param_data { + u8 gate; + u8 data; +} __packed; + +static int st21nfca_factory_mode(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + if (data_len != 1) + return -EINVAL; + + pr_debug("factory mode: %x\n", ((u8 *)data)[0]); + + switch (((u8 *)data)[0]) { + case ST21NFCA_FACTORY_MODE_ON: + test_and_set_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks); + break; + case ST21NFCA_FACTORY_MODE_OFF: + clear_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int st21nfca_hci_clear_all_pipes(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_disconnect_all_gates(hdev); +} + +static int st21nfca_hci_dm_put_data(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_PUTDATA, data, + data_len, NULL); +} + +static int st21nfca_hci_dm_update_aid(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_UPDATE_AID, data, data_len, NULL); +} + +static int st21nfca_hci_dm_get_info(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_GETINFO, + data, data_len, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_DM_GET_INFO, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_get_data(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_GETDATA, + data, data_len, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_DM_GET_DATA, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_load(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_LOAD, data, data_len, NULL); +} + +static int st21nfca_hci_dm_reset(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + r = nfc_hci_send_cmd_async(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_RESET, data, data_len, NULL, NULL); + if (r < 0) + return r; + + r = nfc_llc_stop(hdev->llc); + if (r < 0) + return r; + + return nfc_llc_start(hdev->llc); +} + +static int st21nfca_hci_get_param(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg, *skb; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + struct get_param_data *param = (struct get_param_data *)data; + + if (data_len < sizeof(struct get_param_data)) + return -EPROTO; + + r = nfc_hci_get_param(hdev, param->gate, param->data, &skb); + if (r) + goto exit; + + msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI, + HCI_GET_PARAM, skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); + +free_skb: + kfree_skb(skb); +exit: + return r; +} + +static int st21nfca_hci_dm_field_generator(struct nfc_dev *dev, void *data, + size_t data_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + + return nfc_hci_send_cmd(hdev, + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_HCI_DM_FIELD_GENERATOR, + data, data_len, NULL); +} + +int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *hdev, u8 event, + struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + switch (event) { + case ST21NFCA_EVT_POST_DATA: + info->vendor_info.rx_skb = skb; + break; + default: + nfc_err(&hdev->ndev->dev, "Unexpected event on loopback gate\n"); + } + complete(&info->vendor_info.req_completion); + return 0; +} +EXPORT_SYMBOL(st21nfca_hci_loopback_event_received); + +static int st21nfca_hci_loopback(struct nfc_dev *dev, void *data, + size_t data_len) +{ + int r; + struct sk_buff *msg; + struct nfc_hci_dev *hdev = nfc_get_drvdata(dev); + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + if (data_len <= 0) + return -EPROTO; + + reinit_completion(&info->vendor_info.req_completion); + info->vendor_info.rx_skb = NULL; + + r = nfc_hci_send_event(hdev, NFC_HCI_LOOPBACK_GATE, + ST21NFCA_EVT_POST_DATA, data, data_len); + if (r < 0) { + r = -EPROTO; + goto exit; + } + + wait_for_completion_interruptible(&info->vendor_info.req_completion); + if (!info->vendor_info.rx_skb || + info->vendor_info.rx_skb->len != data_len) { + r = -EPROTO; + goto exit; + } + + msg = nfc_vendor_cmd_alloc_reply_skb(hdev->ndev, + ST21NFCA_VENDOR_OUI, + HCI_LOOPBACK, + info->vendor_info.rx_skb->len); + if (!msg) { + r = -ENOMEM; + goto free_skb; + } + + if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len, + info->vendor_info.rx_skb->data)) { + kfree_skb(msg); + r = -ENOBUFS; + goto free_skb; + } + + r = nfc_vendor_cmd_reply(msg); +free_skb: + kfree_skb(info->vendor_info.rx_skb); +exit: + return r; +} + +static struct nfc_vendor_cmd st21nfca_vendor_cmds[] = { + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = FACTORY_MODE, + .doit = st21nfca_factory_mode, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_CLEAR_ALL_PIPES, + .doit = st21nfca_hci_clear_all_pipes, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_PUT_DATA, + .doit = st21nfca_hci_dm_put_data, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_UPDATE_AID, + .doit = st21nfca_hci_dm_update_aid, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_GET_INFO, + .doit = st21nfca_hci_dm_get_info, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_GET_DATA, + .doit = st21nfca_hci_dm_get_data, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_LOAD, + .doit = st21nfca_hci_dm_load, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_RESET, + .doit = st21nfca_hci_dm_reset, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_GET_PARAM, + .doit = st21nfca_hci_get_param, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_DM_FIELD_GENERATOR, + .doit = st21nfca_hci_dm_field_generator, + }, + { + .vendor_id = ST21NFCA_VENDOR_OUI, + .subcmd = HCI_LOOPBACK, + .doit = st21nfca_hci_loopback, + }, +}; + +int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + init_completion(&info->vendor_info.req_completion); + return nfc_set_vendor_cmds(hdev->ndev, st21nfca_vendor_cmds, + sizeof(st21nfca_vendor_cmds)); +} +EXPORT_SYMBOL(st21nfca_vendor_cmds_init); |