Commit f3e8fb55 authored by Eric Lapuyade's avatar Eric Lapuyade Committed by Samuel Ortiz
Browse files

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: default avatarEric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent e4c4789e
Loading
Loading
Loading
Loading
+42 −15
Original line number Diff line number Diff line
@@ -148,6 +148,9 @@ struct pn544_hci_info {
				 * < 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 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
	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_complete_target_discovered(struct nfc_shdlc *shdlc,
 */
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 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
				memcpy(data, uid, MIFARE_UID_LEN);
			}

			return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
			return nfc_hci_send_cmd_async(hdev,
						      target->hci_reader_gate,
						      PN544_MIFARE_CMD,
						skb->data, skb->len, res_skb);
						      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;
	}
+6 −2
Original line number Diff line number Diff line
@@ -38,8 +38,8 @@ struct nfc_hci_ops {
	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 @@ struct nfc_hci_dev {
	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 */
+2 −2
Original line number Diff line number Diff line
@@ -34,8 +34,8 @@ struct nfc_shdlc_ops {
	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);
};
+39 −18
Original line number Diff line number Diff line
@@ -537,13 +537,37 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev,
{
}

#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 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
	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,
+3 −2
Original line number Diff line number Diff line
@@ -777,12 +777,13 @@ static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev,
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;
}