Skip to contentSkip to conversation

NFC changes for 3.5

PATCH New Samuel Ortiz 1 participant 19 messages Apr 10, 2012

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover NFC changes for 3.5

Hi John,

This is my current NFC patchset for the next merge window. This is what we mainly have:

* An HCI and SHDLC implementation from Eric.
* A bunch of LLCP fixes.
* Some pn533 changes (workqueue conversion and Rx chaining support).
* A new netlink API for exposing lost targets events.

Please note that the first patch from this serie is a 3.4 fix.

You can also fetch those patches from here:

git://git.kernel.org/pub/scm/linux/kernel/git/sameo/nfc-3.0.git nfc-next

Eric Lapuyade (7):
NFC: Export target lost function
NFC: HCI support
NFC: SHDLC implementation
NFC: Add HCI documentation
NFC: Fix next target_idx type and rename for clarity
NFC: Changed target activated state logic
NFC: Add Core support to generate tag lost event

Samuel Ortiz (11):
NFC: Fix the LLCP Tx fragmentation loop
NFC: Add a target lost netlink event
NFC: The core part should generate the target index
NFC: Dump LLCP frames
NFC: No need to apply twice the modulo op to LLCP's recv_n
NFC: Fix LLCP TLV building routine
NFC: Call llcp_add_header properly when sending LLCP DM or DISC
NFC: Convert pn533 from tasklet to workqueues
NFC: pn533 Rx chaining support
NFC: Add MIUX to the local LLCP general bytes
NFC: Fix LLCP link timeout typo

 Documentation/nfc/nfc-hci.txt |  155 +++++++
 drivers/nfc/pn533.c           |  228 ++++++++---
 include/linux/nfc.h           |    1 +
 include/net/nfc/hci.h         |  198 +++++++++
 include/net/nfc/nfc.h         |   12 +-
 include/net/nfc/shdlc.h       |  104 +++++
 net/nfc/Kconfig               |    1 +
 net/nfc/Makefile              |    1 +
 net/nfc/core.c                |  140 ++++++-
 net/nfc/hci/Kconfig           |   16 +
 net/nfc/hci/Makefile          |    8 +
 net/nfc/hci/command.c         |  354 +++++++++++++++
 net/nfc/hci/core.c            |  830 ++++++++++++++++++++++++++++++++++++
 net/nfc/hci/hci.h             |  139 ++++++
 net/nfc/hci/hcp.c             |  156 +++++++
 net/nfc/hci/shdlc.c           |  945 +++++++++++++++++++++++++++++++++++++++++
 net/nfc/llcp/commands.c       |   12 +-
 net/nfc/llcp/llcp.c           |   21 +-
 net/nfc/nci/core.c            |    2 +-
 net/nfc/nci/ntf.c             |   11 +-
 net/nfc/netlink.c             |   30 ++
 net/nfc/nfc.h                 |    1 +
 net/nfc/rawsock.c             |    6 +
 23 files changed, 3299 insertions(+), 72 deletions(-)
 create mode 100644 Documentation/nfc/nfc-hci.txt
 create mode 100644 include/net/nfc/hci.h
 create mode 100644 include/net/nfc/shdlc.h
 create mode 100644 net/nfc/hci/Kconfig
 create mode 100644 net/nfc/hci/Makefile
 create mode 100644 net/nfc/hci/command.c
 create mode 100644 net/nfc/hci/core.c
 create mode 100644 net/nfc/hci/hci.h
 create mode 100644 net/nfc/hci/hcp.c
 create mode 100644 net/nfc/hci/shdlc.c
Signature · 1 line
1.7.9.1
#1 lore raw

PATCH 1/18 NFC: Fix the LLCP Tx fragmentation loop

---
 net/nfc/llcp/commands.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
net/nfc/llcp/commands.c +2 -2
index 7b76eb7..ef10ffc 100644
@@ -474,7 +474,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
 	while (remaining_len > 0) {
 
-		frag_len = min_t(u16, local->remote_miu, remaining_len);
+		frag_len = min_t(size_t, local->remote_miu, remaining_len);
 
 		pr_debug("Fragment %zd bytes remaining %zd",
 			 frag_len, remaining_len);
@@ -497,7 +497,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 		release_sock(sk);
 
 		remaining_len -= frag_len;
-		msg_ptr += len;
+		msg_ptr += frag_len;
 	}
 
 	kfree(msg_data);
Signature · 1 line
1.7.9.1
#2 lore raw

PATCH 2/18 NFC: Add a target lost netlink event

In reply to Samuel Ortiz

Some chips are capable of detecting when a tag is out of the field, so they could send a netlink event about it to userspace.

---
 include/linux/nfc.h |    1 +
 net/nfc/netlink.c   |   30 ++++++++++++++++++++++++++++++
 net/nfc/nfc.h       |    1 +
 3 files changed, 32 insertions(+), 0 deletions(-)
include/linux/nfc.h +1 -0
index 39c1fcf..0ae9b58 100644
@@ -70,6 +70,7 @@ enum nfc_commands {
 	NFC_EVENT_TARGETS_FOUND,
 	NFC_EVENT_DEVICE_ADDED,
 	NFC_EVENT_DEVICE_REMOVED,
+	NFC_EVENT_TARGET_LOST,
 /* private: internal use only */
 	__NFC_CMD_AFTER_LAST
 };
net/nfc/netlink.c +30 -0
index 6404052..ebdb605 100644
@@ -183,6 +183,36 @@ free_msg:
 	return -EMSGSIZE;
 }
 
+int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+			  NFC_EVENT_TARGET_LOST);
+	if (!hdr)
+		goto free_msg;
+
+	NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev));
+	NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target_idx);
+
+	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_device_added(struct nfc_dev *dev)
 {
 	struct sk_buff *msg;
net/nfc/nfc.h +1 -0
index ec8794c..2c868d2 100644
@@ -119,6 +119,7 @@ void nfc_genl_data_init(struct nfc_genl_data *genl_data);
 void nfc_genl_data_exit(struct nfc_genl_data *genl_data);
 
 int nfc_genl_targets_found(struct nfc_dev *dev);
+int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx);
 
 int nfc_genl_device_added(struct nfc_dev *dev);
 int nfc_genl_device_removed(struct nfc_dev *dev);
Signature · 1 line
1.7.9.1
#3 lore raw

PATCH 3/18 NFC: Export target lost function

In reply to Samuel Ortiz

From: Eric Lapuyade <[email protected]>

NFC drivers will call this routine when they detect that a tag leaves the RF field. This will eventually lead to the corresponding netlink event to be sent.

---
 include/net/nfc/nfc.h |    1 +
 net/nfc/core.c        |   39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 0 deletions(-)
include/net/nfc/nfc.h +1 -0
index bac070b..57ea095 100644
@@ -181,6 +181,7 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev,
 
 int nfc_targets_found(struct nfc_dev *dev,
 		      struct nfc_target *targets, int ntargets);
+int nfc_target_lost(struct nfc_dev *dev, u32 target_idx);
 
 int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx,
 		       u8 comm_mode, u8 rf_mode);
net/nfc/core.c +39 -0
index 295d129..deb4721 100644
@@ -455,6 +455,45 @@ int nfc_targets_found(struct nfc_dev *dev,
 }
 EXPORT_SYMBOL(nfc_targets_found);
 
