summaryrefslogtreecommitdiffstats
path: root/drivers/nfc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nfc')
-rw-r--r--drivers/nfc/Kconfig46
-rw-r--r--drivers/nfc/Makefile11
-rw-r--r--drivers/nfc/mei_phy.c173
-rw-r--r--drivers/nfc/mei_phy.h30
-rw-r--r--drivers/nfc/microread/Kconfig38
-rw-r--r--drivers/nfc/microread/Makefile10
-rw-r--r--drivers/nfc/microread/i2c.c340
-rw-r--r--drivers/nfc/microread/mei.c111
-rw-r--r--drivers/nfc/microread/microread.c728
-rw-r--r--drivers/nfc/microread/microread.h33
-rw-r--r--drivers/nfc/nfcwilink.c609
-rw-r--r--drivers/nfc/pn533.c2864
-rw-r--r--drivers/nfc/pn544/Kconfig37
-rw-r--r--drivers/nfc/pn544/Makefile10
-rw-r--r--drivers/nfc/pn544/i2c.c470
-rw-r--r--drivers/nfc/pn544/mei.c111
-rw-r--r--drivers/nfc/pn544/pn544.c881
-rw-r--r--drivers/nfc/pn544/pn544.h32
18 files changed, 6534 insertions, 0 deletions
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
new file mode 100644
index 0000000..4eb7f07
--- /dev/null
+++ b/drivers/nfc/Kconfig
@@ -0,0 +1,46 @@
+#
+# Near Field Communication (NFC) devices
+#
+
+menu "Near Field Communication (NFC) devices"
+ depends on NFC
+
+config NFC_PN533
+ tristate "NXP PN533 USB driver"
+ depends on m
+ 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 m
+ 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_MEI_PHY
+ depends on !BACKPORT_KERNEL_3_10
+ tristate "MEI bus NFC device support"
+ depends on m
+ 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.
+
+source "drivers/nfc/pn544/Kconfig"
+source "drivers/nfc/microread/Kconfig"
+
+endmenu
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
new file mode 100644
index 0000000..0d5335e
--- /dev/null
+++ b/drivers/nfc/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for nfc devices
+#
+
+obj-$(CPTCFG_NFC_PN544) += pn544/
+obj-$(CPTCFG_NFC_MICROREAD) += microread/
+obj-$(CPTCFG_NFC_PN533) += pn533.o
+obj-$(CPTCFG_NFC_WILINK) += nfcwilink.o
+obj-$(CPTCFG_NFC_MEI_PHY) += mei_phy.o
+
+ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c
new file mode 100644
index 0000000..1201bdb
--- /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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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;
+} __attribute__((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("MEI_PHY: Could not enable device\n");
+ return r;
+ }
+
+ r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy);
+ if (r) {
+ pr_err("MEY_PHY: 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);
+ 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 0000000..d669900
--- /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 0000000..8ae8591
--- /dev/null
+++ b/drivers/nfc/microread/Kconfig
@@ -0,0 +1,38 @@
+config NFC_MICROREAD
+ tristate "Inside Secure microread NFC driver"
+ depends on m
+ depends on NFC_HCI
+ depends on 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 m
+ 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 m
+ 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 0000000..c44077a
--- /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-$(CPTCFG_NFC_MICROREAD) += microread.o
+obj-$(CPTCFG_NFC_MICROREAD_I2C) += microread_i2c.o
+obj-$(CPTCFG_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 0000000..1010894
--- /dev/null
+++ b/drivers/nfc/microread/i2c.c
@@ -0,0 +1,340 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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(MICROREAD_I2C_DRIVER_NAME
+ ": CRC error 0x%x != 0x%x\n",
+ crc, skb->data[skb->len-1]);
+
+ pr_info(DRIVER_DESC ": %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;
+
+ pr_debug("%s\n", __func__);
+
+ r = i2c_master_recv(client, &len, 1);
+ if (r != 1) {
+ dev_err(&client->dev, "cannot read len byte\n");
+ return -EREMOTEIO;
+ }
+
+ if ((len < MICROREAD_I2C_LLC_MIN_SIZE) ||
+ (len > MICROREAD_I2C_LLC_MAX_SIZE)) {
+ dev_err(&client->dev, "invalid len byte\n");
+ pr_err("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;
+ dev_dbg(&client->dev, "IRQ\n");
+
+ 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", client);
+
+ if (!pdata) {
+ dev_err(&client->dev, "client %p: missing platform data",
+ client);
+ return -EINVAL;
+ }
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy),
+ GFP_KERNEL);
+ if (!phy) {
+ dev_err(&client->dev, "Can't allocate microread 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) {
+ dev_err(&client->dev, "Unable to register IRQ handler");
+ 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;
+
+ dev_info(&client->dev, "Probed");
+
+ 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);
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ 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 0000000..cdf1bc5
--- /dev/null
+++ b/drivers/nfc/microread/mei.c
@@ -0,0 +1,111 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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);
+
+ pr_info("Removing microread\n");
+
+ 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 0000000..3420d83
--- /dev/null
+++ b/drivers/nfc/microread/microread.c
@@ -0,0 +1,728 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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];
+ memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A_UID],
+ skb->data[MICROREAD_EMCF_A_LEN]);
+ targets->nfcid1_len = skb->data[MICROREAD_EMCF_A_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];
+ memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_A3_UID],
+ skb->data[MICROREAD_EMCF_A3_LEN]);
+ targets->nfcid1_len = skb->data[MICROREAD_EMCF_A3_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", r);
+}
+
+static int microread_event_received(struct nfc_hci_dev *hdev, u8 gate,
+ u8 event, struct sk_buff *skb)
+{
+ int r;
+ 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, se;
+ struct nfc_hci_init_data init_data;
+ int r;
+
+ info = kzalloc(sizeof(struct microread_info), GFP_KERNEL);
+ if (!info) {
+ pr_err("Cannot allocate memory for microread_info.\n");
+ 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;
+
+ se = NFC_SE_UICC | NFC_SE_EMBEDDED;
+
+ info->hdev = nfc_hci_allocate_device(&microread_hci_ops, &init_data,
+ quirks, protocols, se, 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 0000000..64b447a
--- /dev/null
+++ b/drivers/nfc/microread/microread.h
@@ -0,0 +1,33 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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/nfcwilink.c b/drivers/nfc/nfcwilink.c
new file mode 100644
index 0000000..3b731ac
--- /dev/null
+++ b/drivers/nfc/nfcwilink.c
@@ -0,0 +1,609 @@
+/*
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#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 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;
+
+ nfc_dev_dbg(&drv->pdev->dev, "get_bts_file_name entry");
+
+ skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd),
+ GFP_KERNEL);
+ if (!skb) {
+ nfc_dev_err(&drv->pdev->dev,
+ "no memory for nci_vs_nfcc_info_cmd");
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *)drv->ndev;
+
+ 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(skb);
+ if (rc)
+ return rc;
+
+ comp_ret = wait_for_completion_timeout(&drv->completed,
+ msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
+ nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
+ comp_ret);
+ if (comp_ret == 0) {
+ nfc_dev_err(&drv->pdev->dev,
+ "timeout on wait_for_completion_timeout");
+ return -ETIMEDOUT;
+ }
+
+ nfc_dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d",
+ drv->nfcc_info.plen,
+ drv->nfcc_info.status);
+
+ if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) {
+ nfc_dev_err(&drv->pdev->dev,
+ "invalid nci_vs_nfcc_info_rsp");
+ return -EINVAL;
+ }
+
+ snprintf(file_name, BTS_FILE_NAME_MAX_SIZE,
+ "TINfcInit_%d.%d.%d.%d.bts",
+ drv->nfcc_info.hw_id,
+ drv->nfcc_info.sw_ver_x,
+ drv->nfcc_info.sw_ver_z,
+ drv->nfcc_info.patch_id);
+
+ nfc_dev_info(&drv->pdev->dev, "nfcwilink FW file name: %s", 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;
+
+ nfc_dev_dbg(&drv->pdev->dev, "send_bts_cmd entry");
+
+ /* 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_dev_err(&drv->pdev->dev,
+ "ignoring invalid bts cmd, len %d, chnl %d, opcode %d",
+ 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_dev_err(&drv->pdev->dev, "no memory for bts cmd");
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *)drv->ndev;
+
+ memcpy(skb_put(skb, len), data, len);
+
+ rc = nfcwilink_send(skb);
+ if (rc)
+ return rc;
+
+ comp_ret = wait_for_completion_timeout(&drv->completed,
+ msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
+ nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
+ comp_ret);
+ if (comp_ret == 0) {
+ nfc_dev_err(&drv->pdev->dev,
+ "timeout on wait_for_completion_timeout");
+ 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;
+
+ nfc_dev_dbg(&drv->pdev->dev, "download_fw entry");
+
+ set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
+
+ rc = nfcwilink_get_bts_file_name(drv, file_name);
+ if (rc)
+ goto exit;
+
+ rc = request_firmware(&fw, file_name, &drv->pdev->dev);
+ if (rc) {
+ nfc_dev_err(&drv->pdev->dev, "request_firmware failed %d", 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)) {
+ nfc_dev_dbg(&drv->pdev->dev,
+ "request_firmware returned size %d", len);
+ goto release_fw;
+ }
+
+ if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) !=
+ BTS_FILE_HDR_MAGIC) {
+ nfc_dev_err(&drv->pdev->dev, "wrong bts magic number");
+ 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);
+
+ nfc_dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d",
+ 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;
+
+ nfc_dev_dbg(&drv->pdev->dev, "register_complete entry");
+
+ /* 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;
+ }
+
+ nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", 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;
+ }
+
+ skb->dev = (void *) drv->ndev;
+
+ /* Forward skb to NCI core layer */
+ rc = nci_recv_frame(skb);
+ if (rc < 0) {
+ nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", 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;
+
+ nfc_dev_dbg(&drv->pdev->dev, "open entry");
+
+ 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));
+
+ nfc_dev_dbg(&drv->pdev->dev,
+ "wait_for_completion_timeout returned %ld",
+ 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_dev_err(&drv->pdev->dev,
+ "st_register_cb failed %d", rc);
+ goto clear_exit;
+ }
+ } else {
+ nfc_dev_err(&drv->pdev->dev,
+ "st_register failed %d", 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_dev_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d",
+ 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;
+
+ nfc_dev_dbg(&drv->pdev->dev, "close entry");
+
+ if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags))
+ return 0;
+
+ rc = st_unregister(&nfcwilink_proto);
+ if (rc)
+ nfc_dev_err(&drv->pdev->dev, "st_unregister failed %d", rc);
+
+ drv->st_write = NULL;
+
+ return rc;
+}
+
+static int nfcwilink_send(struct sk_buff *skb)
+{
+ struct nci_dev *ndev = (struct nci_dev *)skb->dev;
+ struct nfcwilink *drv = nci_get_drvdata(ndev);
+ struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
+ long len;
+
+ nfc_dev_dbg(&drv->pdev->dev, "send entry, len %d", 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_dev_err(&drv->pdev->dev, "st_write failed %ld", 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;
+
+ nfc_dev_dbg(&pdev->dev, "probe entry");
+
+ 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,
+ NFC_SE_NONE,
+ NFCWILINK_HDR_LEN,
+ 0);
+ if (!drv->ndev) {
+ nfc_dev_err(&pdev->dev, "nci_allocate_device failed");
+ 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_dev_err(&pdev->dev, "nci_register_device failed %d", 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;
+
+ nfc_dev_dbg(&pdev->dev, "remove entry");
+
+ if (!drv)
+ return -EFAULT;
+
+ ndev = drv->ndev;
+
+ nci_unregister_device(ndev);
+ nci_free_device(ndev);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver nfcwilink_driver = {
+ .probe = nfcwilink_probe,
+ .remove = nfcwilink_remove,
+ .driver = {
+ .name = "nfcwilink",
+ .owner = THIS_MODULE,
+ },
+};
+
+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/pn533.c b/drivers/nfc/pn533.c
new file mode 100644
index 0000000..8f6f2ba
--- /dev/null
+++ b/drivers/nfc/pn533.c
@@ -0,0 +1,2864 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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[] = {
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = PN533_VENDOR_ID,
+ .idProduct = PN533_PRODUCT_ID,
+ .driver_info = PN533_DEVICE_STD,
+ },
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = SCM_VENDOR_ID,
+ .idProduct = SCL3711_PRODUCT_ID,
+ .driver_info = PN533_DEVICE_STD,
+ },
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = SONY_VENDOR_ID,
+ .idProduct = PASORI_PRODUCT_ID,
+ .driver_info = PN533_DEVICE_PASORI,
+ },
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = ACS_VENDOR_ID,
+ .idProduct = 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
+
+/* Standard pn533 frame definitions */
+#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*/
+
+/*
+ * 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])
+
+/* 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_STD_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_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_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_NO_SYSTEM_CODE,
+ .tsn = 0,
+ },
+ },
+ .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_NO_SYSTEM_CODE,
+ .tsn = 0,
+ },
+ },
+ .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 workqueue_struct *wq;
+ struct work_struct cmd_work;
+ struct work_struct cmd_complete_work;
+ struct work_struct poll_work;
+ struct work_struct mi_work;
+ struct work_struct tg_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;
+
+ struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
+ u8 poll_mod_count;
+ u8 poll_mod_curr;
+ 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_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);
+ 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_acr122_rx_frame *frame = _frame;
+
+ if (frame->ccid.type != 0x83)
+ 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_STD_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 + 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_STD_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)
+{
+ u8 checksum;
+ struct pn533_std_frame *frame = _frame;
+
+ if (frame->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
+ return false;
+
+ checksum = pn533_std_checksum(frame->datalen);
+ if (checksum != frame->datalen_checksum)
+ return false;
+
+ checksum = pn533_std_data_checksum(frame->data, frame->datalen);
+ if (checksum != PN533_STD_FRAME_CHECKSUM(frame))
+ 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;
+
+ 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;
+
+ return PN533_STD_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:
+ nfc_dev_dbg(&dev->interface->dev,
+ "The urb has been canceled (status %d)",
+ urb->status);
+ goto sched_wq;
+ case -ESHUTDOWN:
+ default:
+ nfc_dev_err(&dev->interface->dev,
+ "Urb failure (status %d)", urb->status);
+ goto sched_wq;
+ }
+
+ in_frame = dev->in_urb->transfer_buffer;
+
+ nfc_dev_dbg(&dev->interface->dev, "Received a frame.");
+ 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)) {
+ nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
+ cmd->status = -EIO;
+ goto sched_wq;
+ }
+
+ if (!pn533_rx_frame_is_cmd_response(dev, in_frame)) {
+ nfc_dev_err(&dev->interface->dev,
+ "It it not the response to the last command");
+ 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:
+ nfc_dev_dbg(&dev->interface->dev,
+ "The urb has been stopped (status %d)",
+ urb->status);
+ goto sched_wq;
+ case -ESHUTDOWN:
+ default:
+ nfc_dev_err(&dev->interface->dev,
+ "Urb failure (status %d)", urb->status);
+ goto sched_wq;
+ }
+
+ in_frame = dev->in_urb->transfer_buffer;
+
+ if (!pn533_std_rx_frame_is_ack(in_frame)) {
+ nfc_dev_err(&dev->interface->dev, "Received an invalid ack");
+ cmd->status = -EIO;
+ goto sched_wq;
+ }
+
+ rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC);
+ if (rc) {
+ nfc_dev_err(&dev->interface->dev,
+ "usb_submit_urb failed with result %d", 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", 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;
+ }
+
+ nfc_dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x", __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:
+ nfc_dev_dbg(&dev->interface->dev,
+ "The urb has been stopped (status %d)",
+ urb->status);
+ break;
+ case -ESHUTDOWN:
+ default:
+ nfc_dev_err(&dev->interface->dev,
+ "Urb failure (status %d)", 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[8];
+ 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;
+
+ 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s - modulation=%d", __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_dev_err(&dev->interface->dev,
+ "Unknown current poll modulation");
+ return -EPROTO;
+ }
+
+ if (rc)
+ return rc;
+
+ if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) {
+ nfc_dev_dbg(&dev->interface->dev,
+ "The Tg found doesn't have the desired protocol");
+ return -EAGAIN;
+ }
+
+ nfc_dev_dbg(&dev->interface->dev,
+ "Target found - supported protocols: 0x%x",
+ 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ 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 int pn533_tm_get_data_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ u8 status;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ status = resp->data[0];
+ skb_pull(resp, sizeof(status));
+
+ if (status != 0) {
+ nfc_tm_deactivated(dev->nfc_dev);
+ dev->tgt_mode = 0;
+ dev_kfree_skb(resp);
+ return 0;
+ }
+
+ return nfc_tm_data_received(dev->nfc_dev, resp);
+}
+
+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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (resp->len < ATR_REQ_GB_OFFSET + 1)
+ return -EINVAL;
+
+ mode = resp->data[0];
+ cmd = &resp->data[1];
+
+ nfc_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_dev_err(&dev->interface->dev,
+ "Error when signaling target activation");
+ 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout");
+
+ dev->cancel_listen = 1;
+
+ pn533_poll_next_mod(dev);
+
+ queue_work(dev->wq, &dev->poll_work);
+}
+
+static int pn533_poll_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ struct pn533_poll_modulations *cur_mod;
+ int rc;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+
+ nfc_dev_err(&dev->interface->dev, "%s Poll complete error %d",
+ __func__, rc);
+
+ if (rc == -ENOENT) {
+ if (dev->poll_mod_count != 0)
+ return rc;
+ else
+ goto stop_poll;
+ } else if (rc < 0) {
+ nfc_dev_err(&dev->interface->dev,
+ "Error %d when running poll", 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) {
+ nfc_dev_dbg(&dev->interface->dev, "Polling has been stoped.");
+ goto done;
+ }
+
+ pn533_poll_next_mod(dev);
+ queue_work(dev->wq, &dev->poll_work);
+
+done:
+ dev_kfree_skb(resp);
+ return rc;
+
+stop_poll:
+ nfc_dev_err(&dev->interface->dev, "Polling operation has been stopped");
+
+ 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];
+
+ nfc_dev_dbg(&dev->interface->dev, "%s mod len %d\n",
+ __func__, mod->len);
+
+ 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_dev_err(&dev->interface->dev, "Failed to allocate skb.");
+ return -ENOMEM;
+ }
+
+ rc = pn533_send_cmd_async(dev, cmd_code, skb, pn533_poll_complete,
+ NULL);
+ if (rc < 0) {
+ dev_kfree_skb(skb);
+ nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc);
+ }
+
+ return rc;
+}
+
+static void pn533_wq_poll(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, poll_work);
+ struct pn533_poll_modulations *cur_mod;
+ int rc;
+
+ cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+ nfc_dev_dbg(&dev->interface->dev,
+ "%s cancel_listen %d modulation len %d",
+ __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);
+
+ nfc_dev_dbg(&dev->interface->dev,
+ "%s: im protocols 0x%x tm protocols 0x%x",
+ __func__, im_protocols, tm_protocols);
+
+ if (dev->tgt_active_prot) {
+ nfc_dev_err(&dev->interface->dev,
+ "Cannot poll with a target already activated");
+ return -EBUSY;
+ }
+
+ if (dev->tgt_mode) {
+ nfc_dev_err(&dev->interface->dev,
+ "Cannot poll while already being activated");
+ return -EBUSY;
+ }
+
+ if (tm_protocols) {
+ dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
+ if (dev->gb == NULL)
+ tm_protocols = 0;
+ }
+
+ dev->poll_mod_curr = 0;
+ pn533_poll_create_mod_list(dev, im_protocols, tm_protocols);
+ dev->poll_protocols = im_protocols;
+ dev->listen_protocols = tm_protocols;
+
+ return pn533_send_poll_frame(dev);
+}
+
+static void pn533_stop_poll(struct nfc_dev *nfc_dev)
+{
+ struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ del_timer(&dev->listen_timer);
+
+ if (!dev->poll_mod_count) {
+ nfc_dev_dbg(&dev->interface->dev,
+ "Polling operation was not running");
+ return;
+ }
+
+ pn533_abort_cmd(dev, GFP_KERNEL);
+ 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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_dev_err(&dev->interface->dev,
+ "Target activation failed (error 0x%x)", 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s - protocol=%u", __func__,
+ protocol);
+
+ if (dev->poll_mod_count) {
+ nfc_dev_err(&dev->interface->dev,
+ "Cannot activate while polling");
+ return -EBUSY;
+ }
+
+ if (dev->tgt_active_prot) {
+ nfc_dev_err(&dev->interface->dev,
+ "There is already an active target");
+ return -EBUSY;
+ }
+
+ if (!dev->tgt_available_prots) {
+ nfc_dev_err(&dev->interface->dev,
+ "There is no available target to activate");
+ return -EINVAL;
+ }
+
+ if (!(dev->tgt_available_prots & (1 << protocol))) {
+ nfc_dev_err(&dev->interface->dev,
+ "Target doesn't support requested proto %u",
+ protocol);
+ return -EINVAL;
+ }
+
+ if (protocol == NFC_PROTO_NFC_DEP) {
+ rc = pn533_activate_target_nfcdep(dev);
+ if (rc) {
+ nfc_dev_err(&dev->interface->dev,
+ "Activating target with DEP failed %d", 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (!dev->tgt_active_prot) {
+ nfc_dev_err(&dev->interface->dev, "There is no active target");
+ 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_dev_err(&dev->interface->dev,
+ "Error 0x%x when releasing the target", 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_dev_err(&dev->interface->dev,
+ "The target does not support DEP");
+ 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_dev_err(&dev->interface->dev,
+ "Bringing DEP link up failed (error 0x%x)", rc);
+ goto error;
+ }
+
+ if (!dev->tgt_available_prots) {
+ struct nfc_target nfc_target;
+
+ nfc_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 == 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_mod_to_baud(struct pn533 *dev)
+{
+ switch (dev->poll_mod_curr) {
+ case PN533_POLL_MOD_106KBPS_A:
+ return 0;
+ case PN533_POLL_MOD_212KBPS_FELICA:
+ return 1;
+ case PN533_POLL_MOD_424KBPS_FELICA:
+ return 2;
+ default:
+ return -EINVAL;
+ }
+}
+
+#define PASSIVE_DATA_LEN 5
+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, baud, skb_len;
+ u8 *next, *arg;
+
+ u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (dev->poll_mod_count) {
+ nfc_dev_err(&dev->interface->dev,
+ "Cannot bring the DEP link up while polling");
+ return -EBUSY;
+ }
+
+ if (dev->tgt_active_prot) {
+ nfc_dev_err(&dev->interface->dev,
+ "There is already an active target");
+ return -EBUSY;
+ }
+
+ baud = pn533_mod_to_baud(dev);
+ if (baud < 0) {
+ nfc_dev_err(&dev->interface->dev,
+ "Invalid curr modulation %d", dev->poll_mod_curr);
+ return baud;
+ }
+
+ skb_len = 3 + gb_len; /* ActPass + BR + Next */
+ if (comm_mode == NFC_COMM_PASSIVE)
+ skb_len += PASSIVE_DATA_LEN;
+
+ skb = pn533_alloc_skb(dev, skb_len);
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, 1) = !comm_mode; /* ActPass */
+ *skb_put(skb, 1) = baud; /* Baud rate */
+
+ next = skb_put(skb, 1); /* Next */
+ *next = 0;
+
+ if (comm_mode == NFC_COMM_PASSIVE && baud > 0) {
+ memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data,
+ PASSIVE_DATA_LEN);
+ *next |= 1;
+ }
+
+ 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;
+
+ 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);
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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;
+
+ nfc_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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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_dev_err(&dev->interface->dev,
+ "Exchanging data failed (error 0x%x)", 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_work);
+ return -EINPROGRESS;
+ }
+
+ skb = pn533_build_response(dev);
+ if (!skb)
+ 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;
+}
+
+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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+ /* TODO: Implement support to multi-part data exchange */
+ nfc_dev_err(&dev->interface->dev,
+ "Data length greater than the max allowed: %d",
+ PN533_CMD_DATAEXCH_DATA_MAXLEN);
+ rc = -ENOSYS;
+ goto error;
+ }
+
+ if (!dev->tgt_active_prot) {
+ nfc_dev_err(&dev->interface->dev,
+ "Can't exchange data if there is no active target");
+ 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:
+ *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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (IS_ERR(resp))
+ return PTR_ERR(resp);
+
+ status = resp->data[0];
+
+ 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+ nfc_dev_err(&dev->interface->dev,
+ "Data length greater than the max allowed: %d",
+ PN533_CMD_DATAEXCH_DATA_MAXLEN);
+ return -ENOSYS;
+ }
+
+ rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
+ pn533_tm_send_complete, NULL);
+ if (rc < 0)
+ dev_kfree_skb(skb);
+
+ return rc;
+}
+
+static void pn533_wq_mi_recv(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, mi_work);
+
+ struct sk_buff *skb;
+ int rc;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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_dev_err(&dev->interface->dev,
+ "Error %d when trying to perform data_exchange", 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 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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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;
+
+ nfc_dev_dbg(&urb->dev->dev, "%s", __func__);
+
+ print_hex_dump(KERN_ERR, "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;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __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(KERN_ERR, "ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
+ cmd, sizeof(cmd), false);
+
+ rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
+ if (rc) {
+ nfc_dev_err(&dev->interface->dev,
+ "Reader power on cmd error %d", rc);
+ return rc;
+ }
+
+ rc = usb_submit_urb(dev->in_urb, GFP_KERNEL);
+ if (rc) {
+ nfc_dev_err(&dev->interface->dev,
+ "Can't submit for reader power on cmd response %d",
+ 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;
+
+ rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD,
+ (u8 *)&rf_field, 1);
+ if (rc) {
+ nfc_dev_err(&dev->interface->dev,
+ "Error on setting RF field");
+ return rc;
+ }
+
+ return rc;
+}
+
+int pn533_dev_up(struct nfc_dev *nfc_dev)
+{
+ return pn533_rf_field(nfc_dev, 1);
+}
+
+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:
+ max_retries.mx_rty_atr = PN533_CONFIG_MAX_RETRIES_ENDLESS;
+ max_retries.mx_rty_psl = 2;
+ 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_204;
+ timing.dep_timeout = PN533_CONFIG_TIMING_409;
+
+ break;
+
+ 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_dev_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_dev_err(&dev->interface->dev,
+ "Error on setting MAX_RETRIES config");
+ return rc;
+ }
+
+
+ rc = pn533_set_configuration(dev, PN533_CFGITEM_TIMING,
+ (u8 *)&timing, sizeof(timing));
+ if (rc) {
+ nfc_dev_err(&dev->interface->dev,
+ "Error on setting RF timings");
+ 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_dev_err(&dev->interface->dev,
+ "Error while settings PASORI config");
+ 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_dev_err(&interface->dev,
+ "Could not find bulk-in or bulk-out endpoint");
+ 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_work, pn533_wq_mi_recv);
+ INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
+ INIT_WORK(&dev->poll_work, pn533_wq_poll);
+ 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);
+
+ 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_dev_err(&dev->interface->dev,
+ "Couldn't poweron the reader (error %d)",
+ rc);
+ goto destroy_wq;
+ }
+ break;
+
+ default:
+ nfc_dev_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_dev_info(&dev->interface->dev,
+ "NXP PN533 firmware ver %d.%d now attached",
+ fw_ver.ver, fw_ver.rev);
+
+
+ dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
+ NFC_SE_NONE,
+ dev->ops->tx_header_len +
+ PN533_CMD_DATAEXCH_HEAD_LEN,
+ dev->ops->tx_tail_len);
+ if (!dev->nfc_dev)
+ 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);
+
+ 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_dev_info(&interface->dev, "NXP PN533 NFC device disconnected");
+}
+
+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 0000000..85d3c94
--- /dev/null
+++ b/drivers/nfc/pn544/Kconfig
@@ -0,0 +1,37 @@
+config NFC_PN544
+ tristate "NXP PN544 NFC driver"
+ depends on m
+ depends on NFC_HCI
+ depends on 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 m
+ 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 m
+ 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 0000000..c12e83c
--- /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-$(CPTCFG_NFC_PN544) += pn544.o
+obj-$(CPTCFG_NFC_PN544_I2C) += pn544_i2c.o
+obj-$(CPTCFG_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 0000000..8cf64c1
--- /dev/null
+++ b/drivers/nfc/pn544/i2c.c
@@ -0,0 +1,470 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/crc-ccitt.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/platform_data/pn544.h>
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "pn544.h"
+
+#define PN544_I2C_FRAME_HEADROOM 1
+#define PN544_I2C_FRAME_TAILROOM 2
+
+/* 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);
+
+#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
+
+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;
+
+ int powered;
+
+ 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);
+
+ pr_info(DRIVER_DESC ": %s\n", __func__);
+ dev_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
+
+ /* Disable fw download */
+ gpio_set_value(phy->gpio_fw, 0);
+
+ for (polarity = 0; polarity < 2; polarity++) {
+ phy->en_polarity = polarity;
+ retry = 3;
+ while (retry--) {
+ /* power off */
+ gpio_set_value(phy->gpio_en, !phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ /* power on */
+ gpio_set_value(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) {
+ dev_info(&phy->i2c_dev->dev,
+ "nfc_en polarity : active %s\n",
+ (polarity == 0 ? "low" : "high"));
+ goto out;
+ }
+ }
+ }
+
+ dev_err(&phy->i2c_dev->dev,
+ "Could not detect nfc_en polarity, fallback to active high\n");
+
+out:
+ gpio_set_value(phy->gpio_en, !phy->en_polarity);
+}
+
+static int pn544_hci_i2c_enable(void *phy_id)
+{
+ struct pn544_i2c_phy *phy = phy_id;
+
+ pr_info(DRIVER_DESC ": %s\n", __func__);
+
+ gpio_set_value(phy->gpio_fw, 0);
+ gpio_set_value(phy->gpio_en, phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ phy->powered = 1;
+
+ return 0;
+}
+
+static void pn544_hci_i2c_disable(void *phy_id)
+{
+ struct pn544_i2c_phy *phy = phy_id;
+
+ pr_info(DRIVER_DESC ": %s\n", __func__);
+
+ gpio_set_value(phy->gpio_fw, 0);
+ gpio_set_value(phy->gpio_en, !phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ gpio_set_value(phy->gpio_en, phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ gpio_set_value(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(PN544_HCI_I2C_DRIVER_NAME
+ ": CRC error 0x%x != 0x%x 0x%x\n",
+ crc, buf[len - 1], buf[len - 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;
+}
+
+/*
+ * 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) {
+ dev_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))) {
+ dev_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;
+}
+
+/*
+ * 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;
+
+ 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_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)) {
+ dev_err(&client->dev, "Need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
+ GFP_KERNEL);
+ if (!phy) {
+ dev_err(&client->dev,
+ "Cannot allocate memory for pn544 i2c phy.\n");
+ return -ENOMEM;
+ }
+
+ phy->i2c_dev = client;
+ i2c_set_clientdata(client, phy);
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ if (pdata->request_resources == NULL) {
+ dev_err(&client->dev, "request_resources() missing\n");
+ return -EINVAL;
+ }
+
+ r = pdata->request_resources(client);
+ if (r) {
+ dev_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);
+
+ 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) {
+ dev_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, &phy->hdev);
+ if (r < 0)
+ goto err_hci;
+
+ return 0;
+
+err_hci:
+ free_irq(client->irq, phy);
+
+err_rti:
+ if (pdata->free_resources != NULL)
+ 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__);
+
+ pn544_hci_remove(phy->hdev);
+
+ if (phy->powered)
+ pn544_hci_i2c_disable(phy);
+
+ free_irq(client->irq, phy);
+ if (pdata->free_resources)
+ pdata->free_resources();
+
+ return 0;
+}
+
+static struct i2c_driver pn544_hci_i2c_driver = {
+ .driver = {
+ .name = PN544_HCI_I2C_DRIVER_NAME,
+ },
+ .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 0000000..b5d3d18
--- /dev/null
+++ b/drivers/nfc/pn544/mei.c
@@ -0,0 +1,111 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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,
+ &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 0000000..9c5f16e
--- /dev/null
+++ b/drivers/nfc/pn544/pn544.c
@@ -0,0 +1,881 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
+#define HCI_MODE 0
+#define FW_MODE 1
+
+enum pn544_state {
+ PN544_ST_COLD,
+ PN544_ST_FW_READY,
+ PN544_ST_READY,
+};
+
+#define FULL_VERSION_LEN 11
+
+/* Proprietary commands */
+#define PN544_WRITE 0x3f
+
+/* 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_NFC_WI_MGMT_GATE 0xA1
+
+#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_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;
+};
+
+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},
+
+ {{0x9e, 0xd9}, 0xff},
+ {{0x9e, 0xda}, 0xff},
+ {{0x9e, 0xdb}, 0x23},
+ {{0x9e, 0xdc}, 0x21},
+ {{0x9e, 0xdd}, 0x22},
+ {{0x9e, 0xde}, 0x24},
+
+ {{0x9c, 0x01}, 0x08},
+
+ {{0x9e, 0xaa}, 0x01},
+
+ {{0x9b, 0xd1}, 0x0d},
+ {{0x9b, 0xd2}, 0x24},
+ {{0x9b, 0xd3}, 0x0a},
+ {{0x9b, 0xd4}, 0x22},
+ {{0x9b, 0xd5}, 0x08},
+ {{0x9b, 0xd6}, 0x1e},
+ {{0x9b, 0xdd}, 0x1c},
+
+ {{0x9b, 0x84}, 0x13},
+ {{0x99, 0x81}, 0x7f},
+ {{0x99, 0x31}, 0x70},
+
+ {{0x98, 0x00}, 0x3f},
+
+ {{0x9f, 0x09}, 0x00},
+
+ {{0x9f, 0x0a}, 0x05},
+
+ {{0x9e, 0xd1}, 0xa1},
+ {{0x99, 0x23}, 0x00},
+
+ {{0x9e, 0x74}, 0x80},
+
+ {{0x9f, 0x28}, 0x10},
+
+ {{0x9f, 0x35}, 0x14},
+
+ {{0x9f, 0x36}, 0x60},
+
+ {{0x9c, 0x31}, 0x00},
+
+ {{0x9c, 0x32}, 0xc8},
+
+ {{0x9c, 0x19}, 0x40},
+
+ {{0x9c, 0x1a}, 0x40},
+
+ {{0x9c, 0x0c}, 0x00},
+
+ {{0x9c, 0x0d}, 0x00},
+
+ {{0x9c, 0x12}, 0x00},
+
+ {{0x9c, 0x13}, 0x00},
+
+ {{0x98, 0xa2}, 0x0e},
+
+ {{0x98, 0x93}, 0x40},
+
+ {{0x98, 0x7d}, 0x02},
+ {{0x98, 0x7e}, 0x00},
+ {{0x9f, 0xc8}, 0x01},
+ };
+ 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", 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;
+ }
+
+ 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);
+
+ 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;
+ target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ } 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", 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) {
+ return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+ PN544_JEWEL_RAW_CMD, NULL, 0, NULL);
+ } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) {
+ return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
+ PN544_FELICA_RAW, NULL, 0, NULL);
+ } 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 gate, u8 event,
+ struct sk_buff *skb)
+{
+ struct sk_buff *rgb_skb = NULL;
+ int r;
+
+ pr_debug("hci event %d", 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", 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 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,
+};
+
+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,
+ struct nfc_hci_dev **hdev)
+{
+ struct pn544_hci_info *info;
+ u32 protocols, se;
+ struct nfc_hci_init_data init_data;
+ int r;
+
+ info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL);
+ if (!info) {
+ pr_err("Cannot allocate memory for pn544_hci_info.\n");
+ r = -ENOMEM;
+ goto err_info_alloc;
+ }
+
+ info->phy_ops = phy_ops;
+ info->phy_id = phy_id;
+ 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;
+
+ se = NFC_SE_UICC | NFC_SE_EMBEDDED;
+
+ info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0,
+ protocols, se, 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 0000000..f47c645
--- /dev/null
+++ b/drivers/nfc/pn544/pn544.h
@@ -0,0 +1,32 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOCAL_PN544_H_
+#define __LOCAL_PN544_H_
+
+#include <net/nfc/hci.h>
+
+#define DRIVER_DESC "HCI NFC driver for PN544"
+
+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,
+ struct nfc_hci_dev **hdev);
+void pn544_hci_remove(struct nfc_hci_dev *hdev);
+
+#endif /* __LOCAL_PN544_H_ */