NFC: Modified hci_transceive to become an asynchronous operation
authorEric Lapuyade <eric.lapuyade@linux.intel.com>
Tue, 11 Sep 2012 08:43:50 +0000 (10:43 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 24 Sep 2012 22:17:25 +0000 (00:17 +0200)
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>
drivers/nfc/pn544_hci.c
include/net/nfc/hci.h
include/net/nfc/shdlc.h
net/nfc/hci/core.c
net/nfc/hci/shdlc.c

index d90aecfce73910357355863887f5aecd00822504..a176d1f7bbf3e5a89da7e97fe8104e11d2cd846a 100644 (file)
@@ -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,
-                                               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;
        }
index 54ba2caab4a67f09e46094f6151086f09cf2b239..6cee6e21fc465d1485e541a74b8fef3ac9a373e5 100644 (file)
@@ -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 */
index 342427362989ca399b354502bd9b3155f384ebc1..fe5e19829352cfb8f4dde56666330147b79bd47f 100644 (file)
@@ -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);
 };
index e387c86e0cc7f73866b09854178c0f9885034495..dc57e3dc15a470111b590ea6f25906c7d6bdd326 100644 (file)
@@ -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,
index 9357ba7362f66d04cdc6e6de05434b7095ea00cd..c63af7d3e859ae75b66fb90d066db1676b88676a 100644 (file)
@@ -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;
 }