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-58-ga151