All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 00/14] Secure Element support for NCI based NFC controller, st21nfcb integration and EVT_TRANSACTION
@ 2015-01-27  0:28 Christophe Ricard
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
  0 siblings, 1 reply; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Samuel,

I have to introduce dynnamic conn_id management has described in the NCI
specification. Only RF static conn_id was supported so far. I have tried to 
do a minimun changeset in order to bring all the necessary NCI functions in
order to operate with a secure element with ST21NFCB NFC controller. Besides the
standard NCI commands, the HCI commands are sent following a proprietary
implementation.

Miscellaneous
-------------
I am also routing the HCI event TRANSACTION up to user space. It may help to understand
the necessity to reference every pipe with a tuple {gate, host}. This for example allows 
to keep the host information generating this particular event.

Open Questions:
---------------
Both integration may have little gap in their card emulation behavior.
For example:
- ST21NFCA will have card emulation active as soon as we activate a secure element.
- ST21NFCB will have card emulation active according to the selected polling mode.
When do you expect to have card emulation to be active ?

When an application is running, it might be necessary to signals to the under layers
that it has finished to operate with a particular secure element. The goal here is to release
a secure element without deactivating it. For example to allow automatic sleep mode.
With my current implementation, 
- With ST21NFCB, enable_se/disable_se may fit this request as both secure element are kept activated.
- With ST21NFCA, enable_se/disable_se does not fit this requirement as i am completely deactivating 
the secure element.
I am not sure if the SE activation should not be done in the discovery_se without any deactivation 
until the device remove function(?).

Any feedback are welcome.

Best Regards
Christophe

Christophe Ricard (14):
  NFC: nci: Add dynamic conn_id NCI concept.
  NFC: nci: Make nci_request available for nfc driver
  NFC: nci: Add NCI NFCEE constant
  NFC: nci: Add nci_nfcee_discover handler command/response/notification
  NFC: nci: Add nci_nfcee_mode_set handler command/response
  NFC: nci: Add nci_core_conn_create handler command/response
  NFC: nci: Add nci_core_conn_close handler command/response
  NFC: st21nfcb: Add HCI protocol over NCI protocol support
  NFC: st21nfcb: Adding support for secure element
  NFC: Forward NFC_EVT_TRANSACTION up to user space
  NFC: nci: Add support RF_NFCEE_ACTION_NTF
  NFC: nci: Change NCI state machine to LISTEN_ACTIVE and ignore
    parameters in rf_intf_activated_ntf
  NFC: st21nfcb: Add support for HCI event transaction
  NFC: st21nfca: Add support for HCI event transaction

 drivers/nfc/st21nfca/st21nfca_se.c       |  21 +
 drivers/nfc/st21nfcb/Makefile            |   2 +-
 drivers/nfc/st21nfcb/st21nfcb.c          |   8 +-
 drivers/nfc/st21nfcb/st21nfcb.h          |   2 +
 drivers/nfc/st21nfcb/st21nfcb_hci_core.c | 783 +++++++++++++++++++++++++++++++
 drivers/nfc/st21nfcb/st21nfcb_hci_core.h | 134 ++++++
 drivers/nfc/st21nfcb/st21nfcb_se.c       | 631 +++++++++++++++++++++++++
 drivers/nfc/st21nfcb/st21nfcb_se.h       |  59 +++
 include/net/nfc/nci.h                    |  95 ++++
 include/net/nfc/nci_core.h               |  40 +-
 include/net/nfc/nfc.h                    |  27 ++
 include/uapi/linux/nfc.h                 |   1 +
 net/nfc/core.c                           |  21 +
 net/nfc/nci/core.c                       | 112 ++++-
 net/nfc/nci/data.c                       |  57 ++-
 net/nfc/nci/ntf.c                        |  82 +++-
 net/nfc/nci/rsp.c                        |  94 +++-
 net/nfc/netlink.c                        |  47 ++
 net/nfc/nfc.h                            |   2 +
 19 files changed, 2172 insertions(+), 46 deletions(-)
 create mode 100644 drivers/nfc/st21nfcb/st21nfcb_hci_core.c
 create mode 100644 drivers/nfc/st21nfcb/st21nfcb_hci_core.h
 create mode 100644 drivers/nfc/st21nfcb/st21nfcb_se.c
 create mode 100644 drivers/nfc/st21nfcb/st21nfcb_se.h

-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [RFC 01/14] NFC: nci: Add dynamic conn_id NCI concept.
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 02/14] NFC: nci: Make nci_request available for nfc driver Christophe Ricard
                     ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The current NCI core only support static conn_id for RF communication.
For other NFC features such as Secure Element communication, we may need
additional conn_id.

In order to address each conn_id dynamically including statics one such as
NCI_STATIC_RF_CONN_ID, we introduce 2 tables referring to conn_id context
by conn_id or by ID (RF Discovery ID or NFCEE ID).

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nci_core.h | 29 +++++++++++++++++------
 net/nfc/nci/core.c         | 28 +++++++++++++++++------
 net/nfc/nci/data.c         | 57 ++++++++++++++++++++++++++++++++++------------
 net/nfc/nci/ntf.c          | 34 +++++++++++++++++++--------
 net/nfc/nci/rsp.c          | 16 ++++++++++++-
 5 files changed, 124 insertions(+), 40 deletions(-)

diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 9e51bb4..25c0a70 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -82,6 +82,22 @@ struct nci_ops {
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES		4
 #define NCI_MAX_DISCOVERED_TARGETS		10
+#define NCI_MAX_NUM_NFCEE   255
+#define NCI_MAX_CONN_ID		7
+
+struct nci_conn_info {
+	__u8	id; /* can be an RF Discovery ID or an NFCEE ID */
+	__u8	conn_id;
+	__u8	max_pkt_payload_len;
+
+	atomic_t credits_cnt;
+	__u8	 initial_num_credits;
+
+	data_exchange_cb_t	data_exchange_cb;
+	void *data_exchange_cb_context;
+
+	struct sk_buff *rx_skb;
+};
 
 /* NCI Core structures */
 struct nci_dev {
@@ -95,7 +111,8 @@ struct nci_dev {
 	unsigned long		flags;
 
 	atomic_t		cmd_cnt;
-	atomic_t		credits_cnt;
+	__u8			cur_conn_id;
+	struct nci_conn_info	*conn_info_by_conn_id[NCI_MAX_CONN_ID];
 
 	struct timer_list	cmd_timer;
 	struct timer_list	data_timer;
@@ -141,13 +158,11 @@ struct nci_dev {
 	__u8			manufact_id;
 	__u32			manufact_specific_info;
 
-	/* received during NCI_OP_RF_INTF_ACTIVATED_NTF */
-	__u8			max_data_pkt_payload_size;
-	__u8			initial_num_credits;
+	/* Save RF Discovery ID or NFCEE ID under conn_create */
+	__u8			cur_id;
+	struct nci_conn_info	*conn_info_by_id[NCI_MAX_NUM_NFCEE];
 
 	/* stored during nci_data_exchange */
-	data_exchange_cb_t	data_exchange_cb;
-	void			*data_exchange_cb_context;
 	struct sk_buff		*rx_data_reassembly;
 
 	/* stored during intf_activated_ntf */
@@ -200,7 +215,7 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb);
 int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload);
 int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb);
 void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
-				int err);
+				__u8 conn_id, int err);
 void nci_clear_target_list(struct nci_dev *ndev);
 
 /* ----- NCI requests ----- */
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 51feb5e..92b42e2 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -712,6 +712,12 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
 	int rc;
+	struct nci_conn_info    *conn_info;
+
+	conn_info =
+		(struct nci_conn_info *)ndev->conn_info_by_conn_id[NCI_STATIC_RF_CONN_ID];
+	if (!conn_info)
+		return -EPROTO;
 
 	pr_debug("target_idx %d, len %d\n", target->idx, skb->len);
 
@@ -724,8 +730,8 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
 		return -EBUSY;
 
 	/* store cb and context to be used on receiving data */
-	ndev->data_exchange_cb = cb;
-	ndev->data_exchange_cb_context = cb_context;
+	conn_info->data_exchange_cb = cb;
+	conn_info->data_exchange_cb_context = cb_context;
 
 	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
 	if (rc)
@@ -1027,20 +1033,26 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
 static void nci_tx_work(struct work_struct *work)
 {
 	struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work);
+	struct nci_conn_info    *conn_info;
 	struct sk_buff *skb;
 
-	pr_debug("credits_cnt %d\n", atomic_read(&ndev->credits_cnt));
+	conn_info =
+		(struct nci_conn_info *)ndev->conn_info_by_conn_id[ndev->cur_conn_id];
+	if (!conn_info)
+		return;
+
+	pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt));
 
 	/* Send queued tx data */
-	while (atomic_read(&ndev->credits_cnt)) {
+	while (atomic_read(&conn_info->credits_cnt)) {
 		skb = skb_dequeue(&ndev->tx_q);
 		if (!skb)
 			return;
 
 		/* Check if data flow control is used */
-		if (atomic_read(&ndev->credits_cnt) !=
+		if (atomic_read(&conn_info->credits_cnt) !=
 		    NCI_DATA_FLOW_CONTROL_NOT_USED)
-			atomic_dec(&ndev->credits_cnt);
+			atomic_dec(&conn_info->credits_cnt);
 
 		pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n",
 			 nci_pbf(skb->data),
@@ -1092,7 +1104,9 @@ static void nci_rx_work(struct work_struct *work)
 	if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) {
 		/* complete the data exchange transaction, if exists */
 		if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
-			nci_data_exchange_complete(ndev, NULL, -ETIMEDOUT);
+			nci_data_exchange_complete(ndev, NULL,
+						   ndev->cur_conn_id,
+						   -ETIMEDOUT);
 
 		clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
 	}
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index a2de2a8..5485293 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -36,10 +36,21 @@
 
 /* Complete data exchange transaction and forward skb to nfc core */
 void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
-				int err)
+				__u8 conn_id, int err)
 {
-	data_exchange_cb_t cb = ndev->data_exchange_cb;
-	void *cb_context = ndev->data_exchange_cb_context;
+	struct nci_conn_info    *conn_info;
+	data_exchange_cb_t cb;
+	void *cb_context;
+
+	conn_info =
+		(struct nci_conn_info *)ndev->conn_info_by_conn_id[conn_id];
+	if (!conn_info) {
+		kfree_skb(skb);
+		goto exit;
+	}
+
+	cb = conn_info->data_exchange_cb;
+	cb_context = conn_info->data_exchange_cb_context;
 
 	pr_debug("len %d, err %d\n", skb ? skb->len : 0, err);
 
@@ -48,9 +59,6 @@ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
 	clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
 
 	if (cb) {
-		ndev->data_exchange_cb = NULL;
-		ndev->data_exchange_cb_context = NULL;
-
 		/* forward skb to nfc core */
 		cb(cb_context, skb, err);
 	} else if (skb) {
@@ -60,6 +68,7 @@ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
 		kfree_skb(skb);
 	}
 
+exit:
 	clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
 }
 
@@ -85,6 +94,7 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev,
 static int nci_queue_tx_data_frags(struct nci_dev *ndev,
 				   __u8 conn_id,
 				   struct sk_buff *skb) {
+	struct nci_conn_info    *conn_info;
 	int total_len = skb->len;
 	unsigned char *data = skb->data;
 	unsigned long flags;
@@ -95,11 +105,17 @@ static int nci_queue_tx_data_frags(struct nci_dev *ndev,
 
 	pr_debug("conn_id 0x%x, total_len %d\n", conn_id, total_len);
 
+	conn_info = ndev->conn_info_by_conn_id[conn_id];
+	if (!conn_info) {
+		rc = -EPROTO;
+		goto free_exit;
+	}
+
 	__skb_queue_head_init(&frags_q);
 
 	while (total_len) {
 		frag_len =
-			min_t(int, total_len, ndev->max_data_pkt_payload_size);
+			min_t(int, total_len, conn_info->max_pkt_payload_len);
 
 		skb_frag = nci_skb_alloc(ndev,
 					 (NCI_DATA_HDR_SIZE + frag_len),
@@ -151,12 +167,19 @@ exit:
 /* Send NCI data */
 int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb)
 {
+	struct nci_conn_info    *conn_info;
 	int rc = 0;
 
 	pr_debug("conn_id 0x%x, plen %d\n", conn_id, skb->len);
 
+	conn_info = (struct nci_conn_info *)ndev->conn_info_by_conn_id[conn_id];
+	if (!conn_info) {
+		rc = -EPROTO;
+		goto free_exit;
+	}
+
 	/* check if the packet need to be fragmented */
-	if (skb->len <= ndev->max_data_pkt_payload_size) {
+	if (skb->len <= conn_info->max_pkt_payload_len) {
 		/* no need to fragment packet */
 		nci_push_data_hdr(ndev, conn_id, skb, NCI_PBF_LAST);
 
@@ -170,6 +193,7 @@ int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb)
 		}
 	}
 
+	ndev->cur_conn_id = conn_id;
 	queue_work(ndev->tx_wq, &ndev->tx_work);
 
 	goto exit;
@@ -185,7 +209,7 @@ exit:
 
 static void nci_add_rx_data_frag(struct nci_dev *ndev,
 				 struct sk_buff *skb,
-				 __u8 pbf, __u8 status)
+				 __u8 pbf, __u8 conn_id, __u8 status)
 {
 	int reassembly_len;
 	int err = 0;
@@ -229,16 +253,13 @@ static void nci_add_rx_data_frag(struct nci_dev *ndev,
 	}
 
 exit:
-	if (ndev->nfc_dev->rf_mode == NFC_RF_INITIATOR) {
-		nci_data_exchange_complete(ndev, skb, err);
-	} else if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
+	if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
 		/* Data received in Target mode, forward to nfc core */
 		err = nfc_tm_data_received(ndev->nfc_dev, skb);
 		if (err)
 			pr_err("unable to handle received data\n");
 	} else {
-		pr_err("rf mode unknown\n");
-		kfree_skb(skb);
+		nci_data_exchange_complete(ndev, skb, conn_id, err);
 	}
 }
 
@@ -247,6 +268,8 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u8 pbf = nci_pbf(skb->data);
 	__u8 status = 0;
+	__u8 conn_id = nci_conn_id(skb->data);
+	struct nci_conn_info    *conn_info;
 
 	pr_debug("len %d\n", skb->len);
 
@@ -255,6 +278,10 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		 nci_conn_id(skb->data),
 		 nci_plen(skb->data));
 
+	conn_info = ndev->conn_info_by_conn_id[conn_id];
+	if (!conn_info)
+		return;
+
 	/* strip the nci data header */
 	skb_pull(skb, NCI_DATA_HDR_SIZE);
 
@@ -268,5 +295,5 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		skb_trim(skb, (skb->len - 1));
 	}
 
-	nci_add_rx_data_frag(ndev, skb, pbf, nci_to_errno(status));
+	nci_add_rx_data_frag(ndev, skb, pbf, conn_id, nci_to_errno(status));
 }
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 22e453c..8c73462 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -43,6 +43,7 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev,
 					     struct sk_buff *skb)
 {
 	struct nci_core_conn_credit_ntf *ntf = (void *) skb->data;
+	struct nci_conn_info	*conn_info;
 	int i;
 
 	pr_debug("num_entries %d\n", ntf->num_entries);
@@ -59,11 +60,12 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev,
 			 i, ntf->conn_entries[i].conn_id,
 			 ntf->conn_entries[i].credits);
 