+int nfc_target_lost(struct nfc_dev *dev, u32 target_idx)
+{
+	struct nfc_target *tg;
+	int i;
+
+	pr_debug("dev_name %s n_target %d\n", dev_name(&dev->dev), target_idx);
+
+	spin_lock_bh(&dev->targets_lock);
+
+	for (i = 0; i < dev->n_targets; i++) {
+		tg = &dev->targets[i];
+		if (tg->idx == target_idx)
+			break;
+	}
+
+	if (i == dev->n_targets) {
+		spin_unlock_bh(&dev->targets_lock);
+		return -EINVAL;
+	}
+
+	dev->targets_generation++;
+	dev->n_targets--;
+
+	if (dev->n_targets) {
+		memcpy(&dev->targets[i], &dev->targets[i + 1],
+		       (dev->n_targets - i) * sizeof(struct nfc_target));
+	} else {
+		kfree(dev->targets);
+		dev->targets = NULL;
+	}
+
+	spin_unlock_bh(&dev->targets_lock);
+
+	nfc_genl_target_lost(dev, target_idx);
+
+	return 0;
+}
+EXPORT_SYMBOL(nfc_target_lost);
+
 static void nfc_release(struct device *d)
 {
 	struct nfc_dev *dev = to_nfc_dev(d);
Signature · 1 line
1.7.9.1
#4 lore raw

PATCH 4/18 NFC: HCI support

In reply to Samuel Ortiz

From: Eric Lapuyade <[email protected]>

This is an implementation of ETSI TS 102 622 specification. Many NFC chipsets use HCI as the host <-> target protocol on top of a serial link like i2c.

---
 include/net/nfc/hci.h |  198 ++++++++++++
 include/net/nfc/nfc.h |    1 +
 net/nfc/Kconfig       |    1 +
 net/nfc/Makefile      |    1 +
 net/nfc/hci/Kconfig   |    8 +
 net/nfc/hci/Makefile  |    7 +
 net/nfc/hci/command.c |  354 +++++++++++++++++++++
 net/nfc/hci/core.c    |  830 +++++++++++++++++++++++++++++++++++++++++++++++++
 net/nfc/hci/hci.h     |  139 ++++++++
 net/nfc/hci/hcp.c     |  156 +++++++++
 10 files changed, 1695 insertions(+), 0 deletions(-)
 create mode 100644 include/net/nfc/hci.h
 create mode 100644 net/nfc/hci/Kconfig
 create mode 100644 net/nfc/hci/Makefile
 create mode 100644 net/nfc/hci/command.c
 create mode 100644 net/nfc/hci/core.c
 create mode 100644 net/nfc/hci/hci.h
 create mode 100644 net/nfc/hci/hcp.c
include/net/nfc/hci.h new file +198 -0
new file mode 100644
index 0000000..aca65a5
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2011  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 __NET_HCI_H
+#define __NET_HCI_H
+
+#include <linux/skbuff.h>
+
+#include <net/nfc/nfc.h>
+
+struct nfc_hci_dev;
+
+struct nfc_hci_ops {
+	int (*open) (struct nfc_hci_dev *hdev);
+	void (*close) (struct nfc_hci_dev *hdev);
+	int (*hci_ready) (struct nfc_hci_dev *hdev);
+	int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+	int (*start_poll) (struct nfc_hci_dev *hdev, u32 protocols);
+	int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate,
+				 struct nfc_target *target);
+	int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
+					   struct nfc_target *target);
+	int (*data_exchange) (struct nfc_hci_dev *hdev,
+			      struct nfc_target *target,
+			      struct sk_buff *skb, struct sk_buff **res_skb);
+};
+
+#define NFC_HCI_MAX_CUSTOM_GATES	15
+struct nfc_hci_init_data {
+	u8 gate_count;
+	u8 gates[NFC_HCI_MAX_CUSTOM_GATES];
+	char session_id[9];
+};
+
+typedef int (*xmit) (struct sk_buff *skb, void *cb_data);
+
+#define NFC_HCI_MAX_GATES		256
+
+struct nfc_hci_dev {
+	struct nfc_dev *ndev;
+
+	u32 max_data_link_payload;
+
+	struct mutex msg_tx_mutex;
+
+	struct list_head msg_tx_queue;
+
+	struct workqueue_struct *msg_tx_wq;
+	struct work_struct msg_tx_work;
+
+	struct timer_list cmd_timer;
+	struct hci_msg *cmd_pending_msg;
+
+	struct sk_buff_head rx_hcp_frags;
+
+	struct workqueue_struct *msg_rx_wq;
+	struct work_struct msg_rx_work;
+
+	struct sk_buff_head msg_rx_queue;
+
+	struct nfc_hci_ops *ops;
+
+	struct nfc_hci_init_data init_data;
+
+	void *clientdata;
+
+	u8 gate2pipe[NFC_HCI_MAX_GATES];
+
+	bool poll_started;
+	struct nfc_target *targets;
+	int target_count;
+
+	u8 sw_romlib;
+	u8 sw_patch;
+	u8 sw_flashlib_major;
+	u8 sw_flashlib_minor;
+
+	u8 hw_derivative;
+	u8 hw_version;
+	u8 hw_mpw;
+	u8 hw_software;
+	u8 hw_bsid;
+};
+
+/* hci device allocation */
+struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
+					    struct nfc_hci_init_data *init_data,
+					    u32 protocols,
+					    int tx_headroom,
+					    int tx_tailroom,
+					    int max_link_payload);
+void nfc_hci_free_device(struct nfc_hci_dev *hdev);
+
+int nfc_hci_register_device(struct nfc_hci_dev *hdev);
+void nfc_hci_unregister_device(struct nfc_hci_dev *hdev);
+
+void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata);
+void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev);
+
+/* Host IDs */
+#define NFC_HCI_HOST_CONTROLLER_ID	0x00
+#define NFC_HCI_TERMINAL_HOST_ID	0x01
+#define NFC_HCI_UICC_HOST_ID		0x02
+
+/* Host Controller Gates and registry indexes */
+#define NFC_HCI_ADMIN_GATE 0x00
+#define NFC_HCI_ADMIN_SESSION_IDENTITY	0x01
+#define NFC_HCI_ADMIN_MAX_PIPE		0x02
+#define NFC_HCI_ADMIN_WHITELIST		0x03
+#define NFC_HCI_ADMIN_HOST_LIST		0x04
+
+#define NFC_HCI_LOOPBACK_GATE		0x04
+
+#define NFC_HCI_ID_MGMT_GATE		0x05
+#define NFC_HCI_ID_MGMT_VERSION_SW	0x01
+#define NFC_HCI_ID_MGMT_VERSION_HW	0x03
+#define NFC_HCI_ID_MGMT_VENDOR_NAME	0x04
+#define NFC_HCI_ID_MGMT_MODEL_ID	0x05
+#define NFC_HCI_ID_MGMT_HCI_VERSION	0x02
+#define NFC_HCI_ID_MGMT_GATES_LIST	0x06
+
+#define NFC_HCI_LINK_MGMT_GATE		0x06
+#define NFC_HCI_LINK_MGMT_REC_ERROR	0x01
+
+#define NFC_HCI_RF_READER_B_GATE			0x11
+#define NFC_HCI_RF_READER_B_PUPI			0x03
+#define NFC_HCI_RF_READER_B_APPLICATION_DATA		0x04
+#define NFC_HCI_RF_READER_B_AFI				0x02
+#define NFC_HCI_RF_READER_B_HIGHER_LAYER_RESPONSE	0x01
+#define NFC_HCI_RF_READER_B_HIGHER_LAYER_DATA		0x05
+
+#define NFC_HCI_RF_READER_A_GATE		0x13
+#define NFC_HCI_RF_READER_A_UID			0x02
+#define NFC_HCI_RF_READER_A_ATQA		0x04
+#define NFC_HCI_RF_READER_A_APPLICATION_DATA	0x05
+#define NFC_HCI_RF_READER_A_SAK			0x03
+#define NFC_HCI_RF_READER_A_FWI_SFGT		0x06
+#define NFC_HCI_RF_READER_A_DATARATE_MAX	0x01
+
+#define NFC_HCI_TYPE_A_SEL_PROT(x)		(((x) & 0x60) >> 5)
+#define NFC_HCI_TYPE_A_SEL_PROT_MIFARE		0
+#define NFC_HCI_TYPE_A_SEL_PROT_ISO14443	1
+#define NFC_HCI_TYPE_A_SEL_PROT_DEP		2
+#define NFC_HCI_TYPE_A_SEL_PROT_ISO14443_DEP	3
+
+/* Generic events */
+#define NFC_HCI_EVT_HCI_END_OF_OPERATION	0x01
+#define NFC_HCI_EVT_POST_DATA			0x02
+#define NFC_HCI_EVT_HOT_PLUG			0x03
+
+/* Reader RF gates events */
+#define NFC_HCI_EVT_READER_REQUESTED	0x10
+#define NFC_HCI_EVT_END_OPERATION	0x11
+
+/* Reader Application gate events */
+#define NFC_HCI_EVT_TARGET_DISCOVERED	0x10
+
+/* receiving messages from lower layer */
+void nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result,
+			   struct sk_buff *skb);
+void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+			  struct sk_buff *skb);
+void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
+			    struct sk_buff *skb);
+void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb);
+
+/* connecting to gates and sending hci instructions */
+int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate);
+int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate);
+int nfc_hci_disconnect_all_gates(struct nfc_hci_dev *hdev);
+int nfc_hci_get_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
+		      struct sk_buff **skb);
+int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
+		      const u8 *param, size_t param_len);
+int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+		     const u8 *param, size_t param_len, struct sk_buff **skb);
+int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
+			  const u8 *param, size_t param_len);
+int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+		       const u8 *param, size_t param_len);
+
+#endif /* __NET_HCI_H */
include/net/nfc/nfc.h +1 -0
index 57ea095..431a6c5 100644
@@ -78,6 +78,7 @@ struct nfc_target {
 	u8 sensb_res[NFC_SENSB_RES_MAXSIZE];
 	u8 sensf_res_len;
 	u8 sensf_res[NFC_SENSF_RES_MAXSIZE];
+	u8 hci_reader_gate;
 };
 
 struct nfc_genl_data {
net/nfc/Kconfig +1 -0
index 44c865b..8d8d9bc 100644
@@ -14,6 +14,7 @@ menuconfig NFC
 	  be called nfc.
 
 source "net/nfc/nci/Kconfig"
+source "net/nfc/hci/Kconfig"
 source "net/nfc/llcp/Kconfig"
 
 source "drivers/nfc/Kconfig"
net/nfc/Makefile +1 -0
index 7b4a6dc..d1a117c 100644
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_NFC) += nfc.o
 obj-$(CONFIG_NFC_NCI) += nci/
