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

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
...@@ -148,6 +148,9 @@ struct pn544_hci_info { ...@@ -148,6 +148,9 @@ struct pn544_hci_info {
* < 0 if hardware error occured (e.g. i2c err) * < 0 if hardware error occured (e.g. i2c err)
* and prevents normal operation. * 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) 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, ...@@ -731,6 +734,26 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
return r; 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_A 0x60
#define MIFARE_CMD_AUTH_KEY_B 0x61 #define MIFARE_CMD_AUTH_KEY_B 0x61
#define MIFARE_CMD_HEADER 2 #define MIFARE_CMD_HEADER 2
...@@ -744,11 +767,11 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc, ...@@ -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, static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
struct nfc_target *target, struct nfc_target *target,
struct sk_buff *skb, struct sk_buff *skb, data_exchange_cb_t cb,
struct sk_buff **res_skb) void *cb_context)
{ {
struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
int r;
pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__, pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,
target->hci_reader_gate); target->hci_reader_gate);
...@@ -773,25 +796,29 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc, ...@@ -773,25 +796,29 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
memcpy(data, uid, MIFARE_UID_LEN); 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, PN544_MIFARE_CMD,
skb->data, skb->len, res_skb); skb->data, skb->len,
cb, cb_context);
} else } else
return 1; return 1;
case PN544_RF_READER_F_GATE: case PN544_RF_READER_F_GATE:
*skb_push(skb, 1) = 0; *skb_push(skb, 1) = 0;
*skb_push(skb, 1) = 0; *skb_push(skb, 1) = 0;
r = nfc_hci_send_cmd(hdev, target->hci_reader_gate, info->async_cb_type = PN544_CB_TYPE_READER_F;
PN544_FELICA_RAW, info->async_cb = cb;
skb->data, skb->len, res_skb); info->async_cb_context = cb_context;
if (r == 0)
skb_pull(*res_skb, 1); return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
return r; PN544_FELICA_RAW, skb->data,
skb->len,
pn544_hci_data_exchange_cb, info);
case PN544_RF_READER_JEWEL_GATE: case PN544_RF_READER_JEWEL_GATE:
return nfc_hci_send_cmd(hdev, target->hci_reader_gate, return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
PN544_JEWEL_RAW_CMD, PN544_JEWEL_RAW_CMD, skb->data,
skb->data, skb->len, res_skb); skb->len, cb, cb_context);
default: default:
return 1; return 1;
} }
......
...@@ -38,8 +38,8 @@ struct nfc_hci_ops { ...@@ -38,8 +38,8 @@ struct nfc_hci_ops {
int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate, int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
struct nfc_target *target); struct nfc_target *target);
int (*data_exchange) (struct nfc_hci_dev *hdev, int (*data_exchange) (struct nfc_hci_dev *hdev,
struct nfc_target *target, struct nfc_target *target, struct sk_buff *skb,
struct sk_buff *skb, struct sk_buff **res_skb); data_exchange_cb_t cb, void *cb_context);
int (*check_presence)(struct nfc_hci_dev *hdev, int (*check_presence)(struct nfc_hci_dev *hdev,
struct nfc_target *target); struct nfc_target *target);
}; };
...@@ -103,6 +103,10 @@ struct nfc_hci_dev { ...@@ -103,6 +103,10 @@ struct nfc_hci_dev {
u8 hw_mpw; u8 hw_mpw;
u8 hw_software; u8 hw_software;
u8 hw_bsid; u8 hw_bsid;
int async_cb_type;
data_exchange_cb_t async_cb;
void *async_cb_context;
}; };
/* hci device allocation */ /* hci device allocation */
......
...@@ -34,8 +34,8 @@ struct nfc_shdlc_ops { ...@@ -34,8 +34,8 @@ struct nfc_shdlc_ops {
int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate, int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate,
struct nfc_target *target); struct nfc_target *target);
int (*data_exchange) (struct nfc_shdlc *shdlc, int (*data_exchange) (struct nfc_shdlc *shdlc,
struct nfc_target *target, struct nfc_target *target, struct sk_buff *skb,
struct sk_buff *skb, struct sk_buff **res_skb); data_exchange_cb_t cb, void *cb_context);
int (*check_presence)(struct nfc_shdlc *shdlc, int (*check_presence)(struct nfc_shdlc *shdlc,
struct nfc_target *target); struct nfc_target *target);
}; };
......
...@@ -537,13 +537,37 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev, ...@@ -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, static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
struct sk_buff *skb, data_exchange_cb_t cb, struct sk_buff *skb, data_exchange_cb_t cb,
void *cb_context) void *cb_context)
{ {
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
int r; int r;
struct sk_buff *res_skb = NULL;
pr_debug("target_idx=%d\n", target->idx); 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, ...@@ -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_A_GATE:
case NFC_HCI_RF_READER_B_GATE: case NFC_HCI_RF_READER_B_GATE:
if (hdev->ops->data_exchange) { if (hdev->ops->data_exchange) {
r = hdev->ops->data_exchange(hdev, target, skb, r = hdev->ops->data_exchange(hdev, target, skb, cb,
&res_skb); cb_context);
if (r <= 0) /* handled */ if (r <= 0) /* handled */
break; break;
} }
*skb_push(skb, 1) = 0; /* CTR, see spec:10.2.2.1 */ *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, hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE;
skb->data, skb->len, &res_skb); hdev->async_cb = cb;
/* hdev->async_cb_context = cb_context;
* TODO: Check RF Error indicator to make sure data is valid.
* It seems that HCI cmd can complete without error, but data r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
* can be invalid if an RF error occured? Ignore for now. NFC_HCI_WR_XCHG_DATA, skb->data,
*/ skb->len, hci_transceive_cb, hdev);
if (r == 0)
skb_trim(res_skb, res_skb->len - 1); /* RF Err ind */
break; break;
default: default:
if (hdev->ops->data_exchange) { if (hdev->ops->data_exchange) {
r = hdev->ops->data_exchange(hdev, target, skb, r = hdev->ops->data_exchange(hdev, target, skb, cb,
&res_skb); cb_context);
if (r == 1) if (r == 1)
r = -ENOTSUPP; r = -ENOTSUPP;
} }
else else
r = -ENOTSUPP; r = -ENOTSUPP;
break;
} }
kfree_skb(skb); kfree_skb(skb);
cb(cb_context, res_skb, r); return r;
return 0;
} }
static int hci_check_presence(struct nfc_dev *nfc_dev, static int hci_check_presence(struct nfc_dev *nfc_dev,
......
...@@ -777,12 +777,13 @@ static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev, ...@@ -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, static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev,
struct nfc_target *target, struct nfc_target *target,
struct sk_buff *skb, 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); struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
if (shdlc->ops->data_exchange) 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; return -EPERM;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment