ANDROID: usb: gadget: f_accessory: Fix for UsbAccessory clean unbind.

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>
This commit is contained in:
Anson Jacob 2016-08-12 20:38:10 -04:00 committed by Amit Pundir
parent 0900cbbc91
commit f7adc63862

View 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;
}
@ -756,7 +759,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;
}
@ -1004,6 +1010,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++)
@ -1135,6 +1145,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);
@ -1147,7 +1158,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);