+obj-$(CONFIG_NFC_HCI) += hci/
 
 nfc-objs := core.o netlink.o af_nfc.o rawsock.o
 nfc-$(CONFIG_NFC_LLCP)	+= llcp/llcp.o llcp/commands.o llcp/sock.o
net/nfc/hci/Kconfig new file +8 -0
new file mode 100644
index 0000000..c32d2d4
@@ -0,0 +1,8 @@
+config NFC_HCI
+       depends on NFC
+       tristate "NFC HCI implementation"
+       default n
+       help
+	 Say Y here if you want to build support for a kernel NFC HCI
+	 implementation. This is mostly needed for devices that only process
+	 HCI frames, like for example the NXP pn544.
net/nfc/hci/Makefile new file +7 -0
new file mode 100644
index 0000000..af17c7b
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux NFC HCI layer.
+#
+
+obj-$(CONFIG_NFC_HCI) += hci.o
+
+hci-y			:= core.o hcp.o command.o
net/nfc/hci/command.c new file +354 -0

354 lines not on this page · raw message

net/nfc/hci/core.c new file +830 -0

830 lines not on this page · raw message

net/nfc/hci/hci.h new file +139 -0

139 lines not on this page · raw message

net/nfc/hci/hcp.c new file +156 -0

156 lines not on this page · raw message

Signature · 1 line
1.7.9.1
#5 lore raw

PATCH 5/18 NFC: SHDLC implementation

In reply to Samuel Ortiz

From: Eric Lapuyade <[email protected]>

Most NFC HCI chipsets actually use a simplified HDLC link layer to carry HCI payloads.
This implementation registers itself as an HCI device on behalf of the NFC driver.

---
 include/net/nfc/shdlc.h |  104 ++++++
 net/nfc/hci/Kconfig     |    8 +
 net/nfc/hci/Makefile    |    1 +
 net/nfc/hci/shdlc.c     |  945 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1058 insertions(+), 0 deletions(-)
 create mode 100644 include/net/nfc/shdlc.h
 create mode 100644 net/nfc/hci/shdlc.c
include/net/nfc/shdlc.h new file +104 -0
new file mode 100644
index 0000000..1071987
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 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 __NFC_SHDLC_H
+#define __NFC_SHDLC_H
+
+struct nfc_shdlc;
+
+struct nfc_shdlc_ops {
+	int (*open) (struct nfc_shdlc *shdlc);
+	void (*close) (struct nfc_shdlc *shdlc);
+	int (*hci_ready) (struct nfc_shdlc *shdlc);
+	int (*xmit) (struct nfc_shdlc *shdlc, struct sk_buff *skb);
+	int (*start_poll) (struct nfc_shdlc *shdlc, u32 protocols);
+	int (*target_from_gate) (struct nfc_shdlc *shdlc, u8 gate,
+				 struct nfc_target *target);
+	int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate,
+					   struct nfc_target *target);
+	int (*data_exchange) (struct nfc_shdlc *shdlc,
+			      struct nfc_target *target,
+			      struct sk_buff *skb, struct sk_buff **res_skb);
+};
+
+enum shdlc_state {
+	SHDLC_DISCONNECTED = 0,
+	SHDLC_CONNECTING = 1,
+	SHDLC_NEGOCIATING = 2,
+	SHDLC_CONNECTED = 3
+};
+
+struct nfc_shdlc {
+	struct mutex state_mutex;
+	enum shdlc_state state;
+	int hard_fault;
+
+	struct nfc_hci_dev *hdev;
+
+	wait_queue_head_t *connect_wq;
+	int connect_tries;
+	int connect_result;
+	struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
+
+	u8 w;				/* window size */
+	bool srej_support;
+
+	struct timer_list t1_timer;	/* send ack timeout */
+	bool t1_active;
+
+	struct timer_list t2_timer;	/* guard/retransmit timeout */
+	bool t2_active;
+
+	int ns;				/* next seq num for send */
+	int nr;				/* next expected seq num for receive */
+	int dnr;			/* oldest sent unacked seq num */
+
+	struct sk_buff_head rcv_q;
+
+	struct sk_buff_head send_q;
+	bool rnr;			/* other side is not ready to receive */
+
+	struct sk_buff_head ack_pending_q;
+
+	struct workqueue_struct *sm_wq;
+	struct work_struct sm_work;
+
+	struct nfc_shdlc_ops *ops;
+
+	int client_headroom;
+	int client_tailroom;
+
+	void *clientdata;
+};
+
+void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb);
+
+struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
+				     struct nfc_hci_init_data *init_data,
+				     u32 protocols,
+				     int tx_headroom, int tx_tailroom,
+				     int max_link_payload, const char *devname);
+
+void nfc_shdlc_free(struct nfc_shdlc *shdlc);
+
+void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata);
+void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc);
+struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc);
+
+#endif /* __NFC_SHDLC_H */
net/nfc/hci/Kconfig +8 -0
index c32d2d4..17213a6 100644
@@ -6,3 +6,11 @@ config NFC_HCI
 	 Say Y here if you want to build support for a kernel NFC HCI
 	 implementation. This is mostly needed for devices that only process
 	 HCI frames, like for example the NXP pn544.
