diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
commit | 57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch) | |
tree | 5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/nfc |
Initial import
Diffstat (limited to 'drivers/nfc')
50 files changed, 18745 insertions, 0 deletions
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig new file mode 100644 index 000000000..107714e44 --- /dev/null +++ b/drivers/nfc/Kconfig @@ -0,0 +1,77 @@ +# +# Near Field Communication (NFC) devices +# + +menu "Near Field Communication (NFC) devices" + depends on NFC + +config NFC_PN533 + tristate "NXP PN533 USB driver" + depends on USB + help + NXP PN533 USB driver. + This driver provides support for NFC NXP PN533 devices. + + Say Y here to compile support for PN533 devices into the + kernel or say M to compile it as module (pn533). + +config NFC_WILINK + tristate "Texas Instruments NFC WiLink driver" + depends on TI_ST && NFC_NCI + help + This enables the NFC driver for Texas Instrument's BT/FM/GPS/NFC + combo devices. This makes use of shared transport line discipline + core driver to communicate with the NFC core of the combo chip. + + Say Y here to compile support for Texas Instrument's NFC WiLink driver + into the kernel or say M to compile it as module. + +config NFC_TRF7970A + tristate "Texas Instruments TRF7970a NFC driver" + depends on SPI && NFC_DIGITAL + help + This option enables the NFC driver for Texas Instruments' TRF7970a + device. Such device supports 5 different protocols: ISO14443A, + ISO14443B, FeLiCa, ISO15693 and ISO18000-3. + + Say Y here to compile support for TRF7970a into the kernel or + say M to compile it as a module. The module will be called + trf7970a.ko. + +config NFC_MEI_PHY + tristate "MEI bus NFC device support" + depends on INTEL_MEI && NFC_HCI + help + This adds support to use an mei bus nfc device. Select this if you + will use an HCI NFC driver for an NFC chip connected behind an + Intel's Management Engine chip. + + If unsure, say N. + +config NFC_SIM + tristate "NFC hardware simulator driver" + help + This driver declares two virtual NFC devices supporting NFC-DEP + protocol. An LLCP connection can be established between them and + all packets sent from one device is sent back to the other, acting as + loopback devices. + + If unsure, say N. + +config NFC_PORT100 + tristate "Sony NFC Port-100 Series USB device support" + depends on USB + depends on NFC_DIGITAL + help + This adds support for Sony Port-100 chip based USB devices such as the + RC-S380 dongle. + + If unsure, say N. + +source "drivers/nfc/pn544/Kconfig" +source "drivers/nfc/microread/Kconfig" +source "drivers/nfc/nfcmrvl/Kconfig" +source "drivers/nfc/st21nfca/Kconfig" +source "drivers/nfc/st21nfcb/Kconfig" +source "drivers/nfc/nxp-nci/Kconfig" +endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile new file mode 100644 index 000000000..a4292d790 --- /dev/null +++ b/drivers/nfc/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for nfc devices +# + +obj-$(CONFIG_NFC_PN544) += pn544/ +obj-$(CONFIG_NFC_MICROREAD) += microread/ +obj-$(CONFIG_NFC_PN533) += pn533.o +obj-$(CONFIG_NFC_WILINK) += nfcwilink.o +obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o +obj-$(CONFIG_NFC_SIM) += nfcsim.o +obj-$(CONFIG_NFC_PORT100) += port100.o +obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ +obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o +obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ +obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/ +obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ + +ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c new file mode 100644 index 000000000..11c7cbdad --- /dev/null +++ b/drivers/nfc/mei_phy.c @@ -0,0 +1,173 @@ +/* + * MEI Library for mei bus nfc device access + * + * Copyright (C) 2013 Intel Corporation. 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/module.h> +#include <linux/slab.h> +#include <linux/nfc.h> + +#include "mei_phy.h" + +struct mei_nfc_hdr { + u8 cmd; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; +} __packed; + +#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD) + +#define MEI_DUMP_SKB_IN(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump_debug("mei in : ", DUMP_PREFIX_OFFSET, \ + 16, 1, (skb)->data, (skb)->len, false); \ +} while (0) + +#define MEI_DUMP_SKB_OUT(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET, \ + 16, 1, (skb)->data, (skb)->len, false); \ +} while (0) + +int nfc_mei_phy_enable(void *phy_id) +{ + int r; + struct nfc_mei_phy *phy = phy_id; + + pr_info("%s\n", __func__); + + if (phy->powered == 1) + return 0; + + r = mei_cl_enable_device(phy->device); + if (r < 0) { + pr_err("Could not enable device\n"); + return r; + } + + r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy); + if (r) { + pr_err("Event cb registration failed\n"); + mei_cl_disable_device(phy->device); + phy->powered = 0; + + return r; + } + + phy->powered = 1; + + return 0; +} +EXPORT_SYMBOL_GPL(nfc_mei_phy_enable); + +void nfc_mei_phy_disable(void *phy_id) +{ + struct nfc_mei_phy *phy = phy_id; + + pr_info("%s\n", __func__); + + mei_cl_disable_device(phy->device); + + phy->powered = 0; +} +EXPORT_SYMBOL_GPL(nfc_mei_phy_disable); + +/* + * 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 nfc_mei_phy_write(void *phy_id, struct sk_buff *skb) +{ + struct nfc_mei_phy *phy = phy_id; + int r; + + MEI_DUMP_SKB_OUT("mei frame sent", skb); + + r = mei_cl_send(phy->device, skb->data, skb->len); + if (r > 0) + r = 0; + + return r; +} + +void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context) +{ + struct nfc_mei_phy *phy = context; + + if (phy->hard_fault != 0) + return; + + if (events & BIT(MEI_CL_EVENT_RX)) { + struct sk_buff *skb; + int reply_size; + + skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL); + if (!skb) + return; + + reply_size = mei_cl_recv(device, skb->data, MEI_NFC_MAX_READ); + if (reply_size < MEI_NFC_HEADER_SIZE) { + kfree_skb(skb); + return; + } + + skb_put(skb, reply_size); + skb_pull(skb, MEI_NFC_HEADER_SIZE); + + MEI_DUMP_SKB_IN("mei frame read", skb); + + nfc_hci_recv_frame(phy->hdev, skb); + } +} +EXPORT_SYMBOL_GPL(nfc_mei_event_cb); + +struct nfc_phy_ops mei_phy_ops = { + .write = nfc_mei_phy_write, + .enable = nfc_mei_phy_enable, + .disable = nfc_mei_phy_disable, +}; +EXPORT_SYMBOL_GPL(mei_phy_ops); + +struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *device) +{ + struct nfc_mei_phy *phy; + + phy = kzalloc(sizeof(struct nfc_mei_phy), GFP_KERNEL); + if (!phy) + return NULL; + + phy->device = device; + mei_cl_set_drvdata(device, phy); + + return phy; +} +EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc); + +void nfc_mei_phy_free(struct nfc_mei_phy *phy) +{ + kfree(phy); +} +EXPORT_SYMBOL_GPL(nfc_mei_phy_free); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("mei bus NFC device interface"); diff --git a/drivers/nfc/mei_phy.h b/drivers/nfc/mei_phy.h new file mode 100644 index 000000000..d669900f8 --- /dev/null +++ b/drivers/nfc/mei_phy.h @@ -0,0 +1,30 @@ +#ifndef __LOCAL_MEI_PHY_H_ +#define __LOCAL_MEI_PHY_H_ + +#include <linux/mei_cl_bus.h> +#include <net/nfc/hci.h> + +#define MEI_NFC_HEADER_SIZE 10 +#define MEI_NFC_MAX_HCI_PAYLOAD 300 + +struct nfc_mei_phy { + struct mei_cl_device *device; + struct nfc_hci_dev *hdev; + + int powered; + + int hard_fault; /* + * < 0 if hardware error occured + * and prevents normal operation. + */ +}; + +extern struct nfc_phy_ops mei_phy_ops; + +int nfc_mei_phy_enable(void *phy_id); +void nfc_mei_phy_disable(void *phy_id); +void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context); +struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *device); +void nfc_mei_phy_free(struct nfc_mei_phy *phy); + +#endif /* __LOCAL_MEI_PHY_H_ */ diff --git a/drivers/nfc/microread/Kconfig b/drivers/nfc/microread/Kconfig new file mode 100644 index 000000000..951d5542f --- /dev/null +++ b/drivers/nfc/microread/Kconfig @@ -0,0 +1,35 @@ +config NFC_MICROREAD + tristate "Inside Secure microread NFC driver" + depends on NFC_HCI + select CRC_CCITT + default n + ---help--- + This module contains the main code for Inside Secure microread + NFC chipsets. It implements the chipset HCI logic and hooks into + the NFC kernel APIs. Physical layers will register against it. + + To compile this driver as a module, choose m here. The module will + be called microread. + Say N if unsure. + +config NFC_MICROREAD_I2C + tristate "NFC Microread i2c support" + depends on NFC_MICROREAD && I2C && NFC_SHDLC + ---help--- + This module adds support for the i2c interface of adapters using + Inside microread chipsets. Select this if your platform is using + the i2c bus. + + If you choose to build a module, it'll be called microread_i2c. + Say N if unsure. + +config NFC_MICROREAD_MEI + tristate "NFC Microread MEI support" + depends on NFC_MICROREAD && NFC_MEI_PHY + ---help--- + This module adds support for the mei interface of adapters using + Inside microread chipsets. Select this if your microread chipset + is handled by Intel's Management Engine Interface on your platform. + + If you choose to build a module, it'll be called microread_mei. + Say N if unsure. diff --git a/drivers/nfc/microread/Makefile b/drivers/nfc/microread/Makefile new file mode 100644 index 000000000..755c24cba --- /dev/null +++ b/drivers/nfc/microread/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for Microread HCI based NFC driver +# + +microread_i2c-objs = i2c.o +microread_mei-objs = mei.o + +obj-$(CONFIG_NFC_MICROREAD) += microread.o +obj-$(CONFIG_NFC_MICROREAD_I2C) += microread_i2c.o +obj-$(CONFIG_NFC_MICROREAD_MEI) += microread_mei.o diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c new file mode 100644 index 000000000..661e2c814 --- /dev/null +++ b/drivers/nfc/microread/i2c.c @@ -0,0 +1,328 @@ +/* + * HCI based Driver for Inside Secure microread NFC Chip - i2c layer + * + * Copyright (C) 2013 Intel Corporation. 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/module.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> + +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "microread.h" + +#define MICROREAD_I2C_DRIVER_NAME "microread" + +#define MICROREAD_I2C_FRAME_HEADROOM 1 +#define MICROREAD_I2C_FRAME_TAILROOM 1 + +/* framing in HCI mode */ +#define MICROREAD_I2C_LLC_LEN 1 +#define MICROREAD_I2C_LLC_CRC 1 +#define MICROREAD_I2C_LLC_LEN_CRC (MICROREAD_I2C_LLC_LEN + \ + MICROREAD_I2C_LLC_CRC) +#define MICROREAD_I2C_LLC_MIN_SIZE (1 + MICROREAD_I2C_LLC_LEN_CRC) +#define MICROREAD_I2C_LLC_MAX_PAYLOAD 29 +#define MICROREAD_I2C_LLC_MAX_SIZE (MICROREAD_I2C_LLC_LEN_CRC + 1 + \ + MICROREAD_I2C_LLC_MAX_PAYLOAD) + +struct microread_i2c_phy { + struct i2c_client *i2c_dev; + struct nfc_hci_dev *hdev; + + int irq; + + int hard_fault; /* + * < 0 if hardware error occured (e.g. i2c err) + * and prevents normal operation. + */ +}; + +#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) + +static void microread_i2c_add_len_crc(struct sk_buff *skb) +{ + int i; + u8 crc = 0; + int len; + + len = skb->len; + *skb_push(skb, 1) = len; + + for (i = 0; i < skb->len; i++) + crc = crc ^ skb->data[i]; + + *skb_put(skb, 1) = crc; +} + +static void microread_i2c_remove_len_crc(struct sk_buff *skb) +{ + skb_pull(skb, MICROREAD_I2C_FRAME_HEADROOM); + skb_trim(skb, MICROREAD_I2C_FRAME_TAILROOM); +} + +static int check_crc(struct sk_buff *skb) +{ + int i; + u8 crc = 0; + + for (i = 0; i < skb->len - 1; i++) + crc = crc ^ skb->data[i]; + + if (crc != skb->data[skb->len-1]) { + pr_err("CRC error 0x%x != 0x%x\n", crc, skb->data[skb->len-1]); + pr_info("%s: BAD CRC\n", __func__); + return -EPERM; + } + + return 0; +} + +static int microread_i2c_enable(void *phy_id) +{ + return 0; +} + +static void microread_i2c_disable(void *phy_id) +{ + return; +} + +static int microread_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r; + struct microread_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + + if (phy->hard_fault != 0) + return phy->hard_fault; + + usleep_range(3000, 6000); + + microread_i2c_add_len_crc(skb); + + I2C_DUMP_SKB("i2c frame written", skb); + + r = i2c_master_send(client, skb->data, skb->len); + + if (r == -EREMOTEIO) { /* Retry, chip was in standby */ + usleep_range(6000, 10000); + r = i2c_master_send(client, skb->data, skb->len); + } + + if (r >= 0) { + if (r != skb->len) + r = -EREMOTEIO; + else + r = 0; + } + + microread_i2c_remove_len_crc(skb); + + return r; +} + + +static int microread_i2c_read(struct microread_i2c_phy *phy, + struct sk_buff **skb) +{ + int r; + u8 len; + u8 tmp[MICROREAD_I2C_LLC_MAX_SIZE - 1]; + struct i2c_client *client = phy->i2c_dev; + + r = i2c_master_recv(client, &len, 1); + if (r != 1) { + nfc_err(&client->dev, "cannot read len byte\n"); + return -EREMOTEIO; + } + + if ((len < MICROREAD_I2C_LLC_MIN_SIZE) || + (len > MICROREAD_I2C_LLC_MAX_SIZE)) { + nfc_err(&client->dev, "invalid len byte\n"); + r = -EBADMSG; + goto flush; + } + + *skb = alloc_skb(1 + len, GFP_KERNEL); + if (*skb == NULL) { + r = -ENOMEM; + goto flush; + } + + *skb_put(*skb, 1) = len; + + r = i2c_master_recv(client, skb_put(*skb, len), len); + if (r != len) { + kfree_skb(*skb); + return -EREMOTEIO; + } + + I2C_DUMP_SKB("cc frame read", *skb); + + r = check_crc(*skb); + if (r != 0) { + kfree_skb(*skb); + r = -EBADMSG; + goto flush; + } + + skb_pull(*skb, 1); + skb_trim(*skb, (*skb)->len - MICROREAD_I2C_FRAME_TAILROOM); + + usleep_range(3000, 6000); + + return 0; + +flush: + if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0) + r = -EREMOTEIO; + + usleep_range(3000, 6000); + + return r; +} + +static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id) +{ + struct microread_i2c_phy *phy = phy_id; + struct i2c_client *client; + struct sk_buff *skb = NULL; + int r; + + if (!phy || irq != phy->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + + client = phy->i2c_dev; + + if (phy->hard_fault != 0) + return IRQ_HANDLED; + + r = microread_i2c_read(phy, &skb); + if (r == -EREMOTEIO) { + phy->hard_fault = r; + + nfc_hci_recv_frame(phy->hdev, NULL); + + return IRQ_HANDLED; + } else if ((r == -ENOMEM) || (r == -EBADMSG)) { + return IRQ_HANDLED; + } + + nfc_hci_recv_frame(phy->hdev, skb); + + return IRQ_HANDLED; +} + +static struct nfc_phy_ops i2c_phy_ops = { + .write = microread_i2c_write, + .enable = microread_i2c_enable, + .disable = microread_i2c_disable, +}; + +static int microread_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct microread_i2c_phy *phy; + struct microread_nfc_platform_data *pdata = + dev_get_platdata(&client->dev); + int r; + + dev_dbg(&client->dev, "client %p\n", client); + + if (!pdata) { + nfc_err(&client->dev, "client %p: missing platform data\n", + client); + return -EINVAL; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + i2c_set_clientdata(client, phy); + phy->i2c_dev = client; + + r = request_threaded_irq(client->irq, NULL, microread_i2c_irq_thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + MICROREAD_I2C_DRIVER_NAME, phy); + if (r) { + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + return r; + } + + r = microread_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, + MICROREAD_I2C_FRAME_HEADROOM, + MICROREAD_I2C_FRAME_TAILROOM, + MICROREAD_I2C_LLC_MAX_PAYLOAD, &phy->hdev); + if (r < 0) + goto err_irq; + + nfc_info(&client->dev, "Probed\n"); + + return 0; + +err_irq: + free_irq(client->irq, phy); + + return r; +} + +static int microread_i2c_remove(struct i2c_client *client) +{ + struct microread_i2c_phy *phy = i2c_get_clientdata(client); + + microread_remove(phy->hdev); + + free_irq(client->irq, phy); + + return 0; +} + +static struct i2c_device_id microread_i2c_id[] = { + { MICROREAD_I2C_DRIVER_NAME, 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, microread_i2c_id); + +static struct i2c_driver microread_i2c_driver = { + .driver = { + .name = MICROREAD_I2C_DRIVER_NAME, + }, + .probe = microread_i2c_probe, + .remove = microread_i2c_remove, + .id_table = microread_i2c_id, +}; + +module_i2c_driver(microread_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c new file mode 100644 index 000000000..2d1395be6 --- /dev/null +++ b/drivers/nfc/microread/mei.c @@ -0,0 +1,109 @@ +/* + * HCI based Driver for Inside Secure microread NFC Chip + * + * Copyright (C) 2013 Intel Corporation. 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/module.h> +#include <linux/mod_devicetable.h> +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "../mei_phy.h" +#include "microread.h" + +#define MICROREAD_DRIVER_NAME "microread" + +static int microread_mei_probe(struct mei_cl_device *device, + const struct mei_cl_device_id *id) +{ + struct nfc_mei_phy *phy; + int r; + + pr_info("Probing NFC microread\n"); + + phy = nfc_mei_phy_alloc(device); + if (!phy) { + pr_err("Cannot allocate memory for microread mei phy.\n"); + return -ENOMEM; + } + + r = microread_probe(phy, &mei_phy_ops, LLC_NOP_NAME, + MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD, + &phy->hdev); + if (r < 0) { + nfc_mei_phy_free(phy); + + return r; + } + + return 0; +} + +static int microread_mei_remove(struct mei_cl_device *device) +{ + struct nfc_mei_phy *phy = mei_cl_get_drvdata(device); + + microread_remove(phy->hdev); + + nfc_mei_phy_free(phy); + + return 0; +} + +static struct mei_cl_device_id microread_mei_tbl[] = { + { MICROREAD_DRIVER_NAME }, + + /* required last entry */ + { } +}; +MODULE_DEVICE_TABLE(mei, microread_mei_tbl); + +static struct mei_cl_driver microread_driver = { + .id_table = microread_mei_tbl, + .name = MICROREAD_DRIVER_NAME, + + .probe = microread_mei_probe, + .remove = microread_mei_remove, +}; + +static int microread_mei_init(void) +{ + int r; + + pr_debug(DRIVER_DESC ": %s\n", __func__); + + r = mei_cl_driver_register(µread_driver); + if (r) { + pr_err(MICROREAD_DRIVER_NAME ": driver registration failed\n"); + return r; + } + + return 0; +} + +static void microread_mei_exit(void) +{ + mei_cl_driver_unregister(µread_driver); +} + +module_init(microread_mei_init); +module_exit(microread_mei_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/microread/microread.c b/drivers/nfc/microread/microread.c new file mode 100644 index 000000000..f454dc68c --- /dev/null +++ b/drivers/nfc/microread/microread.c @@ -0,0 +1,734 @@ +/* + * HCI based Driver for Inside Secure microread NFC Chip + * + * Copyright (C) 2013 Intel Corporation. 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/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/crc-ccitt.h> + +#include <linux/nfc.h> +#include <net/nfc/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "microread.h" + +/* Proprietary gates, events, commands and registers */ +/* Admin */ +#define MICROREAD_GATE_ID_ADM NFC_HCI_ADMIN_GATE +#define MICROREAD_GATE_ID_MGT 0x01 +#define MICROREAD_GATE_ID_OS 0x02 +#define MICROREAD_GATE_ID_TESTRF 0x03 +#define MICROREAD_GATE_ID_LOOPBACK NFC_HCI_LOOPBACK_GATE +#define MICROREAD_GATE_ID_IDT NFC_HCI_ID_MGMT_GATE +#define MICROREAD_GATE_ID_LMS NFC_HCI_LINK_MGMT_GATE + +/* Reader */ +#define MICROREAD_GATE_ID_MREAD_GEN 0x10 +#define MICROREAD_GATE_ID_MREAD_ISO_B NFC_HCI_RF_READER_B_GATE +#define MICROREAD_GATE_ID_MREAD_NFC_T1 0x12 +#define MICROREAD_GATE_ID_MREAD_ISO_A NFC_HCI_RF_READER_A_GATE +#define MICROREAD_GATE_ID_MREAD_NFC_T3 0x14 +#define MICROREAD_GATE_ID_MREAD_ISO_15_3 0x15 +#define MICROREAD_GATE_ID_MREAD_ISO_15_2 0x16 +#define MICROREAD_GATE_ID_MREAD_ISO_B_3 0x17 +#define MICROREAD_GATE_ID_MREAD_BPRIME 0x18 +#define MICROREAD_GATE_ID_MREAD_ISO_A_3 0x19 + +/* Card */ +#define MICROREAD_GATE_ID_MCARD_GEN 0x20 +#define MICROREAD_GATE_ID_MCARD_ISO_B 0x21 +#define MICROREAD_GATE_ID_MCARD_BPRIME 0x22 +#define MICROREAD_GATE_ID_MCARD_ISO_A 0x23 +#define MICROREAD_GATE_ID_MCARD_NFC_T3 0x24 +#define MICROREAD_GATE_ID_MCARD_ISO_15_3 0x25 +#define MICROREAD_GATE_ID_MCARD_ISO_15_2 0x26 +#define MICROREAD_GATE_ID_MCARD_ISO_B_2 0x27 +#define MICROREAD_GATE_ID_MCARD_ISO_CUSTOM 0x28 +#define MICROREAD_GATE_ID_SECURE_ELEMENT 0x2F + +/* P2P */ +#define MICROREAD_GATE_ID_P2P_GEN 0x30 +#define MICROREAD_GATE_ID_P2P_TARGET 0x31 +#define MICROREAD_PAR_P2P_TARGET_MODE 0x01 +#define MICROREAD_PAR_P2P_TARGET_GT 0x04 +#define MICROREAD_GATE_ID_P2P_INITIATOR 0x32 +#define MICROREAD_PAR_P2P_INITIATOR_GI 0x01 +#define MICROREAD_PAR_P2P_INITIATOR_GT 0x03 + +/* Those pipes are created/opened by default in the chip */ +#define MICROREAD_PIPE_ID_LMS 0x00 +#define MICROREAD_PIPE_ID_ADMIN 0x01 +#define MICROREAD_PIPE_ID_MGT 0x02 +#define MICROREAD_PIPE_ID_OS 0x03 +#define MICROREAD_PIPE_ID_HDS_LOOPBACK 0x04 +#define MICROREAD_PIPE_ID_HDS_IDT 0x05 +#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_B 0x08 +#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_BPRIME 0x09 +#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_A 0x0A +#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_15_3 0x0B +#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_15_2 0x0C +#define MICROREAD_PIPE_ID_HDS_MCARD_NFC_T3 0x0D +#define MICROREAD_PIPE_ID_HDS_MCARD_ISO_B_2 0x0E +#define MICROREAD_PIPE_ID_HDS_MCARD_CUSTOM 0x0F +#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_B 0x10 +#define MICROREAD_PIPE_ID_HDS_MREAD_NFC_T1 0x11 +#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_A 0x12 +#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_15_3 0x13 +#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_15_2 0x14 +#define MICROREAD_PIPE_ID_HDS_MREAD_NFC_T3 0x15 +#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_B_3 0x16 +#define MICROREAD_PIPE_ID_HDS_MREAD_BPRIME 0x17 +#define MICROREAD_PIPE_ID_HDS_MREAD_ISO_A_3 0x18 +#define MICROREAD_PIPE_ID_HDS_MREAD_GEN 0x1B +#define MICROREAD_PIPE_ID_HDS_STACKED_ELEMENT 0x1C +#define MICROREAD_PIPE_ID_HDS_INSTANCES 0x1D +#define MICROREAD_PIPE_ID_HDS_TESTRF 0x1E +#define MICROREAD_PIPE_ID_HDS_P2P_TARGET 0x1F +#define MICROREAD_PIPE_ID_HDS_P2P_INITIATOR 0x20 + +/* Events */ +#define MICROREAD_EVT_MREAD_DISCOVERY_OCCURED NFC_HCI_EVT_TARGET_DISCOVERED +#define MICROREAD_EVT_MREAD_CARD_FOUND 0x3D +#define MICROREAD_EMCF_A_ATQA 0 +#define MICROREAD_EMCF_A_SAK 2 +#define MICROREAD_EMCF_A_LEN 3 +#define MICROREAD_EMCF_A_UID 4 +#define MICROREAD_EMCF_A3_ATQA 0 +#define MICROREAD_EMCF_A3_SAK 2 +#define MICROREAD_EMCF_A3_LEN 3 +#define MICROREAD_EMCF_A3_UID 4 +#define MICROREAD_EMCF_B_UID 0 +#define MICROREAD_EMCF_T1_ATQA 0 +#define MICROREAD_EMCF_T1_UID 4 +#define MICROREAD_EMCF_T3_UID 0 +#define MICROREAD_EVT_MREAD_DISCOVERY_START NFC_HCI_EVT_READER_REQUESTED +#define MICROREAD_EVT_MREAD_DISCOVERY_START_SOME 0x3E +#define MICROREAD_EVT_MREAD_DISCOVERY_STOP NFC_HCI_EVT_END_OPERATION +#define MICROREAD_EVT_MREAD_SIM_REQUESTS 0x3F +#define MICROREAD_EVT_MCARD_EXCHANGE NFC_HCI_EVT_TARGET_DISCOVERED +#define MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_TO_RF 0x20 +#define MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_FROM_RF 0x21 +#define MICROREAD_EVT_MCARD_FIELD_ON 0x11 +#define MICROREAD_EVT_P2P_TARGET_ACTIVATED 0x13 +#define MICROREAD_EVT_P2P_TARGET_DEACTIVATED 0x12 +#define MICROREAD_EVT_MCARD_FIELD_OFF 0x14 + +/* Commands */ +#define MICROREAD_CMD_MREAD_EXCHANGE 0x10 +#define MICROREAD_CMD_MREAD_SUBSCRIBE 0x3F + +/* Hosts IDs */ +#define MICROREAD_ELT_ID_HDS NFC_HCI_TERMINAL_HOST_ID +#define MICROREAD_ELT_ID_SIM NFC_HCI_UICC_HOST_ID +#define MICROREAD_ELT_ID_SE1 0x03 +#define MICROREAD_ELT_ID_SE2 0x04 +#define MICROREAD_ELT_ID_SE3 0x05 + +static struct nfc_hci_gate microread_gates[] = { + {MICROREAD_GATE_ID_ADM, MICROREAD_PIPE_ID_ADMIN}, + {MICROREAD_GATE_ID_LOOPBACK, MICROREAD_PIPE_ID_HDS_LOOPBACK}, + {MICROREAD_GATE_ID_IDT, MICROREAD_PIPE_ID_HDS_IDT}, + {MICROREAD_GATE_ID_LMS, MICROREAD_PIPE_ID_LMS}, + {MICROREAD_GATE_ID_MREAD_ISO_B, MICROREAD_PIPE_ID_HDS_MREAD_ISO_B}, + {MICROREAD_GATE_ID_MREAD_ISO_A, MICROREAD_PIPE_ID_HDS_MREAD_ISO_A}, + {MICROREAD_GATE_ID_MREAD_ISO_A_3, MICROREAD_PIPE_ID_HDS_MREAD_ISO_A_3}, + {MICROREAD_GATE_ID_MGT, MICROREAD_PIPE_ID_MGT}, + {MICROREAD_GATE_ID_OS, MICROREAD_PIPE_ID_OS}, + {MICROREAD_GATE_ID_MREAD_NFC_T1, MICROREAD_PIPE_ID_HDS_MREAD_NFC_T1}, + {MICROREAD_GATE_ID_MREAD_NFC_T3, MICROREAD_PIPE_ID_HDS_MREAD_NFC_T3}, + {MICROREAD_GATE_ID_P2P_TARGET, MICROREAD_PIPE_ID_HDS_P2P_TARGET}, + {MICROREAD_GATE_ID_P2P_INITIATOR, MICROREAD_PIPE_ID_HDS_P2P_INITIATOR} +}; + +/* Largest headroom needed for outgoing custom commands */ +#define MICROREAD_CMDS_HEADROOM 2 +#define MICROREAD_CMD_TAILROOM 2 + +struct microread_info { + struct nfc_phy_ops *phy_ops; + void *phy_id; + + struct nfc_hci_dev *hdev; + + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; +}; + +static int microread_open(struct nfc_hci_dev *hdev) +{ + struct microread_info *info = nfc_hci_get_clientdata(hdev); + + return info->phy_ops->enable(info->phy_id); +} + +static void microread_close(struct nfc_hci_dev *hdev) +{ + struct microread_info *info = nfc_hci_get_clientdata(hdev); + + info->phy_ops->disable(info->phy_id); +} + +static int microread_hci_ready(struct nfc_hci_dev *hdev) +{ + int r; + u8 param[4]; + + param[0] = 0x03; + r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_A, + MICROREAD_CMD_MREAD_SUBSCRIBE, param, 1, NULL); + if (r) + return r; + + r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_A_3, + MICROREAD_CMD_MREAD_SUBSCRIBE, NULL, 0, NULL); + if (r) + return r; + + param[0] = 0x00; + param[1] = 0x03; + param[2] = 0x00; + r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_ISO_B, + MICROREAD_CMD_MREAD_SUBSCRIBE, param, 3, NULL); + if (r) + return r; + + r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_NFC_T1, + MICROREAD_CMD_MREAD_SUBSCRIBE, NULL, 0, NULL); + if (r) + return r; + + param[0] = 0xFF; + param[1] = 0xFF; + param[2] = 0x00; + param[3] = 0x00; + r = nfc_hci_send_cmd(hdev, MICROREAD_GATE_ID_MREAD_NFC_T3, + MICROREAD_CMD_MREAD_SUBSCRIBE, param, 4, NULL); + + return r; +} + +static int microread_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct microread_info *info = nfc_hci_get_clientdata(hdev); + + return info->phy_ops->write(info->phy_id, skb); +} + +static int microread_start_poll(struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols) +{ + int r; + + u8 param[2]; + u8 mode; + + param[0] = 0x00; + param[1] = 0x00; + + if (im_protocols & NFC_PROTO_ISO14443_MASK) + param[0] |= (1 << 2); + + if (im_protocols & NFC_PROTO_ISO14443_B_MASK) + param[0] |= 1; + + if (im_protocols & NFC_PROTO_MIFARE_MASK) + param[1] |= 1; + + if (im_protocols & NFC_PROTO_JEWEL_MASK) + param[0] |= (1 << 1); + + if (im_protocols & NFC_PROTO_FELICA_MASK) + param[0] |= (1 << 5); + + if (im_protocols & NFC_PROTO_NFC_DEP_MASK) + param[1] |= (1 << 1); + + if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { + 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; + } + } + + r = nfc_hci_send_event(hdev, MICROREAD_GATE_ID_MREAD_ISO_A, + MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, 0); + if (r) + return r; + + mode = 0xff; + r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET, + MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1); + if (r) + return r; + + if (im_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_INITIATOR, + MICROREAD_PAR_P2P_INITIATOR_GI, + hdev->gb, hdev->gb_len); + if (r) + return r; + } + + if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET, + MICROREAD_PAR_P2P_TARGET_GT, + hdev->gb, hdev->gb_len); + if (r) + return r; + + mode = 0x02; + r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET, + MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1); + if (r) + return r; + } + + return nfc_hci_send_event(hdev, MICROREAD_GATE_ID_MREAD_ISO_A, + MICROREAD_EVT_MREAD_DISCOVERY_START_SOME, + param, 2); +} + +static int microread_dep_link_up(struct nfc_hci_dev *hdev, + struct nfc_target *target, u8 comm_mode, + u8 *gb, size_t gb_len) +{ + struct sk_buff *rgb_skb = NULL; + int r; + + r = nfc_hci_get_param(hdev, target->hci_reader_gate, + MICROREAD_PAR_P2P_INITIATOR_GT, &rgb_skb); + if (r < 0) + return r; + + if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) { + r = -EPROTO; + goto exit; + } + + r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data, + rgb_skb->len); + if (r == 0) + r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode, + NFC_RF_INITIATOR); +exit: + kfree_skb(rgb_skb); + + return r; +} + +static int microread_dep_link_down(struct nfc_hci_dev *hdev) +{ + return nfc_hci_send_event(hdev, MICROREAD_GATE_ID_P2P_INITIATOR, + MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, 0); +} + +static int microread_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, + struct nfc_target *target) +{ + switch (gate) { + case MICROREAD_GATE_ID_P2P_INITIATOR: + target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + break; + default: + return -EPROTO; + } + + return 0; +} + +static int microread_complete_target_discovered(struct nfc_hci_dev *hdev, + u8 gate, + struct nfc_target *target) +{ + return 0; +} + +#define MICROREAD_CB_TYPE_READER_ALL 1 + +static void microread_im_transceive_cb(void *context, struct sk_buff *skb, + int err) +{ + struct microread_info *info = context; + + switch (info->async_cb_type) { + case MICROREAD_CB_TYPE_READER_ALL: + if (err == 0) { + if (skb->len == 0) { + err = -EPROTO; + kfree_skb(skb); + info->async_cb(info->async_cb_context, NULL, + -EPROTO); + return; + } + + if (skb->data[skb->len - 1] != 0) { + err = nfc_hci_result_to_errno( + skb->data[skb->len - 1]); + kfree_skb(skb); + info->async_cb(info->async_cb_context, NULL, + err); + return; + } + + skb_trim(skb, skb->len - 1); /* RF Error ind. */ + } + 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 microread_im_transceive(struct nfc_hci_dev *hdev, + struct nfc_target *target, + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context) +{ + struct microread_info *info = nfc_hci_get_clientdata(hdev); + u8 control_bits; + u16 crc; + + pr_info("data exchange to gate 0x%x\n", target->hci_reader_gate); + + if (target->hci_reader_gate == MICROREAD_GATE_ID_P2P_INITIATOR) { + *skb_push(skb, 1) = 0; + + return nfc_hci_send_event(hdev, target->hci_reader_gate, + MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_TO_RF, + skb->data, skb->len); + } + + switch (target->hci_reader_gate) { + case MICROREAD_GATE_ID_MREAD_ISO_A: + control_bits = 0xCB; + break; + case MICROREAD_GATE_ID_MREAD_ISO_A_3: + control_bits = 0xCB; + break; + case MICROREAD_GATE_ID_MREAD_ISO_B: + control_bits = 0xCB; + break; + case MICROREAD_GATE_ID_MREAD_NFC_T1: + control_bits = 0x1B; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + *skb_put(skb, 1) = crc & 0xff; + *skb_put(skb, 1) = crc >> 8; + break; + case MICROREAD_GATE_ID_MREAD_NFC_T3: + control_bits = 0xDB; + break; + default: + pr_info("Abort im_transceive to invalid gate 0x%x\n", + target->hci_reader_gate); + return 1; + } + + *skb_push(skb, 1) = control_bits; + + info->async_cb_type = MICROREAD_CB_TYPE_READER_ALL; + info->async_cb = cb; + info->async_cb_context = cb_context; + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + MICROREAD_CMD_MREAD_EXCHANGE, + skb->data, skb->len, + microread_im_transceive_cb, info); +} + +static int microread_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + int r; + + r = nfc_hci_send_event(hdev, MICROREAD_GATE_ID_P2P_TARGET, + MICROREAD_EVT_MCARD_EXCHANGE, + skb->data, skb->len); + + kfree_skb(skb); + + return r; +} + +static void microread_target_discovered(struct nfc_hci_dev *hdev, u8 gate, + struct sk_buff *skb) +{ + struct nfc_target *targets; + int r = 0; + + pr_info("target discovered to gate 0x%x\n", gate); + + targets = kzalloc(sizeof(struct nfc_target), GFP_KERNEL); + if (targets == NULL) { + r = -ENOMEM; + goto exit; + } + + targets->hci_reader_gate = gate; + + switch (gate) { + case MICROREAD_GATE_ID_MREAD_ISO_A: + targets->supported_protocols = + nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A_SAK]); + targets->sens_res = + be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A_ATQA]); + targets->sel_res = skb->data[MICROREAD_EMCF_A_SAK]; + targets->nfcid1_len = skb->data[MICROREAD_EMCF_A_LEN]; + if (targets->nfcid1_len > sizeof(targets->nfcid1)) { + r = -EINVAL; + goto exit_free; + } + memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A_UID], + targets->nfcid1_len); + break; + case MICROREAD_GATE_ID_MREAD_ISO_A_3: + targets->supported_protocols = + nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A3_SAK]); + targets->sens_res = + be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A3_ATQA]); + targets->sel_res = skb->data[MICROREAD_EMCF_A3_SAK]; + targets->nfcid1_len = skb->data[MICROREAD_EMCF_A3_LEN]; + if (targets->nfcid1_len > sizeof(targets->nfcid1)) { + r = -EINVAL; + goto exit_free; + } + memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A3_UID], + targets->nfcid1_len); + break; + case MICROREAD_GATE_ID_MREAD_ISO_B: + targets->supported_protocols = NFC_PROTO_ISO14443_B_MASK; + memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_B_UID], 4); + targets->nfcid1_len = 4; + break; + case MICROREAD_GATE_ID_MREAD_NFC_T1: + targets->supported_protocols = NFC_PROTO_JEWEL_MASK; + targets->sens_res = + le16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_T1_ATQA]); + memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T1_UID], 4); + targets->nfcid1_len = 4; + break; + case MICROREAD_GATE_ID_MREAD_NFC_T3: + targets->supported_protocols = NFC_PROTO_FELICA_MASK; + memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T3_UID], 8); + targets->nfcid1_len = 8; + break; + default: + pr_info("discard target discovered to gate 0x%x\n", gate); + goto exit_free; + } + + r = nfc_targets_found(hdev->ndev, targets, 1); + +exit_free: + kfree(targets); + +exit: + kfree_skb(skb); + + if (r) + pr_err("Failed to handle discovered target err=%d\n", r); +} + +static int microread_event_received(struct nfc_hci_dev *hdev, u8 pipe, + u8 event, struct sk_buff *skb) +{ + int r; + u8 gate = hdev->pipes[pipe].gate; + u8 mode; + + pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate); + + switch (event) { + case MICROREAD_EVT_MREAD_CARD_FOUND: + microread_target_discovered(hdev, gate, skb); + return 0; + + case MICROREAD_EVT_P2P_INITIATOR_EXCHANGE_FROM_RF: + if (skb->len < 1) { + kfree_skb(skb); + return -EPROTO; + } + + if (skb->data[skb->len - 1]) { + kfree_skb(skb); + return -EIO; + } + + skb_trim(skb, skb->len - 1); + + r = nfc_tm_data_received(hdev->ndev, skb); + break; + + case MICROREAD_EVT_MCARD_FIELD_ON: + case MICROREAD_EVT_MCARD_FIELD_OFF: + kfree_skb(skb); + return 0; + + case MICROREAD_EVT_P2P_TARGET_ACTIVATED: + r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, skb->data, + skb->len); + + kfree_skb(skb); + break; + + case MICROREAD_EVT_MCARD_EXCHANGE: + if (skb->len < 1) { + kfree_skb(skb); + return -EPROTO; + } + + if (skb->data[skb->len-1]) { + kfree_skb(skb); + return -EIO; + } + + skb_trim(skb, skb->len - 1); + + r = nfc_tm_data_received(hdev->ndev, skb); + break; + + case MICROREAD_EVT_P2P_TARGET_DEACTIVATED: + kfree_skb(skb); + + mode = 0xff; + r = nfc_hci_set_param(hdev, MICROREAD_GATE_ID_P2P_TARGET, + MICROREAD_PAR_P2P_TARGET_MODE, &mode, 1); + if (r) + break; + + r = nfc_hci_send_event(hdev, gate, + MICROREAD_EVT_MREAD_DISCOVERY_STOP, NULL, + 0); + break; + + default: + return 1; + } + + return r; +} + +static struct nfc_hci_ops microread_hci_ops = { + .open = microread_open, + .close = microread_close, + .hci_ready = microread_hci_ready, + .xmit = microread_xmit, + .start_poll = microread_start_poll, + .dep_link_up = microread_dep_link_up, + .dep_link_down = microread_dep_link_down, + .target_from_gate = microread_target_from_gate, + .complete_target_discovered = microread_complete_target_discovered, + .im_transceive = microread_im_transceive, + .tm_send = microread_tm_send, + .check_presence = NULL, + .event_received = microread_event_received, +}; + +int microread_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 microread_info *info; + unsigned long quirks = 0; + u32 protocols; + struct nfc_hci_init_data init_data; + int r; + + info = kzalloc(sizeof(struct microread_info), GFP_KERNEL); + if (!info) { + r = -ENOMEM; + goto err_info_alloc; + } + + info->phy_ops = phy_ops; + info->phy_id = phy_id; + + init_data.gate_count = ARRAY_SIZE(microread_gates); + memcpy(init_data.gates, microread_gates, sizeof(microread_gates)); + + strcpy(init_data.session_id, "MICROREA"); + + set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks); + + protocols = NFC_PROTO_JEWEL_MASK | + NFC_PROTO_MIFARE_MASK | + NFC_PROTO_FELICA_MASK | + NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK | + NFC_PROTO_NFC_DEP_MASK; + + info->hdev = nfc_hci_allocate_device(µread_hci_ops, &init_data, + quirks, protocols, llc_name, + phy_headroom + + MICROREAD_CMDS_HEADROOM, + phy_tailroom + + MICROREAD_CMD_TAILROOM, + phy_payload); + if (!info->hdev) { + pr_err("Cannot allocate nfc hdev\n"); + r = -ENOMEM; + goto err_alloc_hdev; + } + + nfc_hci_set_clientdata(info->hdev, info); + + r = nfc_hci_register_device(info->hdev); + if (r) + goto err_regdev; + + *hdev = info->hdev; + + return 0; + +err_regdev: + nfc_hci_free_device(info->hdev); + +err_alloc_hdev: + kfree(info); + +err_info_alloc: + return r; +} +EXPORT_SYMBOL(microread_probe); + +void microread_remove(struct nfc_hci_dev *hdev) +{ + struct microread_info *info = nfc_hci_get_clientdata(hdev); + + nfc_hci_unregister_device(hdev); + nfc_hci_free_device(hdev); + kfree(info); +} +EXPORT_SYMBOL(microread_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/microread/microread.h b/drivers/nfc/microread/microread.h new file mode 100644 index 000000000..f53864143 --- /dev/null +++ b/drivers/nfc/microread/microread.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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_MICROREAD_H_ +#define __LOCAL_MICROREAD_H_ + +#include <net/nfc/hci.h> + +#define DRIVER_DESC "NFC driver for microread" + +int microread_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); + +void microread_remove(struct nfc_hci_dev *hdev); + +#endif /* __LOCAL_MICROREAD_H_ */ diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig new file mode 100644 index 000000000..5e18afd9a --- /dev/null +++ b/drivers/nfc/nfcmrvl/Kconfig @@ -0,0 +1,23 @@ +config NFC_MRVL + tristate "Marvell NFC driver support" + depends on NFC_NCI + help + The core driver to support Marvell NFC devices. + + This driver is required if you want to support + Marvell NFC device 8897. + + Say Y here to compile Marvell NFC driver into the kernel or + say M to compile it as module. + +config NFC_MRVL_USB + tristate "Marvell NFC-over-USB driver" + depends on NFC_MRVL && USB + help + Marvell NFC-over-USB driver. + + This driver provides support for Marvell NFC-over-USB devices: + 8897. + + Say Y here to compile support for Marvell NFC-over-USB driver + into the kernel or say M to compile it as module. diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile new file mode 100644 index 000000000..97a0de72d --- /dev/null +++ b/drivers/nfc/nfcmrvl/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for NFCMRVL NCI based NFC driver +# + +nfcmrvl-y += main.o +obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o + +nfcmrvl_usb-y += usb.o +obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c new file mode 100644 index 000000000..ad4933cef --- /dev/null +++ b/drivers/nfc/nfcmrvl/main.c @@ -0,0 +1,165 @@ +/* + * Marvell NFC driver: major functions + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include <linux/module.h> +#include <linux/nfc.h> +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> +#include "nfcmrvl.h" + +#define VERSION "1.0" + +static int nfcmrvl_nci_open(struct nci_dev *ndev) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + int err; + + if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return 0; + + err = priv->if_ops->nci_open(priv); + + if (err) + clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags); + + return err; +} + +static int nfcmrvl_nci_close(struct nci_dev *ndev) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + + if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return 0; + + priv->if_ops->nci_close(priv); + + return 0; +} + +static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + + nfc_info(priv->dev, "send entry, len %d\n", skb->len); + + skb->dev = (void *)ndev; + + if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return -EBUSY; + + return priv->if_ops->nci_send(priv, skb); +} + +static int nfcmrvl_nci_setup(struct nci_dev *ndev) +{ + __u8 val; + + val = NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED; + nci_set_config(ndev, NFCMRVL_NOT_ALLOWED_ID, 1, &val); + val = NFCMRVL_GPIO_PIN_NFC_ACTIVE; + nci_set_config(ndev, NFCMRVL_ACTIVE_ID, 1, &val); + val = NFCMRVL_EXT_COEX_ENABLE; + nci_set_config(ndev, NFCMRVL_EXT_COEX_ID, 1, &val); + + return 0; +} + +static struct nci_ops nfcmrvl_nci_ops = { + .open = nfcmrvl_nci_open, + .close = nfcmrvl_nci_close, + .send = nfcmrvl_nci_send, + .setup = nfcmrvl_nci_setup, +}; + +struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, + struct nfcmrvl_if_ops *ops, + struct device *dev) +{ + struct nfcmrvl_private *priv; + int rc; + u32 protocols; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return ERR_PTR(-ENOMEM); + + priv->drv_data = drv_data; + priv->if_ops = ops; + priv->dev = dev; + + protocols = NFC_PROTO_JEWEL_MASK + | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK + | NFC_PROTO_ISO14443_MASK + | NFC_PROTO_ISO14443_B_MASK + | NFC_PROTO_NFC_DEP_MASK; + + priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0); + if (!priv->ndev) { + nfc_err(dev, "nci_allocate_device failed\n"); + rc = -ENOMEM; + goto error; + } + + nci_set_drvdata(priv->ndev, priv); + + rc = nci_register_device(priv->ndev); + if (rc) { + nfc_err(dev, "nci_register_device failed %d\n", rc); + nci_free_device(priv->ndev); + goto error; + } + + nfc_info(dev, "registered with nci successfully\n"); + return priv; + +error: + kfree(priv); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev); + +void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) +{ + struct nci_dev *ndev = priv->ndev; + + nci_unregister_device(ndev); + nci_free_device(ndev); + kfree(priv); +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev); + +int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count) +{ + struct sk_buff *skb; + + skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, count), data, count); + nci_recv_frame(priv->ndev, skb); + + return count; +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell NFC driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h new file mode 100644 index 000000000..54c4a956b --- /dev/null +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -0,0 +1,48 @@ +/** + * Marvell NFC driver + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + **/ + +/* Define private flags: */ +#define NFCMRVL_NCI_RUNNING 1 + +#define NFCMRVL_EXT_COEX_ID 0xE0 +#define NFCMRVL_NOT_ALLOWED_ID 0xE1 +#define NFCMRVL_ACTIVE_ID 0xE2 +#define NFCMRVL_EXT_COEX_ENABLE 1 +#define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA +#define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB +#define NFCMRVL_NCI_MAX_EVENT_SIZE 260 + +struct nfcmrvl_private { + struct nci_dev *ndev; + unsigned long flags; + void *drv_data; + struct device *dev; + struct nfcmrvl_if_ops *if_ops; +}; + +struct nfcmrvl_if_ops { + int (*nci_open) (struct nfcmrvl_private *priv); + int (*nci_close) (struct nfcmrvl_private *priv); + int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb); +}; + +void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv); +int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count); +struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, + struct nfcmrvl_if_ops *ops, + struct device *dev); diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c new file mode 100644 index 000000000..6cf15c1a2 --- /dev/null +++ b/drivers/nfc/nfcmrvl/usb.c @@ -0,0 +1,459 @@ +/** + * Marvell NFC-over-USB driver: USB interface related functions + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + **/ + +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/nfc.h> +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> +#include "nfcmrvl.h" + +#define VERSION "1.0" + +static struct usb_device_id nfcmrvl_table[] = { + { USB_DEVICE_INTERFACE_CLASS(0x1286, 0x2046, 0xff) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, nfcmrvl_table); + +#define NFCMRVL_USB_BULK_RUNNING 1 +#define NFCMRVL_USB_SUSPENDING 2 + +struct nfcmrvl_usb_drv_data { + struct usb_device *udev; + struct usb_interface *intf; + unsigned long flags; + struct work_struct waker; + struct usb_anchor tx_anchor; + struct usb_anchor bulk_anchor; + struct usb_anchor deferred; + int tx_in_flight; + /* protects tx_in_flight */ + spinlock_t txlock; + struct usb_endpoint_descriptor *bulk_tx_ep; + struct usb_endpoint_descriptor *bulk_rx_ep; + int suspend_count; + struct nfcmrvl_private *priv; +}; + +static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) +{ + unsigned long flags; + int rv; + + spin_lock_irqsave(&drv_data->txlock, flags); + rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + if (!rv) + drv_data->tx_in_flight++; + spin_unlock_irqrestore(&drv_data->txlock, flags); + + return rv; +} + +static void nfcmrvl_bulk_complete(struct urb *urb) +{ + struct nfcmrvl_usb_drv_data *drv_data = urb->context; + int err; + + dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d", + urb, urb->status, urb->actual_length); + + if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) + return; + + if (!urb->status) { + if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer, + urb->actual_length) < 0) + nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n"); + } + + if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) + return; + + usb_anchor_urb(urb, &drv_data->bulk_anchor); + usb_mark_last_busy(drv_data->udev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + /* -EPERM: urb is being killed; + * -ENODEV: device got disconnected + */ + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p failed to resubmit (%d)\n", urb, -err); + usb_unanchor_urb(urb); + } +} + +static int +nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) +{ + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE; + + if (!drv_data->bulk_rx_ep) + return -ENODEV; + + urb = usb_alloc_urb(0, mem_flags); + if (!urb) + return -ENOMEM; + + buf = kmalloc(size, mem_flags); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(drv_data->udev, + drv_data->bulk_rx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, + nfcmrvl_bulk_complete, drv_data); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_mark_last_busy(drv_data->udev); + usb_anchor_urb(urb, &drv_data->bulk_anchor); + + err = usb_submit_urb(urb, mem_flags); + if (err) { + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p submission failed (%d)\n", urb, -err); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static void nfcmrvl_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct nci_dev *ndev = (struct nci_dev *)skb->dev; + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + + nfc_info(priv->dev, "urb %p status %d count %d\n", + urb, urb->status, urb->actual_length); + + spin_lock(&drv_data->txlock); + drv_data->tx_in_flight--; + spin_unlock(&drv_data->txlock); + + kfree(urb->setup_packet); + kfree_skb(skb); +} + +static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + int err; + + err = usb_autopm_get_interface(drv_data->intf); + if (err) + return err; + + drv_data->intf->needs_remote_wakeup = 1; + + err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); + if (err) + goto failed; + + set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); + + usb_autopm_put_interface(drv_data->intf); + return 0; + +failed: + usb_autopm_put_interface(drv_data->intf); + return err; +} + +static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) +{ + usb_kill_anchored_urbs(&drv_data->bulk_anchor); +} + +static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + int err; + + cancel_work_sync(&drv_data->waker); + + clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + + nfcmrvl_usb_stop_traffic(drv_data); + usb_kill_anchored_urbs(&drv_data->tx_anchor); + err = usb_autopm_get_interface(drv_data->intf); + if (err) + goto failed; + + drv_data->intf->needs_remote_wakeup = 0; + usb_autopm_put_interface(drv_data->intf); + +failed: + usb_scuttle_anchored_urbs(&drv_data->deferred); + return 0; +} + +static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, + struct sk_buff *skb) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + struct urb *urb; + unsigned int pipe; + int err; + + if (!drv_data->bulk_tx_ep) + return -ENODEV; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + pipe = usb_sndbulkpipe(drv_data->udev, + drv_data->bulk_tx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len, + nfcmrvl_tx_complete, skb); + + err = nfcmrvl_inc_tx(drv_data); + if (err) { + usb_anchor_urb(urb, &drv_data->deferred); + schedule_work(&drv_data->waker); + err = 0; + goto done; + } + + usb_anchor_urb(urb, &drv_data->tx_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p submission failed (%d)\n", urb, -err); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } else { + usb_mark_last_busy(drv_data->udev); + } + +done: + usb_free_urb(urb); + return err; +} + +static struct nfcmrvl_if_ops usb_ops = { + .nci_open = nfcmrvl_usb_nci_open, + .nci_close = nfcmrvl_usb_nci_close, + .nci_send = nfcmrvl_usb_nci_send, +}; + +static void nfcmrvl_waker(struct work_struct *work) +{ + struct nfcmrvl_usb_drv_data *drv_data = + container_of(work, struct nfcmrvl_usb_drv_data, waker); + int err; + + err = usb_autopm_get_interface(drv_data->intf); + if (err) + return; + + usb_autopm_put_interface(drv_data->intf); +} + +static int nfcmrvl_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *ep_desc; + struct nfcmrvl_usb_drv_data *drv_data; + struct nfcmrvl_private *priv; + int i; + struct usb_device *udev = interface_to_usbdev(intf); + + nfc_info(&udev->dev, "intf %p id %p\n", intf, id); + + drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + ep_desc = &intf->cur_altsetting->endpoint[i].desc; + + if (!drv_data->bulk_tx_ep && + usb_endpoint_is_bulk_out(ep_desc)) { + drv_data->bulk_tx_ep = ep_desc; + continue; + } + + if (!drv_data->bulk_rx_ep && + usb_endpoint_is_bulk_in(ep_desc)) { + drv_data->bulk_rx_ep = ep_desc; + continue; + } + } + + if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) + return -ENODEV; + + drv_data->udev = udev; + drv_data->intf = intf; + + INIT_WORK(&drv_data->waker, nfcmrvl_waker); + spin_lock_init(&drv_data->txlock); + + init_usb_anchor(&drv_data->tx_anchor); + init_usb_anchor(&drv_data->bulk_anchor); + init_usb_anchor(&drv_data->deferred); + + priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops, + &drv_data->udev->dev); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + drv_data->priv = priv; + priv->dev = &drv_data->udev->dev; + + usb_set_intfdata(intf, drv_data); + + return 0; +} + +static void nfcmrvl_disconnect(struct usb_interface *intf) +{ + struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); + + if (!drv_data) + return; + + nfc_info(&drv_data->udev->dev, "intf %p\n", intf); + + nfcmrvl_nci_unregister_dev(drv_data->priv); + + usb_set_intfdata(drv_data->intf, NULL); +} + +#ifdef CONFIG_PM +static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); + + nfc_info(&drv_data->udev->dev, "intf %p\n", intf); + + if (drv_data->suspend_count++) + return 0; + + spin_lock_irq(&drv_data->txlock); + if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { + set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + } else { + spin_unlock_irq(&drv_data->txlock); + drv_data->suspend_count--; + return -EBUSY; + } + + nfcmrvl_usb_stop_traffic(drv_data); + usb_kill_anchored_urbs(&drv_data->tx_anchor); + + return 0; +} + +static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) +{ + struct urb *urb; + int err; + + while ((urb = usb_get_from_anchor(&drv_data->deferred))) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) + break; + + drv_data->tx_in_flight++; + } + usb_scuttle_anchored_urbs(&drv_data->deferred); +} + +static int nfcmrvl_resume(struct usb_interface *intf) +{ + struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); + int err = 0; + + nfc_info(&drv_data->udev->dev, "intf %p\n", intf); + + if (--drv_data->suspend_count) + return 0; + + if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) + goto done; + + if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { + err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); + if (err) { + clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + goto failed; + } + + nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); + } + + spin_lock_irq(&drv_data->txlock); + nfcmrvl_play_deferred(drv_data); + clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + + return 0; + +failed: + usb_scuttle_anchored_urbs(&drv_data->deferred); +done: + spin_lock_irq(&drv_data->txlock); + clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + + return err; +} +#endif + +static struct usb_driver nfcmrvl_usb_driver = { + .name = "nfcmrvl", + .probe = nfcmrvl_probe, + .disconnect = nfcmrvl_disconnect, +#ifdef CONFIG_PM + .suspend = nfcmrvl_suspend, + .resume = nfcmrvl_resume, + .reset_resume = nfcmrvl_resume, +#endif + .id_table = nfcmrvl_table, + .supports_autosuspend = 1, + .disable_hub_initiated_lpm = 1, + .soft_unbind = 1, +}; +module_usb_driver(nfcmrvl_usb_driver); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell NFC-over-USB driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c new file mode 100644 index 000000000..93111fa8d --- /dev/null +++ b/drivers/nfc/nfcsim.c @@ -0,0 +1,541 @@ +/* + * NFC hardware simulation driver + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/nfc.h> +#include <net/nfc/nfc.h> + +#define DEV_ERR(_dev, fmt, args...) nfc_err(&_dev->nfc_dev->dev, \ + "%s: " fmt, __func__, ## args) + +#define DEV_DBG(_dev, fmt, args...) dev_dbg(&_dev->nfc_dev->dev, \ + "%s: " fmt, __func__, ## args) + +#define NFCSIM_VERSION "0.1" + +#define NFCSIM_POLL_NONE 0 +#define NFCSIM_POLL_INITIATOR 1 +#define NFCSIM_POLL_TARGET 2 +#define NFCSIM_POLL_DUAL (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET) + +struct nfcsim { + struct nfc_dev *nfc_dev; + + struct mutex lock; + + struct delayed_work recv_work; + + struct sk_buff *clone_skb; + + struct delayed_work poll_work; + u8 polling_mode; + u8 curr_polling_mode; + + u8 shutting_down; + + u8 up; + + u8 initiator; + + data_exchange_cb_t cb; + void *cb_context; + + struct nfcsim *peer_dev; +}; + +static struct nfcsim *dev0; +static struct nfcsim *dev1; + +static struct workqueue_struct *wq; + +static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown) +{ + DEV_DBG(dev, "shutdown=%d\n", shutdown); + + mutex_lock(&dev->lock); + + dev->polling_mode = NFCSIM_POLL_NONE; + dev->shutting_down = shutdown; + dev->cb = NULL; + dev_kfree_skb(dev->clone_skb); + dev->clone_skb = NULL; + + mutex_unlock(&dev->lock); + + cancel_delayed_work_sync(&dev->poll_work); + cancel_delayed_work_sync(&dev->recv_work); +} + +static int nfcsim_target_found(struct nfcsim *dev) +{ + struct nfc_target nfc_tgt; + + DEV_DBG(dev, "\n"); + + memset(&nfc_tgt, 0, sizeof(struct nfc_target)); + + nfc_tgt.supported_protocols = NFC_PROTO_NFC_DEP_MASK; + nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1); + + return 0; +} + +static int nfcsim_dev_up(struct nfc_dev *nfc_dev) +{ + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + + DEV_DBG(dev, "\n"); + + mutex_lock(&dev->lock); + + dev->up = 1; + + mutex_unlock(&dev->lock); + + return 0; +} + +static int nfcsim_dev_down(struct nfc_dev *nfc_dev) +{ + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + + DEV_DBG(dev, "\n"); + + mutex_lock(&dev->lock); + + dev->up = 0; + + mutex_unlock(&dev->lock); + + return 0; +} + +static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev, + struct nfc_target *target, + u8 comm_mode, u8 *gb, size_t gb_len) +{ + int rc; + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + struct nfcsim *peer = dev->peer_dev; + u8 *remote_gb; + size_t remote_gb_len; + + DEV_DBG(dev, "target_idx: %d, comm_mode: %d\n", target->idx, comm_mode); + + mutex_lock(&peer->lock); + + nfc_tm_activated(peer->nfc_dev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_ACTIVE, gb, gb_len); + + remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len); + if (!remote_gb) { + DEV_ERR(peer, "Can't get remote general bytes\n"); + + mutex_unlock(&peer->lock); + return -EINVAL; + } + + mutex_unlock(&peer->lock); + + mutex_lock(&dev->lock); + + rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len); + if (rc) { + DEV_ERR(dev, "Can't set remote general bytes\n"); + mutex_unlock(&dev->lock); + return rc; + } + + rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_ACTIVE, + NFC_RF_INITIATOR); + + mutex_unlock(&dev->lock); + + return rc; +} + +static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev) +{ + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + + DEV_DBG(dev, "\n"); + + nfcsim_cleanup_dev(dev, 0); + + return 0; +} + +static int nfcsim_start_poll(struct nfc_dev *nfc_dev, + u32 im_protocols, u32 tm_protocols) +{ + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + int rc; + + mutex_lock(&dev->lock); + + if (dev->polling_mode != NFCSIM_POLL_NONE) { + DEV_ERR(dev, "Already in polling mode\n"); + rc = -EBUSY; + goto exit; + } + + if (im_protocols & NFC_PROTO_NFC_DEP_MASK) + dev->polling_mode |= NFCSIM_POLL_INITIATOR; + + if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) + dev->polling_mode |= NFCSIM_POLL_TARGET; + + if (dev->polling_mode == NFCSIM_POLL_NONE) { + DEV_ERR(dev, "Unsupported polling mode\n"); + rc = -EINVAL; + goto exit; + } + + dev->initiator = 0; + dev->curr_polling_mode = NFCSIM_POLL_NONE; + + queue_delayed_work(wq, &dev->poll_work, 0); + + DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X\n", im_protocols, + tm_protocols); + + rc = 0; +exit: + mutex_unlock(&dev->lock); + + return rc; +} + +static void nfcsim_stop_poll(struct nfc_dev *nfc_dev) +{ + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + + DEV_DBG(dev, "Stop poll\n"); + + mutex_lock(&dev->lock); + + dev->polling_mode = NFCSIM_POLL_NONE; + + mutex_unlock(&dev->lock); + + cancel_delayed_work_sync(&dev->poll_work); +} + +static int nfcsim_activate_target(struct nfc_dev *nfc_dev, + struct nfc_target *target, u32 protocol) +{ + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + + DEV_DBG(dev, "\n"); + + return -ENOTSUPP; +} + +static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev, + struct nfc_target *target) +{ + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + + DEV_DBG(dev, "\n"); +} + +static void nfcsim_wq_recv(struct work_struct *work) +{ + struct nfcsim *dev = container_of(work, struct nfcsim, + recv_work.work); + + mutex_lock(&dev->lock); + + if (dev->shutting_down || !dev->up || !dev->clone_skb) { + dev_kfree_skb(dev->clone_skb); + goto exit; + } + + if (dev->initiator) { + if (!dev->cb) { + DEV_ERR(dev, "Null recv callback\n"); + dev_kfree_skb(dev->clone_skb); + goto exit; + } + + dev->cb(dev->cb_context, dev->clone_skb, 0); + dev->cb = NULL; + } else { + nfc_tm_data_received(dev->nfc_dev, dev->clone_skb); + } + +exit: + dev->clone_skb = NULL; + + mutex_unlock(&dev->lock); +} + +static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target, + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context) +{ + struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + struct nfcsim *peer = dev->peer_dev; + int err; + + mutex_lock(&dev->lock); + + if (dev->shutting_down || !dev->up) { + mutex_unlock(&dev->lock); + err = -ENODEV; + goto exit; + } + + dev->cb = cb; + dev->cb_context = cb_context; + + mutex_unlock(&dev->lock); + + mutex_lock(&peer->lock); + + peer->clone_skb = skb_clone(skb, GFP_KERNEL); + + if (!peer->clone_skb) { + DEV_ERR(dev, "skb_clone failed\n"); + mutex_unlock(&peer->lock); + err = -ENOMEM; + goto exit; + } + + /* This simulates an arbitrary transmission delay between the 2 devices. + * If packet transmission occurs immediately between them, we have a + * non-stop flow of several tens of thousands SYMM packets per second + * and a burning cpu. + * + * TODO: Add support for a sysfs entry to control this delay. + */ + queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5)); + + mutex_unlock(&peer->lock); + + err = 0; +exit: + dev_kfree_skb(skb); + + return err; +} + +static int nfcsim_im_transceive(struct nfc_dev *nfc_dev, + struct nfc_target *target, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) +{ + return nfcsim_tx(nfc_dev, target, skb, cb, cb_context); +} + +static int nfcsim_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) +{ + return nfcsim_tx(nfc_dev, NULL, skb, NULL, NULL); +} + +static struct nfc_ops nfcsim_nfc_ops = { + .dev_up = nfcsim_dev_up, + .dev_down = nfcsim_dev_down, + .dep_link_up = nfcsim_dep_link_up, + .dep_link_down = nfcsim_dep_link_down, + .start_poll = nfcsim_start_poll, + .stop_poll = nfcsim_stop_poll, + .activate_target = nfcsim_activate_target, + .deactivate_target = nfcsim_deactivate_target, + .im_transceive = nfcsim_im_transceive, + .tm_send = nfcsim_tm_send, +}; + +static void nfcsim_set_polling_mode(struct nfcsim *dev) +{ + if (dev->polling_mode == NFCSIM_POLL_NONE) { + dev->curr_polling_mode = NFCSIM_POLL_NONE; + return; + } + + if (dev->curr_polling_mode == NFCSIM_POLL_NONE) { + if (dev->polling_mode & NFCSIM_POLL_INITIATOR) + dev->curr_polling_mode = NFCSIM_POLL_INITIATOR; + else + dev->curr_polling_mode = NFCSIM_POLL_TARGET; + + return; + } + + if (dev->polling_mode == NFCSIM_POLL_DUAL) { + if (dev->curr_polling_mode == NFCSIM_POLL_TARGET) + dev->curr_polling_mode = NFCSIM_POLL_INITIATOR; + else + dev->curr_polling_mode = NFCSIM_POLL_TARGET; + } +} + +static void nfcsim_wq_poll(struct work_struct *work) +{ + struct nfcsim *dev = container_of(work, struct nfcsim, poll_work.work); + struct nfcsim *peer = dev->peer_dev; + + /* These work items run on an ordered workqueue and are therefore + * serialized. So we can take both mutexes without being dead locked. + */ + mutex_lock(&dev->lock); + mutex_lock(&peer->lock); + + nfcsim_set_polling_mode(dev); + + if (dev->curr_polling_mode == NFCSIM_POLL_NONE) { + DEV_DBG(dev, "Not polling\n"); + goto unlock; + } + + DEV_DBG(dev, "Polling as %s", + dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ? + "initiator\n" : "target\n"); + + if (dev->curr_polling_mode == NFCSIM_POLL_TARGET) + goto sched_work; + + if (peer->curr_polling_mode == NFCSIM_POLL_TARGET) { + peer->polling_mode = NFCSIM_POLL_NONE; + dev->polling_mode = NFCSIM_POLL_NONE; + + dev->initiator = 1; + + nfcsim_target_found(dev); + + goto unlock; + } + +sched_work: + /* This defines the delay for an initiator to check if the other device + * is polling in target mode. + * If the device starts in dual mode polling, it switches between + * initiator and target at every round. + * Because the wq is ordered and only 1 work item is executed at a time, + * we'll always have one device polling as initiator and the other as + * target at some point, even if both are started in dual mode. + */ + queue_delayed_work(wq, &dev->poll_work, msecs_to_jiffies(200)); + +unlock: + mutex_unlock(&peer->lock); + mutex_unlock(&dev->lock); +} + +static struct nfcsim *nfcsim_init_dev(void) +{ + struct nfcsim *dev; + int rc = -ENOMEM; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) + return ERR_PTR(-ENOMEM); + + mutex_init(&dev->lock); + + INIT_DELAYED_WORK(&dev->recv_work, nfcsim_wq_recv); + INIT_DELAYED_WORK(&dev->poll_work, nfcsim_wq_poll); + + dev->nfc_dev = nfc_allocate_device(&nfcsim_nfc_ops, + NFC_PROTO_NFC_DEP_MASK, + 0, 0); + if (!dev->nfc_dev) + goto error; + + nfc_set_drvdata(dev->nfc_dev, dev); + + rc = nfc_register_device(dev->nfc_dev); + if (rc) + goto free_nfc_dev; + + return dev; + +free_nfc_dev: + nfc_free_device(dev->nfc_dev); + +error: + kfree(dev); + + return ERR_PTR(rc); +} + +static void nfcsim_free_device(struct nfcsim *dev) +{ + nfc_unregister_device(dev->nfc_dev); + + nfc_free_device(dev->nfc_dev); + + kfree(dev); +} + +static int __init nfcsim_init(void) +{ + int rc; + + /* We need an ordered wq to ensure that poll_work items are executed + * one at a time. + */ + wq = alloc_ordered_workqueue("nfcsim", 0); + if (!wq) { + rc = -ENOMEM; + goto exit; + } + + dev0 = nfcsim_init_dev(); + if (IS_ERR(dev0)) { + rc = PTR_ERR(dev0); + goto exit; + } + + dev1 = nfcsim_init_dev(); + if (IS_ERR(dev1)) { + kfree(dev0); + + rc = PTR_ERR(dev1); + goto exit; + } + + dev0->peer_dev = dev1; + dev1->peer_dev = dev0; + + pr_debug("NFCsim " NFCSIM_VERSION " initialized\n"); + + rc = 0; +exit: + if (rc) + pr_err("Failed to initialize nfcsim driver (%d)\n", + rc); + + return rc; +} + +static void __exit nfcsim_exit(void) +{ + nfcsim_cleanup_dev(dev0, 1); + nfcsim_cleanup_dev(dev1, 1); + + nfcsim_free_device(dev0); + nfcsim_free_device(dev1); + + destroy_workqueue(wq); +} + +module_init(nfcsim_init); +module_exit(nfcsim_exit); + +MODULE_DESCRIPTION("NFCSim driver ver " NFCSIM_VERSION); +MODULE_VERSION(NFCSIM_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c new file mode 100644 index 000000000..306ca202c --- /dev/null +++ b/drivers/nfc/nfcwilink.c @@ -0,0 +1,578 @@ +/* + * Texas Instrument's NFC Driver For Shared Transport. + * + * NFC Driver acts as interface between NCI core and + * TI Shared Transport Layer. + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.com> + * + * Acknowledgements: + * This file is based on btwilink.c, which was written + * by Raja Mani and Pavan Savoy. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms 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/platform_device.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/firmware.h> +#include <linux/nfc.h> +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> +#include <linux/ti_wilink_st.h> + +#define NFCWILINK_CHNL 12 +#define NFCWILINK_OPCODE 7 +#define NFCWILINK_MAX_FRAME_SIZE 300 +#define NFCWILINK_HDR_LEN 4 +#define NFCWILINK_OFFSET_LEN_IN_HDR 1 +#define NFCWILINK_LEN_SIZE 2 +#define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */ +#define NFCWILINK_CMD_TIMEOUT 5000 /* 5 sec */ + +#define BTS_FILE_NAME_MAX_SIZE 40 +#define BTS_FILE_HDR_MAGIC 0x42535442 +#define BTS_FILE_CMD_MAX_LEN 0xff +#define BTS_FILE_ACTION_TYPE_SEND_CMD 1 + +#define NCI_VS_NFCC_INFO_CMD_GID 0x2f +#define NCI_VS_NFCC_INFO_CMD_OID 0x12 +#define NCI_VS_NFCC_INFO_RSP_GID 0x4f +#define NCI_VS_NFCC_INFO_RSP_OID 0x12 + +struct nfcwilink_hdr { + __u8 chnl; + __u8 opcode; + __le16 len; +} __packed; + +struct nci_vs_nfcc_info_cmd { + __u8 gid; + __u8 oid; + __u8 plen; +} __packed; + +struct nci_vs_nfcc_info_rsp { + __u8 gid; + __u8 oid; + __u8 plen; + __u8 status; + __u8 hw_id; + __u8 sw_ver_x; + __u8 sw_ver_z; + __u8 patch_id; +} __packed; + +struct bts_file_hdr { + __le32 magic; + __le32 ver; + __u8 rfu[24]; + __u8 actions[0]; +} __packed; + +struct bts_file_action { + __le16 type; + __le16 len; + __u8 data[0]; +} __packed; + +struct nfcwilink { + struct platform_device *pdev; + struct nci_dev *ndev; + unsigned long flags; + + char st_register_cb_status; + long (*st_write) (struct sk_buff *); + + struct completion completed; + + struct nci_vs_nfcc_info_rsp nfcc_info; +}; + +/* NFCWILINK driver flags */ +enum { + NFCWILINK_RUNNING, + NFCWILINK_FW_DOWNLOAD, +}; + +static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb); + +static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + NFCWILINK_HDR_LEN, how); + if (skb) + skb_reserve(skb, NFCWILINK_HDR_LEN); + + return skb; +} + +static void nfcwilink_fw_download_receive(struct nfcwilink *drv, + struct sk_buff *skb) +{ + struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data; + + /* Detect NCI_VS_NFCC_INFO_RSP and store the result */ + if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) && + (rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) { + memcpy(&drv->nfcc_info, rsp, + sizeof(struct nci_vs_nfcc_info_rsp)); + } + + kfree_skb(skb); + + complete(&drv->completed); +} + +static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name) +{ + struct nci_vs_nfcc_info_cmd *cmd; + struct sk_buff *skb; + unsigned long comp_ret; + int rc; + + skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd), + GFP_KERNEL); + if (!skb) { + nfc_err(&drv->pdev->dev, + "no memory for nci_vs_nfcc_info_cmd\n"); + return -ENOMEM; + } + + cmd = (struct nci_vs_nfcc_info_cmd *) + skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd)); + cmd->gid = NCI_VS_NFCC_INFO_CMD_GID; + cmd->oid = NCI_VS_NFCC_INFO_CMD_OID; + cmd->plen = 0; + + drv->nfcc_info.plen = 0; + + rc = nfcwilink_send(drv->ndev, skb); + if (rc) + return rc; + + comp_ret = wait_for_completion_timeout(&drv->completed, + msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); + dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n", + comp_ret); + if (comp_ret == 0) { + nfc_err(&drv->pdev->dev, + "timeout on wait_for_completion_timeout\n"); + return -ETIMEDOUT; + } + + dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n", + drv->nfcc_info.plen, drv->nfcc_info.status); + + if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) { + nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n"); + return -EINVAL; + } + + snprintf(file_name, BTS_FILE_NAME_MAX_SIZE, + "/*(DEBLOBBED)*/", + drv->nfcc_info.hw_id, + drv->nfcc_info.sw_ver_x, + drv->nfcc_info.sw_ver_z, + drv->nfcc_info.patch_id); + + nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name); + + return 0; +} + +static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len) +{ + struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data; + struct sk_buff *skb; + unsigned long comp_ret; + int rc; + + /* verify valid cmd for the NFC channel */ + if ((len <= sizeof(struct nfcwilink_hdr)) || + (len > BTS_FILE_CMD_MAX_LEN) || + (hdr->chnl != NFCWILINK_CHNL) || + (hdr->opcode != NFCWILINK_OPCODE)) { + nfc_err(&drv->pdev->dev, + "ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n", + len, hdr->chnl, hdr->opcode); + return 0; + } + + /* remove the ST header */ + len -= sizeof(struct nfcwilink_hdr); + data += sizeof(struct nfcwilink_hdr); + + skb = nfcwilink_skb_alloc(len, GFP_KERNEL); + if (!skb) { + nfc_err(&drv->pdev->dev, "no memory for bts cmd\n"); + return -ENOMEM; + } + + memcpy(skb_put(skb, len), data, len); + + rc = nfcwilink_send(drv->ndev, skb); + if (rc) + return rc; + + comp_ret = wait_for_completion_timeout(&drv->completed, + msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); + dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n", + comp_ret); + if (comp_ret == 0) { + nfc_err(&drv->pdev->dev, + "timeout on wait_for_completion_timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int nfcwilink_download_fw(struct nfcwilink *drv) +{ + unsigned char file_name[BTS_FILE_NAME_MAX_SIZE]; + const struct firmware *fw; + __u16 action_type, action_len; + __u8 *ptr; + int len, rc; + + set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); + + rc = nfcwilink_get_bts_file_name(drv, file_name); + if (rc) + goto exit; + + rc = reject_firmware(&fw, file_name, &drv->pdev->dev); + if (rc) { + nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc); + + /* if the file is not found, don't exit with failure */ + if (rc == -ENOENT) + rc = 0; + + goto exit; + } + + len = fw->size; + ptr = (__u8 *)fw->data; + + if ((len == 0) || (ptr == NULL)) { + dev_dbg(&drv->pdev->dev, + "request_firmware returned size %d\n", len); + goto release_fw; + } + + if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) != + BTS_FILE_HDR_MAGIC) { + nfc_err(&drv->pdev->dev, "wrong bts magic number\n"); + rc = -EINVAL; + goto release_fw; + } + + /* remove the BTS header */ + len -= sizeof(struct bts_file_hdr); + ptr += sizeof(struct bts_file_hdr); + + while (len > 0) { + action_type = + __le16_to_cpu(((struct bts_file_action *)ptr)->type); + action_len = + __le16_to_cpu(((struct bts_file_action *)ptr)->len); + + dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n", + action_type, action_len); + + switch (action_type) { + case BTS_FILE_ACTION_TYPE_SEND_CMD: + rc = nfcwilink_send_bts_cmd(drv, + ((struct bts_file_action *)ptr)->data, + action_len); + if (rc) + goto release_fw; + break; + } + + /* advance to the next action */ + len -= (sizeof(struct bts_file_action) + action_len); + ptr += (sizeof(struct bts_file_action) + action_len); + } + +release_fw: + release_firmware(fw); + +exit: + clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); + return rc; +} + +/* Called by ST when registration is complete */ +static void nfcwilink_register_complete(void *priv_data, char data) +{ + struct nfcwilink *drv = priv_data; + + /* store ST registration status */ + drv->st_register_cb_status = data; + + /* complete the wait in nfc_st_open() */ + complete(&drv->completed); +} + +/* Called by ST when receive data is available */ +static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) +{ + struct nfcwilink *drv = priv_data; + int rc; + + if (!skb) + return -EFAULT; + + if (!drv) { + kfree_skb(skb); + return -EFAULT; + } + + dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len); + + /* strip the ST header + (apart for the chnl byte, which is not received in the hdr) */ + skb_pull(skb, (NFCWILINK_HDR_LEN-1)); + + if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) { + nfcwilink_fw_download_receive(drv, skb); + return 0; + } + + /* Forward skb to NCI core layer */ + rc = nci_recv_frame(drv->ndev, skb); + if (rc < 0) { + nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc); + return rc; + } + + return 0; +} + +/* protocol structure registered with ST */ +static struct st_proto_s nfcwilink_proto = { + .chnl_id = NFCWILINK_CHNL, + .max_frame_size = NFCWILINK_MAX_FRAME_SIZE, + .hdr_len = (NFCWILINK_HDR_LEN-1), /* not including chnl byte */ + .offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR, + .len_size = NFCWILINK_LEN_SIZE, + .reserve = 0, + .recv = nfcwilink_receive, + .reg_complete_cb = nfcwilink_register_complete, + .write = NULL, +}; + +static int nfcwilink_open(struct nci_dev *ndev) +{ + struct nfcwilink *drv = nci_get_drvdata(ndev); + unsigned long comp_ret; + int rc; + + if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) { + rc = -EBUSY; + goto exit; + } + + nfcwilink_proto.priv_data = drv; + + init_completion(&drv->completed); + drv->st_register_cb_status = -EINPROGRESS; + + rc = st_register(&nfcwilink_proto); + if (rc < 0) { + if (rc == -EINPROGRESS) { + comp_ret = wait_for_completion_timeout( + &drv->completed, + msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT)); + + dev_dbg(&drv->pdev->dev, + "wait_for_completion_timeout returned %ld\n", + comp_ret); + + if (comp_ret == 0) { + /* timeout */ + rc = -ETIMEDOUT; + goto clear_exit; + } else if (drv->st_register_cb_status != 0) { + rc = drv->st_register_cb_status; + nfc_err(&drv->pdev->dev, + "st_register_cb failed %d\n", rc); + goto clear_exit; + } + } else { + nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc); + goto clear_exit; + } + } + + /* st_register MUST fill the write callback */ + BUG_ON(nfcwilink_proto.write == NULL); + drv->st_write = nfcwilink_proto.write; + + if (nfcwilink_download_fw(drv)) { + nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n", + rc); + /* open should succeed, even if the FW download failed */ + } + + goto exit; + +clear_exit: + clear_bit(NFCWILINK_RUNNING, &drv->flags); + +exit: + return rc; +} + +static int nfcwilink_close(struct nci_dev *ndev) +{ + struct nfcwilink *drv = nci_get_drvdata(ndev); + int rc; + + if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags)) + return 0; + + rc = st_unregister(&nfcwilink_proto); + if (rc) + nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc); + + drv->st_write = NULL; + + return rc; +} + +static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nfcwilink *drv = nci_get_drvdata(ndev); + struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000}; + long len; + + dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len); + + if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) { + kfree_skb(skb); + return -EINVAL; + } + + /* add the ST hdr to the start of the buffer */ + hdr.len = cpu_to_le16(skb->len); + memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN); + + /* Insert skb to shared transport layer's transmit queue. + * Freeing skb memory is taken care in shared transport layer, + * so don't free skb memory here. + */ + len = drv->st_write(skb); + if (len < 0) { + kfree_skb(skb); + nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len); + return -EFAULT; + } + + return 0; +} + +static struct nci_ops nfcwilink_ops = { + .open = nfcwilink_open, + .close = nfcwilink_close, + .send = nfcwilink_send, +}; + +static int nfcwilink_probe(struct platform_device *pdev) +{ + static struct nfcwilink *drv; + int rc; + __u32 protocols; + + drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL); + if (!drv) { + rc = -ENOMEM; + goto exit; + } + + drv->pdev = pdev; + + protocols = NFC_PROTO_JEWEL_MASK + | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK + | NFC_PROTO_ISO14443_MASK + | NFC_PROTO_ISO14443_B_MASK + | NFC_PROTO_NFC_DEP_MASK; + + drv->ndev = nci_allocate_device(&nfcwilink_ops, + protocols, + NFCWILINK_HDR_LEN, + 0); + if (!drv->ndev) { + nfc_err(&pdev->dev, "nci_allocate_device failed\n"); + rc = -ENOMEM; + goto exit; + } + + nci_set_parent_dev(drv->ndev, &pdev->dev); + nci_set_drvdata(drv->ndev, drv); + + rc = nci_register_device(drv->ndev); + if (rc < 0) { + nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc); + goto free_dev_exit; + } + + dev_set_drvdata(&pdev->dev, drv); + + goto exit; + +free_dev_exit: + nci_free_device(drv->ndev); + +exit: + return rc; +} + +static int nfcwilink_remove(struct platform_device *pdev) +{ + struct nfcwilink *drv = dev_get_drvdata(&pdev->dev); + struct nci_dev *ndev; + + if (!drv) + return -EFAULT; + + ndev = drv->ndev; + + nci_unregister_device(ndev); + nci_free_device(ndev); + + return 0; +} + +static struct platform_driver nfcwilink_driver = { + .probe = nfcwilink_probe, + .remove = nfcwilink_remove, + .driver = { + .name = "nfcwilink", + }, +}; + +module_platform_driver(nfcwilink_driver); + +/* ------ Module Info ------ */ + +MODULE_AUTHOR("Ilan Elias <ilane@ti.com>"); +MODULE_DESCRIPTION("NFC Driver for TI Shared Transport"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig new file mode 100644 index 000000000..37b406125 --- /dev/null +++ b/drivers/nfc/nxp-nci/Kconfig @@ -0,0 +1,25 @@ +config NFC_NXP_NCI + tristate "NXP-NCI NFC driver" + depends on NFC_NCI + default n + ---help--- + Generic core driver for NXP NCI chips such as the NPC100 + or PN7150 families. + This is a driver based on the NCI NFC kernel layers and + will thus not work with NXP libnfc library. + + To compile this driver as a module, choose m here. The module will + be called nxp_nci. + Say N if unsure. + +config NFC_NXP_NCI_I2C + tristate "NXP-NCI I2C support" + depends on NFC_NXP_NCI && I2C + ---help--- + This module adds support for an I2C interface to the NXP NCI + chips. + Select this if your platform is using the I2C bus. + + To compile this driver as a module, choose m here. The module will + be called nxp_nci_i2c. + Say Y if unsure. diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile new file mode 100644 index 000000000..c008be30b --- /dev/null +++ b/drivers/nfc/nxp-nci/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for NXP-NCI NFC driver +# + +nxp-nci-objs = core.o firmware.o +nxp-nci_i2c-objs = i2c.o + +obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o +obj-$(CONFIG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o + +ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c new file mode 100644 index 000000000..8979636d4 --- /dev/null +++ b/drivers/nfc/nxp-nci/core.c @@ -0,0 +1,186 @@ +/* + * Generic driver for NXP NCI NFC chips + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: ClĂ©ment Perrochaud <clement.perrochaud@nxp.com> + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. 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/delay.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/nfc.h> +#include <linux/platform_data/nxp-nci.h> + +#include <net/nfc/nci_core.h> + +#include "nxp-nci.h" + +#define NXP_NCI_HDR_LEN 4 + +#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ + NFC_PROTO_MIFARE_MASK | \ + NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK | \ + NFC_PROTO_NFC_DEP_MASK) + +static int nxp_nci_open(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r = 0; + + mutex_lock(&info->info_lock); + + if (info->mode != NXP_NCI_MODE_COLD) { + r = -EBUSY; + goto open_exit; + } + + if (info->phy_ops->set_mode) + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI); + + info->mode = NXP_NCI_MODE_NCI; + +open_exit: + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_close(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r = 0; + + mutex_lock(&info->info_lock); + + if (info->phy_ops->set_mode) + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + + info->mode = NXP_NCI_MODE_COLD; + + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r; + + if (!info->phy_ops->write) { + r = -ENOTSUPP; + goto send_exit; + } + + if (info->mode != NXP_NCI_MODE_NCI) { + r = -EINVAL; + goto send_exit; + } + + r = info->phy_ops->write(info->phy_id, skb); + if (r < 0) + kfree_skb(skb); + +send_exit: + return r; +} + +static struct nci_ops nxp_nci_ops = { + .open = nxp_nci_open, + .close = nxp_nci_close, + .send = nxp_nci_send, + .fw_download = nxp_nci_fw_download, +}; + +int nxp_nci_probe(void *phy_id, struct device *pdev, + struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, + struct nci_dev **ndev) +{ + struct nxp_nci_info *info; + int r; + + info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL); + if (!info) { + r = -ENOMEM; + goto probe_exit; + } + + info->phy_id = phy_id; + info->pdev = pdev; + info->phy_ops = phy_ops; + info->max_payload = max_payload; + INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); + init_completion(&info->fw_info.cmd_completion); + mutex_init(&info->info_lock); + + if (info->phy_ops->set_mode) { + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + if (r < 0) + goto probe_exit; + } + + info->mode = NXP_NCI_MODE_COLD; + + info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, + NXP_NCI_HDR_LEN, 0); + if (!info->ndev) { + r = -ENOMEM; + goto probe_exit; + } + + nci_set_parent_dev(info->ndev, pdev); + nci_set_drvdata(info->ndev, info); + r = nci_register_device(info->ndev); + if (r < 0) + goto probe_exit_free_nci; + + *ndev = info->ndev; + + goto probe_exit; + +probe_exit_free_nci: + nci_free_device(info->ndev); +probe_exit: + return r; +} +EXPORT_SYMBOL(nxp_nci_probe); + +void nxp_nci_remove(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + + if (info->mode == NXP_NCI_MODE_FW) + nxp_nci_fw_work_complete(info, -ESHUTDOWN); + cancel_work_sync(&info->fw_info.work); + + mutex_lock(&info->info_lock); + + if (info->phy_ops->set_mode) + info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + + nci_unregister_device(ndev); + nci_free_device(ndev); + + mutex_unlock(&info->info_lock); +} +EXPORT_SYMBOL(nxp_nci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("NXP NCI NFC driver"); +MODULE_AUTHOR("ClĂ©ment Perrochaud <clement.perrochaud@nxp.com>"); diff --git a/drivers/nfc/nxp-nci/firmware.c b/drivers/nfc/nxp-nci/firmware.c new file mode 100644 index 000000000..ec69eccab --- /dev/null +++ b/drivers/nfc/nxp-nci/firmware.c @@ -0,0 +1,325 @@ +/* + * Generic driver for NXP NCI NFC chips + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Author: ClĂ©ment Perrochaud <clement.perrochaud@nxp.com> + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. 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/completion.h> +#include <linux/firmware.h> +#include <linux/nfc.h> +#include <linux/unaligned/access_ok.h> + +#include "nxp-nci.h" + +/* Crypto operations can take up to 30 seconds */ +#define NXP_NCI_FW_ANSWER_TIMEOUT msecs_to_jiffies(30000) + +#define NXP_NCI_FW_CMD_RESET 0xF0 +#define NXP_NCI_FW_CMD_GETVERSION 0xF1 +#define NXP_NCI_FW_CMD_CHECKINTEGRITY 0xE0 +#define NXP_NCI_FW_CMD_WRITE 0xC0 +#define NXP_NCI_FW_CMD_READ 0xA2 +#define NXP_NCI_FW_CMD_GETSESSIONSTATE 0xF2 +#define NXP_NCI_FW_CMD_LOG 0xA7 +#define NXP_NCI_FW_CMD_FORCE 0xD0 +#define NXP_NCI_FW_CMD_GET_DIE_ID 0xF4 + +#define NXP_NCI_FW_CHUNK_FLAG 0x0400 + +#define NXP_NCI_FW_RESULT_OK 0x00 +#define NXP_NCI_FW_RESULT_INVALID_ADDR 0x01 +#define NXP_NCI_FW_RESULT_GENERIC_ERROR 0x02 +#define NXP_NCI_FW_RESULT_UNKNOWN_CMD 0x0B +#define NXP_NCI_FW_RESULT_ABORTED_CMD 0x0C +#define NXP_NCI_FW_RESULT_PLL_ERROR 0x0D +#define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR 0x1E +#define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR 0x1F +#define NXP_NCI_FW_RESULT_MEM_BSY 0x20 +#define NXP_NCI_FW_RESULT_SIGNATURE_ERROR 0x21 +#define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR 0x24 +#define NXP_NCI_FW_RESULT_PROTOCOL_ERROR 0x28 +#define NXP_NCI_FW_RESULT_SFWU_DEGRADED 0x2A +#define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK 0x2D +#define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK 0x2E +#define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5 0xC5 + +void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + int r; + + if (info->phy_ops->set_mode) { + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + if (r < 0 && result == 0) + result = -r; + } + + info->mode = NXP_NCI_MODE_COLD; + + if (fw_info->fw) { + release_firmware(fw_info->fw); + fw_info->fw = NULL; + } + + nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result); +} + +/* crc_ccitt cannot be used since it is computed MSB first and not LSB first */ +static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len) +{ + u16 crc = 0xffff; + + while (len--) { + crc = ((crc >> 8) | (crc << 8)) ^ *buffer++; + crc ^= (crc & 0xff) >> 4; + crc ^= (crc & 0xff) << 12; + crc ^= (crc & 0xff) << 5; + } + + return crc; +} + +static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + u16 header, crc; + struct sk_buff *skb; + size_t chunk_len; + size_t remaining_len; + int r; + + skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL); + if (!skb) { + r = -ENOMEM; + goto chunk_exit; + } + + chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN; + remaining_len = fw_info->frame_size - fw_info->written; + + if (remaining_len > chunk_len) { + header = NXP_NCI_FW_CHUNK_FLAG; + } else { + chunk_len = remaining_len; + header = 0x0000; + } + + header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK; + put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN)); + + memcpy(skb_put(skb, chunk_len), fw_info->data + fw_info->written, + chunk_len); + + crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN); + put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN)); + + r = info->phy_ops->write(info->phy_id, skb); + if (r >= 0) + r = chunk_len; + + kfree_skb(skb); + +chunk_exit: + return r; +} + +static int nxp_nci_fw_send(struct nxp_nci_info *info) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + long completion_rc; + int r; + + reinit_completion(&fw_info->cmd_completion); + + if (fw_info->written == 0) { + fw_info->frame_size = get_unaligned_be16(fw_info->data) & + NXP_NCI_FW_FRAME_LEN_MASK; + fw_info->data += NXP_NCI_FW_HDR_LEN; + fw_info->size -= NXP_NCI_FW_HDR_LEN; + } + + if (fw_info->frame_size > fw_info->size) + return -EMSGSIZE; + + r = nxp_nci_fw_send_chunk(info); + if (r < 0) + return r; + + fw_info->written += r; + + if (*fw_info->data == NXP_NCI_FW_CMD_RESET) { + fw_info->cmd_result = 0; + if (fw_info->fw) + schedule_work(&fw_info->work); + } else { + completion_rc = wait_for_completion_interruptible_timeout( + &fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT); + if (completion_rc == 0) + return -ETIMEDOUT; + } + + return 0; +} + +void nxp_nci_fw_work(struct work_struct *work) +{ + struct nxp_nci_info *info; + struct nxp_nci_fw_info *fw_info; + int r; + + fw_info = container_of(work, struct nxp_nci_fw_info, work); + info = container_of(fw_info, struct nxp_nci_info, fw_info); + + mutex_lock(&info->info_lock); + + r = fw_info->cmd_result; + if (r < 0) + goto exit_work; + + if (fw_info->written == fw_info->frame_size) { + fw_info->data += fw_info->frame_size; + fw_info->size -= fw_info->frame_size; + fw_info->written = 0; + } + + if (fw_info->size > 0) + r = nxp_nci_fw_send(info); + +exit_work: + if (r < 0 || fw_info->size == 0) + nxp_nci_fw_work_complete(info, r); + mutex_unlock(&info->info_lock); +} + +int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + struct nxp_nci_fw_info *fw_info = &info->fw_info; + int r; + + mutex_lock(&info->info_lock); + + if (!info->phy_ops->set_mode || !info->phy_ops->write) { + r = -ENOTSUPP; + goto fw_download_exit; + } + + if (!firmware_name || firmware_name[0] == '\0') { + r = -EINVAL; + goto fw_download_exit; + } + + strcpy(fw_info->name, firmware_name); + + r = reject_firmware(&fw_info->fw, firmware_name, + ndev->nfc_dev->dev.parent); + if (r < 0) + goto fw_download_exit; + + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW); + if (r < 0) { + release_firmware(fw_info->fw); + goto fw_download_exit; + } + + info->mode = NXP_NCI_MODE_FW; + + fw_info->data = fw_info->fw->data; + fw_info->size = fw_info->fw->size; + fw_info->written = 0; + fw_info->frame_size = 0; + fw_info->cmd_result = 0; + + schedule_work(&fw_info->work); + +fw_download_exit: + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_fw_read_status(u8 stat) +{ + switch (stat) { + case NXP_NCI_FW_RESULT_OK: + return 0; + case NXP_NCI_FW_RESULT_INVALID_ADDR: + return -EINVAL; + case NXP_NCI_FW_RESULT_UNKNOWN_CMD: + return -EINVAL; + case NXP_NCI_FW_RESULT_ABORTED_CMD: + return -EMSGSIZE; + case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR: + return -EADDRNOTAVAIL; + case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR: + return -ENOBUFS; + case NXP_NCI_FW_RESULT_MEM_BSY: + return -ENOKEY; + case NXP_NCI_FW_RESULT_SIGNATURE_ERROR: + return -EKEYREJECTED; + case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR: + return -EALREADY; + case NXP_NCI_FW_RESULT_PROTOCOL_ERROR: + return -EPROTO; + case NXP_NCI_FW_RESULT_SFWU_DEGRADED: + return -EHWPOISON; + case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK: + return 0; + case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK: + return 0; + case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5: + return -EINVAL; + default: + return -EIO; + } +} + +static u16 nxp_nci_fw_check_crc(struct sk_buff *skb) +{ + u16 crc, frame_crc; + size_t len = skb->len - NXP_NCI_FW_CRC_LEN; + + crc = nxp_nci_fw_crc(skb->data, len); + frame_crc = get_unaligned_be16(skb->data + len); + + return (crc ^ frame_crc); +} + +void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + struct nxp_nci_fw_info *fw_info = &info->fw_info; + + complete(&fw_info->cmd_completion); + + if (skb) { + if (nxp_nci_fw_check_crc(skb) != 0x00) + fw_info->cmd_result = -EBADMSG; + else + fw_info->cmd_result = nxp_nci_fw_read_status( + *skb_pull(skb, NXP_NCI_FW_HDR_LEN)); + kfree_skb(skb); + } else { + fw_info->cmd_result = -EIO; + } + + if (fw_info->fw) + schedule_work(&fw_info->work); +} +EXPORT_SYMBOL(nxp_nci_fw_recv_frame); diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c new file mode 100644 index 000000000..17bd67dbe --- /dev/null +++ b/drivers/nfc/nxp-nci/i2c.c @@ -0,0 +1,415 @@ +/* + * I2C link layer for the NXP NCI driver + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: ClĂ©ment Perrochaud <clement.perrochaud@nxp.com> + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. 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/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/nfc.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/platform_data/nxp-nci.h> +#include <linux/unaligned/access_ok.h> + +#include <net/nfc/nfc.h> + +#include "nxp-nci.h" + +#define NXP_NCI_I2C_DRIVER_NAME "nxp-nci_i2c" + +#define NXP_NCI_I2C_MAX_PAYLOAD 32 + +struct nxp_nci_i2c_phy { + struct i2c_client *i2c_dev; + struct nci_dev *ndev; + + unsigned int gpio_en; + unsigned int gpio_fw; + + int hard_fault; /* + * < 0 if hardware error occurred (e.g. i2c err) + * and prevents normal operation. + */ +}; + +static int nxp_nci_i2c_set_mode(void *phy_id, + enum nxp_nci_mode mode) +{ + struct nxp_nci_i2c_phy *phy = (struct nxp_nci_i2c_phy *) phy_id; + + gpio_set_value(phy->gpio_fw, (mode == NXP_NCI_MODE_FW) ? 1 : 0); + gpio_set_value(phy->gpio_en, (mode != NXP_NCI_MODE_COLD) ? 1 : 0); + usleep_range(10000, 15000); + + if (mode == NXP_NCI_MODE_COLD) + phy->hard_fault = 0; + + return 0; +} + +static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r; + struct nxp_nci_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + + if (phy->hard_fault != 0) + return phy->hard_fault; + + r = i2c_master_send(client, skb->data, skb->len); + if (r == -EREMOTEIO) { + /* Retry, chip was in standby */ + usleep_range(110000, 120000); + r = i2c_master_send(client, skb->data, skb->len); + } + + if (r < 0) { + nfc_err(&client->dev, "Error %d on I2C send\n", r); + } else if (r != skb->len) { + nfc_err(&client->dev, + "Invalid length sent: %u (expected %u)\n", + r, skb->len); + r = -EREMOTEIO; + } else { + /* Success but return 0 and not number of bytes */ + r = 0; + } + + return r; +} + +static struct nxp_nci_phy_ops i2c_phy_ops = { + .set_mode = nxp_nci_i2c_set_mode, + .write = nxp_nci_i2c_write, +}; + +static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy, + struct sk_buff **skb) +{ + struct i2c_client *client = phy->i2c_dev; + u16 header; + size_t frame_len; + int r; + + r = i2c_master_recv(client, (u8 *) &header, NXP_NCI_FW_HDR_LEN); + if (r < 0) { + goto fw_read_exit; + } else if (r != NXP_NCI_FW_HDR_LEN) { + nfc_err(&client->dev, "Incorrect header length: %u\n", r); + r = -EBADMSG; + goto fw_read_exit; + } + + frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) + + NXP_NCI_FW_CRC_LEN; + + *skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL); + if (*skb == NULL) { + r = -ENOMEM; + goto fw_read_exit; + } + + memcpy(skb_put(*skb, NXP_NCI_FW_HDR_LEN), &header, NXP_NCI_FW_HDR_LEN); + + r = i2c_master_recv(client, skb_put(*skb, frame_len), frame_len); + if (r != frame_len) { + nfc_err(&client->dev, + "Invalid frame length: %u (expected %zu)\n", + r, frame_len); + r = -EBADMSG; + goto fw_read_exit_free_skb; + } + + return 0; + +fw_read_exit_free_skb: + kfree_skb(*skb); +fw_read_exit: + return r; +} + +static int nxp_nci_i2c_nci_read(struct nxp_nci_i2c_phy *phy, + struct sk_buff **skb) +{ + struct nci_ctrl_hdr header; /* May actually be a data header */ + struct i2c_client *client = phy->i2c_dev; + int r; + + r = i2c_master_recv(client, (u8 *) &header, NCI_CTRL_HDR_SIZE); + if (r < 0) { + goto nci_read_exit; + } else if (r != NCI_CTRL_HDR_SIZE) { + nfc_err(&client->dev, "Incorrect header length: %u\n", r); + r = -EBADMSG; + goto nci_read_exit; + } + + *skb = alloc_skb(NCI_CTRL_HDR_SIZE + header.plen, GFP_KERNEL); + if (*skb == NULL) { + r = -ENOMEM; + goto nci_read_exit; + } + + memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), (void *) &header, + NCI_CTRL_HDR_SIZE); + + r = i2c_master_recv(client, skb_put(*skb, header.plen), header.plen); + if (r != header.plen) { + nfc_err(&client->dev, + "Invalid frame payload length: %u (expected %u)\n", + r, header.plen); + r = -EBADMSG; + goto nci_read_exit_free_skb; + } + + return 0; + +nci_read_exit_free_skb: + kfree_skb(*skb); +nci_read_exit: + return r; +} + +static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id) +{ + struct nxp_nci_i2c_phy *phy = phy_id; + struct i2c_client *client; + struct nxp_nci_info *info; + + struct sk_buff *skb = NULL; + int r = 0; + + if (!phy || !phy->ndev) + goto exit_irq_none; + + client = phy->i2c_dev; + + if (!client || irq != client->irq) + goto exit_irq_none; + + info = nci_get_drvdata(phy->ndev); + + if (!info) + goto exit_irq_none; + + mutex_lock(&info->info_lock); + + if (phy->hard_fault != 0) + goto exit_irq_handled; + + switch (info->mode) { + case NXP_NCI_MODE_NCI: + r = nxp_nci_i2c_nci_read(phy, &skb); + break; + case NXP_NCI_MODE_FW: + r = nxp_nci_i2c_fw_read(phy, &skb); + break; + case NXP_NCI_MODE_COLD: + r = -EREMOTEIO; + break; + } + + if (r == -EREMOTEIO) { + phy->hard_fault = r; + skb = NULL; + } else if (r < 0) { + nfc_err(&client->dev, "Read failed with error %d\n", r); + goto exit_irq_handled; + } + + switch (info->mode) { + case NXP_NCI_MODE_NCI: + nci_recv_frame(phy->ndev, skb); + break; + case NXP_NCI_MODE_FW: + nxp_nci_fw_recv_frame(phy->ndev, skb); + break; + case NXP_NCI_MODE_COLD: + break; + } + +exit_irq_handled: + mutex_unlock(&info->info_lock); + return IRQ_HANDLED; +exit_irq_none: + WARN_ON_ONCE(1); + return IRQ_NONE; +} + +#ifdef CONFIG_OF + +static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) +{ + struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int r; + + pp = client->dev.of_node; + if (!pp) + return -ENODEV; + + r = of_get_named_gpio(pp, "enable-gpios", 0); + if (r == -EPROBE_DEFER) + r = of_get_named_gpio(pp, "enable-gpios", 0); + if (r < 0) { + nfc_err(&client->dev, "Failed to get EN gpio, error: %d\n", r); + return r; + } + phy->gpio_en = r; + + r = of_get_named_gpio(pp, "firmware-gpios", 0); + if (r == -EPROBE_DEFER) + r = of_get_named_gpio(pp, "firmware-gpios", 0); + if (r < 0) { + nfc_err(&client->dev, "Failed to get FW gpio, error: %d\n", r); + return r; + } + phy->gpio_fw = r; + + r = irq_of_parse_and_map(pp, 0); + if (r < 0) { + nfc_err(&client->dev, "Unable to get irq, error: %d\n", r); + return r; + } + client->irq = r; + + return 0; +} + +#else + +static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) +{ + return -ENODEV; +} + +#endif + +static int nxp_nci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct nxp_nci_i2c_phy *phy; + struct nxp_nci_nfc_platform_data *pdata; + int r; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); + r = -ENODEV; + goto probe_exit; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct nxp_nci_i2c_phy), + GFP_KERNEL); + if (!phy) { + r = -ENOMEM; + goto probe_exit; + } + + phy->i2c_dev = client; + i2c_set_clientdata(client, phy); + + pdata = client->dev.platform_data; + + if (!pdata && client->dev.of_node) { + r = nxp_nci_i2c_parse_devtree(client); + if (r < 0) { + nfc_err(&client->dev, "Failed to get DT data\n"); + goto probe_exit; + } + } else if (pdata) { + phy->gpio_en = pdata->gpio_en; + phy->gpio_fw = pdata->gpio_fw; + client->irq = pdata->irq; + } else { + nfc_err(&client->dev, "No platform data\n"); + r = -EINVAL; + goto probe_exit; + } + + r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en, + GPIOF_OUT_INIT_LOW, "nxp_nci_en"); + if (r < 0) + goto probe_exit; + + r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw, + GPIOF_OUT_INIT_LOW, "nxp_nci_fw"); + if (r < 0) + goto probe_exit; + + r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops, + NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev); + if (r < 0) + goto probe_exit; + + r = request_threaded_irq(client->irq, NULL, + nxp_nci_i2c_irq_thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + NXP_NCI_I2C_DRIVER_NAME, phy); + if (r < 0) + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + +probe_exit: + return r; +} + +static int nxp_nci_i2c_remove(struct i2c_client *client) +{ + struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); + + nxp_nci_remove(phy->ndev); + free_irq(client->irq, phy); + + return 0; +} + +static struct i2c_device_id nxp_nci_i2c_id_table[] = { + {"nxp-nci_i2c", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table); + +static const struct of_device_id of_nxp_nci_i2c_match[] = { + { .compatible = "nxp,nxp-nci-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match); + +static struct i2c_driver nxp_nci_i2c_driver = { + .driver = { + .name = NXP_NCI_I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_nxp_nci_i2c_match), + }, + .probe = nxp_nci_i2c_probe, + .id_table = nxp_nci_i2c_id_table, + .remove = nxp_nci_i2c_remove, +}; + +module_i2c_driver(nxp_nci_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers"); +MODULE_AUTHOR("ClĂ©ment Perrochaud <clement.perrochaud@nxp.com>"); diff --git a/drivers/nfc/nxp-nci/nxp-nci.h b/drivers/nfc/nxp-nci/nxp-nci.h new file mode 100644 index 000000000..f1fecc4e2 --- /dev/null +++ b/drivers/nfc/nxp-nci/nxp-nci.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: ClĂ©ment Perrochaud <clement.perrochaud@nxp.com> + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. 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_NXP_NCI_H_ +#define __LOCAL_NXP_NCI_H_ + +#include <linux/completion.h> +#include <linux/firmware.h> +#include <linux/nfc.h> +#include <linux/platform_data/nxp-nci.h> + +#include <net/nfc/nci_core.h> + +#define NXP_NCI_FW_HDR_LEN 2 +#define NXP_NCI_FW_CRC_LEN 2 + +#define NXP_NCI_FW_FRAME_LEN_MASK 0x03FF + +enum nxp_nci_mode { + NXP_NCI_MODE_COLD, + NXP_NCI_MODE_NCI, + NXP_NCI_MODE_FW +}; + +struct nxp_nci_phy_ops { + int (*set_mode)(void *id, enum nxp_nci_mode mode); + int (*write)(void *id, struct sk_buff *skb); +}; + +struct nxp_nci_fw_info { + char name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; + const struct firmware *fw; + + size_t size; + size_t written; + + const u8 *data; + size_t frame_size; + + struct work_struct work; + struct completion cmd_completion; + + int cmd_result; +}; + +struct nxp_nci_info { + struct nci_dev *ndev; + void *phy_id; + struct device *pdev; + + enum nxp_nci_mode mode; + + struct nxp_nci_phy_ops *phy_ops; + unsigned int max_payload; + + struct mutex info_lock; + + struct nxp_nci_fw_info fw_info; +}; + +int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name); +void nxp_nci_fw_work(struct work_struct *work); +void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); +void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result); + +int nxp_nci_probe(void *phy_id, struct device *pdev, + struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, + struct nci_dev **ndev); +void nxp_nci_remove(struct nci_dev *ndev); + +#endif /* __LOCAL_NXP_NCI_H_ */ diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c new file mode 100644 index 000000000..a03e4eb5f --- /dev/null +++ b/drivers/nfc/pn533.c @@ -0,0 +1,3313 @@ +/* + * Copyright (C) 2011 Instituto Nokia de Tecnologia + * Copyright (C) 2012-2013 Tieto Poland + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/nfc.h> +#include <linux/netdevice.h> +#include <net/nfc/nfc.h> + +#define VERSION "0.2" + +#define PN533_VENDOR_ID 0x4CC +#define PN533_PRODUCT_ID 0x2533 + +#define SCM_VENDOR_ID 0x4E6 +#define SCL3711_PRODUCT_ID 0x5591 + +#define SONY_VENDOR_ID 0x054c +#define PASORI_PRODUCT_ID 0x02e1 + +#define ACS_VENDOR_ID 0x072f +#define ACR122U_PRODUCT_ID 0x2200 + +#define PN533_DEVICE_STD 0x1 +#define PN533_DEVICE_PASORI 0x2 +#define PN533_DEVICE_ACR122U 0x3 + +#define PN533_ALL_PROTOCOLS (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK |\ + NFC_PROTO_FELICA_MASK | NFC_PROTO_ISO14443_MASK |\ + NFC_PROTO_NFC_DEP_MASK |\ + NFC_PROTO_ISO14443_B_MASK) + +#define PN533_NO_TYPE_B_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ + NFC_PROTO_MIFARE_MASK | \ + NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_NFC_DEP_MASK) + +static const struct usb_device_id pn533_table[] = { + { USB_DEVICE(PN533_VENDOR_ID, PN533_PRODUCT_ID), + .driver_info = PN533_DEVICE_STD }, + { USB_DEVICE(SCM_VENDOR_ID, SCL3711_PRODUCT_ID), + .driver_info = PN533_DEVICE_STD }, + { USB_DEVICE(SONY_VENDOR_ID, PASORI_PRODUCT_ID), + .driver_info = PN533_DEVICE_PASORI }, + { USB_DEVICE(ACS_VENDOR_ID, ACR122U_PRODUCT_ID), + .driver_info = PN533_DEVICE_ACR122U }, + { } +}; +MODULE_DEVICE_TABLE(usb, pn533_table); + +/* How much time we spend listening for initiators */ +#define PN533_LISTEN_TIME 2 +/* Delay between each poll frame (ms) */ +#define PN533_POLL_INTERVAL 10 + +/* Standard pn533 frame definitions (standard and extended)*/ +#define PN533_STD_FRAME_HEADER_LEN (sizeof(struct pn533_std_frame) \ + + 2) /* data[0] TFI, data[1] CC */ +#define PN533_STD_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/ + +#define PN533_EXT_FRAME_HEADER_LEN (sizeof(struct pn533_ext_frame) \ + + 2) /* data[0] TFI, data[1] CC */ + +#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262 +#define PN533_CMD_DATAFRAME_MAXLEN 240 /* max data length (send) */ + +/* + * Max extended frame payload len, excluding TFI and CC + * which are already in PN533_FRAME_HEADER_LEN. + */ +#define PN533_STD_FRAME_MAX_PAYLOAD_LEN 263 + +#define PN533_STD_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2), + Postamble (1) */ +#define PN533_STD_FRAME_CHECKSUM(f) (f->data[f->datalen]) +#define PN533_STD_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1]) +/* Half start code (3), LEN (4) should be 0xffff for extended frame */ +#define PN533_STD_IS_EXTENDED(hdr) ((hdr)->datalen == 0xFF \ + && (hdr)->datalen_checksum == 0xFF) +#define PN533_EXT_FRAME_CHECKSUM(f) (f->data[be16_to_cpu(f->datalen)]) + +/* start of frame */ +#define PN533_STD_FRAME_SOF 0x00FF + +/* standard frame identifier: in/out/error */ +#define PN533_STD_FRAME_IDENTIFIER(f) (f->data[0]) /* TFI */ +#define PN533_STD_FRAME_DIR_OUT 0xD4 +#define PN533_STD_FRAME_DIR_IN 0xD5 + +/* ACS ACR122 pn533 frame definitions */ +#define PN533_ACR122_TX_FRAME_HEADER_LEN (sizeof(struct pn533_acr122_tx_frame) \ + + 2) +#define PN533_ACR122_TX_FRAME_TAIL_LEN 0 +#define PN533_ACR122_RX_FRAME_HEADER_LEN (sizeof(struct pn533_acr122_rx_frame) \ + + 2) +#define PN533_ACR122_RX_FRAME_TAIL_LEN 2 +#define PN533_ACR122_FRAME_MAX_PAYLOAD_LEN PN533_STD_FRAME_MAX_PAYLOAD_LEN + +/* CCID messages types */ +#define PN533_ACR122_PC_TO_RDR_ICCPOWERON 0x62 +#define PN533_ACR122_PC_TO_RDR_ESCAPE 0x6B + +#define PN533_ACR122_RDR_TO_PC_ESCAPE 0x83 + +/* PN533 Commands */ +#define PN533_FRAME_CMD(f) (f->data[1]) + +#define PN533_CMD_GET_FIRMWARE_VERSION 0x02 +#define PN533_CMD_RF_CONFIGURATION 0x32 +#define PN533_CMD_IN_DATA_EXCHANGE 0x40 +#define PN533_CMD_IN_COMM_THRU 0x42 +#define PN533_CMD_IN_LIST_PASSIVE_TARGET 0x4A +#define PN533_CMD_IN_ATR 0x50 +#define PN533_CMD_IN_RELEASE 0x52 +#define PN533_CMD_IN_JUMP_FOR_DEP 0x56 + +#define PN533_CMD_TG_INIT_AS_TARGET 0x8c +#define PN533_CMD_TG_GET_DATA 0x86 +#define PN533_CMD_TG_SET_DATA 0x8e +#define PN533_CMD_TG_SET_META_DATA 0x94 +#define PN533_CMD_UNDEF 0xff + +#define PN533_CMD_RESPONSE(cmd) (cmd + 1) + +/* PN533 Return codes */ +#define PN533_CMD_RET_MASK 0x3F +#define PN533_CMD_MI_MASK 0x40 +#define PN533_CMD_RET_SUCCESS 0x00 + +struct pn533; + +typedef int (*pn533_send_async_complete_t) (struct pn533 *dev, void *arg, + struct sk_buff *resp); + +/* structs for pn533 commands */ + +/* PN533_CMD_GET_FIRMWARE_VERSION */ +struct pn533_fw_version { + u8 ic; + u8 ver; + u8 rev; + u8 support; +}; + +/* PN533_CMD_RF_CONFIGURATION */ +#define PN533_CFGITEM_RF_FIELD 0x01 +#define PN533_CFGITEM_TIMING 0x02 +#define PN533_CFGITEM_MAX_RETRIES 0x05 +#define PN533_CFGITEM_PASORI 0x82 + +#define PN533_CFGITEM_RF_FIELD_AUTO_RFCA 0x2 +#define PN533_CFGITEM_RF_FIELD_ON 0x1 +#define PN533_CFGITEM_RF_FIELD_OFF 0x0 + +#define PN533_CONFIG_TIMING_102 0xb +#define PN533_CONFIG_TIMING_204 0xc +#define PN533_CONFIG_TIMING_409 0xd +#define PN533_CONFIG_TIMING_819 0xe + +#define PN533_CONFIG_MAX_RETRIES_NO_RETRY 0x00 +#define PN533_CONFIG_MAX_RETRIES_ENDLESS 0xFF + +struct pn533_config_max_retries { + u8 mx_rty_atr; + u8 mx_rty_psl; + u8 mx_rty_passive_act; +} __packed; + +struct pn533_config_timing { + u8 rfu; + u8 atr_res_timeout; + u8 dep_timeout; +} __packed; + +/* PN533_CMD_IN_LIST_PASSIVE_TARGET */ + +/* felica commands opcode */ +#define PN533_FELICA_OPC_SENSF_REQ 0 +#define PN533_FELICA_OPC_SENSF_RES 1 +/* felica SENSF_REQ parameters */ +#define PN533_FELICA_SENSF_SC_ALL 0xFFFF +#define PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE 0 +#define PN533_FELICA_SENSF_RC_SYSTEM_CODE 1 +#define PN533_FELICA_SENSF_RC_ADVANCED_PROTOCOL 2 + +/* type B initiator_data values */ +#define PN533_TYPE_B_AFI_ALL_FAMILIES 0 +#define PN533_TYPE_B_POLL_METHOD_TIMESLOT 0 +#define PN533_TYPE_B_POLL_METHOD_PROBABILISTIC 1 + +union pn533_cmd_poll_initdata { + struct { + u8 afi; + u8 polling_method; + } __packed type_b; + struct { + u8 opcode; + __be16 sc; + u8 rc; + u8 tsn; + } __packed felica; +}; + +/* Poll modulations */ +enum { + PN533_POLL_MOD_106KBPS_A, + PN533_POLL_MOD_212KBPS_FELICA, + PN533_POLL_MOD_424KBPS_FELICA, + PN533_POLL_MOD_106KBPS_JEWEL, + PN533_POLL_MOD_847KBPS_B, + PN533_LISTEN_MOD, + + __PN533_POLL_MOD_AFTER_LAST, +}; +#define PN533_POLL_MOD_MAX (__PN533_POLL_MOD_AFTER_LAST - 1) + +struct pn533_poll_modulations { + struct { + u8 maxtg; + u8 brty; + union pn533_cmd_poll_initdata initiator_data; + } __packed data; + u8 len; +}; + +static const struct pn533_poll_modulations poll_mod[] = { + [PN533_POLL_MOD_106KBPS_A] = { + .data = { + .maxtg = 1, + .brty = 0, + }, + .len = 2, + }, + [PN533_POLL_MOD_212KBPS_FELICA] = { + .data = { + .maxtg = 1, + .brty = 1, + .initiator_data.felica = { + .opcode = PN533_FELICA_OPC_SENSF_REQ, + .sc = PN533_FELICA_SENSF_SC_ALL, + .rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE, + .tsn = 0x03, + }, + }, + .len = 7, + }, + [PN533_POLL_MOD_424KBPS_FELICA] = { + .data = { + .maxtg = 1, + .brty = 2, + .initiator_data.felica = { + .opcode = PN533_FELICA_OPC_SENSF_REQ, + .sc = PN533_FELICA_SENSF_SC_ALL, + .rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE, + .tsn = 0x03, + }, + }, + .len = 7, + }, + [PN533_POLL_MOD_106KBPS_JEWEL] = { + .data = { + .maxtg = 1, + .brty = 4, + }, + .len = 2, + }, + [PN533_POLL_MOD_847KBPS_B] = { + .data = { + .maxtg = 1, + .brty = 8, + .initiator_data.type_b = { + .afi = PN533_TYPE_B_AFI_ALL_FAMILIES, + .polling_method = + PN533_TYPE_B_POLL_METHOD_TIMESLOT, + }, + }, + .len = 3, + }, + [PN533_LISTEN_MOD] = { + .len = 0, + }, +}; + +/* PN533_CMD_IN_ATR */ + +struct pn533_cmd_activate_response { + u8 status; + u8 nfcid3t[10]; + u8 didt; + u8 bst; + u8 brt; + u8 to; + u8 ppt; + /* optional */ + u8 gt[]; +} __packed; + +struct pn533_cmd_jump_dep_response { + u8 status; + u8 tg; + u8 nfcid3t[10]; + u8 didt; + u8 bst; + u8 brt; + u8 to; + u8 ppt; + /* optional */ + u8 gt[]; +} __packed; + + +/* PN533_TG_INIT_AS_TARGET */ +#define PN533_INIT_TARGET_PASSIVE 0x1 +#define PN533_INIT_TARGET_DEP 0x2 + +#define PN533_INIT_TARGET_RESP_FRAME_MASK 0x3 +#define PN533_INIT_TARGET_RESP_ACTIVE 0x1 +#define PN533_INIT_TARGET_RESP_DEP 0x4 + +enum pn533_protocol_type { + PN533_PROTO_REQ_ACK_RESP = 0, + PN533_PROTO_REQ_RESP +}; + +struct pn533 { + struct usb_device *udev; + struct usb_interface *interface; + struct nfc_dev *nfc_dev; + u32 device_type; + enum pn533_protocol_type protocol_type; + + struct urb *out_urb; + struct urb *in_urb; + + struct sk_buff_head resp_q; + struct sk_buff_head fragment_skb; + + struct workqueue_struct *wq; + struct work_struct cmd_work; + struct work_struct cmd_complete_work; + struct delayed_work poll_work; + struct work_struct mi_rx_work; + struct work_struct mi_tx_work; + struct work_struct mi_tm_rx_work; + struct work_struct mi_tm_tx_work; + struct work_struct tg_work; + struct work_struct rf_work; + + struct list_head cmd_queue; + struct pn533_cmd *cmd; + u8 cmd_pending; + struct mutex cmd_lock; /* protects cmd queue */ + + void *cmd_complete_mi_arg; + void *cmd_complete_dep_arg; + + struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1]; + u8 poll_mod_count; + u8 poll_mod_curr; + u8 poll_dep; + u32 poll_protocols; + u32 listen_protocols; + struct timer_list listen_timer; + int cancel_listen; + + u8 *gb; + size_t gb_len; + + u8 tgt_available_prots; + u8 tgt_active_prot; + u8 tgt_mode; + + struct pn533_frame_ops *ops; +}; + +struct pn533_cmd { + struct list_head queue; + u8 code; + int status; + struct sk_buff *req; + struct sk_buff *resp; + int resp_len; + pn533_send_async_complete_t complete_cb; + void *complete_cb_context; +}; + +struct pn533_std_frame { + u8 preamble; + __be16 start_frame; + u8 datalen; + u8 datalen_checksum; + u8 data[]; +} __packed; + +struct pn533_ext_frame { /* Extended Information frame */ + u8 preamble; + __be16 start_frame; + __be16 eif_flag; /* fixed to 0xFFFF */ + __be16 datalen; + u8 datalen_checksum; + u8 data[]; +} __packed; + +struct pn533_frame_ops { + void (*tx_frame_init)(void *frame, u8 cmd_code); + void (*tx_frame_finish)(void *frame); + void (*tx_update_payload_len)(void *frame, int len); + int tx_header_len; + int tx_tail_len; + + bool (*rx_is_frame_valid)(void *frame, struct pn533 *dev); + int (*rx_frame_size)(void *frame); + int rx_header_len; + int rx_tail_len; + + int max_payload_len; + u8 (*get_cmd_code)(void *frame); +}; + +struct pn533_acr122_ccid_hdr { + u8 type; + u32 datalen; + u8 slot; + u8 seq; + u8 params[3]; /* 3 msg specific bytes or status, error and 1 specific + byte for reposnse msg */ + u8 data[]; /* payload */ +} __packed; + +struct pn533_acr122_apdu_hdr { + u8 class; + u8 ins; + u8 p1; + u8 p2; +} __packed; + +struct pn533_acr122_tx_frame { + struct pn533_acr122_ccid_hdr ccid; + struct pn533_acr122_apdu_hdr apdu; + u8 datalen; + u8 data[]; /* pn533 frame: TFI ... */ +} __packed; + +struct pn533_acr122_rx_frame { + struct pn533_acr122_ccid_hdr ccid; + u8 data[]; /* pn533 frame : TFI ... */ +} __packed; + +static void pn533_acr122_tx_frame_init(void *_frame, u8 cmd_code) +{ + struct pn533_acr122_tx_frame *frame = _frame; + + frame->ccid.type = PN533_ACR122_PC_TO_RDR_ESCAPE; + frame->ccid.datalen = sizeof(frame->apdu) + 1; /* sizeof(apdu_hdr) + + sizeof(datalen) */ + frame->ccid.slot = 0; + frame->ccid.seq = 0; + frame->ccid.params[0] = 0; + frame->ccid.params[1] = 0; + frame->ccid.params[2] = 0; + + frame->data[0] = PN533_STD_FRAME_DIR_OUT; + frame->data[1] = cmd_code; + frame->datalen = 2; /* data[0] + data[1] */ + + frame->apdu.class = 0xFF; + frame->apdu.ins = 0; + frame->apdu.p1 = 0; + frame->apdu.p2 = 0; +} + +static void pn533_acr122_tx_frame_finish(void *_frame) +{ + struct pn533_acr122_tx_frame *frame = _frame; + + frame->ccid.datalen += frame->datalen; +} + +static void pn533_acr122_tx_update_payload_len(void *_frame, int len) +{ + struct pn533_acr122_tx_frame *frame = _frame; + + frame->datalen += len; +} + +static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev) +{ + struct pn533_acr122_rx_frame *frame = _frame; + + if (frame->ccid.type != 0x83) + return false; + + if (!frame->ccid.datalen) + return false; + + if (frame->data[frame->ccid.datalen - 2] == 0x63) + return false; + + return true; +} + +static int pn533_acr122_rx_frame_size(void *frame) +{ + struct pn533_acr122_rx_frame *f = frame; + + /* f->ccid.datalen already includes tail length */ + return sizeof(struct pn533_acr122_rx_frame) + f->ccid.datalen; +} + +static u8 pn533_acr122_get_cmd_code(void *frame) +{ + struct pn533_acr122_rx_frame *f = frame; + + return PN533_FRAME_CMD(f); +} + +static struct pn533_frame_ops pn533_acr122_frame_ops = { + .tx_frame_init = pn533_acr122_tx_frame_init, + .tx_frame_finish = pn533_acr122_tx_frame_finish, + .tx_update_payload_len = pn533_acr122_tx_update_payload_len, + .tx_header_len = PN533_ACR122_TX_FRAME_HEADER_LEN, + .tx_tail_len = PN533_ACR122_TX_FRAME_TAIL_LEN, + + .rx_is_frame_valid = pn533_acr122_is_rx_frame_valid, + .rx_header_len = PN533_ACR122_RX_FRAME_HEADER_LEN, + .rx_tail_len = PN533_ACR122_RX_FRAME_TAIL_LEN, + .rx_frame_size = pn533_acr122_rx_frame_size, + + .max_payload_len = PN533_ACR122_FRAME_MAX_PAYLOAD_LEN, + .get_cmd_code = pn533_acr122_get_cmd_code, +}; + +/* The rule: value(high byte) + value(low byte) + checksum = 0 */ +static inline u8 pn533_ext_checksum(u16 value) +{ + return ~(u8)(((value & 0xFF00) >> 8) + (u8)(value & 0xFF)) + 1; +} + +/* The rule: value + checksum = 0 */ +static inline u8 pn533_std_checksum(u8 value) +{ + return ~value + 1; +} + +/* The rule: sum(data elements) + checksum = 0 */ +static u8 pn533_std_data_checksum(u8 *data, int datalen) +{ + u8 sum = 0; + int i; + + for (i = 0; i < datalen; i++) + sum += data[i]; + + return pn533_std_checksum(sum); +} + +static void pn533_std_tx_frame_init(void *_frame, u8 cmd_code) +{ + struct pn533_std_frame *frame = _frame; + + frame->preamble = 0; + frame->start_frame = cpu_to_be16(PN533_STD_FRAME_SOF); + PN533_STD_FRAME_IDENTIFIER(frame) = PN533_STD_FRAME_DIR_OUT; + PN533_FRAME_CMD(frame) = cmd_code; + frame->datalen = 2; +} + +static void pn533_std_tx_frame_finish(void *_frame) +{ + struct pn533_std_frame *frame = _frame; + + frame->datalen_checksum = pn533_std_checksum(frame->datalen); + + PN533_STD_FRAME_CHECKSUM(frame) = + pn533_std_data_checksum(frame->data, frame->datalen); + + PN533_STD_FRAME_POSTAMBLE(frame) = 0; +} + +static void pn533_std_tx_update_payload_len(void *_frame, int len) +{ + struct pn533_std_frame *frame = _frame; + + frame->datalen += len; +} + +static bool pn533_std_rx_frame_is_valid(void *_frame, struct pn533 *dev) +{ + u8 checksum; + struct pn533_std_frame *stdf = _frame; + + if (stdf->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF)) + return false; + + if (likely(!PN533_STD_IS_EXTENDED(stdf))) { + /* Standard frame code */ + dev->ops->rx_header_len = PN533_STD_FRAME_HEADER_LEN; + + checksum = pn533_std_checksum(stdf->datalen); + if (checksum != stdf->datalen_checksum) + return false; + + checksum = pn533_std_data_checksum(stdf->data, stdf->datalen); + if (checksum != PN533_STD_FRAME_CHECKSUM(stdf)) + return false; + } else { + /* Extended */ + struct pn533_ext_frame *eif = _frame; + + dev->ops->rx_header_len = PN533_EXT_FRAME_HEADER_LEN; + + checksum = pn533_ext_checksum(be16_to_cpu(eif->datalen)); + if (checksum != eif->datalen_checksum) + return false; + + /* check data checksum */ + checksum = pn533_std_data_checksum(eif->data, + be16_to_cpu(eif->datalen)); + if (checksum != PN533_EXT_FRAME_CHECKSUM(eif)) + return false; + } + + return true; +} + +static bool pn533_std_rx_frame_is_ack(struct pn533_std_frame *frame) +{ + if (frame->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF)) + return false; + + if (frame->datalen != 0 || frame->datalen_checksum != 0xFF) + return false; + + return true; +} + +static inline int pn533_std_rx_frame_size(void *frame) +{ + struct pn533_std_frame *f = frame; + + /* check for Extended Information frame */ + if (PN533_STD_IS_EXTENDED(f)) { + struct pn533_ext_frame *eif = frame; + + return sizeof(struct pn533_ext_frame) + + be16_to_cpu(eif->datalen) + PN533_STD_FRAME_TAIL_LEN; + } + + return sizeof(struct pn533_std_frame) + f->datalen + + PN533_STD_FRAME_TAIL_LEN; +} + +static u8 pn533_std_get_cmd_code(void *frame) +{ + struct pn533_std_frame *f = frame; + struct pn533_ext_frame *eif = frame; + + if (PN533_STD_IS_EXTENDED(f)) + return PN533_FRAME_CMD(eif); + else + return PN533_FRAME_CMD(f); +} + +static struct pn533_frame_ops pn533_std_frame_ops = { + .tx_frame_init = pn533_std_tx_frame_init, + .tx_frame_finish = pn533_std_tx_frame_finish, + .tx_update_payload_len = pn533_std_tx_update_payload_len, + .tx_header_len = PN533_STD_FRAME_HEADER_LEN, + .tx_tail_len = PN533_STD_FRAME_TAIL_LEN, + + .rx_is_frame_valid = pn533_std_rx_frame_is_valid, + .rx_frame_size = pn533_std_rx_frame_size, + .rx_header_len = PN533_STD_FRAME_HEADER_LEN, + .rx_tail_len = PN533_STD_FRAME_TAIL_LEN, + + .max_payload_len = PN533_STD_FRAME_MAX_PAYLOAD_LEN, + .get_cmd_code = pn533_std_get_cmd_code, +}; + +static bool pn533_rx_frame_is_cmd_response(struct pn533 *dev, void *frame) +{ + return (dev->ops->get_cmd_code(frame) == + PN533_CMD_RESPONSE(dev->cmd->code)); +} + +static void pn533_recv_response(struct urb *urb) +{ + struct pn533 *dev = urb->context; + struct pn533_cmd *cmd = dev->cmd; + u8 *in_frame; + + cmd->status = urb->status; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + dev_dbg(&dev->interface->dev, + "The urb has been canceled (status %d)\n", + urb->status); + goto sched_wq; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, + "Urb failure (status %d)\n", urb->status); + goto sched_wq; + } + + in_frame = dev->in_urb->transfer_buffer; + + dev_dbg(&dev->interface->dev, "Received a frame\n"); + print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame, + dev->ops->rx_frame_size(in_frame), false); + + if (!dev->ops->rx_is_frame_valid(in_frame, dev)) { + nfc_err(&dev->interface->dev, "Received an invalid frame\n"); + cmd->status = -EIO; + goto sched_wq; + } + + if (!pn533_rx_frame_is_cmd_response(dev, in_frame)) { + nfc_err(&dev->interface->dev, + "It it not the response to the last command\n"); + cmd->status = -EIO; + goto sched_wq; + } + +sched_wq: + queue_work(dev->wq, &dev->cmd_complete_work); +} + +static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags) +{ + dev->in_urb->complete = pn533_recv_response; + + return usb_submit_urb(dev->in_urb, flags); +} + +static void pn533_recv_ack(struct urb *urb) +{ + struct pn533 *dev = urb->context; + struct pn533_cmd *cmd = dev->cmd; + struct pn533_std_frame *in_frame; + int rc; + + cmd->status = urb->status; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + dev_dbg(&dev->interface->dev, + "The urb has been stopped (status %d)\n", + urb->status); + goto sched_wq; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, + "Urb failure (status %d)\n", urb->status); + goto sched_wq; + } + + in_frame = dev->in_urb->transfer_buffer; + + if (!pn533_std_rx_frame_is_ack(in_frame)) { + nfc_err(&dev->interface->dev, "Received an invalid ack\n"); + cmd->status = -EIO; + goto sched_wq; + } + + rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC); + if (rc) { + nfc_err(&dev->interface->dev, + "usb_submit_urb failed with result %d\n", rc); + cmd->status = rc; + goto sched_wq; + } + + return; + +sched_wq: + queue_work(dev->wq, &dev->cmd_complete_work); +} + +static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags) +{ + dev->in_urb->complete = pn533_recv_ack; + + return usb_submit_urb(dev->in_urb, flags); +} + +static int pn533_send_ack(struct pn533 *dev, gfp_t flags) +{ + u8 ack[PN533_STD_FRAME_ACK_SIZE] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00}; + /* spec 7.1.1.3: Preamble, SoPC (2), ACK Code (2), Postamble */ + int rc; + + dev->out_urb->transfer_buffer = ack; + dev->out_urb->transfer_buffer_length = sizeof(ack); + rc = usb_submit_urb(dev->out_urb, flags); + + return rc; +} + +static int __pn533_send_frame_async(struct pn533 *dev, + struct sk_buff *out, + struct sk_buff *in, + int in_len) +{ + int rc; + + dev->out_urb->transfer_buffer = out->data; + dev->out_urb->transfer_buffer_length = out->len; + + dev->in_urb->transfer_buffer = in->data; + dev->in_urb->transfer_buffer_length = in_len; + + print_hex_dump_debug("PN533 TX: ", DUMP_PREFIX_NONE, 16, 1, + out->data, out->len, false); + + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); + if (rc) + return rc; + + if (dev->protocol_type == PN533_PROTO_REQ_RESP) { + /* request for response for sent packet directly */ + rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC); + if (rc) + goto error; + } else if (dev->protocol_type == PN533_PROTO_REQ_ACK_RESP) { + /* request for ACK if that's the case */ + rc = pn533_submit_urb_for_ack(dev, GFP_KERNEL); + if (rc) + goto error; + } + + return 0; + +error: + usb_unlink_urb(dev->out_urb); + return rc; +} + +static void pn533_build_cmd_frame(struct pn533 *dev, u8 cmd_code, + struct sk_buff *skb) +{ + /* payload is already there, just update datalen */ + int payload_len = skb->len; + struct pn533_frame_ops *ops = dev->ops; + + + skb_push(skb, ops->tx_header_len); + skb_put(skb, ops->tx_tail_len); + + ops->tx_frame_init(skb->data, cmd_code); + ops->tx_update_payload_len(skb->data, payload_len); + ops->tx_frame_finish(skb->data); +} + +static int pn533_send_async_complete(struct pn533 *dev) +{ + struct pn533_cmd *cmd = dev->cmd; + int status = cmd->status; + + struct sk_buff *req = cmd->req; + struct sk_buff *resp = cmd->resp; + + int rc; + + dev_kfree_skb(req); + + if (status < 0) { + rc = cmd->complete_cb(dev, cmd->complete_cb_context, + ERR_PTR(status)); + dev_kfree_skb(resp); + goto done; + } + + skb_put(resp, dev->ops->rx_frame_size(resp->data)); + skb_pull(resp, dev->ops->rx_header_len); + skb_trim(resp, resp->len - dev->ops->rx_tail_len); + + rc = cmd->complete_cb(dev, cmd->complete_cb_context, resp); + +done: + kfree(cmd); + dev->cmd = NULL; + return rc; +} + +static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req, struct sk_buff *resp, + int resp_len, + pn533_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct pn533_cmd *cmd; + int rc = 0; + + dev_dbg(&dev->interface->dev, "Sending command 0x%x\n", cmd_code); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->code = cmd_code; + cmd->req = req; + cmd->resp = resp; + cmd->resp_len = resp_len; + cmd->complete_cb = complete_cb; + cmd->complete_cb_context = complete_cb_context; + + pn533_build_cmd_frame(dev, cmd_code, req); + + mutex_lock(&dev->cmd_lock); + + if (!dev->cmd_pending) { + rc = __pn533_send_frame_async(dev, req, resp, resp_len); + if (rc) + goto error; + + dev->cmd_pending = 1; + dev->cmd = cmd; + goto unlock; + } + + dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x\n", + __func__, cmd_code); + + INIT_LIST_HEAD(&cmd->queue); + list_add_tail(&cmd->queue, &dev->cmd_queue); + + goto unlock; + +error: + kfree(cmd); +unlock: + mutex_unlock(&dev->cmd_lock); + return rc; +} + +static int pn533_send_data_async(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req, + pn533_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct sk_buff *resp; + int rc; + int resp_len = dev->ops->rx_header_len + + dev->ops->max_payload_len + + dev->ops->rx_tail_len; + + resp = nfc_alloc_recv_skb(resp_len, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + rc = __pn533_send_async(dev, cmd_code, req, resp, resp_len, complete_cb, + complete_cb_context); + if (rc) + dev_kfree_skb(resp); + + return rc; +} + +static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req, + pn533_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct sk_buff *resp; + int rc; + int resp_len = dev->ops->rx_header_len + + dev->ops->max_payload_len + + dev->ops->rx_tail_len; + + resp = alloc_skb(resp_len, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + rc = __pn533_send_async(dev, cmd_code, req, resp, resp_len, complete_cb, + complete_cb_context); + if (rc) + dev_kfree_skb(resp); + + return rc; +} + +/* + * pn533_send_cmd_direct_async + * + * The function sends a piority cmd directly to the chip omiting the cmd + * queue. It's intended to be used by chaining mechanism of received responses + * where the host has to request every single chunk of data before scheduling + * next cmd from the queue. + */ +static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req, + pn533_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct sk_buff *resp; + struct pn533_cmd *cmd; + int rc; + int resp_len = dev->ops->rx_header_len + + dev->ops->max_payload_len + + dev->ops->rx_tail_len; + + resp = alloc_skb(resp_len, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + dev_kfree_skb(resp); + return -ENOMEM; + } + + cmd->code = cmd_code; + cmd->req = req; + cmd->resp = resp; + cmd->resp_len = resp_len; + cmd->complete_cb = complete_cb; + cmd->complete_cb_context = complete_cb_context; + + pn533_build_cmd_frame(dev, cmd_code, req); + + rc = __pn533_send_frame_async(dev, req, resp, resp_len); + if (rc < 0) { + dev_kfree_skb(resp); + kfree(cmd); + } else { + dev->cmd = cmd; + } + + return rc; +} + +static void pn533_wq_cmd_complete(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work); + int rc; + + rc = pn533_send_async_complete(dev); + if (rc != -EINPROGRESS) + queue_work(dev->wq, &dev->cmd_work); +} + +static void pn533_wq_cmd(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, cmd_work); + struct pn533_cmd *cmd; + int rc; + + mutex_lock(&dev->cmd_lock); + + if (list_empty(&dev->cmd_queue)) { + dev->cmd_pending = 0; + mutex_unlock(&dev->cmd_lock); + return; + } + + cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue); + + list_del(&cmd->queue); + + mutex_unlock(&dev->cmd_lock); + + rc = __pn533_send_frame_async(dev, cmd->req, cmd->resp, cmd->resp_len); + if (rc < 0) { + dev_kfree_skb(cmd->req); + dev_kfree_skb(cmd->resp); + kfree(cmd); + return; + } + + dev->cmd = cmd; +} + +struct pn533_sync_cmd_response { + struct sk_buff *resp; + struct completion done; +}; + +static int pn533_send_sync_complete(struct pn533 *dev, void *_arg, + struct sk_buff *resp) +{ + struct pn533_sync_cmd_response *arg = _arg; + + arg->resp = resp; + complete(&arg->done); + + return 0; +} + +/* pn533_send_cmd_sync + * + * Please note the req parameter is freed inside the function to + * limit a number of return value interpretations by the caller. + * + * 1. negative in case of error during TX path -> req should be freed + * + * 2. negative in case of error during RX path -> req should not be freed + * as it's been already freed at the begining of RX path by + * async_complete_cb. + * + * 3. valid pointer in case of succesfult RX path + * + * A caller has to check a return value with IS_ERR macro. If the test pass, + * the returned pointer is valid. + * + * */ +static struct sk_buff *pn533_send_cmd_sync(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req) +{ + int rc; + struct pn533_sync_cmd_response arg; + + init_completion(&arg.done); + + rc = pn533_send_cmd_async(dev, cmd_code, req, + pn533_send_sync_complete, &arg); + if (rc) { + dev_kfree_skb(req); + return ERR_PTR(rc); + } + + wait_for_completion(&arg.done); + + return arg.resp; +} + +static void pn533_send_complete(struct urb *urb) +{ + struct pn533 *dev = urb->context; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + dev_dbg(&dev->interface->dev, + "The urb has been stopped (status %d)\n", + urb->status); + break; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", + urb->status); + } +} + +static void pn533_abort_cmd(struct pn533 *dev, gfp_t flags) +{ + /* ACR122U does not support any command which aborts last + * issued command i.e. as ACK for standard PN533. Additionally, + * it behaves stange, sending broken or incorrect responses, + * when we cancel urb before the chip will send response. + */ + if (dev->device_type == PN533_DEVICE_ACR122U) + return; + + /* An ack will cancel the last issued command */ + pn533_send_ack(dev, flags); + + /* cancel the urb request */ + usb_kill_urb(dev->in_urb); +} + +static struct sk_buff *pn533_alloc_skb(struct pn533 *dev, unsigned int size) +{ + struct sk_buff *skb; + + skb = alloc_skb(dev->ops->tx_header_len + + size + + dev->ops->tx_tail_len, GFP_KERNEL); + + if (skb) + skb_reserve(skb, dev->ops->tx_header_len); + + return skb; +} + +struct pn533_target_type_a { + __be16 sens_res; + u8 sel_res; + u8 nfcid_len; + u8 nfcid_data[]; +} __packed; + + +#define PN533_TYPE_A_SENS_RES_NFCID1(x) ((u8)((be16_to_cpu(x) & 0x00C0) >> 6)) +#define PN533_TYPE_A_SENS_RES_SSD(x) ((u8)((be16_to_cpu(x) & 0x001F) >> 0)) +#define PN533_TYPE_A_SENS_RES_PLATCONF(x) ((u8)((be16_to_cpu(x) & 0x0F00) >> 8)) + +#define PN533_TYPE_A_SENS_RES_SSD_JEWEL 0x00 +#define PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL 0x0C + +#define PN533_TYPE_A_SEL_PROT(x) (((x) & 0x60) >> 5) +#define PN533_TYPE_A_SEL_CASCADE(x) (((x) & 0x04) >> 2) + +#define PN533_TYPE_A_SEL_PROT_MIFARE 0 +#define PN533_TYPE_A_SEL_PROT_ISO14443 1 +#define PN533_TYPE_A_SEL_PROT_DEP 2 +#define PN533_TYPE_A_SEL_PROT_ISO14443_DEP 3 + +static bool pn533_target_type_a_is_valid(struct pn533_target_type_a *type_a, + int target_data_len) +{ + u8 ssd; + u8 platconf; + + if (target_data_len < sizeof(struct pn533_target_type_a)) + return false; + + /* The lenght check of nfcid[] and ats[] are not being performed because + the values are not being used */ + + /* Requirement 4.6.3.3 from NFC Forum Digital Spec */ + ssd = PN533_TYPE_A_SENS_RES_SSD(type_a->sens_res); + platconf = PN533_TYPE_A_SENS_RES_PLATCONF(type_a->sens_res); + + if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL && + platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) || + (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL && + platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL)) + return false; + + /* Requirements 4.8.2.1, 4.8.2.3, 4.8.2.5 and 4.8.2.7 from NFC Forum */ + if (PN533_TYPE_A_SEL_CASCADE(type_a->sel_res) != 0) + return false; + + return true; +} + +static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data, + int tgt_data_len) +{ + struct pn533_target_type_a *tgt_type_a; + + tgt_type_a = (struct pn533_target_type_a *)tgt_data; + + if (!pn533_target_type_a_is_valid(tgt_type_a, tgt_data_len)) + return -EPROTO; + + switch (PN533_TYPE_A_SEL_PROT(tgt_type_a->sel_res)) { + case PN533_TYPE_A_SEL_PROT_MIFARE: + nfc_tgt->supported_protocols = NFC_PROTO_MIFARE_MASK; + break; + case PN533_TYPE_A_SEL_PROT_ISO14443: + nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK; + break; + case PN533_TYPE_A_SEL_PROT_DEP: + nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + break; + case PN533_TYPE_A_SEL_PROT_ISO14443_DEP: + nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK | + NFC_PROTO_NFC_DEP_MASK; + break; + } + + nfc_tgt->sens_res = be16_to_cpu(tgt_type_a->sens_res); + nfc_tgt->sel_res = tgt_type_a->sel_res; + nfc_tgt->nfcid1_len = tgt_type_a->nfcid_len; + memcpy(nfc_tgt->nfcid1, tgt_type_a->nfcid_data, nfc_tgt->nfcid1_len); + + return 0; +} + +struct pn533_target_felica { + u8 pol_res; + u8 opcode; + u8 nfcid2[NFC_NFCID2_MAXSIZE]; + u8 pad[8]; + /* optional */ + u8 syst_code[]; +} __packed; + +#define PN533_FELICA_SENSF_NFCID2_DEP_B1 0x01 +#define PN533_FELICA_SENSF_NFCID2_DEP_B2 0xFE + +static bool pn533_target_felica_is_valid(struct pn533_target_felica *felica, + int target_data_len) +{ + if (target_data_len < sizeof(struct pn533_target_felica)) + return false; + + if (felica->opcode != PN533_FELICA_OPC_SENSF_RES) + return false; + + return true; +} + +static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data, + int tgt_data_len) +{ + struct pn533_target_felica *tgt_felica; + + tgt_felica = (struct pn533_target_felica *)tgt_data; + + if (!pn533_target_felica_is_valid(tgt_felica, tgt_data_len)) + return -EPROTO; + + if ((tgt_felica->nfcid2[0] == PN533_FELICA_SENSF_NFCID2_DEP_B1) && + (tgt_felica->nfcid2[1] == PN533_FELICA_SENSF_NFCID2_DEP_B2)) + nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + else + nfc_tgt->supported_protocols = NFC_PROTO_FELICA_MASK; + + memcpy(nfc_tgt->sensf_res, &tgt_felica->opcode, 9); + nfc_tgt->sensf_res_len = 9; + + memcpy(nfc_tgt->nfcid2, tgt_felica->nfcid2, NFC_NFCID2_MAXSIZE); + nfc_tgt->nfcid2_len = NFC_NFCID2_MAXSIZE; + + return 0; +} + +struct pn533_target_jewel { + __be16 sens_res; + u8 jewelid[4]; +} __packed; + +static bool pn533_target_jewel_is_valid(struct pn533_target_jewel *jewel, + int target_data_len) +{ + u8 ssd; + u8 platconf; + + if (target_data_len < sizeof(struct pn533_target_jewel)) + return false; + + /* Requirement 4.6.3.3 from NFC Forum Digital Spec */ + ssd = PN533_TYPE_A_SENS_RES_SSD(jewel->sens_res); + platconf = PN533_TYPE_A_SENS_RES_PLATCONF(jewel->sens_res); + + if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL && + platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) || + (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL && + platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL)) + return false; + + return true; +} + +static int pn533_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data, + int tgt_data_len) +{ + struct pn533_target_jewel *tgt_jewel; + + tgt_jewel = (struct pn533_target_jewel *)tgt_data; + + if (!pn533_target_jewel_is_valid(tgt_jewel, tgt_data_len)) + return -EPROTO; + + nfc_tgt->supported_protocols = NFC_PROTO_JEWEL_MASK; + nfc_tgt->sens_res = be16_to_cpu(tgt_jewel->sens_res); + nfc_tgt->nfcid1_len = 4; + memcpy(nfc_tgt->nfcid1, tgt_jewel->jewelid, nfc_tgt->nfcid1_len); + + return 0; +} + +struct pn533_type_b_prot_info { + u8 bitrate; + u8 fsci_type; + u8 fwi_adc_fo; +} __packed; + +#define PN533_TYPE_B_PROT_FCSI(x) (((x) & 0xF0) >> 4) +#define PN533_TYPE_B_PROT_TYPE(x) (((x) & 0x0F) >> 0) +#define PN533_TYPE_B_PROT_TYPE_RFU_MASK 0x8 + +struct pn533_type_b_sens_res { + u8 opcode; + u8 nfcid[4]; + u8 appdata[4]; + struct pn533_type_b_prot_info prot_info; +} __packed; + +#define PN533_TYPE_B_OPC_SENSB_RES 0x50 + +struct pn533_target_type_b { + struct pn533_type_b_sens_res sensb_res; + u8 attrib_res_len; + u8 attrib_res[]; +} __packed; + +static bool pn533_target_type_b_is_valid(struct pn533_target_type_b *type_b, + int target_data_len) +{ + if (target_data_len < sizeof(struct pn533_target_type_b)) + return false; + + if (type_b->sensb_res.opcode != PN533_TYPE_B_OPC_SENSB_RES) + return false; + + if (PN533_TYPE_B_PROT_TYPE(type_b->sensb_res.prot_info.fsci_type) & + PN533_TYPE_B_PROT_TYPE_RFU_MASK) + return false; + + return true; +} + +static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data, + int tgt_data_len) +{ + struct pn533_target_type_b *tgt_type_b; + + tgt_type_b = (struct pn533_target_type_b *)tgt_data; + + if (!pn533_target_type_b_is_valid(tgt_type_b, tgt_data_len)) + return -EPROTO; + + nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_B_MASK; + + return 0; +} + +static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata, + int tgdata_len) +{ + struct nfc_target nfc_tgt; + int rc; + + dev_dbg(&dev->interface->dev, "%s: modulation=%d\n", + __func__, dev->poll_mod_curr); + + if (tg != 1) + return -EPROTO; + + memset(&nfc_tgt, 0, sizeof(struct nfc_target)); + + switch (dev->poll_mod_curr) { + case PN533_POLL_MOD_106KBPS_A: + rc = pn533_target_found_type_a(&nfc_tgt, tgdata, tgdata_len); + break; + case PN533_POLL_MOD_212KBPS_FELICA: + case PN533_POLL_MOD_424KBPS_FELICA: + rc = pn533_target_found_felica(&nfc_tgt, tgdata, tgdata_len); + break; + case PN533_POLL_MOD_106KBPS_JEWEL: + rc = pn533_target_found_jewel(&nfc_tgt, tgdata, tgdata_len); + break; + case PN533_POLL_MOD_847KBPS_B: + rc = pn533_target_found_type_b(&nfc_tgt, tgdata, tgdata_len); + break; + default: + nfc_err(&dev->interface->dev, + "Unknown current poll modulation\n"); + return -EPROTO; + } + + if (rc) + return rc; + + if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) { + dev_dbg(&dev->interface->dev, + "The Tg found doesn't have the desired protocol\n"); + return -EAGAIN; + } + + dev_dbg(&dev->interface->dev, + "Target found - supported protocols: 0x%x\n", + nfc_tgt.supported_protocols); + + dev->tgt_available_prots = nfc_tgt.supported_protocols; + + nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1); + + return 0; +} + +static inline void pn533_poll_next_mod(struct pn533 *dev) +{ + dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count; +} + +static void pn533_poll_reset_mod_list(struct pn533 *dev) +{ + dev->poll_mod_count = 0; +} + +static void pn533_poll_add_mod(struct pn533 *dev, u8 mod_index) +{ + dev->poll_mod_active[dev->poll_mod_count] = + (struct pn533_poll_modulations *)&poll_mod[mod_index]; + dev->poll_mod_count++; +} + +static void pn533_poll_create_mod_list(struct pn533 *dev, + u32 im_protocols, u32 tm_protocols) +{ + pn533_poll_reset_mod_list(dev); + + if ((im_protocols & NFC_PROTO_MIFARE_MASK) || + (im_protocols & NFC_PROTO_ISO14443_MASK) || + (im_protocols & NFC_PROTO_NFC_DEP_MASK)) + pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A); + + if (im_protocols & NFC_PROTO_FELICA_MASK || + im_protocols & NFC_PROTO_NFC_DEP_MASK) { + pn533_poll_add_mod(dev, PN533_POLL_MOD_212KBPS_FELICA); + pn533_poll_add_mod(dev, PN533_POLL_MOD_424KBPS_FELICA); + } + + if (im_protocols & NFC_PROTO_JEWEL_MASK) + pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_JEWEL); + + if (im_protocols & NFC_PROTO_ISO14443_B_MASK) + pn533_poll_add_mod(dev, PN533_POLL_MOD_847KBPS_B); + + if (tm_protocols) + pn533_poll_add_mod(dev, PN533_LISTEN_MOD); +} + +static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp) +{ + u8 nbtg, tg, *tgdata; + int rc, tgdata_len; + + /* Toggle the DEP polling */ + dev->poll_dep = 1; + + nbtg = resp->data[0]; + tg = resp->data[1]; + tgdata = &resp->data[2]; + tgdata_len = resp->len - 2; /* nbtg + tg */ + + if (nbtg) { + rc = pn533_target_found(dev, tg, tgdata, tgdata_len); + + /* We must stop the poll after a valid target found */ + if (rc == 0) { + pn533_poll_reset_mod_list(dev); + return 0; + } + } + + return -EAGAIN; +} + +static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev) +{ + struct sk_buff *skb; + u8 *felica, *nfcid3, *gb; + + u8 *gbytes = dev->gb; + size_t gbytes_len = dev->gb_len; + + u8 felica_params[18] = {0x1, 0xfe, /* DEP */ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xff, 0xff}; /* System code */ + + u8 mifare_params[6] = {0x1, 0x1, /* SENS_RES */ + 0x0, 0x0, 0x0, + 0x40}; /* SEL_RES for DEP */ + + unsigned int skb_len = 36 + /* mode (1), mifare (6), + felica (18), nfcid3 (10), gb_len (1) */ + gbytes_len + + 1; /* len Tk*/ + + skb = pn533_alloc_skb(dev, skb_len); + if (!skb) + return NULL; + + /* DEP support only */ + *skb_put(skb, 1) = PN533_INIT_TARGET_DEP; + + /* MIFARE params */ + memcpy(skb_put(skb, 6), mifare_params, 6); + + /* Felica params */ + felica = skb_put(skb, 18); + memcpy(felica, felica_params, 18); + get_random_bytes(felica + 2, 6); + + /* NFCID3 */ + nfcid3 = skb_put(skb, 10); + memset(nfcid3, 0, 10); + memcpy(nfcid3, felica, 8); + + /* General bytes */ + *skb_put(skb, 1) = gbytes_len; + + gb = skb_put(skb, gbytes_len); + memcpy(gb, gbytes, gbytes_len); + + /* Len Tk */ + *skb_put(skb, 1) = 0; + + return skb; +} + +#define PN533_CMD_DATAEXCH_HEAD_LEN 1 +#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262 +static void pn533_wq_tm_mi_recv(struct work_struct *work); +static struct sk_buff *pn533_build_response(struct pn533 *dev); + +static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp) +{ + struct sk_buff *skb; + u8 status, ret, mi; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (IS_ERR(resp)) { + skb_queue_purge(&dev->resp_q); + return PTR_ERR(resp); + } + + status = resp->data[0]; + + ret = status & PN533_CMD_RET_MASK; + mi = status & PN533_CMD_MI_MASK; + + skb_pull(resp, sizeof(status)); + + if (ret != PN533_CMD_RET_SUCCESS) { + rc = -EIO; + goto error; + } + + skb_queue_tail(&dev->resp_q, resp); + + if (mi) { + queue_work(dev->wq, &dev->mi_tm_rx_work); + return -EINPROGRESS; + } + + skb = pn533_build_response(dev); + if (!skb) { + rc = -EIO; + goto error; + } + + return nfc_tm_data_received(dev->nfc_dev, skb); + +error: + nfc_tm_deactivated(dev->nfc_dev); + dev->tgt_mode = 0; + skb_queue_purge(&dev->resp_q); + dev_kfree_skb(resp); + + return rc; +} + +static void pn533_wq_tm_mi_recv(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, mi_tm_rx_work); + struct sk_buff *skb; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + skb = pn533_alloc_skb(dev, 0); + if (!skb) + return; + + rc = pn533_send_cmd_direct_async(dev, + PN533_CMD_TG_GET_DATA, + skb, + pn533_tm_get_data_complete, + NULL); + + if (rc < 0) + dev_kfree_skb(skb); + + return; +} + +static int pn533_tm_send_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp); +static void pn533_wq_tm_mi_send(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, mi_tm_tx_work); + struct sk_buff *skb; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + /* Grab the first skb in the queue */ + skb = skb_dequeue(&dev->fragment_skb); + if (skb == NULL) { /* No more data */ + /* Reset the queue for future use */ + skb_queue_head_init(&dev->fragment_skb); + goto error; + } + + /* last entry - remove MI bit */ + if (skb_queue_len(&dev->fragment_skb) == 0) { + rc = pn533_send_cmd_direct_async(dev, PN533_CMD_TG_SET_DATA, + skb, pn533_tm_send_complete, NULL); + } else + rc = pn533_send_cmd_direct_async(dev, + PN533_CMD_TG_SET_META_DATA, + skb, pn533_tm_send_complete, NULL); + + if (rc == 0) /* success */ + return; + + dev_err(&dev->interface->dev, + "Error %d when trying to perform set meta data_exchange", rc); + + dev_kfree_skb(skb); + +error: + pn533_send_ack(dev, GFP_KERNEL); + queue_work(dev->wq, &dev->cmd_work); +} + +static void pn533_wq_tg_get_data(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, tg_work); + struct sk_buff *skb; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + skb = pn533_alloc_skb(dev, 0); + if (!skb) + return; + + rc = pn533_send_data_async(dev, PN533_CMD_TG_GET_DATA, skb, + pn533_tm_get_data_complete, NULL); + + if (rc < 0) + dev_kfree_skb(skb); + + return; +} + +#define ATR_REQ_GB_OFFSET 17 +static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp) +{ + u8 mode, *cmd, comm_mode = NFC_COMM_PASSIVE, *gb; + size_t gb_len; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (resp->len < ATR_REQ_GB_OFFSET + 1) + return -EINVAL; + + mode = resp->data[0]; + cmd = &resp->data[1]; + + dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n", + mode, resp->len); + + if ((mode & PN533_INIT_TARGET_RESP_FRAME_MASK) == + PN533_INIT_TARGET_RESP_ACTIVE) + comm_mode = NFC_COMM_ACTIVE; + + if ((mode & PN533_INIT_TARGET_RESP_DEP) == 0) /* Only DEP supported */ + return -EOPNOTSUPP; + + gb = cmd + ATR_REQ_GB_OFFSET; + gb_len = resp->len - (ATR_REQ_GB_OFFSET + 1); + + rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, + comm_mode, gb, gb_len); + if (rc < 0) { + nfc_err(&dev->interface->dev, + "Error when signaling target activation\n"); + return rc; + } + + dev->tgt_mode = 1; + queue_work(dev->wq, &dev->tg_work); + + return 0; +} + +static void pn533_listen_mode_timer(unsigned long data) +{ + struct pn533 *dev = (struct pn533 *)data; + + dev_dbg(&dev->interface->dev, "Listen mode timeout\n"); + + dev->cancel_listen = 1; + + pn533_poll_next_mod(dev); + + queue_delayed_work(dev->wq, &dev->poll_work, + msecs_to_jiffies(PN533_POLL_INTERVAL)); +} + +static int pn533_rf_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp) +{ + int rc = 0; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + + nfc_err(&dev->interface->dev, "RF setting error %d\n", rc); + + return rc; + } + + queue_delayed_work(dev->wq, &dev->poll_work, + msecs_to_jiffies(PN533_POLL_INTERVAL)); + + dev_kfree_skb(resp); + return rc; +} + +static void pn533_wq_rf(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, rf_work); + struct sk_buff *skb; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + skb = pn533_alloc_skb(dev, 2); + if (!skb) + return; + + *skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD; + *skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD_AUTO_RFCA; + + rc = pn533_send_cmd_async(dev, PN533_CMD_RF_CONFIGURATION, skb, + pn533_rf_complete, NULL); + if (rc < 0) { + dev_kfree_skb(skb); + nfc_err(&dev->interface->dev, "RF setting error %d\n", rc); + } + + return; +} + +static int pn533_poll_dep_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp) +{ + struct pn533_cmd_jump_dep_response *rsp; + struct nfc_target nfc_target; + u8 target_gt_len; + int rc; + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rsp = (struct pn533_cmd_jump_dep_response *)resp->data; + + rc = rsp->status & PN533_CMD_RET_MASK; + if (rc != PN533_CMD_RET_SUCCESS) { + /* Not target found, turn radio off */ + queue_work(dev->wq, &dev->rf_work); + + dev_kfree_skb(resp); + return 0; + } + + dev_dbg(&dev->interface->dev, "Creating new target"); + + nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK; + nfc_target.nfcid1_len = 10; + memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len); + rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1); + if (rc) + goto error; + + dev->tgt_available_prots = 0; + dev->tgt_active_prot = NFC_PROTO_NFC_DEP; + + /* ATR_RES general bytes are located at offset 17 */ + target_gt_len = resp->len - 17; + rc = nfc_set_remote_general_bytes(dev->nfc_dev, + rsp->gt, target_gt_len); + if (!rc) { + rc = nfc_dep_link_is_up(dev->nfc_dev, + dev->nfc_dev->targets[0].idx, + 0, NFC_RF_INITIATOR); + + if (!rc) + pn533_poll_reset_mod_list(dev); + } +error: + dev_kfree_skb(resp); + return rc; +} + +#define PASSIVE_DATA_LEN 5 +static int pn533_poll_dep(struct nfc_dev *nfc_dev) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + struct sk_buff *skb; + int rc, skb_len; + u8 *next, nfcid3[NFC_NFCID3_MAXSIZE]; + u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3}; + + dev_dbg(&dev->interface->dev, "%s", __func__); + + if (!dev->gb) { + dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len); + + if (!dev->gb || !dev->gb_len) { + dev->poll_dep = 0; + queue_work(dev->wq, &dev->rf_work); + } + } + + skb_len = 3 + dev->gb_len; /* ActPass + BR + Next */ + skb_len += PASSIVE_DATA_LEN; + + /* NFCID3 */ + skb_len += NFC_NFCID3_MAXSIZE; + nfcid3[0] = 0x1; + nfcid3[1] = 0xfe; + get_random_bytes(nfcid3 + 2, 6); + + skb = pn533_alloc_skb(dev, skb_len); + if (!skb) + return -ENOMEM; + + *skb_put(skb, 1) = 0x01; /* Active */ + *skb_put(skb, 1) = 0x02; /* 424 kbps */ + + next = skb_put(skb, 1); /* Next */ + *next = 0; + + /* Copy passive data */ + memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN); + *next |= 1; + + /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */ + memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3, + NFC_NFCID3_MAXSIZE); + *next |= 2; + + memcpy(skb_put(skb, dev->gb_len), dev->gb, dev->gb_len); + *next |= 4; /* We have some Gi */ + + rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb, + pn533_poll_dep_complete, NULL); + + if (rc < 0) + dev_kfree_skb(skb); + + return rc; +} + +static int pn533_poll_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp) +{ + struct pn533_poll_modulations *cur_mod; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + + nfc_err(&dev->interface->dev, "%s Poll complete error %d\n", + __func__, rc); + + if (rc == -ENOENT) { + if (dev->poll_mod_count != 0) + return rc; + else + goto stop_poll; + } else if (rc < 0) { + nfc_err(&dev->interface->dev, + "Error %d when running poll\n", rc); + goto stop_poll; + } + } + + cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; + + if (cur_mod->len == 0) { /* Target mode */ + del_timer(&dev->listen_timer); + rc = pn533_init_target_complete(dev, resp); + goto done; + } + + /* Initiator mode */ + rc = pn533_start_poll_complete(dev, resp); + if (!rc) + goto done; + + if (!dev->poll_mod_count) { + dev_dbg(&dev->interface->dev, "Polling has been stopped\n"); + goto done; + } + + pn533_poll_next_mod(dev); + /* Not target found, turn radio off */ + queue_work(dev->wq, &dev->rf_work); + +done: + dev_kfree_skb(resp); + return rc; + +stop_poll: + nfc_err(&dev->interface->dev, "Polling operation has been stopped\n"); + + pn533_poll_reset_mod_list(dev); + dev->poll_protocols = 0; + return rc; +} + +static struct sk_buff *pn533_alloc_poll_in_frame(struct pn533 *dev, + struct pn533_poll_modulations *mod) +{ + struct sk_buff *skb; + + skb = pn533_alloc_skb(dev, mod->len); + if (!skb) + return NULL; + + memcpy(skb_put(skb, mod->len), &mod->data, mod->len); + + return skb; +} + +static int pn533_send_poll_frame(struct pn533 *dev) +{ + struct pn533_poll_modulations *mod; + struct sk_buff *skb; + int rc; + u8 cmd_code; + + mod = dev->poll_mod_active[dev->poll_mod_curr]; + + dev_dbg(&dev->interface->dev, "%s mod len %d\n", + __func__, mod->len); + + if (dev->poll_dep) { + dev->poll_dep = 0; + return pn533_poll_dep(dev->nfc_dev); + } + + if (mod->len == 0) { /* Listen mode */ + cmd_code = PN533_CMD_TG_INIT_AS_TARGET; + skb = pn533_alloc_poll_tg_frame(dev); + } else { /* Polling mode */ + cmd_code = PN533_CMD_IN_LIST_PASSIVE_TARGET; + skb = pn533_alloc_poll_in_frame(dev, mod); + } + + if (!skb) { + nfc_err(&dev->interface->dev, "Failed to allocate skb\n"); + return -ENOMEM; + } + + rc = pn533_send_cmd_async(dev, cmd_code, skb, pn533_poll_complete, + NULL); + if (rc < 0) { + dev_kfree_skb(skb); + nfc_err(&dev->interface->dev, "Polling loop error %d\n", rc); + } + + return rc; +} + +static void pn533_wq_poll(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, poll_work.work); + struct pn533_poll_modulations *cur_mod; + int rc; + + cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; + + dev_dbg(&dev->interface->dev, + "%s cancel_listen %d modulation len %d\n", + __func__, dev->cancel_listen, cur_mod->len); + + if (dev->cancel_listen == 1) { + dev->cancel_listen = 0; + pn533_abort_cmd(dev, GFP_ATOMIC); + } + + rc = pn533_send_poll_frame(dev); + if (rc) + return; + + if (cur_mod->len == 0 && dev->poll_mod_count > 1) + mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ); + + return; +} + +static int pn533_start_poll(struct nfc_dev *nfc_dev, + u32 im_protocols, u32 tm_protocols) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + struct pn533_poll_modulations *cur_mod; + u8 rand_mod; + int rc; + + dev_dbg(&dev->interface->dev, + "%s: im protocols 0x%x tm protocols 0x%x\n", + __func__, im_protocols, tm_protocols); + + if (dev->tgt_active_prot) { + nfc_err(&dev->interface->dev, + "Cannot poll with a target already activated\n"); + return -EBUSY; + } + + if (dev->tgt_mode) { + nfc_err(&dev->interface->dev, + "Cannot poll while already being activated\n"); + return -EBUSY; + } + + if (tm_protocols) { + dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len); + if (dev->gb == NULL) + tm_protocols = 0; + } + + pn533_poll_create_mod_list(dev, im_protocols, tm_protocols); + dev->poll_protocols = im_protocols; + dev->listen_protocols = tm_protocols; + + /* Do not always start polling from the same modulation */ + get_random_bytes(&rand_mod, sizeof(rand_mod)); + rand_mod %= dev->poll_mod_count; + dev->poll_mod_curr = rand_mod; + + cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; + + rc = pn533_send_poll_frame(dev); + + /* Start listen timer */ + if (!rc && cur_mod->len == 0 && dev->poll_mod_count > 1) + mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ); + + return rc; +} + +static void pn533_stop_poll(struct nfc_dev *nfc_dev) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + + del_timer(&dev->listen_timer); + + if (!dev->poll_mod_count) { + dev_dbg(&dev->interface->dev, + "Polling operation was not running\n"); + return; + } + + pn533_abort_cmd(dev, GFP_KERNEL); + flush_delayed_work(&dev->poll_work); + pn533_poll_reset_mod_list(dev); +} + +static int pn533_activate_target_nfcdep(struct pn533 *dev) +{ + struct pn533_cmd_activate_response *rsp; + u16 gt_len; + int rc; + struct sk_buff *skb; + struct sk_buff *resp; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + skb = pn533_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/ + if (!skb) + return -ENOMEM; + + *skb_put(skb, sizeof(u8)) = 1; /* TG */ + *skb_put(skb, sizeof(u8)) = 0; /* Next */ + + resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_ATR, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rsp = (struct pn533_cmd_activate_response *)resp->data; + rc = rsp->status & PN533_CMD_RET_MASK; + if (rc != PN533_CMD_RET_SUCCESS) { + nfc_err(&dev->interface->dev, + "Target activation failed (error 0x%x)\n", rc); + dev_kfree_skb(resp); + return -EIO; + } + + /* ATR_RES general bytes are located at offset 16 */ + gt_len = resp->len - 16; + rc = nfc_set_remote_general_bytes(dev->nfc_dev, rsp->gt, gt_len); + + dev_kfree_skb(resp); + return rc; +} + +static int pn533_activate_target(struct nfc_dev *nfc_dev, + struct nfc_target *target, u32 protocol) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + int rc; + + dev_dbg(&dev->interface->dev, "%s: protocol=%u\n", __func__, protocol); + + if (dev->poll_mod_count) { + nfc_err(&dev->interface->dev, + "Cannot activate while polling\n"); + return -EBUSY; + } + + if (dev->tgt_active_prot) { + nfc_err(&dev->interface->dev, + "There is already an active target\n"); + return -EBUSY; + } + + if (!dev->tgt_available_prots) { + nfc_err(&dev->interface->dev, + "There is no available target to activate\n"); + return -EINVAL; + } + + if (!(dev->tgt_available_prots & (1 << protocol))) { + nfc_err(&dev->interface->dev, + "Target doesn't support requested proto %u\n", + protocol); + return -EINVAL; + } + + if (protocol == NFC_PROTO_NFC_DEP) { + rc = pn533_activate_target_nfcdep(dev); + if (rc) { + nfc_err(&dev->interface->dev, + "Activating target with DEP failed %d\n", rc); + return rc; + } + } + + dev->tgt_active_prot = protocol; + dev->tgt_available_prots = 0; + + return 0; +} + +static void pn533_deactivate_target(struct nfc_dev *nfc_dev, + struct nfc_target *target) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (!dev->tgt_active_prot) { + nfc_err(&dev->interface->dev, "There is no active target\n"); + return; + } + + dev->tgt_active_prot = 0; + skb_queue_purge(&dev->resp_q); + + skb = pn533_alloc_skb(dev, sizeof(u8)); + if (!skb) + return; + + *skb_put(skb, 1) = 1; /* TG*/ + + resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_RELEASE, skb); + if (IS_ERR(resp)) + return; + + rc = resp->data[0] & PN533_CMD_RET_MASK; + if (rc != PN533_CMD_RET_SUCCESS) + nfc_err(&dev->interface->dev, + "Error 0x%x when releasing the target\n", rc); + + dev_kfree_skb(resp); + return; +} + + +static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp) +{ + struct pn533_cmd_jump_dep_response *rsp; + u8 target_gt_len; + int rc; + u8 active = *(u8 *)arg; + + kfree(arg); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + if (dev->tgt_available_prots && + !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) { + nfc_err(&dev->interface->dev, + "The target does not support DEP\n"); + rc = -EINVAL; + goto error; + } + + rsp = (struct pn533_cmd_jump_dep_response *)resp->data; + + rc = rsp->status & PN533_CMD_RET_MASK; + if (rc != PN533_CMD_RET_SUCCESS) { + nfc_err(&dev->interface->dev, + "Bringing DEP link up failed (error 0x%x)\n", rc); + goto error; + } + + if (!dev->tgt_available_prots) { + struct nfc_target nfc_target; + + dev_dbg(&dev->interface->dev, "Creating new target\n"); + + nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK; + nfc_target.nfcid1_len = 10; + memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len); + rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1); + if (rc) + goto error; + + dev->tgt_available_prots = 0; + } + + dev->tgt_active_prot = NFC_PROTO_NFC_DEP; + + /* ATR_RES general bytes are located at offset 17 */ + target_gt_len = resp->len - 17; + rc = nfc_set_remote_general_bytes(dev->nfc_dev, + rsp->gt, target_gt_len); + if (rc == 0) + rc = nfc_dep_link_is_up(dev->nfc_dev, + dev->nfc_dev->targets[0].idx, + !active, NFC_RF_INITIATOR); + +error: + dev_kfree_skb(resp); + return rc; +} + +static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf); +static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, + u8 comm_mode, u8 *gb, size_t gb_len) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + struct sk_buff *skb; + int rc, skb_len; + u8 *next, *arg, nfcid3[NFC_NFCID3_MAXSIZE]; + u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3}; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (dev->poll_mod_count) { + nfc_err(&dev->interface->dev, + "Cannot bring the DEP link up while polling\n"); + return -EBUSY; + } + + if (dev->tgt_active_prot) { + nfc_err(&dev->interface->dev, + "There is already an active target\n"); + return -EBUSY; + } + + skb_len = 3 + gb_len; /* ActPass + BR + Next */ + skb_len += PASSIVE_DATA_LEN; + + /* NFCID3 */ + skb_len += NFC_NFCID3_MAXSIZE; + if (target && !target->nfcid2_len) { + nfcid3[0] = 0x1; + nfcid3[1] = 0xfe; + get_random_bytes(nfcid3 + 2, 6); + } + + skb = pn533_alloc_skb(dev, skb_len); + if (!skb) + return -ENOMEM; + + *skb_put(skb, 1) = !comm_mode; /* ActPass */ + *skb_put(skb, 1) = 0x02; /* 424 kbps */ + + next = skb_put(skb, 1); /* Next */ + *next = 0; + + /* Copy passive data */ + memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN); + *next |= 1; + + /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */ + if (target && target->nfcid2_len) + memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2, + target->nfcid2_len); + else + memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3, + NFC_NFCID3_MAXSIZE); + *next |= 2; + + if (gb != NULL && gb_len > 0) { + memcpy(skb_put(skb, gb_len), gb, gb_len); + *next |= 4; /* We have some Gi */ + } else { + *next = 0; + } + + arg = kmalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + *arg = !comm_mode; + + pn533_rf_field(dev->nfc_dev, 0); + + rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb, + pn533_in_dep_link_up_complete, arg); + + if (rc < 0) { + dev_kfree_skb(skb); + kfree(arg); + } + + return rc; +} + +static int pn533_dep_link_down(struct nfc_dev *nfc_dev) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + pn533_poll_reset_mod_list(dev); + + if (dev->tgt_mode || dev->tgt_active_prot) + pn533_abort_cmd(dev, GFP_KERNEL); + + dev->tgt_active_prot = 0; + dev->tgt_mode = 0; + + skb_queue_purge(&dev->resp_q); + + return 0; +} + +struct pn533_data_exchange_arg { + data_exchange_cb_t cb; + void *cb_context; +}; + +static struct sk_buff *pn533_build_response(struct pn533 *dev) +{ + struct sk_buff *skb, *tmp, *t; + unsigned int skb_len = 0, tmp_len = 0; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (skb_queue_empty(&dev->resp_q)) + return NULL; + + if (skb_queue_len(&dev->resp_q) == 1) { + skb = skb_dequeue(&dev->resp_q); + goto out; + } + + skb_queue_walk_safe(&dev->resp_q, tmp, t) + skb_len += tmp->len; + + dev_dbg(&dev->interface->dev, "%s total length %d\n", + __func__, skb_len); + + skb = alloc_skb(skb_len, GFP_KERNEL); + if (skb == NULL) + goto out; + + skb_put(skb, skb_len); + + skb_queue_walk_safe(&dev->resp_q, tmp, t) { + memcpy(skb->data + tmp_len, tmp->data, tmp->len); + tmp_len += tmp->len; + } + +out: + skb_queue_purge(&dev->resp_q); + + return skb; +} + +static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, + struct sk_buff *resp) +{ + struct pn533_data_exchange_arg *arg = _arg; + struct sk_buff *skb; + int rc = 0; + u8 status, ret, mi; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + goto _error; + } + + status = resp->data[0]; + ret = status & PN533_CMD_RET_MASK; + mi = status & PN533_CMD_MI_MASK; + + skb_pull(resp, sizeof(status)); + + if (ret != PN533_CMD_RET_SUCCESS) { + nfc_err(&dev->interface->dev, + "Exchanging data failed (error 0x%x)\n", ret); + rc = -EIO; + goto error; + } + + skb_queue_tail(&dev->resp_q, resp); + + if (mi) { + dev->cmd_complete_mi_arg = arg; + queue_work(dev->wq, &dev->mi_rx_work); + return -EINPROGRESS; + } + + /* Prepare for the next round */ + if (skb_queue_len(&dev->fragment_skb) > 0) { + dev->cmd_complete_dep_arg = arg; + queue_work(dev->wq, &dev->mi_tx_work); + + return -EINPROGRESS; + } + + skb = pn533_build_response(dev); + if (!skb) { + rc = -ENOMEM; + goto error; + } + + arg->cb(arg->cb_context, skb, 0); + kfree(arg); + return 0; + +error: + dev_kfree_skb(resp); +_error: + skb_queue_purge(&dev->resp_q); + arg->cb(arg->cb_context, NULL, rc); + kfree(arg); + return rc; +} + +/* Split the Tx skb into small chunks */ +static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb) +{ + struct sk_buff *frag; + int frag_size; + + do { + /* Remaining size */ + if (skb->len > PN533_CMD_DATAFRAME_MAXLEN) + frag_size = PN533_CMD_DATAFRAME_MAXLEN; + else + frag_size = skb->len; + + /* Allocate and reserve */ + frag = pn533_alloc_skb(dev, frag_size); + if (!frag) { + skb_queue_purge(&dev->fragment_skb); + break; + } + + if (!dev->tgt_mode) { + /* Reserve the TG/MI byte */ + skb_reserve(frag, 1); + + /* MI + TG */ + if (frag_size == PN533_CMD_DATAFRAME_MAXLEN) + *skb_push(frag, sizeof(u8)) = + (PN533_CMD_MI_MASK | 1); + else + *skb_push(frag, sizeof(u8)) = 1; /* TG */ + } + + memcpy(skb_put(frag, frag_size), skb->data, frag_size); + + /* Reduce the size of incoming buffer */ + skb_pull(skb, frag_size); + + /* Add this to skb_queue */ + skb_queue_tail(&dev->fragment_skb, frag); + + } while (skb->len > 0); + + dev_kfree_skb(skb); + + return skb_queue_len(&dev->fragment_skb); +} + +static int pn533_transceive(struct nfc_dev *nfc_dev, + struct nfc_target *target, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + struct pn533_data_exchange_arg *arg = NULL; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (!dev->tgt_active_prot) { + nfc_err(&dev->interface->dev, + "Can't exchange data if there is no active target\n"); + rc = -EINVAL; + goto error; + } + + arg = kmalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) { + rc = -ENOMEM; + goto error; + } + + arg->cb = cb; + arg->cb_context = cb_context; + + switch (dev->device_type) { + case PN533_DEVICE_PASORI: + if (dev->tgt_active_prot == NFC_PROTO_FELICA) { + rc = pn533_send_data_async(dev, PN533_CMD_IN_COMM_THRU, + skb, + pn533_data_exchange_complete, + arg); + + break; + } + default: + /* jumbo frame ? */ + if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) { + rc = pn533_fill_fragment_skbs(dev, skb); + if (rc <= 0) + goto error; + + skb = skb_dequeue(&dev->fragment_skb); + if (!skb) { + rc = -EIO; + goto error; + } + } else { + *skb_push(skb, sizeof(u8)) = 1; /* TG */ + } + + rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE, + skb, pn533_data_exchange_complete, + arg); + + break; + } + + if (rc < 0) /* rc from send_async */ + goto error; + + return 0; + +error: + kfree(arg); + dev_kfree_skb(skb); + return rc; +} + +static int pn533_tm_send_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp) +{ + u8 status; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + status = resp->data[0]; + + /* Prepare for the next round */ + if (skb_queue_len(&dev->fragment_skb) > 0) { + queue_work(dev->wq, &dev->mi_tm_tx_work); + return -EINPROGRESS; + } + dev_kfree_skb(resp); + + if (status != 0) { + nfc_tm_deactivated(dev->nfc_dev); + + dev->tgt_mode = 0; + + return 0; + } + + queue_work(dev->wq, &dev->tg_work); + + return 0; +} + +static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + /* let's split in multiple chunks if size's too big */ + if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) { + rc = pn533_fill_fragment_skbs(dev, skb); + if (rc <= 0) + goto error; + + /* get the first skb */ + skb = skb_dequeue(&dev->fragment_skb); + if (!skb) { + rc = -EIO; + goto error; + } + + rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_META_DATA, skb, + pn533_tm_send_complete, NULL); + } else { + /* Send th skb */ + rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb, + pn533_tm_send_complete, NULL); + } + +error: + if (rc < 0) { + dev_kfree_skb(skb); + skb_queue_purge(&dev->fragment_skb); + } + + return rc; +} + +static void pn533_wq_mi_recv(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, mi_rx_work); + struct sk_buff *skb; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + skb = pn533_alloc_skb(dev, PN533_CMD_DATAEXCH_HEAD_LEN); + if (!skb) + goto error; + + switch (dev->device_type) { + case PN533_DEVICE_PASORI: + if (dev->tgt_active_prot == NFC_PROTO_FELICA) { + rc = pn533_send_cmd_direct_async(dev, + PN533_CMD_IN_COMM_THRU, + skb, + pn533_data_exchange_complete, + dev->cmd_complete_mi_arg); + + break; + } + default: + *skb_put(skb, sizeof(u8)) = 1; /*TG*/ + + rc = pn533_send_cmd_direct_async(dev, + PN533_CMD_IN_DATA_EXCHANGE, + skb, + pn533_data_exchange_complete, + dev->cmd_complete_mi_arg); + + break; + } + + if (rc == 0) /* success */ + return; + + nfc_err(&dev->interface->dev, + "Error %d when trying to perform data_exchange\n", rc); + + dev_kfree_skb(skb); + kfree(dev->cmd_complete_mi_arg); + +error: + pn533_send_ack(dev, GFP_KERNEL); + queue_work(dev->wq, &dev->cmd_work); +} + +static void pn533_wq_mi_send(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, mi_tx_work); + struct sk_buff *skb; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + /* Grab the first skb in the queue */ + skb = skb_dequeue(&dev->fragment_skb); + + if (skb == NULL) { /* No more data */ + /* Reset the queue for future use */ + skb_queue_head_init(&dev->fragment_skb); + goto error; + } + + switch (dev->device_type) { + case PN533_DEVICE_PASORI: + if (dev->tgt_active_prot != NFC_PROTO_FELICA) { + rc = -EIO; + break; + } + + rc = pn533_send_cmd_direct_async(dev, PN533_CMD_IN_COMM_THRU, + skb, + pn533_data_exchange_complete, + dev->cmd_complete_dep_arg); + + break; + + default: + /* Still some fragments? */ + rc = pn533_send_cmd_direct_async(dev,PN533_CMD_IN_DATA_EXCHANGE, + skb, + pn533_data_exchange_complete, + dev->cmd_complete_dep_arg); + + break; + } + + if (rc == 0) /* success */ + return; + + nfc_err(&dev->interface->dev, + "Error %d when trying to perform data_exchange\n", rc); + + dev_kfree_skb(skb); + kfree(dev->cmd_complete_dep_arg); + +error: + pn533_send_ack(dev, GFP_KERNEL); + queue_work(dev->wq, &dev->cmd_work); +} + +static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, + u8 cfgdata_len) +{ + struct sk_buff *skb; + struct sk_buff *resp; + int skb_len; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */ + + skb = pn533_alloc_skb(dev, skb_len); + if (!skb) + return -ENOMEM; + + *skb_put(skb, sizeof(cfgitem)) = cfgitem; + memcpy(skb_put(skb, cfgdata_len), cfgdata, cfgdata_len); + + resp = pn533_send_cmd_sync(dev, PN533_CMD_RF_CONFIGURATION, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + dev_kfree_skb(resp); + return 0; +} + +static int pn533_get_firmware_version(struct pn533 *dev, + struct pn533_fw_version *fv) +{ + struct sk_buff *skb; + struct sk_buff *resp; + + skb = pn533_alloc_skb(dev, 0); + if (!skb) + return -ENOMEM; + + resp = pn533_send_cmd_sync(dev, PN533_CMD_GET_FIRMWARE_VERSION, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + fv->ic = resp->data[0]; + fv->ver = resp->data[1]; + fv->rev = resp->data[2]; + fv->support = resp->data[3]; + + dev_kfree_skb(resp); + return 0; +} + +static int pn533_pasori_fw_reset(struct pn533 *dev) +{ + struct sk_buff *skb; + struct sk_buff *resp; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + skb = pn533_alloc_skb(dev, sizeof(u8)); + if (!skb) + return -ENOMEM; + + *skb_put(skb, sizeof(u8)) = 0x1; + + resp = pn533_send_cmd_sync(dev, 0x18, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + dev_kfree_skb(resp); + + return 0; +} + +struct pn533_acr122_poweron_rdr_arg { + int rc; + struct completion done; +}; + +static void pn533_acr122_poweron_rdr_resp(struct urb *urb) +{ + struct pn533_acr122_poweron_rdr_arg *arg = urb->context; + + dev_dbg(&urb->dev->dev, "%s\n", __func__); + + print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1, + urb->transfer_buffer, urb->transfer_buffer_length, + false); + + arg->rc = urb->status; + complete(&arg->done); +} + +static int pn533_acr122_poweron_rdr(struct pn533 *dev) +{ + /* Power on th reader (CCID cmd) */ + u8 cmd[10] = {PN533_ACR122_PC_TO_RDR_ICCPOWERON, + 0, 0, 0, 0, 0, 0, 3, 0, 0}; + u8 buf[255]; + int rc; + void *cntx; + struct pn533_acr122_poweron_rdr_arg arg; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + init_completion(&arg.done); + cntx = dev->in_urb->context; /* backup context */ + + dev->in_urb->transfer_buffer = buf; + dev->in_urb->transfer_buffer_length = 255; + dev->in_urb->complete = pn533_acr122_poweron_rdr_resp; + dev->in_urb->context = &arg; + + dev->out_urb->transfer_buffer = cmd; + dev->out_urb->transfer_buffer_length = sizeof(cmd); + + print_hex_dump_debug("ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1, + cmd, sizeof(cmd), false); + + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); + if (rc) { + nfc_err(&dev->interface->dev, + "Reader power on cmd error %d\n", rc); + return rc; + } + + rc = usb_submit_urb(dev->in_urb, GFP_KERNEL); + if (rc) { + nfc_err(&dev->interface->dev, + "Can't submit reader poweron cmd response %d\n", rc); + return rc; + } + + wait_for_completion(&arg.done); + dev->in_urb->context = cntx; /* restore context */ + + return arg.rc; +} + +static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + u8 rf_field = !!rf; + int rc; + + rf_field |= PN533_CFGITEM_RF_FIELD_AUTO_RFCA; + + rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD, + (u8 *)&rf_field, 1); + if (rc) { + nfc_err(&dev->interface->dev, "Error on setting RF field\n"); + return rc; + } + + return rc; +} + +static int pn533_dev_up(struct nfc_dev *nfc_dev) +{ + return pn533_rf_field(nfc_dev, 1); +} + +static int pn533_dev_down(struct nfc_dev *nfc_dev) +{ + return pn533_rf_field(nfc_dev, 0); +} + +static struct nfc_ops pn533_nfc_ops = { + .dev_up = pn533_dev_up, + .dev_down = pn533_dev_down, + .dep_link_up = pn533_dep_link_up, + .dep_link_down = pn533_dep_link_down, + .start_poll = pn533_start_poll, + .stop_poll = pn533_stop_poll, + .activate_target = pn533_activate_target, + .deactivate_target = pn533_deactivate_target, + .im_transceive = pn533_transceive, + .tm_send = pn533_tm_send, +}; + +static int pn533_setup(struct pn533 *dev) +{ + struct pn533_config_max_retries max_retries; + struct pn533_config_timing timing; + u8 pasori_cfg[3] = {0x08, 0x01, 0x08}; + int rc; + + switch (dev->device_type) { + case PN533_DEVICE_STD: + case PN533_DEVICE_PASORI: + case PN533_DEVICE_ACR122U: + max_retries.mx_rty_atr = 0x2; + max_retries.mx_rty_psl = 0x1; + max_retries.mx_rty_passive_act = + PN533_CONFIG_MAX_RETRIES_NO_RETRY; + + timing.rfu = PN533_CONFIG_TIMING_102; + timing.atr_res_timeout = PN533_CONFIG_TIMING_102; + timing.dep_timeout = PN533_CONFIG_TIMING_204; + + break; + + default: + nfc_err(&dev->interface->dev, "Unknown device type %d\n", + dev->device_type); + return -EINVAL; + } + + rc = pn533_set_configuration(dev, PN533_CFGITEM_MAX_RETRIES, + (u8 *)&max_retries, sizeof(max_retries)); + if (rc) { + nfc_err(&dev->interface->dev, + "Error on setting MAX_RETRIES config\n"); + return rc; + } + + + rc = pn533_set_configuration(dev, PN533_CFGITEM_TIMING, + (u8 *)&timing, sizeof(timing)); + if (rc) { + nfc_err(&dev->interface->dev, "Error on setting RF timings\n"); + return rc; + } + + switch (dev->device_type) { + case PN533_DEVICE_STD: + break; + + case PN533_DEVICE_PASORI: + pn533_pasori_fw_reset(dev); + + rc = pn533_set_configuration(dev, PN533_CFGITEM_PASORI, + pasori_cfg, 3); + if (rc) { + nfc_err(&dev->interface->dev, + "Error while settings PASORI config\n"); + return rc; + } + + pn533_pasori_fw_reset(dev); + + break; + } + + return 0; +} + +static int pn533_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct pn533_fw_version fw_ver; + struct pn533 *dev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int in_endpoint = 0; + int out_endpoint = 0; + int rc = -ENOMEM; + int i; + u32 protocols; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + mutex_init(&dev->cmd_lock); + + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint)) + in_endpoint = endpoint->bEndpointAddress; + + if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) + out_endpoint = endpoint->bEndpointAddress; + } + + if (!in_endpoint || !out_endpoint) { + nfc_err(&interface->dev, + "Could not find bulk-in or bulk-out endpoint\n"); + rc = -ENODEV; + goto error; + } + + dev->in_urb = usb_alloc_urb(0, GFP_KERNEL); + dev->out_urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!dev->in_urb || !dev->out_urb) + goto error; + + usb_fill_bulk_urb(dev->in_urb, dev->udev, + usb_rcvbulkpipe(dev->udev, in_endpoint), + NULL, 0, NULL, dev); + usb_fill_bulk_urb(dev->out_urb, dev->udev, + usb_sndbulkpipe(dev->udev, out_endpoint), + NULL, 0, pn533_send_complete, dev); + + INIT_WORK(&dev->cmd_work, pn533_wq_cmd); + INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete); + INIT_WORK(&dev->mi_rx_work, pn533_wq_mi_recv); + INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send); + INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data); + INIT_WORK(&dev->mi_tm_rx_work, pn533_wq_tm_mi_recv); + INIT_WORK(&dev->mi_tm_tx_work, pn533_wq_tm_mi_send); + INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll); + INIT_WORK(&dev->rf_work, pn533_wq_rf); + dev->wq = alloc_ordered_workqueue("pn533", 0); + if (dev->wq == NULL) + goto error; + + init_timer(&dev->listen_timer); + dev->listen_timer.data = (unsigned long) dev; + dev->listen_timer.function = pn533_listen_mode_timer; + + skb_queue_head_init(&dev->resp_q); + skb_queue_head_init(&dev->fragment_skb); + + INIT_LIST_HEAD(&dev->cmd_queue); + + usb_set_intfdata(interface, dev); + + dev->ops = &pn533_std_frame_ops; + + dev->protocol_type = PN533_PROTO_REQ_ACK_RESP; + dev->device_type = id->driver_info; + switch (dev->device_type) { + case PN533_DEVICE_STD: + protocols = PN533_ALL_PROTOCOLS; + break; + + case PN533_DEVICE_PASORI: + protocols = PN533_NO_TYPE_B_PROTOCOLS; + break; + + case PN533_DEVICE_ACR122U: + protocols = PN533_NO_TYPE_B_PROTOCOLS; + dev->ops = &pn533_acr122_frame_ops; + dev->protocol_type = PN533_PROTO_REQ_RESP, + + rc = pn533_acr122_poweron_rdr(dev); + if (rc < 0) { + nfc_err(&dev->interface->dev, + "Couldn't poweron the reader (error %d)\n", rc); + goto destroy_wq; + } + break; + + default: + nfc_err(&dev->interface->dev, "Unknown device type %d\n", + dev->device_type); + rc = -EINVAL; + goto destroy_wq; + } + + memset(&fw_ver, 0, sizeof(fw_ver)); + rc = pn533_get_firmware_version(dev, &fw_ver); + if (rc < 0) + goto destroy_wq; + + nfc_info(&dev->interface->dev, + "NXP PN5%02X firmware ver %d.%d now attached\n", + fw_ver.ic, fw_ver.ver, fw_ver.rev); + + + dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols, + dev->ops->tx_header_len + + PN533_CMD_DATAEXCH_HEAD_LEN, + dev->ops->tx_tail_len); + if (!dev->nfc_dev) { + rc = -ENOMEM; + goto destroy_wq; + } + + nfc_set_parent_dev(dev->nfc_dev, &interface->dev); + nfc_set_drvdata(dev->nfc_dev, dev); + + rc = nfc_register_device(dev->nfc_dev); + if (rc) + goto free_nfc_dev; + + rc = pn533_setup(dev); + if (rc) + goto unregister_nfc_dev; + + return 0; + +unregister_nfc_dev: + nfc_unregister_device(dev->nfc_dev); + +free_nfc_dev: + nfc_free_device(dev->nfc_dev); + +destroy_wq: + destroy_workqueue(dev->wq); +error: + usb_free_urb(dev->in_urb); + usb_free_urb(dev->out_urb); + usb_put_dev(dev->udev); + kfree(dev); + return rc; +} + +static void pn533_disconnect(struct usb_interface *interface) +{ + struct pn533 *dev; + struct pn533_cmd *cmd, *n; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + nfc_unregister_device(dev->nfc_dev); + nfc_free_device(dev->nfc_dev); + + usb_kill_urb(dev->in_urb); + usb_kill_urb(dev->out_urb); + + flush_delayed_work(&dev->poll_work); + destroy_workqueue(dev->wq); + + skb_queue_purge(&dev->resp_q); + + del_timer(&dev->listen_timer); + + list_for_each_entry_safe(cmd, n, &dev->cmd_queue, queue) { + list_del(&cmd->queue); + kfree(cmd); + } + + usb_free_urb(dev->in_urb); + usb_free_urb(dev->out_urb); + kfree(dev); + + nfc_info(&interface->dev, "NXP PN533 NFC device disconnected\n"); +} + +static struct usb_driver pn533_driver = { + .name = "pn533", + .probe = pn533_probe, + .disconnect = pn533_disconnect, + .id_table = pn533_table, +}; + +module_usb_driver(pn533_driver); + +MODULE_AUTHOR("Lauro Ramos Venancio <lauro.venancio@openbossa.org>"); +MODULE_AUTHOR("Aloisio Almeida Jr <aloisio.almeida@openbossa.org>"); +MODULE_AUTHOR("Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>"); +MODULE_DESCRIPTION("PN533 usb driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/pn544/Kconfig b/drivers/nfc/pn544/Kconfig new file mode 100644 index 000000000..ccf06f5f6 --- /dev/null +++ b/drivers/nfc/pn544/Kconfig @@ -0,0 +1,34 @@ +config NFC_PN544 + tristate "NXP PN544 NFC driver" + depends on NFC_HCI + select CRC_CCITT + default n + ---help--- + NXP PN544 core driver. + This is a driver based on the HCI NFC kernel layers and + will thus not work with NXP libnfc library. + + To compile this driver as a module, choose m here. The module will + be called pn544. + Say N if unsure. + +config NFC_PN544_I2C + tristate "NFC PN544 i2c support" + depends on NFC_PN544 && I2C && NFC_SHDLC + ---help--- + This module adds support for the NXP pn544 i2c interface. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called pn544_i2c. + Say N if unsure. + +config NFC_PN544_MEI + tristate "NFC PN544 MEI support" + depends on NFC_PN544 && NFC_MEI_PHY + ---help--- + This module adds support for the mei interface of adapters using + NXP pn544 chipsets. Select this if your pn544 chipset + is handled by Intel's Management Engine Interface on your platform. + + If you choose to build a module, it'll be called pn544_mei. + Say N if unsure. diff --git a/drivers/nfc/pn544/Makefile b/drivers/nfc/pn544/Makefile new file mode 100644 index 000000000..29fb5a174 --- /dev/null +++ b/drivers/nfc/pn544/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for PN544 HCI based NFC driver +# + +pn544_i2c-objs = i2c.o +pn544_mei-objs = mei.o + +obj-$(CONFIG_NFC_PN544) += pn544.o +obj-$(CONFIG_NFC_PN544_I2C) += pn544_i2c.o +obj-$(CONFIG_NFC_PN544_MEI) += pn544_mei.o diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c new file mode 100644 index 000000000..6acc85a0e --- /dev/null +++ b/drivers/nfc/pn544/i2c.c @@ -0,0 +1,1198 @@ +/* + * I2C Link Layer for PN544 HCI based Driver + * + * Copyright (C) 2012 Intel Corporation. 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.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/acpi.h> +#include <linux/miscdevice.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/nfc.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/platform_data/pn544.h> +#include <asm/unaligned.h> + +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> +#include <net/nfc/nfc.h> + +#include "pn544.h" + +#define PN544_I2C_FRAME_HEADROOM 1 +#define PN544_I2C_FRAME_TAILROOM 2 + +/* GPIO names */ +#define PN544_GPIO_NAME_IRQ "pn544_irq" +#define PN544_GPIO_NAME_FW "pn544_fw" +#define PN544_GPIO_NAME_EN "pn544_en" + +/* framing in HCI mode */ +#define PN544_HCI_I2C_LLC_LEN 1 +#define PN544_HCI_I2C_LLC_CRC 2 +#define PN544_HCI_I2C_LLC_LEN_CRC (PN544_HCI_I2C_LLC_LEN + \ + PN544_HCI_I2C_LLC_CRC) +#define PN544_HCI_I2C_LLC_MIN_SIZE (1 + PN544_HCI_I2C_LLC_LEN_CRC) +#define PN544_HCI_I2C_LLC_MAX_PAYLOAD 29 +#define PN544_HCI_I2C_LLC_MAX_SIZE (PN544_HCI_I2C_LLC_LEN_CRC + 1 + \ + PN544_HCI_I2C_LLC_MAX_PAYLOAD) + +static struct i2c_device_id pn544_hci_i2c_id_table[] = { + {"pn544", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table); + +static const struct acpi_device_id pn544_hci_i2c_acpi_match[] = { + {"NXP5440", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, pn544_hci_i2c_acpi_match); + +#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c" + +/* + * Exposed through the 4 most significant bytes + * from the HCI SW_VERSION first byte, a.k.a. + * SW RomLib. + */ +#define PN544_HW_VARIANT_C2 0xa +#define PN544_HW_VARIANT_C3 0xb + +#define PN544_FW_CMD_RESET 0x01 +#define PN544_FW_CMD_WRITE 0x08 +#define PN544_FW_CMD_CHECK 0x06 +#define PN544_FW_CMD_SECURE_WRITE 0x0C +#define PN544_FW_CMD_SECURE_CHUNK_WRITE 0x0D + +struct pn544_i2c_fw_frame_write { + u8 cmd; + u16 be_length; + u8 be_dest_addr[3]; + u16 be_datalen; + u8 data[]; +} __packed; + +struct pn544_i2c_fw_frame_check { + u8 cmd; + u16 be_length; + u8 be_start_addr[3]; + u16 be_datalen; + u16 be_crc; +} __packed; + +struct pn544_i2c_fw_frame_response { + u8 status; + u16 be_length; +} __packed; + +struct pn544_i2c_fw_blob { + u32 be_size; + u32 be_destaddr; + u8 data[]; +}; + +struct pn544_i2c_fw_secure_frame { + u8 cmd; + u16 be_datalen; + u8 data[]; +} __packed; + +struct pn544_i2c_fw_secure_blob { + u64 header; + u8 data[]; +}; + +#define PN544_FW_CMD_RESULT_TIMEOUT 0x01 +#define PN544_FW_CMD_RESULT_BAD_CRC 0x02 +#define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08 +#define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B +#define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11 +#define PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND 0x13 +#define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18 +#define PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR 0x19 +#define PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR 0x1D +#define PN544_FW_CMD_RESULT_MEMORY_ERROR 0x20 +#define PN544_FW_CMD_RESULT_CHUNK_OK 0x21 +#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74 +#define PN544_FW_CMD_RESULT_COMMAND_REJECTED 0xE0 +#define PN544_FW_CMD_RESULT_CHUNK_ERROR 0xE6 + +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) + +#define PN544_FW_WRITE_BUFFER_MAX_LEN 0x9f7 +#define PN544_FW_I2C_MAX_PAYLOAD PN544_HCI_I2C_LLC_MAX_SIZE +#define PN544_FW_I2C_WRITE_FRAME_HEADER_LEN 8 +#define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\ + PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\ + PN544_FW_WRITE_BUFFER_MAX_LEN) +#define PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN 3 +#define PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN (PN544_FW_I2C_MAX_PAYLOAD -\ + PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN) +#define PN544_FW_SECURE_FRAME_HEADER_LEN 3 +#define PN544_FW_SECURE_BLOB_HEADER_LEN 8 + +#define FW_WORK_STATE_IDLE 1 +#define FW_WORK_STATE_START 2 +#define FW_WORK_STATE_WAIT_WRITE_ANSWER 3 +#define FW_WORK_STATE_WAIT_CHECK_ANSWER 4 +#define FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER 5 + +struct pn544_i2c_phy { + struct i2c_client *i2c_dev; + struct nfc_hci_dev *hdev; + + unsigned int gpio_en; + unsigned int gpio_irq; + unsigned int gpio_fw; + unsigned int en_polarity; + + u8 hw_variant; + + struct work_struct fw_work; + int fw_work_state; + char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; + const struct firmware *fw; + u32 fw_blob_dest_addr; + size_t fw_blob_size; + const u8 *fw_blob_data; + size_t fw_written; + size_t fw_size; + + int fw_cmd_result; + + int powered; + int run_mode; + + int hard_fault; /* + * < 0 if hardware error occured (e.g. i2c err) + * and prevents normal operation. + */ +}; + +#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) + +static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) +{ + int polarity, retry, ret; + char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 }; + int count = sizeof(rset_cmd); + + nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n"); + + /* Disable fw download */ + gpio_set_value_cansleep(phy->gpio_fw, 0); + + for (polarity = 0; polarity < 2; polarity++) { + phy->en_polarity = polarity; + retry = 3; + while (retry--) { + /* power off */ + gpio_set_value_cansleep(phy->gpio_en, + !phy->en_polarity); + usleep_range(10000, 15000); + + /* power on */ + gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); + usleep_range(10000, 15000); + + /* send reset */ + dev_dbg(&phy->i2c_dev->dev, "Sending reset cmd\n"); + ret = i2c_master_send(phy->i2c_dev, rset_cmd, count); + if (ret == count) { + nfc_info(&phy->i2c_dev->dev, + "nfc_en polarity : active %s\n", + (polarity == 0 ? "low" : "high")); + goto out; + } + } + } + + nfc_err(&phy->i2c_dev->dev, + "Could not detect nfc_en polarity, fallback to active high\n"); + +out: + gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); +} + +static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode) +{ + gpio_set_value_cansleep(phy->gpio_fw, + run_mode == PN544_FW_MODE ? 1 : 0); + gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); + usleep_range(10000, 15000); + + phy->run_mode = run_mode; +} + +static int pn544_hci_i2c_enable(void *phy_id) +{ + struct pn544_i2c_phy *phy = phy_id; + + pr_info("%s\n", __func__); + + pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE); + + phy->powered = 1; + + return 0; +} + +static void pn544_hci_i2c_disable(void *phy_id) +{ + struct pn544_i2c_phy *phy = phy_id; + + gpio_set_value_cansleep(phy->gpio_fw, 0); + gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); + usleep_range(10000, 15000); + + gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); + usleep_range(10000, 15000); + + gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); + usleep_range(10000, 15000); + + phy->powered = 0; +} + +static void pn544_hci_i2c_add_len_crc(struct sk_buff *skb) +{ + u16 crc; + int len; + + len = skb->len + 2; + *skb_push(skb, 1) = len; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + *skb_put(skb, 1) = crc & 0xff; + *skb_put(skb, 1) = crc >> 8; +} + +static void pn544_hci_i2c_remove_len_crc(struct sk_buff *skb) +{ + skb_pull(skb, PN544_I2C_FRAME_HEADROOM); + skb_trim(skb, PN544_I2C_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 pn544_hci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r; + struct pn544_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + + if (phy->hard_fault != 0) + return phy->hard_fault; + + usleep_range(3000, 6000); + + pn544_hci_i2c_add_len_crc(skb); + + I2C_DUMP_SKB("i2c frame written", skb); + + r = i2c_master_send(client, skb->data, skb->len); + + if (r == -EREMOTEIO) { /* Retry, chip was in standby */ + usleep_range(6000, 10000); + r = i2c_master_send(client, skb->data, skb->len); + } + + if (r >= 0) { + if (r != skb->len) + r = -EREMOTEIO; + else + r = 0; + } + + pn544_hci_i2c_remove_len_crc(skb); + + return r; +} + +static int check_crc(u8 *buf, int buflen) +{ + int len; + u16 crc; + + len = buf[0] + 1; + crc = crc_ccitt(0xffff, buf, len - 2); + crc = ~crc; + + if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) { + pr_err("CRC error 0x%x != 0x%x 0x%x\n", + crc, buf[len - 1], buf[len - 2]); + pr_info("%s: BAD CRC\n", __func__); + print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, + 16, 2, buf, buflen, false); + return -EPERM; + } + 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: + * -EREMOTEIO : i2c read error (fatal) + * -EBADMSG : frame was incorrect and discarded + * -ENOMEM : cannot allocate skb, frame dropped + */ +static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb) +{ + int r; + u8 len; + u8 tmp[PN544_HCI_I2C_LLC_MAX_SIZE - 1]; + struct i2c_client *client = phy->i2c_dev; + + r = i2c_master_recv(client, &len, 1); + if (r != 1) { + nfc_err(&client->dev, "cannot read len byte\n"); + return -EREMOTEIO; + } + + if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) || + (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) { + nfc_err(&client->dev, "invalid len byte\n"); + r = -EBADMSG; + goto flush; + } + + *skb = alloc_skb(1 + len, GFP_KERNEL); + if (*skb == NULL) { + r = -ENOMEM; + goto flush; + } + + *skb_put(*skb, 1) = len; + + r = i2c_master_recv(client, skb_put(*skb, len), len); + if (r != len) { + kfree_skb(*skb); + return -EREMOTEIO; + } + + I2C_DUMP_SKB("i2c frame read", *skb); + + r = check_crc((*skb)->data, (*skb)->len); + if (r != 0) { + kfree_skb(*skb); + r = -EBADMSG; + goto flush; + } + + skb_pull(*skb, 1); + skb_trim(*skb, (*skb)->len - 2); + + usleep_range(3000, 6000); + + return 0; + +flush: + if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0) + r = -EREMOTEIO; + + usleep_range(3000, 6000); + + return r; +} + +static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy) +{ + int r; + struct pn544_i2c_fw_frame_response response; + struct i2c_client *client = phy->i2c_dev; + + r = i2c_master_recv(client, (char *) &response, sizeof(response)); + if (r != sizeof(response)) { + nfc_err(&client->dev, "cannot read fw status\n"); + return -EIO; + } + + usleep_range(3000, 6000); + + switch (response.status) { + case 0: + return 0; + case PN544_FW_CMD_RESULT_CHUNK_OK: + return response.status; + case PN544_FW_CMD_RESULT_TIMEOUT: + return -ETIMEDOUT; + case PN544_FW_CMD_RESULT_BAD_CRC: + return -ENODATA; + case PN544_FW_CMD_RESULT_ACCESS_DENIED: + return -EACCES; + case PN544_FW_CMD_RESULT_PROTOCOL_ERROR: + return -EPROTO; + case PN544_FW_CMD_RESULT_INVALID_PARAMETER: + return -EINVAL; + case PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND: + return -ENOTSUPP; + case PN544_FW_CMD_RESULT_INVALID_LENGTH: + return -EBADMSG; + case PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR: + return -ENOKEY; + case PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR: + return -EINVAL; + case PN544_FW_CMD_RESULT_MEMORY_ERROR: + return -ENOMEM; + case PN544_FW_CMD_RESULT_COMMAND_REJECTED: + return -EACCES; + case PN544_FW_CMD_RESULT_WRITE_FAILED: + case PN544_FW_CMD_RESULT_CHUNK_ERROR: + return -EIO; + default: + return -EIO; + } +} + +/* + * Reads an shdlc frame from the chip. This is not as straightforward as it + * seems. There are cases where we could loose the frame start synchronization. + * The frame format is len-data-crc, and corruption can occur anywhere while + * transiting on i2c bus, such that we could read an invalid len. + * In order to recover synchronization with the next frame, we must be sure + * to read the real amount of data without using the len byte. We do this by + * assuming the following: + * - the chip will always present only one single complete frame on the bus + * before triggering the interrupt + * - the chip will not present a new frame until we have completely read + * the previous one (or until we have handled the interrupt). + * The tricky case is when we read a corrupted len that is less than the real + * len. We must detect this here in order to determine that we need to flush + * the bus. This is the reason why we check the crc here. + */ +static irqreturn_t pn544_hci_i2c_irq_thread_fn(int irq, void *phy_id) +{ + struct pn544_i2c_phy *phy = phy_id; + struct i2c_client *client; + struct sk_buff *skb = NULL; + 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; + + if (phy->run_mode == PN544_FW_MODE) { + phy->fw_cmd_result = pn544_hci_i2c_fw_read_status(phy); + schedule_work(&phy->fw_work); + } else { + r = pn544_hci_i2c_read(phy, &skb); + if (r == -EREMOTEIO) { + phy->hard_fault = r; + + nfc_hci_recv_frame(phy->hdev, NULL); + + return IRQ_HANDLED; + } else if ((r == -ENOMEM) || (r == -EBADMSG)) { + return IRQ_HANDLED; + } + + nfc_hci_recv_frame(phy->hdev, skb); + } + return IRQ_HANDLED; +} + +static struct nfc_phy_ops i2c_phy_ops = { + .write = pn544_hci_i2c_write, + .enable = pn544_hci_i2c_enable, + .disable = pn544_hci_i2c_disable, +}; + +static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name, + u8 hw_variant) +{ + struct pn544_i2c_phy *phy = phy_id; + + pr_info("Starting Firmware Download (%s)\n", firmware_name); + + strcpy(phy->firmware_name, firmware_name); + + phy->hw_variant = hw_variant; + phy->fw_work_state = FW_WORK_STATE_START; + + schedule_work(&phy->fw_work); + + return 0; +} + +static void pn544_hci_i2c_fw_work_complete(struct pn544_i2c_phy *phy, + int result) +{ + pr_info("Firmware Download Complete, result=%d\n", result); + + pn544_hci_i2c_disable(phy); + + phy->fw_work_state = FW_WORK_STATE_IDLE; + + if (phy->fw) { + release_firmware(phy->fw); + phy->fw = NULL; + } + + nfc_fw_download_done(phy->hdev->ndev, phy->firmware_name, (u32) -result); +} + +static int pn544_hci_i2c_fw_write_cmd(struct i2c_client *client, u32 dest_addr, + const u8 *data, u16 datalen) +{ + u8 frame[PN544_FW_I2C_MAX_PAYLOAD]; + struct pn544_i2c_fw_frame_write *framep; + u16 params_len; + int framelen; + int r; + + if (datalen > PN544_FW_I2C_WRITE_DATA_MAX_LEN) + datalen = PN544_FW_I2C_WRITE_DATA_MAX_LEN; + + framep = (struct pn544_i2c_fw_frame_write *) frame; + + params_len = sizeof(framep->be_dest_addr) + + sizeof(framep->be_datalen) + datalen; + framelen = params_len + sizeof(framep->cmd) + + sizeof(framep->be_length); + + framep->cmd = PN544_FW_CMD_WRITE; + + put_unaligned_be16(params_len, &framep->be_length); + + framep->be_dest_addr[0] = (dest_addr & 0xff0000) >> 16; + framep->be_dest_addr[1] = (dest_addr & 0xff00) >> 8; + framep->be_dest_addr[2] = dest_addr & 0xff; + + put_unaligned_be16(datalen, &framep->be_datalen); + + memcpy(framep->data, data, datalen); + + r = i2c_master_send(client, frame, framelen); + + if (r == framelen) + return datalen; + else if (r < 0) + return r; + else + return -EIO; +} + +static int pn544_hci_i2c_fw_check_cmd(struct i2c_client *client, u32 start_addr, + const u8 *data, u16 datalen) +{ + struct pn544_i2c_fw_frame_check frame; + int r; + u16 crc; + + /* calculate local crc for the data we want to check */ + crc = crc_ccitt(0xffff, data, datalen); + + frame.cmd = PN544_FW_CMD_CHECK; + + put_unaligned_be16(sizeof(frame.be_start_addr) + + sizeof(frame.be_datalen) + sizeof(frame.be_crc), + &frame.be_length); + + /* tell the chip the memory region to which our crc applies */ + frame.be_start_addr[0] = (start_addr & 0xff0000) >> 16; + frame.be_start_addr[1] = (start_addr & 0xff00) >> 8; + frame.be_start_addr[2] = start_addr & 0xff; + + put_unaligned_be16(datalen, &frame.be_datalen); + + /* + * and give our local crc. Chip will calculate its own crc for the + * region and compare with ours. + */ + put_unaligned_be16(crc, &frame.be_crc); + + r = i2c_master_send(client, (const char *) &frame, sizeof(frame)); + + if (r == sizeof(frame)) + return 0; + else if (r < 0) + return r; + else + return -EIO; +} + +static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy) +{ + int r; + + r = pn544_hci_i2c_fw_write_cmd(phy->i2c_dev, + phy->fw_blob_dest_addr + phy->fw_written, + phy->fw_blob_data + phy->fw_written, + phy->fw_blob_size - phy->fw_written); + if (r < 0) + return r; + + phy->fw_written += r; + phy->fw_work_state = FW_WORK_STATE_WAIT_WRITE_ANSWER; + + return 0; +} + +static int pn544_hci_i2c_fw_secure_write_frame_cmd(struct pn544_i2c_phy *phy, + const u8 *data, u16 datalen) +{ + u8 buf[PN544_FW_I2C_MAX_PAYLOAD]; + struct pn544_i2c_fw_secure_frame *chunk; + int chunklen; + int r; + + if (datalen > PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN) + datalen = PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN; + + chunk = (struct pn544_i2c_fw_secure_frame *) buf; + + chunk->cmd = PN544_FW_CMD_SECURE_CHUNK_WRITE; + + put_unaligned_be16(datalen, &chunk->be_datalen); + + memcpy(chunk->data, data, datalen); + + chunklen = sizeof(chunk->cmd) + sizeof(chunk->be_datalen) + datalen; + + r = i2c_master_send(phy->i2c_dev, buf, chunklen); + + if (r == chunklen) + return datalen; + else if (r < 0) + return r; + else + return -EIO; + +} + +static int pn544_hci_i2c_fw_secure_write_frame(struct pn544_i2c_phy *phy) +{ + struct pn544_i2c_fw_secure_frame *framep; + int r; + + framep = (struct pn544_i2c_fw_secure_frame *) phy->fw_blob_data; + if (phy->fw_written == 0) + phy->fw_blob_size = get_unaligned_be16(&framep->be_datalen) + + PN544_FW_SECURE_FRAME_HEADER_LEN; + + /* Only secure write command can be chunked*/ + if (phy->fw_blob_size > PN544_FW_I2C_MAX_PAYLOAD && + framep->cmd != PN544_FW_CMD_SECURE_WRITE) + return -EINVAL; + + /* The firmware also have other commands, we just send them directly */ + if (phy->fw_blob_size < PN544_FW_I2C_MAX_PAYLOAD) { + r = i2c_master_send(phy->i2c_dev, + (const char *) phy->fw_blob_data, phy->fw_blob_size); + + if (r == phy->fw_blob_size) + goto exit; + else if (r < 0) + return r; + else + return -EIO; + } + + r = pn544_hci_i2c_fw_secure_write_frame_cmd(phy, + phy->fw_blob_data + phy->fw_written, + phy->fw_blob_size - phy->fw_written); + if (r < 0) + return r; + +exit: + phy->fw_written += r; + phy->fw_work_state = FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER; + + /* SW reset command will not trig any response from PN544 */ + if (framep->cmd == PN544_FW_CMD_RESET) { + pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE); + phy->fw_cmd_result = 0; + schedule_work(&phy->fw_work); + } + + return 0; +} + +static void pn544_hci_i2c_fw_work(struct work_struct *work) +{ + struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy, + fw_work); + int r; + struct pn544_i2c_fw_blob *blob; + struct pn544_i2c_fw_secure_blob *secure_blob; + + switch (phy->fw_work_state) { + case FW_WORK_STATE_START: + pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE); + + r = reject_firmware(&phy->fw, phy->firmware_name, + &phy->i2c_dev->dev); + if (r < 0) + goto exit_state_start; + + phy->fw_written = 0; + + switch (phy->hw_variant) { + case PN544_HW_VARIANT_C2: + blob = (struct pn544_i2c_fw_blob *) phy->fw->data; + phy->fw_blob_size = get_unaligned_be32(&blob->be_size); + phy->fw_blob_dest_addr = get_unaligned_be32( + &blob->be_destaddr); + phy->fw_blob_data = blob->data; + + r = pn544_hci_i2c_fw_write_chunk(phy); + break; + case PN544_HW_VARIANT_C3: + secure_blob = (struct pn544_i2c_fw_secure_blob *) + phy->fw->data; + phy->fw_blob_data = secure_blob->data; + phy->fw_size = phy->fw->size; + r = pn544_hci_i2c_fw_secure_write_frame(phy); + break; + default: + r = -ENOTSUPP; + break; + } + +exit_state_start: + if (r < 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + + case FW_WORK_STATE_WAIT_WRITE_ANSWER: + r = phy->fw_cmd_result; + if (r < 0) + goto exit_state_wait_write_answer; + + if (phy->fw_written == phy->fw_blob_size) { + r = pn544_hci_i2c_fw_check_cmd(phy->i2c_dev, + phy->fw_blob_dest_addr, + phy->fw_blob_data, + phy->fw_blob_size); + if (r < 0) + goto exit_state_wait_write_answer; + phy->fw_work_state = FW_WORK_STATE_WAIT_CHECK_ANSWER; + break; + } + + r = pn544_hci_i2c_fw_write_chunk(phy); + +exit_state_wait_write_answer: + if (r < 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + + case FW_WORK_STATE_WAIT_CHECK_ANSWER: + r = phy->fw_cmd_result; + if (r < 0) + goto exit_state_wait_check_answer; + + blob = (struct pn544_i2c_fw_blob *) (phy->fw_blob_data + + phy->fw_blob_size); + phy->fw_blob_size = get_unaligned_be32(&blob->be_size); + if (phy->fw_blob_size != 0) { + phy->fw_blob_dest_addr = + get_unaligned_be32(&blob->be_destaddr); + phy->fw_blob_data = blob->data; + + phy->fw_written = 0; + r = pn544_hci_i2c_fw_write_chunk(phy); + } + +exit_state_wait_check_answer: + if (r < 0 || phy->fw_blob_size == 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + + case FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER: + r = phy->fw_cmd_result; + if (r < 0) + goto exit_state_wait_secure_write_answer; + + if (r == PN544_FW_CMD_RESULT_CHUNK_OK) { + r = pn544_hci_i2c_fw_secure_write_frame(phy); + goto exit_state_wait_secure_write_answer; + } + + if (phy->fw_written == phy->fw_blob_size) { + secure_blob = (struct pn544_i2c_fw_secure_blob *) + (phy->fw_blob_data + phy->fw_blob_size); + phy->fw_size -= phy->fw_blob_size + + PN544_FW_SECURE_BLOB_HEADER_LEN; + if (phy->fw_size >= PN544_FW_SECURE_BLOB_HEADER_LEN + + PN544_FW_SECURE_FRAME_HEADER_LEN) { + phy->fw_blob_data = secure_blob->data; + + phy->fw_written = 0; + r = pn544_hci_i2c_fw_secure_write_frame(phy); + } + } + +exit_state_wait_secure_write_answer: + if (r < 0 || phy->fw_size == 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + + default: + break; + } +} + +static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) +{ + struct pn544_i2c_phy *phy = i2c_get_clientdata(client); + const struct acpi_device_id *id; + struct gpio_desc *gpiod_en, *gpiod_irq, *gpiod_fw; + struct device *dev; + int ret; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + + if (!id) + return -ENODEV; + + /* Get EN GPIO from ACPI */ + gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1); + if (IS_ERR(gpiod_en)) { + nfc_err(dev, + "Unable to get EN GPIO\n"); + return -ENODEV; + } + + phy->gpio_en = desc_to_gpio(gpiod_en); + + /* Configuration EN GPIO */ + ret = gpiod_direction_output(gpiod_en, 0); + if (ret) { + nfc_err(dev, "Fail EN pin direction\n"); + return ret; + } + + /* Get FW GPIO from ACPI */ + gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2); + if (IS_ERR(gpiod_fw)) { + nfc_err(dev, + "Unable to get FW GPIO\n"); + return -ENODEV; + } + + phy->gpio_fw = desc_to_gpio(gpiod_fw); + + /* Configuration FW GPIO */ + ret = gpiod_direction_output(gpiod_fw, 0); + if (ret) { + nfc_err(dev, "Fail FW pin direction\n"); + return ret; + } + + /* Get IRQ GPIO */ + gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0); + if (IS_ERR(gpiod_irq)) { + nfc_err(dev, + "Unable to get IRQ GPIO\n"); + return -ENODEV; + } + + phy->gpio_irq = desc_to_gpio(gpiod_irq); + + /* Configure IRQ GPIO */ + ret = gpiod_direction_input(gpiod_irq); + if (ret) { + nfc_err(dev, "Fail IRQ pin direction\n"); + return ret; + } + + /* Map the pin to an IRQ */ + ret = gpiod_to_irq(gpiod_irq); + if (ret < 0) { + nfc_err(dev, "Fail pin IRQ mapping\n"); + return ret; + } + + nfc_info(dev, "GPIO resource, no:%d irq:%d\n", + desc_to_gpio(gpiod_irq), ret); + client->irq = ret; + + return 0; +} + +#ifdef CONFIG_OF + +static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) +{ + struct pn544_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int ret; + + pp = client->dev.of_node; + if (!pp) { + ret = -ENODEV; + goto err_dt; + } + + /* Obtention of EN GPIO from device tree */ + ret = of_get_named_gpio(pp, "enable-gpios", 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + nfc_err(&client->dev, + "Failed to get EN gpio, error: %d\n", ret); + goto err_dt; + } + phy->gpio_en = ret; + + /* Configuration of EN GPIO */ + ret = gpio_request(phy->gpio_en, PN544_GPIO_NAME_EN); + if (ret) { + nfc_err(&client->dev, "Fail EN pin\n"); + goto err_dt; + } + ret = gpio_direction_output(phy->gpio_en, 0); + if (ret) { + nfc_err(&client->dev, "Fail EN pin direction\n"); + goto err_gpio_en; + } + + /* Obtention of FW GPIO from device tree */ + ret = of_get_named_gpio(pp, "firmware-gpios", 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + nfc_err(&client->dev, + "Failed to get FW gpio, error: %d\n", ret); + goto err_gpio_en; + } + phy->gpio_fw = ret; + + /* Configuration of FW GPIO */ + ret = gpio_request(phy->gpio_fw, PN544_GPIO_NAME_FW); + if (ret) { + nfc_err(&client->dev, "Fail FW pin\n"); + goto err_gpio_en; + } + ret = gpio_direction_output(phy->gpio_fw, 0); + if (ret) { + nfc_err(&client->dev, "Fail FW pin direction\n"); + goto err_gpio_fw; + } + + /* IRQ */ + ret = irq_of_parse_and_map(pp, 0); + if (ret < 0) { + nfc_err(&client->dev, + "Unable to get irq, error: %d\n", ret); + goto err_gpio_fw; + } + client->irq = ret; + + return 0; + +err_gpio_fw: + gpio_free(phy->gpio_fw); +err_gpio_en: + gpio_free(phy->gpio_en); +err_dt: + return ret; +} + +#else + +static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) +{ + return -ENODEV; +} + +#endif + +static int pn544_hci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pn544_i2c_phy *phy; + struct pn544_nfc_platform_data *pdata; + int r = 0; + + 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 pn544_i2c_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work); + phy->fw_work_state = FW_WORK_STATE_IDLE; + + phy->i2c_dev = client; + i2c_set_clientdata(client, phy); + + pdata = client->dev.platform_data; + + /* No platform data, using device tree. */ + if (!pdata && client->dev.of_node) { + r = pn544_hci_i2c_of_request_resources(client); + if (r) { + nfc_err(&client->dev, "No DT data\n"); + return r; + } + /* Using platform data. */ + } else if (pdata) { + + if (pdata->request_resources == NULL) { + nfc_err(&client->dev, "request_resources() missing\n"); + return -EINVAL; + } + + r = pdata->request_resources(client); + if (r) { + nfc_err(&client->dev, + "Cannot get platform resources\n"); + return r; + } + + phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); + phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); + phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); + /* Using ACPI */ + } else if (ACPI_HANDLE(&client->dev)) { + r = pn544_hci_i2c_acpi_request_resources(client); + if (r) { + nfc_err(&client->dev, + "Cannot get ACPI data\n"); + return r; + } + } else { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + pn544_hci_i2c_platform_init(phy); + + r = request_threaded_irq(client->irq, NULL, pn544_hci_i2c_irq_thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + PN544_HCI_I2C_DRIVER_NAME, phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + goto err_rti; + } + + r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, + PN544_I2C_FRAME_HEADROOM, PN544_I2C_FRAME_TAILROOM, + PN544_HCI_I2C_LLC_MAX_PAYLOAD, + pn544_hci_i2c_fw_download, &phy->hdev); + if (r < 0) + goto err_hci; + + return 0; + +err_hci: + free_irq(client->irq, phy); + +err_rti: + if (!pdata) { + gpio_free(phy->gpio_en); + gpio_free(phy->gpio_fw); + } else if (pdata->free_resources) { + pdata->free_resources(); + } + + return r; +} + +static int pn544_hci_i2c_remove(struct i2c_client *client) +{ + struct pn544_i2c_phy *phy = i2c_get_clientdata(client); + struct pn544_nfc_platform_data *pdata = client->dev.platform_data; + + dev_dbg(&client->dev, "%s\n", __func__); + + cancel_work_sync(&phy->fw_work); + if (phy->fw_work_state != FW_WORK_STATE_IDLE) + pn544_hci_i2c_fw_work_complete(phy, -ENODEV); + + pn544_hci_remove(phy->hdev); + + if (phy->powered) + pn544_hci_i2c_disable(phy); + + free_irq(client->irq, phy); + + /* No platform data, GPIOs have been requested by this driver */ + if (!pdata) { + gpio_free(phy->gpio_en); + gpio_free(phy->gpio_fw); + /* Using platform data */ + } else if (pdata->free_resources) { + pdata->free_resources(); + } + + return 0; +} + +static const struct of_device_id of_pn544_i2c_match[] = { + { .compatible = "nxp,pn544-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_pn544_i2c_match); + +static struct i2c_driver pn544_hci_i2c_driver = { + .driver = { + .name = PN544_HCI_I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_pn544_i2c_match), + .acpi_match_table = ACPI_PTR(pn544_hci_i2c_acpi_match), + }, + .probe = pn544_hci_i2c_probe, + .id_table = pn544_hci_i2c_id_table, + .remove = pn544_hci_i2c_remove, +}; + +module_i2c_driver(pn544_hci_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/pn544/mei.c b/drivers/nfc/pn544/mei.c new file mode 100644 index 000000000..330cd4031 --- /dev/null +++ b/drivers/nfc/pn544/mei.c @@ -0,0 +1,109 @@ +/* + * HCI based Driver for NXP pn544 NFC Chip + * + * Copyright (C) 2013 Intel Corporation. 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/mod_devicetable.h> +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "../mei_phy.h" +#include "pn544.h" + +#define PN544_DRIVER_NAME "pn544" + +static int pn544_mei_probe(struct mei_cl_device *device, + const struct mei_cl_device_id *id) +{ + struct nfc_mei_phy *phy; + int r; + + pr_info("Probing NFC pn544\n"); + + phy = nfc_mei_phy_alloc(device); + if (!phy) { + pr_err("Cannot allocate memory for pn544 mei phy.\n"); + return -ENOMEM; + } + + r = pn544_hci_probe(phy, &mei_phy_ops, LLC_NOP_NAME, + MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD, + NULL, &phy->hdev); + if (r < 0) { + nfc_mei_phy_free(phy); + + return r; + } + + return 0; +} + +static int pn544_mei_remove(struct mei_cl_device *device) +{ + struct nfc_mei_phy *phy = mei_cl_get_drvdata(device); + + pr_info("Removing pn544\n"); + + pn544_hci_remove(phy->hdev); + + nfc_mei_phy_free(phy); + + return 0; +} + +static struct mei_cl_device_id pn544_mei_tbl[] = { + { PN544_DRIVER_NAME }, + + /* required last entry */ + { } +}; +MODULE_DEVICE_TABLE(mei, pn544_mei_tbl); + +static struct mei_cl_driver pn544_driver = { + .id_table = pn544_mei_tbl, + .name = PN544_DRIVER_NAME, + + .probe = pn544_mei_probe, + .remove = pn544_mei_remove, +}; + +static int pn544_mei_init(void) +{ + int r; + + pr_debug(DRIVER_DESC ": %s\n", __func__); + + r = mei_cl_driver_register(&pn544_driver); + if (r) { + pr_err(PN544_DRIVER_NAME ": driver registration failed\n"); + return r; + } + + return 0; +} + +static void pn544_mei_exit(void) +{ + mei_cl_driver_unregister(&pn544_driver); +} + +module_init(pn544_mei_init); +module_exit(pn544_mei_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c new file mode 100644 index 000000000..12e819ddf --- /dev/null +++ b/drivers/nfc/pn544/pn544.c @@ -0,0 +1,995 @@ +/* + * HCI based Driver for NXP PN544 NFC Chip + * + * Copyright (C) 2012 Intel Corporation. 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/delay.h> +#include <linux/slab.h> +#include <linux/module.h> + +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "pn544.h" + +/* Timing restrictions (ms) */ +#define PN544_HCI_RESETVEN_TIME 30 + +enum pn544_state { + PN544_ST_COLD, + PN544_ST_FW_READY, + PN544_ST_READY, +}; + +#define FULL_VERSION_LEN 11 + +/* Proprietary commands */ +#define PN544_WRITE 0x3f +#define PN544_TEST_SWP 0x21 + +/* Proprietary gates, events, commands and registers */ + +/* NFC_HCI_RF_READER_A_GATE additional registers and commands */ +#define PN544_RF_READER_A_AUTO_ACTIVATION 0x10 +#define PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION 0x12 +#define PN544_MIFARE_CMD 0x21 + +/* Commands that apply to all RF readers */ +#define PN544_RF_READER_CMD_PRESENCE_CHECK 0x30 +#define PN544_RF_READER_CMD_ACTIVATE_NEXT 0x32 + +/* NFC_HCI_ID_MGMT_GATE additional registers */ +#define PN544_ID_MGMT_FULL_VERSION_SW 0x10 + +#define PN544_RF_READER_ISO15693_GATE 0x12 + +#define PN544_RF_READER_F_GATE 0x14 +#define PN544_FELICA_ID 0x04 +#define PN544_FELICA_RAW 0x20 + +#define PN544_RF_READER_JEWEL_GATE 0x15 +#define PN544_JEWEL_RAW_CMD 0x23 + +#define PN544_RF_READER_NFCIP1_INITIATOR_GATE 0x30 +#define PN544_RF_READER_NFCIP1_TARGET_GATE 0x31 + +#define PN544_SYS_MGMT_GATE 0x90 +#define PN544_SYS_MGMT_INFO_NOTIFICATION 0x02 + +#define PN544_POLLING_LOOP_MGMT_GATE 0x94 +#define PN544_DEP_MODE 0x01 +#define PN544_DEP_ATR_REQ 0x02 +#define PN544_DEP_ATR_RES 0x03 +#define PN544_DEP_MERGE 0x0D +#define PN544_PL_RDPHASES 0x06 +#define PN544_PL_EMULATION 0x07 +#define PN544_PL_NFCT_DEACTIVATED 0x09 + +#define PN544_SWP_MGMT_GATE 0xA0 +#define PN544_SWP_DEFAULT_MODE 0x01 + +#define PN544_NFC_WI_MGMT_GATE 0xA1 +#define PN544_NFC_ESE_DEFAULT_MODE 0x01 + +#define PN544_HCI_EVT_SND_DATA 0x01 +#define PN544_HCI_EVT_ACTIVATED 0x02 +#define PN544_HCI_EVT_DEACTIVATED 0x03 +#define PN544_HCI_EVT_RCV_DATA 0x04 +#define PN544_HCI_EVT_CONTINUE_MI 0x05 +#define PN544_HCI_EVT_SWITCH_MODE 0x03 + +#define PN544_HCI_CMD_ATTREQUEST 0x12 +#define PN544_HCI_CMD_CONTINUE_ACTIVATION 0x13 + +static struct nfc_hci_gate pn544_gates[] = { + {NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LINK_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}, + {PN544_SYS_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_SWP_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_POLLING_LOOP_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_NFC_WI_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_JEWEL_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_NFCIP1_INITIATOR_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_NFCIP1_TARGET_GATE, NFC_HCI_INVALID_PIPE} +}; + +/* Largest headroom needed for outgoing custom commands */ +#define PN544_CMDS_HEADROOM 2 + +struct pn544_hci_info { + struct nfc_phy_ops *phy_ops; + void *phy_id; + + struct nfc_hci_dev *hdev; + + enum pn544_state state; + + struct mutex info_lock; + + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; + + fw_download_t fw_download; +}; + +static int pn544_hci_open(struct nfc_hci_dev *hdev) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + int r = 0; + + mutex_lock(&info->info_lock); + + if (info->state != PN544_ST_COLD) { + r = -EBUSY; + goto out; + } + + r = info->phy_ops->enable(info->phy_id); + + if (r == 0) + info->state = PN544_ST_READY; + +out: + mutex_unlock(&info->info_lock); + return r; +} + +static void pn544_hci_close(struct nfc_hci_dev *hdev) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + mutex_lock(&info->info_lock); + + if (info->state == PN544_ST_COLD) + goto out; + + info->phy_ops->disable(info->phy_id); + + info->state = PN544_ST_COLD; + +out: + mutex_unlock(&info->info_lock); +} + +static int pn544_hci_ready(struct nfc_hci_dev *hdev) +{ + struct sk_buff *skb; + static struct hw_config { + u8 adr[2]; + u8 value; + } hw_config[] = { + {{0x9f, 0x9a}, 0x00}, + + {{0x98, 0x10}, 0xbc}, + + {{0x9e, 0x71}, 0x00}, + + {{0x98, 0x09}, 0x00}, + + {{0x9e, 0xb4}, 0x00}, + + {{0x9c, 0x01}, 0x08}, + + {{0x9e, 0xaa}, 0x01}, + + {{0x9b, 0xd1}, 0x17}, + {{0x9b, 0xd2}, 0x58}, + {{0x9b, 0xd3}, 0x10}, + {{0x9b, 0xd4}, 0x47}, + {{0x9b, 0xd5}, 0x0c}, + {{0x9b, 0xd6}, 0x37}, + {{0x9b, 0xdd}, 0x33}, + + {{0x9b, 0x84}, 0x00}, + {{0x99, 0x81}, 0x79}, + {{0x99, 0x31}, 0x79}, + + {{0x98, 0x00}, 0x3f}, + + {{0x9f, 0x09}, 0x02}, + + {{0x9f, 0x0a}, 0x05}, + + {{0x9e, 0xd1}, 0xa1}, + {{0x99, 0x23}, 0x01}, + + {{0x9e, 0x74}, 0x00}, + {{0x9e, 0x90}, 0x00}, + {{0x9f, 0x28}, 0x10}, + + {{0x9f, 0x35}, 0x04}, + + {{0x9f, 0x36}, 0x11}, + + {{0x9c, 0x31}, 0x00}, + + {{0x9c, 0x32}, 0x00}, + + {{0x9c, 0x19}, 0x0a}, + + {{0x9c, 0x1a}, 0x0a}, + + {{0x9c, 0x0c}, 0x00}, + + {{0x9c, 0x0d}, 0x00}, + + {{0x9c, 0x12}, 0x00}, + + {{0x9c, 0x13}, 0x00}, + + {{0x98, 0xa2}, 0x09}, + + {{0x98, 0x93}, 0x00}, + + {{0x98, 0x7d}, 0x08}, + {{0x98, 0x7e}, 0x00}, + {{0x9f, 0xc8}, 0x00}, + }; + struct hw_config *p = hw_config; + int count = ARRAY_SIZE(hw_config); + struct sk_buff *res_skb; + u8 param[4]; + int r; + + param[0] = 0; + while (count--) { + param[1] = p->adr[0]; + param[2] = p->adr[1]; + param[3] = p->value; + + r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_WRITE, + param, 4, &res_skb); + if (r < 0) + return r; + + if (res_skb->len != 1) { + kfree_skb(res_skb); + return -EPROTO; + } + + if (res_skb->data[0] != p->value) { + kfree_skb(res_skb); + return -EIO; + } + + kfree_skb(res_skb); + + p++; + } + + param[0] = NFC_HCI_UICC_HOST_ID; + r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_WHITELIST, param, 1); + if (r < 0) + return r; + + param[0] = 0x3d; + r = nfc_hci_set_param(hdev, PN544_SYS_MGMT_GATE, + PN544_SYS_MGMT_INFO_NOTIFICATION, param, 1); + if (r < 0) + return r; + + param[0] = 0x0; + r = nfc_hci_set_param(hdev, NFC_HCI_RF_READER_A_GATE, + PN544_RF_READER_A_AUTO_ACTIVATION, param, 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; + + param[0] = 0x1; + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_NFCT_DEACTIVATED, param, 1); + if (r < 0) + return r; + + param[0] = 0x0; + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_RDPHASES, param, 1); + if (r < 0) + return r; + + r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, + PN544_ID_MGMT_FULL_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 pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + return info->phy_ops->write(info->phy_id, skb); +} + +static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols) +{ + u8 phases = 0; + int r; + u8 duration[2]; + u8 activated; + u8 i_mode = 0x3f; /* Enable all supported modes */ + u8 t_mode = 0x0f; + u8 t_merge = 0x01; /* Enable merge by default */ + + 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; + + duration[0] = 0x18; + duration[1] = 0x6a; + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_EMULATION, duration, 2); + if (r < 0) + return r; + + activated = 0; + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_NFCT_DEACTIVATED, &activated, 1); + if (r < 0) + return r; + + if (im_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK | + NFC_PROTO_JEWEL_MASK)) + phases |= 1; /* Type A */ + if (im_protocols & NFC_PROTO_FELICA_MASK) { + phases |= (1 << 2); /* Type F 212 */ + phases |= (1 << 3); /* Type F 424 */ + } + + phases |= (1 << 5); /* NFC active */ + + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_RDPHASES, &phases, 1); + if (r < 0) + return r; + + if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { + hdev->gb = nfc_get_local_general_bytes(hdev->ndev, + &hdev->gb_len); + pr_debug("generate local bytes %p\n", hdev->gb); + if (hdev->gb == NULL || hdev->gb_len == 0) { + im_protocols &= ~NFC_PROTO_NFC_DEP_MASK; + tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK; + } + } + + if (im_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_DEP_MODE, &i_mode, 1); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_DEP_ATR_REQ, hdev->gb, hdev->gb_len); + if (r < 0) + return r; + + r = nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + if (r < 0) + nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + } + + if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_MODE, &t_mode, 1); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_ATR_RES, hdev->gb, hdev->gb_len); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_MERGE, &t_merge, 1); + 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); + + return r; +} + +static int pn544_hci_dep_link_up(struct nfc_hci_dev *hdev, + struct nfc_target *target, u8 comm_mode, + u8 *gb, size_t gb_len) +{ + struct sk_buff *rgb_skb = NULL; + int r; + + r = nfc_hci_get_param(hdev, target->hci_reader_gate, + PN544_DEP_ATR_RES, &rgb_skb); + if (r < 0) + return r; + + if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) { + r = -EPROTO; + goto exit; + } + print_hex_dump(KERN_DEBUG, "remote gb: ", DUMP_PREFIX_OFFSET, + 16, 1, rgb_skb->data, rgb_skb->len, true); + + r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data, + rgb_skb->len); + + if (r == 0) + r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode, + NFC_RF_INITIATOR); +exit: + kfree_skb(rgb_skb); + return r; +} + +static int pn544_hci_dep_link_down(struct nfc_hci_dev *hdev) +{ + + return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); +} + +static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, + struct nfc_target *target) +{ + switch (gate) { + case PN544_RF_READER_F_GATE: + target->supported_protocols = NFC_PROTO_FELICA_MASK; + break; + case PN544_RF_READER_JEWEL_GATE: + target->supported_protocols = NFC_PROTO_JEWEL_MASK; + target->sens_res = 0x0c00; + break; + case PN544_RF_READER_NFCIP1_INITIATOR_GATE: + target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + break; + default: + return -EPROTO; + } + + return 0; +} + +static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev, + u8 gate, + struct nfc_target *target) +{ + struct sk_buff *uid_skb; + int r = 0; + + if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) + return r; + + if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_send_cmd(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_HCI_CMD_CONTINUE_ACTIVATION, NULL, 0, NULL); + if (r < 0) + return r; + + target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE; + } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { + if (target->nfcid1_len != 4 && target->nfcid1_len != 7 && + target->nfcid1_len != 10) + return -EPROTO; + + r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, + PN544_RF_READER_CMD_ACTIVATE_NEXT, + target->nfcid1, target->nfcid1_len, NULL); + } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) { + r = nfc_hci_get_param(hdev, PN544_RF_READER_F_GATE, + PN544_FELICA_ID, &uid_skb); + if (r < 0) + return r; + + if (uid_skb->len != 8) { + kfree_skb(uid_skb); + return -EPROTO; + } + + /* Type F NFC-DEP IDm has prefix 0x01FE */ + if ((uid_skb->data[0] == 0x01) && (uid_skb->data[1] == 0xfe)) { + kfree_skb(uid_skb); + r = nfc_hci_send_cmd(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_HCI_CMD_CONTINUE_ACTIVATION, + NULL, 0, NULL); + if (r < 0) + return r; + + target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + target->hci_reader_gate = + PN544_RF_READER_NFCIP1_INITIATOR_GATE; + } else { + r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE, + PN544_RF_READER_CMD_ACTIVATE_NEXT, + uid_skb->data, uid_skb->len, NULL); + kfree_skb(uid_skb); + } + } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) { + /* + * TODO: maybe other ISO 14443 require some kind of continue + * activation, but for now we've seen only this one below. + */ + if (target->sens_res == 0x4403) /* Type 4 Mifare DESFire */ + r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, + PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION, + NULL, 0, NULL); + } + + return r; +} + +#define PN544_CB_TYPE_READER_F 1 + +static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, + int err) +{ + struct pn544_hci_info *info = context; + + switch (info->async_cb_type) { + case PN544_CB_TYPE_READER_F: + if (err == 0) + skb_pull(skb, 1); + info->async_cb(info->async_cb_context, skb, err); + break; + default: + if (err == 0) + kfree_skb(skb); + break; + } +} + +#define MIFARE_CMD_AUTH_KEY_A 0x60 +#define MIFARE_CMD_AUTH_KEY_B 0x61 +#define MIFARE_CMD_HEADER 2 +#define MIFARE_UID_LEN 4 +#define MIFARE_KEY_LEN 6 +#define MIFARE_CMD_LEN 12 +/* + * Returns: + * <= 0: driver handled the data exchange + * 1: driver doesn't especially handle, please do standard processing + */ +static int pn544_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 pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__, + target->hci_reader_gate); + + switch (target->hci_reader_gate) { + case NFC_HCI_RF_READER_A_GATE: + if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { + /* + * It seems that pn544 is inverting key and UID for + * MIFARE authentication commands. + */ + if (skb->len == MIFARE_CMD_LEN && + (skb->data[0] == MIFARE_CMD_AUTH_KEY_A || + skb->data[0] == MIFARE_CMD_AUTH_KEY_B)) { + u8 uid[MIFARE_UID_LEN]; + u8 *data = skb->data + MIFARE_CMD_HEADER; + + memcpy(uid, data + MIFARE_KEY_LEN, + MIFARE_UID_LEN); + memmove(data + MIFARE_UID_LEN, data, + MIFARE_KEY_LEN); + memcpy(data, uid, MIFARE_UID_LEN); + } + + return nfc_hci_send_cmd_async(hdev, + target->hci_reader_gate, + PN544_MIFARE_CMD, + skb->data, skb->len, + cb, cb_context); + } else + return 1; + case PN544_RF_READER_F_GATE: + *skb_push(skb, 1) = 0; + *skb_push(skb, 1) = 0; + + info->async_cb_type = PN544_CB_TYPE_READER_F; + info->async_cb = cb; + info->async_cb_context = cb_context; + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + PN544_FELICA_RAW, skb->data, + skb->len, + pn544_hci_data_exchange_cb, info); + case PN544_RF_READER_JEWEL_GATE: + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + PN544_JEWEL_RAW_CMD, skb->data, + skb->len, cb, cb_context); + case PN544_RF_READER_NFCIP1_INITIATOR_GATE: + *skb_push(skb, 1) = 0; + + return nfc_hci_send_event(hdev, target->hci_reader_gate, + PN544_HCI_EVT_SND_DATA, skb->data, + skb->len); + default: + return 1; + } +} + +static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + int r; + + /* Set default false for multiple information chaining */ + *skb_push(skb, 1) = 0; + + r = nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_HCI_EVT_SND_DATA, skb->data, skb->len); + + kfree_skb(skb); + + return r; +} + +static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, + struct nfc_target *target) +{ + pr_debug("supported protocol %d\b", target->supported_protocols); + if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK)) { + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + PN544_RF_READER_CMD_PRESENCE_CHECK, + NULL, 0, NULL); + } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { + if (target->nfcid1_len != 4 && target->nfcid1_len != 7 && + target->nfcid1_len != 10) + return -EOPNOTSUPP; + + return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, + PN544_RF_READER_CMD_ACTIVATE_NEXT, + target->nfcid1, target->nfcid1_len, NULL); + } else if (target->supported_protocols & (NFC_PROTO_JEWEL_MASK | + NFC_PROTO_FELICA_MASK)) { + return -EOPNOTSUPP; + } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) { + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + PN544_HCI_CMD_ATTREQUEST, + NULL, 0, NULL); + } + + return 0; +} + +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ +static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, + struct sk_buff *skb) +{ + struct sk_buff *rgb_skb = NULL; + u8 gate = hdev->pipes[pipe].gate; + int r; + + pr_debug("hci event %d\n", event); + switch (event) { + case PN544_HCI_EVT_ACTIVATED: + if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) { + r = nfc_hci_target_discovered(hdev, gate); + } else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { + r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, + &rgb_skb); + if (r < 0) + goto exit; + + r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, rgb_skb->data, + rgb_skb->len); + + kfree_skb(rgb_skb); + } else { + r = -EINVAL; + } + break; + case PN544_HCI_EVT_DEACTIVATED: + r = nfc_hci_send_event(hdev, gate, NFC_HCI_EVT_END_OPERATION, + NULL, 0); + break; + case PN544_HCI_EVT_RCV_DATA: + if (skb->len < 2) { + r = -EPROTO; + goto exit; + } + + if (skb->data[0] != 0) { + pr_debug("data0 %d\n", skb->data[0]); + r = -EPROTO; + goto exit; + } + + skb_pull(skb, 2); + return nfc_tm_data_received(hdev->ndev, skb); + default: + return 1; + } + +exit: + kfree_skb(skb); + + return r; +} + +static int pn544_hci_fw_download(struct nfc_hci_dev *hdev, + const char *firmware_name) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + if (info->fw_download == NULL) + return -ENOTSUPP; + + return info->fw_download(info->phy_id, firmware_name, hdev->sw_romlib); +} + +static int pn544_hci_discover_se(struct nfc_hci_dev *hdev) +{ + u32 se_idx = 0; + u8 ese_mode = 0x01; /* Default mode */ + struct sk_buff *res_skb; + int r; + + r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_TEST_SWP, + NULL, 0, &res_skb); + + if (r == 0) { + if (res_skb->len == 2 && res_skb->data[0] == 0x00) + nfc_add_se(hdev->ndev, se_idx++, NFC_SE_UICC); + + kfree_skb(res_skb); + } + + r = nfc_hci_send_event(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_HCI_EVT_SWITCH_MODE, + &ese_mode, 1); + if (r == 0) + nfc_add_se(hdev->ndev, se_idx++, NFC_SE_EMBEDDED); + + return !se_idx; +} + +#define PN544_SE_MODE_OFF 0x00 +#define PN544_SE_MODE_ON 0x01 +static int pn544_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + struct nfc_se *se; + u8 enable = PN544_SE_MODE_ON; + static struct uicc_gatelist { + u8 head; + u8 adr[2]; + u8 value; + } uicc_gatelist[] = { + {0x00, {0x9e, 0xd9}, 0x23}, + {0x00, {0x9e, 0xda}, 0x21}, + {0x00, {0x9e, 0xdb}, 0x22}, + {0x00, {0x9e, 0xdc}, 0x24}, + }; + struct uicc_gatelist *p = uicc_gatelist; + int count = ARRAY_SIZE(uicc_gatelist); + struct sk_buff *res_skb; + int r; + + se = nfc_find_se(hdev->ndev, se_idx); + + switch (se->type) { + case NFC_SE_UICC: + while (count--) { + r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, + PN544_WRITE, (u8 *)p, 4, &res_skb); + if (r < 0) + return r; + + if (res_skb->len != 1) { + kfree_skb(res_skb); + return -EPROTO; + } + + if (res_skb->data[0] != p->value) { + kfree_skb(res_skb); + return -EIO; + } + + kfree_skb(res_skb); + + p++; + } + + return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE, + PN544_SWP_DEFAULT_MODE, &enable, 1); + case NFC_SE_EMBEDDED: + return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_NFC_ESE_DEFAULT_MODE, &enable, 1); + + default: + return -EINVAL; + } +} + +static int pn544_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + struct nfc_se *se; + u8 disable = PN544_SE_MODE_OFF; + + se = nfc_find_se(hdev->ndev, se_idx); + + switch (se->type) { + case NFC_SE_UICC: + return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE, + PN544_SWP_DEFAULT_MODE, &disable, 1); + case NFC_SE_EMBEDDED: + return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_NFC_ESE_DEFAULT_MODE, &disable, 1); + default: + return -EINVAL; + } +} + +static struct nfc_hci_ops pn544_hci_ops = { + .open = pn544_hci_open, + .close = pn544_hci_close, + .hci_ready = pn544_hci_ready, + .xmit = pn544_hci_xmit, + .start_poll = pn544_hci_start_poll, + .dep_link_up = pn544_hci_dep_link_up, + .dep_link_down = pn544_hci_dep_link_down, + .target_from_gate = pn544_hci_target_from_gate, + .complete_target_discovered = pn544_hci_complete_target_discovered, + .im_transceive = pn544_hci_im_transceive, + .tm_send = pn544_hci_tm_send, + .check_presence = pn544_hci_check_presence, + .event_received = pn544_hci_event_received, + .fw_download = pn544_hci_fw_download, + .discover_se = pn544_hci_discover_se, + .enable_se = pn544_hci_enable_se, + .disable_se = pn544_hci_disable_se, +}; + +int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, + int phy_headroom, int phy_tailroom, int phy_payload, + fw_download_t fw_download, struct nfc_hci_dev **hdev) +{ + struct pn544_hci_info *info; + u32 protocols; + struct nfc_hci_init_data init_data; + int r; + + info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL); + if (!info) { + r = -ENOMEM; + goto err_info_alloc; + } + + info->phy_ops = phy_ops; + info->phy_id = phy_id; + info->fw_download = fw_download; + info->state = PN544_ST_COLD; + mutex_init(&info->info_lock); + + init_data.gate_count = ARRAY_SIZE(pn544_gates); + + memcpy(init_data.gates, pn544_gates, sizeof(pn544_gates)); + + /* + * TODO: Session id must include the driver name + some bus addr + * persistent info to discriminate 2 identical chips + */ + strcpy(init_data.session_id, "ID544HCI"); + + protocols = NFC_PROTO_JEWEL_MASK | + NFC_PROTO_MIFARE_MASK | + NFC_PROTO_FELICA_MASK | + NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK | + NFC_PROTO_NFC_DEP_MASK; + + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0, + protocols, llc_name, + phy_headroom + PN544_CMDS_HEADROOM, + phy_tailroom, phy_payload); + if (!info->hdev) { + pr_err("Cannot allocate nfc hdev\n"); + r = -ENOMEM; + goto err_alloc_hdev; + } + + nfc_hci_set_clientdata(info->hdev, info); + + r = nfc_hci_register_device(info->hdev); + if (r) + goto err_regdev; + + *hdev = info->hdev; + + return 0; + +err_regdev: + nfc_hci_free_device(info->hdev); + +err_alloc_hdev: + kfree(info); + +err_info_alloc: + return r; +} +EXPORT_SYMBOL(pn544_hci_probe); + +void pn544_hci_remove(struct nfc_hci_dev *hdev) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + nfc_hci_unregister_device(hdev); + nfc_hci_free_device(hdev); + kfree(info); +} +EXPORT_SYMBOL(pn544_hci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/pn544/pn544.h b/drivers/nfc/pn544/pn544.h new file mode 100644 index 000000000..2aa9233e8 --- /dev/null +++ b/drivers/nfc/pn544/pn544.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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_PN544_H_ +#define __LOCAL_PN544_H_ + +#include <net/nfc/hci.h> + +#define DRIVER_DESC "HCI NFC driver for PN544" + +#define PN544_HCI_MODE 0 +#define PN544_FW_MODE 1 + +typedef int (*fw_download_t)(void *context, const char *firmware_name, + u8 hw_variant); + +int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, + int phy_headroom, int phy_tailroom, int phy_payload, + fw_download_t fw_download, struct nfc_hci_dev **hdev); +void pn544_hci_remove(struct nfc_hci_dev *hdev); + +#endif /* __LOCAL_PN544_H_ */ diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c new file mode 100644 index 000000000..87d509996 --- /dev/null +++ b/drivers/nfc/port100.c @@ -0,0 +1,1579 @@ +/* + * Sony NFC Port-100 Series driver + * Copyright (c) 2013, Intel Corporation. + * + * Partly based/Inspired by Stephen Tiedemann's nfcpy + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include <linux/module.h> +#include <linux/usb.h> +#include <net/nfc/digital.h> + +#define VERSION "0.1" + +#define SONY_VENDOR_ID 0x054c +#define RCS380_PRODUCT_ID 0x06c1 + +#define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ + NFC_PROTO_MIFARE_MASK | \ + NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_NFC_DEP_MASK | \ + NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK) + +#define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \ + NFC_DIGITAL_DRV_CAPS_TG_CRC) + +/* Standard port100 frame definitions */ +#define PORT100_FRAME_HEADER_LEN (sizeof(struct port100_frame) \ + + 2) /* data[0] CC, data[1] SCC */ +#define PORT100_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/ + +#define PORT100_COMM_RF_HEAD_MAX_LEN (sizeof(struct port100_tg_comm_rf_cmd)) + +/* + * Max extended frame payload len, excluding CC and SCC + * which are already in PORT100_FRAME_HEADER_LEN. + */ +#define PORT100_FRAME_MAX_PAYLOAD_LEN 1001 + +#define PORT100_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2), + Postamble (1) */ +static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = { + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 +}; + +#define PORT100_FRAME_CHECKSUM(f) (f->data[le16_to_cpu(f->datalen)]) +#define PORT100_FRAME_POSTAMBLE(f) (f->data[le16_to_cpu(f->datalen) + 1]) + +/* start of frame */ +#define PORT100_FRAME_SOF 0x00FF +#define PORT100_FRAME_EXT 0xFFFF +#define PORT100_FRAME_ACK 0x00FF + +/* Port-100 command: in or out */ +#define PORT100_FRAME_DIRECTION(f) (f->data[0]) /* CC */ +#define PORT100_FRAME_DIR_OUT 0xD6 +#define PORT100_FRAME_DIR_IN 0xD7 + +/* Port-100 sub-command */ +#define PORT100_FRAME_CMD(f) (f->data[1]) /* SCC */ + +#define PORT100_CMD_GET_FIRMWARE_VERSION 0x20 +#define PORT100_CMD_GET_COMMAND_TYPE 0x28 +#define PORT100_CMD_SET_COMMAND_TYPE 0x2A + +#define PORT100_CMD_IN_SET_RF 0x00 +#define PORT100_CMD_IN_SET_PROTOCOL 0x02 +#define PORT100_CMD_IN_COMM_RF 0x04 + +#define PORT100_CMD_TG_SET_RF 0x40 +#define PORT100_CMD_TG_SET_PROTOCOL 0x42 +#define PORT100_CMD_TG_SET_RF_OFF 0x46 +#define PORT100_CMD_TG_COMM_RF 0x48 + +#define PORT100_CMD_SWITCH_RF 0x06 + +#define PORT100_CMD_RESPONSE(cmd) (cmd + 1) + +#define PORT100_CMD_TYPE_IS_SUPPORTED(mask, cmd_type) \ + ((mask) & (0x01 << (cmd_type))) +#define PORT100_CMD_TYPE_0 0 +#define PORT100_CMD_TYPE_1 1 + +#define PORT100_CMD_STATUS_OK 0x00 +#define PORT100_CMD_STATUS_TIMEOUT 0x80 + +#define PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK 0x01 +#define PORT100_MDAA_TGT_WAS_ACTIVATED_MASK 0x02 + +struct port100; + +typedef void (*port100_send_async_complete_t)(struct port100 *dev, void *arg, + struct sk_buff *resp); + +/** + * Setting sets structure for in_set_rf command + * + * @in_*_set_number: Represent the entry indexes in the port-100 RF Base Table. + * This table contains multiple RF setting sets required for RF + * communication. + * + * @in_*_comm_type: Theses fields set the communication type to be used. + */ +struct port100_in_rf_setting { + u8 in_send_set_number; + u8 in_send_comm_type; + u8 in_recv_set_number; + u8 in_recv_comm_type; +} __packed; + +#define PORT100_COMM_TYPE_IN_212F 0x01 +#define PORT100_COMM_TYPE_IN_424F 0x02 +#define PORT100_COMM_TYPE_IN_106A 0x03 +#define PORT100_COMM_TYPE_IN_106B 0x07 + +static const struct port100_in_rf_setting in_rf_settings[] = { + [NFC_DIGITAL_RF_TECH_212F] = { + .in_send_set_number = 1, + .in_send_comm_type = PORT100_COMM_TYPE_IN_212F, + .in_recv_set_number = 15, + .in_recv_comm_type = PORT100_COMM_TYPE_IN_212F, + }, + [NFC_DIGITAL_RF_TECH_424F] = { + .in_send_set_number = 1, + .in_send_comm_type = PORT100_COMM_TYPE_IN_424F, + .in_recv_set_number = 15, + .in_recv_comm_type = PORT100_COMM_TYPE_IN_424F, + }, + [NFC_DIGITAL_RF_TECH_106A] = { + .in_send_set_number = 2, + .in_send_comm_type = PORT100_COMM_TYPE_IN_106A, + .in_recv_set_number = 15, + .in_recv_comm_type = PORT100_COMM_TYPE_IN_106A, + }, + [NFC_DIGITAL_RF_TECH_106B] = { + .in_send_set_number = 3, + .in_send_comm_type = PORT100_COMM_TYPE_IN_106B, + .in_recv_set_number = 15, + .in_recv_comm_type = PORT100_COMM_TYPE_IN_106B, + }, + /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */ + [NFC_DIGITAL_RF_TECH_LAST] = { 0 }, +}; + +/** + * Setting sets structure for tg_set_rf command + * + * @tg_set_number: Represents the entry index in the port-100 RF Base Table. + * This table contains multiple RF setting sets required for RF + * communication. this field is used for both send and receive + * settings. + * + * @tg_comm_type: Sets the communication type to be used to send and receive + * data. + */ +struct port100_tg_rf_setting { + u8 tg_set_number; + u8 tg_comm_type; +} __packed; + +#define PORT100_COMM_TYPE_TG_106A 0x0B +#define PORT100_COMM_TYPE_TG_212F 0x0C +#define PORT100_COMM_TYPE_TG_424F 0x0D + +static const struct port100_tg_rf_setting tg_rf_settings[] = { + [NFC_DIGITAL_RF_TECH_106A] = { + .tg_set_number = 8, + .tg_comm_type = PORT100_COMM_TYPE_TG_106A, + }, + [NFC_DIGITAL_RF_TECH_212F] = { + .tg_set_number = 8, + .tg_comm_type = PORT100_COMM_TYPE_TG_212F, + }, + [NFC_DIGITAL_RF_TECH_424F] = { + .tg_set_number = 8, + .tg_comm_type = PORT100_COMM_TYPE_TG_424F, + }, + /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */ + [NFC_DIGITAL_RF_TECH_LAST] = { 0 }, + +}; + +#define PORT100_IN_PROT_INITIAL_GUARD_TIME 0x00 +#define PORT100_IN_PROT_ADD_CRC 0x01 +#define PORT100_IN_PROT_CHECK_CRC 0x02 +#define PORT100_IN_PROT_MULTI_CARD 0x03 +#define PORT100_IN_PROT_ADD_PARITY 0x04 +#define PORT100_IN_PROT_CHECK_PARITY 0x05 +#define PORT100_IN_PROT_BITWISE_AC_RECV_MODE 0x06 +#define PORT100_IN_PROT_VALID_BIT_NUMBER 0x07 +#define PORT100_IN_PROT_CRYPTO1 0x08 +#define PORT100_IN_PROT_ADD_SOF 0x09 +#define PORT100_IN_PROT_CHECK_SOF 0x0A +#define PORT100_IN_PROT_ADD_EOF 0x0B +#define PORT100_IN_PROT_CHECK_EOF 0x0C +#define PORT100_IN_PROT_DEAF_TIME 0x0E +#define PORT100_IN_PROT_CRM 0x0F +#define PORT100_IN_PROT_CRM_MIN_LEN 0x10 +#define PORT100_IN_PROT_T1_TAG_FRAME 0x11 +#define PORT100_IN_PROT_RFCA 0x12 +#define PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR 0x13 +#define PORT100_IN_PROT_END 0x14 + +#define PORT100_IN_MAX_NUM_PROTOCOLS 19 + +#define PORT100_TG_PROT_TU 0x00 +#define PORT100_TG_PROT_RF_OFF 0x01 +#define PORT100_TG_PROT_CRM 0x02 +#define PORT100_TG_PROT_END 0x03 + +#define PORT100_TG_MAX_NUM_PROTOCOLS 3 + +struct port100_protocol { + u8 number; + u8 value; +} __packed; + +static struct port100_protocol +in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = { + [NFC_DIGITAL_FRAMING_NFCA_SHORT] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 }, + { PORT100_IN_PROT_ADD_CRC, 0 }, + { PORT100_IN_PROT_CHECK_CRC, 0 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 0 }, + { PORT100_IN_PROT_CHECK_PARITY, 1 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 7 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 }, + { PORT100_IN_PROT_ADD_CRC, 0 }, + { PORT100_IN_PROT_CHECK_CRC, 0 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 1 }, + { PORT100_IN_PROT_CHECK_PARITY, 1 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 }, + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 1 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 1 }, + { PORT100_IN_PROT_CHECK_PARITY, 1 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T1T] = { + /* nfc_digital_framing_nfca_short */ + { PORT100_IN_PROT_ADD_CRC, 2 }, + { PORT100_IN_PROT_CHECK_CRC, 2 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 2 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T2T] = { + /* nfc_digital_framing_nfca_standard */ + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 0 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T4T] = { + /* nfc_digital_framing_nfca_standard_with_crc_a */ + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = { + /* nfc_digital_framing_nfca_standard */ + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 18 }, + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 1 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 0 }, + { PORT100_IN_PROT_CHECK_PARITY, 0 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF_T3T] = { + /* nfc_digital_framing_nfcf */ + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = { + /* nfc_digital_framing_nfcf */ + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = { + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCB] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 20 }, + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 1 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 0 }, + { PORT100_IN_PROT_CHECK_PARITY, 0 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 1 }, + { PORT100_IN_PROT_CHECK_SOF, 1 }, + { PORT100_IN_PROT_ADD_EOF, 1 }, + { PORT100_IN_PROT_CHECK_EOF, 1 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCB_T4T] = { + /* nfc_digital_framing_nfcb */ + { PORT100_IN_PROT_END, 0 }, + }, + /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */ + [NFC_DIGITAL_FRAMING_LAST] = { + { PORT100_IN_PROT_END, 0 }, + }, +}; + +static struct port100_protocol +tg_protocols[][PORT100_TG_MAX_NUM_PROTOCOLS + 1] = { + [NFC_DIGITAL_FRAMING_NFCA_SHORT] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T1T] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T2T] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = { + { PORT100_TG_PROT_TU, 1 }, + { PORT100_TG_PROT_RF_OFF, 0 }, + { PORT100_TG_PROT_CRM, 7 }, + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF_T3T] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = { + { PORT100_TG_PROT_TU, 1 }, + { PORT100_TG_PROT_RF_OFF, 0 }, + { PORT100_TG_PROT_CRM, 7 }, + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = { + { PORT100_TG_PROT_RF_OFF, 1 }, + { PORT100_TG_PROT_END, 0 }, + }, + /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */ + [NFC_DIGITAL_FRAMING_LAST] = { + { PORT100_TG_PROT_END, 0 }, + }, +}; + +struct port100 { + struct nfc_digital_dev *nfc_digital_dev; + + int skb_headroom; + int skb_tailroom; + + struct usb_device *udev; + struct usb_interface *interface; + + struct urb *out_urb; + struct urb *in_urb; + + struct work_struct cmd_complete_work; + + u8 cmd_type; + + /* The digital stack serializes commands to be sent. There is no need + * for any queuing/locking mechanism at driver level. + */ + struct port100_cmd *cmd; +}; + +struct port100_cmd { + u8 code; + int status; + struct sk_buff *req; + struct sk_buff *resp; + int resp_len; + port100_send_async_complete_t complete_cb; + void *complete_cb_context; +}; + +struct port100_frame { + u8 preamble; + __be16 start_frame; + __be16 extended_frame; + __le16 datalen; + u8 datalen_checksum; + u8 data[]; +} __packed; + +struct port100_ack_frame { + u8 preamble; + __be16 start_frame; + __be16 ack_frame; + u8 postambule; +} __packed; + +struct port100_cb_arg { + nfc_digital_cmd_complete_t complete_cb; + void *complete_arg; + u8 mdaa; +}; + +struct port100_tg_comm_rf_cmd { + __le16 guard_time; + __le16 send_timeout; + u8 mdaa; + u8 nfca_param[6]; + u8 nfcf_param[18]; + u8 mf_halted; + u8 arae_flag; + __le16 recv_timeout; + u8 data[]; +} __packed; + +struct port100_tg_comm_rf_res { + u8 comm_type; + u8 ar_status; + u8 target_activated; + __le32 status; + u8 data[]; +} __packed; + +/* The rule: value + checksum = 0 */ +static inline u8 port100_checksum(u16 value) +{ + return ~(((u8 *)&value)[0] + ((u8 *)&value)[1]) + 1; +} + +/* The rule: sum(data elements) + checksum = 0 */ +static u8 port100_data_checksum(u8 *data, int datalen) +{ + u8 sum = 0; + int i; + + for (i = 0; i < datalen; i++) + sum += data[i]; + + return port100_checksum(sum); +} + +static void port100_tx_frame_init(void *_frame, u8 cmd_code) +{ + struct port100_frame *frame = _frame; + + frame->preamble = 0; + frame->start_frame = cpu_to_be16(PORT100_FRAME_SOF); + frame->extended_frame = cpu_to_be16(PORT100_FRAME_EXT); + PORT100_FRAME_DIRECTION(frame) = PORT100_FRAME_DIR_OUT; + PORT100_FRAME_CMD(frame) = cmd_code; + frame->datalen = cpu_to_le16(2); +} + +static void port100_tx_frame_finish(void *_frame) +{ + struct port100_frame *frame = _frame; + + frame->datalen_checksum = port100_checksum(le16_to_cpu(frame->datalen)); + + PORT100_FRAME_CHECKSUM(frame) = + port100_data_checksum(frame->data, le16_to_cpu(frame->datalen)); + + PORT100_FRAME_POSTAMBLE(frame) = 0; +} + +static void port100_tx_update_payload_len(void *_frame, int len) +{ + struct port100_frame *frame = _frame; + + frame->datalen = cpu_to_le16(le16_to_cpu(frame->datalen) + len); +} + +static bool port100_rx_frame_is_valid(void *_frame) +{ + u8 checksum; + struct port100_frame *frame = _frame; + + if (frame->start_frame != cpu_to_be16(PORT100_FRAME_SOF) || + frame->extended_frame != cpu_to_be16(PORT100_FRAME_EXT)) + return false; + + checksum = port100_checksum(le16_to_cpu(frame->datalen)); + if (checksum != frame->datalen_checksum) + return false; + + checksum = port100_data_checksum(frame->data, + le16_to_cpu(frame->datalen)); + if (checksum != PORT100_FRAME_CHECKSUM(frame)) + return false; + + return true; +} + +static bool port100_rx_frame_is_ack(struct port100_ack_frame *frame) +{ + return (frame->start_frame == cpu_to_be16(PORT100_FRAME_SOF) && + frame->ack_frame == cpu_to_be16(PORT100_FRAME_ACK)); +} + +static inline int port100_rx_frame_size(void *frame) +{ + struct port100_frame *f = frame; + + return sizeof(struct port100_frame) + le16_to_cpu(f->datalen) + + PORT100_FRAME_TAIL_LEN; +} + +static bool port100_rx_frame_is_cmd_response(struct port100 *dev, void *frame) +{ + struct port100_frame *f = frame; + + return (PORT100_FRAME_CMD(f) == PORT100_CMD_RESPONSE(dev->cmd->code)); +} + +static void port100_recv_response(struct urb *urb) +{ + struct port100 *dev = urb->context; + struct port100_cmd *cmd = dev->cmd; + u8 *in_frame; + + cmd->status = urb->status; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + nfc_err(&dev->interface->dev, + "The urb has been canceled (status %d)\n", urb->status); + goto sched_wq; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", + urb->status); + goto sched_wq; + } + + in_frame = dev->in_urb->transfer_buffer; + + if (!port100_rx_frame_is_valid(in_frame)) { + nfc_err(&dev->interface->dev, "Received an invalid frame\n"); + cmd->status = -EIO; + goto sched_wq; + } + + print_hex_dump_debug("PORT100 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame, + port100_rx_frame_size(in_frame), false); + + if (!port100_rx_frame_is_cmd_response(dev, in_frame)) { + nfc_err(&dev->interface->dev, + "It's not the response to the last command\n"); + cmd->status = -EIO; + goto sched_wq; + } + +sched_wq: + schedule_work(&dev->cmd_complete_work); +} + +static int port100_submit_urb_for_response(struct port100 *dev, gfp_t flags) +{ + dev->in_urb->complete = port100_recv_response; + + return usb_submit_urb(dev->in_urb, flags); +} + +static void port100_recv_ack(struct urb *urb) +{ + struct port100 *dev = urb->context; + struct port100_cmd *cmd = dev->cmd; + struct port100_ack_frame *in_frame; + int rc; + + cmd->status = urb->status; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + nfc_err(&dev->interface->dev, + "The urb has been stopped (status %d)\n", urb->status); + goto sched_wq; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", + urb->status); + goto sched_wq; + } + + in_frame = dev->in_urb->transfer_buffer; + + if (!port100_rx_frame_is_ack(in_frame)) { + nfc_err(&dev->interface->dev, "Received an invalid ack\n"); + cmd->status = -EIO; + goto sched_wq; + } + + rc = port100_submit_urb_for_response(dev, GFP_ATOMIC); + if (rc) { + nfc_err(&dev->interface->dev, + "usb_submit_urb failed with result %d\n", rc); + cmd->status = rc; + goto sched_wq; + } + + return; + +sched_wq: + schedule_work(&dev->cmd_complete_work); +} + +static int port100_submit_urb_for_ack(struct port100 *dev, gfp_t flags) +{ + dev->in_urb->complete = port100_recv_ack; + + return usb_submit_urb(dev->in_urb, flags); +} + +static int port100_send_ack(struct port100 *dev) +{ + int rc; + + dev->out_urb->transfer_buffer = ack_frame; + dev->out_urb->transfer_buffer_length = sizeof(ack_frame); + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); + + return rc; +} + +static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out, + struct sk_buff *in, int in_len) +{ + int rc; + + dev->out_urb->transfer_buffer = out->data; + dev->out_urb->transfer_buffer_length = out->len; + + dev->in_urb->transfer_buffer = in->data; + dev->in_urb->transfer_buffer_length = in_len; + + print_hex_dump_debug("PORT100 TX: ", DUMP_PREFIX_NONE, 16, 1, + out->data, out->len, false); + + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); + if (rc) + return rc; + + rc = port100_submit_urb_for_ack(dev, GFP_KERNEL); + if (rc) + goto error; + + return 0; + +error: + usb_unlink_urb(dev->out_urb); + return rc; +} + +static void port100_build_cmd_frame(struct port100 *dev, u8 cmd_code, + struct sk_buff *skb) +{ + /* payload is already there, just update datalen */ + int payload_len = skb->len; + + skb_push(skb, PORT100_FRAME_HEADER_LEN); + skb_put(skb, PORT100_FRAME_TAIL_LEN); + + port100_tx_frame_init(skb->data, cmd_code); + port100_tx_update_payload_len(skb->data, payload_len); + port100_tx_frame_finish(skb->data); +} + +static void port100_send_async_complete(struct port100 *dev) +{ + struct port100_cmd *cmd = dev->cmd; + int status = cmd->status; + + struct sk_buff *req = cmd->req; + struct sk_buff *resp = cmd->resp; + + dev_kfree_skb(req); + + dev->cmd = NULL; + + if (status < 0) { + cmd->complete_cb(dev, cmd->complete_cb_context, + ERR_PTR(status)); + dev_kfree_skb(resp); + goto done; + } + + skb_put(resp, port100_rx_frame_size(resp->data)); + skb_pull(resp, PORT100_FRAME_HEADER_LEN); + skb_trim(resp, resp->len - PORT100_FRAME_TAIL_LEN); + + cmd->complete_cb(dev, cmd->complete_cb_context, resp); + +done: + kfree(cmd); +} + +static int port100_send_cmd_async(struct port100 *dev, u8 cmd_code, + struct sk_buff *req, + port100_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct port100_cmd *cmd; + struct sk_buff *resp; + int rc; + int resp_len = PORT100_FRAME_HEADER_LEN + + PORT100_FRAME_MAX_PAYLOAD_LEN + + PORT100_FRAME_TAIL_LEN; + + resp = alloc_skb(resp_len, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + dev_kfree_skb(resp); + return -ENOMEM; + } + + cmd->code = cmd_code; + cmd->req = req; + cmd->resp = resp; + cmd->resp_len = resp_len; + cmd->complete_cb = complete_cb; + cmd->complete_cb_context = complete_cb_context; + + port100_build_cmd_frame(dev, cmd_code, req); + + dev->cmd = cmd; + + rc = port100_send_frame_async(dev, req, resp, resp_len); + if (rc) { + kfree(cmd); + dev_kfree_skb(resp); + dev->cmd = NULL; + } + + return rc; +} + +struct port100_sync_cmd_response { + struct sk_buff *resp; + struct completion done; +}; + +static void port100_wq_cmd_complete(struct work_struct *work) +{ + struct port100 *dev = container_of(work, struct port100, + cmd_complete_work); + + port100_send_async_complete(dev); +} + +static void port100_send_sync_complete(struct port100 *dev, void *_arg, + struct sk_buff *resp) +{ + struct port100_sync_cmd_response *arg = _arg; + + arg->resp = resp; + complete(&arg->done); +} + +static struct sk_buff *port100_send_cmd_sync(struct port100 *dev, u8 cmd_code, + struct sk_buff *req) +{ + int rc; + struct port100_sync_cmd_response arg; + + init_completion(&arg.done); + + rc = port100_send_cmd_async(dev, cmd_code, req, + port100_send_sync_complete, &arg); + if (rc) { + dev_kfree_skb(req); + return ERR_PTR(rc); + } + + wait_for_completion(&arg.done); + + return arg.resp; +} + +static void port100_send_complete(struct urb *urb) +{ + struct port100 *dev = urb->context; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + nfc_err(&dev->interface->dev, + "The urb has been stopped (status %d)\n", urb->status); + break; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", + urb->status); + } +} + +static void port100_abort_cmd(struct nfc_digital_dev *ddev) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + + /* An ack will cancel the last issued command */ + port100_send_ack(dev); + + /* cancel the urb request */ + usb_kill_urb(dev->in_urb); +} + +static struct sk_buff *port100_alloc_skb(struct port100 *dev, unsigned int size) +{ + struct sk_buff *skb; + + skb = alloc_skb(dev->skb_headroom + dev->skb_tailroom + size, + GFP_KERNEL); + if (skb) + skb_reserve(skb, dev->skb_headroom); + + return skb; +} + +static int port100_set_command_type(struct port100 *dev, u8 command_type) +{ + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + + skb = port100_alloc_skb(dev, 1); + if (!skb) + return -ENOMEM; + + *skb_put(skb, sizeof(u8)) = command_type; + + resp = port100_send_cmd_sync(dev, PORT100_CMD_SET_COMMAND_TYPE, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static u64 port100_get_command_type_mask(struct port100 *dev) +{ + struct sk_buff *skb; + struct sk_buff *resp; + u64 mask; + + skb = port100_alloc_skb(dev, 0); + if (!skb) + return -ENOMEM; + + resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_COMMAND_TYPE, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + if (resp->len < 8) + mask = 0; + else + mask = be64_to_cpu(*(__be64 *)resp->data); + + dev_kfree_skb(resp); + + return mask; +} + +static u16 port100_get_firmware_version(struct port100 *dev) +{ + struct sk_buff *skb; + struct sk_buff *resp; + u16 fw_ver; + + skb = port100_alloc_skb(dev, 0); + if (!skb) + return 0; + + resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_FIRMWARE_VERSION, + skb); + if (IS_ERR(resp)) + return 0; + + fw_ver = le16_to_cpu(*(__le16 *)resp->data); + + dev_kfree_skb(resp); + + return fw_ver; +} + +static int port100_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct sk_buff *skb, *resp; + + skb = port100_alloc_skb(dev, 1); + if (!skb) + return -ENOMEM; + + *skb_put(skb, 1) = on ? 1 : 0; + + resp = port100_send_cmd_sync(dev, PORT100_CMD_SWITCH_RF, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + dev_kfree_skb(resp); + + return 0; +} + +static int port100_in_set_rf(struct nfc_digital_dev *ddev, u8 rf) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + + if (rf >= NFC_DIGITAL_RF_TECH_LAST) + return -EINVAL; + + skb = port100_alloc_skb(dev, sizeof(struct port100_in_rf_setting)); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, sizeof(struct port100_in_rf_setting)), + &in_rf_settings[rf], + sizeof(struct port100_in_rf_setting)); + + resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_RF, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static int port100_in_set_framing(struct nfc_digital_dev *ddev, int param) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_protocol *protocols; + struct sk_buff *skb; + struct sk_buff *resp; + int num_protocols; + size_t size; + int rc; + + if (param >= NFC_DIGITAL_FRAMING_LAST) + return -EINVAL; + + protocols = in_protocols[param]; + + num_protocols = 0; + while (protocols[num_protocols].number != PORT100_IN_PROT_END) + num_protocols++; + + if (!num_protocols) + return 0; + + size = sizeof(struct port100_protocol) * num_protocols; + + skb = port100_alloc_skb(dev, size); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, size), protocols, size); + + resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_PROTOCOL, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static int port100_in_configure_hw(struct nfc_digital_dev *ddev, int type, + int param) +{ + if (type == NFC_DIGITAL_CONFIG_RF_TECH) + return port100_in_set_rf(ddev, param); + + if (type == NFC_DIGITAL_CONFIG_FRAMING) + return port100_in_set_framing(ddev, param); + + return -EINVAL; +} + +static void port100_in_comm_rf_complete(struct port100 *dev, void *arg, + struct sk_buff *resp) +{ + struct port100_cb_arg *cb_arg = arg; + nfc_digital_cmd_complete_t cb = cb_arg->complete_cb; + u32 status; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + goto exit; + } + + if (resp->len < 4) { + nfc_err(&dev->interface->dev, + "Invalid packet length received\n"); + rc = -EIO; + goto error; + } + + status = le32_to_cpu(*(__le32 *)resp->data); + + skb_pull(resp, sizeof(u32)); + + if (status == PORT100_CMD_STATUS_TIMEOUT) { + rc = -ETIMEDOUT; + goto error; + } + + if (status != PORT100_CMD_STATUS_OK) { + nfc_err(&dev->interface->dev, + "in_comm_rf failed with status 0x%08x\n", status); + rc = -EIO; + goto error; + } + + /* Remove collision bits byte */ + skb_pull(resp, 1); + + goto exit; + +error: + kfree_skb(resp); + resp = ERR_PTR(rc); + +exit: + cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp); + + kfree(cb_arg); +} + +static int port100_in_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 _timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_cb_arg *cb_arg; + __le16 timeout; + + cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL); + if (!cb_arg) + return -ENOMEM; + + cb_arg->complete_cb = cb; + cb_arg->complete_arg = arg; + + timeout = cpu_to_le16(_timeout * 10); + + memcpy(skb_push(skb, sizeof(__le16)), &timeout, sizeof(__le16)); + + return port100_send_cmd_async(dev, PORT100_CMD_IN_COMM_RF, skb, + port100_in_comm_rf_complete, cb_arg); +} + +static int port100_tg_set_rf(struct nfc_digital_dev *ddev, u8 rf) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + + if (rf >= NFC_DIGITAL_RF_TECH_LAST) + return -EINVAL; + + skb = port100_alloc_skb(dev, sizeof(struct port100_tg_rf_setting)); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, sizeof(struct port100_tg_rf_setting)), + &tg_rf_settings[rf], + sizeof(struct port100_tg_rf_setting)); + + resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_RF, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static int port100_tg_set_framing(struct nfc_digital_dev *ddev, int param) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_protocol *protocols; + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + int num_protocols; + size_t size; + + if (param >= NFC_DIGITAL_FRAMING_LAST) + return -EINVAL; + + protocols = tg_protocols[param]; + + num_protocols = 0; + while (protocols[num_protocols].number != PORT100_TG_PROT_END) + num_protocols++; + + if (!num_protocols) + return 0; + + size = sizeof(struct port100_protocol) * num_protocols; + + skb = port100_alloc_skb(dev, size); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, size), protocols, size); + + resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_PROTOCOL, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static int port100_tg_configure_hw(struct nfc_digital_dev *ddev, int type, + int param) +{ + if (type == NFC_DIGITAL_CONFIG_RF_TECH) + return port100_tg_set_rf(ddev, param); + + if (type == NFC_DIGITAL_CONFIG_FRAMING) + return port100_tg_set_framing(ddev, param); + + return -EINVAL; +} + +static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated) +{ + u8 mask; + + switch (dev->cmd_type) { + case PORT100_CMD_TYPE_0: + mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK; + break; + case PORT100_CMD_TYPE_1: + mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK | + PORT100_MDAA_TGT_WAS_ACTIVATED_MASK; + break; + default: + nfc_err(&dev->interface->dev, "Unknown command type\n"); + return false; + } + + return ((tgt_activated & mask) == mask); +} + +static void port100_tg_comm_rf_complete(struct port100 *dev, void *arg, + struct sk_buff *resp) +{ + u32 status; + struct port100_cb_arg *cb_arg = arg; + nfc_digital_cmd_complete_t cb = cb_arg->complete_cb; + struct port100_tg_comm_rf_res *hdr; + + if (IS_ERR(resp)) + goto exit; + + hdr = (struct port100_tg_comm_rf_res *)resp->data; + + status = le32_to_cpu(hdr->status); + + if (cb_arg->mdaa && + !port100_tg_target_activated(dev, hdr->target_activated)) { + kfree_skb(resp); + resp = ERR_PTR(-ETIMEDOUT); + + goto exit; + } + + skb_pull(resp, sizeof(struct port100_tg_comm_rf_res)); + + if (status != PORT100_CMD_STATUS_OK) { + kfree_skb(resp); + + if (status == PORT100_CMD_STATUS_TIMEOUT) + resp = ERR_PTR(-ETIMEDOUT); + else + resp = ERR_PTR(-EIO); + } + +exit: + cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp); + + kfree(cb_arg); +} + +static int port100_tg_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_tg_comm_rf_cmd *hdr; + struct port100_cb_arg *cb_arg; + + cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL); + if (!cb_arg) + return -ENOMEM; + + cb_arg->complete_cb = cb; + cb_arg->complete_arg = arg; + + skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd)); + + hdr = (struct port100_tg_comm_rf_cmd *)skb->data; + + memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd)); + hdr->guard_time = cpu_to_le16(500); + hdr->send_timeout = cpu_to_le16(0xFFFF); + hdr->recv_timeout = cpu_to_le16(timeout); + + return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb, + port100_tg_comm_rf_complete, cb_arg); +} + +static int port100_listen_mdaa(struct nfc_digital_dev *ddev, + struct digital_tg_mdaa_params *params, + u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_tg_comm_rf_cmd *hdr; + struct port100_cb_arg *cb_arg; + struct sk_buff *skb; + int rc; + + rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, + NFC_DIGITAL_RF_TECH_106A); + if (rc) + return rc; + + rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_NFC_DEP); + if (rc) + return rc; + + cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL); + if (!cb_arg) + return -ENOMEM; + + cb_arg->complete_cb = cb; + cb_arg->complete_arg = arg; + cb_arg->mdaa = 1; + + skb = port100_alloc_skb(dev, 0); + if (!skb) { + kfree(cb_arg); + return -ENOMEM; + } + + skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd)); + hdr = (struct port100_tg_comm_rf_cmd *)skb->data; + + memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd)); + + hdr->guard_time = 0; + hdr->send_timeout = cpu_to_le16(0xFFFF); + hdr->mdaa = 1; + hdr->nfca_param[0] = (params->sens_res >> 8) & 0xFF; + hdr->nfca_param[1] = params->sens_res & 0xFF; + memcpy(hdr->nfca_param + 2, params->nfcid1, 3); + hdr->nfca_param[5] = params->sel_res; + memcpy(hdr->nfcf_param, params->nfcid2, 8); + hdr->nfcf_param[16] = (params->sc >> 8) & 0xFF; + hdr->nfcf_param[17] = params->sc & 0xFF; + hdr->recv_timeout = cpu_to_le16(timeout); + + return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb, + port100_tg_comm_rf_complete, cb_arg); +} + +static int port100_listen(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct sk_buff *skb; + + skb = port100_alloc_skb(dev, 0); + if (!skb) + return -ENOMEM; + + return port100_tg_send_cmd(ddev, skb, timeout, cb, arg); +} + +static struct nfc_digital_ops port100_digital_ops = { + .in_configure_hw = port100_in_configure_hw, + .in_send_cmd = port100_in_send_cmd, + + .tg_listen_mdaa = port100_listen_mdaa, + .tg_listen = port100_listen, + .tg_configure_hw = port100_tg_configure_hw, + .tg_send_cmd = port100_tg_send_cmd, + + .switch_rf = port100_switch_rf, + .abort_cmd = port100_abort_cmd, +}; + +static const struct usb_device_id port100_table[] = { + { USB_DEVICE(SONY_VENDOR_ID, RCS380_PRODUCT_ID), }, + { } +}; +MODULE_DEVICE_TABLE(usb, port100_table); + +static int port100_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct port100 *dev; + int rc; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int in_endpoint; + int out_endpoint; + u16 fw_version; + u64 cmd_type_mask; + int i; + + dev = devm_kzalloc(&interface->dev, sizeof(struct port100), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + usb_set_intfdata(interface, dev); + + in_endpoint = out_endpoint = 0; + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint)) + in_endpoint = endpoint->bEndpointAddress; + + if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) + out_endpoint = endpoint->bEndpointAddress; + } + + if (!in_endpoint || !out_endpoint) { + nfc_err(&interface->dev, + "Could not find bulk-in or bulk-out endpoint\n"); + rc = -ENODEV; + goto error; + } + + dev->in_urb = usb_alloc_urb(0, GFP_KERNEL); + dev->out_urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!dev->in_urb || !dev->out_urb) { + nfc_err(&interface->dev, "Could not allocate USB URBs\n"); + rc = -ENOMEM; + goto error; + } + + usb_fill_bulk_urb(dev->in_urb, dev->udev, + usb_rcvbulkpipe(dev->udev, in_endpoint), + NULL, 0, NULL, dev); + usb_fill_bulk_urb(dev->out_urb, dev->udev, + usb_sndbulkpipe(dev->udev, out_endpoint), + NULL, 0, port100_send_complete, dev); + + dev->skb_headroom = PORT100_FRAME_HEADER_LEN + + PORT100_COMM_RF_HEAD_MAX_LEN; + dev->skb_tailroom = PORT100_FRAME_TAIL_LEN; + + INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete); + + /* The first thing to do with the Port-100 is to set the command type + * to be used. If supported we use command type 1. 0 otherwise. + */ + cmd_type_mask = port100_get_command_type_mask(dev); + if (!cmd_type_mask) { + nfc_err(&interface->dev, + "Could not get supported command types\n"); + rc = -ENODEV; + goto error; + } + + if (PORT100_CMD_TYPE_IS_SUPPORTED(cmd_type_mask, PORT100_CMD_TYPE_1)) + dev->cmd_type = PORT100_CMD_TYPE_1; + else + dev->cmd_type = PORT100_CMD_TYPE_0; + + rc = port100_set_command_type(dev, dev->cmd_type); + if (rc) { + nfc_err(&interface->dev, + "The device does not support command type %u\n", + dev->cmd_type); + goto error; + } + + fw_version = port100_get_firmware_version(dev); + if (!fw_version) + nfc_err(&interface->dev, + "Could not get device firmware version\n"); + + nfc_info(&interface->dev, + "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n", + (fw_version & 0xFF00) >> 8, fw_version & 0xFF); + + dev->nfc_digital_dev = nfc_digital_allocate_device(&port100_digital_ops, + PORT100_PROTOCOLS, + PORT100_CAPABILITIES, + dev->skb_headroom, + dev->skb_tailroom); + if (!dev->nfc_digital_dev) { + nfc_err(&interface->dev, + "Could not allocate nfc_digital_dev\n"); + rc = -ENOMEM; + goto error; + } + + nfc_digital_set_parent_dev(dev->nfc_digital_dev, &interface->dev); + nfc_digital_set_drvdata(dev->nfc_digital_dev, dev); + + rc = nfc_digital_register_device(dev->nfc_digital_dev); + if (rc) { + nfc_err(&interface->dev, + "Could not register digital device\n"); + goto free_nfc_dev; + } + + return 0; + +free_nfc_dev: + nfc_digital_free_device(dev->nfc_digital_dev); + +error: + usb_free_urb(dev->in_urb); + usb_free_urb(dev->out_urb); + usb_put_dev(dev->udev); + + return rc; +} + +static void port100_disconnect(struct usb_interface *interface) +{ + struct port100 *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + nfc_digital_unregister_device(dev->nfc_digital_dev); + nfc_digital_free_device(dev->nfc_digital_dev); + + usb_kill_urb(dev->in_urb); + usb_kill_urb(dev->out_urb); + + usb_free_urb(dev->in_urb); + usb_free_urb(dev->out_urb); + usb_put_dev(dev->udev); + + kfree(dev->cmd); + + nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected\n"); +} + +static struct usb_driver port100_driver = { + .name = "port100", + .probe = port100_probe, + .disconnect = port100_disconnect, + .id_table = port100_table, +}; + +module_usb_driver(port100_driver); + +MODULE_DESCRIPTION("NFC Port-100 series usb driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig new file mode 100644 index 000000000..ee459f066 --- /dev/null +++ b/drivers/nfc/st21nfca/Kconfig @@ -0,0 +1,23 @@ +config NFC_ST21NFCA + tristate "STMicroelectronics ST21NFCA NFC driver" + depends on NFC_HCI + select CRC_CCITT + default n + ---help--- + STMicroelectronics ST21NFCA core driver. It implements the chipset + HCI logic and hooks into the NFC kernel APIs. Physical layers will + register against it. + + To compile this driver as a module, choose m here. The module will + be called st21nfca. + Say N if unsure. + +config NFC_ST21NFCA_I2C + tristate "NFC ST21NFCA i2c support" + depends on NFC_ST21NFCA && I2C && NFC_SHDLC + ---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..97edab4bb --- /dev/null +++ b/drivers/nfc/st21nfca/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for ST21NFCA HCI based NFC driver +# + +st21nfca_hci-objs = st21nfca.o st21nfca_dep.o st21nfca_se.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/i2c.c b/drivers/nfc/st21nfca/i2c.c new file mode 100644 index 000000000..a32143951 --- /dev/null +++ b/drivers/nfc/st21nfca/i2c.c @@ -0,0 +1,695 @@ +/* + * 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.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/miscdevice.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/nfc.h> +#include <linux/firmware.h> +#include <linux/platform_data/st21nfca.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_I2C_DRIVER_NAME "st21nfca_hci_i2c" + +static struct i2c_device_id st21nfca_hci_i2c_id_table[] = { + {ST21NFCA_HCI_DRIVER_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); + +struct st21nfca_i2c_phy { + struct i2c_client *i2c_dev; + struct nfc_hci_dev *hdev; + + unsigned int gpio_ena; + unsigned int irq_polarity; + + 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; + + gpio_set_value(phy->gpio_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; + + pr_info("\n"); + gpio_set_value(phy->gpio_ena, 0); + + phy->powered = 0; +} + +static void st21nfca_hci_add_len_crc(struct sk_buff *skb) +{ + u16 crc; + u8 tmp; + + *skb_push(skb, 1) = 0; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + + tmp = crc & 0x00ff; + *skb_put(skb, 1) = tmp; + + tmp = (crc >> 8) & 0x00ff; + *skb_put(skb, 1) = 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(skb, 1) = ST21NFCA_SOF_EOF; + /* add ST21NFCA_SOF_EOF on head */ + *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; + } + + memcpy(skb_put(skb, len), 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, +}; + +#ifdef CONFIG_OF +static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) +{ + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int gpio; + int r; + + pp = client->dev.of_node; + if (!pp) + return -ENODEV; + + /* Get GPIO from device tree */ + gpio = of_get_named_gpio(pp, "enable-gpios", 0); + if (gpio < 0) { + nfc_err(&client->dev, "Failed to retrieve enable-gpios from device tree\n"); + return gpio; + } + + /* GPIO request and configuration */ + r = devm_gpio_request_one(&client->dev, gpio, GPIOF_OUT_INIT_HIGH, + "clf_enable"); + if (r) { + nfc_err(&client->dev, "Failed to request enable pin\n"); + return r; + } + + phy->gpio_ena = gpio; + + phy->irq_polarity = irq_get_trigger_type(client->irq); + + phy->se_status.is_ese_present = + of_property_read_bool(pp, "ese-present"); + phy->se_status.is_uicc_present = + of_property_read_bool(pp, "uicc-present"); + + return 0; +} +#else +static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) +{ + return -ENODEV; +} +#endif + +static int st21nfca_hci_i2c_request_resources(struct i2c_client *client) +{ + struct st21nfca_nfc_platform_data *pdata; + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + int r; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + /* store for later use */ + phy->gpio_ena = pdata->gpio_ena; + phy->irq_polarity = pdata->irq_polarity; + + if (phy->gpio_ena > 0) { + r = devm_gpio_request_one(&client->dev, phy->gpio_ena, + GPIOF_OUT_INIT_HIGH, "clf_enable"); + if (r) { + pr_err("%s : ena gpio_request failed\n", __FILE__); + return r; + } + } + + phy->se_status.is_ese_present = pdata->is_ese_present; + phy->se_status.is_uicc_present = pdata->is_uicc_present; + + return 0; +} + +static int st21nfca_hci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct st21nfca_i2c_phy *phy; + struct st21nfca_nfc_platform_data *pdata; + 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); + + pdata = client->dev.platform_data; + if (!pdata && client->dev.of_node) { + r = st21nfca_hci_i2c_of_request_resources(client); + if (r) { + nfc_err(&client->dev, "No platform data\n"); + return r; + } + } else if (pdata) { + r = st21nfca_hci_i2c_request_resources(client); + if (r) { + nfc_err(&client->dev, "Cannot get platform resources\n"); + return r; + } + } else { + nfc_err(&client->dev, "st21nfca platform resources not available\n"); + return -ENODEV; + } + + r = st21nfca_hci_platform_init(phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to reboot st21nfca\n"); + return r; + } + + r = devm_request_threaded_irq(&client->dev, client->irq, NULL, + st21nfca_hci_irq_thread_fn, + phy->irq_polarity | IRQF_ONESHOT, + ST21NFCA_HCI_DRIVER_NAME, phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + return r; + } + + return 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); +} + +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); + + return 0; +} + +#ifdef CONFIG_OF +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); +#endif + +static struct i2c_driver st21nfca_hci_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = ST21NFCA_HCI_I2C_DRIVER_NAME, + .of_match_table = of_match_ptr(of_st21nfca_i2c_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/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c new file mode 100644 index 000000000..d251f7229 --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca.c @@ -0,0 +1,1063 @@ +/* + * 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" +#include "st21nfca_dep.h" +#include "st21nfca_se.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_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE}, + {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_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) + goto free_info; + + /* 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) + goto free_info; + + /* 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 != ST21NFCA_ESE_HOST_ID) { + pr_err("Unexpected apdu_reader pipe on host %x\n", + info->src_host_id); + continue; + } + + for (j = 0; (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)) { + st21nfca_gates[j].pipe = pipe_info[2]; + + hdev->gate2pipe[st21nfca_gates[j].gate] = + st21nfca_gates[j].pipe; + hdev->pipes[st21nfca_gates[j].pipe].gate = + st21nfca_gates[j].gate; + hdev->pipes[st21nfca_gates[j].pipe].dest_host = + info->src_host_id; + } + } + + /* + * 3 gates have a well known pipe ID. + * They will never appear in the pipe list + */ + if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) { + for (i = skb_pipe_list->len + 3; + i < ARRAY_SIZE(st21nfca_gates) - 2; i++) { + r = nfc_hci_connect_gate(hdev, + NFC_HCI_HOST_CONTROLLER_ID, + st21nfca_gates[i].gate, + st21nfca_gates[i].pipe); + if (r < 0) + goto free_info; + } + } + + memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates)); +free_info: + kfree_skb(skb_pipe_info); + 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_ese_present && + info->se_status->is_uicc_present) { + white_list[wl_size++] = NFC_HCI_UICC_HOST_ID; + white_list[wl_size++] = ST21NFCA_ESE_HOST_ID; + } else if (!info->se_status->is_ese_present && + info->se_status->is_uicc_present) { + white_list[wl_size++] = NFC_HCI_UICC_HOST_ID; + } else if (info->se_status->is_ese_present && + !info->se_status->is_uicc_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); + /* 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); + + *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: + *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; + + *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; + } + 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); + 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) { + r = -ENOMEM; + goto err_alloc_hdev; + } + + 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) + return -ENODEV; + + 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); + + 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/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h new file mode 100644 index 000000000..15a78d330 --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca.h @@ -0,0 +1,99 @@ +/* + * 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 "st21nfca_dep.h" +#include "st21nfca_se.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) + +#define DRIVER_DESC "HCI NFC driver for ST21NFCA" + +#define ST21NFCA_HCI_MODE 0 + +#define ST21NFCA_NUM_DEVICES 256 + +struct st21nfca_se_status { + bool is_ese_present; + bool is_uicc_present; +}; + +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); + +enum st21nfca_state { + ST21NFCA_ST_COLD, + ST21NFCA_ST_READY, +}; + +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; +}; + +/* 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 + +#endif /* __LOCAL_ST21NFCA_H_ */ diff --git a/drivers/nfc/st21nfca/st21nfca_dep.c b/drivers/nfc/st21nfca/st21nfca_dep.c new file mode 100644 index 000000000..8882181d6 --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca_dep.c @@ -0,0 +1,689 @@ +/* + * 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" +#include "st21nfca_dep.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) + 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); + + *skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni; + *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_RES; + *skb_push(skb, 1) = ST21NFCA_NFCIP1_RES; + *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: + 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; + + *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; + memcpy(skb_put(skb, gb_len), gb, gb_len); + } + atr_req->length = sizeof(struct st21nfca_atr_req) + hdev->gb_len; + + *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); + *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ; + *skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ; + *skb_push(skb, 1) = skb->len; + *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; + + *skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni; + *skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ; + *skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ; + *skb_push(skb, 1) = skb->len; + + *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/st21nfca_dep.h b/drivers/nfc/st21nfca/st21nfca_dep.h new file mode 100644 index 000000000..baf4664b4 --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca_dep.h @@ -0,0 +1,43 @@ +/* + * 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 __ST21NFCA_DEP_H +#define __ST21NFCA_DEP_H + +#include <linux/skbuff.h> +#include <linux/workqueue.h> + +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; + +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); +#endif /* __ST21NFCA_DEP_H */ diff --git a/drivers/nfc/st21nfca/st21nfca_se.c b/drivers/nfc/st21nfca/st21nfca_se.c new file mode 100644 index 000000000..3197e9bb6 --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca_se.c @@ -0,0 +1,420 @@ +/* + * 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" +#include "st21nfca_se.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_ESE_HOST_ID 0xc0 + +#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; + 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; + + host_id = sk_host_list->data[sk_host_list->len - 1]; + 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 (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_timeout(unsigned long data) +{ + /* + * 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 = (struct st21nfca_hci_info *) data; + + 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_activation_timeout(unsigned long data) +{ + struct st21nfca_hci_info *info = (struct st21nfca_hci_info *) data; + + 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: + 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); + + transaction->aid_len = skb->data[1]; + 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) + return -EPROTO; + + transaction->params_len = skb->data[transaction->aid_len + 3]; + memcpy(transaction->params, skb->data + + transaction->aid_len + 4, transaction->params_len); + + r = nfc_se_transaction(hdev->ndev, host, transaction); + break; + default: + 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); + 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; + } + +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); + /* initialize timers */ + init_timer(&info->se_info.bwi_timer); + info->se_info.bwi_timer.data = (unsigned long)info; + info->se_info.bwi_timer.function = st21nfca_se_wt_timeout; + info->se_info.bwi_active = false; + + init_timer(&info->se_info.se_active_timer); + info->se_info.se_active_timer.data = (unsigned long)info; + info->se_info.se_active_timer.function = st21nfca_se_activation_timeout; + 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); + + info->se_info.bwi_active = false; + info->se_info.se_active = false; +} +EXPORT_SYMBOL(st21nfca_se_deinit); diff --git a/drivers/nfc/st21nfca/st21nfca_se.h b/drivers/nfc/st21nfca/st21nfca_se.h new file mode 100644 index 000000000..b172cfcae --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca_se.h @@ -0,0 +1,63 @@ +/* + * 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 __ST21NFCA_SE_H +#define __ST21NFCA_SE_H + +#include <linux/skbuff.h> +#include <linux/workqueue.h> + +/* + * 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 + +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; +}; + +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); +#endif /* __ST21NFCA_SE_H */ diff --git a/drivers/nfc/st21nfcb/Kconfig b/drivers/nfc/st21nfcb/Kconfig new file mode 100644 index 000000000..e0322dd03 --- /dev/null +++ b/drivers/nfc/st21nfcb/Kconfig @@ -0,0 +1,22 @@ +config NFC_ST21NFCB + tristate "STMicroelectronics ST21NFCB NFC driver" + depends on NFC_NCI + default n + ---help--- + STMicroelectronics ST21NFCB core driver. It implements the chipset + NCI logic and hooks into the NFC kernel APIs. Physical layers will + register against it. + + To compile this driver as a module, choose m here. The module will + be called st21nfcb. + Say N if unsure. + +config NFC_ST21NFCB_I2C + tristate "NFC ST21NFCB i2c support" + depends on NFC_ST21NFCB && I2C + ---help--- + This module adds support for the STMicroelectronics st21nfcb i2c interface. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called st21nfcb_i2c. + Say N if unsure. diff --git a/drivers/nfc/st21nfcb/Makefile b/drivers/nfc/st21nfcb/Makefile new file mode 100644 index 000000000..ce659a9e5 --- /dev/null +++ b/drivers/nfc/st21nfcb/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for ST21NFCB NCI based NFC driver +# + +st21nfcb_nci-objs = ndlc.o st21nfcb.o st21nfcb_se.o +obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb_nci.o + +st21nfcb_i2c-objs = i2c.o +obj-$(CONFIG_NFC_ST21NFCB_I2C) += st21nfcb_i2c.o diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c new file mode 100644 index 000000000..c44f8cf53 --- /dev/null +++ b/drivers/nfc/st21nfcb/i2c.c @@ -0,0 +1,388 @@ +/* + * I2C Link Layer for ST21NFCB NCI 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/module.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/nfc.h> +#include <linux/platform_data/st21nfcb.h> + +#include "ndlc.h" + +#define DRIVER_DESC "NCI NFC driver for ST21NFCB" + +/* ndlc header */ +#define ST21NFCB_FRAME_HEADROOM 1 +#define ST21NFCB_FRAME_TAILROOM 0 + +#define ST21NFCB_NCI_I2C_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ +#define ST21NFCB_NCI_I2C_MAX_SIZE 250 /* req 4.2.1 */ + +#define ST21NFCB_NCI_I2C_DRIVER_NAME "st21nfcb_nci_i2c" + +static struct i2c_device_id st21nfcb_nci_i2c_id_table[] = { + {ST21NFCB_NCI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st21nfcb_nci_i2c_id_table); + +struct st21nfcb_i2c_phy { + struct i2c_client *i2c_dev; + struct llt_ndlc *ndlc; + + unsigned int gpio_reset; + unsigned int irq_polarity; + + int powered; +}; + +#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) + +static int st21nfcb_nci_i2c_enable(void *phy_id) +{ + struct st21nfcb_i2c_phy *phy = phy_id; + + gpio_set_value(phy->gpio_reset, 0); + usleep_range(10000, 15000); + gpio_set_value(phy->gpio_reset, 1); + phy->powered = 1; + usleep_range(80000, 85000); + + return 0; +} + +static void st21nfcb_nci_i2c_disable(void *phy_id) +{ + struct st21nfcb_i2c_phy *phy = phy_id; + + phy->powered = 0; + /* reset chip in order to flush clf */ + gpio_set_value(phy->gpio_reset, 0); + usleep_range(10000, 15000); + gpio_set_value(phy->gpio_reset, 1); +} + +/* + * 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 st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r = -1; + struct st21nfcb_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + + I2C_DUMP_SKB("st21nfcb_nci_i2c_write", skb); + + if (phy->ndlc->hard_fault != 0) + return phy->ndlc->hard_fault; + + r = i2c_master_send(client, skb->data, skb->len); + if (r < 0) { /* Retry, chip was in standby */ + usleep_range(1000, 4000); + r = i2c_master_send(client, skb->data, skb->len); + } + + if (r >= 0) { + if (r != skb->len) + r = -EREMOTEIO; + else + r = 0; + } + + return r; +} + +/* + * Reads an ndlc frame and returns it in a newly allocated sk_buff. + * returns: + * frame size : if received frame is complete (find ST21NFCB_SOF_EOF at + * end of read) + * -EAGAIN : if received frame is incomplete (not find ST21NFCB_SOF_EOF + * at end of read) + * -EREMOTEIO : i2c read error (fatal) + * -EBADMSG : frame was incorrect and discarded + * (value returned from st21nfcb_nci_i2c_repack) + * -EIO : if no ST21NFCB_SOF_EOF is found after reaching + * the read length end sequence + */ +static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, + struct sk_buff **skb) +{ + int r; + u8 len; + u8 buf[ST21NFCB_NCI_I2C_MAX_SIZE]; + struct i2c_client *client = phy->i2c_dev; + + r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); + if (r < 0) { /* Retry, chip was in standby */ + usleep_range(1000, 4000); + r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); + } + + if (r != ST21NFCB_NCI_I2C_MIN_SIZE) + return -EREMOTEIO; + + len = be16_to_cpu(*(__be16 *) (buf + 2)); + if (len > ST21NFCB_NCI_I2C_MAX_SIZE) { + nfc_err(&client->dev, "invalid frame len\n"); + return -EBADMSG; + } + + *skb = alloc_skb(ST21NFCB_NCI_I2C_MIN_SIZE + len, GFP_KERNEL); + if (*skb == NULL) + return -ENOMEM; + + skb_reserve(*skb, ST21NFCB_NCI_I2C_MIN_SIZE); + skb_put(*skb, ST21NFCB_NCI_I2C_MIN_SIZE); + memcpy((*skb)->data, buf, ST21NFCB_NCI_I2C_MIN_SIZE); + + if (!len) + return 0; + + r = i2c_master_recv(client, buf, len); + if (r != len) { + kfree_skb(*skb); + return -EREMOTEIO; + } + + skb_put(*skb, len); + memcpy((*skb)->data + ST21NFCB_NCI_I2C_MIN_SIZE, buf, len); + + I2C_DUMP_SKB("i2c frame read", *skb); + + return 0; +} + +/* + * Reads an ndlc frame from the chip. + * + * On ST21NFCB, IRQ goes in idle state when read starts. + */ +static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id) +{ + struct st21nfcb_i2c_phy *phy = phy_id; + struct i2c_client *client; + struct sk_buff *skb = NULL; + int r; + + if (!phy || !phy->ndlc || irq != phy->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + + client = phy->i2c_dev; + dev_dbg(&client->dev, "IRQ\n"); + + if (phy->ndlc->hard_fault) + return IRQ_HANDLED; + + if (!phy->powered) { + st21nfcb_nci_i2c_disable(phy); + return IRQ_HANDLED; + } + + r = st21nfcb_nci_i2c_read(phy, &skb); + if (r == -EREMOTEIO || r == -ENOMEM || r == -EBADMSG) + return IRQ_HANDLED; + + ndlc_recv(phy->ndlc, skb); + + return IRQ_HANDLED; +} + +static struct nfc_phy_ops i2c_phy_ops = { + .write = st21nfcb_nci_i2c_write, + .enable = st21nfcb_nci_i2c_enable, + .disable = st21nfcb_nci_i2c_disable, +}; + +#ifdef CONFIG_OF +static int st21nfcb_nci_i2c_of_request_resources(struct i2c_client *client) +{ + struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int gpio; + int r; + + pp = client->dev.of_node; + if (!pp) + return -ENODEV; + + /* Get GPIO from device tree */ + gpio = of_get_named_gpio(pp, "reset-gpios", 0); + if (gpio < 0) { + nfc_err(&client->dev, + "Failed to retrieve reset-gpios from device tree\n"); + return gpio; + } + + /* GPIO request and configuration */ + r = devm_gpio_request_one(&client->dev, gpio, + GPIOF_OUT_INIT_HIGH, "clf_reset"); + if (r) { + nfc_err(&client->dev, "Failed to request reset pin\n"); + return r; + } + phy->gpio_reset = gpio; + + phy->irq_polarity = irq_get_trigger_type(client->irq); + + return 0; +} +#else +static int st21nfcb_nci_i2c_of_request_resources(struct i2c_client *client) +{ + return -ENODEV; +} +#endif + +static int st21nfcb_nci_i2c_request_resources(struct i2c_client *client) +{ + struct st21nfcb_nfc_platform_data *pdata; + struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + int r; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + /* store for later use */ + phy->gpio_reset = pdata->gpio_reset; + phy->irq_polarity = pdata->irq_polarity; + + r = devm_gpio_request_one(&client->dev, + phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset"); + if (r) { + pr_err("%s : reset gpio_request failed\n", __FILE__); + return r; + } + + return 0; +} + +static int st21nfcb_nci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct st21nfcb_i2c_phy *phy; + struct st21nfcb_nfc_platform_data *pdata; + 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 st21nfcb_i2c_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->i2c_dev = client; + + i2c_set_clientdata(client, phy); + + pdata = client->dev.platform_data; + if (!pdata && client->dev.of_node) { + r = st21nfcb_nci_i2c_of_request_resources(client); + if (r) { + nfc_err(&client->dev, "No platform data\n"); + return r; + } + } else if (pdata) { + r = st21nfcb_nci_i2c_request_resources(client); + if (r) { + nfc_err(&client->dev, + "Cannot get platform resources\n"); + return r; + } + } else { + nfc_err(&client->dev, + "st21nfcb platform resources not available\n"); + return -ENODEV; + } + + r = ndlc_probe(phy, &i2c_phy_ops, &client->dev, + ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM, + &phy->ndlc); + if (r < 0) { + nfc_err(&client->dev, "Unable to register ndlc layer\n"); + return r; + } + + r = devm_request_threaded_irq(&client->dev, client->irq, NULL, + st21nfcb_nci_irq_thread_fn, + phy->irq_polarity | IRQF_ONESHOT, + ST21NFCB_NCI_DRIVER_NAME, phy); + if (r < 0) + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + + return r; +} + +static int st21nfcb_nci_i2c_remove(struct i2c_client *client) +{ + struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __func__); + + ndlc_remove(phy->ndlc); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id of_st21nfcb_i2c_match[] = { + { .compatible = "st,st21nfcb-i2c", }, + { .compatible = "st,st21nfcb_i2c", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_st21nfcb_i2c_match); +#endif + +static struct i2c_driver st21nfcb_nci_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = ST21NFCB_NCI_I2C_DRIVER_NAME, + .of_match_table = of_match_ptr(of_st21nfcb_i2c_match), + }, + .probe = st21nfcb_nci_i2c_probe, + .id_table = st21nfcb_nci_i2c_id_table, + .remove = st21nfcb_nci_i2c_remove, +}; + +module_i2c_driver(st21nfcb_nci_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c new file mode 100644 index 000000000..6014b5859 --- /dev/null +++ b/drivers/nfc/st21nfcb/ndlc.c @@ -0,0 +1,300 @@ +/* + * Low Level Transport (NDLC) 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/sched.h> +#include <net/nfc/nci_core.h> + +#include "ndlc.h" +#include "st21nfcb.h" + +#define NDLC_TIMER_T1 100 +#define NDLC_TIMER_T1_WAIT 400 +#define NDLC_TIMER_T2 1200 + +#define PCB_TYPE_DATAFRAME 0x80 +#define PCB_TYPE_SUPERVISOR 0xc0 +#define PCB_TYPE_MASK PCB_TYPE_SUPERVISOR + +#define PCB_SYNC_ACK 0x20 +#define PCB_SYNC_NACK 0x10 +#define PCB_SYNC_WAIT 0x30 +#define PCB_SYNC_NOINFO 0x00 +#define PCB_SYNC_MASK PCB_SYNC_WAIT + +#define PCB_DATAFRAME_RETRANSMIT_YES 0x00 +#define PCB_DATAFRAME_RETRANSMIT_NO 0x04 +#define PCB_DATAFRAME_RETRANSMIT_MASK PCB_DATAFRAME_RETRANSMIT_NO + +#define PCB_SUPERVISOR_RETRANSMIT_YES 0x00 +#define PCB_SUPERVISOR_RETRANSMIT_NO 0x02 +#define PCB_SUPERVISOR_RETRANSMIT_MASK PCB_SUPERVISOR_RETRANSMIT_NO + +#define PCB_FRAME_CRC_INFO_PRESENT 0x08 +#define PCB_FRAME_CRC_INFO_NOTPRESENT 0x00 +#define PCB_FRAME_CRC_INFO_MASK PCB_FRAME_CRC_INFO_PRESENT + +#define NDLC_DUMP_SKB(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \ + 16, 1, skb->data, skb->len, 0); \ +} while (0) + +int ndlc_open(struct llt_ndlc *ndlc) +{ + /* toggle reset pin */ + ndlc->ops->enable(ndlc->phy_id); + return 0; +} +EXPORT_SYMBOL(ndlc_open); + +void ndlc_close(struct llt_ndlc *ndlc) +{ + /* toggle reset pin */ + ndlc->ops->disable(ndlc->phy_id); +} +EXPORT_SYMBOL(ndlc_close); + +int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb) +{ + /* add ndlc header */ + u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO | + PCB_FRAME_CRC_INFO_NOTPRESENT; + + *skb_push(skb, 1) = pcb; + skb_queue_tail(&ndlc->send_q, skb); + + schedule_work(&ndlc->sm_work); + + return 0; +} +EXPORT_SYMBOL(ndlc_send); + +static void llt_ndlc_send_queue(struct llt_ndlc *ndlc) +{ + struct sk_buff *skb; + int r; + unsigned long time_sent; + + if (ndlc->send_q.qlen) + pr_debug("sendQlen=%d unackQlen=%d\n", + ndlc->send_q.qlen, ndlc->ack_pending_q.qlen); + + while (ndlc->send_q.qlen) { + skb = skb_dequeue(&ndlc->send_q); + NDLC_DUMP_SKB("ndlc frame written", skb); + r = ndlc->ops->write(ndlc->phy_id, skb); + if (r < 0) { + ndlc->hard_fault = r; + break; + } + time_sent = jiffies; + *(unsigned long *)skb->cb = time_sent; + + skb_queue_tail(&ndlc->ack_pending_q, skb); + + /* start timer t1 for ndlc aknowledge */ + ndlc->t1_active = true; + mod_timer(&ndlc->t1_timer, time_sent + + msecs_to_jiffies(NDLC_TIMER_T1)); + /* start timer t2 for chip availability */ + ndlc->t2_active = true; + mod_timer(&ndlc->t2_timer, time_sent + + msecs_to_jiffies(NDLC_TIMER_T2)); + } +} + +static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc) +{ + struct sk_buff *skb; + u8 pcb; + + while ((skb = skb_dequeue_tail(&ndlc->ack_pending_q))) { + pcb = skb->data[0]; + switch (pcb & PCB_TYPE_MASK) { + case PCB_TYPE_SUPERVISOR: + skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) | + PCB_SUPERVISOR_RETRANSMIT_YES; + break; + case PCB_TYPE_DATAFRAME: + skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) | + PCB_DATAFRAME_RETRANSMIT_YES; + break; + default: + pr_err("UNKNOWN Packet Control Byte=%d\n", pcb); + kfree_skb(skb); + continue; + } + skb_queue_head(&ndlc->send_q, skb); + } +} + +static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc) +{ + struct sk_buff *skb; + u8 pcb; + unsigned long time_sent; + + if (ndlc->rcv_q.qlen) + pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen); + + while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) { + pcb = skb->data[0]; + skb_pull(skb, 1); + if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) { + switch (pcb & PCB_SYNC_MASK) { + case PCB_SYNC_ACK: + del_timer_sync(&ndlc->t1_timer); + del_timer_sync(&ndlc->t2_timer); + ndlc->t2_active = false; + ndlc->t1_active = false; + break; + case PCB_SYNC_NACK: + llt_ndlc_requeue_data_pending(ndlc); + llt_ndlc_send_queue(ndlc); + /* start timer t1 for ndlc aknowledge */ + time_sent = jiffies; + ndlc->t1_active = true; + mod_timer(&ndlc->t1_timer, time_sent + + msecs_to_jiffies(NDLC_TIMER_T1)); + break; + case PCB_SYNC_WAIT: + time_sent = jiffies; + ndlc->t1_active = true; + mod_timer(&ndlc->t1_timer, time_sent + + msecs_to_jiffies(NDLC_TIMER_T1_WAIT)); + break; + default: + pr_err("UNKNOWN Packet Control Byte=%d\n", pcb); + kfree_skb(skb); + break; + } + } else { + nci_recv_frame(ndlc->ndev, skb); + } + } +} + +static void llt_ndlc_sm_work(struct work_struct *work) +{ + struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work); + + llt_ndlc_send_queue(ndlc); + llt_ndlc_rcv_queue(ndlc); + + if (ndlc->t1_active && timer_pending(&ndlc->t1_timer) == 0) { + pr_debug + ("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n"); + ndlc->t1_active = false; + + llt_ndlc_requeue_data_pending(ndlc); + llt_ndlc_send_queue(ndlc); + } + + if (ndlc->t2_active && timer_pending(&ndlc->t2_timer) == 0) { + pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n"); + ndlc->t2_active = false; + ndlc->t1_active = false; + del_timer_sync(&ndlc->t1_timer); + del_timer_sync(&ndlc->t2_timer); + ndlc_close(ndlc); + ndlc->hard_fault = -EREMOTEIO; + } +} + +void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb) +{ + if (skb == NULL) { + pr_err("NULL Frame -> link is dead\n"); + ndlc->hard_fault = -EREMOTEIO; + ndlc_close(ndlc); + } else { + NDLC_DUMP_SKB("incoming frame", skb); + skb_queue_tail(&ndlc->rcv_q, skb); + } + + schedule_work(&ndlc->sm_work); +} +EXPORT_SYMBOL(ndlc_recv); + +static void ndlc_t1_timeout(unsigned long data) +{ + struct llt_ndlc *ndlc = (struct llt_ndlc *)data; + + pr_debug("\n"); + + schedule_work(&ndlc->sm_work); +} + +static void ndlc_t2_timeout(unsigned long data) +{ + struct llt_ndlc *ndlc = (struct llt_ndlc *)data; + + pr_debug("\n"); + + schedule_work(&ndlc->sm_work); +} + +int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, + int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id) +{ + struct llt_ndlc *ndlc; + + ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL); + if (!ndlc) + return -ENOMEM; + + ndlc->ops = phy_ops; + ndlc->phy_id = phy_id; + ndlc->dev = dev; + + *ndlc_id = ndlc; + + /* initialize timers */ + init_timer(&ndlc->t1_timer); + ndlc->t1_timer.data = (unsigned long)ndlc; + ndlc->t1_timer.function = ndlc_t1_timeout; + + init_timer(&ndlc->t2_timer); + ndlc->t2_timer.data = (unsigned long)ndlc; + ndlc->t2_timer.function = ndlc_t2_timeout; + + skb_queue_head_init(&ndlc->rcv_q); + skb_queue_head_init(&ndlc->send_q); + skb_queue_head_init(&ndlc->ack_pending_q); + + INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work); + + return st21nfcb_nci_probe(ndlc, phy_headroom, phy_tailroom); +} +EXPORT_SYMBOL(ndlc_probe); + +void ndlc_remove(struct llt_ndlc *ndlc) +{ + /* cancel timers */ + del_timer_sync(&ndlc->t1_timer); + del_timer_sync(&ndlc->t2_timer); + ndlc->t2_active = false; + ndlc->t1_active = false; + + skb_queue_purge(&ndlc->rcv_q); + skb_queue_purge(&ndlc->send_q); + + st21nfcb_nci_remove(ndlc->ndev); +} +EXPORT_SYMBOL(ndlc_remove); diff --git a/drivers/nfc/st21nfcb/ndlc.h b/drivers/nfc/st21nfcb/ndlc.h new file mode 100644 index 000000000..b28140e0c --- /dev/null +++ b/drivers/nfc/st21nfcb/ndlc.h @@ -0,0 +1,59 @@ +/* + * NCI 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/>. + */ + +#ifndef __LOCAL_NDLC_H_ +#define __LOCAL_NDLC_H_ + +#include <linux/skbuff.h> +#include <net/nfc/nfc.h> + +/* Low Level Transport description */ +struct llt_ndlc { + struct nci_dev *ndev; + struct nfc_phy_ops *ops; + void *phy_id; + + struct timer_list t1_timer; + bool t1_active; + + struct timer_list t2_timer; + bool t2_active; + + struct sk_buff_head rcv_q; + struct sk_buff_head send_q; + struct sk_buff_head ack_pending_q; + + struct work_struct sm_work; + + struct device *dev; + + /* + * < 0 if hardware error occured + * and prevents normal operation. + */ + int hard_fault; +}; + +int ndlc_open(struct llt_ndlc *ndlc); +void ndlc_close(struct llt_ndlc *ndlc); +int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb); +void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb); +int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, + int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id); +void ndlc_remove(struct llt_ndlc *ndlc); +#endif /* __LOCAL_NDLC_H__ */ diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c new file mode 100644 index 000000000..c7dc282d5 --- /dev/null +++ b/drivers/nfc/st21nfcb/st21nfcb.c @@ -0,0 +1,140 @@ +/* + * NCI 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/nci.h> +#include <net/nfc/nci_core.h> + +#include "st21nfcb.h" +#include "st21nfcb_se.h" + +#define DRIVER_DESC "NCI NFC driver for ST21NFCB" + +#define ST21NFCB_NCI1_X_PROPRIETARY_ISO15693 0x83 + +static int st21nfcb_nci_open(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + int r; + + if (test_and_set_bit(ST21NFCB_NCI_RUNNING, &info->flags)) + return 0; + + r = ndlc_open(info->ndlc); + if (r) + clear_bit(ST21NFCB_NCI_RUNNING, &info->flags); + + return r; +} + +static int st21nfcb_nci_close(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + if (!test_and_clear_bit(ST21NFCB_NCI_RUNNING, &info->flags)) + return 0; + + ndlc_close(info->ndlc); + + return 0; +} + +static int st21nfcb_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + skb->dev = (void *)ndev; + + if (!test_bit(ST21NFCB_NCI_RUNNING, &info->flags)) + return -EBUSY; + + return ndlc_send(info->ndlc, skb); +} + +static __u32 st21nfcb_nci_get_rfprotocol(struct nci_dev *ndev, + __u8 rf_protocol) +{ + return rf_protocol == ST21NFCB_NCI1_X_PROPRIETARY_ISO15693 ? + NFC_PROTO_ISO15693_MASK : 0; +} + +static struct nci_ops st21nfcb_nci_ops = { + .open = st21nfcb_nci_open, + .close = st21nfcb_nci_close, + .send = st21nfcb_nci_send, + .get_rfprotocol = st21nfcb_nci_get_rfprotocol, + .discover_se = st21nfcb_nci_discover_se, + .enable_se = st21nfcb_nci_enable_se, + .disable_se = st21nfcb_nci_disable_se, + .se_io = st21nfcb_nci_se_io, + .hci_load_session = st21nfcb_hci_load_session, + .hci_event_received = st21nfcb_hci_event_received, + .hci_cmd_received = st21nfcb_hci_cmd_received, +}; + +int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, + int phy_tailroom) +{ + struct st21nfcb_nci_info *info; + int r; + u32 protocols; + + info = devm_kzalloc(ndlc->dev, + sizeof(struct st21nfcb_nci_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + 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; + + ndlc->ndev = nci_allocate_device(&st21nfcb_nci_ops, protocols, + phy_headroom, phy_tailroom); + if (!ndlc->ndev) { + pr_err("Cannot allocate nfc ndev\n"); + return -ENOMEM; + } + info->ndlc = ndlc; + + nci_set_drvdata(ndlc->ndev, info); + + r = nci_register_device(ndlc->ndev); + if (r) { + pr_err("Cannot register nfc device to nci core\n"); + nci_free_device(ndlc->ndev); + return r; + } + + return st21nfcb_se_init(ndlc->ndev); +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_probe); + +void st21nfcb_nci_remove(struct nci_dev *ndev) +{ + nci_unregister_device(ndev); + nci_free_device(ndev); +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st21nfcb/st21nfcb.h new file mode 100644 index 000000000..5ef8a58c9 --- /dev/null +++ b/drivers/nfc/st21nfcb/st21nfcb.h @@ -0,0 +1,38 @@ +/* + * NCI 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/>. + */ + +#ifndef __LOCAL_ST21NFCB_H_ +#define __LOCAL_ST21NFCB_H_ + +#include "st21nfcb_se.h" +#include "ndlc.h" + +/* Define private flags: */ +#define ST21NFCB_NCI_RUNNING 1 + +struct st21nfcb_nci_info { + struct llt_ndlc *ndlc; + unsigned long flags; + struct st21nfcb_se_info se_info; +}; + +void st21nfcb_nci_remove(struct nci_dev *ndev); +int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, + int phy_tailroom); + +#endif /* __LOCAL_ST21NFCB_H_ */ diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.c b/drivers/nfc/st21nfcb/st21nfcb_se.c new file mode 100644 index 000000000..24862a525 --- /dev/null +++ b/drivers/nfc/st21nfcb/st21nfcb_se.c @@ -0,0 +1,713 @@ +/* + * NCI 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 <linux/delay.h> +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> + +#include "st21nfcb.h" +#include "st21nfcb_se.h" + +struct st21nfcb_pipe_info { + u8 pipe_state; + u8 src_host_id; + u8 src_gate_id; + u8 dst_host_id; + u8 dst_gate_id; +} __packed; + +/* Hosts */ +#define ST21NFCB_HOST_CONTROLLER_ID 0x00 +#define ST21NFCB_TERMINAL_HOST_ID 0x01 +#define ST21NFCB_UICC_HOST_ID 0x02 +#define ST21NFCB_ESE_HOST_ID 0xc0 + +/* Gates */ +#define ST21NFCB_DEVICE_MGNT_GATE 0x01 +#define ST21NFCB_APDU_READER_GATE 0xf0 +#define ST21NFCB_CONNECTIVITY_GATE 0x41 + +/* Pipes */ +#define ST21NFCB_DEVICE_MGNT_PIPE 0x02 + +/* Connectivity pipe only */ +#define ST21NFCB_SE_COUNT_PIPE_UICC 0x01 +/* Connectivity + APDU Reader pipe */ +#define ST21NFCB_SE_COUNT_PIPE_EMBEDDED 0x02 + +#define ST21NFCB_SE_TO_HOT_PLUG 1000 /* msecs */ +#define ST21NFCB_SE_TO_PIPES 2000 + +#define ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80) + +#define NCI_HCI_APDU_PARAM_ATR 0x01 +#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01 +#define NCI_HCI_ADMIN_PARAM_WHITELIST 0x03 +#define NCI_HCI_ADMIN_PARAM_HOST_LIST 0x04 + +#define ST21NFCB_EVT_SE_HARD_RESET 0x20 +#define ST21NFCB_EVT_TRANSMIT_DATA 0x10 +#define ST21NFCB_EVT_WTX_REQUEST 0x11 +#define ST21NFCB_EVT_SE_SOFT_RESET 0x11 +#define ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER 0x21 +#define ST21NFCB_EVT_HOT_PLUG 0x03 + +#define ST21NFCB_SE_MODE_OFF 0x00 +#define ST21NFCB_SE_MODE_ON 0x01 + +#define ST21NFCB_EVT_CONNECTIVITY 0x10 +#define ST21NFCB_EVT_TRANSACTION 0x12 + +#define ST21NFCB_DM_GETINFO 0x13 +#define ST21NFCB_DM_GETINFO_PIPE_LIST 0x02 +#define ST21NFCB_DM_GETINFO_PIPE_INFO 0x01 +#define ST21NFCB_DM_PIPE_CREATED 0x02 +#define ST21NFCB_DM_PIPE_OPEN 0x04 +#define ST21NFCB_DM_RF_ACTIVE 0x80 +#define ST21NFCB_DM_DISCONNECT 0x30 + +#define ST21NFCB_DM_IS_PIPE_OPEN(p) \ + ((p & 0x0f) == (ST21NFCB_DM_PIPE_CREATED | ST21NFCB_DM_PIPE_OPEN)) + +#define ST21NFCB_ATR_DEFAULT_BWI 0x04 + +/* + * WT = 2^BWI/10[s], convert into msecs and add a secure + * room by increasing by 2 this timeout + */ +#define ST21NFCB_BWI_TO_TIMEOUT(x) ((1 << x) * 200) +#define ST21NFCB_ATR_GET_Y_FROM_TD(x) (x >> 4) + +/* If TA is present bit 0 is set */ +#define ST21NFCB_ATR_TA_PRESENT(x) (x & 0x01) +/* If TB is present bit 1 is set */ +#define ST21NFCB_ATR_TB_PRESENT(x) (x & 0x02) + +#define ST21NFCB_NUM_DEVICES 256 + +static DECLARE_BITMAP(dev_mask, ST21NFCB_NUM_DEVICES); + +/* Here are the mandatory pipe for st21nfcb */ +static struct nci_hci_gate st21nfcb_gates[] = { + {NCI_HCI_ADMIN_GATE, NCI_HCI_ADMIN_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, + {NCI_HCI_LINK_MGMT_GATE, NCI_HCI_LINK_MGMT_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, + {ST21NFCB_DEVICE_MGNT_GATE, ST21NFCB_DEVICE_MGNT_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, + + /* Secure element pipes are created by secure element host */ + {ST21NFCB_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, + {ST21NFCB_APDU_READER_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, +}; + +static u8 st21nfcb_se_get_bwi(struct nci_dev *ndev) +{ + int i; + u8 td; + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + /* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */ + for (i = 1; i < ST21NFCB_ESE_MAX_LENGTH; i++) { + td = ST21NFCB_ATR_GET_Y_FROM_TD(info->se_info.atr[i]); + if (ST21NFCB_ATR_TA_PRESENT(td)) + i++; + if (ST21NFCB_ATR_TB_PRESENT(td)) { + i++; + return info->se_info.atr[i] >> 4; + } + } + return ST21NFCB_ATR_DEFAULT_BWI; +} + +static void st21nfcb_se_get_atr(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + int r; + struct sk_buff *skb; + + r = nci_hci_get_param(ndev, ST21NFCB_APDU_READER_GATE, + NCI_HCI_APDU_PARAM_ATR, &skb); + if (r < 0) + return; + + if (skb->len <= ST21NFCB_ESE_MAX_LENGTH) { + memcpy(info->se_info.atr, skb->data, skb->len); + + info->se_info.wt_timeout = + ST21NFCB_BWI_TO_TIMEOUT(st21nfcb_se_get_bwi(ndev)); + } + kfree_skb(skb); +} + +int st21nfcb_hci_load_session(struct nci_dev *ndev) +{ + int i, j, r; + struct sk_buff *skb_pipe_list, *skb_pipe_info; + struct st21nfcb_pipe_info *dm_pipe_info; + u8 pipe_list[] = { ST21NFCB_DM_GETINFO_PIPE_LIST, + ST21NFCB_TERMINAL_HOST_ID}; + u8 pipe_info[] = { ST21NFCB_DM_GETINFO_PIPE_INFO, + ST21NFCB_TERMINAL_HOST_ID, 0}; + + /* On ST21NFCB device pipes number are dynamics + * 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 ST21NFCB is to: + * - get a pipe list for each host. + * (eg: ST21NFCB_HOST_CONTROLLER_ID for now). + * (TODO Later on UICC HOST and eSE HOST) + * - get pipe information + * - match retrieved pipe list in st21nfcb_gates + * ST21NFCB_DEVICE_MGNT_GATE is a proprietary gate + * with ST21NFCB_DEVICE_MGNT_PIPE. + * Pipe can be closed and need to be open. + */ + r = nci_hci_connect_gate(ndev, ST21NFCB_HOST_CONTROLLER_ID, + ST21NFCB_DEVICE_MGNT_GATE, + ST21NFCB_DEVICE_MGNT_PIPE); + if (r < 0) + goto free_info; + + /* Get pipe list */ + r = nci_hci_send_cmd(ndev, ST21NFCB_DEVICE_MGNT_GATE, + ST21NFCB_DM_GETINFO, pipe_list, sizeof(pipe_list), + &skb_pipe_list); + if (r < 0) + goto free_info; + + /* Complete the existing gate_pipe table */ + for (i = 0; i < skb_pipe_list->len; i++) { + pipe_info[2] = skb_pipe_list->data[i]; + r = nci_hci_send_cmd(ndev, ST21NFCB_DEVICE_MGNT_GATE, + ST21NFCB_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) + */ + dm_pipe_info = (struct st21nfcb_pipe_info *)skb_pipe_info->data; + if (dm_pipe_info->dst_gate_id == ST21NFCB_APDU_READER_GATE && + dm_pipe_info->src_host_id != ST21NFCB_ESE_HOST_ID) { + pr_err("Unexpected apdu_reader pipe on host %x\n", + dm_pipe_info->src_host_id); + continue; + } + + for (j = 0; (j < ARRAY_SIZE(st21nfcb_gates)) && + (st21nfcb_gates[j].gate != dm_pipe_info->dst_gate_id); j++) + ; + + if (j < ARRAY_SIZE(st21nfcb_gates) && + st21nfcb_gates[j].gate == dm_pipe_info->dst_gate_id && + ST21NFCB_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) { + st21nfcb_gates[j].pipe = pipe_info[2]; + + ndev->hci_dev->gate2pipe[st21nfcb_gates[j].gate] = + st21nfcb_gates[j].pipe; + ndev->hci_dev->pipes[st21nfcb_gates[j].pipe].gate = + st21nfcb_gates[j].gate; + ndev->hci_dev->pipes[st21nfcb_gates[j].pipe].host = + dm_pipe_info->src_host_id; + } + } + + memcpy(ndev->hci_dev->init_data.gates, st21nfcb_gates, + sizeof(st21nfcb_gates)); + +free_info: + kfree_skb(skb_pipe_info); + kfree_skb(skb_pipe_list); + return r; +} +EXPORT_SYMBOL_GPL(st21nfcb_hci_load_session); + +static void st21nfcb_hci_admin_event_received(struct nci_dev *ndev, + u8 event, struct sk_buff *skb) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + switch (event) { + case ST21NFCB_EVT_HOT_PLUG: + if (info->se_info.se_active) { + if (!ST21NFCB_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(ST21NFCB_SE_TO_PIPES)); + } + } + break; + } +} + +static int st21nfcb_hci_apdu_reader_event_received(struct nci_dev *ndev, + u8 event, + struct sk_buff *skb) +{ + int r = 0; + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + pr_debug("apdu reader gate event: %x\n", event); + + switch (event) { + case ST21NFCB_EVT_TRANSMIT_DATA: + del_timer_sync(&info->se_info.bwi_timer); + info->se_info.bwi_active = false; + info->se_info.cb(info->se_info.cb_context, + skb->data, skb->len, 0); + break; + case ST21NFCB_EVT_WTX_REQUEST: + mod_timer(&info->se_info.bwi_timer, jiffies + + msecs_to_jiffies(info->se_info.wt_timeout)); + break; + } + + kfree_skb(skb); + return r; +} + +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ +static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, + u8 host, u8 event, + struct sk_buff *skb) +{ + int r = 0; + struct device *dev = &ndev->nfc_dev->dev; + struct nfc_evt_transaction *transaction; + + pr_debug("connectivity gate event: %x\n", event); + + switch (event) { + case ST21NFCB_EVT_CONNECTIVITY: + + break; + case ST21NFCB_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); + + transaction->aid_len = skb->data[1]; + 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) + return -EPROTO; + + transaction->params_len = skb->data[transaction->aid_len + 3]; + memcpy(transaction->params, skb->data + + transaction->aid_len + 4, transaction->params_len); + + r = nfc_se_transaction(ndev->nfc_dev, host, transaction); + break; + default: + return 1; + } + kfree_skb(skb); + return r; +} + +void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe, + u8 event, struct sk_buff *skb) +{ + u8 gate = ndev->hci_dev->pipes[pipe].gate; + u8 host = ndev->hci_dev->pipes[pipe].host; + + switch (gate) { + case NCI_HCI_ADMIN_GATE: + st21nfcb_hci_admin_event_received(ndev, event, skb); + break; + case ST21NFCB_APDU_READER_GATE: + st21nfcb_hci_apdu_reader_event_received(ndev, event, skb); + break; + case ST21NFCB_CONNECTIVITY_GATE: + st21nfcb_hci_connectivity_event_received(ndev, host, event, + skb); + break; + } +} +EXPORT_SYMBOL_GPL(st21nfcb_hci_event_received); + + +void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, + struct sk_buff *skb) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + u8 gate = ndev->hci_dev->pipes[pipe].gate; + + pr_debug("cmd: %x\n", cmd); + + switch (cmd) { + case NCI_HCI_ANY_OPEN_PIPE: + if (gate != ST21NFCB_APDU_READER_GATE && + ndev->hci_dev->pipes[pipe].host != ST21NFCB_UICC_HOST_ID) + ndev->hci_dev->count_pipes++; + + if (ndev->hci_dev->count_pipes == + ndev->hci_dev->expected_pipes) { + del_timer_sync(&info->se_info.se_active_timer); + info->se_info.se_active = false; + ndev->hci_dev->count_pipes = 0; + complete(&info->se_info.req_completion); + } + break; + } +} +EXPORT_SYMBOL_GPL(st21nfcb_hci_cmd_received); + +/* + * Remarks: On some early st21nfcb firmware, nci_nfcee_mode_set(0) + * is rejected + */ +static int st21nfcb_nci_control_se(struct nci_dev *ndev, u8 se_idx, + u8 state) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + int r; + struct sk_buff *sk_host_list; + u8 host_id; + + switch (se_idx) { + case ST21NFCB_UICC_HOST_ID: + ndev->hci_dev->count_pipes = 0; + ndev->hci_dev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_UICC; + break; + case ST21NFCB_ESE_HOST_ID: + ndev->hci_dev->count_pipes = 0; + ndev->hci_dev->expected_pipes = ST21NFCB_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 = nci_nfcee_mode_set(ndev, se_idx, NCI_NFCEE_ENABLE); + if (r != NCI_STATUS_OK) + return r; + + mod_timer(&info->se_info.se_active_timer, jiffies + + msecs_to_jiffies(ST21NFCB_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); + + /* There might be some "collision" after receiving a HOT_PLUG event + * This may cause the CLF to not answer to the next hci command. + * There is no possible synchronization to prevent this. + * Adding a small delay is the only way to solve the issue. + */ + usleep_range(3000, 5000); + + r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADMIN_PARAM_HOST_LIST, &sk_host_list); + if (r != NCI_HCI_ANY_OK) + return r; + + host_id = sk_host_list->data[sk_host_list->len - 1]; + kfree_skb(sk_host_list); + if (state == ST21NFCB_SE_MODE_ON && host_id == se_idx) + return se_idx; + else if (state == ST21NFCB_SE_MODE_OFF && host_id != se_idx) + return se_idx; + + return -1; +} + +int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx) +{ + int r; + + pr_debug("st21nfcb_nci_disable_se\n"); + + if (se_idx == NFC_SE_EMBEDDED) { + r = nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, + ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0); + if (r < 0) + return r; + } + + return 0; +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_disable_se); + +int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx) +{ + int r; + + pr_debug("st21nfcb_nci_enable_se\n"); + + if (se_idx == ST21NFCB_HCI_HOST_ID_ESE) { + r = nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, + ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0); + if (r < 0) + return r; + } + + return 0; +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_enable_se); + +static int st21nfcb_hci_network_init(struct nci_dev *ndev) +{ + struct core_conn_create_dest_spec_params *dest_params; + struct dest_spec_params spec_params; + struct nci_conn_info *conn_info; + int r, dev_num; + + dest_params = + kzalloc(sizeof(struct core_conn_create_dest_spec_params) + + sizeof(struct dest_spec_params), GFP_KERNEL); + if (dest_params == NULL) { + r = -ENOMEM; + goto exit; + } + + dest_params->type = NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE; + dest_params->length = sizeof(struct dest_spec_params); + spec_params.id = ndev->hci_dev->nfcee_id; + spec_params.protocol = NCI_NFCEE_INTERFACE_HCI_ACCESS; + memcpy(dest_params->value, &spec_params, sizeof(struct dest_spec_params)); + r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCEE, 1, + sizeof(struct core_conn_create_dest_spec_params) + + sizeof(struct dest_spec_params), + dest_params); + if (r != NCI_STATUS_OK) + goto free_dest_params; + + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) + goto free_dest_params; + + memcpy(ndev->hci_dev->init_data.gates, st21nfcb_gates, + sizeof(st21nfcb_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, ST21NFCB_NUM_DEVICES); + if (dev_num >= ST21NFCB_NUM_DEVICES) { + r = -ENODEV; + goto free_dest_params; + } + + scnprintf(ndev->hci_dev->init_data.session_id, + sizeof(ndev->hci_dev->init_data.session_id), + "%s%2x", "ST21BH", dev_num); + + r = nci_hci_dev_session_init(ndev); + if (r != NCI_HCI_ANY_OK) + goto free_dest_params; + + r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id, + NCI_NFCEE_ENABLE); + if (r != NCI_STATUS_OK) + goto free_dest_params; + +free_dest_params: + kfree(dest_params); + +exit: + return r; +} + +int st21nfcb_nci_discover_se(struct nci_dev *ndev) +{ + u8 param[2]; + int r; + int se_count = 0; + + pr_debug("st21nfcb_nci_discover_se\n"); + + r = st21nfcb_hci_network_init(ndev); + if (r != 0) + return r; + + param[0] = ST21NFCB_UICC_HOST_ID; + param[1] = ST21NFCB_HCI_HOST_ID_ESE; + r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADMIN_PARAM_WHITELIST, + param, sizeof(param)); + if (r != NCI_HCI_ANY_OK) + return r; + + r = st21nfcb_nci_control_se(ndev, ST21NFCB_UICC_HOST_ID, + ST21NFCB_SE_MODE_ON); + if (r == ST21NFCB_UICC_HOST_ID) { + nfc_add_se(ndev->nfc_dev, ST21NFCB_UICC_HOST_ID, NFC_SE_UICC); + se_count++; + } + + /* Try to enable eSE in order to check availability */ + r = st21nfcb_nci_control_se(ndev, ST21NFCB_HCI_HOST_ID_ESE, + ST21NFCB_SE_MODE_ON); + if (r == ST21NFCB_HCI_HOST_ID_ESE) { + nfc_add_se(ndev->nfc_dev, ST21NFCB_HCI_HOST_ID_ESE, + NFC_SE_EMBEDDED); + se_count++; + st21nfcb_se_get_atr(ndev); + } + + return !se_count; +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_discover_se); + +int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + pr_debug("\n"); + + switch (se_idx) { + case ST21NFCB_HCI_HOST_ID_ESE: + 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 nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, + ST21NFCB_EVT_TRANSMIT_DATA, apdu, + apdu_length); + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(st21nfcb_nci_se_io); + +static void st21nfcb_se_wt_timeout(unsigned long data) +{ + /* + * 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 st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data; + + pr_debug("\n"); + + info->se_info.bwi_active = false; + + if (!info->se_info.xch_error) { + info->se_info.xch_error = true; + nci_hci_send_event(info->ndlc->ndev, ST21NFCB_APDU_READER_GATE, + ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0); + } else { + info->se_info.xch_error = false; + nci_hci_send_event(info->ndlc->ndev, ST21NFCB_DEVICE_MGNT_GATE, + ST21NFCB_EVT_SE_HARD_RESET, ¶m, 1); + } + info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME); +} + +static void st21nfcb_se_activation_timeout(unsigned long data) +{ + struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data; + + pr_debug("\n"); + + info->se_info.se_active = false; + + complete(&info->se_info.req_completion); +} + +int st21nfcb_se_init(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + init_completion(&info->se_info.req_completion); + /* initialize timers */ + init_timer(&info->se_info.bwi_timer); + info->se_info.bwi_timer.data = (unsigned long)info; + info->se_info.bwi_timer.function = st21nfcb_se_wt_timeout; + info->se_info.bwi_active = false; + + init_timer(&info->se_info.se_active_timer); + info->se_info.se_active_timer.data = (unsigned long)info; + info->se_info.se_active_timer.function = + st21nfcb_se_activation_timeout; + info->se_info.se_active = false; + + info->se_info.xch_error = false; + + info->se_info.wt_timeout = + ST21NFCB_BWI_TO_TIMEOUT(ST21NFCB_ATR_DEFAULT_BWI); + + return 0; +} +EXPORT_SYMBOL(st21nfcb_se_init); + +void st21nfcb_se_deinit(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + 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); + + info->se_info.se_active = false; + info->se_info.bwi_active = false; +} +EXPORT_SYMBOL(st21nfcb_se_deinit); + diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.h b/drivers/nfc/st21nfcb/st21nfcb_se.h new file mode 100644 index 000000000..52a323872 --- /dev/null +++ b/drivers/nfc/st21nfcb/st21nfcb_se.h @@ -0,0 +1,61 @@ +/* + * NCI 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/>. + */ +#ifndef __LOCAL_ST21NFCB_SE_H_ +#define __LOCAL_ST21NFCB_SE_H_ + +/* + * ref ISO7816-3 chap 8.1. the initial character TS is followed by a + * sequence of at most 32 characters. + */ +#define ST21NFCB_ESE_MAX_LENGTH 33 +#define ST21NFCB_HCI_HOST_ID_ESE 0xc0 + +struct st21nfcb_se_info { + u8 atr[ST21NFCB_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; + + bool xch_error; + + se_io_cb_t cb; + void *cb_context; +}; + +int st21nfcb_se_init(struct nci_dev *ndev); +void st21nfcb_se_deinit(struct nci_dev *ndev); + +int st21nfcb_nci_discover_se(struct nci_dev *ndev); +int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx); +int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx); +int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context); +int st21nfcb_hci_load_session(struct nci_dev *ndev); +void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe, + u8 event, struct sk_buff *skb); +void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, + struct sk_buff *skb); + + +#endif /* __LOCAL_ST21NFCB_NCI_H_ */ diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c new file mode 100644 index 000000000..aa6a333b2 --- /dev/null +++ b/drivers/nfc/trf7970a.c @@ -0,0 +1,2218 @@ +/* + * TI TRF7970a RFID/NFC Transceiver Driver + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Erick Macias <emacias@ti.com> + * Author: Felipe Balbi <balbi@ti.com> + * Author: Mark A. Greer <mgreer@animalcreek.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/nfc.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> + +#include <net/nfc/nfc.h> +#include <net/nfc/digital.h> + +/* There are 3 ways the host can communicate with the trf7970a: + * parallel mode, SPI with Slave Select (SS) mode, and SPI without + * SS mode. The driver only supports the two SPI modes. + * + * The trf7970a is very timing sensitive and the VIN, EN2, and EN + * pins must asserted in that order and with specific delays in between. + * The delays used in the driver were provided by TI and have been + * confirmed to work with this driver. There is a bug with the current + * version of the trf7970a that requires that EN2 remain low no matter + * what. If it goes high, it will generate an RF field even when in + * passive target mode. TI has indicated that the chip will work okay + * when EN2 is left low. The 'en2-rf-quirk' device tree property + * indicates that trf7970a currently being used has the erratum and + * that EN2 must be kept low. + * + * Timeouts are implemented using the delayed workqueue kernel facility. + * Timeouts are required so things don't hang when there is no response + * from the trf7970a (or tag). Using this mechanism creates a race with + * interrupts, however. That is, an interrupt and a timeout could occur + * closely enough together that one is blocked by the mutex while the other + * executes. When the timeout handler executes first and blocks the + * interrupt handler, it will eventually set the state to IDLE so the + * interrupt handler will check the state and exit with no harm done. + * When the interrupt handler executes first and blocks the timeout handler, + * the cancel_delayed_work() call will know that it didn't cancel the + * work item (i.e., timeout) and will return zero. That return code is + * used by the timer handler to indicate that it should ignore the timeout + * once its unblocked. + * + * Aborting an active command isn't as simple as it seems because the only + * way to abort a command that's already been sent to the tag is so turn + * off power to the tag. If we do that, though, we'd have to go through + * the entire anticollision procedure again but the digital layer doesn't + * support that. So, if an abort is received before trf7970a_send_cmd() + * has sent the command to the tag, it simply returns -ECANCELED. If the + * command has already been sent to the tag, then the driver continues + * normally and recieves the response data (or error) but just before + * sending the data upstream, it frees the rx_skb and sends -ECANCELED + * upstream instead. If the command failed, that error will be sent + * upstream. + * + * When recieving data from a tag and the interrupt status register has + * only the SRX bit set, it means that all of the data has been received + * (once what's in the fifo has been read). However, depending on timing + * an interrupt status with only the SRX bit set may not be recived. In + * those cases, the timeout mechanism is used to wait 20 ms in case more + * data arrives. After 20 ms, it is assumed that all of the data has been + * received and the accumulated rx data is sent upstream. The + * 'TRF7970A_ST_WAIT_FOR_RX_DATA_CONT' state is used for this purpose + * (i.e., it indicates that some data has been received but we're not sure + * if there is more coming so a timeout in this state means all data has + * been received and there isn't an error). The delay is 20 ms since delays + * of ~16 ms have been observed during testing. + * + * When transmitting a frame larger than the FIFO size (127 bytes), the + * driver will wait 20 ms for the FIFO to drain past the low-watermark + * and generate an interrupt. The low-watermark set to 32 bytes so the + * interrupt should fire after 127 - 32 = 95 bytes have been sent. At + * the lowest possible bit rate (6.62 kbps for 15693), it will take up + * to ~14.35 ms so 20 ms is used for the timeout. + * + * Type 2 write and sector select commands respond with a 4-bit ACK or NACK. + * Having only 4 bits in the FIFO won't normally generate an interrupt so + * driver enables the '4_bit_RX' bit of the Special Functions register 1 + * to cause an interrupt in that case. Leaving that bit for a read command + * messes up the data returned so it is only enabled when the framing is + * 'NFC_DIGITAL_FRAMING_NFCA_T2T' and the command is not a read command. + * Unfortunately, that means that the driver has to peek into tx frames + * when the framing is 'NFC_DIGITAL_FRAMING_NFCA_T2T'. This is done by + * the trf7970a_per_cmd_config() routine. + * + * ISO/IEC 15693 frames specify whether to use single or double sub-carrier + * frequencies and whether to use low or high data rates in the flags byte + * of the frame. This means that the driver has to peek at all 15693 frames + * to determine what speed to set the communication to. In addition, write + * and lock commands use the OPTION flag to indicate that an EOF must be + * sent to the tag before it will send its response. So the driver has to + * examine all frames for that reason too. + * + * It is unclear how long to wait before sending the EOF. According to the + * Note under Table 1-1 in section 1.6 of + * http://www.ti.com/lit/ug/scbu011/scbu011.pdf, that wait should be at least + * 10 ms for TI Tag-it HF-I tags; however testing has shown that is not long + * enough so 20 ms is used. So the timer is set to 40 ms - 20 ms to drain + * up to 127 bytes in the FIFO at the lowest bit rate plus another 20 ms to + * ensure the wait is long enough before sending the EOF. This seems to work + * reliably. + */ + +#define TRF7970A_SUPPORTED_PROTOCOLS \ + (NFC_PROTO_MIFARE_MASK | NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_ISO15693_MASK | NFC_PROTO_NFC_DEP_MASK) + +#define TRF7970A_AUTOSUSPEND_DELAY 30000 /* 30 seconds */ + +#define TRF7970A_RX_SKB_ALLOC_SIZE 256 + +#define TRF7970A_FIFO_SIZE 127 + +/* TX length is 3 nibbles long ==> 4KB - 1 bytes max */ +#define TRF7970A_TX_MAX (4096 - 1) + +#define TRF7970A_WAIT_FOR_TX_IRQ 20 +#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT 20 +#define TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT 20 +#define TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF 40 + +/* Guard times for various RF technologies (in us) */ +#define TRF7970A_GUARD_TIME_NFCA 5000 +#define TRF7970A_GUARD_TIME_NFCB 5000 +#define TRF7970A_GUARD_TIME_NFCF 20000 +#define TRF7970A_GUARD_TIME_15693 1000 + +/* Quirks */ +/* Erratum: When reading IRQ Status register on trf7970a, we must issue a + * read continuous command for IRQ Status and Collision Position registers. + */ +#define TRF7970A_QUIRK_IRQ_STATUS_READ BIT(0) +#define TRF7970A_QUIRK_EN2_MUST_STAY_LOW BIT(1) + +/* Direct commands */ +#define TRF7970A_CMD_IDLE 0x00 +#define TRF7970A_CMD_SOFT_INIT 0x03 +#define TRF7970A_CMD_RF_COLLISION 0x04 +#define TRF7970A_CMD_RF_COLLISION_RESPONSE_N 0x05 +#define TRF7970A_CMD_RF_COLLISION_RESPONSE_0 0x06 +#define TRF7970A_CMD_FIFO_RESET 0x0f +#define TRF7970A_CMD_TRANSMIT_NO_CRC 0x10 +#define TRF7970A_CMD_TRANSMIT 0x11 +#define TRF7970A_CMD_DELAY_TRANSMIT_NO_CRC 0x12 +#define TRF7970A_CMD_DELAY_TRANSMIT 0x13 +#define TRF7970A_CMD_EOF 0x14 +#define TRF7970A_CMD_CLOSE_SLOT 0x15 +#define TRF7970A_CMD_BLOCK_RX 0x16 +#define TRF7970A_CMD_ENABLE_RX 0x17 +#define TRF7970A_CMD_TEST_INT_RF 0x18 +#define TRF7970A_CMD_TEST_EXT_RF 0x19 +#define TRF7970A_CMD_RX_GAIN_ADJUST 0x1a + +/* Bits determining whether its a direct command or register R/W, + * whether to use a continuous SPI transaction or not, and the actual + * direct cmd opcode or regster address. + */ +#define TRF7970A_CMD_BIT_CTRL BIT(7) +#define TRF7970A_CMD_BIT_RW BIT(6) +#define TRF7970A_CMD_BIT_CONTINUOUS BIT(5) +#define TRF7970A_CMD_BIT_OPCODE(opcode) ((opcode) & 0x1f) + +/* Registers addresses */ +#define TRF7970A_CHIP_STATUS_CTRL 0x00 +#define TRF7970A_ISO_CTRL 0x01 +#define TRF7970A_ISO14443B_TX_OPTIONS 0x02 +#define TRF7970A_ISO14443A_HIGH_BITRATE_OPTIONS 0x03 +#define TRF7970A_TX_TIMER_SETTING_H_BYTE 0x04 +#define TRF7970A_TX_TIMER_SETTING_L_BYTE 0x05 +#define TRF7970A_TX_PULSE_LENGTH_CTRL 0x06 +#define TRF7970A_RX_NO_RESPONSE_WAIT 0x07 +#define TRF7970A_RX_WAIT_TIME 0x08 +#define TRF7970A_MODULATOR_SYS_CLK_CTRL 0x09 +#define TRF7970A_RX_SPECIAL_SETTINGS 0x0a +#define TRF7970A_REG_IO_CTRL 0x0b +#define TRF7970A_IRQ_STATUS 0x0c +#define TRF7970A_COLLISION_IRQ_MASK 0x0d +#define TRF7970A_COLLISION_POSITION 0x0e +#define TRF7970A_RSSI_OSC_STATUS 0x0f +#define TRF7970A_SPECIAL_FCN_REG1 0x10 +#define TRF7970A_SPECIAL_FCN_REG2 0x11 +#define TRF7970A_RAM1 0x12 +#define TRF7970A_RAM2 0x13 +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS 0x14 +#define TRF7970A_NFC_LOW_FIELD_LEVEL 0x16 +#define TRF7970A_NFCID1 0x17 +#define TRF7970A_NFC_TARGET_LEVEL 0x18 +#define TRF79070A_NFC_TARGET_PROTOCOL 0x19 +#define TRF7970A_TEST_REGISTER1 0x1a +#define TRF7970A_TEST_REGISTER2 0x1b +#define TRF7970A_FIFO_STATUS 0x1c +#define TRF7970A_TX_LENGTH_BYTE1 0x1d +#define TRF7970A_TX_LENGTH_BYTE2 0x1e +#define TRF7970A_FIFO_IO_REGISTER 0x1f + +/* Chip Status Control Register Bits */ +#define TRF7970A_CHIP_STATUS_VRS5_3 BIT(0) +#define TRF7970A_CHIP_STATUS_REC_ON BIT(1) +#define TRF7970A_CHIP_STATUS_AGC_ON BIT(2) +#define TRF7970A_CHIP_STATUS_PM_ON BIT(3) +#define TRF7970A_CHIP_STATUS_RF_PWR BIT(4) +#define TRF7970A_CHIP_STATUS_RF_ON BIT(5) +#define TRF7970A_CHIP_STATUS_DIRECT BIT(6) +#define TRF7970A_CHIP_STATUS_STBY BIT(7) + +/* ISO Control Register Bits */ +#define TRF7970A_ISO_CTRL_15693_SGL_1OF4_662 0x00 +#define TRF7970A_ISO_CTRL_15693_SGL_1OF256_662 0x01 +#define TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648 0x02 +#define TRF7970A_ISO_CTRL_15693_SGL_1OF256_2648 0x03 +#define TRF7970A_ISO_CTRL_15693_DBL_1OF4_667a 0x04 +#define TRF7970A_ISO_CTRL_15693_DBL_1OF256_667 0x05 +#define TRF7970A_ISO_CTRL_15693_DBL_1OF4_2669 0x06 +#define TRF7970A_ISO_CTRL_15693_DBL_1OF256_2669 0x07 +#define TRF7970A_ISO_CTRL_14443A_106 0x08 +#define TRF7970A_ISO_CTRL_14443A_212 0x09 +#define TRF7970A_ISO_CTRL_14443A_424 0x0a +#define TRF7970A_ISO_CTRL_14443A_848 0x0b +#define TRF7970A_ISO_CTRL_14443B_106 0x0c +#define TRF7970A_ISO_CTRL_14443B_212 0x0d +#define TRF7970A_ISO_CTRL_14443B_424 0x0e +#define TRF7970A_ISO_CTRL_14443B_848 0x0f +#define TRF7970A_ISO_CTRL_FELICA_212 0x1a +#define TRF7970A_ISO_CTRL_FELICA_424 0x1b +#define TRF7970A_ISO_CTRL_NFC_NFCA_106 0x01 +#define TRF7970A_ISO_CTRL_NFC_NFCF_212 0x02 +#define TRF7970A_ISO_CTRL_NFC_NFCF_424 0x03 +#define TRF7970A_ISO_CTRL_NFC_CE_14443A 0x00 +#define TRF7970A_ISO_CTRL_NFC_CE_14443B 0x01 +#define TRF7970A_ISO_CTRL_NFC_CE BIT(2) +#define TRF7970A_ISO_CTRL_NFC_ACTIVE BIT(3) +#define TRF7970A_ISO_CTRL_NFC_INITIATOR BIT(4) +#define TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE BIT(5) +#define TRF7970A_ISO_CTRL_RFID BIT(5) +#define TRF7970A_ISO_CTRL_DIR_MODE BIT(6) +#define TRF7970A_ISO_CTRL_RX_CRC_N BIT(7) /* true == No CRC */ + +#define TRF7970A_ISO_CTRL_RFID_SPEED_MASK 0x1f + +/* Modulator and SYS_CLK Control Register Bits */ +#define TRF7970A_MODULATOR_DEPTH(n) ((n) & 0x7) +#define TRF7970A_MODULATOR_DEPTH_ASK10 (TRF7970A_MODULATOR_DEPTH(0)) +#define TRF7970A_MODULATOR_DEPTH_OOK (TRF7970A_MODULATOR_DEPTH(1)) +#define TRF7970A_MODULATOR_DEPTH_ASK7 (TRF7970A_MODULATOR_DEPTH(2)) +#define TRF7970A_MODULATOR_DEPTH_ASK8_5 (TRF7970A_MODULATOR_DEPTH(3)) +#define TRF7970A_MODULATOR_DEPTH_ASK13 (TRF7970A_MODULATOR_DEPTH(4)) +#define TRF7970A_MODULATOR_DEPTH_ASK16 (TRF7970A_MODULATOR_DEPTH(5)) +#define TRF7970A_MODULATOR_DEPTH_ASK22 (TRF7970A_MODULATOR_DEPTH(6)) +#define TRF7970A_MODULATOR_DEPTH_ASK30 (TRF7970A_MODULATOR_DEPTH(7)) +#define TRF7970A_MODULATOR_EN_ANA BIT(3) +#define TRF7970A_MODULATOR_CLK(n) (((n) & 0x3) << 4) +#define TRF7970A_MODULATOR_CLK_DISABLED (TRF7970A_MODULATOR_CLK(0)) +#define TRF7970A_MODULATOR_CLK_3_6 (TRF7970A_MODULATOR_CLK(1)) +#define TRF7970A_MODULATOR_CLK_6_13 (TRF7970A_MODULATOR_CLK(2)) +#define TRF7970A_MODULATOR_CLK_13_27 (TRF7970A_MODULATOR_CLK(3)) +#define TRF7970A_MODULATOR_EN_OOK BIT(6) +#define TRF7970A_MODULATOR_27MHZ BIT(7) + +#define TRF7970A_RX_SPECIAL_SETTINGS_NO_LIM BIT(0) +#define TRF7970A_RX_SPECIAL_SETTINGS_AGCR BIT(1) +#define TRF7970A_RX_SPECIAL_SETTINGS_GD_0DB (0x0 << 2) +#define TRF7970A_RX_SPECIAL_SETTINGS_GD_5DB (0x1 << 2) +#define TRF7970A_RX_SPECIAL_SETTINGS_GD_10DB (0x2 << 2) +#define TRF7970A_RX_SPECIAL_SETTINGS_GD_15DB (0x3 << 2) +#define TRF7970A_RX_SPECIAL_SETTINGS_HBT BIT(4) +#define TRF7970A_RX_SPECIAL_SETTINGS_M848 BIT(5) +#define TRF7970A_RX_SPECIAL_SETTINGS_C424 BIT(6) +#define TRF7970A_RX_SPECIAL_SETTINGS_C212 BIT(7) + +#define TRF7970A_REG_IO_CTRL_VRS(v) ((v) & 0x07) +#define TRF7970A_REG_IO_CTRL_IO_LOW BIT(5) +#define TRF7970A_REG_IO_CTRL_EN_EXT_PA BIT(6) +#define TRF7970A_REG_IO_CTRL_AUTO_REG BIT(7) + +/* IRQ Status Register Bits */ +#define TRF7970A_IRQ_STATUS_NORESP BIT(0) /* ISO15693 only */ +#define TRF7970A_IRQ_STATUS_NFC_COL_ERROR BIT(0) +#define TRF7970A_IRQ_STATUS_COL BIT(1) +#define TRF7970A_IRQ_STATUS_FRAMING_EOF_ERROR BIT(2) +#define TRF7970A_IRQ_STATUS_NFC_RF BIT(2) +#define TRF7970A_IRQ_STATUS_PARITY_ERROR BIT(3) +#define TRF7970A_IRQ_STATUS_NFC_SDD BIT(3) +#define TRF7970A_IRQ_STATUS_CRC_ERROR BIT(4) +#define TRF7970A_IRQ_STATUS_NFC_PROTO_ERROR BIT(4) +#define TRF7970A_IRQ_STATUS_FIFO BIT(5) +#define TRF7970A_IRQ_STATUS_SRX BIT(6) +#define TRF7970A_IRQ_STATUS_TX BIT(7) + +#define TRF7970A_IRQ_STATUS_ERROR \ + (TRF7970A_IRQ_STATUS_COL | \ + TRF7970A_IRQ_STATUS_FRAMING_EOF_ERROR | \ + TRF7970A_IRQ_STATUS_PARITY_ERROR | \ + TRF7970A_IRQ_STATUS_CRC_ERROR) + +#define TRF7970A_RSSI_OSC_STATUS_RSSI_MASK (BIT(2) | BIT(1) | BIT(0)) +#define TRF7970A_RSSI_OSC_STATUS_RSSI_X_MASK (BIT(5) | BIT(4) | BIT(3)) +#define TRF7970A_RSSI_OSC_STATUS_RSSI_OSC_OK BIT(6) + +#define TRF7970A_SPECIAL_FCN_REG1_COL_7_6 BIT(0) +#define TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL BIT(1) +#define TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX BIT(2) +#define TRF7970A_SPECIAL_FCN_REG1_SP_DIR_MODE BIT(3) +#define TRF7970A_SPECIAL_FCN_REG1_NEXT_SLOT_37US BIT(4) +#define TRF7970A_SPECIAL_FCN_REG1_PAR43 BIT(5) + +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_124 (0x0 << 2) +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_120 (0x1 << 2) +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_112 (0x2 << 2) +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 (0x3 << 2) +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_4 0x0 +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_8 0x1 +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_16 0x2 +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32 0x3 + +#define TRF7970A_NFC_LOW_FIELD_LEVEL_RFDET(v) ((v) & 0x07) +#define TRF7970A_NFC_LOW_FIELD_LEVEL_CLEX_DIS BIT(7) + +#define TRF7970A_NFC_TARGET_LEVEL_RFDET(v) ((v) & 0x07) +#define TRF7970A_NFC_TARGET_LEVEL_HI_RF BIT(3) +#define TRF7970A_NFC_TARGET_LEVEL_SDD_EN BIT(3) +#define TRF7970A_NFC_TARGET_LEVEL_LD_S_4BYTES (0x0 << 6) +#define TRF7970A_NFC_TARGET_LEVEL_LD_S_7BYTES (0x1 << 6) +#define TRF7970A_NFC_TARGET_LEVEL_LD_S_10BYTES (0x2 << 6) + +#define TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_106 BIT(0) +#define TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_212 BIT(1) +#define TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_424 (BIT(0) | BIT(1)) +#define TRF79070A_NFC_TARGET_PROTOCOL_PAS_14443B BIT(2) +#define TRF79070A_NFC_TARGET_PROTOCOL_PAS_106 BIT(3) +#define TRF79070A_NFC_TARGET_PROTOCOL_FELICA BIT(4) +#define TRF79070A_NFC_TARGET_PROTOCOL_RF_L BIT(6) +#define TRF79070A_NFC_TARGET_PROTOCOL_RF_H BIT(7) + +#define TRF79070A_NFC_TARGET_PROTOCOL_106A \ + (TRF79070A_NFC_TARGET_PROTOCOL_RF_H | \ + TRF79070A_NFC_TARGET_PROTOCOL_RF_L | \ + TRF79070A_NFC_TARGET_PROTOCOL_PAS_106 | \ + TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_106) + +#define TRF79070A_NFC_TARGET_PROTOCOL_106B \ + (TRF79070A_NFC_TARGET_PROTOCOL_RF_H | \ + TRF79070A_NFC_TARGET_PROTOCOL_RF_L | \ + TRF79070A_NFC_TARGET_PROTOCOL_PAS_14443B | \ + TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_106) + +#define TRF79070A_NFC_TARGET_PROTOCOL_212F \ + (TRF79070A_NFC_TARGET_PROTOCOL_RF_H | \ + TRF79070A_NFC_TARGET_PROTOCOL_RF_L | \ + TRF79070A_NFC_TARGET_PROTOCOL_FELICA | \ + TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_212) + +#define TRF79070A_NFC_TARGET_PROTOCOL_424F \ + (TRF79070A_NFC_TARGET_PROTOCOL_RF_H | \ + TRF79070A_NFC_TARGET_PROTOCOL_RF_L | \ + TRF79070A_NFC_TARGET_PROTOCOL_FELICA | \ + TRF79070A_NFC_TARGET_PROTOCOL_NFCBR_424) + +#define TRF7970A_FIFO_STATUS_OVERFLOW BIT(7) + +/* NFC (ISO/IEC 14443A) Type 2 Tag commands */ +#define NFC_T2T_CMD_READ 0x30 + +/* ISO 15693 commands codes */ +#define ISO15693_CMD_INVENTORY 0x01 +#define ISO15693_CMD_READ_SINGLE_BLOCK 0x20 +#define ISO15693_CMD_WRITE_SINGLE_BLOCK 0x21 +#define ISO15693_CMD_LOCK_BLOCK 0x22 +#define ISO15693_CMD_READ_MULTIPLE_BLOCK 0x23 +#define ISO15693_CMD_WRITE_MULTIPLE_BLOCK 0x24 +#define ISO15693_CMD_SELECT 0x25 +#define ISO15693_CMD_RESET_TO_READY 0x26 +#define ISO15693_CMD_WRITE_AFI 0x27 +#define ISO15693_CMD_LOCK_AFI 0x28 +#define ISO15693_CMD_WRITE_DSFID 0x29 +#define ISO15693_CMD_LOCK_DSFID 0x2a +#define ISO15693_CMD_GET_SYSTEM_INFO 0x2b +#define ISO15693_CMD_GET_MULTIPLE_BLOCK_SECURITY_STATUS 0x2c + +/* ISO 15693 request and response flags */ +#define ISO15693_REQ_FLAG_SUB_CARRIER BIT(0) +#define ISO15693_REQ_FLAG_DATA_RATE BIT(1) +#define ISO15693_REQ_FLAG_INVENTORY BIT(2) +#define ISO15693_REQ_FLAG_PROTOCOL_EXT BIT(3) +#define ISO15693_REQ_FLAG_SELECT BIT(4) +#define ISO15693_REQ_FLAG_AFI BIT(4) +#define ISO15693_REQ_FLAG_ADDRESS BIT(5) +#define ISO15693_REQ_FLAG_NB_SLOTS BIT(5) +#define ISO15693_REQ_FLAG_OPTION BIT(6) + +#define ISO15693_REQ_FLAG_SPEED_MASK \ + (ISO15693_REQ_FLAG_SUB_CARRIER | ISO15693_REQ_FLAG_DATA_RATE) + +enum trf7970a_state { + TRF7970A_ST_PWR_OFF, + TRF7970A_ST_RF_OFF, + TRF7970A_ST_IDLE, + TRF7970A_ST_IDLE_RX_BLOCKED, + TRF7970A_ST_WAIT_FOR_TX_FIFO, + TRF7970A_ST_WAIT_FOR_RX_DATA, + TRF7970A_ST_WAIT_FOR_RX_DATA_CONT, + TRF7970A_ST_WAIT_TO_ISSUE_EOF, + TRF7970A_ST_LISTENING, + TRF7970A_ST_LISTENING_MD, + TRF7970A_ST_MAX +}; + +struct trf7970a { + enum trf7970a_state state; + struct device *dev; + struct spi_device *spi; + struct regulator *regulator; + struct nfc_digital_dev *ddev; + u32 quirks; + bool is_initiator; + bool aborting; + struct sk_buff *tx_skb; + struct sk_buff *rx_skb; + nfc_digital_cmd_complete_t cb; + void *cb_arg; + u8 chip_status_ctrl; + u8 iso_ctrl; + u8 iso_ctrl_tech; + u8 modulator_sys_clk_ctrl; + u8 special_fcn_reg1; + unsigned int guard_time; + int technology; + int framing; + u8 md_rf_tech; + u8 tx_cmd; + bool issue_eof; + int en2_gpio; + int en_gpio; + struct mutex lock; + unsigned int timeout; + bool ignore_timeout; + struct delayed_work timeout_work; +}; + + +static int trf7970a_cmd(struct trf7970a *trf, u8 opcode) +{ + u8 cmd = TRF7970A_CMD_BIT_CTRL | TRF7970A_CMD_BIT_OPCODE(opcode); + int ret; + + dev_dbg(trf->dev, "cmd: 0x%x\n", cmd); + + ret = spi_write(trf->spi, &cmd, 1); + if (ret) + dev_err(trf->dev, "%s - cmd: 0x%x, ret: %d\n", __func__, cmd, + ret); + return ret; +} + +static int trf7970a_read(struct trf7970a *trf, u8 reg, u8 *val) +{ + u8 addr = TRF7970A_CMD_BIT_RW | reg; + int ret; + + ret = spi_write_then_read(trf->spi, &addr, 1, val, 1); + if (ret) + dev_err(trf->dev, "%s - addr: 0x%x, ret: %d\n", __func__, addr, + ret); + + dev_dbg(trf->dev, "read(0x%x): 0x%x\n", addr, *val); + + return ret; +} + +static int trf7970a_read_cont(struct trf7970a *trf, u8 reg, u8 *buf, size_t len) +{ + u8 addr = reg | TRF7970A_CMD_BIT_RW | TRF7970A_CMD_BIT_CONTINUOUS; + struct spi_transfer t[2]; + struct spi_message m; + int ret; + + dev_dbg(trf->dev, "read_cont(0x%x, %zd)\n", addr, len); + + spi_message_init(&m); + + memset(&t, 0, sizeof(t)); + + t[0].tx_buf = &addr; + t[0].len = sizeof(addr); + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + + ret = spi_sync(trf->spi, &m); + if (ret) + dev_err(trf->dev, "%s - addr: 0x%x, ret: %d\n", __func__, addr, + ret); + return ret; +} + +static int trf7970a_write(struct trf7970a *trf, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + int ret; + + dev_dbg(trf->dev, "write(0x%x): 0x%x\n", reg, val); + + ret = spi_write(trf->spi, buf, 2); + if (ret) + dev_err(trf->dev, "%s - write: 0x%x 0x%x, ret: %d\n", __func__, + buf[0], buf[1], ret); + + return ret; +} + +static int trf7970a_read_irqstatus(struct trf7970a *trf, u8 *status) +{ + int ret; + u8 buf[2]; + u8 addr; + + addr = TRF7970A_IRQ_STATUS | TRF7970A_CMD_BIT_RW; + + if (trf->quirks & TRF7970A_QUIRK_IRQ_STATUS_READ) { + addr |= TRF7970A_CMD_BIT_CONTINUOUS; + ret = spi_write_then_read(trf->spi, &addr, 1, buf, 2); + } else { + ret = spi_write_then_read(trf->spi, &addr, 1, buf, 1); + } + + if (ret) + dev_err(trf->dev, "%s - irqstatus: Status read failed: %d\n", + __func__, ret); + else + *status = buf[0]; + + return ret; +} + +static int trf7970a_read_target_proto(struct trf7970a *trf, u8 *target_proto) +{ + int ret; + u8 buf[2]; + u8 addr; + + addr = TRF79070A_NFC_TARGET_PROTOCOL | TRF7970A_CMD_BIT_RW | + TRF7970A_CMD_BIT_CONTINUOUS; + + ret = spi_write_then_read(trf->spi, &addr, 1, buf, 2); + if (ret) + dev_err(trf->dev, "%s - target_proto: Read failed: %d\n", + __func__, ret); + else + *target_proto = buf[0]; + + return ret; +} + +static int trf7970a_mode_detect(struct trf7970a *trf, u8 *rf_tech) +{ + int ret; + u8 target_proto, tech; + + ret = trf7970a_read_target_proto(trf, &target_proto); + if (ret) + return ret; + + switch (target_proto) { + case TRF79070A_NFC_TARGET_PROTOCOL_106A: + tech = NFC_DIGITAL_RF_TECH_106A; + break; + case TRF79070A_NFC_TARGET_PROTOCOL_106B: + tech = NFC_DIGITAL_RF_TECH_106B; + break; + case TRF79070A_NFC_TARGET_PROTOCOL_212F: + tech = NFC_DIGITAL_RF_TECH_212F; + break; + case TRF79070A_NFC_TARGET_PROTOCOL_424F: + tech = NFC_DIGITAL_RF_TECH_424F; + break; + default: + dev_dbg(trf->dev, "%s - mode_detect: target_proto: 0x%x\n", + __func__, target_proto); + return -EIO; + } + + *rf_tech = tech; + + return ret; +} + +static void trf7970a_send_upstream(struct trf7970a *trf) +{ + dev_kfree_skb_any(trf->tx_skb); + trf->tx_skb = NULL; + + if (trf->rx_skb && !IS_ERR(trf->rx_skb) && !trf->aborting) + print_hex_dump_debug("trf7970a rx data: ", DUMP_PREFIX_NONE, + 16, 1, trf->rx_skb->data, trf->rx_skb->len, + false); + + trf->state = TRF7970A_ST_IDLE; + + if (trf->aborting) { + dev_dbg(trf->dev, "Abort process complete\n"); + + if (!IS_ERR(trf->rx_skb)) { + kfree_skb(trf->rx_skb); + trf->rx_skb = ERR_PTR(-ECANCELED); + } + + trf->aborting = false; + } + + trf->cb(trf->ddev, trf->cb_arg, trf->rx_skb); + + trf->rx_skb = NULL; +} + +static void trf7970a_send_err_upstream(struct trf7970a *trf, int errno) +{ + dev_dbg(trf->dev, "Error - state: %d, errno: %d\n", trf->state, errno); + + cancel_delayed_work(&trf->timeout_work); + + kfree_skb(trf->rx_skb); + trf->rx_skb = ERR_PTR(errno); + + trf7970a_send_upstream(trf); +} + +static int trf7970a_transmit(struct trf7970a *trf, struct sk_buff *skb, + unsigned int len, u8 *prefix, unsigned int prefix_len) +{ + struct spi_transfer t[2]; + struct spi_message m; + unsigned int timeout; + int ret; + + print_hex_dump_debug("trf7970a tx data: ", DUMP_PREFIX_NONE, + 16, 1, skb->data, len, false); + + spi_message_init(&m); + + memset(&t, 0, sizeof(t)); + + t[0].tx_buf = prefix; + t[0].len = prefix_len; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = skb->data; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + + ret = spi_sync(trf->spi, &m); + if (ret) { + dev_err(trf->dev, "%s - Can't send tx data: %d\n", __func__, + ret); + return ret; + } + + skb_pull(skb, len); + + if (skb->len > 0) { + trf->state = TRF7970A_ST_WAIT_FOR_TX_FIFO; + timeout = TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT; + } else { + if (trf->issue_eof) { + trf->state = TRF7970A_ST_WAIT_TO_ISSUE_EOF; + timeout = TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF; + } else { + trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA; + + if (!trf->timeout) + timeout = TRF7970A_WAIT_FOR_TX_IRQ; + else + timeout = trf->timeout; + } + } + + dev_dbg(trf->dev, "Setting timeout for %d ms, state: %d\n", timeout, + trf->state); + + schedule_delayed_work(&trf->timeout_work, msecs_to_jiffies(timeout)); + + return 0; +} + +static void trf7970a_fill_fifo(struct trf7970a *trf) +{ + struct sk_buff *skb = trf->tx_skb; + unsigned int len; + int ret; + u8 fifo_bytes; + u8 prefix; + + ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes); + if (ret) { + trf7970a_send_err_upstream(trf, ret); + return; + } + + dev_dbg(trf->dev, "Filling FIFO - fifo_bytes: 0x%x\n", fifo_bytes); + + fifo_bytes &= ~TRF7970A_FIFO_STATUS_OVERFLOW; + + /* Calculate how much more data can be written to the fifo */ + len = TRF7970A_FIFO_SIZE - fifo_bytes; + if (!len) { + schedule_delayed_work(&trf->timeout_work, + msecs_to_jiffies(TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT)); + return; + } + + len = min(skb->len, len); + + prefix = TRF7970A_CMD_BIT_CONTINUOUS | TRF7970A_FIFO_IO_REGISTER; + + ret = trf7970a_transmit(trf, skb, len, &prefix, sizeof(prefix)); + if (ret) + trf7970a_send_err_upstream(trf, ret); +} + +static void trf7970a_drain_fifo(struct trf7970a *trf, u8 status) +{ + struct sk_buff *skb = trf->rx_skb; + int ret; + u8 fifo_bytes; + + if (status & TRF7970A_IRQ_STATUS_ERROR) { + trf7970a_send_err_upstream(trf, -EIO); + return; + } + + ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes); + if (ret) { + trf7970a_send_err_upstream(trf, ret); + return; + } + + dev_dbg(trf->dev, "Draining FIFO - fifo_bytes: 0x%x\n", fifo_bytes); + + fifo_bytes &= ~TRF7970A_FIFO_STATUS_OVERFLOW; + + if (!fifo_bytes) + goto no_rx_data; + + if (fifo_bytes > skb_tailroom(skb)) { + skb = skb_copy_expand(skb, skb_headroom(skb), + max_t(int, fifo_bytes, + TRF7970A_RX_SKB_ALLOC_SIZE), + GFP_KERNEL); + if (!skb) { + trf7970a_send_err_upstream(trf, -ENOMEM); + return; + } + + kfree_skb(trf->rx_skb); + trf->rx_skb = skb; + } + + ret = trf7970a_read_cont(trf, TRF7970A_FIFO_IO_REGISTER, + skb_put(skb, fifo_bytes), fifo_bytes); + if (ret) { + trf7970a_send_err_upstream(trf, ret); + return; + } + + /* If received Type 2 ACK/NACK, shift right 4 bits and pass up */ + if ((trf->framing == NFC_DIGITAL_FRAMING_NFCA_T2T) && (skb->len == 1) && + (trf->special_fcn_reg1 == + TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX)) { + skb->data[0] >>= 4; + status = TRF7970A_IRQ_STATUS_SRX; + } else { + trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA_CONT; + + ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes); + if (ret) { + trf7970a_send_err_upstream(trf, ret); + return; + } + + fifo_bytes &= ~TRF7970A_FIFO_STATUS_OVERFLOW; + + /* If there are bytes in the FIFO, set status to '0' so + * the if stmt below doesn't fire and the driver will wait + * for the trf7970a to generate another RX interrupt. + */ + if (fifo_bytes) + status = 0; + } + +no_rx_data: + if (status == TRF7970A_IRQ_STATUS_SRX) { /* Receive complete */ + trf7970a_send_upstream(trf); + return; + } + + dev_dbg(trf->dev, "Setting timeout for %d ms\n", + TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT); + + schedule_delayed_work(&trf->timeout_work, + msecs_to_jiffies(TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT)); +} + +static irqreturn_t trf7970a_irq(int irq, void *dev_id) +{ + struct trf7970a *trf = dev_id; + int ret; + u8 status, fifo_bytes, iso_ctrl; + + mutex_lock(&trf->lock); + + if (trf->state == TRF7970A_ST_RF_OFF) { + mutex_unlock(&trf->lock); + return IRQ_NONE; + } + + ret = trf7970a_read_irqstatus(trf, &status); + if (ret) { + mutex_unlock(&trf->lock); + return IRQ_NONE; + } + + dev_dbg(trf->dev, "IRQ - state: %d, status: 0x%x\n", trf->state, + status); + + if (!status) { + mutex_unlock(&trf->lock); + return IRQ_NONE; + } + + switch (trf->state) { + case TRF7970A_ST_IDLE: + case TRF7970A_ST_IDLE_RX_BLOCKED: + /* If initiator and getting interrupts caused by RF noise, + * turn off the receiver to avoid unnecessary interrupts. + * It will be turned back on in trf7970a_send_cmd() when + * the next command is issued. + */ + if (trf->is_initiator && (status & TRF7970A_IRQ_STATUS_ERROR)) { + trf7970a_cmd(trf, TRF7970A_CMD_BLOCK_RX); + trf->state = TRF7970A_ST_IDLE_RX_BLOCKED; + } + + trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET); + break; + case TRF7970A_ST_WAIT_FOR_TX_FIFO: + if (status & TRF7970A_IRQ_STATUS_TX) { + trf->ignore_timeout = + !cancel_delayed_work(&trf->timeout_work); + trf7970a_fill_fifo(trf); + } else { + trf7970a_send_err_upstream(trf, -EIO); + } + break; + case TRF7970A_ST_WAIT_FOR_RX_DATA: + case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT: + if (status & TRF7970A_IRQ_STATUS_SRX) { + trf->ignore_timeout = + !cancel_delayed_work(&trf->timeout_work); + trf7970a_drain_fifo(trf, status); + } else if (status & TRF7970A_IRQ_STATUS_FIFO) { + ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, + &fifo_bytes); + + fifo_bytes &= ~TRF7970A_FIFO_STATUS_OVERFLOW; + + if (ret) + trf7970a_send_err_upstream(trf, ret); + else if (!fifo_bytes) + trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET); + } else if ((status == TRF7970A_IRQ_STATUS_TX) || + (!trf->is_initiator && + (status == (TRF7970A_IRQ_STATUS_TX | + TRF7970A_IRQ_STATUS_NFC_RF)))) { + trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET); + + if (!trf->timeout) { + trf->ignore_timeout = !cancel_delayed_work( + &trf->timeout_work); + trf->rx_skb = ERR_PTR(0); + trf7970a_send_upstream(trf); + break; + } + + if (trf->is_initiator) + break; + + iso_ctrl = trf->iso_ctrl; + + switch (trf->framing) { + case NFC_DIGITAL_FRAMING_NFCA_STANDARD: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC; + iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; + trf->iso_ctrl = 0xff; /* Force ISO_CTRL write */ + break; + case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT; + iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N; + trf->iso_ctrl = 0xff; /* Force ISO_CTRL write */ + break; + case NFC_DIGITAL_FRAMING_NFCA_ANTICOL_COMPLETE: + ret = trf7970a_write(trf, + TRF7970A_SPECIAL_FCN_REG1, + TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL); + if (ret) + goto err_unlock_exit; + + trf->special_fcn_reg1 = + TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL; + break; + default: + break; + } + + if (iso_ctrl != trf->iso_ctrl) { + ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, + iso_ctrl); + if (ret) + goto err_unlock_exit; + + trf->iso_ctrl = iso_ctrl; + } + } else { + trf7970a_send_err_upstream(trf, -EIO); + } + break; + case TRF7970A_ST_WAIT_TO_ISSUE_EOF: + if (status != TRF7970A_IRQ_STATUS_TX) + trf7970a_send_err_upstream(trf, -EIO); + break; + case TRF7970A_ST_LISTENING: + if (status & TRF7970A_IRQ_STATUS_SRX) { + trf->ignore_timeout = + !cancel_delayed_work(&trf->timeout_work); + trf7970a_drain_fifo(trf, status); + } else if (!(status & TRF7970A_IRQ_STATUS_NFC_RF)) { + trf7970a_send_err_upstream(trf, -EIO); + } + break; + case TRF7970A_ST_LISTENING_MD: + if (status & TRF7970A_IRQ_STATUS_SRX) { + trf->ignore_timeout = + !cancel_delayed_work(&trf->timeout_work); + + ret = trf7970a_mode_detect(trf, &trf->md_rf_tech); + if (ret) { + trf7970a_send_err_upstream(trf, ret); + } else { + trf->state = TRF7970A_ST_LISTENING; + trf7970a_drain_fifo(trf, status); + } + } else if (!(status & TRF7970A_IRQ_STATUS_NFC_RF)) { + trf7970a_send_err_upstream(trf, -EIO); + } + break; + default: + dev_err(trf->dev, "%s - Driver in invalid state: %d\n", + __func__, trf->state); + } + +err_unlock_exit: + mutex_unlock(&trf->lock); + return IRQ_HANDLED; +} + +static void trf7970a_issue_eof(struct trf7970a *trf) +{ + int ret; + + dev_dbg(trf->dev, "Issuing EOF\n"); + + ret = trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET); + if (ret) + trf7970a_send_err_upstream(trf, ret); + + ret = trf7970a_cmd(trf, TRF7970A_CMD_EOF); + if (ret) + trf7970a_send_err_upstream(trf, ret); + + trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA; + + dev_dbg(trf->dev, "Setting timeout for %d ms, state: %d\n", + trf->timeout, trf->state); + + schedule_delayed_work(&trf->timeout_work, + msecs_to_jiffies(trf->timeout)); +} + +static void trf7970a_timeout_work_handler(struct work_struct *work) +{ + struct trf7970a *trf = container_of(work, struct trf7970a, + timeout_work.work); + + dev_dbg(trf->dev, "Timeout - state: %d, ignore_timeout: %d\n", + trf->state, trf->ignore_timeout); + + mutex_lock(&trf->lock); + + if (trf->ignore_timeout) + trf->ignore_timeout = false; + else if (trf->state == TRF7970A_ST_WAIT_FOR_RX_DATA_CONT) + trf7970a_drain_fifo(trf, TRF7970A_IRQ_STATUS_SRX); + else if (trf->state == TRF7970A_ST_WAIT_TO_ISSUE_EOF) + trf7970a_issue_eof(trf); + else + trf7970a_send_err_upstream(trf, -ETIMEDOUT); + + mutex_unlock(&trf->lock); +} + +static int trf7970a_init(struct trf7970a *trf) +{ + int ret; + + dev_dbg(trf->dev, "Initializing device - state: %d\n", trf->state); + + ret = trf7970a_cmd(trf, TRF7970A_CMD_SOFT_INIT); + if (ret) + goto err_out; + + ret = trf7970a_cmd(trf, TRF7970A_CMD_IDLE); + if (ret) + goto err_out; + + usleep_range(1000, 2000); + + trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON; + + ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, 0); + if (ret) + goto err_out; + + trf->modulator_sys_clk_ctrl = 0; + + ret = trf7970a_write(trf, TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS, + TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 | + TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32); + if (ret) + goto err_out; + + ret = trf7970a_write(trf, TRF7970A_SPECIAL_FCN_REG1, 0); + if (ret) + goto err_out; + + trf->special_fcn_reg1 = 0; + + trf->iso_ctrl = 0xff; + return 0; + +err_out: + dev_dbg(trf->dev, "Couldn't init device: %d\n", ret); + return ret; +} + +static void trf7970a_switch_rf_off(struct trf7970a *trf) +{ + if ((trf->state == TRF7970A_ST_PWR_OFF) || + (trf->state == TRF7970A_ST_RF_OFF)) + return; + + dev_dbg(trf->dev, "Switching rf off\n"); + + trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON; + + trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, trf->chip_status_ctrl); + + trf->aborting = false; + trf->state = TRF7970A_ST_RF_OFF; + + pm_runtime_mark_last_busy(trf->dev); + pm_runtime_put_autosuspend(trf->dev); +} + +static int trf7970a_switch_rf_on(struct trf7970a *trf) +{ + int ret; + + dev_dbg(trf->dev, "Switching rf on\n"); + + pm_runtime_get_sync(trf->dev); + + if (trf->state != TRF7970A_ST_RF_OFF) { /* Power on, RF off */ + dev_err(trf->dev, "%s - Incorrect state: %d\n", __func__, + trf->state); + return -EINVAL; + } + + ret = trf7970a_init(trf); + if (ret) { + dev_err(trf->dev, "%s - Can't initialize: %d\n", __func__, ret); + return ret; + } + + trf->state = TRF7970A_ST_IDLE; + + return 0; +} + +static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + int ret = 0; + + dev_dbg(trf->dev, "Switching RF - state: %d, on: %d\n", trf->state, on); + + mutex_lock(&trf->lock); + + if (on) { + switch (trf->state) { + case TRF7970A_ST_PWR_OFF: + case TRF7970A_ST_RF_OFF: + ret = trf7970a_switch_rf_on(trf); + break; + case TRF7970A_ST_IDLE: + case TRF7970A_ST_IDLE_RX_BLOCKED: + break; + default: + dev_err(trf->dev, "%s - Invalid request: %d %d\n", + __func__, trf->state, on); + trf7970a_switch_rf_off(trf); + ret = -EINVAL; + } + } else { + switch (trf->state) { + case TRF7970A_ST_PWR_OFF: + case TRF7970A_ST_RF_OFF: + break; + default: + dev_err(trf->dev, "%s - Invalid request: %d %d\n", + __func__, trf->state, on); + ret = -EINVAL; + /* FALLTHROUGH */ + case TRF7970A_ST_IDLE: + case TRF7970A_ST_IDLE_RX_BLOCKED: + case TRF7970A_ST_WAIT_FOR_RX_DATA: + case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT: + trf7970a_switch_rf_off(trf); + } + } + + mutex_unlock(&trf->lock); + return ret; +} + +static int trf7970a_in_config_rf_tech(struct trf7970a *trf, int tech) +{ + int ret = 0; + + dev_dbg(trf->dev, "rf technology: %d\n", tech); + + switch (tech) { + case NFC_DIGITAL_RF_TECH_106A: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443A_106; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK; + trf->guard_time = TRF7970A_GUARD_TIME_NFCA; + break; + case NFC_DIGITAL_RF_TECH_106B: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443B_106; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + trf->guard_time = TRF7970A_GUARD_TIME_NFCB; + break; + case NFC_DIGITAL_RF_TECH_212F: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_212; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + trf->guard_time = TRF7970A_GUARD_TIME_NFCF; + break; + case NFC_DIGITAL_RF_TECH_424F: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_424; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + trf->guard_time = TRF7970A_GUARD_TIME_NFCF; + break; + case NFC_DIGITAL_RF_TECH_ISO15693: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK; + trf->guard_time = TRF7970A_GUARD_TIME_15693; + break; + default: + dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech); + return -EINVAL; + } + + trf->technology = tech; + + /* If in initiator mode and not changing the RF tech due to a + * PSL sequence (indicated by 'trf->iso_ctrl == 0xff' from + * trf7970a_init()), clear the NFC Target Detection Level register + * due to erratum. + */ + if (trf->iso_ctrl == 0xff) + ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, 0); + + return ret; +} + +static int trf7970a_is_rf_field(struct trf7970a *trf, bool *is_rf_field) +{ + int ret; + u8 rssi; + + ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, + trf->chip_status_ctrl | TRF7970A_CHIP_STATUS_REC_ON); + if (ret) + return ret; + + ret = trf7970a_cmd(trf, TRF7970A_CMD_TEST_EXT_RF); + if (ret) + return ret; + + usleep_range(50, 60); + + ret = trf7970a_read(trf, TRF7970A_RSSI_OSC_STATUS, &rssi); + if (ret) + return ret; + + ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, + trf->chip_status_ctrl); + if (ret) + return ret; + + if (rssi & TRF7970A_RSSI_OSC_STATUS_RSSI_MASK) + *is_rf_field = true; + else + *is_rf_field = false; + + return 0; +} + +static int trf7970a_in_config_framing(struct trf7970a *trf, int framing) +{ + u8 iso_ctrl = trf->iso_ctrl_tech; + bool is_rf_field = false; + int ret; + + dev_dbg(trf->dev, "framing: %d\n", framing); + + switch (framing) { + case NFC_DIGITAL_FRAMING_NFCA_SHORT: + case NFC_DIGITAL_FRAMING_NFCA_STANDARD: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC; + iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; + break; + case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: + case NFC_DIGITAL_FRAMING_NFCA_T4T: + case NFC_DIGITAL_FRAMING_NFCB: + case NFC_DIGITAL_FRAMING_NFCB_T4T: + case NFC_DIGITAL_FRAMING_NFCF: + case NFC_DIGITAL_FRAMING_NFCF_T3T: + case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY: + case NFC_DIGITAL_FRAMING_ISO15693_T5T: + case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP: + case NFC_DIGITAL_FRAMING_NFCF_NFC_DEP: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT; + iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N; + break; + case NFC_DIGITAL_FRAMING_NFCA_T2T: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT; + iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; + break; + default: + dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing); + return -EINVAL; + } + + trf->framing = framing; + + if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) { + ret = trf7970a_is_rf_field(trf, &is_rf_field); + if (ret) + return ret; + + if (is_rf_field) + return -EBUSY; + } + + if (iso_ctrl != trf->iso_ctrl) { + ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl); + if (ret) + return ret; + + trf->iso_ctrl = iso_ctrl; + + ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, + trf->modulator_sys_clk_ctrl); + if (ret) + return ret; + } + + if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) { + ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, + trf->chip_status_ctrl | + TRF7970A_CHIP_STATUS_RF_ON); + if (ret) + return ret; + + trf->chip_status_ctrl |= TRF7970A_CHIP_STATUS_RF_ON; + + usleep_range(trf->guard_time, trf->guard_time + 1000); + } + + return 0; +} + +static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type, + int param) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + int ret; + + dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param); + + mutex_lock(&trf->lock); + + trf->is_initiator = true; + + if ((trf->state == TRF7970A_ST_PWR_OFF) || + (trf->state == TRF7970A_ST_RF_OFF)) { + ret = trf7970a_switch_rf_on(trf); + if (ret) + goto err_unlock; + } + + switch (type) { + case NFC_DIGITAL_CONFIG_RF_TECH: + ret = trf7970a_in_config_rf_tech(trf, param); + break; + case NFC_DIGITAL_CONFIG_FRAMING: + ret = trf7970a_in_config_framing(trf, param); + break; + default: + dev_dbg(trf->dev, "Unknown type: %d\n", type); + ret = -EINVAL; + } + +err_unlock: + mutex_unlock(&trf->lock); + return ret; +} + +static int trf7970a_is_iso15693_write_or_lock(u8 cmd) +{ + switch (cmd) { + case ISO15693_CMD_WRITE_SINGLE_BLOCK: + case ISO15693_CMD_LOCK_BLOCK: + case ISO15693_CMD_WRITE_MULTIPLE_BLOCK: + case ISO15693_CMD_WRITE_AFI: + case ISO15693_CMD_LOCK_AFI: + case ISO15693_CMD_WRITE_DSFID: + case ISO15693_CMD_LOCK_DSFID: + return 1; + break; + default: + return 0; + } +} + +static int trf7970a_per_cmd_config(struct trf7970a *trf, struct sk_buff *skb) +{ + u8 *req = skb->data; + u8 special_fcn_reg1, iso_ctrl; + int ret; + + trf->issue_eof = false; + + /* When issuing Type 2 read command, make sure the '4_bit_RX' bit in + * special functions register 1 is cleared; otherwise, its a write or + * sector select command and '4_bit_RX' must be set. + * + * When issuing an ISO 15693 command, inspect the flags byte to see + * what speed to use. Also, remember if the OPTION flag is set on + * a Type 5 write or lock command so the driver will know that it + * has to send an EOF in order to get a response. + */ + if ((trf->technology == NFC_DIGITAL_RF_TECH_106A) && + (trf->framing == NFC_DIGITAL_FRAMING_NFCA_T2T)) { + if (req[0] == NFC_T2T_CMD_READ) + special_fcn_reg1 = 0; + else + special_fcn_reg1 = TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX; + + if (special_fcn_reg1 != trf->special_fcn_reg1) { + ret = trf7970a_write(trf, TRF7970A_SPECIAL_FCN_REG1, + special_fcn_reg1); + if (ret) + return ret; + + trf->special_fcn_reg1 = special_fcn_reg1; + } + } else if (trf->technology == NFC_DIGITAL_RF_TECH_ISO15693) { + iso_ctrl = trf->iso_ctrl & ~TRF7970A_ISO_CTRL_RFID_SPEED_MASK; + + switch (req[0] & ISO15693_REQ_FLAG_SPEED_MASK) { + case 0x00: + iso_ctrl |= TRF7970A_ISO_CTRL_15693_SGL_1OF4_662; + break; + case ISO15693_REQ_FLAG_SUB_CARRIER: + iso_ctrl |= TRF7970A_ISO_CTRL_15693_DBL_1OF4_667a; + break; + case ISO15693_REQ_FLAG_DATA_RATE: + iso_ctrl |= TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648; + break; + case (ISO15693_REQ_FLAG_SUB_CARRIER | + ISO15693_REQ_FLAG_DATA_RATE): + iso_ctrl |= TRF7970A_ISO_CTRL_15693_DBL_1OF4_2669; + break; + } + + if (iso_ctrl != trf->iso_ctrl) { + ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl); + if (ret) + return ret; + + trf->iso_ctrl = iso_ctrl; + } + + if ((trf->framing == NFC_DIGITAL_FRAMING_ISO15693_T5T) && + trf7970a_is_iso15693_write_or_lock(req[1]) && + (req[0] & ISO15693_REQ_FLAG_OPTION)) + trf->issue_eof = true; + } + + return 0; +} + +static int trf7970a_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + u8 prefix[5]; + unsigned int len; + int ret; + u8 status; + + dev_dbg(trf->dev, "New request - state: %d, timeout: %d ms, len: %d\n", + trf->state, timeout, skb->len); + + if (skb->len > TRF7970A_TX_MAX) + return -EINVAL; + + mutex_lock(&trf->lock); + + if ((trf->state != TRF7970A_ST_IDLE) && + (trf->state != TRF7970A_ST_IDLE_RX_BLOCKED)) { + dev_err(trf->dev, "%s - Bogus state: %d\n", __func__, + trf->state); + ret = -EIO; + goto out_err; + } + + if (trf->aborting) { + dev_dbg(trf->dev, "Abort process complete\n"); + trf->aborting = false; + ret = -ECANCELED; + goto out_err; + } + + if (timeout) { + trf->rx_skb = nfc_alloc_recv_skb(TRF7970A_RX_SKB_ALLOC_SIZE, + GFP_KERNEL); + if (!trf->rx_skb) { + dev_dbg(trf->dev, "Can't alloc rx_skb\n"); + ret = -ENOMEM; + goto out_err; + } + } + + if (trf->state == TRF7970A_ST_IDLE_RX_BLOCKED) { + ret = trf7970a_cmd(trf, TRF7970A_CMD_ENABLE_RX); + if (ret) + goto out_err; + + trf->state = TRF7970A_ST_IDLE; + } + + if (trf->is_initiator) { + ret = trf7970a_per_cmd_config(trf, skb); + if (ret) + goto out_err; + } + + trf->ddev = ddev; + trf->tx_skb = skb; + trf->cb = cb; + trf->cb_arg = arg; + trf->timeout = timeout; + trf->ignore_timeout = false; + + len = skb->len; + + /* TX data must be prefixed with a FIFO reset cmd, a cmd that depends + * on what the current framing is, the address of the TX length byte 1 + * register (0x1d), and the 2 byte length of the data to be transmitted. + * That totals 5 bytes. + */ + prefix[0] = TRF7970A_CMD_BIT_CTRL | + TRF7970A_CMD_BIT_OPCODE(TRF7970A_CMD_FIFO_RESET); + prefix[1] = TRF7970A_CMD_BIT_CTRL | + TRF7970A_CMD_BIT_OPCODE(trf->tx_cmd); + prefix[2] = TRF7970A_CMD_BIT_CONTINUOUS | TRF7970A_TX_LENGTH_BYTE1; + + if (trf->framing == NFC_DIGITAL_FRAMING_NFCA_SHORT) { + prefix[3] = 0x00; + prefix[4] = 0x0f; /* 7 bits */ + } else { + prefix[3] = (len & 0xf00) >> 4; + prefix[3] |= ((len & 0xf0) >> 4); + prefix[4] = ((len & 0x0f) << 4); + } + + len = min_t(int, skb->len, TRF7970A_FIFO_SIZE); + + /* Clear possible spurious interrupt */ + ret = trf7970a_read_irqstatus(trf, &status); + if (ret) + goto out_err; + + ret = trf7970a_transmit(trf, skb, len, prefix, sizeof(prefix)); + if (ret) { + kfree_skb(trf->rx_skb); + trf->rx_skb = NULL; + } + +out_err: + mutex_unlock(&trf->lock); + return ret; +} + +static int trf7970a_tg_config_rf_tech(struct trf7970a *trf, int tech) +{ + int ret = 0; + + dev_dbg(trf->dev, "rf technology: %d\n", tech); + + switch (tech) { + case NFC_DIGITAL_RF_TECH_106A: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE | + TRF7970A_ISO_CTRL_NFC_CE | + TRF7970A_ISO_CTRL_NFC_CE_14443A; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK; + break; + case NFC_DIGITAL_RF_TECH_212F: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE | + TRF7970A_ISO_CTRL_NFC_NFCF_212; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + break; + case NFC_DIGITAL_RF_TECH_424F: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE | + TRF7970A_ISO_CTRL_NFC_NFCF_424; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + break; + default: + dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech); + return -EINVAL; + } + + trf->technology = tech; + + /* Normally we write the ISO_CTRL register in + * trf7970a_tg_config_framing() because the framing can change + * the value written. However, when sending a PSL RES, + * digital_tg_send_psl_res_complete() doesn't call + * trf7970a_tg_config_framing() so we must write the register + * here. + */ + if ((trf->framing == NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED) && + (trf->iso_ctrl_tech != trf->iso_ctrl)) { + ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, + trf->iso_ctrl_tech); + + trf->iso_ctrl = trf->iso_ctrl_tech; + } + + return ret; +} + +/* Since this is a target routine, several of the framing calls are + * made between receiving the request and sending the response so they + * should take effect until after the response is sent. This is accomplished + * by skipping the ISO_CTRL register write here and doing it in the interrupt + * handler. + */ +static int trf7970a_tg_config_framing(struct trf7970a *trf, int framing) +{ + u8 iso_ctrl = trf->iso_ctrl_tech; + int ret; + + dev_dbg(trf->dev, "framing: %d\n", framing); + + switch (framing) { + case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC; + iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; + break; + case NFC_DIGITAL_FRAMING_NFCA_STANDARD: + case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: + case NFC_DIGITAL_FRAMING_NFCA_ANTICOL_COMPLETE: + /* These ones are applied in the interrupt handler */ + iso_ctrl = trf->iso_ctrl; /* Don't write to ISO_CTRL yet */ + break; + case NFC_DIGITAL_FRAMING_NFCF_NFC_DEP: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT; + iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N; + break; + case NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT; + iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N; + break; + default: + dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing); + return -EINVAL; + } + + trf->framing = framing; + + if (iso_ctrl != trf->iso_ctrl) { + ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl); + if (ret) + return ret; + + trf->iso_ctrl = iso_ctrl; + + ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, + trf->modulator_sys_clk_ctrl); + if (ret) + return ret; + } + + if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) { + ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, + trf->chip_status_ctrl | + TRF7970A_CHIP_STATUS_RF_ON); + if (ret) + return ret; + + trf->chip_status_ctrl |= TRF7970A_CHIP_STATUS_RF_ON; + } + + return 0; +} + +static int trf7970a_tg_configure_hw(struct nfc_digital_dev *ddev, int type, + int param) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + int ret; + + dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param); + + mutex_lock(&trf->lock); + + trf->is_initiator = false; + + if ((trf->state == TRF7970A_ST_PWR_OFF) || + (trf->state == TRF7970A_ST_RF_OFF)) { + ret = trf7970a_switch_rf_on(trf); + if (ret) + goto err_unlock; + } + + switch (type) { + case NFC_DIGITAL_CONFIG_RF_TECH: + ret = trf7970a_tg_config_rf_tech(trf, param); + break; + case NFC_DIGITAL_CONFIG_FRAMING: + ret = trf7970a_tg_config_framing(trf, param); + break; + default: + dev_dbg(trf->dev, "Unknown type: %d\n", type); + ret = -EINVAL; + } + +err_unlock: + mutex_unlock(&trf->lock); + return ret; +} + +static int _trf7970a_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg, bool mode_detect) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + int ret; + + mutex_lock(&trf->lock); + + if ((trf->state != TRF7970A_ST_IDLE) && + (trf->state != TRF7970A_ST_IDLE_RX_BLOCKED)) { + dev_err(trf->dev, "%s - Bogus state: %d\n", __func__, + trf->state); + ret = -EIO; + goto out_err; + } + + if (trf->aborting) { + dev_dbg(trf->dev, "Abort process complete\n"); + trf->aborting = false; + ret = -ECANCELED; + goto out_err; + } + + trf->rx_skb = nfc_alloc_recv_skb(TRF7970A_RX_SKB_ALLOC_SIZE, + GFP_KERNEL); + if (!trf->rx_skb) { + dev_dbg(trf->dev, "Can't alloc rx_skb\n"); + ret = -ENOMEM; + goto out_err; + } + + ret = trf7970a_write(trf, TRF7970A_RX_SPECIAL_SETTINGS, + TRF7970A_RX_SPECIAL_SETTINGS_HBT | + TRF7970A_RX_SPECIAL_SETTINGS_M848 | + TRF7970A_RX_SPECIAL_SETTINGS_C424 | + TRF7970A_RX_SPECIAL_SETTINGS_C212); + if (ret) + goto out_err; + + ret = trf7970a_write(trf, TRF7970A_REG_IO_CTRL, + TRF7970A_REG_IO_CTRL_VRS(0x1)); + if (ret) + goto out_err; + + ret = trf7970a_write(trf, TRF7970A_NFC_LOW_FIELD_LEVEL, + TRF7970A_NFC_LOW_FIELD_LEVEL_RFDET(0x3)); + if (ret) + goto out_err; + + ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, + TRF7970A_NFC_TARGET_LEVEL_RFDET(0x7)); + if (ret) + goto out_err; + + trf->ddev = ddev; + trf->cb = cb; + trf->cb_arg = arg; + trf->timeout = timeout; + trf->ignore_timeout = false; + + ret = trf7970a_cmd(trf, TRF7970A_CMD_ENABLE_RX); + if (ret) + goto out_err; + + trf->state = mode_detect ? TRF7970A_ST_LISTENING_MD : + TRF7970A_ST_LISTENING; + + schedule_delayed_work(&trf->timeout_work, msecs_to_jiffies(timeout)); + +out_err: + mutex_unlock(&trf->lock); + return ret; +} + +static int trf7970a_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + + dev_dbg(trf->dev, "Listen - state: %d, timeout: %d ms\n", + trf->state, timeout); + + return _trf7970a_tg_listen(ddev, timeout, cb, arg, false); +} + +static int trf7970a_tg_listen_md(struct nfc_digital_dev *ddev, + u16 timeout, nfc_digital_cmd_complete_t cb, void *arg) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + int ret; + + dev_dbg(trf->dev, "Listen MD - state: %d, timeout: %d ms\n", + trf->state, timeout); + + ret = trf7970a_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, + NFC_DIGITAL_RF_TECH_106A); + if (ret) + return ret; + + ret = trf7970a_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_NFC_DEP); + if (ret) + return ret; + + return _trf7970a_tg_listen(ddev, timeout, cb, arg, true); +} + +static int trf7970a_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + + dev_dbg(trf->dev, "Get RF Tech - state: %d, rf_tech: %d\n", + trf->state, trf->md_rf_tech); + + *rf_tech = trf->md_rf_tech; + + return 0; +} + +static void trf7970a_abort_cmd(struct nfc_digital_dev *ddev) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + + dev_dbg(trf->dev, "Abort process initiated\n"); + + mutex_lock(&trf->lock); + + switch (trf->state) { + case TRF7970A_ST_WAIT_FOR_TX_FIFO: + case TRF7970A_ST_WAIT_FOR_RX_DATA: + case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT: + case TRF7970A_ST_WAIT_TO_ISSUE_EOF: + trf->aborting = true; + break; + case TRF7970A_ST_LISTENING: + trf->ignore_timeout = !cancel_delayed_work(&trf->timeout_work); + trf7970a_send_err_upstream(trf, -ECANCELED); + dev_dbg(trf->dev, "Abort process complete\n"); + break; + default: + break; + } + + mutex_unlock(&trf->lock); +} + +static struct nfc_digital_ops trf7970a_nfc_ops = { + .in_configure_hw = trf7970a_in_configure_hw, + .in_send_cmd = trf7970a_send_cmd, + .tg_configure_hw = trf7970a_tg_configure_hw, + .tg_send_cmd = trf7970a_send_cmd, + .tg_listen = trf7970a_tg_listen, + .tg_listen_md = trf7970a_tg_listen_md, + .tg_get_rf_tech = trf7970a_tg_get_rf_tech, + .switch_rf = trf7970a_switch_rf, + .abort_cmd = trf7970a_abort_cmd, +}; + +static int trf7970a_power_up(struct trf7970a *trf) +{ + int ret; + + dev_dbg(trf->dev, "Powering up - state: %d\n", trf->state); + + if (trf->state != TRF7970A_ST_PWR_OFF) + return 0; + + ret = regulator_enable(trf->regulator); + if (ret) { + dev_err(trf->dev, "%s - Can't enable VIN: %d\n", __func__, ret); + return ret; + } + + usleep_range(5000, 6000); + + if (!(trf->quirks & TRF7970A_QUIRK_EN2_MUST_STAY_LOW)) { + gpio_set_value(trf->en2_gpio, 1); + usleep_range(1000, 2000); + } + + gpio_set_value(trf->en_gpio, 1); + + usleep_range(20000, 21000); + + trf->state = TRF7970A_ST_RF_OFF; + + return 0; +} + +static int trf7970a_power_down(struct trf7970a *trf) +{ + int ret; + + dev_dbg(trf->dev, "Powering down - state: %d\n", trf->state); + + if (trf->state == TRF7970A_ST_PWR_OFF) + return 0; + + if (trf->state != TRF7970A_ST_RF_OFF) { + dev_dbg(trf->dev, "Can't power down - not RF_OFF state (%d)\n", + trf->state); + return -EBUSY; + } + + gpio_set_value(trf->en_gpio, 0); + gpio_set_value(trf->en2_gpio, 0); + + ret = regulator_disable(trf->regulator); + if (ret) + dev_err(trf->dev, "%s - Can't disable VIN: %d\n", __func__, + ret); + + trf->state = TRF7970A_ST_PWR_OFF; + + return ret; +} + +static int trf7970a_startup(struct trf7970a *trf) +{ + int ret; + + ret = trf7970a_power_up(trf); + if (ret) + return ret; + + pm_runtime_set_active(trf->dev); + pm_runtime_enable(trf->dev); + pm_runtime_mark_last_busy(trf->dev); + + return 0; +} + +static void trf7970a_shutdown(struct trf7970a *trf) +{ + switch (trf->state) { + case TRF7970A_ST_WAIT_FOR_TX_FIFO: + case TRF7970A_ST_WAIT_FOR_RX_DATA: + case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT: + case TRF7970A_ST_WAIT_TO_ISSUE_EOF: + case TRF7970A_ST_LISTENING: + trf7970a_send_err_upstream(trf, -ECANCELED); + /* FALLTHROUGH */ + case TRF7970A_ST_IDLE: + case TRF7970A_ST_IDLE_RX_BLOCKED: + trf7970a_switch_rf_off(trf); + break; + default: + break; + } + + pm_runtime_disable(trf->dev); + pm_runtime_set_suspended(trf->dev); + + trf7970a_power_down(trf); +} + +static int trf7970a_get_autosuspend_delay(struct device_node *np) +{ + int autosuspend_delay, ret; + + ret = of_property_read_u32(np, "autosuspend-delay", &autosuspend_delay); + if (ret) + autosuspend_delay = TRF7970A_AUTOSUSPEND_DELAY; + + return autosuspend_delay; +} + +static int trf7970a_get_vin_voltage_override(struct device_node *np, + u32 *vin_uvolts) +{ + return of_property_read_u32(np, "vin-voltage-override", vin_uvolts); +} + +static int trf7970a_probe(struct spi_device *spi) +{ + struct device_node *np = spi->dev.of_node; + struct trf7970a *trf; + int uvolts, autosuspend_delay, ret; + + if (!np) { + dev_err(&spi->dev, "No Device Tree entry\n"); + return -EINVAL; + } + + trf = devm_kzalloc(&spi->dev, sizeof(*trf), GFP_KERNEL); + if (!trf) + return -ENOMEM; + + trf->state = TRF7970A_ST_PWR_OFF; + trf->dev = &spi->dev; + trf->spi = spi; + + spi->mode = SPI_MODE_1; + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(trf->dev, "Can't set up SPI Communication\n"); + return ret; + } + + if (of_property_read_bool(np, "irq-status-read-quirk")) + trf->quirks |= TRF7970A_QUIRK_IRQ_STATUS_READ; + + /* There are two enable pins - both must be present */ + trf->en_gpio = of_get_named_gpio(np, "ti,enable-gpios", 0); + if (!gpio_is_valid(trf->en_gpio)) { + dev_err(trf->dev, "No EN GPIO property\n"); + return trf->en_gpio; + } + + ret = devm_gpio_request_one(trf->dev, trf->en_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "trf7970a EN"); + if (ret) { + dev_err(trf->dev, "Can't request EN GPIO: %d\n", ret); + return ret; + } + + trf->en2_gpio = of_get_named_gpio(np, "ti,enable-gpios", 1); + if (!gpio_is_valid(trf->en2_gpio)) { + dev_err(trf->dev, "No EN2 GPIO property\n"); + return trf->en2_gpio; + } + + ret = devm_gpio_request_one(trf->dev, trf->en2_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "trf7970a EN2"); + if (ret) { + dev_err(trf->dev, "Can't request EN2 GPIO: %d\n", ret); + return ret; + } + + if (of_property_read_bool(np, "en2-rf-quirk")) + trf->quirks |= TRF7970A_QUIRK_EN2_MUST_STAY_LOW; + + ret = devm_request_threaded_irq(trf->dev, spi->irq, NULL, + trf7970a_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "trf7970a", trf); + if (ret) { + dev_err(trf->dev, "Can't request IRQ#%d: %d\n", spi->irq, ret); + return ret; + } + + mutex_init(&trf->lock); + INIT_DELAYED_WORK(&trf->timeout_work, trf7970a_timeout_work_handler); + + trf->regulator = devm_regulator_get(&spi->dev, "vin"); + if (IS_ERR(trf->regulator)) { + ret = PTR_ERR(trf->regulator); + dev_err(trf->dev, "Can't get VIN regulator: %d\n", ret); + goto err_destroy_lock; + } + + ret = regulator_enable(trf->regulator); + if (ret) { + dev_err(trf->dev, "Can't enable VIN: %d\n", ret); + goto err_destroy_lock; + } + + ret = trf7970a_get_vin_voltage_override(np, &uvolts); + if (ret) + uvolts = regulator_get_voltage(trf->regulator); + + if (uvolts > 4000000) + trf->chip_status_ctrl = TRF7970A_CHIP_STATUS_VRS5_3; + + trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops, + TRF7970A_SUPPORTED_PROTOCOLS, + NFC_DIGITAL_DRV_CAPS_IN_CRC | + NFC_DIGITAL_DRV_CAPS_TG_CRC, 0, 0); + if (!trf->ddev) { + dev_err(trf->dev, "Can't allocate NFC digital device\n"); + ret = -ENOMEM; + goto err_disable_regulator; + } + + nfc_digital_set_parent_dev(trf->ddev, trf->dev); + nfc_digital_set_drvdata(trf->ddev, trf); + spi_set_drvdata(spi, trf); + + autosuspend_delay = trf7970a_get_autosuspend_delay(np); + + pm_runtime_set_autosuspend_delay(trf->dev, autosuspend_delay); + pm_runtime_use_autosuspend(trf->dev); + + ret = trf7970a_startup(trf); + if (ret) + goto err_free_ddev; + + ret = nfc_digital_register_device(trf->ddev); + if (ret) { + dev_err(trf->dev, "Can't register NFC digital device: %d\n", + ret); + goto err_shutdown; + } + + return 0; + +err_shutdown: + trf7970a_shutdown(trf); +err_free_ddev: + nfc_digital_free_device(trf->ddev); +err_disable_regulator: + regulator_disable(trf->regulator); +err_destroy_lock: + mutex_destroy(&trf->lock); + return ret; +} + +static int trf7970a_remove(struct spi_device *spi) +{ + struct trf7970a *trf = spi_get_drvdata(spi); + + mutex_lock(&trf->lock); + + trf7970a_shutdown(trf); + + mutex_unlock(&trf->lock); + + nfc_digital_unregister_device(trf->ddev); + nfc_digital_free_device(trf->ddev); + + regulator_disable(trf->regulator); + + mutex_destroy(&trf->lock); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int trf7970a_suspend(struct device *dev) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct trf7970a *trf = spi_get_drvdata(spi); + + dev_dbg(dev, "Suspend\n"); + + mutex_lock(&trf->lock); + + trf7970a_shutdown(trf); + + mutex_unlock(&trf->lock); + + return 0; +} + +static int trf7970a_resume(struct device *dev) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct trf7970a *trf = spi_get_drvdata(spi); + int ret; + + dev_dbg(dev, "Resume\n"); + + mutex_lock(&trf->lock); + + ret = trf7970a_startup(trf); + + mutex_unlock(&trf->lock); + + return ret; +} +#endif + +#ifdef CONFIG_PM +static int trf7970a_pm_runtime_suspend(struct device *dev) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct trf7970a *trf = spi_get_drvdata(spi); + int ret; + + dev_dbg(dev, "Runtime suspend\n"); + + mutex_lock(&trf->lock); + + ret = trf7970a_power_down(trf); + + mutex_unlock(&trf->lock); + + return ret; +} + +static int trf7970a_pm_runtime_resume(struct device *dev) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct trf7970a *trf = spi_get_drvdata(spi); + int ret; + + dev_dbg(dev, "Runtime resume\n"); + + ret = trf7970a_power_up(trf); + if (!ret) + pm_runtime_mark_last_busy(dev); + + return ret; +} +#endif + +static const struct dev_pm_ops trf7970a_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(trf7970a_suspend, trf7970a_resume) + SET_RUNTIME_PM_OPS(trf7970a_pm_runtime_suspend, + trf7970a_pm_runtime_resume, NULL) +}; + +static const struct spi_device_id trf7970a_id_table[] = { + { "trf7970a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, trf7970a_id_table); + +static struct spi_driver trf7970a_spi_driver = { + .probe = trf7970a_probe, + .remove = trf7970a_remove, + .id_table = trf7970a_id_table, + .driver = { + .name = "trf7970a", + .owner = THIS_MODULE, + .pm = &trf7970a_pm_ops, + }, +}; + +module_spi_driver(trf7970a_spi_driver); + +MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI trf7970a RFID/NFC Transceiver Driver"); |