From f36acc334fb86da3761583dce50faa17a4cbd4de Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 22 Mar 2016 23:17:00 +0000 Subject: [PATCH 01/31] NFC: set info->ram_patch to NULL when it is released When info->ram_patch is released info->otp_patch is being set to NULL rather than info->ram_patch. I believe this is a cut-n-paste bug from almost identical code proceeding it that uses the same idiom for info->otp_patch. Signed-off-by: Colin Ian King Signed-off-by: Samuel Ortiz --- drivers/nfc/fdp/fdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c index e44a7a2f4061..d93d3145365f 100644 --- a/drivers/nfc/fdp/fdp.c +++ b/drivers/nfc/fdp/fdp.c @@ -345,7 +345,7 @@ static void fdp_nci_release_firmware(struct nci_dev *ndev) if (info->ram_patch) { release_firmware(info->ram_patch); - info->otp_patch = NULL; + info->ram_patch = NULL; } } From f86dec94e3a86c992a637df1c301a4df25a85801 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 15 Apr 2016 18:14:25 +0200 Subject: [PATCH 02/31] NFC: hci: delete unused nfc_llc_get_rx_head_tail_room() It used to be EXPORTed, but then EXPORT usage was cleaned up (in 2012), without noticing that the function has no users at all (and curiously, never had any users). Delete it. While at it, remove non-static "inline" hints on nearby functions: these hints don't work across compilation units anyway, and these functions are not used in their .c file, thus they are never inlined. IOW: "inline" here does not help in any way. Signed-off-by: Denys Vlasenko CC: Samuel Ortiz CC: Christophe Ricard CC: linux-wireless@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Samuel Ortiz --- include/net/nfc/llc.h | 4 ---- net/nfc/hci/llc.c | 17 +++++------------ 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h index c25fbdee0d61..7ecb45757897 100644 --- a/include/net/nfc/llc.h +++ b/include/net/nfc/llc.h @@ -37,10 +37,6 @@ struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev, int tx_tailroom, llc_failure_t llc_failure); void nfc_llc_free(struct nfc_llc *llc); -void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom, - int *rx_tailroom); - - int nfc_llc_start(struct nfc_llc *llc); int nfc_llc_stop(struct nfc_llc *llc); void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb); diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index 1399a03fa6e6..3d699cbc7435 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c @@ -133,36 +133,29 @@ void nfc_llc_free(struct nfc_llc *llc) kfree(llc); } -inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom, - int *rx_tailroom) -{ - *rx_headroom = llc->rx_headroom; - *rx_tailroom = llc->rx_tailroom; -} - -inline int nfc_llc_start(struct nfc_llc *llc) +int nfc_llc_start(struct nfc_llc *llc) { return llc->ops->start(llc); } EXPORT_SYMBOL(nfc_llc_start); -inline int nfc_llc_stop(struct nfc_llc *llc) +int nfc_llc_stop(struct nfc_llc *llc) { return llc->ops->stop(llc); } EXPORT_SYMBOL(nfc_llc_stop); -inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) +void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) { llc->ops->rcv_from_drv(llc, skb); } -inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) +int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) { return llc->ops->xmit_from_hci(llc, skb); } -inline void *nfc_llc_get_data(struct nfc_llc *llc) +void *nfc_llc_get_data(struct nfc_llc *llc) { return llc->data; } From 6d2f70cae481a2e8e85a244d3b2d36828dadff83 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 17 May 2016 10:32:19 +0300 Subject: [PATCH 03/31] NFC: pn533: double free on error in probe() We can't pass devm_ allocated pointers to kfree() because they will be freed again after the drive is unloaded. Signed-off-by: Dan Carpenter Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533/usb.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c index 8ca060324b6a..33ed78be2750 100644 --- a/drivers/nfc/pn533/usb.c +++ b/drivers/nfc/pn533/usb.c @@ -464,10 +464,8 @@ static int pn533_usb_probe(struct usb_interface *interface, return -ENOMEM; in_buf = kzalloc(in_buf_len, GFP_KERNEL); - if (!in_buf) { - rc = -ENOMEM; - goto out_free_phy; - } + if (!in_buf) + return -ENOMEM; phy->udev = usb_get_dev(interface_to_usbdev(interface)); phy->interface = interface; @@ -554,8 +552,7 @@ static int pn533_usb_probe(struct usb_interface *interface, usb_free_urb(phy->out_urb); usb_put_dev(phy->udev); kfree(in_buf); -out_free_phy: - kfree(phy); + return rc; } From fa1ce54ea38f7f83473fce62e64fefbd7ebd170e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 5 Jun 2016 11:17:10 +0200 Subject: [PATCH 04/31] NFC: fdp: Detect errors from fdp_nci_create_conn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/nfc/fdp/fdp.c: In function ‘fdp_nci_patch_otp’: drivers/nfc/fdp/fdp.c:373: warning: comparison is always false due to limited range of data type drivers/nfc/fdp/fdp.c: In function ‘fdp_nci_patch_ram’: drivers/nfc/fdp/fdp.c:444: warning: comparison is always false due to limited range of data type fdp_nci_create_conn() may return a negative error code, which is silently ignored by assigning it to a u8. Change conn_id from u8 to int to fix this. Fixes: a06347c04c13e380 ("NFC: Add Intel Fields Peak NFC solution driver") Signed-off-by: Geert Uytterhoeven Signed-off-by: Samuel Ortiz --- drivers/nfc/fdp/fdp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c index d93d3145365f..7c1eaea3b685 100644 --- a/drivers/nfc/fdp/fdp.c +++ b/drivers/nfc/fdp/fdp.c @@ -353,7 +353,7 @@ static int fdp_nci_patch_otp(struct nci_dev *ndev) { struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - u8 conn_id; + int conn_id; int r = 0; if (info->otp_version >= info->otp_patch_version) @@ -424,7 +424,7 @@ static int fdp_nci_patch_ram(struct nci_dev *ndev) { struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - u8 conn_id; + int conn_id; int r = 0; if (info->ram_version >= info->ram_patch_version) From 58d46f538b602d4a93fbae945e5c844a90c01f14 Mon Sep 17 00:00:00 2001 From: Geoff Lansberry Date: Mon, 18 Apr 2016 15:48:39 -0400 Subject: [PATCH 05/31] NFC: trf7970a: add TI recommended write of zero to Register 0x18 Signed-off-by: Geoff Lansberry Signed-off-by: Samuel Ortiz --- drivers/nfc/trf7970a.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index 10842b7051b3..26c9dbbccb0c 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -1048,6 +1048,10 @@ static int trf7970a_init(struct trf7970a *trf) if (ret) goto err_out; + ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, 0); + if (ret) + goto err_out; + usleep_range(1000, 2000); trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON; From a81ba50a89930a96e34862cf236b4f4461e741ae Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 7 Jun 2016 16:21:51 +0200 Subject: [PATCH 06/31] NFC: port100: Explicitly set NFC-F framing for NFC-DEP When setting the driver framing as NFC_DIGITAL_FRAMING_NFCF_NFC_DEP it used to be already configured as NFC_DIGITAL_FRAMING_NFCF which is the same. So this entry was empty in the in_protocols table. Now that the digital stack can handle PLS requests, it can be changed on the fly from NFC_DIGITAL_FRAMING_NFCA_NFC_DEP. This patch explicitly defines the framing configuration values for NFC_DIGITAL_FRAMING_NFCF_NFC_DEP. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 87d509996704..2d4bbe3fad57 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -343,7 +343,26 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = { }, [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = { /* nfc_digital_framing_nfcf */ - { PORT100_IN_PROT_END, 0 }, + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 18 }, + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 1 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 0 }, + { PORT100_IN_PROT_CHECK_PARITY, 0 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, }, [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = { { PORT100_IN_PROT_END, 0 }, From 7854a44526de84142e367f08288c9f3a33c4c8ee Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 7 Jun 2016 16:21:52 +0200 Subject: [PATCH 07/31] NFC: digital: Add a delay between poll cycles This replaces the polling work struct with a delayed work struct and add a 10 ms delay between 2 poll cycles. This avoids to flood the device with 'switch off'/'switch on' commands. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 2 +- net/nfc/digital_core.c | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 0ae101eef0f4..506e3f6eabef 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -220,7 +220,7 @@ struct nfc_digital_dev { struct list_head cmd_queue; struct mutex cmd_lock; - struct work_struct poll_work; + struct delayed_work poll_work; u8 curr_protocol; u8 curr_rf_tech; diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index dd9003f38822..27769ac89d27 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -30,6 +30,9 @@ #define DIGITAL_PROTO_ISO15693_RF_TECH NFC_PROTO_ISO15693_MASK +/* Delay between each poll frame (ms) */ +#define DIGITAL_POLL_INTERVAL 10 + struct digital_cmd { struct list_head queue; @@ -419,7 +422,8 @@ void digital_poll_next_tech(struct nfc_digital_dev *ddev) mutex_unlock(&ddev->poll_lock); - schedule_work(&ddev->poll_work); + schedule_delayed_work(&ddev->poll_work, + msecs_to_jiffies(DIGITAL_POLL_INTERVAL)); } static void digital_wq_poll(struct work_struct *work) @@ -428,7 +432,7 @@ static void digital_wq_poll(struct work_struct *work) struct digital_poll_tech *poll_tech; struct nfc_digital_dev *ddev = container_of(work, struct nfc_digital_dev, - poll_work); + poll_work.work); mutex_lock(&ddev->poll_lock); if (!ddev->poll_tech_count) { @@ -543,7 +547,7 @@ static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols, return -EINVAL; } - schedule_work(&ddev->poll_work); + schedule_delayed_work(&ddev->poll_work, 0); return 0; } @@ -564,7 +568,7 @@ static void digital_stop_poll(struct nfc_dev *nfc_dev) mutex_unlock(&ddev->poll_lock); - cancel_work_sync(&ddev->poll_work); + cancel_delayed_work_sync(&ddev->poll_work); digital_abort_cmd(ddev); } @@ -770,7 +774,7 @@ struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops, INIT_WORK(&ddev->cmd_complete_work, digital_wq_cmd_complete); mutex_init(&ddev->poll_lock); - INIT_WORK(&ddev->poll_work, digital_wq_poll); + INIT_DELAYED_WORK(&ddev->poll_work, digital_wq_poll); if (supported_protocols & NFC_PROTO_JEWEL_MASK) ddev->protocols |= NFC_PROTO_JEWEL_MASK; @@ -832,7 +836,7 @@ void nfc_digital_unregister_device(struct nfc_digital_dev *ddev) ddev->poll_tech_count = 0; mutex_unlock(&ddev->poll_lock); - cancel_work_sync(&ddev->poll_work); + cancel_delayed_work_sync(&ddev->poll_work); cancel_work_sync(&ddev->cmd_work); cancel_work_sync(&ddev->cmd_complete_work); From 806bfe31c96f77e917eac476ba87164f7bbd1366 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 7 Jun 2016 16:21:53 +0200 Subject: [PATCH 08/31] NFC: llcp: Use dynamic debug for hex dump LLCP skb tx and rx functions now use print_hex_dump_debug() making these verbose traces controllable using dynamic debug. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/llcp_core.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index 98876274a1ee..e69786c6804c 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -732,9 +732,8 @@ static void nfc_llcp_tx_work(struct work_struct *work) int ret; pr_debug("Sending pending skb\n"); - print_hex_dump(KERN_DEBUG, "LLCP Tx: ", - DUMP_PREFIX_OFFSET, 16, 1, - skb->data, skb->len, true); + print_hex_dump_debug("LLCP Tx: ", DUMP_PREFIX_OFFSET, + 16, 1, skb->data, skb->len, true); if (ptype == LLCP_PDU_DISC && sk != NULL && sk->sk_state == LLCP_DISCONNECTING) { @@ -1412,8 +1411,8 @@ static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb) pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap); if (ptype != LLCP_PDU_SYMM) - print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET, - 16, 1, skb->data, skb->len, true); + print_hex_dump_debug("LLCP Rx: ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb->len, true); switch (ptype) { case LLCP_PDU_SYMM: From 204bddcb508fe3bca5c97a9f528bafd7ba8fcec8 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 23 Jun 2016 11:20:26 +0200 Subject: [PATCH 09/31] NFC: nfcsim: Make use of the Digital layer With this complete rewrite, the loopback nfcsim driver now relies on the Digital layer of the nfc stack. As with the previous version, 2 nfc devices are declared when the driver is initialized. The driver supports the NFC_DEP protocol in NFC-A and NFC-F technologies. The 2 devices are using a pair of virtual links for sk_buff exchange. The out-link of one device is the in-link of the other and conversely. To receive data, a device calls nfcsim_link_recv_skb() on its in-link and waits for incoming data on a wait queue. To send data, a device calls nfcsim_link_send_skb() on its out-link which stores the passed skb and signals its wait queue. If the peer device was in the nfcsim_link_recv_skb() call, it will be signaled and will be able to pass the received sk_buff up to the Digital layer. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcsim.c | 852 +++++++++++++++++++------------------------ 1 file changed, 378 insertions(+), 474 deletions(-) diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 93aaca586858..40b4846509f4 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -18,523 +18,427 @@ #include #include #include +#include -#define DEV_ERR(_dev, fmt, args...) nfc_err(&_dev->nfc_dev->dev, \ - "%s: " fmt, __func__, ## args) +#define NFCSIM_ERR(d, fmt, args...) nfc_err(&d->nfc_digital_dev->nfc_dev->dev, \ + "%s: " fmt, __func__, ## args) -#define DEV_DBG(_dev, fmt, args...) dev_dbg(&_dev->nfc_dev->dev, \ - "%s: " fmt, __func__, ## args) +#define NFCSIM_DBG(d, fmt, args...) dev_dbg(&d->nfc_digital_dev->nfc_dev->dev, \ + "%s: " fmt, __func__, ## args) -#define NFCSIM_VERSION "0.1" +#define NFCSIM_VERSION "0.2" -#define NFCSIM_POLL_NONE 0 -#define NFCSIM_POLL_INITIATOR 1 -#define NFCSIM_POLL_TARGET 2 -#define NFCSIM_POLL_DUAL (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET) +#define NFCSIM_MODE_NONE 0 +#define NFCSIM_MODE_INITIATOR 1 +#define NFCSIM_MODE_TARGET 2 -#define RX_DEFAULT_DELAY 5 +#define NFCSIM_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \ + NFC_DIGITAL_DRV_CAPS_TG_CRC) struct nfcsim { - struct nfc_dev *nfc_dev; + struct nfc_digital_dev *nfc_digital_dev; + struct work_struct recv_work; + struct delayed_work send_work; + + struct nfcsim_link *link_in; + struct nfcsim_link *link_out; + + bool up; + u8 mode; + u8 rf_tech; + + u16 recv_timeout; + + nfc_digital_cmd_complete_t cb; + void *arg; +}; + +struct nfcsim_link { struct mutex lock; - struct delayed_work recv_work; + u8 rf_tech; + u8 mode; - struct sk_buff *clone_skb; + u8 shutdown; - struct delayed_work poll_work; - u8 polling_mode; - u8 curr_polling_mode; - - u8 shutting_down; - - u8 up; - - u8 initiator; - - u32 rx_delay; - - data_exchange_cb_t cb; - void *cb_context; - - struct nfcsim *peer_dev; + struct sk_buff *skb; + wait_queue_head_t recv_wait; + u8 cond; }; +static struct nfcsim_link *nfcsim_link_new(void) +{ + struct nfcsim_link *link; + + link = kzalloc(sizeof(struct nfcsim_link), GFP_KERNEL); + if (!link) + return NULL; + + mutex_init(&link->lock); + init_waitqueue_head(&link->recv_wait); + + return link; +} + +static void nfcsim_link_free(struct nfcsim_link *link) +{ + dev_kfree_skb(link->skb); + kfree(link); +} + +static void nfcsim_link_recv_wake(struct nfcsim_link *link) +{ + link->cond = 1; + wake_up_interruptible(&link->recv_wait); +} + +static void nfcsim_link_set_skb(struct nfcsim_link *link, struct sk_buff *skb, + u8 rf_tech, u8 mode) +{ + mutex_lock(&link->lock); + + dev_kfree_skb(link->skb); + link->skb = skb; + link->rf_tech = rf_tech; + link->mode = mode; + + mutex_unlock(&link->lock); +} + +static void nfcsim_link_recv_cancel(struct nfcsim_link *link) +{ + mutex_lock(&link->lock); + + link->mode = NFCSIM_MODE_NONE; + + mutex_unlock(&link->lock); + + nfcsim_link_recv_wake(link); +} + +static void nfcsim_link_shutdown(struct nfcsim_link *link) +{ + mutex_lock(&link->lock); + + link->shutdown = 1; + link->mode = NFCSIM_MODE_NONE; + + mutex_unlock(&link->lock); + + nfcsim_link_recv_wake(link); +} + +static struct sk_buff *nfcsim_link_recv_skb(struct nfcsim_link *link, + int timeout, u8 rf_tech, u8 mode) +{ + int rc; + struct sk_buff *skb; + + rc = wait_event_interruptible_timeout(link->recv_wait, + link->cond, + msecs_to_jiffies(timeout)); + + mutex_lock(&link->lock); + + skb = link->skb; + link->skb = NULL; + + if (!rc) { + rc = -ETIMEDOUT; + goto done; + } + + if (!skb || link->rf_tech != rf_tech || link->mode == mode) { + rc = -EINVAL; + goto done; + } + + if (link->shutdown) { + rc = -ENODEV; + goto done; + } + +done: + mutex_unlock(&link->lock); + + if (rc < 0) { + dev_kfree_skb(skb); + skb = ERR_PTR(rc); + } + + link->cond = 0; + + return skb; +} + +static void nfcsim_send_wq(struct work_struct *work) +{ + struct nfcsim *dev = container_of(work, struct nfcsim, send_work.work); + + /* + * To effectively send data, the device just wake up its link_out which + * is the link_in of the peer device. The exchanged skb has already been + * stored in the dev->link_out through nfcsim_link_set_skb(). + */ + nfcsim_link_recv_wake(dev->link_out); +} + +static void nfcsim_recv_wq(struct work_struct *work) +{ + struct nfcsim *dev = container_of(work, struct nfcsim, recv_work); + struct sk_buff *skb; + + skb = nfcsim_link_recv_skb(dev->link_in, dev->recv_timeout, + dev->rf_tech, dev->mode); + + if (!dev->up) { + NFCSIM_ERR(dev, "Device is down\n"); + + if (!IS_ERR(skb)) + dev_kfree_skb(skb); + + skb = ERR_PTR(-ENODEV); + } + + dev->cb(dev->nfc_digital_dev, dev->arg, skb); +} + +static int nfcsim_send(struct nfc_digital_dev *ddev, struct sk_buff *skb, + u16 timeout, nfc_digital_cmd_complete_t cb, void *arg) +{ + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); + u8 delay; + + if (!dev->up) { + NFCSIM_ERR(dev, "Device is down\n"); + return -ENODEV; + } + + dev->recv_timeout = timeout; + dev->cb = cb; + dev->arg = arg; + + schedule_work(&dev->recv_work); + + if (skb) { + nfcsim_link_set_skb(dev->link_out, skb, dev->rf_tech, + dev->mode); + + /* Add random delay (between 3 and 10 ms) before sending data */ + get_random_bytes(&delay, 1); + delay = 3 + (delay & 0x07); + + schedule_delayed_work(&dev->send_work, msecs_to_jiffies(delay)); + } + + return 0; +} + +static void nfcsim_abort_cmd(struct nfc_digital_dev *ddev) +{ + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); + + nfcsim_link_recv_cancel(dev->link_in); +} + +static int nfcsim_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); + + dev->up = on; + + return 0; +} + +static int nfcsim_in_configure_hw(struct nfc_digital_dev *ddev, + int type, int param) +{ + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); + + switch (type) { + case NFC_DIGITAL_CONFIG_RF_TECH: + dev->up = true; + dev->mode = NFCSIM_MODE_INITIATOR; + dev->rf_tech = param; + break; + + case NFC_DIGITAL_CONFIG_FRAMING: + break; + + default: + NFCSIM_ERR(dev, "Invalid configuration type: %d\n", type); + return -EINVAL; + } + + return 0; +} + +static int nfcsim_in_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + return nfcsim_send(ddev, skb, timeout, cb, arg); +} + +static int nfcsim_tg_configure_hw(struct nfc_digital_dev *ddev, + int type, int param) +{ + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); + + switch (type) { + case NFC_DIGITAL_CONFIG_RF_TECH: + dev->up = true; + dev->mode = NFCSIM_MODE_TARGET; + dev->rf_tech = param; + break; + + case NFC_DIGITAL_CONFIG_FRAMING: + break; + + default: + NFCSIM_ERR(dev, "Invalid configuration type: %d\n", type); + return -EINVAL; + } + + return 0; +} + +static int nfcsim_tg_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + return nfcsim_send(ddev, skb, timeout, cb, arg); +} + +static int nfcsim_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + return nfcsim_send(ddev, NULL, timeout, cb, arg); +} + +static struct nfc_digital_ops nfcsim_digital_ops = { + .in_configure_hw = nfcsim_in_configure_hw, + .in_send_cmd = nfcsim_in_send_cmd, + + .tg_listen = nfcsim_tg_listen, + .tg_configure_hw = nfcsim_tg_configure_hw, + .tg_send_cmd = nfcsim_tg_send_cmd, + + .abort_cmd = nfcsim_abort_cmd, + .switch_rf = nfcsim_switch_rf, +}; + +static struct nfcsim *nfcsim_device_new(struct nfcsim_link *link_in, + struct nfcsim_link *link_out) +{ + struct nfcsim *dev; + int rc; + + dev = kzalloc(sizeof(struct nfcsim), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + INIT_DELAYED_WORK(&dev->send_work, nfcsim_send_wq); + INIT_WORK(&dev->recv_work, nfcsim_recv_wq); + + dev->nfc_digital_dev = + nfc_digital_allocate_device(&nfcsim_digital_ops, + NFC_PROTO_NFC_DEP_MASK, + NFCSIM_CAPABILITIES, + 0, 0); + if (!dev->nfc_digital_dev) { + kfree(dev); + return ERR_PTR(-ENOMEM); + } + + nfc_digital_set_drvdata(dev->nfc_digital_dev, dev); + + dev->link_in = link_in; + dev->link_out = link_out; + + rc = nfc_digital_register_device(dev->nfc_digital_dev); + if (rc) { + pr_err("Could not register digital device (%d)\n", rc); + nfc_digital_free_device(dev->nfc_digital_dev); + kfree(dev); + + return ERR_PTR(rc); + } + + return dev; +} + +static void nfcsim_device_free(struct nfcsim *dev) +{ + nfc_digital_unregister_device(dev->nfc_digital_dev); + + dev->up = false; + + nfcsim_link_shutdown(dev->link_in); + + cancel_delayed_work_sync(&dev->send_work); + cancel_work_sync(&dev->recv_work); + + nfc_digital_free_device(dev->nfc_digital_dev); + + kfree(dev); +} + static struct nfcsim *dev0; static struct nfcsim *dev1; -static struct workqueue_struct *wq; - -static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown) -{ - DEV_DBG(dev, "shutdown=%d\n", shutdown); - - mutex_lock(&dev->lock); - - dev->polling_mode = NFCSIM_POLL_NONE; - dev->shutting_down = shutdown; - dev->cb = NULL; - dev_kfree_skb(dev->clone_skb); - dev->clone_skb = NULL; - - mutex_unlock(&dev->lock); - - cancel_delayed_work_sync(&dev->poll_work); - cancel_delayed_work_sync(&dev->recv_work); -} - -static int nfcsim_target_found(struct nfcsim *dev) -{ - struct nfc_target nfc_tgt; - - DEV_DBG(dev, "\n"); - - memset(&nfc_tgt, 0, sizeof(struct nfc_target)); - - nfc_tgt.supported_protocols = NFC_PROTO_NFC_DEP_MASK; - nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1); - - return 0; -} - -static int nfcsim_dev_up(struct nfc_dev *nfc_dev) -{ - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - - DEV_DBG(dev, "\n"); - - mutex_lock(&dev->lock); - - dev->up = 1; - - mutex_unlock(&dev->lock); - - return 0; -} - -static int nfcsim_dev_down(struct nfc_dev *nfc_dev) -{ - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - - DEV_DBG(dev, "\n"); - - mutex_lock(&dev->lock); - - dev->up = 0; - - mutex_unlock(&dev->lock); - - return 0; -} - -static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev, - struct nfc_target *target, - u8 comm_mode, u8 *gb, size_t gb_len) -{ - int rc; - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - struct nfcsim *peer = dev->peer_dev; - u8 *remote_gb; - size_t remote_gb_len; - - DEV_DBG(dev, "target_idx: %d, comm_mode: %d\n", target->idx, comm_mode); - - mutex_lock(&peer->lock); - - nfc_tm_activated(peer->nfc_dev, NFC_PROTO_NFC_DEP_MASK, - NFC_COMM_ACTIVE, gb, gb_len); - - remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len); - if (!remote_gb) { - DEV_ERR(peer, "Can't get remote general bytes\n"); - - mutex_unlock(&peer->lock); - return -EINVAL; - } - - mutex_unlock(&peer->lock); - - mutex_lock(&dev->lock); - - rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len); - if (rc) { - DEV_ERR(dev, "Can't set remote general bytes\n"); - mutex_unlock(&dev->lock); - return rc; - } - - rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_ACTIVE, - NFC_RF_INITIATOR); - - mutex_unlock(&dev->lock); - - return rc; -} - -static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev) -{ - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - - DEV_DBG(dev, "\n"); - - nfcsim_cleanup_dev(dev, 0); - - return 0; -} - -static int nfcsim_start_poll(struct nfc_dev *nfc_dev, - u32 im_protocols, u32 tm_protocols) -{ - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - int rc; - - mutex_lock(&dev->lock); - - if (dev->polling_mode != NFCSIM_POLL_NONE) { - DEV_ERR(dev, "Already in polling mode\n"); - rc = -EBUSY; - goto exit; - } - - if (im_protocols & NFC_PROTO_NFC_DEP_MASK) - dev->polling_mode |= NFCSIM_POLL_INITIATOR; - - if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) - dev->polling_mode |= NFCSIM_POLL_TARGET; - - if (dev->polling_mode == NFCSIM_POLL_NONE) { - DEV_ERR(dev, "Unsupported polling mode\n"); - rc = -EINVAL; - goto exit; - } - - dev->initiator = 0; - dev->curr_polling_mode = NFCSIM_POLL_NONE; - - queue_delayed_work(wq, &dev->poll_work, 0); - - DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X\n", im_protocols, - tm_protocols); - - rc = 0; -exit: - mutex_unlock(&dev->lock); - - return rc; -} - -static void nfcsim_stop_poll(struct nfc_dev *nfc_dev) -{ - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - - DEV_DBG(dev, "Stop poll\n"); - - mutex_lock(&dev->lock); - - dev->polling_mode = NFCSIM_POLL_NONE; - - mutex_unlock(&dev->lock); - - cancel_delayed_work_sync(&dev->poll_work); -} - -static int nfcsim_activate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target, u32 protocol) -{ - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - - DEV_DBG(dev, "\n"); - - return -ENOTSUPP; -} - -static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target, u8 mode) -{ - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - - DEV_DBG(dev, "\n"); -} - -static void nfcsim_wq_recv(struct work_struct *work) -{ - struct nfcsim *dev = container_of(work, struct nfcsim, - recv_work.work); - - mutex_lock(&dev->lock); - - if (dev->shutting_down || !dev->up || !dev->clone_skb) { - dev_kfree_skb(dev->clone_skb); - goto exit; - } - - if (dev->initiator) { - if (!dev->cb) { - DEV_ERR(dev, "Null recv callback\n"); - dev_kfree_skb(dev->clone_skb); - goto exit; - } - - dev->cb(dev->cb_context, dev->clone_skb, 0); - dev->cb = NULL; - } else { - nfc_tm_data_received(dev->nfc_dev, dev->clone_skb); - } - -exit: - dev->clone_skb = NULL; - - mutex_unlock(&dev->lock); -} - -static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target, - struct sk_buff *skb, data_exchange_cb_t cb, - void *cb_context) -{ - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - struct nfcsim *peer = dev->peer_dev; - int err; - - mutex_lock(&dev->lock); - - if (dev->shutting_down || !dev->up) { - mutex_unlock(&dev->lock); - err = -ENODEV; - goto exit; - } - - dev->cb = cb; - dev->cb_context = cb_context; - - mutex_unlock(&dev->lock); - - mutex_lock(&peer->lock); - - peer->clone_skb = skb_clone(skb, GFP_KERNEL); - - if (!peer->clone_skb) { - DEV_ERR(dev, "skb_clone failed\n"); - mutex_unlock(&peer->lock); - err = -ENOMEM; - goto exit; - } - - /* This simulates an arbitrary transmission delay between the 2 devices. - * If packet transmission occurs immediately between them, we have a - * non-stop flow of several tens of thousands SYMM packets per second - * and a burning cpu. - */ - queue_delayed_work(wq, &peer->recv_work, - msecs_to_jiffies(dev->rx_delay)); - - mutex_unlock(&peer->lock); - - err = 0; -exit: - dev_kfree_skb(skb); - - return err; -} - -static int nfcsim_im_transceive(struct nfc_dev *nfc_dev, - struct nfc_target *target, struct sk_buff *skb, - data_exchange_cb_t cb, void *cb_context) -{ - return nfcsim_tx(nfc_dev, target, skb, cb, cb_context); -} - -static int nfcsim_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) -{ - return nfcsim_tx(nfc_dev, NULL, skb, NULL, NULL); -} - -static struct nfc_ops nfcsim_nfc_ops = { - .dev_up = nfcsim_dev_up, - .dev_down = nfcsim_dev_down, - .dep_link_up = nfcsim_dep_link_up, - .dep_link_down = nfcsim_dep_link_down, - .start_poll = nfcsim_start_poll, - .stop_poll = nfcsim_stop_poll, - .activate_target = nfcsim_activate_target, - .deactivate_target = nfcsim_deactivate_target, - .im_transceive = nfcsim_im_transceive, - .tm_send = nfcsim_tm_send, -}; - -static void nfcsim_set_polling_mode(struct nfcsim *dev) -{ - if (dev->polling_mode == NFCSIM_POLL_NONE) { - dev->curr_polling_mode = NFCSIM_POLL_NONE; - return; - } - - if (dev->curr_polling_mode == NFCSIM_POLL_NONE) { - if (dev->polling_mode & NFCSIM_POLL_INITIATOR) - dev->curr_polling_mode = NFCSIM_POLL_INITIATOR; - else - dev->curr_polling_mode = NFCSIM_POLL_TARGET; - - return; - } - - if (dev->polling_mode == NFCSIM_POLL_DUAL) { - if (dev->curr_polling_mode == NFCSIM_POLL_TARGET) - dev->curr_polling_mode = NFCSIM_POLL_INITIATOR; - else - dev->curr_polling_mode = NFCSIM_POLL_TARGET; - } -} - -static void nfcsim_wq_poll(struct work_struct *work) -{ - struct nfcsim *dev = container_of(work, struct nfcsim, poll_work.work); - struct nfcsim *peer = dev->peer_dev; - - /* These work items run on an ordered workqueue and are therefore - * serialized. So we can take both mutexes without being dead locked. - */ - mutex_lock(&dev->lock); - mutex_lock(&peer->lock); - - nfcsim_set_polling_mode(dev); - - if (dev->curr_polling_mode == NFCSIM_POLL_NONE) { - DEV_DBG(dev, "Not polling\n"); - goto unlock; - } - - DEV_DBG(dev, "Polling as %s", - dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ? - "initiator\n" : "target\n"); - - if (dev->curr_polling_mode == NFCSIM_POLL_TARGET) - goto sched_work; - - if (peer->curr_polling_mode == NFCSIM_POLL_TARGET) { - peer->polling_mode = NFCSIM_POLL_NONE; - dev->polling_mode = NFCSIM_POLL_NONE; - - dev->initiator = 1; - - nfcsim_target_found(dev); - - goto unlock; - } - -sched_work: - /* This defines the delay for an initiator to check if the other device - * is polling in target mode. - * If the device starts in dual mode polling, it switches between - * initiator and target at every round. - * Because the wq is ordered and only 1 work item is executed at a time, - * we'll always have one device polling as initiator and the other as - * target at some point, even if both are started in dual mode. - */ - queue_delayed_work(wq, &dev->poll_work, msecs_to_jiffies(200)); - -unlock: - mutex_unlock(&peer->lock); - mutex_unlock(&dev->lock); -} - -static struct nfcsim *nfcsim_init_dev(void) -{ - struct nfcsim *dev; - int rc = -ENOMEM; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) - return ERR_PTR(-ENOMEM); - - mutex_init(&dev->lock); - - INIT_DELAYED_WORK(&dev->recv_work, nfcsim_wq_recv); - INIT_DELAYED_WORK(&dev->poll_work, nfcsim_wq_poll); - - dev->nfc_dev = nfc_allocate_device(&nfcsim_nfc_ops, - NFC_PROTO_NFC_DEP_MASK, - 0, 0); - if (!dev->nfc_dev) - goto error; - - nfc_set_drvdata(dev->nfc_dev, dev); - - rc = nfc_register_device(dev->nfc_dev); - if (rc) - goto free_nfc_dev; - - dev->rx_delay = RX_DEFAULT_DELAY; - return dev; - -free_nfc_dev: - nfc_free_device(dev->nfc_dev); - -error: - kfree(dev); - - return ERR_PTR(rc); -} - -static void nfcsim_free_device(struct nfcsim *dev) -{ - nfc_unregister_device(dev->nfc_dev); - - nfc_free_device(dev->nfc_dev); - - kfree(dev); -} - static int __init nfcsim_init(void) { + struct nfcsim_link *link0, *link1; int rc; - /* We need an ordered wq to ensure that poll_work items are executed - * one at a time. - */ - wq = alloc_ordered_workqueue("nfcsim", 0); - if (!wq) { + link0 = nfcsim_link_new(); + link1 = nfcsim_link_new(); + if (!link0 || !link1) { rc = -ENOMEM; - goto exit; + goto exit_err; } - dev0 = nfcsim_init_dev(); + dev0 = nfcsim_device_new(link0, link1); if (IS_ERR(dev0)) { rc = PTR_ERR(dev0); - goto exit; + goto exit_err; } - dev1 = nfcsim_init_dev(); + dev1 = nfcsim_device_new(link1, link0); if (IS_ERR(dev1)) { - kfree(dev0); + nfcsim_device_free(dev0); rc = PTR_ERR(dev1); - goto exit; + goto exit_err; } - dev0->peer_dev = dev1; - dev1->peer_dev = dev0; + pr_info("nfcsim " NFCSIM_VERSION " initialized\n"); - pr_debug("NFCsim " NFCSIM_VERSION " initialized\n"); + return 0; - rc = 0; -exit: - if (rc) - pr_err("Failed to initialize nfcsim driver (%d)\n", - rc); +exit_err: + pr_err("Failed to initialize nfcsim driver (%d)\n", rc); + + nfcsim_link_free(link0); + nfcsim_link_free(link1); return rc; } static void __exit nfcsim_exit(void) { - nfcsim_cleanup_dev(dev0, 1); - nfcsim_cleanup_dev(dev1, 1); + struct nfcsim_link *link0, *link1; - nfcsim_free_device(dev0); - nfcsim_free_device(dev1); + link0 = dev0->link_in; + link1 = dev0->link_out; - destroy_workqueue(wq); + nfcsim_device_free(dev0); + nfcsim_device_free(dev1); + + nfcsim_link_free(link0); + nfcsim_link_free(link1); } module_init(nfcsim_init); From de9e5aeb4f40e72fa3bb087d378c9bd4ecf65c7f Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Wed, 29 Jun 2016 10:48:22 +0200 Subject: [PATCH 10/31] NFC: llcp: Fix usage of llcp_add_tlv() In functions using llcp_add_tlv(), a skb pointer could be set to NULL and then reuse afterward. With this patch, the skb pointer returned by llcp_add_tlv() is ignored since it can only be the passed skb pointer or NULL when the passed TLV is NULL. There is also no need to check for the TLV pointer as this is done by llcp_add_tlv(). Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/llcp_commands.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 3425532c39f7..4112d009b032 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -438,12 +438,9 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) goto error_tlv; } - if (service_name_tlv != NULL) - skb = llcp_add_tlv(skb, service_name_tlv, - service_name_tlv_length); - - skb = llcp_add_tlv(skb, miux_tlv, miux_tlv_length); - skb = llcp_add_tlv(skb, rw_tlv, rw_tlv_length); + llcp_add_tlv(skb, service_name_tlv, service_name_tlv_length); + llcp_add_tlv(skb, miux_tlv, miux_tlv_length); + llcp_add_tlv(skb, rw_tlv, rw_tlv_length); skb_queue_tail(&local->tx_queue, skb); @@ -493,8 +490,8 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) goto error_tlv; } - skb = llcp_add_tlv(skb, miux_tlv, miux_tlv_length); - skb = llcp_add_tlv(skb, rw_tlv, rw_tlv_length); + llcp_add_tlv(skb, miux_tlv, miux_tlv_length); + llcp_add_tlv(skb, rw_tlv, rw_tlv_length); skb_queue_tail(&local->tx_queue, skb); From 256f3ee3d1468660ca3b10ad3beab7e8f6cbd969 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Wed, 29 Jun 2016 10:48:23 +0200 Subject: [PATCH 11/31] NFC: llcp: Fix 2 memory leaks Once copied into the sk_buff data area using llcp_add_tlv(), the allocated TLVs must be freed. With this patch nfc_llcp_send_connect() and nfc_llcp_send_cc() don't return immediately on success and now free the allocated TLVs. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/llcp_commands.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 4112d009b032..c5959ce503e6 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -444,10 +444,11 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) skb_queue_tail(&local->tx_queue, skb); - return 0; + err = 0; error_tlv: - pr_err("error %d\n", err); + if (err) + pr_err("error %d\n", err); kfree(service_name_tlv); kfree(miux_tlv); @@ -495,10 +496,11 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) skb_queue_tail(&local->tx_queue, skb); - return 0; + err = 0; error_tlv: - pr_err("error %d\n", err); + if (err) + pr_err("error %d\n", err); kfree(miux_tlv); kfree(rw_tlv); From e3e0258839a01f793a8a0c0885e8ad387681cdc6 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:25:20 +0200 Subject: [PATCH 12/31] NFC: port100: Don't send a new command if one is still pending This patch ensures that a command is not still in process before sending a new one to the device. This can happen when neard is in constant polling mode: the configure_hw command can be sent when neard restarts polling after a LLCP SYMM timeout but before the device has returned in timeout from the last DEP frame sent. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 2d4bbe3fad57..14a3cc2d0fd6 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -809,6 +809,12 @@ static int port100_send_cmd_async(struct port100 *dev, u8 cmd_code, PORT100_FRAME_MAX_PAYLOAD_LEN + PORT100_FRAME_TAIL_LEN; + if (dev->cmd) { + nfc_err(&dev->interface->dev, + "A command is still in process\n"); + return -EBUSY; + } + resp = alloc_skb(resp_len, GFP_KERNEL); if (!resp) return -ENOMEM; From b74584c1a6d68110d135a6ce0336aab0055f4341 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:25:21 +0200 Subject: [PATCH 13/31] NFC: port100: Fix the command cancellation process The USB out_urb used to send commands to the device can be submitted through the standard command processing queue coming from the Digital Protocol layer but it can also be submitted from port100_abort_cmd(). To not submit the URB while already active, a mutex is now used to protect it and a cmd_cancel flag is used to not send command while canceling the previous one. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 14a3cc2d0fd6..909e3df2c16a 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -456,6 +456,12 @@ struct port100 { struct urb *out_urb; struct urb *in_urb; + /* This mutex protects the out_urb and avoids to submit a new command + * through port100_send_frame_async() while the previous one is being + * canceled through port100_abort_cmd(). + */ + struct mutex out_urb_lock; + struct work_struct cmd_complete_work; u8 cmd_type; @@ -464,6 +470,8 @@ struct port100 { * for any queuing/locking mechanism at driver level. */ struct port100_cmd *cmd; + + bool cmd_cancel; }; struct port100_cmd { @@ -718,10 +726,22 @@ static int port100_send_ack(struct port100 *dev) { int rc; + mutex_lock(&dev->out_urb_lock); + + usb_kill_urb(dev->out_urb); + dev->out_urb->transfer_buffer = ack_frame; dev->out_urb->transfer_buffer_length = sizeof(ack_frame); rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); + /* Set the cmd_cancel flag only if the URB has been successfully + * submitted. It will be reset by the out URB completion callback + * port100_send_complete(). + */ + dev->cmd_cancel = !rc; + + mutex_unlock(&dev->out_urb_lock); + return rc; } @@ -730,6 +750,16 @@ static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out, { int rc; + mutex_lock(&dev->out_urb_lock); + + /* A command cancel frame as been sent through dev->out_urb. Don't try + * to submit a new one. + */ + if (dev->cmd_cancel) { + rc = -EAGAIN; + goto exit; + } + dev->out_urb->transfer_buffer = out->data; dev->out_urb->transfer_buffer_length = out->len; @@ -741,16 +771,15 @@ static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out, rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); if (rc) - return rc; + goto exit; rc = port100_submit_urb_for_ack(dev, GFP_KERNEL); if (rc) - goto error; + usb_unlink_urb(dev->out_urb); - return 0; +exit: + mutex_unlock(&dev->out_urb_lock); -error: - usb_unlink_urb(dev->out_urb); return rc; } @@ -892,6 +921,8 @@ static void port100_send_complete(struct urb *urb) { struct port100 *dev = urb->context; + dev->cmd_cancel = false; + switch (urb->status) { case 0: break; /* success */ @@ -1455,6 +1486,7 @@ static int port100_probe(struct usb_interface *interface, if (!dev) return -ENOMEM; + mutex_init(&dev->out_urb_lock); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; usb_set_intfdata(interface, dev); From a52bd7d2753b0567c71d604c640e9c4a86221756 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:25:22 +0200 Subject: [PATCH 14/31] NFC: port100: Make port100_abort_cmd() synchronous This patch makes the abort_cmd function synchronous. This allows the caller to immediately send a new command after abort_cmd() returns. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 909e3df2c16a..481cb435e19f 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -472,6 +472,7 @@ struct port100 { struct port100_cmd *cmd; bool cmd_cancel; + struct completion cmd_cancel_done; }; struct port100_cmd { @@ -728,6 +729,8 @@ static int port100_send_ack(struct port100 *dev) mutex_lock(&dev->out_urb_lock); + init_completion(&dev->cmd_cancel_done); + usb_kill_urb(dev->out_urb); dev->out_urb->transfer_buffer = ack_frame; @@ -742,6 +745,9 @@ static int port100_send_ack(struct port100 *dev) mutex_unlock(&dev->out_urb_lock); + if (!rc) + wait_for_completion(&dev->cmd_cancel_done); + return rc; } @@ -921,7 +927,10 @@ static void port100_send_complete(struct urb *urb) { struct port100 *dev = urb->context; - dev->cmd_cancel = false; + if (dev->cmd_cancel) { + dev->cmd_cancel = false; + complete(&dev->cmd_cancel_done); + } switch (urb->status) { case 0: From 9f0c4542c49cbd041f2b6943b16644af0a3ff48f Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:25:23 +0200 Subject: [PATCH 15/31] NFC: port100: Abort current command before switching RF off If a command is still being processed by the device, the switch RF off command will be rejected. With this patch, the port100 driver calls port100_abort_cmd() before sending the switch RF off command. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 481cb435e19f..2b2330b235e6 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -1050,6 +1050,10 @@ static int port100_switch_rf(struct nfc_digital_dev *ddev, bool on) *skb_put(skb, 1) = on ? 1 : 0; + /* Cancel the last command if the device is being switched off */ + if (!on) + port100_abort_cmd(ddev); + resp = port100_send_cmd_sync(dev, PORT100_CMD_SWITCH_RF, skb); if (IS_ERR(resp)) From 8f49bec6c36c73f0c212e07229b19fb4bba85788 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 4 Jul 2016 16:42:55 +0200 Subject: [PATCH 16/31] NFC: nfcsim: Fix missing dependency on NFC_DIGITAL The nfcsim driver now depends on the Digital layer. This patch adds the missing dependency on NFC_DIGITAL for NFC_SIM config. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index ea8321a483f9..9d2369269abf 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -40,6 +40,7 @@ config NFC_MEI_PHY config NFC_SIM tristate "NFC hardware simulator driver" + depends on NFC_DIGITAL help This driver declares two virtual NFC devices supporting NFC-DEP protocol. An LLCP connection can be established between them and From b77693447db987e77a39afaa8774e8702cb110d5 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:24:41 +0200 Subject: [PATCH 17/31] NFC: digital: Fix a memory leak in NFC-F listening mode When configured as a target listening for a SENSF_REQ poll command, a nfcid2 array was allocated for no reason leading to a memory leak. The nfcid2 is sent by the target in the SENSF_RES reply. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_technology.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c index fb58ed2dd41d..d9080dec5d27 100644 --- a/net/nfc/digital_technology.c +++ b/net/nfc/digital_technology.c @@ -1257,21 +1257,12 @@ static int digital_tg_config_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech) int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech) { int rc; - u8 *nfcid2; rc = digital_tg_config_nfcf(ddev, rf_tech); if (rc) return rc; - nfcid2 = kzalloc(NFC_NFCID2_MAXSIZE, GFP_KERNEL); - if (!nfcid2) - return -ENOMEM; - - nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1; - nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2; - get_random_bytes(nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2); - - return digital_tg_listen(ddev, 300, digital_tg_recv_sensf_req, nfcid2); + return digital_tg_listen(ddev, 300, digital_tg_recv_sensf_req, NULL); } void digital_tg_recv_md_req(struct nfc_digital_dev *ddev, void *arg, From 3f89fea35fc37b326d6b3697fcc9cba235a60811 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:24:42 +0200 Subject: [PATCH 18/31] NFC: digital: Rework error handling in DEP_RES response The Digital Protocol stack used to send a NACK frame whatever the error type it receives in digital_in_recv_dep_res(). It actually should only send a NACK frame on CRC or parity check errors or on any transmission error if a NACK frame was previously sent. Existing drivers used to send EIO error for this kind of issues so this patch limits sending of NACK frames on EIO errors. All other errors will be reported to the upper layers. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index f72be7433df3..b62c85dc12a2 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -664,7 +664,7 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, rc = PTR_ERR(resp); resp = NULL; - if (((rc != -ETIMEDOUT) || ddev->nack_count) && + if ((rc == -EIO || (rc == -ETIMEDOUT && ddev->nack_count)) && (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) { ddev->atn_count = 0; From 82e57952869fbbdf09d8f9e7ac284df13741e93d Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:24:43 +0200 Subject: [PATCH 19/31] NFC: digital: Call pending command callbacks at device unregister With this patch, when freeing the command queue in the module unregister function, the callbacks of the commands still queued are called with a ENODEV error. This gives a chance to the command issuer to free any memory it could have allocate. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 27769ac89d27..6e0b255aec66 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -842,6 +842,14 @@ void nfc_digital_unregister_device(struct nfc_digital_dev *ddev) list_for_each_entry_safe(cmd, n, &ddev->cmd_queue, queue) { list_del(&cmd->queue); + + /* Call the command callback if any and pass it a ENODEV error. + * This gives a chance to the command issuer to free any + * allocated buffer. + */ + if (cmd->cmd_cb) + cmd->cmd_cb(ddev, cmd->cb_context, ERR_PTR(-ENODEV)); + kfree(cmd->mdaa_params); kfree(cmd); } From af66df0f53b9120437556d8eb00d70a36e791258 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:24:44 +0200 Subject: [PATCH 20/31] NFC: digital: Set the command pending flag There is a flag in the command structure indicating that this command is pending. It was checked before sending the command to not send the same command twice but it was actually never set. This is now fixed. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 6e0b255aec66..0146e42ee28f 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -176,6 +176,8 @@ static void digital_wq_cmd(struct work_struct *work) return; } + cmd->pending = 1; + mutex_unlock(&ddev->cmd_lock); if (cmd->req) From 3cc952dbf1a7176b9247da4cd2612c9ddc1d1b51 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:24:45 +0200 Subject: [PATCH 21/31] NFC: digital: Abort last command when dep link goes down With this patch, the Digital Protocol layer abort the last issued command when the dep link goes down. That way it does not have to wait for the driver to reply with a timeout error before sending a new command (i.e. a start poll command if constant polling is on). Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 0146e42ee28f..0fd5518bf252 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -612,6 +612,8 @@ static int digital_dep_link_down(struct nfc_dev *nfc_dev) { struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + digital_abort_cmd(ddev); + ddev->curr_protocol = 0; return 0; From 1d984c2e03c1fb21539a9f50627e312788512013 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 8 Jul 2016 15:52:39 +0200 Subject: [PATCH 22/31] NFC: digital: Fix handling of saved PDU sk_buff pointers This patch fixes the way an I-PDU is saved in case it needs to be sent again. It is now copied using pskb_copy() and not simply referenced using skb_get() since it could be modified by the driver. digital_in_send_saved_skb() and digital_tg_send_saved_skb() still get a reference on the saved skb which is re-sent but release it if the send operation fails. That way the caller doesn't have to take care about skb ref in case of error. RTOX supervisor PDU must not be saved as this can override a previously saved I-PDU that should be re-sent later on. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 1 - net/nfc/digital_dep.c | 63 +++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 506e3f6eabef..f9a4e4771861 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -237,7 +237,6 @@ struct nfc_digital_dev { int nack_count; struct sk_buff *saved_skb; - unsigned int saved_skb_len; u16 target_fsc; diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index b62c85dc12a2..804585cb3f8e 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -524,8 +524,7 @@ static int digital_in_send_ack(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - ddev->saved_skb = skb_get(skb); - ddev->saved_skb_len = skb->len; + ddev->saved_skb = pskb_copy(skb, GFP_KERNEL); rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, data_exch); @@ -627,16 +626,10 @@ static int digital_in_send_rtox(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - ddev->saved_skb = skb_get(skb); - ddev->saved_skb_len = skb->len; - rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, data_exch); - if (rc) { + if (rc) kfree_skb(skb); - kfree_skb(ddev->saved_skb); - ddev->saved_skb = NULL; - } return rc; } @@ -644,11 +637,19 @@ static int digital_in_send_rtox(struct nfc_digital_dev *ddev, static int digital_in_send_saved_skb(struct nfc_digital_dev *ddev, struct digital_data_exch *data_exch) { - skb_get(ddev->saved_skb); - skb_push(ddev->saved_skb, ddev->saved_skb_len); + int rc; - return digital_in_send_cmd(ddev, ddev->saved_skb, 1500, - digital_in_recv_dep_res, data_exch); + if (!ddev->saved_skb) + return -EINVAL; + + skb_get(ddev->saved_skb); + + rc = digital_in_send_cmd(ddev, ddev->saved_skb, 1500, + digital_in_recv_dep_res, data_exch); + if (rc) + kfree_skb(ddev->saved_skb); + + return rc; } static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, @@ -812,17 +813,12 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { /* ATN */ rc = digital_in_send_saved_skb(ddev, data_exch); - if (rc) { - kfree_skb(ddev->saved_skb); + if (rc) goto error; - } return; } - kfree_skb(ddev->saved_skb); - ddev->saved_skb = NULL; - rc = digital_in_send_rtox(ddev, data_exch, resp->data[0]); if (rc) goto error; @@ -876,8 +872,7 @@ int digital_in_send_dep_req(struct nfc_digital_dev *ddev, ddev->skb_add_crc(tmp_skb); - ddev->saved_skb = skb_get(tmp_skb); - ddev->saved_skb_len = tmp_skb->len; + ddev->saved_skb = pskb_copy(tmp_skb, GFP_KERNEL); rc = digital_in_send_cmd(ddev, tmp_skb, 1500, digital_in_recv_dep_res, data_exch); @@ -956,8 +951,7 @@ static int digital_tg_send_ack(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - ddev->saved_skb = skb_get(skb); - ddev->saved_skb_len = skb->len; + ddev->saved_skb = pskb_copy(skb, GFP_KERNEL); rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req, data_exch); @@ -1009,11 +1003,19 @@ static int digital_tg_send_atn(struct nfc_digital_dev *ddev) static int digital_tg_send_saved_skb(struct nfc_digital_dev *ddev) { - skb_get(ddev->saved_skb); - skb_push(ddev->saved_skb, ddev->saved_skb_len); + int rc; - return digital_tg_send_cmd(ddev, ddev->saved_skb, 1500, - digital_tg_recv_dep_req, NULL); + if (!ddev->saved_skb) + return -EINVAL; + + skb_get(ddev->saved_skb); + + rc = digital_tg_send_cmd(ddev, ddev->saved_skb, 1500, + digital_tg_recv_dep_req, NULL); + if (rc) + kfree_skb(ddev->saved_skb); + + return rc; } static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, @@ -1163,10 +1165,8 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, ddev->atn_count = 0; rc = digital_tg_send_saved_skb(ddev); - if (rc) { - kfree_skb(ddev->saved_skb); + if (rc) goto exit; - } } return; @@ -1235,8 +1235,7 @@ int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb) ddev->skb_add_crc(tmp_skb); - ddev->saved_skb = skb_get(tmp_skb); - ddev->saved_skb_len = tmp_skb->len; + ddev->saved_skb = pskb_copy(tmp_skb, GFP_KERNEL); rc = digital_tg_send_cmd(ddev, tmp_skb, 1500, digital_tg_recv_dep_req, NULL); From e8e7f4217564fc115b60a9373646afb193aa08cf Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 8 Jul 2016 15:52:40 +0200 Subject: [PATCH 23/31] NFC: digital: Remove useless call to skb_reserve() When allocating chained I-PDUs, there is no need to call skb_reserve() since it's already done by digital_alloc_skb() and contains enough room for the driver head and tail data. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 804585cb3f8e..ed3a52971d65 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -190,8 +190,6 @@ digital_send_dep_data_prep(struct nfc_digital_dev *ddev, struct sk_buff *skb, return ERR_PTR(-ENOMEM); } - skb_reserve(new_skb, ddev->tx_headroom + NFC_HEADER_SIZE + - DIGITAL_NFC_DEP_REQ_RES_HEADROOM); memcpy(skb_put(new_skb, ddev->remote_payload_max), skb->data, ddev->remote_payload_max); skb_pull(skb, ddev->remote_payload_max); From f23a9868b1c45e77ec6082eb95508885111ffda1 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 8 Jul 2016 15:52:41 +0200 Subject: [PATCH 24/31] NFC: digital: Fix target DEP_REQ I-PDU handling after ATN PDU When the initiator sends a DEP_REQ I-PDU, the target device may not reply in a timely manner. In this case the initiator device must send an attention PDU (ATN) and if the recipient replies with an ATN PDU in return, then the last I-PDU must be sent again by the initiator. This patch fixes how the target handles I-PDU received after an ATN PDU has been received. There are 2 possible cases: - The target has received the initial DEP_REQ and sends back the DEP_RES but the initiator did not receive it. In this case, after the initiator has sent an ATN PDU and the target replied it (with an ATN as well), the initiator sends the saved skb of the initial DEP_REQ again and the target replies with the saved skb of the initial DEP_RES. - Or the target did not even received the initial DEP_REQ. In this case, after the ATN PDUs exchange, the initiator sends the saved skb and the target simply passes it up, just as usual. This behavior is controlled using the atn_count and the PNI field of the digital device structure. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 47 +++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index ed3a52971d65..1778c23751d4 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -1086,24 +1086,40 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, case DIGITAL_NFC_DEP_PFB_I_PDU: pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n"); - if ((ddev->atn_count && (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) != - ddev->curr_nfc_dep_pni)) || - (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni)) { + if (ddev->atn_count) { + /* The target has received (and replied to) at least one + * ATN DEP_REQ. + */ + ddev->atn_count = 0; + + /* pni of resp PDU equal to the target current pni - 1 + * means resp is the previous DEP_REQ PDU received from + * the initiator so the target replies with saved_skb + * which is the previous DEP_RES saved in + * digital_tg_send_dep_res(). + */ + if (DIGITAL_NFC_DEP_PFB_PNI(pfb) == + DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni - 1)) { + rc = digital_tg_send_saved_skb(ddev); + if (rc) + goto exit; + + goto free_resp; + } + + /* atn_count > 0 and PDU pni != curr_nfc_dep_pni - 1 + * means the target probably did not received the last + * DEP_REQ PDU sent by the initiator. The target + * fallbacks to normal processing then. + */ + } + + if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) { PROTOCOL_ERR("14.12.3.4"); rc = -EIO; goto exit; } - if (ddev->atn_count) { - ddev->atn_count = 0; - - rc = digital_tg_send_saved_skb(ddev); - if (rc) - goto exit; - - return; - } - kfree_skb(ddev->saved_skb); ddev->saved_skb = NULL; @@ -1197,6 +1213,11 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, if (rc) kfree_skb(resp); + + return; + +free_resp: + dev_kfree_skb(resp); } int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb) From 482333b277de181ce80c833d84f2598e2527b267 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 8 Jul 2016 15:52:42 +0200 Subject: [PATCH 25/31] NFC: digital: Fix ACK & NACK PDUs handling in target mode When the target receives a NACK PDU, it re-sends the last sent PDU. ACK PDUs are received by the target as a reply from the initiator to chained I-PDUs. There are 3 cases to handle: - If the target has previously received 1 or more ATN PDUs and the PNI in the ACK PDU is equal to the target PNI - 1, then it means that the initiator did not received the last issued PDU from the target. In this case it re-sends this PDU. - If the target has received 1 or more ATN PDUs but the ACK PNI is not the target PNI - 1, then this means that this ACK is the reply of the previous chained I-PDU sent by the target. The target did not received it on the first attempt and it is being re-sent by the initiator. The process continues as usual. - No ATN PDU received before this ACK PDU. This is the reply of a chained I-PDU. The target keeps on processing its chained I-PDU. The code has been refactored to avoid too many indentation levels. Also, ACK and NACK PDUs were not freed. This is now fixed. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 79 +++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 1778c23751d4..e0268777ab18 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -1141,37 +1141,9 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, rc = 0; break; case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: - if (!DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* ACK */ - if ((ddev->atn_count && - (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) != - ddev->curr_nfc_dep_pni)) || - (DIGITAL_NFC_DEP_PFB_PNI(pfb) != - ddev->curr_nfc_dep_pni) || - !ddev->chaining_skb || !ddev->saved_skb) { - rc = -EIO; - goto exit; - } - - if (ddev->atn_count) { - ddev->atn_count = 0; - - rc = digital_tg_send_saved_skb(ddev); - if (rc) - goto exit; - - return; - } - - kfree_skb(ddev->saved_skb); - ddev->saved_skb = NULL; - - rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb); - if (rc) - goto exit; - } else { /* NACK */ - if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) != - ddev->curr_nfc_dep_pni) || - !ddev->saved_skb) { + if (DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* NACK */ + if (DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) != + ddev->curr_nfc_dep_pni) { rc = -EIO; goto exit; } @@ -1181,9 +1153,52 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, rc = digital_tg_send_saved_skb(ddev); if (rc) goto exit; + + goto free_resp; } - return; + /* ACK */ + if (ddev->atn_count) { + /* The target has previously recevied one or more ATN + * PDUs. + */ + ddev->atn_count = 0; + + /* If the ACK PNI is equal to the target PNI - 1 means + * that the initiator did not receive the previous PDU + * sent by the target so re-send it. + */ + if (DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) == + ddev->curr_nfc_dep_pni) { + rc = digital_tg_send_saved_skb(ddev); + if (rc) + goto exit; + + goto free_resp; + } + + /* Otherwise, the target did not receive the previous + * ACK PDU from the initiator. Fallback to normal + * processing of chained PDU then. + */ + } + + /* Keep on sending chained PDU */ + if (!ddev->chaining_skb || + DIGITAL_NFC_DEP_PFB_PNI(pfb) != + ddev->curr_nfc_dep_pni) { + rc = -EIO; + goto exit; + } + + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + + rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb); + if (rc) + goto exit; + + goto free_resp; case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { rc = -EINVAL; From e073eb6797191abe2fe30ca643ab0cc3d8e1e534 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 8 Jul 2016 15:52:43 +0200 Subject: [PATCH 26/31] NFC: digital: Rework ACK PDU handling in initiator mode With this patch, ACK PDU sk_buffs are now freed and code has been refactored for better errors handling. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index e0268777ab18..03bfc74745f7 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -782,6 +782,12 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, break; case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: + if (DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { + PROTOCOL_ERR("14.12.4.5"); + rc = -EIO; + goto exit; + } + if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) { PROTOCOL_ERR("14.12.3.3"); rc = -EIO; @@ -791,22 +797,25 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1); - if (ddev->chaining_skb && !DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { - kfree_skb(ddev->saved_skb); - ddev->saved_skb = NULL; - - rc = digital_in_send_dep_req(ddev, NULL, - ddev->chaining_skb, - ddev->data_exch); - if (rc) - goto error; - - return; + if (!ddev->chaining_skb) { + PROTOCOL_ERR("14.12.4.3"); + rc = -EIO; + goto exit; } - pr_err("Received a ACK/NACK PDU\n"); - rc = -EINVAL; - goto exit; + /* The initiator has received a valid ACK. Free the last sent + * PDU and keep on sending chained skb. + */ + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + + rc = digital_in_send_dep_req(ddev, NULL, + ddev->chaining_skb, + ddev->data_exch); + if (rc) + goto error; + + goto free_resp; case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { /* ATN */ @@ -839,6 +848,11 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, if (rc) kfree_skb(resp); + + return; + +free_resp: + dev_kfree_skb(resp); } int digital_in_send_dep_req(struct nfc_digital_dev *ddev, From e200f008ace69eebac0a1432dc9e24ab5cd0d029 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 8 Jul 2016 15:52:44 +0200 Subject: [PATCH 27/31] NFC: digital: Free supervisor PDUs This patch frees the RTOX resp sk_buff in initiator mode. It also makes use of the free_resp exit point for ATN supervisor PDUs in both initiator and target mode. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 03bfc74745f7..ba52a5dbf3cc 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -823,15 +823,14 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, if (rc) goto error; - return; + goto free_resp; } rc = digital_in_send_rtox(ddev, data_exch, resp->data[0]); if (rc) goto error; - kfree_skb(resp); - return; + goto free_resp; } exit: @@ -1225,8 +1224,7 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, ddev->atn_count++; - kfree_skb(resp); - return; + goto free_resp; } rc = nfc_tm_data_received(ddev->nfc_dev, resp); From 1a09c56f545c8ff8d338a38c7c40d79f4165a94c Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 8 Jul 2016 15:52:45 +0200 Subject: [PATCH 28/31] NFC: digital: Add support for NFC DEP Response Waiting Time When sending an ATR_REQ, the initiator must wait for the ATR_RES at least 'RWT(nfcdep,activation) + dRWT(nfcdep)' and no more than 'RWT(nfcdep,activation) + dRWT(nfcdep) + dT(nfcdep,initiator)'. This gives a timeout value between 1237 ms and 1337 ms. This patch defines DIGITAL_ATR_RES_RWT to 1337 used for the timeout value of ATR_REQ command. For other DEP PDUs, the initiator must wait between 'RWT + dRWT(nfcdep)' and 'RWT + dRWT(nfcdep) + dT(nfcdep,initiator)' where RWT is given by the following formula: '(256 * 16 / f(c)) * 2^wt' where wt is the value of the TO field in the ATR_RES response and is in the range between 0 and 14. This patch declares a mapping table for wt values and gives RWT max values between 100 ms and 5049 ms. This patch also defines DIGITAL_ATR_RES_TO_WT, the maximum wt value in target mode, to 8. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 1 + net/nfc/digital_dep.c | 71 ++++++++++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index f9a4e4771861..74fa7eb94e72 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -226,6 +226,7 @@ struct nfc_digital_dev { u8 curr_rf_tech; u8 curr_nfc_dep_pni; u8 did; + u16 dep_rwt; u8 local_payload_max; u8 remote_payload_max; diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index ba52a5dbf3cc..6cf2eeb2e865 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -35,6 +35,8 @@ #define DIGITAL_ATR_REQ_MIN_SIZE 16 #define DIGITAL_ATR_REQ_MAX_SIZE 64 +#define DIGITAL_ATR_RES_TO_WT(s) ((s) & 0xF) + #define DIGITAL_DID_MAX 14 #define DIGITAL_PAYLOAD_SIZE_MAX 254 @@ -122,6 +124,37 @@ static const u8 digital_payload_bits_map[4] = { [3] = 254 }; +/* Response Waiting Time for ATR_RES PDU in ms + * + * RWT(ATR_RES) = RWT(nfcdep,activation) + dRWT(nfcdep) + dT(nfcdep,initiator) + * + * with: + * RWT(nfcdep,activation) = 4096 * 2^12 / f(c) s + * dRWT(nfcdep) = 16 / f(c) s + * dT(nfcdep,initiator) = 100 ms + * f(c) = 13560000 Hz + */ +#define DIGITAL_ATR_RES_RWT 1337 + +/* Response Waiting Time for other DEP PDUs in ms + * + * max_rwt = rwt + dRWT(nfcdep) + dT(nfcdep,initiator) + * + * with: + * rwt = (256 * 16 / f(c)) * 2^wt s + * dRWT(nfcdep) = 16 / f(c) s + * dT(nfcdep,initiator) = 100 ms + * f(c) = 13560000 Hz + * 0 <= wt <= 14 (given by the target by the TO field of ATR_RES response) + */ +#define DIGITAL_NFC_DEP_IN_MAX_WT 14 +#define DIGITAL_NFC_DEP_TG_MAX_WT 8 +static const u16 digital_rwt_map[DIGITAL_NFC_DEP_IN_MAX_WT + 1] = { + 100, 101, 101, 102, 105, + 110, 119, 139, 177, 255, + 409, 719, 1337, 2575, 5049, +}; + static u8 digital_payload_bits_to_size(u8 payload_bits) { if (payload_bits >= ARRAY_SIZE(digital_payload_bits_map)) @@ -366,8 +399,8 @@ static int digital_in_send_psl_req(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res, - target); + rc = digital_in_send_cmd(ddev, skb, ddev->dep_rwt, + digital_in_recv_psl_res, target); if (rc) kfree_skb(skb); @@ -380,6 +413,7 @@ static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg, struct nfc_target *target = arg; struct digital_atr_res *atr_res; u8 gb_len, payload_bits; + u8 wt; int rc; if (IS_ERR(resp)) { @@ -409,6 +443,11 @@ static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg, atr_res = (struct digital_atr_res *)resp->data; + wt = DIGITAL_ATR_RES_TO_WT(atr_res->to); + if (wt > DIGITAL_NFC_DEP_IN_MAX_WT) + wt = DIGITAL_NFC_DEP_IN_MAX_WT; + ddev->dep_rwt = digital_rwt_map[wt]; + payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_res->pp); ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits); @@ -490,8 +529,8 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, - target); + rc = digital_in_send_cmd(ddev, skb, DIGITAL_ATR_RES_RWT, + digital_in_recv_atr_res, target); if (rc) kfree_skb(skb); @@ -524,8 +563,8 @@ static int digital_in_send_ack(struct nfc_digital_dev *ddev, ddev->saved_skb = pskb_copy(skb, GFP_KERNEL); - rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, - data_exch); + rc = digital_in_send_cmd(ddev, skb, ddev->dep_rwt, + digital_in_recv_dep_res, data_exch); if (rc) { kfree_skb(skb); kfree_skb(ddev->saved_skb); @@ -559,8 +598,8 @@ static int digital_in_send_nack(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, - data_exch); + rc = digital_in_send_cmd(ddev, skb, ddev->dep_rwt, + digital_in_recv_dep_res, data_exch); if (rc) kfree_skb(skb); @@ -590,8 +629,8 @@ static int digital_in_send_atn(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, - data_exch); + rc = digital_in_send_cmd(ddev, skb, ddev->dep_rwt, + digital_in_recv_dep_res, data_exch); if (rc) kfree_skb(skb); @@ -624,8 +663,8 @@ static int digital_in_send_rtox(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, - data_exch); + rc = digital_in_send_cmd(ddev, skb, ddev->dep_rwt, + digital_in_recv_dep_res, data_exch); if (rc) kfree_skb(skb); @@ -642,7 +681,7 @@ static int digital_in_send_saved_skb(struct nfc_digital_dev *ddev, skb_get(ddev->saved_skb); - rc = digital_in_send_cmd(ddev, ddev->saved_skb, 1500, + rc = digital_in_send_cmd(ddev, ddev->saved_skb, ddev->dep_rwt, digital_in_recv_dep_res, data_exch); if (rc) kfree_skb(ddev->saved_skb); @@ -885,8 +924,8 @@ int digital_in_send_dep_req(struct nfc_digital_dev *ddev, ddev->saved_skb = pskb_copy(tmp_skb, GFP_KERNEL); - rc = digital_in_send_cmd(ddev, tmp_skb, 1500, digital_in_recv_dep_res, - data_exch); + rc = digital_in_send_cmd(ddev, tmp_skb, ddev->dep_rwt, + digital_in_recv_dep_res, data_exch); if (rc) { if (tmp_skb != skb) kfree_skb(tmp_skb); @@ -1465,7 +1504,7 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev, atr_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; atr_res->cmd = DIGITAL_CMD_ATR_RES; memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3)); - atr_res->to = 8; + atr_res->to = DIGITAL_NFC_DEP_TG_MAX_WT; ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX; payload_bits = digital_payload_size_to_bits(ddev->local_payload_max); From d85a301c26621d3466956dc477c32c20c15a52ee Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 8 Jul 2016 15:52:46 +0200 Subject: [PATCH 29/31] NFC: digital: Fix RTOX supervisor PDU handling When the target needs more time to process the received PDU, it sends Response Timeout Extension (RTOX) PDU. When the initiator receives a RTOX PDU, it must reply with a RTOX PDU and extends the current rwt value with the formula: rwt_int = rwt * rtox This patch takes care of the rtox value passed by the target in the RTOX PDU and extends the timeout for the next response accordingly. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 6cf2eeb2e865..f864ce19e13d 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -65,6 +65,9 @@ #define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_DID_BIT) #define DIGITAL_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03) +#define DIGITAL_NFC_DEP_RTOX_VALUE(data) ((data) & 0x3F) +#define DIGITAL_NFC_DEP_RTOX_MAX 59 + #define DIGITAL_NFC_DEP_PFB_I_PDU 0x00 #define DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU 0x40 #define DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU 0x80 @@ -643,6 +646,11 @@ static int digital_in_send_rtox(struct nfc_digital_dev *ddev, struct digital_dep_req_res *dep_req; struct sk_buff *skb; int rc; + u16 rwt_int; + + rwt_int = ddev->dep_rwt * rtox; + if (rwt_int > digital_rwt_map[DIGITAL_NFC_DEP_IN_MAX_WT]) + rwt_int = digital_rwt_map[DIGITAL_NFC_DEP_IN_MAX_WT]; skb = digital_skb_alloc(ddev, 1); if (!skb) @@ -663,7 +671,7 @@ static int digital_in_send_rtox(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - rc = digital_in_send_cmd(ddev, skb, ddev->dep_rwt, + rc = digital_in_send_cmd(ddev, skb, rwt_int, digital_in_recv_dep_res, data_exch); if (rc) kfree_skb(skb); @@ -697,6 +705,7 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, u8 pfb; uint size; int rc; + u8 rtox; if (IS_ERR(resp)) { rc = PTR_ERR(resp); @@ -865,7 +874,20 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, goto free_resp; } - rc = digital_in_send_rtox(ddev, data_exch, resp->data[0]); + if (ddev->atn_count || ddev->nack_count) { + PROTOCOL_ERR("14.12.4.4"); + rc = -EIO; + goto error; + } + + rtox = DIGITAL_NFC_DEP_RTOX_VALUE(resp->data[0]); + if (!rtox || rtox > DIGITAL_NFC_DEP_RTOX_MAX) { + PROTOCOL_ERR("14.8.4.1"); + rc = -EIO; + goto error; + } + + rc = digital_in_send_rtox(ddev, data_exch, rtox); if (rc) goto error; From f9ac6273e5b8fa45e7cd2086e1bbc91af9af7f19 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 19 Jul 2016 11:58:16 +0200 Subject: [PATCH 30/31] NFC: nfcsim: Add support for sysfs control entry The idea is to have a way to control and/or modify the behavior of the nfcsim virtual devices. This patch creates a folder tree in the debug filesystem. The debugfs is usually mounted into /sys/kernel/debug and the nfcsim entries are located in DEBUGFS/nfcsim/nfcX/ where X is either 0 or 1 depending on the device you want to address. These folders are empty for now and control entries will be added by upcoming commits. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcsim.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 40b4846509f4..97067a5f248c 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -329,6 +331,49 @@ static struct nfc_digital_ops nfcsim_digital_ops = { .switch_rf = nfcsim_switch_rf, }; +static struct dentry *nfcsim_debugfs_root; + +static void nfcsim_debugfs_init(void) +{ + nfcsim_debugfs_root = debugfs_create_dir("nfcsim", NULL); + + if (!nfcsim_debugfs_root) + pr_err("Could not create debugfs entry\n"); + +} + +static void nfcsim_debugfs_remove(void) +{ + debugfs_remove_recursive(nfcsim_debugfs_root); +} + +static void nfcsim_debugfs_init_dev(struct nfcsim *dev) +{ + struct dentry *dev_dir; + char devname[5]; /* nfcX\0 */ + u32 idx; + int n; + + if (!nfcsim_debugfs_root) { + NFCSIM_ERR(dev, "nfcsim debugfs not initialized\n"); + return; + } + + idx = dev->nfc_digital_dev->nfc_dev->idx; + n = snprintf(devname, sizeof(devname), "nfc%d", idx); + if (n >= sizeof(devname)) { + NFCSIM_ERR(dev, "Could not compute dev name for dev %d\n", idx); + return; + } + + dev_dir = debugfs_create_dir(devname, nfcsim_debugfs_root); + if (!dev_dir) { + NFCSIM_ERR(dev, "Could not create debugfs entries for nfc%d\n", + idx); + return; + } +} + static struct nfcsim *nfcsim_device_new(struct nfcsim_link *link_in, struct nfcsim_link *link_out) { @@ -366,6 +411,8 @@ static struct nfcsim *nfcsim_device_new(struct nfcsim_link *link_in, return ERR_PTR(rc); } + nfcsim_debugfs_init_dev(dev); + return dev; } @@ -400,6 +447,8 @@ static int __init nfcsim_init(void) goto exit_err; } + nfcsim_debugfs_init(); + dev0 = nfcsim_device_new(link0, link1); if (IS_ERR(dev0)) { rc = PTR_ERR(dev0); @@ -439,6 +488,8 @@ static void __exit nfcsim_exit(void) nfcsim_link_free(link0); nfcsim_link_free(link1); + + nfcsim_debugfs_remove(); } module_init(nfcsim_init); From 2a0fe4fe5bf2a6e2277354e7e8f369a20d881891 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 19 Jul 2016 11:58:17 +0200 Subject: [PATCH 31/31] NFC: nfcsim: Simulate lost frames through debugfs entry This patch allows to simulate the lost of frames exchanged between the 2 nfcsim devices through a control entry in the debugfs and is used as follow: echo n > /sys/kernel/debug/nfcsim/nfcX/dropframe Where n specifies the number of frames to be dropped between 0 and 255 and nfcX is either nfc0 or nfc1, one of the two nfcsim devices. In the following example, the next frame that should be sent by the nfc0 device will be dropped and thus not received by the nfc1 device: echo 1 > /sys/kernel/debug/nfcsim/nfc0/dropframe The value of 0 can be used to reset the dropframe counter. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcsim.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 97067a5f248c..a466e7978466 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -54,6 +54,8 @@ struct nfcsim { nfc_digital_cmd_complete_t cb; void *arg; + + u8 dropframe; }; struct nfcsim_link { @@ -223,6 +225,14 @@ static int nfcsim_send(struct nfc_digital_dev *ddev, struct sk_buff *skb, schedule_work(&dev->recv_work); + if (dev->dropframe) { + NFCSIM_DBG(dev, "dropping frame (out of %d)\n", dev->dropframe); + dev_kfree_skb(skb); + dev->dropframe--; + + return 0; + } + if (skb) { nfcsim_link_set_skb(dev->link_out, skb, dev->rf_tech, dev->mode); @@ -372,6 +382,8 @@ static void nfcsim_debugfs_init_dev(struct nfcsim *dev) idx); return; } + + debugfs_create_u8("dropframe", 0664, dev_dir, &dev->dropframe); } static struct nfcsim *nfcsim_device_new(struct nfcsim_link *link_in,