+
+config NFC_SHDLC
+	depends on NFC_HCI
+	bool "SHDLC link layer for HCI based NFC drivers"
+	default n
+	---help---
+	  Say yes if you use an NFC HCI driver that requires SHDLC link layer.
+	  If unsure, say N here.
net/nfc/hci/Makefile +1 -0
index af17c7b..f9c44b2 100644
@@ -5,3 +5,4 @@
 obj-$(CONFIG_NFC_HCI) += hci.o
 
 hci-y			:= core.o hcp.o command.o
+hci-$(CONFIG_NFC_SHDLC)	+= shdlc.o
net/nfc/hci/shdlc.c new file +945 -0

945 lines not on this page · raw message

Signature · 1 line
1.7.9.1
#6 lore raw

PATCH 6/18 NFC: Add HCI documentation

In reply to Samuel Ortiz

From: Eric Lapuyade <[email protected]>

---
 Documentation/nfc/nfc-hci.txt |  155 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 155 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/nfc/nfc-hci.txt
Documentation/nfc/nfc-hci.txt new file +155 -0
new file mode 100644
index 0000000..216b725
@@ -0,0 +1,155 @@
+HCI backend for NFC Core
+
+Author: Eric Lapuyade, Samuel Ortiz
+Contact: [email protected], [email protected]
+
+General
+-------
+
+The HCI layer implements much of the ETSI TS 102 622 V10.2.0 specification. It
+enables easy writing of HCI-based NFC drivers. The HCI layer runs as an NFC Core
+backend, implementing an abstract nfc device and translating NFC Core API
+to HCI commands and events.
+
+HCI
+---
+
+HCI registers as an nfc device with NFC Core. Requests coming from userspace are
+routed through netlink sockets to NFC Core and then to HCI. From this point,
+they are translated in a sequence of HCI commands sent to the HCI layer in the
+host controller (the chip). The sending context blocks while waiting for the
+response to arrive.
+HCI events can also be received from the host controller. They will be handled
+and a translation will be forwarded to NFC Core as needed.
+HCI uses 2 execution contexts:
+- one if for executing commands : nfc_hci_msg_tx_work(). Only one command
+can be executing at any given moment.
+- one if for dispatching received events and responses : nfc_hci_msg_rx_work()
+
+HCI Session initialization:
+---------------------------
+
+The Session initialization is an HCI standard which must unfortunately
+support proprietary gates. This is the reason why the driver will pass a list
+of proprietary gates that must be part of the session. HCI will ensure all
+those gates have pipes connected when the hci device is set up.
+
+HCI Gates and Pipes
+-------------------
+
+A gate defines the 'port' where some service can be found. In order to access
+a service, one must create a pipe to that gate and open it. In this
+implementation, pipes are totally hidden. The public API only knows gates.
+This is consistent with the driver need to send commands to proprietary gates
+without knowing the pipe connected to it.
+
+Driver interface
+----------------
+
+A driver would normally register itself with HCI and provide the following
+entry points:
+
+struct nfc_hci_ops {
+	int (*open)(struct nfc_hci_dev *hdev);
+	void (*close)(struct nfc_hci_dev *hdev);
+	int (*xmit)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
+	int (*start_poll)(struct nfc_hci_dev *hdev, u32 protocols);
+	int (*target_from_gate)(struct nfc_hci_dev *hdev, u8 gate,
+				struct nfc_target *target);
+};
+
+open() and close() shall turn the hardware on and off. xmit() shall simply
+write a frame to the chip. start_poll() is an optional entrypoint that shall
+set the hardware in polling mode. This must be implemented only if the hardware
+uses proprietary gates or a mechanism slightly different from the HCI standard.
+target_from_gate() is another optional entrypoint to return the protocols
+corresponding to a proprietary gate.
+
+On the rx path, the driver is responsible to push incoming HCP frames to HCI
+using nfc_hci_recv_frame(). HCI will take care of re-aggregation and handling
+This must be done from a context that can sleep.
+
+SHDLC
+-----
+
+Most chips use shdlc to ensure integrity and delivery ordering of the HCP
+frames between the host controller (the chip) and hosts (entities connected
+to the chip, like the cpu). In order to simplify writing the driver, an shdlc
+layer is available for use by the driver.
+When used, the driver actually registers with shdlc, and shdlc will register
+with HCI. HCI sees shdlc as the driver and thus send its HCP frames
+through shdlc->xmit.
+SHDLC adds a new execution context (nfc_shdlc_sm_work()) to run its state
+machine and handle both its rx and tx path.
+
+Included Drivers
+----------------
+
+An HCI based driver for an NXP PN544, connected through I2C bus, and using
+shdlc is included.
+
+Execution Contexts
+------------------
+
+The execution contexts are the following:
+- IRQ handler (IRQH):
+fast, cannot sleep. stores incoming frames into an shdlc rx queue
+
+- SHDLC State Machine worker (SMW)
+handles shdlc rx & tx queues. Dispatches HCI cmd responses.
+
+- HCI Tx Cmd worker (MSGTXWQ)
+Serialize execution of HCI commands. Complete execution in case of resp timeout.
+
+- HCI Rx worker (MSGRXWQ)
+Dispatches incoming HCI commands or events.
+
+- Syscall context from a userspace call (SYSCALL)
+Any entrypoint in HCI called from NFC Core
+
+Workflow executing an HCI command (using shdlc)
+-----------------------------------------------
+
+Executing an HCI command can easily be performed synchronously using the
+following API:
+
+int nfc_hci_send_cmd (struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+			const u8 *param, size_t param_len, struct sk_buff **skb)
+
+The API must be invoked from a context that can sleep. Most of the time, this
+will be the syscall context. skb will return the result that was received in
+the response.
+
+Internally, execution is asynchronous. So all this API does is to enqueue the
+HCI command, setup a local wait queue on stack, and wait_event() for completion.
+The wait is not interruptible because it is guaranteed that the command will
+complete after some short timeout anyway.
+
+MSGTXWQ context will then be scheduled and invoke nfc_hci_msg_tx_work().
+This function will dequeue the next pending command and send its HCP fragments
+to the lower layer which happens to be shdlc. It will then start a timer to be
+able to complete the command with a timeout error if no response arrive.
+
+SMW context gets scheduled and invokes nfc_shdlc_sm_work(). This function
+handles shdlc framing in and out. It uses the driver xmit to send frames and
+receives incoming frames in an skb queue filled from the driver IRQ handler.
+SHDLC I(nformation) frames payload are HCP fragments. They are agregated to
+form complete HCI frames, which can be a response, command, or event.
+
+HCI Responses are dispatched immediately from this context to unblock
+waiting command execution. Reponse processing involves invoking the completion
+callback that was provided by nfc_hci_msg_tx_work() when it sent the command.
+The completion callback will then wake the syscall context.
+
+Workflow receiving an HCI event or command
+------------------------------------------
+
+HCI commands or events are not dispatched from SMW context. Instead, they are
+queued to HCI rx_queue and will be dispatched from HCI rx worker
+context (MSGRXWQ). This is done this way to allow a cmd or event handler
+to also execute other commands (for example, handling the
+NFC_HCI_EVT_TARGET_DISCOVERED event from PN544 requires to issue an
+ANY_GET_PARAMETER to the reader A gate to get information on the target
+that was discovered).
+
+Typically, such an event will be propagated to NFC Core from MSGRXWQ context.
Signature · 1 line
1.7.9.1
#7 lore raw

PATCH 7/18 NFC: The core part should generate the target index

In reply to Samuel Ortiz

The target index can be used by userspace to uniquely identify a target and thus should be kept unique, per NFC adapter. Moreover, some protocols do not provide a logical index when discovering new targets, so we have to generate one for them.
For NCI or pn533 to fetch their logical index, we added a logical_idx field to the target structure.

---
 include/net/nfc/nfc.h |    2 ++
 net/nfc/core.c        |    5 +++++
 net/nfc/nci/core.c    |    2 +-
 net/nfc/nci/ntf.c     |   11 ++++++-----
 net/nfc/rawsock.c     |    6 ++++++
 5 files changed, 20 insertions(+), 6 deletions(-)
