aboutsummaryrefslogtreecommitdiffstats
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
parente3e0258839a01f793a8a0c0885e8ad387681cdc6 (diff)
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>
-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 14a3cc2..909e3df 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);