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>
This commit is contained in:
Thierry Escande 2016-06-16 20:25:21 +02:00 committed by Samuel Ortiz
parent e3e0258839
commit b74584c1a6

View File

@ -456,6 +456,12 @@ struct port100 {
struct urb *out_urb; struct urb *out_urb;
struct urb *in_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; struct work_struct cmd_complete_work;
u8 cmd_type; u8 cmd_type;
@ -464,6 +470,8 @@ struct port100 {
* for any queuing/locking mechanism at driver level. * for any queuing/locking mechanism at driver level.
*/ */
struct port100_cmd *cmd; struct port100_cmd *cmd;
bool cmd_cancel;
}; };
struct port100_cmd { struct port100_cmd {
@ -718,10 +726,22 @@ static int port100_send_ack(struct port100 *dev)
{ {
int rc; 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 = ack_frame;
dev->out_urb->transfer_buffer_length = sizeof(ack_frame); dev->out_urb->transfer_buffer_length = sizeof(ack_frame);
rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); 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; return rc;
} }
@ -730,6 +750,16 @@ static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out,
{ {
int rc; 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 = out->data;
dev->out_urb->transfer_buffer_length = out->len; 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); rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
if (rc) if (rc)
return rc; goto exit;
rc = port100_submit_urb_for_ack(dev, GFP_KERNEL); rc = port100_submit_urb_for_ack(dev, GFP_KERNEL);
if (rc) 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; return rc;
} }
@ -892,6 +921,8 @@ static void port100_send_complete(struct urb *urb)
{ {
struct port100 *dev = urb->context; struct port100 *dev = urb->context;
dev->cmd_cancel = false;
switch (urb->status) { switch (urb->status) {
case 0: case 0:
break; /* success */ break; /* success */
@ -1455,6 +1486,7 @@ static int port100_probe(struct usb_interface *interface,
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
mutex_init(&dev->out_urb_lock);
dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface; dev->interface = interface;
usb_set_intfdata(interface, dev); usb_set_intfdata(interface, dev);