include/net/nfc/nfc.h +2 -0
index 431a6c5..45f0563 100644
@@ -79,6 +79,7 @@ struct nfc_target {
 	u8 sensf_res_len;
 	u8 sensf_res[NFC_SENSF_RES_MAXSIZE];
 	u8 hci_reader_gate;
+	u8 logical_idx;
 };
 
 struct nfc_genl_data {
@@ -88,6 +89,7 @@ struct nfc_genl_data {
 
 struct nfc_dev {
 	unsigned idx;
+	unsigned target_idx;
 	struct nfc_target *targets;
 	int n_targets;
 	int targets_generation;
net/nfc/core.c +5 -0
index deb4721..d924000 100644
@@ -428,10 +428,15 @@ EXPORT_SYMBOL(nfc_alloc_recv_skb);
 int nfc_targets_found(struct nfc_dev *dev,
 		      struct nfc_target *targets, int n_targets)
 {
+	int i;
+
 	pr_debug("dev_name=%s n_targets=%d\n", dev_name(&dev->dev), n_targets);
 
 	dev->polling = false;
 
+	for (i = 0; i < n_targets; i++)
+		targets[i].idx = dev->target_idx++;
+
 	spin_lock_bh(&dev->targets_lock);
 
 	dev->targets_generation++;
net/nfc/nci/core.c +1 -1
index 9ec065b..8737c20 100644
@@ -477,7 +477,7 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx,
 	}
 
 	if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) {
-		param.rf_discovery_id = target->idx;
+		param.rf_discovery_id = target->logical_idx;
 
 		if (protocol == NFC_PROTO_JEWEL)
 			param.rf_protocol = NCI_RF_PROTOCOL_T1T;
net/nfc/nci/ntf.c +6 -5
index 2e3dee4..99e1632 100644
@@ -227,7 +227,7 @@ static void nci_add_new_target(struct nci_dev *ndev,
 
 	for (i = 0; i < ndev->n_targets; i++) {
 		target = &ndev->targets[i];
-		if (target->idx == ntf->rf_discovery_id) {
+		if (target->logical_idx == ntf->rf_discovery_id) {
 			/* This target already exists, add the new protocol */
 			nci_add_new_protocol(ndev, target, ntf->rf_protocol,
 					     ntf->rf_tech_and_mode,
@@ -248,10 +248,10 @@ static void nci_add_new_target(struct nci_dev *ndev,
 				  ntf->rf_tech_and_mode,
 				  &ntf->rf_tech_specific_params);
 	if (!rc) {
-		target->idx = ntf->rf_discovery_id;
+		target->logical_idx = ntf->rf_discovery_id;
 		ndev->n_targets++;
 
-		pr_debug("target_idx %d, n_targets %d\n", target->idx,
+		pr_debug("logical idx %d, n_targets %d\n", target->logical_idx,
 			 ndev->n_targets);
 	}
 }
@@ -372,10 +372,11 @@ static void nci_target_auto_activated(struct nci_dev *ndev,
 	if (rc)
 		return;
 
-	target->idx = ntf->rf_discovery_id;
+	target->logical_idx = ntf->rf_discovery_id;
 	ndev->n_targets++;
 
-	pr_debug("target_idx %d, n_targets %d\n", target->idx, ndev->n_targets);
+	pr_debug("logical idx %d, n_targets %d\n",
+		 target->logical_idx, ndev->n_targets);
 
 	nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets);
 }
net/nfc/rawsock.c +6 -0
index 5a839ce..b2825aa 100644
@@ -92,6 +92,12 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr,
 		goto error;
 	}
 
+	if (addr->target_idx > dev->target_idx - 1 ||
+	    addr->target_idx < dev->target_idx - dev->n_targets) {
+		rc = -EINVAL;
+		goto error;
+	}
+
 	rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol);
 	if (rc)
 		goto put_dev;
Signature · 1 line
1.7.9.1
#8 lore raw

PATCH 8/18 NFC: Fix next target_idx type and rename for clarity

In reply to Samuel Ortiz

From: Eric Lapuyade <[email protected]>

---
 include/net/nfc/nfc.h |    2 +-
 net/nfc/core.c        |    2 +-
 net/nfc/rawsock.c     |    4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)
include/net/nfc/nfc.h +1 -1
index 45f0563..f4f6950 100644
@@ -89,7 +89,7 @@ struct nfc_genl_data {
 
 struct nfc_dev {
 	unsigned idx;
-	unsigned target_idx;
+	u32 target_next_idx;
 	struct nfc_target *targets;
 	int n_targets;
 	int targets_generation;
net/nfc/core.c +1 -1
index d924000..db88429 100644
@@ -435,7 +435,7 @@ int nfc_targets_found(struct nfc_dev *dev,
 	dev->polling = false;
 
 	for (i = 0; i < n_targets; i++)
-		targets[i].idx = dev->target_idx++;
+		targets[i].idx = dev->target_next_idx++;
 
 	spin_lock_bh(&dev->targets_lock);
 
net/nfc/rawsock.c +2 -2
index b2825aa..ec1134c 100644
@@ -92,8 +92,8 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr,
 		goto error;
 	}
 
-	if (addr->target_idx > dev->target_idx - 1 ||
-	    addr->target_idx < dev->target_idx - dev->n_targets) {
+	if (addr->target_idx > dev->target_next_idx - 1 ||
+	    addr->target_idx < dev->target_next_idx - dev->n_targets) {
 		rc = -EINVAL;
 		goto error;
 	}
Signature · 1 line
1.7.9.1
#9 lore raw

PATCH 9/18 NFC: Changed target activated state logic

In reply to Samuel Ortiz

From: Eric Lapuyade <[email protected]>

---
 include/net/nfc/nfc.h |    3 ++-
 net/nfc/core.c        |   24 +++++++++++++++++++++---
 2 files changed, 23 insertions(+), 4 deletions(-)
include/net/nfc/nfc.h +2 -1
index f4f6950..7273ff1 100644
@@ -66,6 +66,7 @@ struct nfc_ops {
 
 #define NFC_TARGET_IDX_ANY -1
 #define NFC_MAX_GT_LEN 48
+#define NFC_TARGET_IDX_NONE 0xffffffff
 
 struct nfc_target {
 	u32 idx;
@@ -97,7 +98,7 @@ struct nfc_dev {
 	struct device dev;
 	bool dev_up;
 	bool polling;
-	bool remote_activated;
+	u32 activated_target_idx;
 	bool dep_link_up;
 	u32 dep_rf_mode;
 	struct nfc_genl_data genl_data;
net/nfc/core.c +21 -3
index db88429..44a7018 100644
@@ -95,7 +95,7 @@ int nfc_dev_down(struct nfc_dev *dev)
 		goto error;
 	}
 
-	if (dev->polling || dev->remote_activated) {
+	if (dev->polling || dev->activated_target_idx != NFC_TARGET_IDX_NONE) {
 		rc = -EBUSY;
 		goto error;
 	}
@@ -211,6 +211,8 @@ int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
 	}
 
 	rc = dev->ops->dep_link_up(dev, target_index, comm_mode, gb, gb_len);
+	if (!rc)
+		dev->activated_target_idx = target_index;
 
 error:
 	device_unlock(&dev->dev);
@@ -246,6 +248,7 @@ int nfc_dep_link_down(struct nfc_dev *dev)
 	rc = dev->ops->dep_link_down(dev);
 	if (!rc) {
 		dev->dep_link_up = false;
+		dev->activated_target_idx = NFC_TARGET_IDX_NONE;
 		nfc_llcp_mac_is_down(dev);
 		nfc_genl_dep_link_down_event(dev);
 	}
@@ -290,7 +293,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
 
 	rc = dev->ops->activate_target(dev, target_idx, protocol);
 	if (!rc)
-		dev->remote_activated = true;
+		dev->activated_target_idx = target_idx;
 
 error:
 	device_unlock(&dev->dev);
@@ -318,7 +321,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx)
 	}
 
 	dev->ops->deactivate_target(dev, target_idx);
-	dev->remote_activated = false;
+	dev->activated_target_idx = NFC_TARGET_IDX_NONE;
 
 error:
 	device_unlock(&dev->dev);
@@ -352,6 +355,18 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
 		goto error;
 	}
 
+	if (dev->activated_target_idx == NFC_TARGET_IDX_NONE) {
+		rc = -ENOTCONN;
+		kfree_skb(skb);
+		goto error;
+	}
+
+	if (target_idx != dev->activated_target_idx) {
+		rc = -EADDRNOTAVAIL;
+		kfree_skb(skb);
+		goto error;
+	}
+
 	rc = dev->ops->data_exchange(dev, target_idx, skb, cb, cb_context);
 
 error:
@@ -482,6 +497,7 @@ int nfc_target_lost(struct nfc_dev *dev, u32 target_idx)
 
 	dev->targets_generation++;
 	dev->n_targets--;
+	dev->activated_target_idx = NFC_TARGET_IDX_NONE;
 
 	if (dev->n_targets) {
 		memcpy(&dev->targets[i], &dev->targets[i + 1],
@@ -575,6 +591,8 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
 	/* first generation must not be 0 */
 	dev->targets_generation = 1;
 
+	dev->activated_target_idx = NFC_TARGET_IDX_NONE;
+
 	return dev;
 }
 EXPORT_SYMBOL(nfc_allocate_device);
Signature · 1 line
1.7.9.1
#10 lore raw

PATCH 10/18 NFC: Add Core support to generate tag lost event

In reply to Samuel Ortiz

From: Eric Lapuyade <[email protected]>

Some HW/drivers get notifications when a tag moves out of the radio field. This notification is now forwarded to user space through netlink.

---
 include/net/nfc/nfc.h |    5 +++
 net/nfc/core.c        |   72 ++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 76 insertions(+), 1 deletions(-)
include/net/nfc/nfc.h +5 -0
index 7273ff1..313d00f 100644
@@ -62,6 +62,7 @@ struct nfc_ops {
 	int (*data_exchange)(struct nfc_dev *dev, u32 target_idx,
 			     struct sk_buff *skb, data_exchange_cb_t cb,
 			     void *cb_context);
+	int (*check_presence)(struct nfc_dev *dev, u32 target_idx);
 };
 
 #define NFC_TARGET_IDX_ANY -1
@@ -107,6 +108,10 @@ struct nfc_dev {
 	int tx_headroom;
 	int tx_tailroom;
 
+	struct timer_list check_pres_timer;
+	struct workqueue_struct *check_pres_wq;
+	struct work_struct check_pres_work;
+
 	struct nfc_ops *ops;
 };
 #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
net/nfc/core.c +71 -1

117 lines not on this page · raw message

Signature · 1 line
1.7.9.1
#11 lore raw

PATCH 11/18 NFC: Dump LLCP frames

In reply to Samuel Ortiz

At KERN_DEBUG level.

---
 net/nfc/llcp/llcp.c |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)
net/nfc/llcp/llcp.c +7 -0
index 17a578f..d5e87c3 100644
@@ -388,6 +388,9 @@ static void nfc_llcp_tx_work(struct work_struct *work)
 	skb = skb_dequeue(&local->tx_queue);
 	if (skb != NULL) {
 		pr_debug("Sending pending skb\n");
+		print_hex_dump(KERN_DEBUG, "LLCP Tx: ", DUMP_PREFIX_OFFSET,
+			       16, 1, skb->data, skb->len, true);
+
 		nfc_data_exchange(local->dev, local->target_idx,
 				  skb, nfc_llcp_recv, local);
 	} else {
@@ -814,6 +817,10 @@ static void nfc_llcp_rx_work(struct work_struct *work)
 
 	pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap);
 
+	if (ptype != LLCP_PDU_SYMM)
+		print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET,
+			       16, 1, skb->data, skb->len, true);
+
 	switch (ptype) {
 	case LLCP_PDU_SYMM:
 		pr_debug("SYMM\n");
Signature · 1 line
1.7.9.1
#12 lore raw

PATCH 12/18 NFC: No need to apply twice the modulo op to LLCP's recv_n

In reply to Samuel Ortiz

recv_n is set properly when receiving an HDLC frame.

---
 net/nfc/llcp/commands.c |    2 +-
 net/nfc/llcp/llcp.c     |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
net/nfc/llcp/commands.c +1 -1
index ef10ffc..4aa52b8 100644
@@ -522,7 +522,7 @@ int nfc_llcp_send_rr(struct nfc_llcp_sock *sock)
 
 	skb_put(skb, LLCP_SEQUENCE_SIZE);
 
-	skb->data[2] = sock->recv_n % 16;
+	skb->data[2] = sock->recv_n;
 
 	skb_queue_head(&local->tx_queue, skb);
 
net/nfc/llcp/llcp.c +1 -1
index d5e87c3..2cf01e6 100644
@@ -428,7 +428,7 @@ static u8 nfc_llcp_nr(struct sk_buff *pdu)
 
 static void nfc_llcp_set_nrns(struct nfc_llcp_sock *sock, struct sk_buff *pdu)
 {
-	pdu->data[2] = (sock->send_n << 4) | (sock->recv_n % 16);
+	pdu->data[2] = (sock->send_n << 4) | (sock->recv_n);
 	sock->send_n = (sock->send_n + 1) % 16;
 	sock->recv_ack_n = (sock->recv_n - 1) % 16;
 }
Signature · 1 line
1.7.9.1
#13 lore raw

PATCH 13/18 NFC: Fix LLCP TLV building routine

In reply to Samuel Ortiz

The if logic could lead to zero length TLVs.

---
 net/nfc/llcp/commands.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
net/nfc/llcp/commands.c +1 -1
index 4aa52b8..34ee684 100644
@@ -102,7 +102,7 @@ u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length)
 	length = llcp_tlv_length[type];
 	if (length == 0 && value_length == 0)
 		return NULL;
-	else
+	else if (length == 0)
 		length = value_length;
 
 	*tlv_length = 2 + length;
Signature · 1 line
1.7.9.1
#14 lore raw

PATCH 14/18 NFC: Call llcp_add_header properly when sending LLCP DM or DISC

In reply to Samuel Ortiz

dsap and ssap were swapped when sending DN or DISC.

---
 net/nfc/llcp/commands.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
net/nfc/llcp/commands.c +2 -2
index 34ee684..11a3b7d 100644
@@ -248,7 +248,7 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
 
 	skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
 
-	skb = llcp_add_header(skb, sock->ssap, sock->dsap, LLCP_PDU_DISC);
+	skb = llcp_add_header(skb, sock->dsap, sock->ssap, LLCP_PDU_DISC);
 
 	skb_queue_tail(&local->tx_queue, skb);
 
@@ -416,7 +416,7 @@ int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
 
 	skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
 
-	skb = llcp_add_header(skb, ssap, dsap, LLCP_PDU_DM);
+	skb = llcp_add_header(skb, dsap, ssap, LLCP_PDU_DM);
 
 	memcpy(skb_put(skb, 1), &reason, 1);
 
Signature · 1 line
1.7.9.1
#15 lore raw

PATCH 15/18 NFC: Convert pn533 from tasklet to workqueues

In reply to Samuel Ortiz

There is no need for soft IRQ contexts, and workqueues are more flexible.

---
 drivers/nfc/pn533.c |   86 ++++++++++++++++++++++++++++----------------------
 1 files changed, 48 insertions(+), 38 deletions(-)
drivers/nfc/pn533.c +48 -38
index cb6204f..f2ee06f 100644
@@ -266,9 +266,10 @@ struct pn533 {
 	int in_maxlen;
 	struct pn533_frame *in_frame;
 
-	struct tasklet_struct tasklet;
-	struct pn533_frame *tklt_in_frame;
-	int tklt_in_error;
+	struct workqueue_struct	*wq;
+	struct work_struct cmd_work;
+	struct pn533_frame *wq_in_frame;
+	int wq_in_error;
 
 	pn533_cmd_complete_t cmd_complete;
 	void *cmd_complete_arg;
@@ -383,15 +384,21 @@ static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd)
 	return (PN533_FRAME_CMD(frame) == PN533_CMD_RESPONSE(cmd));
 }
 
-static void pn533_tasklet_cmd_complete(unsigned long arg)
+
+static void pn533_wq_cmd_complete(struct work_struct *work)
 {
-	struct pn533 *dev = (struct pn533 *) arg;
-	struct pn533_frame *in_frame = dev->tklt_in_frame;
+	struct pn533 *dev = container_of(work, struct pn533, cmd_work);
+	struct pn533_frame *in_frame;
 	int rc;
 
-	if (dev->tklt_in_error)
+	if (dev == NULL)
+		return;
+
+	in_frame = dev->wq_in_frame;
+
+	if (dev->wq_in_error)
 		rc = dev->cmd_complete(dev, dev->cmd_complete_arg, NULL,
-							dev->tklt_in_error);
+							dev->wq_in_error);
 	else
 		rc = dev->cmd_complete(dev, dev->cmd_complete_arg,
 					PN533_FRAME_CMD_PARAMS_PTR(in_frame),
@@ -406,7 +413,7 @@ static void pn533_recv_response(struct urb *urb)
 	struct pn533 *dev = urb->context;
 	struct pn533_frame *in_frame;
 
-	dev->tklt_in_frame = NULL;
+	dev->wq_in_frame = NULL;
 
 	switch (urb->status) {
 	case 0:
@@ -417,36 +424,36 @@ static void pn533_recv_response(struct urb *urb)
 	case -ESHUTDOWN:
 		nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
 						" status: %d", urb->status);
-		dev->tklt_in_error = urb->status;
-		goto sched_tasklet;
+		dev->wq_in_error = urb->status;
+		goto sched_wq;
 	default:
 		nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:"
 							" %d", urb->status);
-		dev->tklt_in_error = urb->status;
-		goto sched_tasklet;
+		dev->wq_in_error = urb->status;
+		goto sched_wq;
 	}
 
 	in_frame = dev->in_urb->transfer_buffer;
 
 	if (!pn533_rx_frame_is_valid(in_frame)) {
 		nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
-		dev->tklt_in_error = -EIO;
-		goto sched_tasklet;
+		dev->wq_in_error = -EIO;
+		goto sched_wq;
 	}
 
 	if (!pn533_rx_frame_is_cmd_response(in_frame, dev->cmd)) {
 		nfc_dev_err(&dev->interface->dev, "The received frame is not "
 						"response to the last command");
-		dev->tklt_in_error = -EIO;
-		goto sched_tasklet;
+		dev->wq_in_error = -EIO;
+		goto sched_wq;
 	}
 
 	nfc_dev_dbg(&dev->interface->dev, "Received a valid frame");
-	dev->tklt_in_error = 0;
-	dev->tklt_in_frame = in_frame;
+	dev->wq_in_error = 0;
+	dev->wq_in_frame = in_frame;
 
-sched_tasklet:
-	tasklet_schedule(&dev->tasklet);
+sched_wq:
+	queue_work(dev->wq, &dev->cmd_work);
 }
 
 static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags)
@@ -471,21 +478,21 @@ static void pn533_recv_ack(struct urb *urb)
 	case -ESHUTDOWN:
 		nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with"
 						" status: %d", urb->status);
