OSDN Git Service

usb: gadget: f_accessory: Fix for UsbAccessory clean unbind.
authorAnson Jacob <ansonjacob.aj@gmail.com>
Sat, 13 Aug 2016 00:38:10 +0000 (20:38 -0400)
committerDmitry Shmidt <dimitrysh@google.com>
Mon, 13 Mar 2017 18:02:03 +0000 (18:02 +0000)
Reapplying fix by Darren Whobrey (Change 69674)

Fixes issues: 20545, 59667 and 61390.
With prior version of f_accessory.c, UsbAccessories would not
unbind cleanly when application is closed or i/o stopped
while the usb cable is still connected. The accessory gadget
driver would be left in an invalid state which was not reset
on subsequent binding or opening. A reboot was necessary to clear.

In some phones this issues causes the phone to reboot upon
unplugging the USB cable.

Main problem was that acc_disconnect was being called on I/O error
which reset disconnected and online.

Minor fix required to properly track setting and unsetting of
disconnected and online flags. Also added urb Q wakeup's on unbind
to help unblock waiting threads.

Tested on Nexus 7 grouper. Expected behaviour now observed:
closing accessory causes blocked i/o to interrupt with IOException.
Accessory can be restarted following closing of file handle
and re-opening.

This is a generic fix that applies to all devices.

Change-Id: I4e08b326730dd3a2820c863124cee10f7cb5501e
Signed-off-by: Darren Whobrey <d.whobrey@mildai.org>
Signed-off-by: Anson Jacob <ansonjacob.aj@gmail.com>
drivers/usb/gadget/function/f_accessory.c

index f2fa0c2..76b8ae0 100644 (file)
@@ -77,9 +77,13 @@ struct acc_dev {
        struct usb_ep *ep_in;
        struct usb_ep *ep_out;
 
-       /* set to 1 when we connect */
+       /* online indicates state of function_set_alt & function_unbind
+        * set to 1 when we connect
+        */
        int online:1;
-       /* Set to 1 when we disconnect.
+
+       /* disconnected indicates state of open & release
+        * Set to 1 when we disconnect.
         * Not cleared until our file is closed.
         */
        int disconnected:1;
@@ -263,7 +267,6 @@ static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head)
 
 static void acc_set_disconnected(struct acc_dev *dev)
 {
-       dev->online = 0;
        dev->disconnected = 1;
 }
 
@@ -764,7 +767,10 @@ static int acc_release(struct inode *ip, struct file *fp)
        printk(KERN_INFO "acc_release\n");
 
        WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0));
-       _acc_dev->disconnected = 0;
+       /* indicate that we are disconnected
+        * still could be online so don't touch online flag
+        */
+       _acc_dev->disconnected = 1;
        return 0;
 }
 
@@ -1012,6 +1018,10 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
        struct usb_request *req;
        int i;
 
+       dev->online = 0;                /* clear online flag */
+       wake_up(&dev->read_wq);         /* unblock reads on closure */
+       wake_up(&dev->write_wq);        /* likewise for writes */
+
        while ((req = req_get(dev, &dev->tx_idle)))
                acc_request_free(req, dev->ep_in);
        for (i = 0; i < RX_REQ_MAX; i++)
@@ -1143,6 +1153,7 @@ static int acc_function_set_alt(struct usb_function *f,
        }
 
        dev->online = 1;
+       dev->disconnected = 0; /* if online then not disconnected */
 
        /* readers may be blocked waiting for us to go online */
        wake_up(&dev->read_wq);
@@ -1155,7 +1166,8 @@ static void acc_function_disable(struct usb_function *f)
        struct usb_composite_dev        *cdev = dev->cdev;
 
        DBG(cdev, "acc_function_disable\n");
-       acc_set_disconnected(dev);
+       acc_set_disconnected(dev); /* this now only sets disconnected */
+       dev->online = 0; /* so now need to clear online flag here too */
        usb_ep_disable(dev->ep_in);
        usb_ep_disable(dev->ep_out);