summaryrefslogtreecommitdiff
path: root/drivers/nfc
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/nfc
Initial import
Diffstat (limited to 'drivers/nfc')
-rw-r--r--drivers/nfc/Kconfig77
-rw-r--r--drivers/nfc/Makefile18
-rw-r--r--drivers/nfc/mei_phy.c173
-rw-r--r--drivers/nfc/mei_phy.h30
-rw-r--r--drivers/nfc/microread/Kconfig35
-rw-r--r--drivers/nfc/microread/Makefile10
-rw-r--r--drivers/nfc/microread/i2c.c328
-rw-r--r--drivers/nfc/microread/mei.c109
-rw-r--r--drivers/nfc/microread/microread.c734
-rw-r--r--drivers/nfc/microread/microread.h31
-rw-r--r--drivers/nfc/nfcmrvl/Kconfig23
-rw-r--r--drivers/nfc/nfcmrvl/Makefile9
-rw-r--r--drivers/nfc/nfcmrvl/main.c165
-rw-r--r--drivers/nfc/nfcmrvl/nfcmrvl.h48
-rw-r--r--drivers/nfc/nfcmrvl/usb.c459
-rw-r--r--drivers/nfc/nfcsim.c541
-rw-r--r--drivers/nfc/nfcwilink.c578
-rw-r--r--drivers/nfc/nxp-nci/Kconfig25
-rw-r--r--drivers/nfc/nxp-nci/Makefile11
-rw-r--r--drivers/nfc/nxp-nci/core.c186
-rw-r--r--drivers/nfc/nxp-nci/firmware.c325
-rw-r--r--drivers/nfc/nxp-nci/i2c.c415
-rw-r--r--drivers/nfc/nxp-nci/nxp-nci.h89
-rw-r--r--drivers/nfc/pn533.c3313
-rw-r--r--drivers/nfc/pn544/Kconfig34
-rw-r--r--drivers/nfc/pn544/Makefile10
-rw-r--r--drivers/nfc/pn544/i2c.c1198
-rw-r--r--drivers/nfc/pn544/mei.c109
-rw-r--r--drivers/nfc/pn544/pn544.c995
-rw-r--r--drivers/nfc/pn544/pn544.h36
-rw-r--r--drivers/nfc/port100.c1579
-rw-r--r--drivers/nfc/st21nfca/Kconfig23
-rw-r--r--drivers/nfc/st21nfca/Makefile9
-rw-r--r--drivers/nfc/st21nfca/i2c.c695
-rw-r--r--drivers/nfc/st21nfca/st21nfca.c1063
-rw-r--r--drivers/nfc/st21nfca/st21nfca.h99
-rw-r--r--drivers/nfc/st21nfca/st21nfca_dep.c689
-rw-r--r--drivers/nfc/st21nfca/st21nfca_dep.h43
-rw-r--r--drivers/nfc/st21nfca/st21nfca_se.c420
-rw-r--r--drivers/nfc/st21nfca/st21nfca_se.h63
-rw-r--r--drivers/nfc/st21nfcb/Kconfig22
-rw-r--r--drivers/nfc/st21nfcb/Makefile9
-rw-r--r--drivers/nfc/st21nfcb/i2c.c388
-rw-r--r--drivers/nfc/st21nfcb/ndlc.c300
-rw-r--r--drivers/nfc/st21nfcb/ndlc.h59
-rw-r--r--drivers/nfc/st21nfcb/st21nfcb.c140
-rw-r--r--drivers/nfc/st21nfcb/st21nfcb.h38
-rw-r--r--drivers/nfc/st21nfcb/st21nfcb_se.c713
-rw-r--r--drivers/nfc/st21nfcb/st21nfcb_se.h61
-rw-r--r--drivers/nfc/trf7970a.c2218
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(&microread_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(&microread_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(&microread_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, &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;
+
+ 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, &param, 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, &param, 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");