-		dev->tklt_in_error = urb->status;
-		goto sched_tasklet;
+		dev->wq_in_error = urb->status;
+		goto sched_wq;
 	default:
 		nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:"
 							" %d", urb->status);
-		dev->tklt_in_error = urb->status;
-		goto sched_tasklet;
+		dev->wq_in_error = urb->status;
+		goto sched_wq;
 	}
 
 	in_frame = dev->in_urb->transfer_buffer;
 
 	if (!pn533_rx_frame_is_ack(in_frame)) {
 		nfc_dev_err(&dev->interface->dev, "Received an invalid ack");
-		dev->tklt_in_error = -EIO;
-		goto sched_tasklet;
+		dev->wq_in_error = -EIO;
+		goto sched_wq;
 	}
 
 	nfc_dev_dbg(&dev->interface->dev, "Received a valid ack");
@@ -494,15 +501,15 @@ static void pn533_recv_ack(struct urb *urb)
 	if (rc) {
 		nfc_dev_err(&dev->interface->dev, "usb_submit_urb failed with"
 							" result %d", rc);
-		dev->tklt_in_error = rc;
-		goto sched_tasklet;
+		dev->wq_in_error = rc;
+		goto sched_wq;
 	}
 
 	return;
 
-sched_tasklet:
-	dev->tklt_in_frame = NULL;
-	tasklet_schedule(&dev->tasklet);
+sched_wq:
+	dev->wq_in_frame = NULL;
+	queue_work(dev->wq, &dev->cmd_work);
 }
 
 static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags)
