NFC: Modified hci_transceive to become an asynchronous operation

This enables the completion callback to be called from a different
context, preventing a possible deadlock if the callback resulted in the
invocation of a nested call to the currently locked nfc_dev.
This is also more in line with the im_transceive nfc_ops for NFC Core or
NCI drivers which already behave asynchronously.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c
index d90aecf..a176d1f7 100644
--- a/drivers/nfc/pn544_hci.c
+++ b/drivers/nfc/pn544_hci.c
@@ -148,6 +148,9 @@
 				 * < 0 if hardware error occured (e.g. i2c err)
 				 * and prevents normal operation.
 				 */
+	int async_cb_type;
+	data_exchange_cb_t async_cb;
+	void *async_cb_context;
 };
 
 static void pn544_hci_platform_init(struct pn544_hci_info *info)
@@ -731,6 +734,26 @@
 	return r;
 }
 
+#define PN544_CB_TYPE_READER_F 1
+
+static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb,
+				       int err)
+{
+	struct pn544_hci_info *info = context;
+
+	switch (info->async_cb_type) {
+	case PN544_CB_TYPE_READER_F:
+		if (err == 0)
+			skb_pull(skb, 1);
+		info->async_cb(info->async_cb_context, skb, err);
+		break;
+	default:
+		if (err == 0)
+			kfree_skb(skb);
+		break;
+	}
+}
+
 #define MIFARE_CMD_AUTH_KEY_A	0x60
 #define MIFARE_CMD_AUTH_KEY_B	0x61
 #define MIFARE_CMD_HEADER	2
@@ -744,11 +767,11 @@
  */
 static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
 				   struct nfc_target *target,
-				   struct sk_buff *skb,
-				   struct sk_buff **res_skb)
+				   struct sk_buff *skb, data_exchange_cb_t cb,
+				   void *cb_context)
 {
+	struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
 	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
-	int r;
 
 	pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,
 		target->hci_reader_gate);
@@ -773,25 +796,29 @@
 				memcpy(data, uid, MIFARE_UID_LEN);
 			}
 
-			return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-						PN544_MIFARE_CMD,
-						skb->data, skb->len, res_skb);
+			return nfc_hci_send_cmd_async(hdev,
+						      target->hci_reader_gate,
+						      PN544_MIFARE_CMD,
+						      skb->data, skb->len,
+						      cb, cb_context);
 		} else
 			return 1;
 	case PN544_RF_READER_F_GATE:
 		*skb_push(skb, 1) = 0;
 		*skb_push(skb, 1) = 0;
 
-		r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-				     PN544_FELICA_RAW,
-				     skb->data, skb->len, res_skb);
-		if (r == 0)
-			skb_pull(*res_skb, 1);
-		return r;
+		info->async_cb_type = PN544_CB_TYPE_READER_F;
+		info->async_cb = cb;
+		info->async_cb_context = cb_context;
+
+		return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+					      PN544_FELICA_RAW, skb->data,
+					      skb->len,
+					      pn544_hci_data_exchange_cb, info);
 	case PN544_RF_READER_JEWEL_GATE:
-		return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-					PN544_JEWEL_RAW_CMD,
-					skb->data, skb->len, res_skb);
+		return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+					      PN544_JEWEL_RAW_CMD, skb->data,
+					      skb->len, cb, cb_context);
 	default:
 		return 1;
 	}
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h
index 54ba2ca..6cee6e2 100644
--- a/include/net/nfc/hci.h
+++ b/include/net/nfc/hci.h
@@ -38,8 +38,8 @@
 	int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
 					   struct nfc_target *target);
 	int (*data_exchange) (struct nfc_hci_dev *hdev,
-			      struct nfc_target *target,
-			      struct sk_buff *skb, struct sk_buff **res_skb);
+			      struct nfc_target *target, struct sk_buff *skb,
+			      data_exchange_cb_t cb, void *cb_context);
 	int (*check_presence)(struct nfc_hci_dev *hdev,
 			      struct nfc_target *target);
 };
@@ -103,6 +103,10 @@
 	u8 hw_mpw;
 	u8 hw_software;
 	u8 hw_bsid;
+
+	int async_cb_type;
+	data_exchange_cb_t async_cb;
+	void *async_cb_context;
 };
 
 /* hci device allocation */
