From f36acc334fb86da3761583dce50faa17a4cbd4de Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 22 Mar 2016 23:17:00 +0000 Subject: 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(-) (limited to 'drivers/nfc') 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; } } -- cgit v1.2.3 From 6d2f70cae481a2e8e85a244d3b2d36828dadff83 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 17 May 2016 10:32:19 +0300 Subject: 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(-) (limited to 'drivers/nfc') 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 @@ error: usb_free_urb(phy->out_urb); usb_put_dev(phy->udev); kfree(in_buf); -out_free_phy: - kfree(phy); + return rc; } -- cgit v1.2.3 From fa1ce54ea38f7f83473fce62e64fefbd7ebd170e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 5 Jun 2016 11:17:10 +0200 Subject: 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(-) (limited to 'drivers/nfc') 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) -- cgit v1.2.3 From 58d46f538b602d4a93fbae945e5c844a90c01f14 Mon Sep 17 00:00:00 2001 From: Geoff Lansberry Date: Mon, 18 Apr 2016 15:48:39 -0400 Subject: 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(+) (limited to 'drivers/nfc') 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; -- cgit v1.2.3 From a81ba50a89930a96e34862cf236b4f4461e741ae Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 7 Jun 2016 16:21:51 +0200 Subject: 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(-) (limited to 'drivers/nfc') 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 }, -- cgit v1.2.3 From 204bddcb508fe3bca5c97a9f528bafd7ba8fcec8 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 23 Jun 2016 11:20:26 +0200 Subject: 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 | 612 ++++++++++++++++++++++----------------------------- 1 file changed, 258 insertions(+), 354 deletions(-) (limited to 'drivers/nfc') 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 mutex lock; - - struct delayed_work recv_work; + struct work_struct recv_work; + struct delayed_work send_work; - struct sk_buff *clone_skb; + struct nfcsim_link *link_in; + struct nfcsim_link *link_out; - struct delayed_work poll_work; - u8 polling_mode; - u8 curr_polling_mode; + bool up; + u8 mode; + u8 rf_tech; - u8 shutting_down; + u16 recv_timeout; - u8 up; + nfc_digital_cmd_complete_t cb; + void *arg; +}; - u8 initiator; +struct nfcsim_link { + struct mutex lock; - u32 rx_delay; + u8 rf_tech; + u8 mode; - data_exchange_cb_t cb; - void *cb_context; + u8 shutdown; - struct nfcsim *peer_dev; + struct sk_buff *skb; + wait_queue_head_t recv_wait; + u8 cond; }; -static struct nfcsim *dev0; -static struct nfcsim *dev1; - -static struct workqueue_struct *wq; - -static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown) +static struct nfcsim_link *nfcsim_link_new(void) { - DEV_DBG(dev, "shutdown=%d\n", shutdown); - - mutex_lock(&dev->lock); + struct nfcsim_link *link; - dev->polling_mode = NFCSIM_POLL_NONE; - dev->shutting_down = shutdown; - dev->cb = NULL; - dev_kfree_skb(dev->clone_skb); - dev->clone_skb = NULL; + link = kzalloc(sizeof(struct nfcsim_link), GFP_KERNEL); + if (!link) + return NULL; - mutex_unlock(&dev->lock); + mutex_init(&link->lock); + init_waitqueue_head(&link->recv_wait); - cancel_delayed_work_sync(&dev->poll_work); - cancel_delayed_work_sync(&dev->recv_work); + return link; } -static int nfcsim_target_found(struct nfcsim *dev) +static void nfcsim_link_free(struct nfcsim_link *link) { - struct nfc_target nfc_tgt; + dev_kfree_skb(link->skb); + kfree(link); +} - DEV_DBG(dev, "\n"); +static void nfcsim_link_recv_wake(struct nfcsim_link *link) +{ + link->cond = 1; + wake_up_interruptible(&link->recv_wait); +} - memset(&nfc_tgt, 0, sizeof(struct nfc_target)); +static void nfcsim_link_set_skb(struct nfcsim_link *link, struct sk_buff *skb, + u8 rf_tech, u8 mode) +{ + mutex_lock(&link->lock); - nfc_tgt.supported_protocols = NFC_PROTO_NFC_DEP_MASK; - nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1); + dev_kfree_skb(link->skb); + link->skb = skb; + link->rf_tech = rf_tech; + link->mode = mode; - return 0; + mutex_unlock(&link->lock); } -static int nfcsim_dev_up(struct nfc_dev *nfc_dev) +static void nfcsim_link_recv_cancel(struct nfcsim_link *link) { - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + mutex_lock(&link->lock); - DEV_DBG(dev, "\n"); + link->mode = NFCSIM_MODE_NONE; - mutex_lock(&dev->lock); + mutex_unlock(&link->lock); - dev->up = 1; - - mutex_unlock(&dev->lock); - - return 0; + nfcsim_link_recv_wake(link); } -static int nfcsim_dev_down(struct nfc_dev *nfc_dev) +static void nfcsim_link_shutdown(struct nfcsim_link *link) { - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - - DEV_DBG(dev, "\n"); + mutex_lock(&link->lock); - mutex_lock(&dev->lock); + link->shutdown = 1; + link->mode = NFCSIM_MODE_NONE; - dev->up = 0; + mutex_unlock(&link->lock); - mutex_unlock(&dev->lock); - - return 0; + nfcsim_link_recv_wake(link); } -static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev, - struct nfc_target *target, - u8 comm_mode, u8 *gb, size_t gb_len) +static struct sk_buff *nfcsim_link_recv_skb(struct nfcsim_link *link, + int timeout, u8 rf_tech, u8 mode) { int rc; - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - struct nfcsim *peer = dev->peer_dev; - u8 *remote_gb; - size_t remote_gb_len; + struct sk_buff *skb; - DEV_DBG(dev, "target_idx: %d, comm_mode: %d\n", target->idx, comm_mode); + rc = wait_event_interruptible_timeout(link->recv_wait, + link->cond, + msecs_to_jiffies(timeout)); - mutex_lock(&peer->lock); + mutex_lock(&link->lock); - nfc_tm_activated(peer->nfc_dev, NFC_PROTO_NFC_DEP_MASK, - NFC_COMM_ACTIVE, gb, gb_len); + skb = link->skb; + link->skb = NULL; - 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"); + if (!rc) { + rc = -ETIMEDOUT; + goto done; + } - mutex_unlock(&peer->lock); - return -EINVAL; + if (!skb || link->rf_tech != rf_tech || link->mode == mode) { + rc = -EINVAL; + goto done; } - mutex_unlock(&peer->lock); + if (link->shutdown) { + rc = -ENODEV; + goto done; + } - mutex_lock(&dev->lock); +done: + mutex_unlock(&link->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; + if (rc < 0) { + dev_kfree_skb(skb); + skb = ERR_PTR(rc); } - rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_ACTIVE, - NFC_RF_INITIATOR); - - mutex_unlock(&dev->lock); + link->cond = 0; - return rc; + return skb; } -static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev) +static void nfcsim_send_wq(struct work_struct *work) { - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + struct nfcsim *dev = container_of(work, struct nfcsim, send_work.work); - DEV_DBG(dev, "\n"); - - nfcsim_cleanup_dev(dev, 0); - - return 0; + /* + * 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 int nfcsim_start_poll(struct nfc_dev *nfc_dev, - u32 im_protocols, u32 tm_protocols) +static void nfcsim_recv_wq(struct work_struct *work) { - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - int rc; - - mutex_lock(&dev->lock); + struct nfcsim *dev = container_of(work, struct nfcsim, recv_work); + struct sk_buff *skb; - if (dev->polling_mode != NFCSIM_POLL_NONE) { - DEV_ERR(dev, "Already in polling mode\n"); - rc = -EBUSY; - goto exit; - } + skb = nfcsim_link_recv_skb(dev->link_in, dev->recv_timeout, + dev->rf_tech, dev->mode); - if (im_protocols & NFC_PROTO_NFC_DEP_MASK) - dev->polling_mode |= NFCSIM_POLL_INITIATOR; + if (!dev->up) { + NFCSIM_ERR(dev, "Device is down\n"); - if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) - dev->polling_mode |= NFCSIM_POLL_TARGET; + if (!IS_ERR(skb)) + dev_kfree_skb(skb); - if (dev->polling_mode == NFCSIM_POLL_NONE) { - DEV_ERR(dev, "Unsupported polling mode\n"); - rc = -EINVAL; - goto exit; + skb = ERR_PTR(-ENODEV); } - 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; + dev->cb(dev->nfc_digital_dev, dev->arg, skb); } -static void nfcsim_stop_poll(struct nfc_dev *nfc_dev) +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_get_drvdata(nfc_dev); + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); + u8 delay; - DEV_DBG(dev, "Stop poll\n"); + if (!dev->up) { + NFCSIM_ERR(dev, "Device is down\n"); + return -ENODEV; + } + + dev->recv_timeout = timeout; + dev->cb = cb; + dev->arg = arg; - mutex_lock(&dev->lock); + schedule_work(&dev->recv_work); - dev->polling_mode = NFCSIM_POLL_NONE; + if (skb) { + nfcsim_link_set_skb(dev->link_out, skb, dev->rf_tech, + dev->mode); - mutex_unlock(&dev->lock); + /* Add random delay (between 3 and 10 ms) before sending data */ + get_random_bytes(&delay, 1); + delay = 3 + (delay & 0x07); - cancel_delayed_work_sync(&dev->poll_work); + schedule_delayed_work(&dev->send_work, msecs_to_jiffies(delay)); + } + + return 0; } -static int nfcsim_activate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target, u32 protocol) +static void nfcsim_abort_cmd(struct nfc_digital_dev *ddev) { - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - - DEV_DBG(dev, "\n"); + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); - return -ENOTSUPP; + nfcsim_link_recv_cancel(dev->link_in); } -static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev, - struct nfc_target *target, u8 mode) +static int nfcsim_switch_rf(struct nfc_digital_dev *ddev, bool on) { - struct nfcsim *dev = nfc_get_drvdata(nfc_dev); + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); - DEV_DBG(dev, "\n"); + dev->up = on; + + return 0; } -static void nfcsim_wq_recv(struct work_struct *work) +static int nfcsim_in_configure_hw(struct nfc_digital_dev *ddev, + int type, int param) { - struct nfcsim *dev = container_of(work, struct nfcsim, - recv_work.work); + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); - mutex_lock(&dev->lock); + switch (type) { + case NFC_DIGITAL_CONFIG_RF_TECH: + dev->up = true; + dev->mode = NFCSIM_MODE_INITIATOR; + dev->rf_tech = param; + break; - if (dev->shutting_down || !dev->up || !dev->clone_skb) { - dev_kfree_skb(dev->clone_skb); - goto exit; - } + case NFC_DIGITAL_CONFIG_FRAMING: + break; - 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); + default: + NFCSIM_ERR(dev, "Invalid configuration type: %d\n", type); + return -EINVAL; } -exit: - dev->clone_skb = NULL; - - mutex_unlock(&dev->lock); + return 0; } -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) +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) { - 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; + return nfcsim_send(ddev, skb, timeout, cb, arg); +} - mutex_unlock(&dev->lock); +static int nfcsim_tg_configure_hw(struct nfc_digital_dev *ddev, + int type, int param) +{ + struct nfcsim *dev = nfc_digital_get_drvdata(ddev); - mutex_lock(&peer->lock); + switch (type) { + case NFC_DIGITAL_CONFIG_RF_TECH: + dev->up = true; + dev->mode = NFCSIM_MODE_TARGET; + dev->rf_tech = param; + break; - peer->clone_skb = skb_clone(skb, GFP_KERNEL); + case NFC_DIGITAL_CONFIG_FRAMING: + break; - if (!peer->clone_skb) { - DEV_ERR(dev, "skb_clone failed\n"); - mutex_unlock(&peer->lock); - err = -ENOMEM; - goto exit; + default: + NFCSIM_ERR(dev, "Invalid configuration type: %d\n", type); + return -EINVAL; } - /* 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; + return 0; } -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) +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_tx(nfc_dev, target, skb, cb, cb_context); + return nfcsim_send(ddev, skb, timeout, cb, arg); } -static int nfcsim_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) +static int nfcsim_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) { - return nfcsim_tx(nfc_dev, NULL, skb, NULL, NULL); + return nfcsim_send(ddev, NULL, timeout, cb, arg); } -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; - } +static struct nfc_digital_ops nfcsim_digital_ops = { + .in_configure_hw = nfcsim_in_configure_hw, + .in_send_cmd = nfcsim_in_send_cmd, - 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; + .tg_listen = nfcsim_tg_listen, + .tg_configure_hw = nfcsim_tg_configure_hw, + .tg_send_cmd = nfcsim_tg_send_cmd, - 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; - } -} + .abort_cmd = nfcsim_abort_cmd, + .switch_rf = nfcsim_switch_rf, +}; -static void nfcsim_wq_poll(struct work_struct *work) +static struct nfcsim *nfcsim_device_new(struct nfcsim_link *link_in, + struct nfcsim_link *link_out) { - struct nfcsim *dev = container_of(work, struct nfcsim, poll_work.work); - struct nfcsim *peer = dev->peer_dev; + struct nfcsim *dev; + int rc; - /* 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); + dev = kzalloc(sizeof(struct nfcsim), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); - nfcsim_set_polling_mode(dev); + INIT_DELAYED_WORK(&dev->send_work, nfcsim_send_wq); + INIT_WORK(&dev->recv_work, nfcsim_recv_wq); - if (dev->curr_polling_mode == NFCSIM_POLL_NONE) { - DEV_DBG(dev, "Not polling\n"); - goto unlock; + 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); } - 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; + nfc_digital_set_drvdata(dev->nfc_digital_dev, dev); - if (peer->curr_polling_mode == NFCSIM_POLL_TARGET) { - peer->polling_mode = NFCSIM_POLL_NONE; - dev->polling_mode = NFCSIM_POLL_NONE; + dev->link_in = link_in; + dev->link_out = link_out; - dev->initiator = 1; - - nfcsim_target_found(dev); + 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); - goto unlock; + return ERR_PTR(rc); } -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); + return dev; } -static struct nfcsim *nfcsim_init_dev(void) +static void nfcsim_device_free(struct nfcsim *dev) { - 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_digital_unregister_device(dev->nfc_digital_dev); - nfc_set_drvdata(dev->nfc_dev, dev); + dev->up = false; - rc = nfc_register_device(dev->nfc_dev); - if (rc) - goto free_nfc_dev; + nfcsim_link_shutdown(dev->link_in); - dev->rx_delay = RX_DEFAULT_DELAY; - return dev; + cancel_delayed_work_sync(&dev->send_work); + cancel_work_sync(&dev->recv_work); -free_nfc_dev: - nfc_free_device(dev->nfc_dev); + nfc_digital_free_device(dev->nfc_digital_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 struct nfcsim *dev0; +static struct nfcsim *dev1; 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; + +exit_err: + pr_err("Failed to initialize nfcsim driver (%d)\n", rc); - rc = 0; -exit: - if (rc) - 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; + + link0 = dev0->link_in; + link1 = dev0->link_out; - nfcsim_free_device(dev0); - nfcsim_free_device(dev1); + nfcsim_device_free(dev0); + nfcsim_device_free(dev1); - destroy_workqueue(wq); + nfcsim_link_free(link0); + nfcsim_link_free(link1); } module_init(nfcsim_init); -- cgit v1.2.3 From e3e0258839a01f793a8a0c0885e8ad387681cdc6 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:25:20 +0200 Subject: 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(+) (limited to 'drivers/nfc') 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; -- cgit v1.2.3 From b74584c1a6d68110d135a6ce0336aab0055f4341 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:25:21 +0200 Subject: 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(-) (limited to 'drivers/nfc') 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); -- cgit v1.2.3 From a52bd7d2753b0567c71d604c640e9c4a86221756 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:25:22 +0200 Subject: 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(-) (limited to 'drivers/nfc') 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: -- cgit v1.2.3 From 9f0c4542c49cbd041f2b6943b16644af0a3ff48f Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 16 Jun 2016 20:25:23 +0200 Subject: 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(+) (limited to 'drivers/nfc') 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)) -- cgit v1.2.3 From 8f49bec6c36c73f0c212e07229b19fb4bba85788 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 4 Jul 2016 16:42:55 +0200 Subject: 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(+) (limited to 'drivers/nfc') 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 -- cgit v1.2.3 From f9ac6273e5b8fa45e7cd2086e1bbc91af9af7f19 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 19 Jul 2016 11:58:16 +0200 Subject: 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(+) (limited to 'drivers/nfc') 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); -- cgit v1.2.3 From 2a0fe4fe5bf2a6e2277354e7e8f369a20d881891 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 19 Jul 2016 11:58:17 +0200 Subject: 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(+) (limited to 'drivers/nfc') 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, -- cgit v1.2.3