@@ -1668,7 +1675,10 @@ static int pn533_probe(struct usb_interface *interface,
 			NULL, 0,
 			pn533_send_complete, dev);
 
-	tasklet_init(&dev->tasklet, pn533_tasklet_cmd_complete, (ulong)dev);
+	INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete);
+	dev->wq = create_singlethread_workqueue("pn533");
+	if (dev->wq == NULL)
+		goto error;
 
 	usb_set_intfdata(interface, dev);
 
@@ -1678,7 +1688,7 @@ static int pn533_probe(struct usb_interface *interface,
 	rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame,
 								dev->in_maxlen);
 	if (rc)
-		goto kill_tasklet;
+		goto destroy_wq;
 
 	fw_ver = (struct pn533_fw_version *)
 				PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame);
@@ -1694,7 +1704,7 @@ static int pn533_probe(struct usb_interface *interface,
 					   PN533_CMD_DATAEXCH_HEAD_LEN,
 					   PN533_FRAME_TAIL_SIZE);
 	if (!dev->nfc_dev)
-		goto kill_tasklet;
+		goto destroy_wq;
 
 	nfc_set_parent_dev(dev->nfc_dev, &interface->dev);
 	nfc_set_drvdata(dev->nfc_dev, dev);
@@ -1720,8 +1730,8 @@ static int pn533_probe(struct usb_interface *interface,
 
 free_nfc_dev:
 	nfc_free_device(dev->nfc_dev);
-kill_tasklet:
-	tasklet_kill(&dev->tasklet);
+destroy_wq:
+	destroy_workqueue(dev->wq);
 error:
 	kfree(dev->in_frame);
 	usb_free_urb(dev->in_urb);
@@ -1744,7 +1754,7 @@ static void pn533_disconnect(struct usb_interface *interface)
 	usb_kill_urb(dev->in_urb);
 	usb_kill_urb(dev->out_urb);
 
-	tasklet_kill(&dev->tasklet);
+	destroy_workqueue(dev->wq);
 
 	kfree(dev->in_frame);
 	usb_free_urb(dev->in_urb);
Signature · 1 line
1.7.9.1
#16 lore raw

PATCH 16/18 NFC: pn533 Rx chaining support

In reply to Samuel Ortiz

When buffers on the receiption path exceed 262 bytes, the pn533 uses a chaining mechanism where the initiator has to send NULL data frames to fetch the remaining frames.
We do that from a workqueue context while holding the cmd lock. Once the MI bit is gone, we aggregate the queued received skbs.

---
 drivers/nfc/pn533.c |  144 +++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 128 insertions(+), 16 deletions(-)
drivers/nfc/pn533.c +128 -16
index f2ee06f..e6ec16d 100644
@@ -266,8 +266,11 @@ struct pn533 {
 	int in_maxlen;
 	struct pn533_frame *in_frame;
 
+	struct sk_buff_head resp_q;
+
 	struct workqueue_struct	*wq;
 	struct work_struct cmd_work;
+	struct work_struct mi_work;
 	struct pn533_frame *wq_in_frame;
 	int wq_in_error;
 
@@ -1256,6 +1259,8 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, u32 target_idx)
 
 	dev->tgt_active_prot = 0;
 
+	skb_queue_purge(&dev->resp_q);
+
 	pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_RELEASE);
 
 	tg = 1;
