Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72ec46f5 authored by Anson Jacob's avatar Anson Jacob Committed by Dmitry Shmidt
Browse files

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: default avatarDarren Whobrey <d.whobrey@mildai.org>
Signed-off-by: default avatarAnson Jacob <ansonjacob.aj@gmail.com>
parent a711fa7f
Loading
Loading
Loading
Loading
+17 −5
Original line number Diff line number Diff line
@@ -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;
}

@@ -763,7 +766,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;
}

@@ -1011,6 +1017,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++)
@@ -1142,6 +1152,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);
@@ -1154,7 +1165,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);