diff --git a/include/net/nfc/shdlc.h b/include/net/nfc/shdlc.h
index 3424273..fe5e198 100644
--- a/include/net/nfc/shdlc.h
+++ b/include/net/nfc/shdlc.h
@@ -34,8 +34,8 @@
 	int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate,
 					   struct nfc_target *target);
 	int (*data_exchange) (struct nfc_shdlc *shdlc,
-			      struct nfc_target *target,
-			      struct sk_buff *skb, struct sk_buff **res_skb);
+			      struct nfc_target *target, struct sk_buff *skb,
+			      data_exchange_cb_t cb, void *cb_context);
 	int (*check_presence)(struct nfc_shdlc *shdlc,
 			      struct nfc_target *target);
 };
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index e387c86..dc57e3d 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -537,13 +537,37 @@
 {
 }
 
+#define HCI_CB_TYPE_TRANSCEIVE 1
+
+static void hci_transceive_cb(void *context, struct sk_buff *skb, int err)
+{
+	struct nfc_hci_dev *hdev = context;
+
+	switch (hdev->async_cb_type) {
+	case HCI_CB_TYPE_TRANSCEIVE:
+		/*
+		 * TODO: Check RF Error indicator to make sure data is valid.
+		 * It seems that HCI cmd can complete without error, but data
+		 * can be invalid if an RF error occured? Ignore for now.
+		 */
+		if (err == 0)
+			skb_trim(skb, skb->len - 1); /* RF Err ind */
+
+		hdev->async_cb(hdev->async_cb_context, skb, err);
+		break;
+	default:
+		if (err == 0)
+			kfree_skb(skb);
+		break;
+	}
+}
+
 static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
 			  struct sk_buff *skb, data_exchange_cb_t cb,
 			  void *cb_context)
 {
 	struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 	int r;
-	struct sk_buff *res_skb = NULL;
 
 	pr_debug("target_idx=%d\n", target->idx);
 
@@ -551,40 +575,37 @@
 	case NFC_HCI_RF_READER_A_GATE:
 	case NFC_HCI_RF_READER_B_GATE:
 		if (hdev->ops->data_exchange) {
-			r = hdev->ops->data_exchange(hdev, target, skb,
-						     &res_skb);
+			r = hdev->ops->data_exchange(hdev, target, skb, cb,
+						     cb_context);
 			if (r <= 0)	/* handled */
 				break;
 		}
 
 		*skb_push(skb, 1) = 0;	/* CTR, see spec:10.2.2.1 */
-		r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-				     NFC_HCI_WR_XCHG_DATA,
-				     skb->data, skb->len, &res_skb);
-		/*
-		 * TODO: Check RF Error indicator to make sure data is valid.
-		 * It seems that HCI cmd can complete without error, but data
-		 * can be invalid if an RF error occured? Ignore for now.
-		 */
-		if (r == 0)
-			skb_trim(res_skb, res_skb->len - 1); /* RF Err ind */
+
+		hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE;
+		hdev->async_cb = cb;
+		hdev->async_cb_context = cb_context;
+
+		r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+					   NFC_HCI_WR_XCHG_DATA, skb->data,
+					   skb->len, hci_transceive_cb, hdev);
 		break;
 	default:
 		if (hdev->ops->data_exchange) {
-			r = hdev->ops->data_exchange(hdev, target, skb,
-						     &res_skb);
+			r = hdev->ops->data_exchange(hdev, target, skb, cb,
+						     cb_context);
 			if (r == 1)
 				r = -ENOTSUPP;
 		}
 		else
 			r = -ENOTSUPP;
+		break;
 	}
 
 	kfree_skb(skb);
 
-	cb(cb_context, res_skb, r);
-
-	return 0;
+	return r;
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c
index 9357ba7..c63af7d 100644
--- a/net/nfc/hci/shdlc.c
+++ b/net/nfc/hci/shdlc.c
@@ -777,12 +777,13 @@
 static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev,
 				   struct nfc_target *target,
 				   struct sk_buff *skb,
-				   struct sk_buff **res_skb)
+				   data_exchange_cb_t cb, void *cb_context)
 {
 	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
 
 	if (shdlc->ops->data_exchange)
-		return shdlc->ops->data_exchange(shdlc, target, skb, res_skb);
+		return shdlc->ops->data_exchange(shdlc, target, skb, cb,
+						 cb_context);
 
 	return -EPERM;
 }