summaryrefslogtreecommitdiffstats
path: root/drivers/nfc
diff options
context:
space:
mode:
authorThierry Escande <thierry.escande@collabora.com>2016-06-16 20:25:21 +0200
committerSamuel Ortiz <sameo@linux.intel.com>2016-07-06 10:02:07 +0200
commitb74584c1a6d68110d135a6ce0336aab0055f4341 (patch)
tree86cd2ef99b4e43c242ca0b84c4ed0efce68fed1c /drivers/nfc
parentNFC: port100: Don't send a new command if one is still pending (diff)
downloadlinux-b74584c1a6d68110d135a6ce0336aab0055f4341.tar.xz
linux-b74584c1a6d68110d135a6ce0336aab0055f4341.zip
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 <thierry.escande@collabora.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc')
-rw-r--r--drivers/nfc/port100.c42
1 files 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);