-		if (ntf->conn_entries[i].conn_id == NCI_STATIC_RF_CONN_ID) {
-			/* found static rf connection */
-			atomic_add(ntf->conn_entries[i].credits,
-				   &ndev->credits_cnt);
-		}
+		conn_info = ndev->conn_info_by_conn_id[ntf->conn_entries[i].conn_id];
+		if (!conn_info)
+			return;
+
+		atomic_add(ntf->conn_entries[i].credits,
+			   &conn_info->credits_cnt);
 	}
 
 	/* trigger the next tx */
@@ -96,7 +98,7 @@ static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev,
 
 	/* complete the data exchange transaction, if exists */
 	if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
-		nci_data_exchange_complete(ndev, NULL, -EIO);
+		nci_data_exchange_complete(ndev, NULL, ntf->conn_id, -EIO);
 }
 
 static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev,
@@ -513,6 +515,7 @@ static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
 static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
 					     struct sk_buff *skb)
 {
+	struct nci_conn_info    *conn_info;
 	struct nci_rf_intf_activated_ntf ntf;
 	__u8 *data = skb->data;
 	int err = NCI_STATUS_OK;
@@ -614,11 +617,16 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
 
 exit:
 	if (err == NCI_STATUS_OK) {
-		ndev->max_data_pkt_payload_size = ntf.max_data_pkt_payload_size;
-		ndev->initial_num_credits = ntf.initial_num_credits;
+		conn_info = ndev->conn_info_by_conn_id[NCI_STATIC_RF_CONN_ID];
+		if (!conn_info)
+			return;
+
+		conn_info->max_pkt_payload_len = ntf.max_data_pkt_payload_size;
+		conn_info->initial_num_credits = ntf.initial_num_credits;
 
 		/* set the available credits to initial value */
-		atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
+		atomic_set(&conn_info->credits_cnt,
+			   conn_info->initial_num_credits);
 
 		/* store general bytes to be reported later in dep_link_up */
 		if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
@@ -661,10 +669,15 @@ exit:
 static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
 					 struct sk_buff *skb)
 {
+	struct nci_conn_info    *conn_info;
 	struct nci_rf_deactivate_ntf *ntf = (void *) skb->data;
 
 	pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason);
 
+	conn_info = ndev->conn_info_by_conn_id[NCI_STATIC_RF_CONN_ID];
+	if (!conn_info)
+		return;
+
 	/* drop tx data queue */
 	skb_queue_purge(&ndev->tx_q);
 
@@ -676,7 +689,8 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
 
 	/* complete the data exchange transaction, if exists */
 	if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
-		nci_data_exchange_complete(ndev, NULL, -EIO);
+		nci_data_exchange_complete(ndev, NULL, NCI_STATIC_RF_CONN_ID,
+					   -EIO);
 
 	switch (ntf->type) {
 	case NCI_DEACTIVATE_TYPE_IDLE_MODE:
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 041de51..ff8721e 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -140,13 +140,27 @@ static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev,
 
 static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
+	struct nci_conn_info    *conn_info;
 	__u8 status = skb->data[0];
 
 	pr_debug("status 0x%x\n", status);
 
-	if (status == NCI_STATUS_OK)
+	if (status == NCI_STATUS_OK) {
 		atomic_set(&ndev->state, NCI_DISCOVERY);
+		if (!ndev->conn_info_by_conn_id[NCI_STATIC_RF_CONN_ID]) {
+			conn_info = devm_kzalloc(&ndev->nfc_dev->dev,
+						 sizeof(struct nci_conn_info),
+						 GFP_KERNEL);
+			if (!conn_info) {
+				status = NCI_STATUS_REJECTED;
+				goto exit;
+			}
+			ndev->conn_info_by_conn_id[NCI_STATIC_RF_CONN_ID] =
+								conn_info;
+		}
+	}
 
+exit:
 	nci_req_complete(ndev, status);
 }
 
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 02/14] NFC: nci: Make nci_request available for nfc driver
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
  2015-01-27  0:28   ` [RFC 01/14] NFC: nci: Add dynamic conn_id NCI concept Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 03/14] NFC: nci: Add NCI NFCEE constant Christophe Ricard
                     ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

NFC drivers may need to send raw data over NCI. Make this function
available in the NCI core framework.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nci_core.h | 3 +++
 net/nfc/nci/core.c         | 8 ++++----
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 25c0a70..395f804 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -217,6 +217,9 @@ int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb);
 void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
 				__u8 conn_id, int err);
 void nci_clear_target_list(struct nci_dev *ndev);
+int nci_request(struct nci_dev *ndev,
+			void (*req)(struct nci_dev *ndev, unsigned long opt),
+			unsigned long opt, __u32 timeout);
 
 /* ----- NCI requests ----- */
 #define NCI_REQ_DONE		0
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 92b42e2..b3ec63e 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -109,10 +109,9 @@ static int __nci_request(struct nci_dev *ndev,
 	return rc;
 }
 
-static inline int nci_request(struct nci_dev *ndev,
-			      void (*req)(struct nci_dev *ndev,
-					  unsigned long opt),
-			      unsigned long opt, __u32 timeout)
+inline int nci_request(struct nci_dev *ndev,
+		void (*req)(struct nci_dev *ndev, unsigned long opt),
+		unsigned long opt, __u32 timeout)
 {
 	int rc;
 
@@ -126,6 +125,7 @@ static inline int nci_request(struct nci_dev *ndev,
 
 	return rc;
 }
+EXPORT_SYMBOL_GPL(nci_request);
 
 static void nci_reset_req(struct nci_dev *ndev, unsigned long opt)
 {
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 03/14] NFC: nci: Add NCI NFCEE constant
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
  2015-01-27  0:28   ` [RFC 01/14] NFC: nci: Add dynamic conn_id NCI concept Christophe Ricard
  2015-01-27  0:28   ` [RFC 02/14] NFC: nci: Make nci_request available for nfc driver Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 04/14] NFC: nci: Add nci_nfcee_discover handler command/response/notification Christophe Ricard
                     ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Add NFCEE NCI constant for:
- NFCEE Interface/Protocols
- Destination type
- Destination-specific parameters type
- NFCEE Discovery Action

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nci.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index e7257a4..6d99e8f 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -62,6 +62,25 @@
 #define NCI_STATUS_NFCEE_PROTOCOL_ERROR				0xc2
 #define NCI_STATUS_NFCEE_TIMEOUT_ERROR				0xc3
 
+/* NFCEE Interface/Protocols */
+#define NCI_NFCEE_INTERFACE_APDU           0x00
+#define NCI_NFCEE_INTERFACE_HCI_ACCESS     0x01
+#define NCI_NFCEE_INTERFACE_TYPE3_CMD_SET  0x02
+#define NCI_NFCEE_INTERFACE_TRANSPARENT        0x03
+
+/* Destination type */
+#define NCI_DESTINATION_NFCC_LOOPBACK      0x01
+#define NCI_DESTINATION_REMOTE_NFC_ENDPOINT    0x02
+#define NCI_DESTINATION_NFCEE              0x03
+
+/* Destination-specific parameters type */
+#define NCI_DESTINATION_SPECIFIC_PARAM_RF_TYPE     0x00
+#define NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE  0x01
+
+/* NFCEE Discovery Action */
+#define NCI_NFCEE_DISCOVERY_ACTION_DISABLE			0x00
+#define NCI_NFCEE_DISCOVERY_ACTION_ENABLE			0x01
+
 /* NCI RF Technology and Mode */
 #define NCI_NFC_A_PASSIVE_POLL_MODE				0x00
 #define NCI_NFC_B_PASSIVE_POLL_MODE				0x01
@@ -260,6 +279,11 @@ struct nci_rf_deactivate_cmd {
 	__u8	type;
 } __packed;
 
+#define NCI_OP_NFCEE_DISCOVER_CMD nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x00)
+struct nci_nfcee_discover_cmd {
+	__u8	discovery_action;
+} __packed;
+
 /* ----------------------- */
 /* ---- NCI Responses ---- */
 /* ----------------------- */
@@ -303,6 +327,12 @@ struct nci_core_set_config_rsp {
 
 #define NCI_OP_RF_DEACTIVATE_RSP	nci_opcode_pack(NCI_GID_RF_MGMT, 0x06)
 
+#define NCI_OP_NFCEE_DISCOVER_RSP nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x00)
+struct nci_nfcee_discover_rsp {
+	__u8	status;
+	__u8	num_nfcee;
+} __packed;
+
 /* --------------------------- */
 /* ---- NCI Notifications ---- */
 /* --------------------------- */
@@ -430,4 +460,22 @@ struct nci_rf_deactivate_ntf {
 	__u8	reason;
 } __packed;
 
+#define NCI_OP_NFCEE_DISCOVER_NTF nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x00)
+struct nci_nfcee_supported_protocol {
+	__u8	num_protocol;
+	__u8	supported_protocol[0];
+} __packed;
+
+struct nci_nfcee_information_tlv {
+	__u8	num_tlv;
+	__u8	information_tlv[0];
+} __packed;
+
+struct nci_nfcee_discover_ntf {
+	__u8	nfcee_id;
+	__u8	nfcee_status;
+	struct nci_nfcee_supported_protocol supported_protocols;
+	struct nci_nfcee_information_tlv	information_tlv;
+} __packed;
+
 #endif /* __NCI_H */
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 04/14] NFC: nci: Add nci_nfcee_discover handler command/response/notification
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (2 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 03/14] NFC: nci: Add NCI NFCEE constant Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 05/14] NFC: nci: Add nci_nfcee_mode_set handler command/response Christophe Ricard
                     ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Add nci_nfcee_discover handler according to NFC Forum NCI specification.
It is necessary for example for secure element handling.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nci_core.h |  4 ++++
 net/nfc/nci/core.c         | 17 +++++++++++++++++
 net/nfc/nci/ntf.c          | 29 +++++++++++++++++++++++++++++
 net/nfc/nci/rsp.c          | 21 +++++++++++++++++++++
 4 files changed, 71 insertions(+)

diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 395f804..3df7cd6 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -99,6 +99,8 @@ struct nci_conn_info {
 	struct sk_buff *rx_skb;
 };
 
+#define NCI_INVALID_CONN_ID 0x80
+
 /* NCI Core structures */
 struct nci_dev {
 	struct nfc_dev		*nfc_dev;
@@ -181,6 +183,8 @@ void nci_unregister_device(struct nci_dev *ndev);
 int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
 int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val);
 
+int nci_nfcee_discover(struct nci_dev *ndev, u8 action);
+
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
 					    unsigned int len,
 					    gfp_t how)
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index b3ec63e..bd968d5 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -456,6 +456,23 @@ int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val)
 }
 EXPORT_SYMBOL(nci_set_config);
 
+static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct nci_nfcee_discover_cmd cmd;
+	__u8 action = opt;
+
+	cmd.discovery_action = action;
+
+	nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd);
+}
+
+int nci_nfcee_discover(struct nci_dev *ndev, u8 action)
+{
+	return nci_request(ndev, nci_nfcee_discover_req, action,
+				msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_nfcee_discover);
+
 static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 8c73462..5af0564 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -710,6 +710,32 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
 	nci_req_complete(ndev, NCI_STATUS_OK);
 }
 
+static void nci_nfcee_discover_ntf_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	u8 status = NCI_STATUS_OK;
+	struct nci_conn_info    *conn_info;
+	struct nci_nfcee_discover_ntf   *nfcee_ntf =
+				(struct nci_nfcee_discover_ntf *)skb->data;
+
+	pr_debug("\n");
+
+	conn_info = devm_kzalloc(&ndev->nfc_dev->dev,
+				 sizeof(struct nci_conn_info), GFP_KERNEL);
+	if (!conn_info) {
+		status = NCI_STATUS_REJECTED;
+		goto exit;
+	}
+
+	conn_info->id = nfcee_ntf->nfcee_id;
+	conn_info->conn_id = NCI_INVALID_CONN_ID;
+
+	ndev->conn_info_by_id[conn_info->id] = conn_info;
+
+exit:
+	nci_req_complete(ndev, status);
+}
+
 void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u16 ntf_opcode = nci_opcode(skb->data);
@@ -748,6 +774,9 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		nci_rf_deactivate_ntf_packet(ndev, skb);
 		break;
 
+	case NCI_OP_NFCEE_DISCOVER_NTF:
+		nci_nfcee_discover_ntf_packet(ndev, skb);
+		break;
 	default:
 		pr_err("unknown ntf opcode 0x%x\n", ntf_opcode);
 		break;
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index ff8721e..a93133b 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -192,6 +192,23 @@ static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev,
 	}
 }
 
+static void nci_nfcee_discover_rsp_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	struct nci_nfcee_discover_rsp *discover_rsp;
+
+	if (skb->len != 2) {
+		nci_req_complete(ndev, NCI_STATUS_NFCEE_PROTOCOL_ERROR);
+		return;
+	}
+
+	discover_rsp = (struct nci_nfcee_discover_rsp *)skb->data;
+
+	if (discover_rsp->status != NCI_STATUS_OK ||
+	    discover_rsp->num_nfcee == 0)
+		nci_req_complete(ndev, discover_rsp->status);
+}
+
 void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u16 rsp_opcode = nci_opcode(skb->data);
@@ -237,6 +254,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		nci_rf_deactivate_rsp_packet(ndev, skb);
 		break;
 
+	case NCI_OP_NFCEE_DISCOVER_RSP:
+		nci_nfcee_discover_rsp_packet(ndev, skb);
+		break;
+
 	default:
 		pr_err("unknown rsp opcode 0x%x\n", rsp_opcode);
 		break;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 05/14] NFC: nci: Add nci_nfcee_mode_set handler command/response
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (3 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 04/14] NFC: nci: Add nci_nfcee_discover handler command/response/notification Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 06/14] NFC: nci: Add nci_core_conn_create " Christophe Ricard
                     ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Add nci_nfcee_mode_set handler according to NFC Forum NCI specification.
It is necessary for example for secure element handling.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nci.h      |  9 +++++++++
 include/net/nfc/nci_core.h |  1 +
 net/nfc/nci/core.c         | 21 +++++++++++++++++++++
 net/nfc/nci/rsp.c          | 13 +++++++++++++
 4 files changed, 44 insertions(+)

diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index 6d99e8f..230f227 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -284,6 +284,14 @@ struct nci_nfcee_discover_cmd {
 	__u8	discovery_action;
 } __packed;
 
+#define NCI_OP_NFCEE_MODE_SET_CMD nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x01)
+#define NCI_NFCEE_DISABLE	0x00
+#define NCI_NFCEE_ENABLE	0x01
+struct nci_nfcee_mode_set_cmd {
+	__u8	nfcee_id;
+	__u8	nfcee_mode;
+} __packed;
+
 /* ----------------------- */
 /* ---- NCI Responses ---- */
 /* ----------------------- */
@@ -333,6 +341,7 @@ struct nci_nfcee_discover_rsp {
 	__u8	num_nfcee;
 } __packed;
 
+#define NCI_OP_NFCEE_MODE_SET_RSP nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x01)
 /* --------------------------- */
 /* ---- NCI Notifications ---- */
 /* --------------------------- */
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 3df7cd6..46bdf86 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -184,6 +184,7 @@ int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
 int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val);
 
 int nci_nfcee_discover(struct nci_dev *ndev, u8 action);
+int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode);
 
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
 					    unsigned int len,
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index bd968d5..0de7d90 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -473,6 +473,27 @@ int nci_nfcee_discover(struct nci_dev *ndev, u8 action)
 }
 EXPORT_SYMBOL(nci_nfcee_discover);
 
+static void nci_nfcee_mode_set_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct nci_nfcee_mode_set_cmd *cmd =
+					(struct nci_nfcee_mode_set_cmd *)opt;
+
+	nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD,
+		     sizeof(struct nci_nfcee_mode_set_cmd), cmd);
+}
+
+int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode)
+{
+	struct nci_nfcee_mode_set_cmd cmd;
+
+	cmd.nfcee_id = nfcee_id;
+	cmd.nfcee_mode = nfcee_mode;
+
+	return nci_request(ndev, nci_nfcee_mode_set_req, (unsigned long)&cmd,
+			   msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_nfcee_mode_set);
+
 static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index a93133b..cb358cd 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -209,6 +209,15 @@ static void nci_nfcee_discover_rsp_packet(struct nci_dev *ndev,
 		nci_req_complete(ndev, discover_rsp->status);
 }
 
+static void nci_nfcee_mode_set_rsp_packet(struct nci_dev *ndev,
+					  struct sk_buff *skb)
+{
+	__u8 status = skb->data[0];
+
+	pr_debug("status 0x%x\n", status);
+	nci_req_complete(ndev, status);
+}
+
 void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u16 rsp_opcode = nci_opcode(skb->data);
@@ -258,6 +267,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		nci_nfcee_discover_rsp_packet(ndev, skb);
 		break;
 
+	case NCI_OP_NFCEE_MODE_SET_RSP:
+		nci_nfcee_mode_set_rsp_packet(ndev, skb);
+		break;
+
 	default:
 		pr_err("unknown rsp opcode 0x%x\n", rsp_opcode);
 		break;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 06/14] NFC: nci: Add nci_core_conn_create handler command/response
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (4 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 05/14] NFC: nci: Add nci_nfcee_mode_set handler command/response Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 07/14] NFC: nci: Add nci_core_conn_close " Christophe Ricard
                     ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Add nci_core_conn_create handler according to NFC Forum NCI specification.
It is necessary for example for secure element handling.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nci.h      | 25 +++++++++++++++++++++++++
 include/net/nfc/nci_core.h |  2 ++
 net/nfc/nci/core.c         | 24 ++++++++++++++++++++++++
 net/nfc/nci/rsp.c          | 31 +++++++++++++++++++++++++++++++
 4 files changed, 82 insertions(+)

diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index 230f227..b8ff50c 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -243,6 +243,23 @@ struct nci_core_set_config_cmd {
 	struct	set_config_param param; /* support 1 param per cmd is enough */
 } __packed;
 
+#define NCI_OP_CORE_CONN_CREATE_CMD	nci_opcode_pack(NCI_GID_CORE, 0x04)
+struct dest_spec_params {
+	__u8	id;
+	__u8	protocol;
+} __packed;
+
+struct core_conn_create_dest_spec_params {
+	__u8	type;
+	__u8	length;
+	struct dest_spec_params value;
+} __packed;
+
+struct nci_core_conn_create_cmd {
+	__u8	destination_type;
+	__u8	number_destination_params;
+	struct core_conn_create_dest_spec_params params;
+} __packed;
 #define NCI_OP_RF_DISCOVER_MAP_CMD	nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 struct disc_map_config {
 	__u8	rf_protocol;
@@ -327,6 +344,14 @@ struct nci_core_set_config_rsp {
 	__u8	params_id[0];	/* variable size array */
 } __packed;
 
+#define NCI_OP_CORE_CONN_CREATE_RSP	nci_opcode_pack(NCI_GID_CORE, 0x04)
+struct nci_core_conn_create_rsp {
+	__u8	status;
+	__u8	max_ctrl_pkt_payload_len;
+	__u8    credits;
+	__u8	conn_id;
+} __packed;
+
 #define NCI_OP_RF_DISCOVER_MAP_RSP	nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 
 #define NCI_OP_RF_DISCOVER_RSP		nci_opcode_pack(NCI_GID_RF_MGMT, 0x03)
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 46bdf86..21da170 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -185,6 +185,8 @@ int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val);
 
 int nci_nfcee_discover(struct nci_dev *ndev, u8 action);
 int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode);
+int nci_core_conn_create(struct nci_dev *ndev,
+			 struct core_conn_create_dest_spec_params *params);
 
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
 					    unsigned int len,
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 0de7d90..cbe6ea3 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -494,6 +494,30 @@ int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode)
 }
 EXPORT_SYMBOL(nci_nfcee_mode_set);
 
+static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct nci_core_conn_create_cmd cmd;
+	struct core_conn_create_dest_spec_params *params =
+				(struct core_conn_create_dest_spec_params *)opt;
+
+	cmd.destination_type = NCI_DESTINATION_NFCEE;
+	cmd.number_destination_params = 1;
+	memcpy(&cmd.params.type, params,
+	       sizeof(struct core_conn_create_dest_spec_params));
+	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD,
+		     sizeof(struct nci_core_conn_create_cmd), &cmd);
+}
+
+int nci_core_conn_create(struct nci_dev *ndev,
+			 struct core_conn_create_dest_spec_params *params)
+{
+	ndev->cur_id = params->value.id;
+	return nci_request(ndev, nci_core_conn_create_req,
+			(unsigned long)params,
+			msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_core_conn_create);
+
 static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index cb358cd..5b3c66e 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -218,6 +218,33 @@ static void nci_nfcee_mode_set_rsp_packet(struct nci_dev *ndev,
 	nci_req_complete(ndev, status);
 }
 
+static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev,
+					    struct sk_buff *skb)
+{
+	__u8 status = skb->data[0];
+	struct nci_conn_info *conn_info;
+	struct nci_core_conn_create_rsp *rsp;
+
+	pr_debug("status 0x%x\n", status);
+
+	if (status == NCI_STATUS_OK) {
+		rsp = (struct nci_core_conn_create_rsp *)skb->data;
+		conn_info = ndev->conn_info_by_id[ndev->cur_id];
+		if (!conn_info) {
+			status = NCI_STATUS_REJECTED;
+			goto exit;
+		}
+
+		conn_info->conn_id = rsp->conn_id;
+		ndev->conn_info_by_conn_id[conn_info->conn_id] = conn_info;
+		conn_info->max_pkt_payload_len = rsp->max_ctrl_pkt_payload_len;
+		atomic_set(&conn_info->credits_cnt, rsp->credits);
+	}
+
+exit:
+	nci_req_complete(ndev, status);
+}
+
 void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u16 rsp_opcode = nci_opcode(skb->data);
@@ -247,6 +274,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		nci_core_set_config_rsp_packet(ndev, skb);
 		break;
 
+	case NCI_OP_CORE_CONN_CREATE_RSP:
+		nci_core_conn_create_rsp_packet(ndev, skb);
+		break;
+
 	case NCI_OP_RF_DISCOVER_MAP_RSP:
 		nci_rf_disc_map_rsp_packet(ndev, skb);
 		break;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 07/14] NFC: nci: Add nci_core_conn_close handler command/response
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (5 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 06/14] NFC: nci: Add nci_core_conn_create " Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 08/14] NFC: st21nfcb: Add HCI protocol over NCI protocol support Christophe Ricard
                     ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Add nci_core_conn_close handler according to NFC Forum NCI specification.
It is necessary for example for secure element handling.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nci.h      |  5 +++++
 include/net/nfc/nci_core.h |  1 +
 net/nfc/nci/core.c         | 14 ++++++++++++++
 net/nfc/nci/rsp.c          | 13 +++++++++++++
 4 files changed, 33 insertions(+)

diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index b8ff50c..deac78b 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -260,6 +260,9 @@ struct nci_core_conn_create_cmd {
 	__u8	number_destination_params;
 	struct core_conn_create_dest_spec_params params;
 } __packed;
+
+#define NCI_OP_CORE_CONN_CLOSE_CMD	nci_opcode_pack(NCI_GID_CORE, 0x05)
+
 #define NCI_OP_RF_DISCOVER_MAP_CMD	nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 struct disc_map_config {
 	__u8	rf_protocol;
@@ -352,6 +355,8 @@ struct nci_core_conn_create_rsp {
 	__u8	conn_id;
 } __packed;
 
+#define NCI_OP_CORE_CONN_CLOSE_RSP	nci_opcode_pack(NCI_GID_CORE, 0x05)
+
 #define NCI_OP_RF_DISCOVER_MAP_RSP	nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 
 #define NCI_OP_RF_DISCOVER_RSP		nci_opcode_pack(NCI_GID_RF_MGMT, 0x03)
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 21da170..4d1a12e 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -187,6 +187,7 @@ int nci_nfcee_discover(struct nci_dev *ndev, u8 action);
 int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode);
 int nci_core_conn_create(struct nci_dev *ndev,
 			 struct core_conn_create_dest_spec_params *params);
+int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id);
 
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
 					    unsigned int len,
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index cbe6ea3..ed42a71 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -518,6 +518,20 @@ int nci_core_conn_create(struct nci_dev *ndev,
 }
 EXPORT_SYMBOL(nci_core_conn_create);
 
+static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt)
+{
+	__u8 conn_id = opt;
+
+	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id);
+}
+
+int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id)
+{
+	return nci_request(ndev, nci_core_conn_close_req, conn_id,
+				msecs_to_jiffies(NCI_CMD_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_core_conn_close);
+
 static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 5b3c66e..662f021 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -245,6 +245,15 @@ exit:
 	nci_req_complete(ndev, status);
 }
 
+static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev,
+					   struct sk_buff *skb)
+{
+	__u8 status = skb->data[0];
+
+	pr_debug("status 0x%x\n", status);
+	nci_req_complete(ndev, status);
+}
+
 void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u16 rsp_opcode = nci_opcode(skb->data);
@@ -278,6 +287,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
 		nci_core_conn_create_rsp_packet(ndev, skb);
 		break;
 
+	case NCI_OP_CORE_CONN_CLOSE_RSP:
+		nci_core_conn_close_rsp_packet(ndev, skb);
+		break;
+
 	case NCI_OP_RF_DISCOVER_MAP_RSP:
 		nci_rf_disc_map_rsp_packet(ndev, skb);
 		break;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 08/14] NFC: st21nfcb: Add HCI protocol over NCI protocol support
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (6 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 07/14] NFC: nci: Add nci_core_conn_close " Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 09/14] NFC: st21nfcb: Adding support for secure element Christophe Ricard
                     ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Some st21nfcb features are only available through HCI commands. Those
HCI commands can be address over NCI by sending data using a dynamic
conn_id. This is useful for example for Secure Element communication.

The HCI core brings the minimal HCI functions required to communicate with
a secure element.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 drivers/nfc/st21nfcb/Makefile            |   2 +-
 drivers/nfc/st21nfcb/st21nfcb_hci_core.c | 783 +++++++++++++++++++++++++++++++
 drivers/nfc/st21nfcb/st21nfcb_hci_core.h | 134 ++++++
 3 files changed, 918 insertions(+), 1 deletion(-)
 create mode 100644 drivers/nfc/st21nfcb/st21nfcb_hci_core.c
 create mode 100644 drivers/nfc/st21nfcb/st21nfcb_hci_core.h

diff --git a/drivers/nfc/st21nfcb/Makefile b/drivers/nfc/st21nfcb/Makefile
index f4d835d..974c2e9 100644
--- a/drivers/nfc/st21nfcb/Makefile
+++ b/drivers/nfc/st21nfcb/Makefile
@@ -2,7 +2,7 @@
 # Makefile for ST21NFCB NCI based NFC driver
 #
 
-st21nfcb_nci-objs = ndlc.o st21nfcb.o
+st21nfcb_nci-objs = ndlc.o st21nfcb.o st21nfcb_hci_core.o
 obj-$(CONFIG_NFC_ST21NFCB)     += st21nfcb_nci.o
 
 st21nfcb_i2c-objs = i2c.o
diff --git a/drivers/nfc/st21nfcb/st21nfcb_hci_core.c b/drivers/nfc/st21nfcb/st21nfcb_hci_core.c
new file mode 100644
index 0000000..f5a8f2e
--- /dev/null
+++ b/drivers/nfc/st21nfcb/st21nfcb_hci_core.c
@@ -0,0 +1,783 @@
+/*
+ * NCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+
+#include "st21nfcb_hci_core.h"
+
+struct nci_data {
+	u8		conn_id;
+	u8		pipe;
+	u8		cmd;
+	const u8	*data;
+	u32		data_len;
+} __packed;
+
+struct st21nfcb_hci_create_pipe_params {
+	u8 src_gate;
+	u8 dest_host;
+	u8 dest_gate;
+} __packed;
+
+struct st21nfcb_hci_create_pipe_resp {
+	u8 src_host;
+	u8 src_gate;
+	u8 dest_host;
+	u8 dest_gate;
+	u8 pipe;
+} __packed;
+
+struct st21nfcb_hci_delete_pipe_noti {
+	u8 pipe;
+} __packed;
+
+struct st21nfcb_hci_all_pipe_cleared_noti {
+	u8 host;
+} __packed;
+
+struct st21nfcb_hcp_message {
+	u8 header;	/* type -cmd,evt,rsp- + instruction */
+	u8 data[];
+} __packed;
+
+struct st21nfcb_hcp_packet {
+	u8 header;	/* cbit+pipe */
+	struct st21nfcb_hcp_message message;
+} __packed;
+
+
+#define ST21NFCB_HCI_ANY_SET_PARAMETER	0x01
+#define ST21NFCB_HCI_ANY_GET_PARAMETER	0x02
+#define ST21NFCB_HCI_ANY_CLOSE_PIPE	0x04
+
+#define ST21NFCB_HFP_NO_CHAINING	0x80
+
+#define NCI_NFCEE_ID_HCI		0x80
+
+#define ST21NFCB_EVT_HOT_PLUG		0x03
+
+#define ST21NFCB_HCI_ADMIN_PARAM_SESSION_IDENTITY       0x01
+
+/* HCP headers */
+#define ST21NFCB_HCI_HCP_PACKET_HEADER_LEN	1
+#define ST21NFCB_HCI_HCP_MESSAGE_HEADER_LEN	1
+#define ST21NFCB_HCI_HCP_HEADER_LEN		2
+
+/* HCP types */
+#define ST21NFCB_HCI_HCP_COMMAND	0x00
+#define ST21NFCB_HCI_HCP_EVENT		0x01
+#define ST21NFCB_HCI_HCP_RESPONSE	0x02
+
+#define ST21NFCB_HCI_ADM_NOTIFY_PIPE_CREATED     0x12
+#define ST21NFCB_HCI_ADM_NOTIFY_PIPE_DELETED     0x13
+#define ST21NFCB_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED 0x15
+
+#define ST21NFCB_HCI_FRAGMENT		0x7f
+#define ST21NFCB_HCP_HEADER(type, instr) ((((type) & 0x03) << 6) |\
+					   ((instr) & 0x3f))
+
+#define ST21NFCB_HCP_MSG_GET_TYPE(header) ((header & 0xc0) >> 6)
+#define ST21NFCB_HCP_MSG_GET_CMD(header)  (header & 0x3f)
+#define ST21NFCB_HCP_MSG_GET_PIPE(header) (header & 0x7f)
+
+#define ST21NFCB_NUM_DEVICES		256
+
+static DECLARE_BITMAP(dev_mask, ST21NFCB_NUM_DEVICES);
+
+static void st21nfcb_hci_data_received_cb(void *context,
+					  struct sk_buff *skb, int err);
+
+static void st21nfcb_hci_reset_pipes(struct st21nfcb_hci_dev *hdev)
+{
+	int i;
+
+	for (i = 0; i < ST21NFCB_HCI_MAX_PIPES; i++) {
+		hdev->pipes[i].gate = ST21NFCB_HCI_INVALID_GATE;
+		hdev->pipes[i].host = ST21NFCB_HCI_INVALID_HOST;
+	}
+	memset(hdev->gate2pipe, ST21NFCB_HCI_INVALID_PIPE,
+	       sizeof(hdev->gate2pipe));
+}
+
+static void st21nfcb_hci_reset_pipes_per_host(struct st21nfcb_hci_dev *hdev, u8 host)
+{
+	int i;
+
+	for (i = 0; i < ST21NFCB_HCI_MAX_PIPES; i++) {
+		if (hdev->pipes[i].host == host) {
+			hdev->pipes[i].gate = ST21NFCB_HCI_INVALID_GATE;
+			hdev->pipes[i].host = ST21NFCB_HCI_INVALID_HOST;
+		}
+	}
+}
+
+/* Fragment HCI data over NCI packet.
+ * NFC Forum NCI 10.2.2 Data Exchange:
+ * The payload of the Data Packets sent on the Logical Connection SHALL be
+ * valid HCP packets, as defined within [ETSI_102622]. Each Data Packet SHALL
+ * contain a single HCP packet. NCI Segmentation and Reassembly SHALL NOT be
+ * applied to Data Messages in either direction. The HCI fragmentation mechanism
+ * is used if required.
+ */
+static int st21nfcb_hci_send_data(struct nci_dev *ndev, u8 conn_id,
+				  u8 pipe, const u8 data_type, const u8 *data,
+				  size_t data_len)
+{
+	struct nci_conn_info    *conn_info;
+	struct sk_buff *skb;
+	int len, i, r;
+	u8 cb = pipe;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		return -EPROTO;
+
+	skb = nci_skb_alloc(ndev, 2 + conn_info->max_pkt_payload_len +
+			NCI_DATA_HDR_SIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_reserve(skb, 2 + NCI_DATA_HDR_SIZE);
+	*skb_push(skb, 1) = data_type;
+
+	i = 0;
+	len = conn_info->max_pkt_payload_len;
+
+	do {
+		/* If last packet add ST21NFCB_HFP_NO_CHAINING */
+		if (i + conn_info->max_pkt_payload_len -
+		    (skb->len + 1) >= data_len) {
+			cb |= ST21NFCB_HFP_NO_CHAINING;
+			len = data_len - i;
+		} else {
+			len = conn_info->max_pkt_payload_len - skb->len - 1;
+		}
+
+		*skb_push(skb, 1) = cb;
+
+		if (len > 0)
+			memcpy(skb_put(skb, len), data + i, len);
+
+		r = nci_send_data(ndev, conn_info->conn_id, skb);
+		if (r < 0)
+			return r;
+
+		i += len;
+		if (i < data_len) {
+			skb_trim(skb, 0);
+			skb_pull(skb, len);
+		}
+	} while (i < data_len);
+
+	return i;
+}
+
+static void st21nfcb_hci_send_data_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct nci_data *data = (struct nci_data *) opt;
+
+	st21nfcb_hci_send_data(ndev, data->conn_id, data->pipe, data->cmd,
+			       data->data, data->data_len);
+}
+
+int st21nfcb_hci_send_event(struct st21nfcb_hci_dev *hdev, u8 gate, u8 event,
+				const u8 *param, size_t param_len)
+{
+	struct nci_dev *ndev = st21nfcb_hci_get_nci(hdev);
+	struct nci_conn_info    *conn_info;
+	u8 pipe = hdev->gate2pipe[gate];
+
+	if (pipe == ST21NFCB_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		return -EPROTO;
+
+	return st21nfcb_hci_send_data(ndev, conn_info->conn_id, pipe,
+			ST21NFCB_HCP_HEADER(ST21NFCB_HCI_HCP_EVENT, event),
+			param, param_len);
+}
+EXPORT_SYMBOL(st21nfcb_hci_send_event);
+
+int st21nfcb_hci_send_cmd(struct st21nfcb_hci_dev *hdev, u8 gate,
+			u8 cmd, const u8 *param, size_t param_len,
+			struct sk_buff **skb)
+{
+	struct nci_dev *ndev = st21nfcb_hci_get_nci(hdev);
+	struct nci_conn_info    *conn_info;
+	struct nci_data data;
+	int r;
+	u8 pipe = hdev->gate2pipe[gate];
+
+	if (pipe == ST21NFCB_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		return -EPROTO;
+
+	data.conn_id = conn_info->conn_id;
+	data.pipe = pipe;
+	data.cmd = ST21NFCB_HCP_HEADER(ST21NFCB_HCI_HCP_COMMAND, cmd);
+	data.data = param;
+	data.data_len = param_len;
+
+	r = nci_request(ndev, st21nfcb_hci_send_data_req, (unsigned long)&data,
+			 msecs_to_jiffies(NCI_DATA_TIMEOUT));
+
+	if (r == NCI_STATUS_OK)
+		*skb = conn_info->rx_skb;
+
+	return r;
+}
+EXPORT_SYMBOL(st21nfcb_hci_send_cmd);
+
+int st21nfcb_hci_send_response(struct st21nfcb_hci_dev *hdev, u8 pipe,
+				u8 cmd, const u8 *param, size_t param_len)
+{
+	struct nci_dev *ndev = st21nfcb_hci_get_nci(hdev);
+	struct nci_conn_info    *conn_info;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		return -EPROTO;
+
+	return st21nfcb_hci_send_data(ndev, conn_info->conn_id, pipe,
+			ST21NFCB_HCP_HEADER(ST21NFCB_HCI_HCP_RESPONSE, cmd),
+			param, param_len);
+}
+EXPORT_SYMBOL(st21nfcb_hci_send_response);
+
+static void st21nfcb_hci_event_received(struct st21nfcb_hci_dev *hdev, u8 pipe,
+					u8 event, struct sk_buff *skb)
+{
+	if (hdev->ops->event_received)
+		hdev->ops->event_received(hdev, pipe, event, skb);
+}
+
+static void st21nfcb_hci_cmd_received(struct st21nfcb_hci_dev *hdev, u8 pipe,
+				      u8 cmd, struct sk_buff *skb)
+{
+	u8 gate = hdev->pipes[pipe].gate;
+	u8 status = ST21NFCB_HCI_ANY_OK;
+	struct st21nfcb_hci_create_pipe_resp *create_info;
+	struct st21nfcb_hci_delete_pipe_noti *delete_info;
+	struct st21nfcb_hci_all_pipe_cleared_noti *cleared_info;
+
+	pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
+
+	switch (cmd) {
+	case ST21NFCB_HCI_ADM_NOTIFY_PIPE_CREATED:
+		if (skb->len != 5) {
+			status = ST21NFCB_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		create_info = (struct st21nfcb_hci_create_pipe_resp *)skb->data;
+
+		/*
+		 * Save the new created pipe and bind with local gate,
+		 * the description for skb->data[3] is destination gate id
+		 * but since we received this cmd from host controller, we
+		 * are the destination and it is our local gate
+		 */
+		hdev->gate2pipe[create_info->dest_gate] = create_info->pipe;
+		hdev->pipes[create_info->pipe].gate = create_info->dest_gate;
+		hdev->pipes[create_info->pipe].host = create_info->src_host;
+		break;
+	case ST21NFCB_HCI_ANY_OPEN_PIPE:
+		/* If the pipe is not created report an error */
+		if (gate == ST21NFCB_HCI_INVALID_GATE) {
+			status = ST21NFCB_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		break;
+	case ST21NFCB_HCI_ADM_NOTIFY_PIPE_DELETED:
+		if (skb->len != 1) {
+			status = ST21NFCB_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		delete_info = (struct st21nfcb_hci_delete_pipe_noti *)skb->data;
+
+		hdev->pipes[delete_info->pipe].gate = ST21NFCB_HCI_INVALID_GATE;
+		hdev->pipes[delete_info->pipe].host = ST21NFCB_HCI_INVALID_HOST;
+		break;
+	case ST21NFCB_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
+		if (skb->len != 1) {
+			status = ST21NFCB_HCI_ANY_E_NOK;
+			goto exit;
+		}
+		cleared_info = (struct st21nfcb_hci_all_pipe_cleared_noti *)skb->data;
+		st21nfcb_hci_reset_pipes_per_host(hdev, cleared_info->host);
+		break;
+	default:
+		pr_debug("Discarded unknown cmd %x to gate %x\n", cmd, gate);
+		break;
+	}
+
+	if (hdev->ops->cmd_received)
+		hdev->ops->cmd_received(hdev, pipe, cmd, skb);
+
+exit:
+	st21nfcb_hci_send_response(hdev, pipe, status, NULL, 0);
+
+	kfree_skb(skb);
+}
+
+static void st21nfcb_hci_resp_received(struct st21nfcb_hci_dev *hdev, u8 pipe,
+					u8 result, struct sk_buff *skb)
+{
+	struct nci_dev *ndev = st21nfcb_hci_get_nci(hdev);
+	struct nci_conn_info    *conn_info;
+	u8 status = result;
+
+	if (result != ST21NFCB_HCI_ANY_OK)
+		goto exit;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info) {
+		status = NCI_STATUS_REJECTED;
+		goto exit;
+	}
+
+	conn_info->rx_skb = skb;
+
+exit:
+	nci_req_complete(ndev, status);
+}
+
+/*
+ * Receive hcp message for pipe, with type and cmd.
+ * skb contains optional message data only.
+ */
+static void st21nfcb_hci_hcp_message_rx(struct st21nfcb_hci_dev *hdev, u8 pipe,
+				u8 type, u8 instruction, struct sk_buff *skb)
+{
+	struct nci_dev *ndev = st21nfcb_hci_get_nci(hdev);
+
+	switch (type) {
+	case ST21NFCB_HCI_HCP_RESPONSE:
+		st21nfcb_hci_resp_received(hdev, pipe, instruction, skb);
+		break;
+	case ST21NFCB_HCI_HCP_COMMAND:
+		st21nfcb_hci_cmd_received(hdev, pipe, instruction, skb);
+		break;
+	case ST21NFCB_HCI_HCP_EVENT:
+		st21nfcb_hci_event_received(hdev, pipe, instruction, skb);
+		break;
+	default:
+		pr_err("UNKNOWN MSG Type %d, instruction=%d\n",
+			type, instruction);
+		kfree_skb(skb);
+		break;
+	}
+
+	nci_req_complete(ndev, 0);
+}
+
+static void st21nfcb_hci_msg_rx_work(struct work_struct *work)
+{
+	struct st21nfcb_hci_dev *hdev = container_of(work,
+						struct st21nfcb_hci_dev,
+						msg_rx_work);
+	struct sk_buff *skb;
+	struct st21nfcb_hcp_message *message;
+	u8 pipe, type, instruction;
+
+	while ((skb = skb_dequeue(&hdev->msg_rx_queue)) != NULL) {
+		pipe = skb->data[0];
+		skb_pull(skb, ST21NFCB_HCI_HCP_PACKET_HEADER_LEN);
+		message = (struct st21nfcb_hcp_message *)skb->data;
+		type = ST21NFCB_HCP_MSG_GET_TYPE(message->header);
+		instruction = ST21NFCB_HCP_MSG_GET_CMD(message->header);
+		skb_pull(skb, ST21NFCB_HCI_HCP_MESSAGE_HEADER_LEN);
+
+		st21nfcb_hci_hcp_message_rx(hdev, pipe, type, instruction, skb);
+	}
+}
+
+static void st21nfcb_hci_data_received_cb(void *context,
+				struct sk_buff *skb, int err)
+{
+	struct st21nfcb_hci_dev *hdev = (struct st21nfcb_hci_dev *)context;
+	struct st21nfcb_hcp_packet *packet;
+	u8 pipe, type, instruction;
+	struct sk_buff *hcp_skb;
+	struct sk_buff *frag_skb;
+	int msg_len;
+
+	pr_debug("\n");
+
+	if (err) {
+		nci_req_complete(hdev->ndev, err);
+		return;
+	}
+
+	packet = (struct st21nfcb_hcp_packet *)skb->data;
+	if ((packet->header & ~ST21NFCB_HCI_FRAGMENT) == 0) {
+		skb_queue_tail(&hdev->rx_hcp_frags, skb);
+		return;
+	}
+
+	/* it's the last fragment. Does it need re-aggregation? */
+	if (skb_queue_len(&hdev->rx_hcp_frags)) {
+		pipe = packet->header & ST21NFCB_HCI_FRAGMENT;
+		skb_queue_tail(&hdev->rx_hcp_frags, skb);
+
+		msg_len = 0;
+		skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
+			msg_len += (frag_skb->len -
+				    ST21NFCB_HCI_HCP_PACKET_HEADER_LEN);
+		}
+
+		hcp_skb = nfc_alloc_recv_skb(ST21NFCB_HCI_HCP_PACKET_HEADER_LEN +
+					     msg_len, GFP_KERNEL);
+		if (hcp_skb == NULL) {
+			nci_req_complete(hdev->ndev, -ENOMEM);
+			return;
+		}
+
+		*skb_put(hcp_skb, ST21NFCB_HCI_HCP_PACKET_HEADER_LEN) = pipe;
+
+		skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
+			msg_len = frag_skb->len -
+				  ST21NFCB_HCI_HCP_PACKET_HEADER_LEN;
+			memcpy(skb_put(hcp_skb, msg_len), frag_skb->data +
+			       ST21NFCB_HCI_HCP_PACKET_HEADER_LEN, msg_len);
+		}
+
+		skb_queue_purge(&hdev->rx_hcp_frags);
+	} else {
+		packet->header &= ST21NFCB_HCI_FRAGMENT;
+		hcp_skb = skb;
+	}
+
+	/* if this is a response, dispatch immediately to
+	 * unblock waiting cmd context. Otherwise, enqueue to dispatch
+	 * in separate context where handler can also execute command.
+	 */
+	packet = (struct st21nfcb_hcp_packet *)hcp_skb->data;
+	type = ST21NFCB_HCP_MSG_GET_TYPE(packet->message.header);
+	if (type == ST21NFCB_HCI_HCP_RESPONSE) {
+		pipe = packet->header;
+		instruction = ST21NFCB_HCP_MSG_GET_CMD(packet->message.header);
+		skb_pull(hcp_skb, ST21NFCB_HCI_HCP_PACKET_HEADER_LEN +
+			ST21NFCB_HCI_HCP_MESSAGE_HEADER_LEN);
+		st21nfcb_hci_hcp_message_rx(hdev, pipe, type, instruction,
+					    hcp_skb);
+	} else {
+		skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
+		schedule_work(&hdev->msg_rx_work);
+	}
+}
+
+int st21nfcb_hci_open_pipe(struct st21nfcb_hci_dev *hdev, u8 pipe)
+{
+	struct nci_dev *ndev = st21nfcb_hci_get_nci(hdev);
+	struct nci_data data;
+	struct nci_conn_info    *conn_info;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		return -EPROTO;
+
+	data.conn_id = conn_info->conn_id;
+	data.pipe = pipe;
+	data.cmd = ST21NFCB_HCP_HEADER(ST21NFCB_HCI_HCP_COMMAND,
+				       ST21NFCB_HCI_ANY_OPEN_PIPE);
+	data.data = NULL;
+	data.data_len = 0;
+
+	return nci_request(ndev, st21nfcb_hci_send_data_req,
+			(unsigned long)&data,
+			msecs_to_jiffies(NCI_DATA_TIMEOUT));
+}
+EXPORT_SYMBOL(st21nfcb_hci_open_pipe);
+
+int st21nfcb_hci_set_param(struct st21nfcb_hci_dev *hdev, u8 gate, u8 idx,
+			const u8 *param, size_t param_len)
+{
+	struct nci_dev *ndev = st21nfcb_hci_get_nci(hdev);
+	struct nci_conn_info *conn_info;
+	struct nci_data data;
+	int r;
+	u8 *tmp;
+	u8 pipe = hdev->gate2pipe[gate];
+
+	pr_debug("idx=%d to gate %d\n", idx, gate);
+
+	if (pipe == ST21NFCB_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		return -EPROTO;
+
+	tmp = kmalloc(1 + param_len, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	*tmp = idx;
+	memcpy(tmp + 1, param, param_len);
+
+	data.conn_id = conn_info->conn_id;
+	data.pipe = pipe;
+	data.cmd = ST21NFCB_HCP_HEADER(ST21NFCB_HCI_HCP_COMMAND,
+				ST21NFCB_HCI_ANY_SET_PARAMETER);
+	data.data = tmp;
+	data.data_len = param_len + 1;
+
+	r = nci_request(ndev, st21nfcb_hci_send_data_req,
+			(unsigned long)&data,
+			msecs_to_jiffies(NCI_DATA_TIMEOUT));
+
+	kfree(tmp);
+	return r;
+}
+EXPORT_SYMBOL(st21nfcb_hci_set_param);
+
+int st21nfcb_hci_get_param(struct st21nfcb_hci_dev *hdev, u8 gate, u8 idx,
+			struct sk_buff **skb)
+{
+	struct nci_dev *ndev = st21nfcb_hci_get_nci(hdev);
+	struct nci_conn_info    *conn_info;
+	struct nci_data data;
+	int r;
+	u8 pipe = hdev->gate2pipe[gate];
+
+	pr_debug("idx=%d to gate %d\n", idx, gate);
+
+	if (pipe == ST21NFCB_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		return -EPROTO;
+
+	data.conn_id = conn_info->conn_id;
+	data.pipe = pipe;
+	data.cmd = ST21NFCB_HCP_HEADER(ST21NFCB_HCI_HCP_COMMAND,
+				ST21NFCB_HCI_ANY_GET_PARAMETER);
+	data.data = &idx;
+	data.data_len = 1;
+
+	r = nci_request(ndev, st21nfcb_hci_send_data_req, (unsigned long)&data,
+			msecs_to_jiffies(NCI_DATA_TIMEOUT));
+
+	if (r == NCI_STATUS_OK)
+		*skb = conn_info->rx_skb;
+
+	return r;
+}
+EXPORT_SYMBOL(st21nfcb_hci_get_param);
+
+int st21nfcb_hci_connect_gate(struct st21nfcb_hci_dev *hdev,
+				u8 dest_host, u8 dest_gate, u8 pipe)
+{
+	int r;
+
+	if (pipe == ST21NFCB_HCI_DO_NOT_OPEN_PIPE)
+		return 0;
+
+	if (hdev->gate2pipe[dest_gate] != ST21NFCB_HCI_INVALID_PIPE)
+		return -EADDRINUSE;
+
+	if (pipe != ST21NFCB_HCI_INVALID_PIPE)
+		goto open_pipe;
+
+	switch (dest_gate) {
+	case ST21NFCB_HCI_LINK_MGMT_GATE:
+		pipe = ST21NFCB_HCI_LINK_MGMT_PIPE;
+	break;
+	case ST21NFCB_HCI_ADMIN_GATE:
+		pipe = ST21NFCB_HCI_ADMIN_PIPE;
+	break;
+	}
+
+open_pipe:
+	r = st21nfcb_hci_open_pipe(hdev, pipe);
+	if (r < 0)
+		return r;
+
+	hdev->pipes[pipe].gate = dest_gate;
+	hdev->pipes[pipe].host = dest_host;
+	hdev->gate2pipe[dest_gate] = pipe;
+
+	return 0;
+}
+EXPORT_SYMBOL(st21nfcb_hci_connect_gate);
+
+static int st21nfcb_hci_dev_connect_gates(struct st21nfcb_hci_dev *hdev,
+					  u8 gate_count,
+					  struct st21nfcb_hci_gate *gates)
+{
+	int r;
+
+	while (gate_count--) {
+		r = st21nfcb_hci_connect_gate(hdev, ST21NFCB_HOST_CONTROLLER_ID,
+					      gates->gate, gates->pipe);
+		if (r < 0)
+			return r;
+		gates++;
+	}
+
+	return 0;
+}
+
+static int st21nfcb_hci_dev_session_init(struct st21nfcb_hci_dev *hdev,
+					 const char *session_id)
+{
+	struct sk_buff *skb;
+	int r, dev_num;
+
+	hdev->count_pipes = 0;
+	hdev->expected_pipes = 0;
+
+	/*
+	 * Session id must include the driver name + i2c bus addr
+	 * persistent info to discriminate 2 identical chips
+	 */
+	dev_num = find_first_zero_bit(dev_mask, ST21NFCB_NUM_DEVICES);
+	if (dev_num >= ST21NFCB_NUM_DEVICES)
+		return -ENODEV;
+
+	scnprintf(hdev->init_data.session_id,
+		  sizeof(hdev->init_data.session_id), "%s%2x", session_id,
+		  dev_num);
+
+	if (hdev->init_data.gates[0].gate != ST21NFCB_HCI_ADMIN_GATE)
+		return -EPROTO;
+
+	r = st21nfcb_hci_connect_gate(hdev, ST21NFCB_HOST_CONTROLLER_ID,
+				hdev->init_data.gates[0].gate,
+				hdev->init_data.gates[0].pipe);
+	if (r < 0)
+		goto exit;
+
+	r = st21nfcb_hci_get_param(hdev, ST21NFCB_HCI_ADMIN_GATE,
+				ST21NFCB_HCI_ADMIN_PARAM_SESSION_IDENTITY,
+				&skb);
+	if (r < 0)
+		goto exit;
+
+	if (skb->len && skb->len == strlen(hdev->init_data.session_id) &&
+	    memcmp(hdev->init_data.session_id, skb->data, skb->len) == 0 &&
+		   hdev->ops->load_session) {
+		/* Restore gate<->pipe table from some proprietary location. */
+		r = hdev->ops->load_session(hdev);
+		if (r < 0)
+			goto exit;
+	} else {
+		r = st21nfcb_hci_dev_connect_gates(hdev,
+					hdev->init_data.gate_count,
+					hdev->init_data.gates);
+		if (r < 0)
+			goto exit;
+
+		r = st21nfcb_hci_set_param(hdev, ST21NFCB_HCI_ADMIN_GATE,
+				ST21NFCB_HCI_ADMIN_PARAM_SESSION_IDENTITY,
+				hdev->init_data.session_id,
+				strlen(hdev->init_data.session_id));
+	}
+	if (r == 0)
+		goto exit;
+
+exit:
+	kfree_skb(skb);
+
+	return r;
+}
+
+struct st21nfcb_hci_dev *st21nfcb_hci_allocate(struct nci_dev *ndev,
+					       struct st21nfcb_hci_ops *ops,
+					       const char *session_id,
+					       struct st21nfcb_hci_gate *gates,
+					       int gates_len)
+{
+	struct st21nfcb_hci_dev  *hdev;
+	struct core_conn_create_dest_spec_params dest_params;
+	struct nci_conn_info    *conn_info;
+	int r;
+
+	hdev = kzalloc(sizeof(struct st21nfcb_hci_dev), GFP_KERNEL);
+	if (!hdev)
+		return NULL;
+
+	r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE);
+	if (r != NCI_STATUS_OK)
+		goto exit;
+
+	dest_params.type = NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE;
+	dest_params.length = sizeof(struct dest_spec_params);
+	dest_params.value.id = NCI_NFCEE_ID_HCI;
+	dest_params.value.protocol = NCI_NFCEE_INTERFACE_HCI_ACCESS;
+	r = nci_core_conn_create(ndev, &dest_params);
+	if (r != NCI_STATUS_OK)
+		goto exit;
+
+	conn_info = ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		goto exit;
+
+	conn_info->data_exchange_cb = st21nfcb_hci_data_received_cb;
+	conn_info->data_exchange_cb_context = hdev;
+
+	hdev->ndev = ndev;
+	hdev->ops = ops;
+
+	skb_queue_head_init(&hdev->rx_hcp_frags);
+	INIT_WORK(&hdev->msg_rx_work, st21nfcb_hci_msg_rx_work);
+	skb_queue_head_init(&hdev->msg_rx_queue);
+
+	memcpy(hdev->init_data.gates, gates, gates_len);
+
+	st21nfcb_hci_reset_pipes(hdev);
+	r = st21nfcb_hci_dev_session_init(hdev, "ST21BH");
+	if (r != ST21NFCB_HCI_ANY_OK)
+		goto exit;
+
+	r = nci_nfcee_mode_set(ndev, NCI_NFCEE_ID_HCI, NCI_NFCEE_ENABLE);
+	if (r != NCI_STATUS_OK)
+		goto exit;
+
+	return hdev;
+
+exit:
+	kfree(hdev);
+	return NULL;
+}
+EXPORT_SYMBOL(st21nfcb_hci_allocate);
+
+void st21nfcb_hci_free(struct st21nfcb_hci_dev *hdev)
+{
+	struct nci_conn_info    *conn_info;
+
+	conn_info = hdev->ndev->conn_info_by_id[NCI_NFCEE_ID_HCI];
+	if (!conn_info)
+		return;
+
+	nci_core_conn_close(hdev->ndev, conn_info->conn_id);
+	kfree(hdev);
+}
+EXPORT_SYMBOL(st21nfcb_hci_free);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st21nfcb/st21nfcb_hci_core.h b/drivers/nfc/st21nfcb/st21nfcb_hci_core.h
new file mode 100644
index 0000000..fa468b4
--- /dev/null
+++ b/drivers/nfc/st21nfcb/st21nfcb_hci_core.h
@@ -0,0 +1,134 @@
+/*
+ * NCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_ST21NFCB_HCI_CORE_H_
+#define __LOCAL_ST21NFCB_HCI_CORE_H_
+
+#define ST21NFCB_HCI_ANY_OPEN_PIPE	0x03
+
+/* Hosts */
+#define ST21NFCB_HOST_CONTROLLER_ID     0x00
+#define ST21NFCB_TERMINAL_HOST_ID       0x01
+#define ST21NFCB_UICC_HOST_ID           0x02
+#define ST21NFCB_ESE_HOST_ID            0xc0
+
+/* Gates */
+#define ST21NFCB_HCI_ADMIN_GATE         0x00
+#define ST21NFCB_DEVICE_MGNT_GATE       0x01
+#define ST21NFCB_HCI_LINK_MGMT_GATE     0x06
+#define ST21NFCB_APDU_READER_GATE       0xf0
+#define ST21NFCB_CONNECTIVITY_GATE      0x41
+
+/* Pipes */
+#define ST21NFCB_HCI_LINK_MGMT_PIPE             0x00
+#define ST21NFCB_HCI_ADMIN_PIPE                 0x01
+#define ST21NFCB_DEVICE_MGNT_PIPE               0x02
+
+/* Generic responses */
+#define ST21NFCB_HCI_ANY_OK                     0x00
+#define ST21NFCB_HCI_ANY_E_NOT_CONNECTED        0x01
+#define ST21NFCB_HCI_ANY_E_CMD_PAR_UNKNOWN      0x02
+#define ST21NFCB_HCI_ANY_E_NOK                  0x03
+#define ST21NFCB_HCI_ANY_E_PIPES_FULL           0x04
+#define ST21NFCB_HCI_ANY_E_REG_PAR_UNKNOWN      0x05
+#define ST21NFCB_HCI_ANY_E_PIPE_NOT_OPENED      0x06
+#define ST21NFCB_HCI_ANY_E_CMD_NOT_SUPPORTED    0x07
+#define ST21NFCB_HCI_ANY_E_INHIBITED            0x08
+#define ST21NFCB_HCI_ANY_E_TIMEOUT              0x09
+#define ST21NFCB_HCI_ANY_E_REG_ACCESS_DENIED    0x0a
+#define ST21NFCB_HCI_ANY_E_PIPE_ACCESS_DENIED   0x0b
+
+#define ST21NFCB_HCI_DO_NOT_OPEN_PIPE		0x81
+#define ST21NFCB_HCI_INVALID_PIPE		0x80
+#define ST21NFCB_HCI_INVALID_GATE		0xFF
+#define ST21NFCB_HCI_INVALID_HOST		0x80
+
+#define ST21NFCB_HCI_MAX_CUSTOM_GATES   50
+#define ST21NFCB_HCI_MAX_PIPES          127
+
+struct st21nfcb_hci_gate {
+	u8 gate;
+	u8 pipe;
+} __packed;
+
+struct st21nfcb_hci_pipe {
+	u8 gate;
+	u8 host;
+} __packed;
+
+struct st21nfcb_hci_init_data {
+	u8 gate_count;
+	struct st21nfcb_hci_gate gates[ST21NFCB_HCI_MAX_CUSTOM_GATES];
+	char session_id[9];
+};
+
+#define ST21NFCB_HCI_MAX_GATES		256
+
+struct st21nfcb_hci_dev {
+	struct nci_dev *ndev;
+
+	struct st21nfcb_hci_init_data init_data;
+	struct st21nfcb_hci_pipe pipes[ST21NFCB_HCI_MAX_PIPES];
+	u8 gate2pipe[ST21NFCB_HCI_MAX_GATES];
+	int expected_pipes;
+	int count_pipes;
+
+	struct sk_buff_head rx_hcp_frags;
+	struct work_struct msg_rx_work;
+	struct sk_buff_head msg_rx_queue;
+
+	struct st21nfcb_hci_ops *ops;
+};
+
+struct st21nfcb_hci_ops {
+	int (*load_session)(struct st21nfcb_hci_dev *hdev);
+	void (*event_received)(struct st21nfcb_hci_dev *hdev, u8 pipe, u8 event,
+			      struct sk_buff *skb);
+	void (*cmd_received)(struct st21nfcb_hci_dev *ndev, u8 pipe, u8 cmd,
+			    struct sk_buff *skb);
+};
+
+static inline struct nci_dev *st21nfcb_hci_get_nci(struct st21nfcb_hci_dev *hdev)
+{
+	return hdev->ndev;
+}
+
+int st21nfcb_hci_send_event(struct st21nfcb_hci_dev *hdev, u8 gate, u8 event,
+			    const u8 *param, size_t param_len);
+int st21nfcb_hci_send_cmd(struct st21nfcb_hci_dev *hdev, u8 gate,
+			  u8 cmd, const u8 *param, size_t param_len,
+			  struct sk_buff **skb);
+int st21nfcb_hci_send_response(struct st21nfcb_hci_dev *hdev, u8 pipe,
+			       u8 cmd, const u8 *param, size_t param_len);
+
+int st21nfcb_hci_open_pipe(struct st21nfcb_hci_dev *hdev, u8 pipe);
+int st21nfcb_hci_connect_gate(struct st21nfcb_hci_dev *hdev,
+			      u8 dest_host, u8 dest_gate, u8 pipe);
+int st21nfcb_hci_set_param(struct st21nfcb_hci_dev *hdev, u8 gate, u8 idx,
+			   const u8 *param, size_t param_len);
+int st21nfcb_hci_get_param(struct st21nfcb_hci_dev *hdev, u8 gate, u8 idx,
+			   struct sk_buff **skb);
+
+struct st21nfcb_hci_dev *st21nfcb_hci_allocate(struct nci_dev *ndev,
+					struct st21nfcb_hci_ops *ops,
+					const char *session_id,
+					struct st21nfcb_hci_gate *gates,
+					int gates_len);
+void st21nfcb_hci_free(struct st21nfcb_hci_dev *hdev);
+
+#endif /* __LOCAL_ST21NFCB_HCI_CORE_H_ */
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 09/14] NFC: st21nfcb: Adding support for secure element
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (7 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 08/14] NFC: st21nfcb: Add HCI protocol over NCI protocol support Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 10/14] NFC: Forward NFC_EVT_TRANSACTION up to user space Christophe Ricard
                     ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

st21nfcb has 3 SWP lines and can support up to 3 secure elements (UICC/eSE and µSD in the future).

Some st21nfcb firmware does not support the nci command nci_nfcee_mode_set(NCI_NFCEE_DISABLE).
For this reason, we assume 2 secures elements are always present (UICC and eSE).
They will be added to the SE list once successfully activated. They will
be available only after running through enable_se handler or when the poll
in listen mode is started.

During initialization, the white_list will be always set assuming both
UICC & eSE are present.

On ese activation, the atr is retrieved to calculate a command exchange
timeout according to the first atr(TB) value.

The se_io will allow to transfer data over SWP. 2 kind of event may appear
after a data is sent over:
- ST21NFCB_EVT_TRANSMIT_DATA when receiving an apdu answer
- ST21NFCB_EVT_WTX_REQUEST when the secure element needs more time than
expected to compute a command. If this timeout expired, a first recovery
tentative consist to send a simple software reset command. If this
tentative still fail, a second recovery tentative consist to send a
hardware reset.
This function is only relevant for eSE like secure element.

Like st21nfca, we do reference pipe with a tuple (gate, host) in order to
keep the host information when receiving secure element event
(ex: EVT_TRANSACTION).

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 drivers/nfc/st21nfcb/Makefile      |   2 +-
 drivers/nfc/st21nfcb/st21nfcb.c    |   8 +-
 drivers/nfc/st21nfcb/st21nfcb.h    |   2 +
 drivers/nfc/st21nfcb/st21nfcb_se.c | 610 +++++++++++++++++++++++++++++++++++++
 drivers/nfc/st21nfcb/st21nfcb_se.h |  59 ++++
 5 files changed, 679 insertions(+), 2 deletions(-)
 create mode 100644 drivers/nfc/st21nfcb/st21nfcb_se.c
 create mode 100644 drivers/nfc/st21nfcb/st21nfcb_se.h

diff --git a/drivers/nfc/st21nfcb/Makefile b/drivers/nfc/st21nfcb/Makefile
index 974c2e9..2f452d7 100644
--- a/drivers/nfc/st21nfcb/Makefile
+++ b/drivers/nfc/st21nfcb/Makefile
@@ -2,7 +2,7 @@
 # Makefile for ST21NFCB NCI based NFC driver
 #
 
-st21nfcb_nci-objs = ndlc.o st21nfcb.o st21nfcb_hci_core.o
+st21nfcb_nci-objs = ndlc.o st21nfcb.o st21nfcb_hci_core.o st21nfcb_se.o
 obj-$(CONFIG_NFC_ST21NFCB)     += st21nfcb_nci.o
 
 st21nfcb_i2c-objs = i2c.o
diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c
index ea63d58..d2b60eb 100644
--- a/drivers/nfc/st21nfcb/st21nfcb.c
+++ b/drivers/nfc/st21nfcb/st21nfcb.c
@@ -22,6 +22,7 @@
 #include <net/nfc/nci_core.h>
 
 #include "st21nfcb.h"
+#include "st21nfcb_se.h"
 
 #define DRIVER_DESC "NCI NFC driver for ST21NFCB"
 
@@ -78,6 +79,10 @@ static struct nci_ops st21nfcb_nci_ops = {
 	.close = st21nfcb_nci_close,
 	.send = st21nfcb_nci_send,
 	.get_rfprotocol = st21nfcb_nci_get_rfprotocol,
+	.discover_se = st21nfcb_nci_discover_se,
+	.enable_se = st21nfcb_nci_enable_se,
+	.disable_se = st21nfcb_nci_disable_se,
+	.se_io = st21nfcb_nci_se_io,
 };
 
 int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
@@ -114,9 +119,10 @@ int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
 	if (r) {
 		pr_err("Cannot register nfc device to nci core\n");
 		nci_free_device(ndlc->ndev);
+		return r;
 	}
 
-	return r;
+	return st21nfcb_se_init(ndlc->ndev);
 }
 EXPORT_SYMBOL_GPL(st21nfcb_nci_probe);
 
diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st21nfcb/st21nfcb.h
index ea58a56..5ef8a58 100644
--- a/drivers/nfc/st21nfcb/st21nfcb.h
+++ b/drivers/nfc/st21nfcb/st21nfcb.h
@@ -19,6 +19,7 @@
 #ifndef __LOCAL_ST21NFCB_H_
 #define __LOCAL_ST21NFCB_H_
 
+#include "st21nfcb_se.h"
 #include "ndlc.h"
 
 /* Define private flags: */
@@ -27,6 +28,7 @@
 struct st21nfcb_nci_info {
 	struct llt_ndlc *ndlc;
 	unsigned long flags;
+	struct st21nfcb_se_info se_info;
 };
 
 void st21nfcb_nci_remove(struct nci_dev *ndev);
diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.c b/drivers/nfc/st21nfcb/st21nfcb_se.c
new file mode 100644
index 0000000..cf65368
--- /dev/null
+++ b/drivers/nfc/st21nfcb/st21nfcb_se.c
@@ -0,0 +1,610 @@
+/*
+ * NCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+
+#include "st21nfcb.h"
+#include "st21nfcb_se.h"
+#include "st21nfcb_hci_core.h"
+
+struct st21nfcb_pipe_info {
+	u8 pipe_state;
+	u8 src_host_id;
+	u8 src_gate_id;
+	u8 dst_host_id;
+	u8 dst_gate_id;
+} __packed;
+
+/* Connectivity pipe only */
+#define ST21NFCB_SE_COUNT_PIPE_UICC             0x01
+/* Connectivity + APDU Reader pipe */
+#define ST21NFCB_SE_COUNT_PIPE_EMBEDDED         0x02
+
+#define ST21NFCB_SE_TO_HOT_PLUG			1000 /* msecs */
+#define ST21NFCB_SE_TO_PIPES			2000
+
+#define ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(x)   (x->data[0] & 0x80)
+
+#define ST21NFCB_HCI_APDU_PARAM_ATR                     0x01
+#define ST21NFCB_HCI_ADMIN_PARAM_SESSION_IDENTITY       0x01
+#define ST21NFCB_HCI_ADMIN_PARAM_WHITELIST              0x03
+#define ST21NFCB_HCI_ADMIN_PARAM_HOST_LIST              0x04
+
+#define ST21NFCB_EVT_SE_HARD_RESET		0x20
+#define ST21NFCB_EVT_TRANSMIT_DATA		0x10
+#define ST21NFCB_EVT_WTX_REQUEST		0x11
+#define ST21NFCB_EVT_SE_SOFT_RESET		0x11
+#define ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER	0x21
+#define ST21NFCB_EVT_HOT_PLUG			0x03
+
+#define ST21NFCB_SE_MODE_OFF                    0x00
+#define ST21NFCB_SE_MODE_ON                     0x01
+
+#define ST21NFCB_EVT_CONNECTIVITY       0x10
+#define ST21NFCB_EVT_TRANSACTION        0x12
+
+#define ST21NFCB_DM_GETINFO             0x13
+#define ST21NFCB_DM_GETINFO_PIPE_LIST   0x02
+#define ST21NFCB_DM_GETINFO_PIPE_INFO   0x01
+#define ST21NFCB_DM_PIPE_CREATED        0x02
+#define ST21NFCB_DM_PIPE_OPEN           0x04
+#define ST21NFCB_DM_RF_ACTIVE           0x80
+#define ST21NFCB_DM_DISCONNECT          0x30
+
+#define ST21NFCB_DM_IS_PIPE_OPEN(p) \
+	((p & 0x0f) == (ST21NFCB_DM_PIPE_CREATED | ST21NFCB_DM_PIPE_OPEN))
+
+#define ST21NFCB_ATR_DEFAULT_BWI        0x04
+
+/*
+ * WT = 2^BWI/10[s], convert into msecs and add a secure
+ * room by increasing by 2 this timeout
+ */
+#define ST21NFCB_BWI_TO_TIMEOUT(x)      ((1 << x) * 200)
+#define ST21NFCB_ATR_GET_Y_FROM_TD(x)   (x >> 4)
+
+/* If TA is present bit 0 is set */
+#define ST21NFCB_ATR_TA_PRESENT(x) (x & 0x01)
+/* If TB is present bit 1 is set */
+#define ST21NFCB_ATR_TB_PRESENT(x) (x & 0x02)
+
+/* Here are the mandatory pipe for st21nfcb */
+static struct st21nfcb_hci_gate st21nfcb_gates[] = {
+	{ST21NFCB_HCI_ADMIN_GATE, ST21NFCB_HCI_ADMIN_PIPE},
+	{ST21NFCB_HCI_LINK_MGMT_GATE, ST21NFCB_HCI_LINK_MGMT_PIPE},
+	{ST21NFCB_DEVICE_MGNT_GATE, ST21NFCB_DEVICE_MGNT_PIPE},
+
+	/* Secure element pipes are created by secure element host */
+	{ST21NFCB_CONNECTIVITY_GATE, ST21NFCB_HCI_DO_NOT_OPEN_PIPE},
+	{ST21NFCB_APDU_READER_GATE, ST21NFCB_HCI_DO_NOT_OPEN_PIPE},
+};
+
+static u8 st21nfcb_se_get_bwi(struct nci_dev *ndev)
+{
+	int i;
+	u8 td;
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	/* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */
+	for (i = 1; i < ST21NFCB_ESE_MAX_LENGTH; i++) {
+		td = ST21NFCB_ATR_GET_Y_FROM_TD(info->se_info.atr[i]);
+		if (ST21NFCB_ATR_TA_PRESENT(td))
+			i++;
+		if (ST21NFCB_ATR_TB_PRESENT(td)) {
+			i++;
+			return info->se_info.atr[i] >> 4;
+		}
+	}
+	return ST21NFCB_ATR_DEFAULT_BWI;
+}
+
+static void st21nfcb_se_get_atr(struct nci_dev *ndev)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	struct st21nfcb_hci_dev  *hdev = info->se_info.hci_dev;
+	int r;
+	struct sk_buff *skb;
+
+	r = st21nfcb_hci_get_param(hdev, ST21NFCB_APDU_READER_GATE,
+				ST21NFCB_HCI_APDU_PARAM_ATR, &skb);
+	if (r < 0)
+		return;
+
+	if (skb->len <= ST21NFCB_ESE_MAX_LENGTH) {
+		memcpy(info->se_info.atr, skb->data, skb->len);
+
+		info->se_info.wt_timeout =
+			ST21NFCB_BWI_TO_TIMEOUT(st21nfcb_se_get_bwi(ndev));
+	}
+	kfree_skb(skb);
+}
+
+static int st21nfcb_hci_load_session(struct st21nfcb_hci_dev *hdev)
+{
+	int i, j, r;
+	struct sk_buff *skb_pipe_list, *skb_pipe_info;
+	struct st21nfcb_pipe_info *dm_pipe_info;
+	u8 pipe_list[] = { ST21NFCB_DM_GETINFO_PIPE_LIST,
+			ST21NFCB_TERMINAL_HOST_ID};
+	u8 pipe_info[] = { ST21NFCB_DM_GETINFO_PIPE_INFO,
+			ST21NFCB_TERMINAL_HOST_ID, 0};
+
+	/* On ST21NFCB device pipes number are dynamics
+	 * If pipes are already created, hci_dev_up will fail.
+	 * Doing a clear all pipe is a bad idea because:
+	 * - It does useless EEPROM cycling
+	 * - It might cause issue for secure elements support
+	 * (such as removing connectivity or APDU reader pipe)
+	 * A better approach on ST21NFCB is to:
+	 * - get a pipe list for each host.
+	 * (eg: ST21NFCB_HOST_CONTROLLER_ID for now).
+	 * (TODO Later on UICC HOST and eSE HOST)
+	 * - get pipe information
+	 * - match retrieved pipe list in st21nfcb_gates
+	 * ST21NFCB_DEVICE_MGNT_GATE is a proprietary gate
+	 * with ST21NFCB_DEVICE_MGNT_PIPE.
+	 * Pipe can be closed and need to be open.
+	 */
+	r = st21nfcb_hci_connect_gate(hdev, ST21NFCB_HOST_CONTROLLER_ID,
+				ST21NFCB_DEVICE_MGNT_GATE,
+				ST21NFCB_DEVICE_MGNT_PIPE);
+	if (r < 0)
+		goto free_info;
+
+	/* Get pipe list */
+	r = st21nfcb_hci_send_cmd(hdev, ST21NFCB_DEVICE_MGNT_GATE,
+			ST21NFCB_DM_GETINFO, pipe_list, sizeof(pipe_list),
+			&skb_pipe_list);
+	if (r < 0)
+		goto free_info;
+
+	/* Complete the existing gate_pipe table */
+	for (i = 0; i < skb_pipe_list->len; i++) {
+		pipe_info[2] = skb_pipe_list->data[i];
+		r = st21nfcb_hci_send_cmd(hdev, ST21NFCB_DEVICE_MGNT_GATE,
+					ST21NFCB_DM_GETINFO, pipe_info,
+					sizeof(pipe_info), &skb_pipe_info);
+
+		if (r)
+			continue;
+
+		/*
+		 * Match pipe ID and gate ID
+		 * Output format from ST21NFC_DM_GETINFO is:
+		 * - pipe state (1byte)
+		 * - source hid (1byte)
+		 * - source gid (1byte)
+		 * - destination hid (1byte)
+		 * - destination gid (1byte)
+		 */
+		dm_pipe_info = (struct st21nfcb_pipe_info *)skb_pipe_info->data;
+		if (dm_pipe_info->dst_gate_id == ST21NFCB_APDU_READER_GATE &&
+		    dm_pipe_info->src_host_id != ST21NFCB_ESE_HOST_ID) {
+			pr_err("Unexpected apdu_reader pipe on host %x\n",
+			       dm_pipe_info->src_host_id);
+			continue;
+		}
+
+		for (j = 0; (j < ARRAY_SIZE(st21nfcb_gates)) &&
+		     (st21nfcb_gates[j].gate != dm_pipe_info->dst_gate_id); j++)
+			;
+
+		if (j < ARRAY_SIZE(st21nfcb_gates) &&
+		    st21nfcb_gates[j].gate == dm_pipe_info->dst_gate_id &&
+		    ST21NFCB_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) {
+			st21nfcb_gates[j].pipe = pipe_info[2];
+
+			hdev->gate2pipe[st21nfcb_gates[j].gate] =
+						st21nfcb_gates[j].pipe;
+			hdev->pipes[st21nfcb_gates[j].pipe].gate =
+						st21nfcb_gates[j].gate;
+			hdev->pipes[st21nfcb_gates[j].pipe].host =
+						dm_pipe_info->src_host_id;
+		}
+	}
+
+	memcpy(hdev->init_data.gates, st21nfcb_gates, sizeof(st21nfcb_gates));
+
+free_info:
+	kfree_skb(skb_pipe_info);
+	kfree_skb(skb_pipe_list);
+	return r;
+}
+
+static void st21nfcb_hci_admin_event_received(struct st21nfcb_hci_dev *hdev,
+					      u8 event, struct sk_buff *skb)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(hdev->ndev);
+
+	switch (event) {
+	case ST21NFCB_EVT_HOT_PLUG:
+		if (info->se_info.se_active) {
+			if (!ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(skb)) {
+				del_timer_sync(&info->se_info.se_active_timer);
+				info->se_info.se_active = false;
+				complete(&info->se_info.req_completion);
+			} else {
+				mod_timer(&info->se_info.se_active_timer,
+				      jiffies +
+				      msecs_to_jiffies(ST21NFCB_SE_TO_PIPES));
+			}
+		}
+	break;
+	}
+}
+
+static int st21nfcb_hci_apdu_reader_event_received(struct st21nfcb_hci_dev *hdev,
+						   u8 event,
+						   struct sk_buff *skb)
+{
+	int r = 0;
+	struct st21nfcb_nci_info *info = nci_get_drvdata(hdev->ndev);
+
+	pr_debug("apdu reader gate event: %x\n", event);
+
+	switch (event) {
+	case ST21NFCB_EVT_TRANSMIT_DATA:
+		del_timer_sync(&info->se_info.bwi_timer);
+		info->se_info.bwi_active = false;
+		info->se_info.cb(info->se_info.cb_context,
+				 skb->data, skb->len, 0);
+	break;
+	case ST21NFCB_EVT_WTX_REQUEST:
+		mod_timer(&info->se_info.bwi_timer, jiffies +
+			  msecs_to_jiffies(info->se_info.wt_timeout));
+	break;
+	}
+
+	kfree_skb(skb);
+	return r;
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ *    1: driver does not handle the event, please do standard processing
+ */
+static int st21nfcb_hci_connectivity_event_received(struct st21nfcb_hci_dev *hdev,
+						    u8 host, u8 event,
+						    struct sk_buff *skb)
+{
+	int r = 0;
+
+	pr_debug("connectivity gate event: %x\n", event);
+
+	switch (event) {
+	case ST21NFCB_EVT_CONNECTIVITY:
+	break;
+	case ST21NFCB_EVT_TRANSACTION:
+	break;
+	default:
+		return 1;
+	}
+	kfree_skb(skb);
+	return r;
+}
+
+static void st21nfcb_hci_event_received(struct st21nfcb_hci_dev *hdev, u8 pipe,
+					u8 event, struct sk_buff *skb)
+{
+	u8 gate = hdev->pipes[pipe].gate;
+	u8 host = hdev->pipes[pipe].host;
+
+	switch (gate) {
+	case ST21NFCB_HCI_ADMIN_GATE:
+		st21nfcb_hci_admin_event_received(hdev, event, skb);
+	break;
+	case ST21NFCB_APDU_READER_GATE:
+		st21nfcb_hci_apdu_reader_event_received(hdev, event, skb);
+	break;
+	case ST21NFCB_CONNECTIVITY_GATE:
+		st21nfcb_hci_connectivity_event_received(hdev, host, event,
+							 skb);
+	break;
+	}
+}
+
+static void st21nfcb_hci_cmd_received(struct st21nfcb_hci_dev *hdev, u8 pipe, u8 cmd,
+				      struct sk_buff *skb)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(hdev->ndev);
+	u8 gate = hdev->pipes[pipe].gate;
+
+	pr_debug("cmd: %x\n", cmd);
+
+	switch (cmd) {
+	case ST21NFCB_HCI_ANY_OPEN_PIPE:
+		if (gate != ST21NFCB_APDU_READER_GATE &&
+		    hdev->pipes[pipe].host != ST21NFCB_UICC_HOST_ID)
+			hdev->count_pipes++;
+
+		if (hdev->count_pipes == hdev->expected_pipes) {
+			del_timer_sync(&info->se_info.se_active_timer);
+			info->se_info.se_active = false;
+			hdev->count_pipes = 0;
+			complete(&info->se_info.req_completion);
+		}
+	break;
+	}
+}
+
+/*
+ * Remarks: On some early st21nfcb firmware, nci_nfcee_mode_set(0)
+ * is rejected
+ */
+static int st21nfcb_nci_control_se(struct nci_dev *ndev, u8 se_idx,
+				   u8 state)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	struct st21nfcb_hci_dev  *hdev = info->se_info.hci_dev;
+	int r;
+	struct sk_buff *sk_host_list;
+	u8 host_id;
+
+	switch (se_idx) {
+	case ST21NFCB_UICC_HOST_ID:
+		hdev->count_pipes = 0;
+		hdev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_UICC;
+		break;
+	case ST21NFCB_ESE_HOST_ID:
+		hdev->count_pipes = 0;
+		hdev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_EMBEDDED;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * Wait for an EVT_HOT_PLUG in order to
+	 * retrieve a relevant host list.
+	 */
+	reinit_completion(&info->se_info.req_completion);
+	r = nci_nfcee_mode_set(ndev, se_idx, NCI_NFCEE_ENABLE);
+	if (r != NCI_STATUS_OK)
+		return r;
+
+	mod_timer(&info->se_info.se_active_timer, jiffies +
+		msecs_to_jiffies(ST21NFCB_SE_TO_HOT_PLUG));
+	info->se_info.se_active = true;
+
+	/* Ignore return value and check in any case the host_list */
+	wait_for_completion_interruptible(&info->se_info.req_completion);
+
+	r = st21nfcb_hci_get_param(hdev, ST21NFCB_HCI_ADMIN_GATE,
+			ST21NFCB_HCI_ADMIN_PARAM_HOST_LIST, &sk_host_list);
+	if (r != ST21NFCB_HCI_ANY_OK)
+		return r;
+
+	host_id = sk_host_list->data[sk_host_list->len - 1];
+	kfree_skb(sk_host_list);
+	if (state == ST21NFCB_SE_MODE_ON && host_id == se_idx)
+		return se_idx;
+	else if (state == ST21NFCB_SE_MODE_OFF && host_id != se_idx)
+		return se_idx;
+
+	return -1;
+}
+
+int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	struct st21nfcb_hci_dev  *hdev = info->se_info.hci_dev;
+	int r;
+
+	pr_debug("st21nfcb_nci_disable_se\n");
+
+	if (se_idx == NFC_SE_EMBEDDED) {
+		r = st21nfcb_hci_send_event(hdev, ST21NFCB_APDU_READER_GATE,
+				ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0);
+		if (r < 0)
+			return r;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(st21nfcb_nci_disable_se);
+
+int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	struct st21nfcb_hci_dev  *hdev = info->se_info.hci_dev;
+	int r;
+
+	pr_debug("st21nfcb_nci_enable_se\n");
+
+	if (se_idx == ST21NFCB_HCI_HOST_ID_ESE) {
+		r = st21nfcb_hci_send_event(hdev, ST21NFCB_APDU_READER_GATE,
+				ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0);
+		if (r < 0)
+			return r;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(st21nfcb_nci_enable_se);
+
+static struct st21nfcb_hci_ops hci_ops = {
+	.load_session = st21nfcb_hci_load_session,
+	.event_received = st21nfcb_hci_event_received,
+	.cmd_received = st21nfcb_hci_cmd_received,
+};
+
+int st21nfcb_nci_discover_se(struct nci_dev *ndev)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	struct st21nfcb_hci_dev  *hdev;
+	u8 param[2];
+	int r;
+	int se_count = 0;
+
+	pr_debug("st21nfcb_nci_discover_se\n");
+
+	hdev = st21nfcb_hci_allocate(ndev, &hci_ops, "ST21BH",
+				     st21nfcb_gates, sizeof(st21nfcb_gates));
+	if (!hdev)
+		return -ENOMEM;
+
+	info->se_info.hci_dev = hdev;
+
+	r = st21nfcb_hci_open_pipe(hdev, ST21NFCB_HCI_ADMIN_PIPE);
+	if (r != ST21NFCB_HCI_ANY_OK)
+		return r;
+
+	param[0] = ST21NFCB_UICC_HOST_ID;
+	param[1] = ST21NFCB_HCI_HOST_ID_ESE;
+	r = st21nfcb_hci_set_param(hdev, ST21NFCB_HCI_ADMIN_GATE,
+				ST21NFCB_HCI_ADMIN_PARAM_WHITELIST,
+				param, sizeof(param));
+	if (r != ST21NFCB_HCI_ANY_OK)
+		return r;
+
+	r = st21nfcb_nci_control_se(ndev, ST21NFCB_UICC_HOST_ID,
+				ST21NFCB_SE_MODE_ON);
+	if (r == ST21NFCB_UICC_HOST_ID) {
+		nfc_add_se(ndev->nfc_dev, ST21NFCB_UICC_HOST_ID, NFC_SE_UICC);
+		se_count++;
+	}
+
+	/* Try to enable eSE in order to check availability */
+	r = st21nfcb_nci_control_se(ndev, ST21NFCB_HCI_HOST_ID_ESE,
+				ST21NFCB_SE_MODE_ON);
+	if (r == ST21NFCB_HCI_HOST_ID_ESE) {
+		nfc_add_se(ndev->nfc_dev, ST21NFCB_HCI_HOST_ID_ESE,
+			   NFC_SE_EMBEDDED);
+		se_count++;
+		st21nfcb_se_get_atr(ndev);
+	}
+
+	return !se_count;
+}
+EXPORT_SYMBOL_GPL(st21nfcb_nci_discover_se);
+
+int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx,
+		       u8 *apdu, size_t apdu_length,
+		       se_io_cb_t cb, void *cb_context)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+	struct st21nfcb_hci_dev  *hdev = info->se_info.hci_dev;
+
+	pr_debug("\n");
+
+	switch (se_idx) {
+	case ST21NFCB_HCI_HOST_ID_ESE:
+		info->se_info.cb = cb;
+		info->se_info.cb_context = cb_context;
+		mod_timer(&info->se_info.bwi_timer, jiffies +
+			  msecs_to_jiffies(info->se_info.wt_timeout));
+		info->se_info.bwi_active = true;
+		return st21nfcb_hci_send_event(hdev, ST21NFCB_APDU_READER_GATE,
+					ST21NFCB_EVT_TRANSMIT_DATA, apdu,
+					apdu_length);
+	default:
+		return -ENODEV;
+	}
+}
+EXPORT_SYMBOL(st21nfcb_nci_se_io);
+
+static void st21nfcb_se_wt_timeout(unsigned long data)
+{
+	/*
+	 * No answer from the secure element
+	 * within the defined timeout.
+	 * Let's send a reset request as recovery procedure.
+	 * According to the situation, we first try to send a software reset
+	 * to the secure element. If the next command is still not
+	 * answering in time, we send to the CLF a secure element hardware
+	 * reset request.
+	 */
+	/* hardware reset managed through VCC_UICC_OUT power supply */
+	u8 param = 0x01;
+	struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data;
+	struct st21nfcb_hci_dev  *hdev = info->se_info.hci_dev;
+
+	pr_debug("\n");
+
+	info->se_info.bwi_active = false;
+
+	if (!info->se_info.xch_error) {
+		info->se_info.xch_error = true;
+		st21nfcb_hci_send_event(hdev, ST21NFCB_APDU_READER_GATE,
+				ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0);
+	} else {
+		info->se_info.xch_error = false;
+		st21nfcb_hci_send_event(hdev, ST21NFCB_DEVICE_MGNT_GATE,
+				ST21NFCB_EVT_SE_HARD_RESET, &param, 1);
+	}
+	info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME);
+}
+
+static void st21nfcb_se_activation_timeout(unsigned long data)
+{
+	struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data;
+
+	pr_debug("\n");
+
+	info->se_info.se_active = false;
+
+	complete(&info->se_info.req_completion);
+}
+
+int st21nfcb_se_init(struct nci_dev *ndev)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	init_completion(&info->se_info.req_completion);
+	/* initialize timers */
+	init_timer(&info->se_info.bwi_timer);
+	info->se_info.bwi_timer.data = (unsigned long)info;
+	info->se_info.bwi_timer.function = st21nfcb_se_wt_timeout;
+	info->se_info.bwi_active = false;
+
+	init_timer(&info->se_info.se_active_timer);
+	info->se_info.se_active_timer.data = (unsigned long)info;
+	info->se_info.se_active_timer.function =
+			st21nfcb_se_activation_timeout;
+	info->se_info.se_active = false;
+
+	info->se_info.xch_error = false;
+
+	info->se_info.wt_timeout =
+		ST21NFCB_BWI_TO_TIMEOUT(ST21NFCB_ATR_DEFAULT_BWI);
+
+	return 0;
+}
+EXPORT_SYMBOL(st21nfcb_se_init);
+
+void st21nfcb_se_deinit(struct nci_dev *ndev)
+{
+	struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
+
+	if (info->se_info.bwi_active)
+		del_timer_sync(&info->se_info.bwi_timer);
+	if (info->se_info.se_active)
+		del_timer_sync(&info->se_info.se_active_timer);
+
+	info->se_info.se_active = false;
+	info->se_info.bwi_active = false;
+	st21nfcb_hci_free(info->se_info.hci_dev);
+}
+EXPORT_SYMBOL(st21nfcb_se_deinit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.h b/drivers/nfc/st21nfcb/st21nfcb_se.h
new file mode 100644
index 0000000..979c7dd
--- /dev/null
+++ b/drivers/nfc/st21nfcb/st21nfcb_se.h
@@ -0,0 +1,59 @@
+/*
+ * NCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __LOCAL_ST21NFCB_SE_H_
+#define __LOCAL_ST21NFCB_SE_H_
+
+#include "st21nfcb_hci_core.h"
+
+/*
+ * ref ISO7816-3 chap 8.1. the initial character TS is followed by a
+ * sequence of at most 32 characters.
+ */
+#define ST21NFCB_ESE_MAX_LENGTH		33
+#define ST21NFCB_HCI_HOST_ID_ESE	0xc0
+
+struct st21nfcb_se_info {
+	u8 atr[ST21NFCB_ESE_MAX_LENGTH];
+	struct completion req_completion;
+
+	struct timer_list bwi_timer;
+	int wt_timeout; /* in msecs */
+	bool bwi_active;
+
+	struct timer_list se_active_timer;
+	bool se_active;
+
+	struct st21nfcb_hci_dev *hci_dev;
+
+	bool xch_error;
+
+	se_io_cb_t cb;
+	void *cb_context;
+};
+
+int st21nfcb_se_init(struct nci_dev *ndev);
+void st21nfcb_se_deinit(struct nci_dev *ndev);
+
+int st21nfcb_nci_discover_se(struct nci_dev *ndev);
+int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx);
+int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx);
+int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx,
+					u8 *apdu, size_t apdu_length,
+					se_io_cb_t cb, void *cb_context);
+
+#endif /* __LOCAL_ST21NFCB_NCI_H_ */
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 10/14] NFC: Forward NFC_EVT_TRANSACTION up to user space
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (8 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 09/14] NFC: st21nfcb: Adding support for secure element Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 11/14] NFC: nci: Add support RF_NFCEE_ACTION_NTF Christophe Ricard
                     ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

NFC_EVT_TRANSACTION is not managed so far.

Forward event information up to user space in order to tell an application
that some user action(s) are needed.

Forwarded information are:
- SE host generating the event
- Application IDentifier doing the operation
- Applications parameters

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nfc.h    | 27 +++++++++++++++++++++++++++
 include/uapi/linux/nfc.h |  1 +
 net/nfc/core.c           | 21 +++++++++++++++++++++
 net/nfc/netlink.c        | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 net/nfc/nfc.h            |  2 ++
 5 files changed, 98 insertions(+)

diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index 12adb81..73190e6 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -135,6 +135,31 @@ struct nfc_se {
 	u16 state;
 };
 
+/**
+ * nfc_evt_transaction - A struct for NFC secure element event transaction.
+ *
+ * @aid: The application identifier triggering the event
+ *
+ * @aid_len: The application identifier length [5:16]
+ *
+ * @params: The application parameters transmitted during the transaction
+ *
+ * @params_len: The applications parameters length [0:255]
+ *
+ */
+#define NFC_MIN_AID_LENGTH	5
+#define	NFC_MAX_AID_LENGTH	16
+#define NFC_MAX_PARAMS_LENGTH	255
+
+#define NFC_EVT_TRANSACTION_AID_TAG	0x81
+#define NFC_EVT_TRANSACTION_PARAMS_TAG	0x82
+struct nfc_evt_transaction {
+	u32 aid_len;
+	u8 aid[NFC_MAX_AID_LENGTH];
+	u8 params_len;
+	u8 params[NFC_MAX_PARAMS_LENGTH];
+} __packed;
+
 struct nfc_genl_data {
 	u32 poll_req_portid;
 	struct mutex genl_data_mutex;
@@ -262,6 +287,8 @@ 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_se_transaction(struct nfc_dev *dev, u8 se_idx,
+		       struct nfc_evt_transaction *evt_transaction);
 int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
 int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
 struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx);
diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h
index 8119255..c1e2e63 100644
--- a/include/uapi/linux/nfc.h
+++ b/include/uapi/linux/nfc.h
@@ -183,6 +183,7 @@ enum nfc_attrs {
 	NFC_ATTR_SE_APDU,
 	NFC_ATTR_TARGET_ISO15693_DSFID,
 	NFC_ATTR_TARGET_ISO15693_UID,
+	NFC_ATTR_SE_PARAMS,
 /* private: internal use only */
 	__NFC_ATTR_AFTER_LAST
 };
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 7f1b635..cff3f16 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -932,6 +932,27 @@ int nfc_remove_se(struct nfc_dev *dev, u32 se_idx)
 }
 EXPORT_SYMBOL(nfc_remove_se);
 
+int nfc_se_transaction(struct nfc_dev *dev, u8 se_idx,
+		       struct nfc_evt_transaction *evt_transaction)
+{
+	int rc;
+
+	pr_debug("transaction: %x\n", se_idx);
+
+	device_lock(&dev->dev);
+
+	if (!evt_transaction) {
+		rc = -EPROTO;
+		goto out;
+	}
+
+	rc = nfc_genl_se_transaction(dev, se_idx, evt_transaction);
+out:
+	device_unlock(&dev->dev);
+	return rc;
+}
+EXPORT_SYMBOL(nfc_se_transaction);
+
 static void nfc_release(struct device *d)
 {
 	struct nfc_dev *dev = to_nfc_dev(d);
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 44989fc..2fd6777 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -496,6 +496,53 @@ free_msg:
 	return -EMSGSIZE;
 }
 
+int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx,
+			    struct nfc_evt_transaction *evt_transaction)
+{
+	struct nfc_se *se;
+	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_TRANSACTION);
+	if (!hdr)
+		goto free_msg;
+
+	se = nfc_find_se(dev, se_idx);
+	if (!se)
+		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, se->type) ||
+	    nla_put(msg, NFC_ATTR_SE_AID, evt_transaction->aid_len,
+		    evt_transaction->aid) ||
+	    nla_put(msg, NFC_ATTR_SE_PARAMS, evt_transaction->params_len,
+		    evt_transaction->params))
+		goto nla_put_failure;
+
+	/* evt_transaction is no more used */
+	devm_kfree(&dev->dev, evt_transaction);
+
+	genlmsg_end(msg, hdr);
+
+	genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+free_msg:
+	/* evt_transaction is no more used */
+	devm_kfree(&dev->dev, evt_transaction);
+	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,
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index 88d6006..a8ce80b 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -100,6 +100,8 @@ 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);
+int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx,
+			    struct nfc_evt_transaction *evt_transaction);
 
 struct nfc_dev *nfc_get_device(unsigned int idx);
 
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 11/14] NFC: nci: Add support RF_NFCEE_ACTION_NTF
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (9 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 10/14] NFC: Forward NFC_EVT_TRANSACTION up to user space Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 12/14] NFC: nci: Change NCI state machine to LISTEN_ACTIVE and ignore parameters in rf_intf_activated_ntf Christophe Ricard
                     ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

RF NFCEE Action is the mechanism used to indicate that an action involving one of the NFCEEs
in the device has occurred that may be of interest to the DH.

This may happen for example when doing an RF Transaction with a payment reader.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 include/net/nfc/nci.h |  8 ++++++++
 net/nfc/nci/ntf.c     | 11 +++++++++++
 2 files changed, 19 insertions(+)

diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index deac78b..6c1beb2 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -499,6 +499,14 @@ struct nci_rf_deactivate_ntf {
 	__u8	reason;
 } __packed;
 
+#define NCI_OP_RF_NFCEE_ACTION_NTF	nci_opcode_pack(NCI_GID_RF_MGMT, 0x09)
+struct nci_rf_nfcee_action_ntf {
+	__u8 nfcee_id;
+	__u8 trigger;
+	__u8 supported_data_length;
+	__u8 supported_data[0];
+} __packed;
+
 #define NCI_OP_NFCEE_DISCOVER_NTF nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x00)
 struct nci_nfcee_supported_protocol {
 	__u8	num_protocol;
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 5af0564..83ba1f8 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -736,6 +736,12 @@ exit:
 	nci_req_complete(ndev, status);
 }
 
+static void nci_nfcee_action_ntf_packet(struct nci_dev *ndev,
+					struct sk_buff *skb)
+{
+	pr_debug("\n");
+}
+
 void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u16 ntf_opcode = nci_opcode(skb->data);
@@ -777,6 +783,11 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
 	case NCI_OP_NFCEE_DISCOVER_NTF:
 		nci_nfcee_discover_ntf_packet(ndev, skb);
 		break;
+
+	case NCI_OP_RF_NFCEE_ACTION_NTF:
+		nci_nfcee_action_ntf_packet(ndev, skb);
+		break;
+
 	default:
 		pr_err("unknown ntf opcode 0x%x\n", ntf_opcode);
 		break;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 12/14] NFC: nci: Change NCI state machine to LISTEN_ACTIVE and ignore parameters in rf_intf_activated_ntf
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (10 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 11/14] NFC: nci: Add support RF_NFCEE_ACTION_NTF Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 13/14] NFC: st21nfcb: Add support for HCI event transaction Christophe Ricard
  2015-01-27  0:28   ` [RFC 14/14] NFC: st21nfca: " Christophe Ricard
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