@@ -1454,11 +1459,49 @@ struct pn533_data_exchange_arg {
 	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\n", __func__);
+
+	if (skb_queue_empty(&dev->resp_q))
+		return NULL;
+
+	if (skb_queue_len(&dev->resp_q) == 1) {
+		skb = skb_dequeue(&dev->resp_q);
+		goto out;
+	}
+
+	skb_queue_walk_safe(&dev->resp_q, tmp, t)
+		skb_len += tmp->len;
+
+	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,
 						u8 *params, int params_len)
 {
 	struct pn533_data_exchange_arg *arg = _arg;
-	struct sk_buff *skb_resp = arg->skb_resp;
+	struct sk_buff *skb = NULL, *skb_resp = arg->skb_resp;
 	struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
 	int err = 0;
 	u8 status;
@@ -1466,15 +1509,13 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
 
 	nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-	dev_kfree_skb_irq(arg->skb_out);
+	dev_kfree_skb(arg->skb_out);
 
 	if (params_len < 0) { /* error */
 		err = params_len;
 		goto error;
 	}
 
-	skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
-
 	status = params[0];
 
 	cmd_ret = status & PN533_CMD_RET_MASK;
@@ -1485,25 +1526,27 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
 		goto error;
 	}
 
+	skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
+	skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
+	skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
+	skb_queue_tail(&dev->resp_q, skb_resp);
+
 	if (status & PN533_CMD_MI_MASK) {
-		/* TODO: Implement support to multi-part data exchange */
-		nfc_dev_err(&dev->interface->dev, "Multi-part message not yet"
-								" supported");
-		/* Prevent the other messages from controller */
-		pn533_send_ack(dev, GFP_ATOMIC);
-		err = -ENOSYS;
-		goto error;
+		queue_work(dev->wq, &dev->mi_work);
+		return -EINPROGRESS;
 	}
 
-	skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
-	skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
+	skb = pn533_build_response(dev);
+	if (skb == NULL)
+		goto error;
 
-	arg->cb(arg->cb_context, skb_resp, 0);
+	arg->cb(arg->cb_context, skb, 0);
 	kfree(arg);
 	return 0;
 
 error:
-	dev_kfree_skb_irq(skb_resp);
+	skb_queue_purge(&dev->resp_q);
+	dev_kfree_skb(skb_resp);
 	arg->cb(arg->cb_context, NULL, err);
 	kfree(arg);
 	return 0;
@@ -1578,6 +1621,68 @@ error:
 	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_cmd;
+	struct pn533_data_exchange_arg *arg = dev->cmd_complete_arg;
+	struct pn533_frame *out_frame, *in_frame;
+	struct sk_buff *skb_resp;
+	int skb_resp_len;
+	int rc;
+
+	nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+	/* This is a zero payload size skb */
+	skb_cmd = alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN + PN533_FRAME_TAIL_SIZE,
+			    GFP_KERNEL);
+	if (skb_cmd == NULL)
+		goto error_cmd;
+
+	skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN);
+
+	rc = pn533_data_exchange_tx_frame(dev, skb_cmd);
+	if (rc)
+		goto error_frame;
+
+	skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
+			PN533_CMD_DATAEXCH_DATA_MAXLEN +
+			PN533_FRAME_TAIL_SIZE;
+	skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL);
+	if (!skb_resp) {
+		rc = -ENOMEM;
+		goto error_frame;
+	}
+
+	in_frame = (struct pn533_frame *) skb_resp->data;
+	out_frame = (struct pn533_frame *) skb_cmd->data;
+
+	arg->skb_resp = skb_resp;
+	arg->skb_out = skb_cmd;
+
+	rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
+					  skb_resp_len,
+					  pn533_data_exchange_complete,
+					  dev->cmd_complete_arg, GFP_KERNEL);
+	if (!rc)
+		return;
+
+	nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
+						" perform data_exchange", rc);
+
+	kfree_skb(skb_resp);
+
+error_frame:
+	kfree_skb(skb_cmd);
+
+error_cmd:
+	pn533_send_ack(dev, GFP_KERNEL);
+
+	kfree(arg);
+
+	up(&dev->cmd_lock);
+}
+
 static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
 								u8 cfgdata_len)
 {
@@ -1676,10 +1781,15 @@ static int pn533_probe(struct usb_interface *interface,
 			pn533_send_complete, dev);
 
 	INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete);
-	dev->wq = create_singlethread_workqueue("pn533");
+	INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
+	dev->wq = alloc_workqueue("pn533",
+				  WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
+				  1);
 	if (dev->wq == NULL)
 		goto error;
 
+	skb_queue_head_init(&dev->resp_q);
+
 	usb_set_intfdata(interface, dev);
 
 	pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION);
@@ -1756,6 +1866,8 @@ static void pn533_disconnect(struct usb_interface *interface)
 
 	destroy_workqueue(dev->wq);
 
+	skb_queue_purge(&dev->resp_q);
+
 	kfree(dev->in_frame);
 	usb_free_urb(dev->in_urb);
 	kfree(dev->out_frame);
Signature · 1 line
1.7.9.1
#17 lore raw

PATCH 17/18 NFC: Add MIUX to the local LLCP general bytes

In reply to Samuel Ortiz
---
 net/nfc/llcp/llcp.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)
net/nfc/llcp/llcp.c +10 -0
index 2cf01e6..31a05e5 100644
@@ -307,6 +307,8 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
 	u8 *gb_cur, *version_tlv, version, version_length;
 	u8 *lto_tlv, lto, lto_length;
 	u8 *wks_tlv, wks_length;
+	u8 *miux_tlv, miux_length;
+	__be16 miux;
 	u8 gb_len = 0;
 
 	version = LLCP_VERSION_11;
@@ -324,6 +326,11 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
 				     &wks_length);
 	gb_len += wks_length;
 
+	miux = cpu_to_be16(LLCP_MAX_MIUX);
+	miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
+				      &miux_length);
+	gb_len += miux_length;
+
 	gb_len += ARRAY_SIZE(llcp_magic);
 
 	if (gb_len > NFC_MAX_GT_LEN) {
@@ -345,6 +352,9 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
 	memcpy(gb_cur, wks_tlv, wks_length);
 	gb_cur += wks_length;
 
+	memcpy(gb_cur, miux_tlv, miux_length);
+	gb_cur += miux_length;
+
 	kfree(version_tlv);
 	kfree(lto_tlv);
 
Signature · 1 line
1.7.9.1
#18 lore raw

PATCH 18/18 NFC: Fix LLCP link timeout typo

In reply to Samuel Ortiz

We were sending the LTO TLV as a version TLV instead of the actual link timeout one.

---
 net/nfc/llcp/llcp.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
net/nfc/llcp/llcp.c +1 -1
index 31a05e5..92988aa 100644
@@ -318,7 +318,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
 
 	/* 1500 ms */
 	lto = 150;
-	lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &lto, 1, &lto_length);
+	lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &lto, 1, &lto_length);
 	gb_len += lto_length;
 
 	pr_debug("Local wks 0x%lx\n", local->local_wks);
Signature · 1 line
1.7.9.1