nfc: nci: Add nci_nfcc_loopback to the nci core
authorChristophe Ricard <christophe.ricard@gmail.com>
Sat, 30 Apr 2016 07:12:52 +0000 (09:12 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Tue, 3 May 2016 23:48:16 +0000 (01:48 +0200)
For test purpose, provide the generic nci loopback function.

Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
include/net/nfc/nci_core.h
net/nfc/nci/core.c

index ebb50d286ef6d679e77f4be3db798b662cdfa46f..87499b6b35d6dd75ea3058449c5db484e2aca611 100644 (file)
@@ -306,6 +306,8 @@ int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
                         size_t params_len,
                         struct core_conn_create_dest_spec_params *params);
 int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id);
+int nci_nfcc_loopback(struct nci_dev *ndev, void *data, size_t data_len,
+                     struct sk_buff **resp);
 
 struct nci_hci_dev *nci_hci_allocate(struct nci_dev *ndev);
 int nci_hci_send_event(struct nci_dev *ndev, u8 gate, u8 event,
index 74f2d54df4fc100606ee783e2ad3607fb17fd057..61fff422424f7405e4f39f0d6ed01f32c8ffdbb1 100644 (file)
@@ -400,6 +400,83 @@ int nci_core_init(struct nci_dev *ndev)
 }
 EXPORT_SYMBOL(nci_core_init);
 
+struct nci_loopback_data {
+       u8 conn_id;
+       struct sk_buff *data;
+};
+
+static void nci_send_data_req(struct nci_dev *ndev, unsigned long opt)
+{
+       struct nci_loopback_data *data = (struct nci_loopback_data *)opt;
+
+       nci_send_data(ndev, data->conn_id, data->data);
+}
+
+static void nci_nfcc_loopback_cb(void *context, struct sk_buff *skb, int err)
+{
+       struct nci_dev *ndev = (struct nci_dev *)context;
+       struct nci_conn_info    *conn_info;
+
+       conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id);
+       if (!conn_info) {
+               nci_req_complete(ndev, NCI_STATUS_REJECTED);
+               return;
+       }
+
+       conn_info->rx_skb = skb;
+
+       nci_req_complete(ndev, NCI_STATUS_OK);
+}
+
+int nci_nfcc_loopback(struct nci_dev *ndev, void *data, size_t data_len,
+                     struct sk_buff **resp)
+{
+       int r;
+       struct nci_loopback_data loopback_data;
+       struct nci_conn_info *conn_info;
+       struct sk_buff *skb;
+       int conn_id = nci_get_conn_info_by_dest_type_params(ndev,
+                                       NCI_DESTINATION_NFCC_LOOPBACK, NULL);
+
+       if (conn_id < 0) {
+               r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCC_LOOPBACK,
+                                        0, 0, NULL);
+               if (r != NCI_STATUS_OK)
+                       return r;
+
+               conn_id = nci_get_conn_info_by_dest_type_params(ndev,
+                                       NCI_DESTINATION_NFCC_LOOPBACK,
+                                       NULL);
+       }
+
+       conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
+       if (!conn_info)
+               return -EPROTO;
+
+       /* store cb and context to be used on receiving data */
+       conn_info->data_exchange_cb = nci_nfcc_loopback_cb;
+       conn_info->data_exchange_cb_context = ndev;
+
+       skb = nci_skb_alloc(ndev, NCI_DATA_HDR_SIZE + data_len, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_reserve(skb, NCI_DATA_HDR_SIZE);
+       memcpy(skb_put(skb, data_len), data, data_len);
+
+       loopback_data.conn_id = conn_id;
+       loopback_data.data = skb;
+
+       ndev->cur_conn_id = conn_id;
+       r = nci_request(ndev, nci_send_data_req, (unsigned long)&loopback_data,
+                       msecs_to_jiffies(NCI_DATA_TIMEOUT));
+       if (r == NCI_STATUS_OK && resp)
+               *resp = conn_info->rx_skb;
+
+       return r;
+}
+EXPORT_SYMBOL(nci_nfcc_loopback);
+
 static int nci_open_device(struct nci_dev *ndev)
 {
        int rc = 0;