When receiving a intf_activated notification, if the rf interface is
NCI_RF_INTERFACE_NFCEE_DIRECT, we need to ignore the next parameters and
change the NCI state machine to NCI_LISTEN_ACTIVE.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 net/nfc/nci/ntf.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 83ba1f8..d2f8512 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -540,6 +540,13 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
 	pr_debug("rf_tech_specific_params_len %d\n",
 		 ntf.rf_tech_specific_params_len);
 
+	/* If this contains a value of 0x00 (NFCEE Direct RF
+	 * Interface) then all following parameters SHALL contain a
+	 * value of 0 and SHALL be ignored.
+	 */
+	if (ntf.rf_interface == NCI_RF_INTERFACE_NFCEE_DIRECT)
+		goto listen;
+
 	if (ntf.rf_tech_specific_params_len > 0) {
 		switch (ntf.activation_rf_tech_and_mode) {
 		case NCI_NFC_A_PASSIVE_POLL_MODE:
@@ -651,6 +658,7 @@ exit:
 			nci_req_complete(ndev, err);
 		}
 	} else {
+listen:
 		/* Listen mode */
 		atomic_set(&ndev->state, NCI_LISTEN_ACTIVE);
 		if (err == NCI_STATUS_OK &&
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 13/14] NFC: st21nfcb: Add support for HCI event transaction
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (11 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 12/14] NFC: nci: Change NCI state machine to LISTEN_ACTIVE and ignore parameters in rf_intf_activated_ntf Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  2015-01-27  0:28   ` [RFC 14/14] NFC: st21nfca: " Christophe Ricard
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The event transaction notifies the Device Host that an action is required
to manage a specific Secure Element application.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 drivers/nfc/st21nfcb/st21nfcb_se.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.c b/drivers/nfc/st21nfcb/st21nfcb_se.c
index cf65368..6868043 100644
--- a/drivers/nfc/st21nfcb/st21nfcb_se.c
+++ b/drivers/nfc/st21nfcb/st21nfcb_se.c
@@ -287,14 +287,35 @@ static int st21nfcb_hci_connectivity_event_received(struct st21nfcb_hci_dev *hde
 						    struct sk_buff *skb)
 {
 	int r = 0;
+	struct device *dev = &hdev->ndev->nfc_dev->dev;
+	struct nfc_evt_transaction *transaction;
 
 	pr_debug("connectivity gate event: %x\n", event);
 
 	switch (event) {
 	case ST21NFCB_EVT_CONNECTIVITY:
+
 	break;
 	case ST21NFCB_EVT_TRANSACTION:
-	break;
+		if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
+		    skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
+			return -EPROTO;
+
+		transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev,
+					    skb->len - 2, GFP_KERNEL);
+
+		transaction->aid_len = skb->data[1];
+		memcpy(transaction->aid, &skb->data[2], skb->data[1]);
+
+		if (skb->data[transaction->aid_len + 2] !=
+		    NFC_EVT_TRANSACTION_PARAMS_TAG)
+			return -EPROTO;
+
+		transaction->params_len = skb->data[transaction->aid_len + 3];
+		memcpy(transaction->params, skb->data +
+		       transaction->aid_len + 4, transaction->params_len);
+
+		r = nfc_se_transaction(hdev->ndev->nfc_dev, host, transaction);
 	default:
 		return 1;
 	}
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC 14/14] NFC: st21nfca: Add support for HCI event transaction
       [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
                     ` (12 preceding siblings ...)
  2015-01-27  0:28   ` [RFC 13/14] NFC: st21nfcb: Add support for HCI event transaction Christophe Ricard
@ 2015-01-27  0:28   ` Christophe Ricard
  13 siblings, 0 replies; 15+ messages in thread
From: Christophe Ricard @ 2015-01-27  0:28 UTC (permalink / raw)
  To: sameo-VuQAYsv1563Yd54FQh9/CA
  Cc: linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	christophe.ricard-Re5JQEeQqe8AvxtiuMwx3w,
	christophe-h.ricard-qxv4g6HH51o,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The event transaction notifies the Device Host that an action is required
to manage a specific Secure Element application.

Signed-off-by: Christophe Ricard <christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
---
 drivers/nfc/st21nfca/st21nfca_se.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/drivers/nfc/st21nfca/st21nfca_se.c b/drivers/nfc/st21nfca/st21nfca_se.c
index 9b93d39..bd13cac 100644
--- a/drivers/nfc/st21nfca/st21nfca_se.c
+++ b/drivers/nfc/st21nfca/st21nfca_se.c
@@ -301,6 +301,8 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
 				u8 event, struct sk_buff *skb)
 {
 	int r = 0;
+	struct device *dev = &hdev->ndev->dev;
+	struct nfc_evt_transaction *transaction;
 
 	pr_debug("connectivity gate event: %x\n", event);
 
@@ -308,6 +310,25 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
 	case ST21NFCA_EVT_CONNECTIVITY:
 		break;
 	case ST21NFCA_EVT_TRANSACTION:
+		if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
+		    skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
+			return -EPROTO;
+
+		transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev,
+						   skb->len - 2, GFP_KERNEL);
+
+		transaction->aid_len = skb->data[1];
+		memcpy(transaction->aid, &skb->data[2], skb->data[1]);
+
+		if (skb->data[transaction->aid_len + 2] !=
+		    NFC_EVT_TRANSACTION_PARAMS_TAG)
+			return -EPROTO;
+
+		transaction->params_len = skb->data[transaction->aid_len + 3];
+		memcpy(transaction->params, skb->data +
+		       transaction->aid_len + 4, transaction->params_len);
+
+		r = nfc_se_transaction(hdev->ndev, host, transaction);
 		break;
 	default:
 		return 1;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2015-01-27  0:28 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-27  0:28 [RFC 00/14] Secure Element support for NCI based NFC controller, st21nfcb integration and EVT_TRANSACTION Christophe Ricard
     [not found] ` <1422318504-10039-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
2015-01-27  0:28   ` [RFC 01/14] NFC: nci: Add dynamic conn_id NCI concept Christophe Ricard
2015-01-27  0:28   ` [RFC 02/14] NFC: nci: Make nci_request available for nfc driver Christophe Ricard
2015-01-27  0:28   ` [RFC 03/14] NFC: nci: Add NCI NFCEE constant Christophe Ricard
2015-01-27  0:28   ` [RFC 04/14] NFC: nci: Add nci_nfcee_discover handler command/response/notification Christophe Ricard
2015-01-27  0:28   ` [RFC 05/14] NFC: nci: Add nci_nfcee_mode_set handler command/response Christophe Ricard
2015-01-27  0:28   ` [RFC 06/14] NFC: nci: Add nci_core_conn_create " Christophe Ricard
2015-01-27  0:28   ` [RFC 07/14] NFC: nci: Add nci_core_conn_close " Christophe Ricard
2015-01-27  0:28   ` [RFC 08/14] NFC: st21nfcb: Add HCI protocol over NCI protocol support Christophe Ricard
2015-01-27  0:28   ` [RFC 09/14] NFC: st21nfcb: Adding support for secure element Christophe Ricard
2015-01-27  0:28   ` [RFC 10/14] NFC: Forward NFC_EVT_TRANSACTION up to user space Christophe Ricard
2015-01-27  0:28   ` [RFC 11/14] NFC: nci: Add support RF_NFCEE_ACTION_NTF Christophe Ricard
2015-01-27  0:28   ` [RFC 12/14] NFC: nci: Change NCI state machine to LISTEN_ACTIVE and ignore parameters in rf_intf_activated_ntf Christophe Ricard
2015-01-27  0:28   ` [RFC 13/14] NFC: st21nfcb: Add support for HCI event transaction Christophe Ricard
2015-01-27  0:28   ` [RFC 14/14] NFC: st21nfca: " Christophe Ricard

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.