summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2013-06-14 13:34:39 -0400
committerJohn W. Linville <linville@tuxdriver.com>2013-06-14 13:34:39 -0400
commitb9db44784723625a331a2c9139774d1202ebad24 (patch)
treeb65cb7d1a3f3862c4bcf61c52ed2200ff35aba98
parent65574866d1f3f6db328923f951218c2995b66c77 (diff)
parentbda7eb27635c4d7414fb05a5f55501b71f436cc0 (diff)
Merge tag 'nfc-next-3.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/nfc-next
Samuel Ortiz <sameo@linux.intel.com> says: "These are the pending NFC patches for the 3.11 merge window. It contains the pending fixes that were on nfc-fixes (nfc-fixes-3.10-2), along with a few more for the pn544 and pn533 drivers, the LLCP disconnection path and an LLCP memory leak. Highlights for this one are: - An initial secure element API. NFC chipsets can carry an embedded secure element or get access to the SIM one. In both cases they control the secure elements and this API provides a way to discover, enable and disable the available SEs. It also exports that to userspace in order for SE focused middleware to actually do something with them (e.g. payments). - NCI over SPI support. SPI is the most complex NCI specified transport layer and we now have support for it in the kernel. The next step will be to implement drivers for NCI chipsets using this transport like e.g. bcm2079x. - NFC p2p hardware simulation driver. We now have an nfcsim driver that is mostly a loopback device between 2 NFC interfaces. It also implements the rest of the NFC core API like polling and target detection. This driver, with neard running on top of it, allows us to completely test the LLCP, SNEP and Handover implementation without physical hardware. - A Firmware update netlink API. Most (All ?) HCI chipsets have a special firmware update mode where applications can push a new firmware that will be flashed. We now have a netlink API for providing that mode to e.g. nfctool." Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/nfc/Kconfig10
-rw-r--r--drivers/nfc/Makefile1
-rw-r--r--drivers/nfc/mei_phy.c6
-rw-r--r--drivers/nfc/microread/microread.c6
-rw-r--r--drivers/nfc/nfcsim.c541
-rw-r--r--drivers/nfc/nfcwilink.c18
-rw-r--r--drivers/nfc/pn533.c31
-rw-r--r--drivers/nfc/pn544/pn544.c40
-rw-r--r--include/net/nfc/hci.h7
-rw-r--r--include/net/nfc/nci_core.h58
-rw-r--r--include/net/nfc/nfc.h35
-rw-r--r--include/uapi/linux/nfc.h18
-rw-r--r--net/nfc/core.c224
-rw-r--r--net/nfc/hci/core.c75
-rw-r--r--net/nfc/llcp.h3
-rw-r--r--net/nfc/llcp_commands.c22
-rw-r--r--net/nfc/llcp_core.c16
-rw-r--r--net/nfc/llcp_sock.c19
-rw-r--r--net/nfc/nci/Kconfig10
-rw-r--r--net/nfc/nci/Makefile4
-rw-r--r--net/nfc/nci/core.c37
-rw-r--r--net/nfc/nci/data.c2
-rw-r--r--net/nfc/nci/spi.c378
-rw-r--r--net/nfc/netlink.c183
-rw-r--r--net/nfc/nfc.h11
25 files changed, 1629 insertions, 126 deletions
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 74a852e4e41f..b0b64ccb7d7d 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -36,6 +36,16 @@ config NFC_MEI_PHY
If unsure, say N.
+config NFC_SIM
+ tristate "NFC hardware simulator driver"
+ help
+ This driver declares two virtual NFC devices supporting NFC-DEP
+ protocol. An LLCP connection can be established between them and
+ all packets sent from one device is sent back to the other, acting as
+ loopback devices.
+
+ If unsure, say N.
+
source "drivers/nfc/pn544/Kconfig"
source "drivers/nfc/microread/Kconfig"
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index aa6bd657ef40..be7636abcb3f 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_NFC_MICROREAD) += microread/
obj-$(CONFIG_NFC_PN533) += pn533.o
obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o
+obj-$(CONFIG_NFC_SIM) += nfcsim.o
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c
index 1201bdbfb791..606bf55e76ec 100644
--- a/drivers/nfc/mei_phy.c
+++ b/drivers/nfc/mei_phy.c
@@ -30,7 +30,7 @@ struct mei_nfc_hdr {
u16 req_id;
u32 reserved;
u16 data_size;
-} __attribute__((packed));
+} __packed;
#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
@@ -60,8 +60,8 @@ int nfc_mei_phy_enable(void *phy_id)
r = mei_cl_enable_device(phy->device);
if (r < 0) {
- pr_err("MEI_PHY: Could not enable device\n");
- return r;
+ pr_err("MEI_PHY: Could not enable device\n");
+ return r;
}
r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy);
diff --git a/drivers/nfc/microread/microread.c b/drivers/nfc/microread/microread.c
index 3420d833db17..cdb9f6de132a 100644
--- a/drivers/nfc/microread/microread.c
+++ b/drivers/nfc/microread/microread.c
@@ -650,7 +650,7 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
{
struct microread_info *info;
unsigned long quirks = 0;
- u32 protocols, se;
+ u32 protocols;
struct nfc_hci_init_data init_data;
int r;
@@ -678,10 +678,8 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
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,
+ quirks, protocols, llc_name,
phy_headroom +
MICROREAD_CMDS_HEADROOM,
phy_tailroom +
diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c
new file mode 100644
index 000000000000..c5c30fb1d7bf
--- /dev/null
+++ b/drivers/nfc/nfcsim.c
@@ -0,0 +1,541 @@
+/*
+ * NFC hardware simulation driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nfc.h>
+
+#define DEV_ERR(_dev, fmt, args...) nfc_dev_err(&_dev->nfc_dev->dev, \
+ "%s: " fmt, __func__, ## args)
+
+#define DEV_DBG(_dev, fmt, args...) nfc_dev_dbg(&_dev->nfc_dev->dev, \
+ "%s: " fmt, __func__, ## args)
+
+#define NFCSIM_VERSION "0.1"
+
+#define NFCSIM_POLL_NONE 0
+#define NFCSIM_POLL_INITIATOR 1
+#define NFCSIM_POLL_TARGET 2
+#define NFCSIM_POLL_DUAL (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET)
+
+struct nfcsim {
+ struct nfc_dev *nfc_dev;
+
+ struct mutex lock;
+
+ struct delayed_work recv_work;
+
+ struct sk_buff *clone_skb;
+
+ struct delayed_work poll_work;
+ u8 polling_mode;
+ u8 curr_polling_mode;
+
+ u8 shutting_down;
+
+ u8 up;
+
+ u8 initiator;
+
+ data_exchange_cb_t cb;
+ void *cb_context;
+
+ struct nfcsim *peer_dev;
+};
+
+static struct nfcsim *dev0;
+static struct nfcsim *dev1;
+
+struct workqueue_struct *wq;
+
+static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
+{
+ DEV_DBG(dev, "shutdown=%d", shutdown);
+
+ mutex_lock(&dev->lock);
+
+ dev->polling_mode = NFCSIM_POLL_NONE;
+ dev->shutting_down = shutdown;
+ dev->cb = NULL;
+ dev_kfree_skb(dev->clone_skb);
+ dev->clone_skb = NULL;
+
+ mutex_unlock(&dev->lock);
+
+ cancel_delayed_work_sync(&dev->poll_work);
+ cancel_delayed_work_sync(&dev->recv_work);
+}
+
+static int nfcsim_target_found(struct nfcsim *dev)
+{
+ struct nfc_target nfc_tgt;
+
+ DEV_DBG(dev, "");
+
+ memset(&nfc_tgt, 0, sizeof(struct nfc_target));
+
+ nfc_tgt.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1);
+
+ return 0;
+}
+
+static int nfcsim_dev_up(struct nfc_dev *nfc_dev)
+{
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+ DEV_DBG(dev, "");
+
+ mutex_lock(&dev->lock);
+
+ dev->up = 1;
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int nfcsim_dev_down(struct nfc_dev *nfc_dev)
+{
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+ DEV_DBG(dev, "");
+
+ mutex_lock(&dev->lock);
+
+ dev->up = 0;
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
+ struct nfc_target *target,
+ u8 comm_mode, u8 *gb, size_t gb_len)
+{
+ int rc;
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+ struct nfcsim *peer = dev->peer_dev;
+ u8 *remote_gb;
+ size_t remote_gb_len;
+
+ DEV_DBG(dev, "target_idx: %d, comm_mode: %d\n", target->idx, comm_mode);
+
+ mutex_lock(&peer->lock);
+
+ nfc_tm_activated(peer->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
+ NFC_COMM_ACTIVE, gb, gb_len);
+
+ remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len);
+ if (!remote_gb) {
+ DEV_ERR(peer, "Can't get remote general bytes");
+
+ mutex_unlock(&peer->lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&peer->lock);
+
+ mutex_lock(&dev->lock);
+
+ rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len);
+ if (rc) {
+ DEV_ERR(dev, "Can't set remote general bytes");
+ mutex_unlock(&dev->lock);
+ return rc;
+ }
+
+ rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_ACTIVE,
+ NFC_RF_INITIATOR);
+
+ mutex_unlock(&dev->lock);
+
+ return rc;
+}
+
+static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev)
+{
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+ DEV_DBG(dev, "");
+
+ nfcsim_cleanup_dev(dev, 0);
+
+ return 0;
+}
+
+static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
+ u32 im_protocols, u32 tm_protocols)
+{
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->polling_mode != NFCSIM_POLL_NONE) {
+ DEV_ERR(dev, "Already in polling mode");
+ rc = -EBUSY;
+ goto exit;
+ }
+
+ if (im_protocols & NFC_PROTO_NFC_DEP_MASK)
+ dev->polling_mode |= NFCSIM_POLL_INITIATOR;
+
+ if (tm_protocols & NFC_PROTO_NFC_DEP_MASK)
+ dev->polling_mode |= NFCSIM_POLL_TARGET;
+
+ if (dev->polling_mode == NFCSIM_POLL_NONE) {
+ DEV_ERR(dev, "Unsupported polling mode");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ dev->initiator = 0;
+ dev->curr_polling_mode = NFCSIM_POLL_NONE;
+
+ queue_delayed_work(wq, &dev->poll_work, 0);
+
+ DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X", im_protocols,
+ tm_protocols);
+
+ rc = 0;
+exit:
+ mutex_unlock(&dev->lock);
+
+ return rc;
+}
+
+static void nfcsim_stop_poll(struct nfc_dev *nfc_dev)
+{
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+ DEV_DBG(dev, "Stop poll");
+
+ mutex_lock(&dev->lock);
+
+ dev->polling_mode = NFCSIM_POLL_NONE;
+
+ mutex_unlock(&dev->lock);
+
+ cancel_delayed_work_sync(&dev->poll_work);
+}
+
+static int nfcsim_activate_target(struct nfc_dev *nfc_dev,
+ struct nfc_target *target, u32 protocol)
+{
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+ DEV_DBG(dev, "");
+
+ return -ENOTSUPP;
+}
+
+static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev,
+ struct nfc_target *target)
+{
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+ DEV_DBG(dev, "");
+}
+
+static void nfcsim_wq_recv(struct work_struct *work)
+{
+ struct nfcsim *dev = container_of(work, struct nfcsim,
+ recv_work.work);
+
+ mutex_lock(&dev->lock);
+
+ if (dev->shutting_down || !dev->up || !dev->clone_skb) {
+ dev_kfree_skb(dev->clone_skb);
+ goto exit;
+ }
+
+ if (dev->initiator) {
+ if (!dev->cb) {
+ DEV_ERR(dev, "Null recv callback");
+ dev_kfree_skb(dev->clone_skb);
+ goto exit;
+ }
+
+ dev->cb(dev->cb_context, dev->clone_skb, 0);
+ dev->cb = NULL;
+ } else {
+ nfc_tm_data_received(dev->nfc_dev, dev->clone_skb);
+ }
+
+exit:
+ dev->clone_skb = NULL;
+
+ mutex_unlock(&dev->lock);
+}
+
+static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
+ struct sk_buff *skb, data_exchange_cb_t cb,
+ void *cb_context)
+{
+ struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+ struct nfcsim *peer = dev->peer_dev;
+ int err;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->shutting_down || !dev->up) {
+ mutex_unlock(&dev->lock);
+ err = -ENODEV;
+ goto exit;
+ }
+
+ dev->cb = cb;
+ dev->cb_context = cb_context;
+
+ mutex_unlock(&dev->lock);
+
+ mutex_lock(&peer->lock);
+
+ peer->clone_skb = skb_clone(skb, GFP_KERNEL);
+
+ if (!peer->clone_skb) {
+ DEV_ERR(dev, "skb_clone failed");
+ mutex_unlock(&peer->lock);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ /* This simulates an arbitrary transmission delay between the 2 devices.
+ * If packet transmission occurs immediately between them, we have a
+ * non-stop flow of several tens of thousands SYMM packets per second
+ * and a burning cpu.
+ *
+ * TODO: Add support for a sysfs entry to control this delay.
+ */
+ queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5));
+
+ mutex_unlock(&peer->lock);
+
+ err = 0;
+exit:
+ dev_kfree_skb(skb);
+
+ return err;
+}
+
+static int nfcsim_im_transceive(struct nfc_dev *nfc_dev,
+ struct nfc_target *target, struct sk_buff *skb,
+ data_exchange_cb_t cb, void *cb_context)
+{
+ return nfcsim_tx(nfc_dev, target, skb, cb, cb_context);
+}
+
+static int nfcsim_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+ return nfcsim_tx(nfc_dev, NULL, skb, NULL, NULL);
+}
+
+static struct nfc_ops nfcsim_nfc_ops = {
+ .dev_up = nfcsim_dev_up,
+ .dev_down = nfcsim_dev_down,
+ .dep_link_up = nfcsim_dep_link_up,
+ .dep_link_down = nfcsim_dep_link_down,
+ .start_poll = nfcsim_start_poll,
+ .stop_poll = nfcsim_stop_poll,
+ .activate_target = nfcsim_activate_target,
+ .deactivate_target = nfcsim_deactivate_target,
+ .im_transceive = nfcsim_im_transceive,
+ .tm_send = nfcsim_tm_send,
+};
+
+static void nfcsim_set_polling_mode(struct nfcsim *dev)
+{
+ if (dev->polling_mode == NFCSIM_POLL_NONE) {
+ dev->curr_polling_mode = NFCSIM_POLL_NONE;
+ return;
+ }
+
+ if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
+ if (dev->polling_mode & NFCSIM_POLL_INITIATOR)
+ dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
+ else
+ dev->curr_polling_mode = NFCSIM_POLL_TARGET;
+
+ return;
+ }
+
+ if (dev->polling_mode == NFCSIM_POLL_DUAL) {
+ if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
+ dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
+ else
+ dev->curr_polling_mode = NFCSIM_POLL_TARGET;
+ }
+}
+
+static void nfcsim_wq_poll(struct work_struct *work)
+{
+ struct nfcsim *dev = container_of(work, struct nfcsim, poll_work.work);
+ struct nfcsim *peer = dev->peer_dev;
+
+ /* These work items run on an ordered workqueue and are therefore
+ * serialized. So we can take both mutexes without being dead locked.
+ */
+ mutex_lock(&dev->lock);
+ mutex_lock(&peer->lock);
+
+ nfcsim_set_polling_mode(dev);
+
+ if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
+ DEV_DBG(dev, "Not polling");
+ goto unlock;
+ }
+
+ DEV_DBG(dev, "Polling as %s",
+ dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ?
+ "initiator" : "target");
+
+ if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
+ goto sched_work;
+
+ if (peer->curr_polling_mode == NFCSIM_POLL_TARGET) {
+ peer->polling_mode = NFCSIM_POLL_NONE;
+ dev->polling_mode = NFCSIM_POLL_NONE;
+
+ dev->initiator = 1;
+
+ nfcsim_target_found(dev);
+
+ goto unlock;
+ }
+
+sched_work:
+ /* This defines the delay for an initiator to check if the other device
+ * is polling in target mode.
+ * If the device starts in dual mode polling, it switches between
+ * initiator and target at every round.
+ * Because the wq is ordered and only 1 work item is executed at a time,
+ * we'll always have one device polling as initiator and the other as
+ * target at some point, even if both are started in dual mode.
+ */
+ queue_delayed_work(wq, &dev->poll_work, msecs_to_jiffies(200));
+
+unlock:
+ mutex_unlock(&peer->lock);
+ mutex_unlock(&dev->lock);
+}
+
+static struct nfcsim *nfcsim_init_dev(void)
+{
+ struct nfcsim *dev;
+ int rc = -ENOMEM;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&dev->lock);
+
+ INIT_DELAYED_WORK(&dev->recv_work, nfcsim_wq_recv);
+ INIT_DELAYED_WORK(&dev->poll_work, nfcsim_wq_poll);
+
+ dev->nfc_dev = nfc_allocate_device(&nfcsim_nfc_ops,
+ NFC_PROTO_NFC_DEP_MASK,
+ 0, 0);
+ if (!dev->nfc_dev)
+ goto error;
+
+ nfc_set_drvdata(dev->nfc_dev, dev);
+
+ rc = nfc_register_device(dev->nfc_dev);
+ if (rc)
+ goto free_nfc_dev;
+
+ return dev;
+
+free_nfc_dev:
+ nfc_free_device(dev->nfc_dev);
+
+error:
+ kfree(dev);
+
+ return ERR_PTR(rc);
+}
+
+static void nfcsim_free_device(struct nfcsim *dev)
+{
+ nfc_unregister_device(dev->nfc_dev);
+
+ nfc_free_device(dev->nfc_dev);
+
+ kfree(dev);
+}
+
+int __init nfcsim_init(void)
+{
+ int rc;
+
+ /* We need an ordered wq to ensure that poll_work items are executed
+ * one at a time.
+ */
+ wq = alloc_ordered_workqueue("nfcsim", 0);
+ if (!wq) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ dev0 = nfcsim_init_dev();
+ if (IS_ERR(dev0)) {
+ rc = PTR_ERR(dev0);
+ goto exit;
+ }
+
+ dev1 = nfcsim_init_dev();
+ if (IS_ERR(dev1)) {
+ kfree(dev0);
+
+ rc = PTR_ERR(dev1);
+ goto exit;
+ }
+
+ dev0->peer_dev = dev1;
+ dev1->peer_dev = dev0;
+
+ pr_debug("NFCsim " NFCSIM_VERSION " initialized\n");
+
+ rc = 0;
+exit:
+ if (rc)
+ pr_err("Failed to initialize nfcsim driver (%d)\n",
+ rc);
+
+ return rc;
+}
+
+void __exit nfcsim_exit(void)
+{
+ nfcsim_cleanup_dev(dev0, 1);
+ nfcsim_cleanup_dev(dev1, 1);
+
+ nfcsim_free_device(dev0);
+ nfcsim_free_device(dev1);
+
+ destroy_workqueue(wq);
+}
+
+module_init(nfcsim_init);
+module_exit(nfcsim_exit);
+
+MODULE_DESCRIPTION("NFCSim driver ver " NFCSIM_VERSION);
+MODULE_VERSION(NFCSIM_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c
index 3b731acbc408..59f95d8fc98c 100644
--- a/drivers/nfc/nfcwilink.c
+++ b/drivers/nfc/nfcwilink.c
@@ -109,7 +109,7 @@ enum {
NFCWILINK_FW_DOWNLOAD,
};
-static int nfcwilink_send(struct sk_buff *skb);
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb);
static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
{
@@ -156,8 +156,6 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
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;
@@ -166,7 +164,7 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
drv->nfcc_info.plen = 0;
- rc = nfcwilink_send(skb);
+ rc = nfcwilink_send(drv->ndev, skb);
if (rc)
return rc;
@@ -232,11 +230,9 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
return -ENOMEM;
}
- skb->dev = (void *)drv->ndev;
-
memcpy(skb_put(skb, len), data, len);
- rc = nfcwilink_send(skb);
+ rc = nfcwilink_send(drv->ndev, skb);
if (rc)
return rc;
@@ -371,10 +367,8 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
return 0;
}
- skb->dev = (void *) drv->ndev;
-
/* Forward skb to NCI core layer */
- rc = nci_recv_frame(skb);
+ rc = nci_recv_frame(drv->ndev, skb);
if (rc < 0) {
nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc);
return rc;
@@ -480,9 +474,8 @@ static int nfcwilink_close(struct nci_dev *ndev)
return rc;
}
-static int nfcwilink_send(struct sk_buff *skb)
+static int nfcwilink_send(struct nci_dev *ndev, 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;
@@ -542,7 +535,6 @@ static int nfcwilink_probe(struct platform_device *pdev)
drv->ndev = nci_allocate_device(&nfcwilink_ops,
protocols,
- NFC_SE_NONE,
NFCWILINK_HDR_LEN,
0);
if (!drv->ndev) {
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
index 8f6f2baa930d..bfb4a4e7c604 100644
--- a/drivers/nfc/pn533.c
+++ b/drivers/nfc/pn533.c
@@ -258,7 +258,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
.opcode = PN533_FELICA_OPC_SENSF_REQ,
.sc = PN533_FELICA_SENSF_SC_ALL,
.rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
- .tsn = 0,
+ .tsn = 0x03,
},
},
.len = 7,
@@ -271,7 +271,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
.opcode = PN533_FELICA_OPC_SENSF_REQ,
.sc = PN533_FELICA_SENSF_SC_ALL,
.rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
- .tsn = 0,
+ .tsn = 0x03,
},
},
.len = 7,
@@ -1235,7 +1235,7 @@ static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
struct pn533_target_felica {
u8 pol_res;
u8 opcode;
- u8 nfcid2[8];
+ u8 nfcid2[NFC_NFCID2_MAXSIZE];
u8 pad[8];
/* optional */
u8 syst_code[];
@@ -1275,6 +1275,9 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
memcpy(nfc_tgt->sensf_res, &tgt_felica->opcode, 9);
nfc_tgt->sensf_res_len = 9;
+ memcpy(nfc_tgt->nfcid2, tgt_felica->nfcid2, NFC_NFCID2_MAXSIZE);
+ nfc_tgt->nfcid2_len = NFC_NFCID2_MAXSIZE;
+
return 0;
}
@@ -2084,6 +2087,9 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
if (comm_mode == NFC_COMM_PASSIVE)
skb_len += PASSIVE_DATA_LEN;
+ if (target && target->nfcid2_len)
+ skb_len += NFC_NFCID3_MAXSIZE;
+
skb = pn533_alloc_skb(dev, skb_len);
if (!skb)
return -ENOMEM;
@@ -2100,6 +2106,12 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
*next |= 1;
}
+ if (target && target->nfcid2_len) {
+ memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
+ target->nfcid2_len);
+ *next |= 2;
+ }
+
if (gb != NULL && gb_len > 0) {
memcpy(skb_put(skb, gb_len), gb, gb_len);
*next |= 4; /* We have some Gi */
@@ -2489,7 +2501,7 @@ static void pn533_acr122_poweron_rdr_resp(struct urb *urb)
nfc_dev_dbg(&urb->dev->dev, "%s", __func__);
- print_hex_dump(KERN_ERR, "ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
+ print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
urb->transfer_buffer, urb->transfer_buffer_length,
false);
@@ -2520,7 +2532,7 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev)
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,
+ print_hex_dump_debug("ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
cmd, sizeof(cmd), false);
rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
@@ -2774,17 +2786,18 @@ static int pn533_probe(struct usb_interface *interface,
goto destroy_wq;
nfc_dev_info(&dev->interface->dev,
- "NXP PN533 firmware ver %d.%d now attached",
- fw_ver.ver, fw_ver.rev);
+ "NXP PN5%02X firmware ver %d.%d now attached",
+ fw_ver.ic, 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)
+ if (!dev->nfc_dev) {
+ rc = -ENOMEM;
goto destroy_wq;
+ }
nfc_set_parent_dev(dev->nfc_dev, &interface->dev);
nfc_set_drvdata(dev->nfc_dev, dev);
diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c
index 9c5f16e7baef..0d17da7675b7 100644
--- a/drivers/nfc/pn544/pn544.c
+++ b/drivers/nfc/pn544/pn544.c
@@ -551,20 +551,25 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
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,
+ /* Type F NFC-DEP IDm has prefix 0x01FE */
+ if ((uid_skb->data[0] == 0x01) && (uid_skb->data[1] == 0xfe)) {
+ kfree_skb(uid_skb);
+ r = nfc_hci_send_cmd(hdev,
PN544_RF_READER_NFCIP1_INITIATOR_GATE,
PN544_HCI_CMD_CONTINUE_ACTIVATION,
NULL, 0, NULL);
- if (r < 0)
- return r;
+ if (r < 0)
+ return r;
- target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE;
- target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+ target->hci_reader_gate =
+ PN544_RF_READER_NFCIP1_INITIATOR_GATE;
+ } else {
+ r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
+ PN544_RF_READER_CMD_ACTIVATE_NEXT,
+ uid_skb->data, uid_skb->len, NULL);
+ kfree_skb(uid_skb);
+ }
} else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) {
/*
* TODO: maybe other ISO 14443 require some kind of continue
@@ -706,12 +711,9 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
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_JEWEL_MASK |
+ NFC_PROTO_FELICA_MASK)) {
+ return -EOPNOTSUPP;
} else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
PN544_HCI_CMD_ATTREQUEST,
@@ -801,7 +803,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
struct nfc_hci_dev **hdev)
{
struct pn544_hci_info *info;
- u32 protocols, se;
+ u32 protocols;
struct nfc_hci_init_data init_data;
int r;
@@ -834,10 +836,8 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
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,
+ protocols, llc_name,
phy_headroom + PN544_CMDS_HEADROOM,
phy_tailroom, phy_payload);
if (!info->hdev) {
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h
index b87a1692b086..0af851c3b038 100644
--- a/include/net/nfc/hci.h
+++ b/include/net/nfc/hci.h
@@ -59,8 +59,10 @@ struct nfc_hci_ops {
struct nfc_target *target);
int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
struct sk_buff *skb);
- int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
- int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
+ int (*fw_upload)(struct nfc_hci_dev *hdev, const char *firmware_name);
+ int (*discover_se)(struct nfc_hci_dev *dev);
+ int (*enable_se)(struct nfc_hci_dev *dev, u32 se_idx);
+ int (*disable_se)(struct nfc_hci_dev *dev, u32 se_idx);
};
/* Pipes */
@@ -152,7 +154,6 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
struct nfc_hci_init_data *init_data,
unsigned long quirks,
u32 protocols,
- u32 supported_se,
const char *llc_name,
int tx_headroom,
int tx_tailroom,
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 5bc0c460edc0..99fc1f3a392a 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -3,6 +3,7 @@
* NFC Controller (NFCC) and a Device Host (DH).
*
* Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
*
* Written by Ilan Elias <ilane@ti.com>
*
@@ -66,7 +67,7 @@ struct nci_dev;
struct nci_ops {
int (*open)(struct nci_dev *ndev);
int (*close)(struct nci_dev *ndev);
- int (*send)(struct sk_buff *skb);
+ int (*send)(struct nci_dev *ndev, struct sk_buff *skb);
};
#define NCI_MAX_SUPPORTED_RF_INTERFACES 4
@@ -147,13 +148,12 @@ struct nci_dev {
/* ----- NCI Devices ----- */
struct nci_dev *nci_allocate_device(struct nci_ops *ops,
__u32 supported_protocols,
- __u32 supported_se,
int tx_headroom,
int tx_tailroom);
void nci_free_device(struct nci_dev *ndev);
int nci_register_device(struct nci_dev *ndev);
void nci_unregister_device(struct nci_dev *ndev);
-int nci_recv_frame(struct sk_buff *skb);
+int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
unsigned int len,
@@ -202,4 +202,56 @@ void nci_req_complete(struct nci_dev *ndev, int result);
/* ----- NCI status code ----- */
int nci_to_errno(__u8 code);
+/* ----- NCI over SPI acknowledge modes ----- */
+#define NCI_SPI_CRC_DISABLED 0x00
+#define NCI_SPI_CRC_ENABLED 0x01
+
+/* ----- NCI SPI structures ----- */
+struct nci_spi_dev;
+
+struct nci_spi_ops {
+ int (*open)(struct nci_spi_dev *ndev);
+ int (*close)(struct nci_spi_dev *ndev);
+ void (*assert_int)(struct nci_spi_dev *ndev);
+ void (*deassert_int)(struct nci_spi_dev *ndev);
+};
+
+struct nci_spi_dev {
+ struct nci_dev *nci_dev;
+ struct spi_device *spi;
+ struct nci_spi_ops *ops;
+
+ unsigned int xfer_udelay; /* microseconds delay between
+ transactions */
+ u8 acknowledge_mode;
+
+ struct completion req_completion;
+ u8 req_result;
+
+ void *driver_data;
+};
+
+/* ----- NCI SPI Devices ----- */
+struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
+ struct nci_spi_ops *ops,
+ u32 supported_protocols,
+ u32 supported_se,
+ u8 acknowledge_mode,
+ unsigned int delay);
+void nci_spi_free_device(struct nci_spi_dev *ndev);
+int nci_spi_register_device(struct nci_spi_dev *ndev);
+void nci_spi_unregister_device(struct nci_spi_dev *ndev);
+int nci_spi_recv_frame(struct nci_spi_dev *ndev);
+
+static inline void nci_spi_set_drvdata(struct nci_spi_dev *ndev,
+ void *data)
+{
+ ndev->driver_data = data;
+}
+
+static inline void *nci_spi_get_drvdata(struct nci_spi_dev *ndev)
+{
+ return ndev->driver_data;
+}
+
#endif /* __NCI_CORE_H */
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index 5eb80bb3cbb2..0e353f1658bb 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -68,8 +68,12 @@ struct nfc_ops {
void *cb_context);
int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb);
int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target);
- int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
- int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
+ int (*fw_upload)(struct nfc_dev *dev, const char *firmware_name);
+
+ /* Secure Element API */
+ int (*discover_se)(struct nfc_dev *dev);
+ int (*enable_se)(struct nfc_dev *dev, u32 se_idx);
+ int (*disable_se)(struct nfc_dev *dev, u32 se_idx);
};
#define NFC_TARGET_IDX_ANY -1
@@ -83,6 +87,8 @@ struct nfc_target {
u8 sel_res;
u8 nfcid1_len;
u8 nfcid1[NFC_NFCID1_MAXSIZE];
+ u8 nfcid2_len;
+ u8 nfcid2[NFC_NFCID2_MAXSIZE];
u8 sensb_res_len;
u8 sensb_res[NFC_SENSB_RES_MAXSIZE];
u8 sensf_res_len;
@@ -91,6 +97,23 @@ struct nfc_target {
u8 logical_idx;
};
+/**
+ * nfc_se - A structure for NFC accessible secure elements.
+ *
+ * @idx: The secure element index. User space will enable or
+ * disable a secure element by its index.
+ * @type: The secure element type. It can be SE_UICC or
+ * SE_EMBEDDED.
+ * @state: The secure element state, either enabled or disabled.
+ *
+ */
+struct nfc_se {
+ struct list_head list;
+ u32 idx;
+ u16 type;
+ u16 state;
+};
+
struct nfc_genl_data {
u32 poll_req_portid;
struct mutex genl_data_mutex;
@@ -104,6 +127,7 @@ struct nfc_dev {
int targets_generation;
struct device dev;
bool dev_up;
+ bool fw_upload_in_progress;
u8 rf_mode;
bool polling;
struct nfc_target *active_target;
@@ -111,8 +135,7 @@ struct nfc_dev {
struct nfc_genl_data genl_data;
u32 supported_protocols;
- u32 supported_se;
- u32 active_se;
+ struct list_head secure_elements;
int tx_headroom;
int tx_tailroom;
@@ -132,7 +155,6 @@ extern struct class nfc_class;
struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
u32 supported_protocols,
- u32 supported_se,
int tx_headroom,
int tx_tailroom);
@@ -216,4 +238,7 @@ int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb);
void nfc_driver_failure(struct nfc_dev *dev, int err);
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
+
#endif /* __NET_NFC_H */
diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h
index 7c6f627a717d..caed0f324d5f 100644
--- a/include/uapi/linux/nfc.h
+++ b/include/uapi/linux/nfc.h
@@ -69,6 +69,8 @@
* starting a poll from a device which has a secure element enabled means
* we want to do SE based card emulation.
* @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
+ * @NFC_CMD_FW_UPLOAD: Request to Load/flash firmware, or event to inform that
+ * some firmware was loaded
*/
enum nfc_commands {
NFC_CMD_UNSPEC,
@@ -92,6 +94,9 @@ enum nfc_commands {
NFC_CMD_DISABLE_SE,
NFC_CMD_LLC_SDREQ,
NFC_EVENT_LLC_SDRES,
+ NFC_CMD_FW_UPLOAD,
+ NFC_EVENT_SE_ADDED,
+ NFC_EVENT_SE_REMOVED,
/* private: internal use only */
__NFC_CMD_AFTER_LAST
};
@@ -121,6 +126,9 @@ enum nfc_commands {
* @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter
* @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter
* @NFC_ATTR_SE: Available Secure Elements
+ * @NFC_ATTR_FIRMWARE_NAME: Free format firmware version
+ * @NFC_ATTR_SE_INDEX: Secure element index
+ * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
*/
enum nfc_attrs {
NFC_ATTR_UNSPEC,
@@ -143,6 +151,9 @@ enum nfc_attrs {
NFC_ATTR_LLC_PARAM_MIUX,
NFC_ATTR_SE,
NFC_ATTR_LLC_SDP,
+ NFC_ATTR_FIRMWARE_NAME,
+ NFC_ATTR_SE_INDEX,
+ NFC_ATTR_SE_TYPE,
/* private: internal use only */
__NFC_ATTR_AFTER_LAST
};
@@ -159,9 +170,12 @@ enum nfc_sdp_attr {
#define NFC_DEVICE_NAME_MAXSIZE 8
#define NFC_NFCID1_MAXSIZE 10
+#define NFC_NFCID2_MAXSIZE 8
+#define NFC_NFCID3_MAXSIZE 10
#define NFC_SENSB_RES_MAXSIZE 12
#define NFC_SENSF_RES_MAXSIZE 18
#define NFC_GB_MAXSIZE 48
+#define NFC_FIRMWARE_NAME_MAXSIZE 32
/* NFC protocols */
#define NFC_PROTO_JEWEL 1
@@ -191,10 +205,12 @@ enum nfc_sdp_attr {
#define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B)
/* NFC Secure Elements */
-#define NFC_SE_NONE 0x0
#define NFC_SE_UICC 0x1
#define NFC_SE_EMBEDDED 0x2
+#define NFC_SE_DISABLED 0x0
+#define NFC_SE_ENABLED 0x1
+
struct sockaddr_nfc {
sa_family_t sa_family;
__u32 dev_idx;
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 40d2527693da..dc96a83aa6ab 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -44,6 +44,47 @@ DEFINE_MUTEX(nfc_devlist_mutex);
/* NFC device ID bitmap */
static DEFINE_IDA(nfc_index_ida);
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name)
+{
+ int rc = 0;
+
+ pr_debug("%s do firmware %s\n", dev_name(&dev->dev), firmware_name);
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (dev->dev_up) {
+ rc = -EBUSY;
+ goto error;
+ }
+
+ if (!dev->ops->fw_upload) {
+ rc = -EOPNOTSUPP;
+ goto error;
+ }
+
+ dev->fw_upload_in_progress = true;
+ rc = dev->ops->fw_upload(dev, firmware_name);
+ if (rc)
+ dev->fw_upload_in_progress = false;
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+ dev->fw_upload_in_progress = false;
+
+ return nfc_genl_fw_upload_done(dev, firmware_name);
+}
+EXPORT_SYMBOL(nfc_fw_upload_done);
+
/**
* nfc_dev_up - turn on the NFC device
*
@@ -69,6 +110,11 @@ int nfc_dev_up(struct nfc_dev *dev)
goto error;
}
+ if (dev->fw_upload_in_progress) {
+ rc = -EBUSY;
+ goto error;
+ }
+
if (dev->dev_up) {
rc = -EALREADY;
goto error;
@@ -80,6 +126,13 @@ int nfc_dev_up(struct nfc_dev *dev)
if (!rc)
dev->dev_up = true;
+ /* We have to enable the device before discovering SEs */
+ if (dev->ops->discover_se) {
+ rc = dev->ops->discover_se(dev);
+ if (!rc)
+ pr_warn("SE discovery failed\n");
+ }
+
error:
device_unlock(&dev->dev);
return rc;
@@ -475,6 +528,108 @@ error:
return rc;
}
+static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx)
+{
+ struct nfc_se *se, *n;
+
+ list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+ if (se->idx == se_idx)
+ return se;
+
+ return NULL;
+}
+
+int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
+{
+
+ struct nfc_se *se;
+ int rc;
+
+ pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->dev_up) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (dev->polling) {
+ rc = -EBUSY;
+ goto error;
+ }
+
+ if (!dev->ops->enable_se || !dev->ops->disable_se) {
+ rc = -EOPNOTSUPP;
+ goto error;
+ }
+
+ se = find_se(dev, se_idx);
+ if (!se) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (se->type == NFC_SE_ENABLED) {
+ rc = -EALREADY;
+ goto error;
+ }
+
+ rc = dev->ops->enable_se(dev, se_idx);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
+int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
+{
+
+ struct nfc_se *se;
+ int rc;
+
+ pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->dev_up) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->ops->enable_se || !dev->ops->disable_se) {
+ rc = -EOPNOTSUPP;
+ goto error;
+ }
+
+ se = find_se(dev, se_idx);
+ if (!se) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (se->type == NFC_SE_DISABLED) {
+ rc = -EALREADY;
+ goto error;
+ }
+
+ rc = dev->ops->disable_se(dev, se_idx);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len)
{
pr_debug("dev_name=%s gb_len=%d\n", dev_name(&dev->dev), gb_len);
@@ -707,14 +862,79 @@ inline void nfc_driver_failure(struct nfc_dev *dev, int err)
}
EXPORT_SYMBOL(nfc_driver_failure);
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+ struct nfc_se *se;
+ int rc;
+
+ pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+ se = find_se(dev, se_idx);
+ if (se)
+ return -EALREADY;
+
+ se = kzalloc(sizeof(struct nfc_se), GFP_KERNEL);
+ if (!se)
+ return -ENOMEM;
+
+ se->idx = se_idx;
+ se->type = type;
+ se->state = NFC_SE_DISABLED;
+ INIT_LIST_HEAD(&se->list);
+
+ list_add(&se->list, &dev->secure_elements);
+
+ rc = nfc_genl_se_added(dev, se_idx, type);
+ if (rc < 0) {
+ list_del(&se->list);
+ kfree(se);
+
+ return rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(nfc_add_se);
+
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx)
+{
+ struct nfc_se *se, *n;
+ int rc;
+
+ pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+ list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+ if (se->idx == se_idx) {
+ rc = nfc_genl_se_removed(dev, se_idx);
+ if (rc < 0)
+ return rc;
+
+ list_del(&se->list);
+ kfree(se);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(nfc_remove_se);
+
static void nfc_release(struct device *d)
{
struct nfc_dev *dev = to_nfc_dev(d);
+ struct nfc_se *se, *n;
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
nfc_genl_data_exit(&dev->genl_data);
kfree(dev->targets);
+
+ list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
+ nfc_genl_se_removed(dev, se->idx);
+ list_del(&se->list);
+ kfree(se);
+ }
+
kfree(dev);
}
@@ -786,7 +1006,6 @@ struct nfc_dev *nfc_get_device(unsigned int idx)
*/
struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
u32 supported_protocols,
- u32 supported_se,
int tx_headroom, int tx_tailroom)
{
struct nfc_dev *dev;
@@ -804,10 +1023,9 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
dev->ops = ops;
dev->supported_protocols = supported_protocols;
- dev->supported_se = supported_se;
- dev->active_se = NFC_SE_NONE;
dev->tx_headroom = tx_headroom;
dev->tx_tailroom = tx_tailroom;
+ INIT_LIST_HEAD(&dev->secure_elements);
nfc_genl_data_init(&dev->genl_data);
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 91020b210d87..7b1c186736eb 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -570,21 +570,21 @@ static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
- if (hdev->ops->dep_link_up)
- return hdev->ops->dep_link_up(hdev, target, comm_mode,
- gb, gb_len);
+ if (!hdev->ops->dep_link_up)
+ return 0;
- return 0;
+ return hdev->ops->dep_link_up(hdev, target, comm_mode,
+ gb, gb_len);
}
static int hci_dep_link_down(struct nfc_dev *nfc_dev)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
- if (hdev->ops->dep_link_down)
- return hdev->ops->dep_link_down(hdev);
+ if (!hdev->ops->dep_link_down)
+ return 0;
- return 0;
+ return hdev->ops->dep_link_down(hdev);
}
static int hci_activate_target(struct nfc_dev *nfc_dev,
@@ -673,12 +673,12 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
- if (hdev->ops->tm_send)
- return hdev->ops->tm_send(hdev, skb);
-
- kfree_skb(skb);
+ if (!hdev->ops->tm_send) {
+ kfree_skb(skb);
+ return -ENOTSUPP;
+ }
- return -ENOTSUPP;
+ return hdev->ops->tm_send(hdev, skb);
}
static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -686,8 +686,38 @@ static int hci_check_presence(struct nfc_dev *nfc_dev,
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
- if (hdev->ops->check_presence)
- return hdev->ops->check_presence(hdev, target);
+ if (!hdev->ops->check_presence)
+ return 0;
+
+ return hdev->ops->check_presence(hdev, target);
+}
+
+static int hci_discover_se(struct nfc_dev *nfc_dev)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->discover_se)
+ return hdev->ops->discover_se(hdev);
+
+ return 0;
+}
+
+static int hci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->enable_se)
+ return hdev->ops->enable_se(hdev, se_idx);
+
+ return 0;
+}
+
+static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->disable_se)
+ return hdev->ops->enable_se(hdev, se_idx);
return 0;
}
@@ -779,6 +809,16 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
}
}
+static int hci_fw_upload(struct nfc_dev *nfc_dev, const char *firmware_name)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (!hdev->ops->fw_upload)
+ return -ENOTSUPP;
+
+ return hdev->ops->fw_upload(hdev, firmware_name);
+}
+
static struct nfc_ops hci_nfc_ops = {
.dev_up = hci_dev_up,
.dev_down = hci_dev_down,
@@ -791,13 +831,16 @@ static struct nfc_ops hci_nfc_ops = {
.im_transceive = hci_transceive,
.tm_send = hci_tm_send,
.check_presence = hci_check_presence,
+ .fw_upload = hci_fw_upload,
+ .discover_se = hci_discover_se,
+ .enable_se = hci_enable_se,
+ .disable_se = hci_disable_se,
};
struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
struct nfc_hci_init_data *init_data,
unsigned long quirks,
u32 protocols,
- u32 supported_se,
const char *llc_name,
int tx_headroom,
int tx_tailroom,
@@ -823,7 +866,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
return NULL;
}
- hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se,
+ hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
tx_headroom + HCI_CMDS_HEADROOM,
tx_tailroom);
if (!hdev->ndev) {
diff --git a/net/nfc/llcp.h b/net/nfc/llcp.h
index ff8c434f7df8..f4d48b57ea11 100644
--- a/net/nfc/llcp.h
+++ b/net/nfc/llcp.h
@@ -19,6 +19,8 @@
enum llcp_state {
LLCP_CONNECTED = 1, /* wait_for_packet() wants that */
+ LLCP_CONNECTING,
+ LLCP_DISCONNECTING,
LLCP_CLOSED,
LLCP_BOUND,
LLCP_LISTEN,
@@ -246,7 +248,6 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head);
void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
-int nfc_llcp_disconnect(struct nfc_llcp_sock *sock);
int nfc_llcp_send_symm(struct nfc_dev *dev);
int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index c1b23eef83ca..1017894807c0 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -339,7 +339,7 @@ static struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock,
return skb;
}
-int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
+int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
{
struct sk_buff *skb;
struct nfc_dev *dev;
@@ -630,26 +630,6 @@ int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
return 0;
}
-int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
-{
- struct sk_buff *skb;
- struct nfc_llcp_local *local;
-
- pr_debug("Send DISC\n");
-
- local = sock->local;
- if (local == NULL)
- return -ENODEV;
-
- skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
- if (skb == NULL)
- return -ENOMEM;
-
- skb_queue_head(&local->tx_queue, skb);
-
- return 0;
-}
-
int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
struct msghdr *msg, size_t len)
{
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 158bdbf668cc..81cd3416c7d4 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -537,6 +537,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
u8 *lto_tlv, lto_length;
u8 *wks_tlv, wks_length;
u8 *miux_tlv, miux_length;
+ __be16 wks = cpu_to_be16(local->local_wks);
u8 gb_len = 0;
int ret = 0;
@@ -549,8 +550,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
gb_len += lto_length;
pr_debug("Local wks 0x%lx\n", local->local_wks);
- wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&local->local_wks, 2,
- &wks_length);
+ wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&wks, 2, &wks_length);
gb_len += wks_length;
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
@@ -719,6 +719,10 @@ static void nfc_llcp_tx_work(struct work_struct *work)
llcp_sock = nfc_llcp_sock(sk);
if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
+ kfree_skb(skb);
+ nfc_llcp_send_symm(local->dev);
+ } else if (llcp_sock && !llcp_sock->remote_ready) {
+ skb_queue_head(&local->tx_queue, skb);
nfc_llcp_send_symm(local->dev);
} else {
struct sk_buff *copy_skb = NULL;
@@ -730,6 +734,13 @@ static void nfc_llcp_tx_work(struct work_struct *work)
DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb->len, true);
+ if (ptype == LLCP_PDU_DISC && sk != NULL &&
+ sk->sk_state == LLCP_DISCONNECTING) {
+ nfc_llcp_sock_unlink(&local->sockets, sk);
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
if (ptype == LLCP_PDU_I)
copy_skb = skb_copy(skb, GFP_ATOMIC);
@@ -1579,6 +1590,7 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
local->lto = 150; /* 1500 ms */
local->rw = LLCP_MAX_RW;
local->miux = cpu_to_be16(LLCP_MAX_MIUX);
+ local->local_wks = 0x1; /* LLC Link Management */
nfc_llcp_build_gb(local);
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 380253eccb74..d308402b67d8 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -571,7 +571,7 @@ static unsigned int llcp_sock_poll(struct file *file, struct socket *sock,
if (sk->sk_shutdown == SHUTDOWN_MASK)
mask |= POLLHUP;
- if (sock_writeable(sk))
+ if (sock_writeable(sk) && sk->sk_state == LLCP_CONNECTED)
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
@@ -603,7 +603,7 @@ static int llcp_sock_release(struct socket *sock)
/* Send a DISC */
if (sk->sk_state == LLCP_CONNECTED)
- nfc_llcp_disconnect(llcp_sock);
+ nfc_llcp_send_disconnect(llcp_sock);
if (sk->sk_state == LLCP_LISTEN) {
struct nfc_llcp_sock *lsk, *n;
@@ -614,7 +614,7 @@ static int llcp_sock_release(struct socket *sock)
accept_sk = &lsk->sk;
lock_sock(accept_sk);
- nfc_llcp_disconnect(lsk);
+ nfc_llcp_send_disconnect(lsk);
nfc_llcp_accept_unlink(accept_sk);
release_sock(accept_sk);
@@ -626,6 +626,13 @@ static int llcp_sock_release(struct socket *sock)
release_sock(sk);
+ /* Keep this sock alive and therefore do not remove it from the sockets
+ * list until the DISC PDU has been actually sent. Otherwise we would
+ * reply with DM PDUs before sending the DISC one.
+ */
+ if (sk->sk_state == LLCP_DISCONNECTING)
+ return err;
+
if (sock->type == SOCK_RAW)
nfc_llcp_sock_unlink(&local->raw_sockets, sk);
else
@@ -722,14 +729,16 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
if (ret)
goto sock_unlink;
+ sk->sk_state = LLCP_CONNECTING;
+
ret = sock_wait_state(sk, LLCP_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
- if (ret)
+ if (ret && ret != -EINPROGRESS)
goto sock_unlink;
release_sock(sk);
- return 0;
+ return ret;
sock_unlink:
nfc_llcp_put_ssap(local, llcp_sock->ssap);
diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig
index 6d69b5f0f19b..2a2416080b4f 100644
--- a/net/nfc/nci/Kconfig
+++ b/net/nfc/nci/Kconfig
@@ -8,3 +8,13 @@ config NFC_NCI
Say Y here to compile NCI support into the kernel or say M to
compile it as module (nci).
+
+config NFC_NCI_SPI
+ depends on NFC_NCI && SPI
+ bool "NCI over SPI protocol support"
+ default n
+ help
+ NCI (NFC Controller Interface) is a communication protocol between
+ an NFC Controller (NFCC) and a Device Host (DH).
+
+ Say yes if you use an NCI driver that requires SPI link layer.
diff --git a/net/nfc/nci/Makefile b/net/nfc/nci/Makefile
index cdb3a2e44471..7aeedc43187d 100644
--- a/net/nfc/nci/Makefile
+++ b/net/nfc/nci/Makefile
@@ -4,4 +4,6 @@
obj-$(CONFIG_NFC_NCI) += nci.o
-nci-objs := core.o data.o lib.o ntf.o rsp.o \ No newline at end of file
+nci-objs := core.o data.o lib.o ntf.o rsp.o
+
+nci-$(CONFIG_NFC_NCI_SPI) += spi.o
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 48ada0ec749e..b943d46a1644 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -636,6 +636,21 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
return rc;
}
+static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+ return 0;
+}
+
+static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+ return 0;
+}
+
+static int nci_discover_se(struct nfc_dev *nfc_dev)
+{
+ return 0;
+}
+
static struct nfc_ops nci_nfc_ops = {
.dev_up = nci_dev_up,
.dev_down = nci_dev_down,
@@ -646,6 +661,9 @@ static struct nfc_ops nci_nfc_ops = {
.activate_target = nci_activate_target,
.deactivate_target = nci_deactivate_target,
.im_transceive = nci_transceive,
+ .enable_se = nci_enable_se,
+ .disable_se = nci_disable_se,
+ .discover_se = nci_discover_se,
};
/* ---- Interface to NCI drivers ---- */
@@ -658,7 +676,6 @@ static struct nfc_ops nci_nfc_ops = {
*/
struct nci_dev *nci_allocate_device(struct nci_ops *ops,
__u32 supported_protocols,
- __u32 supported_se,
int tx_headroom, int tx_tailroom)
{
struct nci_dev *ndev;
@@ -681,7 +698,6 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
supported_protocols,
- supported_se,
tx_headroom + NCI_DATA_HDR_SIZE,
tx_tailroom);
if (!ndev->nfc_dev)
@@ -797,12 +813,11 @@ EXPORT_SYMBOL(nci_unregister_device);
/**
* nci_recv_frame - receive frame from NCI drivers
*
+ * @ndev: The nci device
* @skb: The sk_buff to receive
*/
-int nci_recv_frame(struct sk_buff *skb)
+int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
{
- struct nci_dev *ndev = (struct nci_dev *) skb->dev;
-
pr_debug("len %d\n", skb->len);
if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
@@ -819,10 +834,8 @@ int nci_recv_frame(struct sk_buff *skb)
}
EXPORT_SYMBOL(nci_recv_frame);
-static int nci_send_frame(struct sk_buff *skb)
+static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
{
- struct nci_dev *ndev = (struct nci_dev *) skb->dev;
-
pr_debug("len %d\n", skb->len);
if (!ndev) {
@@ -833,7 +846,7 @@ static int nci_send_frame(struct sk_buff *skb)
/* Get rid of skb owner, prior to sending to the driver. */
skb_orphan(skb);
- return ndev->ops->send(skb);
+ return ndev->ops->send(ndev, skb);
}
/* Send NCI command */
@@ -861,8 +874,6 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
if (plen)
memcpy(skb_put(skb, plen), payload, plen);
- skb->dev = (void *) ndev;
-
skb_queue_tail(&ndev->cmd_q, skb);
queue_work(ndev->cmd_wq, &ndev->cmd_work);
@@ -894,7 +905,7 @@ static void nci_tx_work(struct work_struct *work)
nci_conn_id(skb->data),
nci_plen(skb->data));
- nci_send_frame(skb);
+ nci_send_frame(ndev, skb);
mod_timer(&ndev->data_timer,
jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
@@ -963,7 +974,7 @@ static void nci_cmd_work(struct work_struct *work)
nci_opcode_oid(nci_opcode(skb->data)),
nci_plen(skb->data));
- nci_send_frame(skb);
+ nci_send_frame(ndev, skb);
mod_timer(&ndev->cmd_timer,
jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index 76c48c5324f8..2a9399dd6c68 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -80,8 +80,6 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev,
nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
nci_pbf_set((__u8 *)hdr, pbf);
-
- skb->dev = (void *) ndev;
}
static int nci_queue_tx_data_frags(struct nci_dev *ndev,
diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c
new file mode 100644
index 000000000000..c7cf37ba7298
--- /dev/null
+++ b/net/nfc/nci/spi.c
@@ -0,0 +1,378 @@
+/*
+ * 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 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
+
+#include <linux/export.h>
+#include <linux/spi/spi.h>
+#include <linux/crc-ccitt.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci_core.h>
+
+#define NCI_SPI_HDR_LEN 4
+#define NCI_SPI_CRC_LEN 2
+#define NCI_SPI_ACK_SHIFT 6
+#define NCI_SPI_MSB_PAYLOAD_MASK 0x3F
+
+#define NCI_SPI_SEND_TIMEOUT (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \
+ NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT)
+
+#define NCI_SPI_DIRECT_WRITE 0x01
+#define NCI_SPI_DIRECT_READ 0x02
+
+#define ACKNOWLEDGE_NONE 0
+#define ACKNOWLEDGE_ACK 1
+#define ACKNOWLEDGE_NACK 2
+
+#define CRC_INIT 0xFFFF
+
+static int nci_spi_open(struct nci_dev *nci_dev)
+{
+ struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+
+ return ndev->ops->open(ndev);
+}
+
+static int nci_spi_close(struct nci_dev *nci_dev)
+{
+ struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+
+ return ndev->ops->close(ndev);
+}
+
+static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb)
+{
+ struct spi_message m;
+ struct spi_transfer t;
+
+ t.tx_buf = skb->data;
+ t.len = skb->len;
+ t.cs_change = 0;
+ t.delay_usecs = ndev->xfer_udelay;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ return spi_sync(ndev->spi, &m);
+}
+
+static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)
+{
+ struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+ unsigned int payload_len = skb->len;
+ unsigned char *hdr;
+ int ret;
+ long completion_rc;
+
+ ndev->ops->deassert_int(ndev);
+
+ /* add the NCI SPI header to the start of the buffer */
+ hdr = skb_push(skb, NCI_SPI_HDR_LEN);
+ hdr[0] = NCI_SPI_DIRECT_WRITE;
+ hdr[1] = ndev->acknowledge_mode;
+ hdr[2] = payload_len >> 8;
+ hdr[3] = payload_len & 0xFF;
+
+ if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+ u16 crc;
+
+ crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
+ *skb_put(skb, 1) = crc >> 8;
+ *skb_put(skb, 1) = crc & 0xFF;
+ }
+
+ ret = __nci_spi_send(ndev, skb);
+
+ kfree_skb(skb);
+ ndev->ops->assert_int(ndev);
+
+ if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED)
+ goto done;
+
+ init_completion(&ndev->req_completion);
+ completion_rc =
+ wait_for_completion_interruptible_timeout(&ndev->req_completion,
+ NCI_SPI_SEND_TIMEOUT);
+
+ if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK)
+ ret = -EIO;
+
+done:
+ return ret;
+}
+
+static struct nci_ops nci_spi_ops = {
+ .open = nci_spi_open,
+ .close = nci_spi_close,
+ .send = nci_spi_send,
+};
+
+/* ---- Interface to NCI SPI drivers ---- */
+
+/**
+ * nci_spi_allocate_device - allocate a new nci spi device
+ *
+ * @spi: SPI device
+ * @ops: device operations
+ * @supported_protocols: NFC protocols supported by the device
+ * @supported_se: NFC Secure Elements supported by the device
+ * @acknowledge_mode: Acknowledge mode used by the device
+ * @delay: delay between transactions in us
+ */
+struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
+ struct nci_spi_ops *ops,
+ u32 supported_protocols,
+ u32 supported_se,
+ u8 acknowledge_mode,
+ unsigned int delay)
+{
+ struct nci_spi_dev *ndev;
+ int tailroom = 0;
+
+ if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int)
+ return NULL;
+
+ if (!supported_protocols)
+ return NULL;
+
+ ndev = devm_kzalloc(&spi->dev, sizeof(struct nci_dev), GFP_KERNEL);
+ if (!ndev)
+ return NULL;
+
+ ndev->ops = ops;
+ ndev->acknowledge_mode = acknowledge_mode;
+ ndev->xfer_udelay = delay;
+
+ if (acknowledge_mode == NCI_SPI_CRC_ENABLED)
+ tailroom += NCI_SPI_CRC_LEN;
+
+ ndev->nci_dev = nci_allocate_device(&nci_spi_ops, supported_protocols,
+ NCI_SPI_HDR_LEN, tailroom);
+ if (!ndev->nci_dev)
+ return NULL;
+
+ nci_set_drvdata(ndev->nci_dev, ndev);
+
+ return ndev;
+}
+EXPORT_SYMBOL_GPL(nci_spi_allocate_device);
+
+/**
+ * nci_spi_free_device - deallocate nci spi device
+ *
+ * @ndev: The nci spi device to deallocate
+ */
+void nci_spi_free_device(struct nci_spi_dev *ndev)
+{
+ nci_free_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_free_device);
+
+/**
+ * nci_spi_register_device - register a nci spi device in the nfc subsystem
+ *
+ * @pdev: The nci spi device to register
+ */
+int nci_spi_register_device(struct nci_spi_dev *ndev)
+{
+ return nci_register_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_register_device);
+
+/**
+ * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem
+ *
+ * @dev: The nci spi device to unregister
+ */
+void nci_spi_unregister_device(struct nci_spi_dev *ndev)
+{
+ nci_unregister_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_unregister_device);
+
+static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
+{
+ struct sk_buff *skb;
+ unsigned char *hdr;
+ u16 crc;
+ int ret;
+
+ skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL);
+
+ /* add the NCI SPI header to the start of the buffer */
+ hdr = skb_push(skb, NCI_SPI_HDR_LEN);
+ hdr[0] = NCI_SPI_DIRECT_WRITE;
+ hdr[1] = NCI_SPI_CRC_ENABLED;
+ hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
+ hdr[3] = 0;
+
+ crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
+ *skb_put(skb, 1) = crc >> 8;
+ *skb_put(skb, 1) = crc & 0xFF;
+
+ ret = __nci_spi_send(ndev, skb);
+
+ kfree_skb(skb);
+
+ return ret;
+}
+
+static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
+{
+ struct sk_buff *skb;
+ struct spi_message m;
+ unsigned char req[2], resp_hdr[2];
+ struct spi_transfer tx, rx;
+ unsigned short rx_len = 0;
+ int ret;
+
+ spi_message_init(&m);
+ req[0] = NCI_SPI_DIRECT_READ;
+ req[1] = ndev->acknowledge_mode;
+ tx.tx_buf = req;
+ tx.len = 2;
+ tx.cs_change = 0;
+ spi_message_add_tail(&tx, &m);
+ rx.rx_buf = resp_hdr;
+ rx.len = 2;
+ rx.cs_change = 1;
+ spi_message_add_tail(&rx, &m);
+ ret = spi_sync(ndev->spi, &m);
+
+ if (ret)
+ return NULL;
+
+ if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+ rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
+ resp_hdr[1] + NCI_SPI_CRC_LEN;
+ else
+ rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
+
+ skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL);
+ if (!skb)
+ return NULL;
+
+ spi_message_init(&m);
+ rx.rx_buf = skb_put(skb, rx_len);
+ rx.len = rx_len;
+ rx.cs_change = 0;
+ rx.delay_usecs = ndev->xfer_udelay;
+ spi_message_add_tail(&rx, &m);
+ ret = spi_sync(ndev->spi, &m);
+
+ if (ret)
+ goto receive_error;
+
+ if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+ *skb_push(skb, 1) = resp_hdr[1];
+ *skb_push(skb, 1) = resp_hdr[0];
+ }
+
+ return skb;
+
+receive_error:
+ kfree_skb(skb);
+
+ return NULL;
+}
+
+static int nci_spi_check_crc(struct sk_buff *skb)
+{
+ u16 crc_data = (skb->data[skb->len - 2] << 8) |
+ skb->data[skb->len - 1];
+ int ret;
+
+ ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
+ == crc_data);
+
+ skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
+
+ return ret;
+}
+
+static u8 nci_spi_get_ack(struct sk_buff *skb)
+{
+ u8 ret;
+
+ ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
+
+ /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
+ skb_pull(skb, 2);
+
+ return ret;
+}
+
+/**
+ * nci_spi_recv_frame - receive frame from NCI SPI drivers
+ *
+ * @ndev: The nci spi device
+ * Context: can sleep
+ *
+ * This call may only be used from a context that may sleep. The sleep
+ * is non-interruptible, and has no timeout.
+ *
+ * It returns zero on success, else a negative error code.
+ */
+int nci_spi_recv_frame(struct nci_spi_dev *ndev)
+{
+ struct sk_buff *skb;
+ int ret = 0;
+
+ ndev->ops->deassert_int(ndev);
+
+ /* Retrieve frame from SPI */
+ skb = __nci_spi_recv_frame(ndev);
+ if (!skb) {
+ ret = -EIO;
+ goto done;
+ }
+
+ if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+ if (!nci_spi_check_crc(skb)) {
+ send_acknowledge(ndev, ACKNOWLEDGE_NACK);
+ goto done;
+ }
+
+ /* In case of acknowledged mode: if ACK or NACK received,
+ * unblock completion of latest frame sent.
+ */
+ ndev->req_result = nci_spi_get_ack(skb);
+ if (ndev->req_result)
+ complete(&ndev->req_completion);
+ }
+
+ /* If there is no payload (ACK/NACK only frame),
+ * free the socket buffer
+ */
+ if (skb->len == 0) {
+ kfree_skb(skb);
+ goto done;
+ }
+
+ if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+ send_acknowledge(ndev, ACKNOWLEDGE_ACK);
+
+ /* Forward skb to NCI core layer */
+ ret = nci_recv_frame(ndev->nci_dev, skb);
+
+done:
+ ndev->ops->assert_int(ndev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nci_spi_recv_frame);
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index f0c4d61f37c0..b05ad909778f 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -56,6 +56,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
[NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 },
[NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 },
[NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
+ [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
+ .len = NFC_FIRMWARE_NAME_MAXSIZE },
};
static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
@@ -424,6 +426,69 @@ free_msg:
return rc;
}
+int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_SE_ADDED);
+ if (!hdr)
+ goto free_msg;
+
+ if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+ nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) ||
+ nla_put_u8(msg, NFC_ATTR_SE_TYPE, type))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_SE_REMOVED);
+ if (!hdr)
+ goto free_msg;
+
+ if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+ nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
u32 portid, u32 seq,
struct netlink_callback *cb,
@@ -442,7 +507,6 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
- nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) ||
nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
goto nla_put_failure;
@@ -1025,6 +1089,108 @@ exit:
return rc;
}
+static int nfc_genl_fw_upload(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx;
+ char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ nla_strlcpy(firmware_name, info->attrs[NFC_ATTR_FIRMWARE_NAME],
+ sizeof(firmware_name));
+
+ rc = nfc_fw_upload(dev, firmware_name);
+
+ nfc_put_device(dev);
+ return rc;
+}
+
+int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_CMD_FW_UPLOAD);
+ if (!hdr)
+ goto free_msg;
+
+ if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
+ nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
+static int nfc_genl_enable_se(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx, se_idx;
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+ !info->attrs[NFC_ATTR_SE_INDEX])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+ se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ rc = nfc_enable_se(dev, se_idx);
+
+ nfc_put_device(dev);
+ return rc;
+}
+
+static int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ int rc;
+ u32 idx, se_idx;
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+ !info->attrs[NFC_ATTR_SE_INDEX])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+ se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ rc = nfc_disable_se(dev, se_idx);
+
+ nfc_put_device(dev);
+ return rc;
+}
+
static struct genl_ops nfc_genl_ops[] = {
{
.cmd = NFC_CMD_GET_DEVICE,
@@ -1084,6 +1250,21 @@ static struct genl_ops nfc_genl_ops[] = {
.doit = nfc_genl_llc_sdreq,
.policy = nfc_genl_policy,
},
+ {
+ .cmd = NFC_CMD_FW_UPLOAD,
+ .doit = nfc_genl_fw_upload,
+ .policy = nfc_genl_policy,
+ },
+ {
+ .cmd = NFC_CMD_ENABLE_SE,
+ .doit = nfc_genl_enable_se,
+ .policy = nfc_genl_policy,
+ },
+ {
+ .cmd = NFC_CMD_DISABLE_SE,
+ .doit = nfc_genl_disable_se,
+ .policy = nfc_genl_policy,
+ },
};
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index afa1f84ba040..ee85a1fc1b24 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -94,6 +94,9 @@ int nfc_genl_tm_deactivated(struct nfc_dev *dev);
int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list);
+int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type);
+int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx);
+
struct nfc_dev *nfc_get_device(unsigned int idx);
static inline void nfc_put_device(struct nfc_dev *dev)
@@ -120,6 +123,11 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
class_dev_iter_exit(iter);
}
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name);
+int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name);
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name);
+
int nfc_dev_up(struct nfc_dev *dev);
int nfc_dev_down(struct nfc_dev *dev);
@@ -139,4 +147,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx);
int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
data_exchange_cb_t cb, void *cb_context);
+int nfc_enable_se(struct nfc_dev *dev, u32 se_idx);
+int nfc_disable_se(struct nfc_dev *dev, u32 se_idx);
+
#endif /* __LOCAL_NFC_H */