diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c
index 24886aa29e84f161b451a75dd9f36084471f987f..2ebc0363843e336d1c5388a38d64ab700612cb23 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -41,6 +41,8 @@
 #define BULK_BUFFER_SIZE    16384
 #define ACC_STRING_SIZE     256
 
+#define PROTOCOL_VERSION    1
+
 /* String IDs */
 #define INTERFACE_STRING_INDEX	0
 
@@ -56,13 +58,19 @@ struct acc_dev {
 	struct usb_ep *ep_in;
 	struct usb_ep *ep_out;
 
+	/* set to 1 when we connect */
 	int online:1;
+	/* Set to 1 when we disconnect.
+	 * Not cleared until our file is closed.
+	 */
+	int disconnected:1;
 
 	/* strings sent by the host */
 	char manufacturer[ACC_STRING_SIZE];
 	char model[ACC_STRING_SIZE];
-	char type[ACC_STRING_SIZE];
+	char description[ACC_STRING_SIZE];
 	char version[ACC_STRING_SIZE];
+	char uri[ACC_STRING_SIZE];
 
 	/* for acc_complete_set_string */
 	int string_index;
@@ -180,21 +188,6 @@ static void acc_request_free(struct usb_request *req, struct usb_ep *ep)
 	}
 }
 
-static inline int _lock(atomic_t *excl)
-{
-	if (atomic_inc_return(excl) == 1) {
-		return 0;
-	} else {
-		atomic_dec(excl);
-		return -1;
-	}
-}
-
-static inline void _unlock(atomic_t *excl)
-{
-	atomic_dec(excl);
-}
-
 /* add a request to the tail of a list */
 static void req_put(struct acc_dev *dev, struct list_head *head,
 		struct usb_request *req)
@@ -227,8 +220,10 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req)
 {
 	struct acc_dev *dev = _acc_dev;
 
-	if (req->status != 0)
+	if (req->status != 0) {
 		dev->online = 0;
+		dev->disconnected = 1;
+	}
 
 	req_put(dev, &dev->tx_idle, req);
 
@@ -240,8 +235,10 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req)
 	struct acc_dev *dev = _acc_dev;
 
 	dev->rx_done = 1;
-	if (req->status != 0)
+	if (req->status != 0) {
 		dev->online = 0;
+		dev->disconnected = 1;
+	}
 
 	wake_up(&dev->read_wq);
 }
@@ -258,11 +255,6 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req)
 		return;
 	}
 
-	if (length > ACC_STRING_SIZE) {
-		DBG(cdev, "accessory string too long (length %d)\n", length);
-		return;
-	}
-
 	switch (dev->string_index) {
 	case ACCESSORY_STRING_MANUFACTURER:
 		string_dest = dev->manufacturer;
@@ -270,19 +262,26 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req)
 	case ACCESSORY_STRING_MODEL:
 		string_dest = dev->model;
 		break;
-	case ACCESSORY_STRING_TYPE:
-		string_dest = dev->type;
+	case ACCESSORY_STRING_DESCRIPTION:
+		string_dest = dev->description;
 		break;
 	case ACCESSORY_STRING_VERSION:
 		string_dest = dev->version;
 		break;
+	case ACCESSORY_STRING_URI:
+		string_dest = dev->uri;
+		break;
 	}
 	if (string_dest) {
 		unsigned long flags;
 
+		if (length >= ACC_STRING_SIZE)
+			length = ACC_STRING_SIZE - 1;
+
 		spin_lock_irqsave(&dev->lock, flags);
-		if (string_dest)
-			memcpy(string_dest, cdev->req->buf, length);
+		memcpy(string_dest, cdev->req->buf, length);
+		/* ensure zero termination */
+		string_dest[length] = 0;
 		spin_unlock_irqrestore(&dev->lock, flags);
 	} else {
 		DBG(cdev, "unknown accessory string index %d\n",
@@ -348,6 +347,10 @@ static int __init create_bulk_endpoints(struct acc_dev *dev,
 
 fail:
 	printk(KERN_ERR "acc_bind() could not allocate requests\n");
+	while ((req = req_get(dev, &dev->tx_idle)))
+		acc_request_free(req, dev->ep_in);
+	for (i = 0; i < RX_REQ_MAX; i++)
+		acc_request_free(dev->rx_req[i], dev->ep_out);
 	return -1;
 }
 
@@ -362,6 +365,9 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
 
 	DBG(cdev, "acc_read(%d)\n", count);
 
+	if (dev->disconnected)
+		return -ENODEV;
+
 	if (count > BULK_BUFFER_SIZE)
 		count = BULK_BUFFER_SIZE;
 
@@ -422,12 +428,8 @@ static ssize_t acc_write(struct file *fp, const char __user *buf,
 
 	DBG(cdev, "acc_write(%d)\n", count);
 
-	spin_lock_irq(&dev->lock);
-	if (!dev->online) {
-		spin_unlock_irq(&dev->lock);
+	if (!dev->online || dev->disconnected)
 		return -ENODEV;
-	}
-	spin_unlock_irq(&dev->lock);
 
 	while (count > 0) {
 		if (!dev->online) {
@@ -479,52 +481,42 @@ static ssize_t acc_write(struct file *fp, const char __user *buf,
 static long acc_ioctl(struct file *fp, unsigned code, unsigned long value)
 {
 	struct acc_dev *dev = fp->private_data;
-	int ret = -EINVAL;
+	char *src = NULL;
+	int ret;
 
 	if (dev->function.disabled)
 		return -ENODEV;
 
 	switch (code) {
 	case ACCESSORY_GET_STRING_MANUFACTURER:
-	spin_lock_irq(&dev->lock);
-	ret = strlen(dev->manufacturer) + 1;
-	if (copy_to_user((void __user *)value, dev->manufacturer, ret))
-		ret = -EFAULT;
-	spin_unlock_irq(&dev->lock);
-	break;
-
+		src = dev->manufacturer;
+		break;
 	case ACCESSORY_GET_STRING_MODEL:
-	spin_lock_irq(&dev->lock);
-	ret = strlen(dev->model) + 1;
-	if (copy_to_user((void __user *)value, dev->model, ret))
-		ret = -EFAULT;
-	spin_unlock_irq(&dev->lock);
-	break;
-
-	case ACCESSORY_GET_STRING_TYPE:
-	spin_lock_irq(&dev->lock);
-	ret = strlen(dev->type) + 1;
-	if (copy_to_user((void __user *)value, dev->type, ret))
-		ret = -EFAULT;
-	spin_unlock_irq(&dev->lock);
-	break;
-
+		src = dev->model;
+		break;
+	case ACCESSORY_GET_STRING_DESCRIPTION:
+		src = dev->description;
+		break;
 	case ACCESSORY_GET_STRING_VERSION:
-	spin_lock_irq(&dev->lock);
-	ret = strlen(dev->version) + 1;
-	if (copy_to_user((void __user *)value, dev->version, ret))
-		ret = -EFAULT;
-	spin_unlock_irq(&dev->lock);
-	break;
+		src = dev->version;
+		break;
+	case ACCESSORY_GET_STRING_URI:
+		src = dev->uri;
+		break;
 	}
+	if (!src)
+		return -EINVAL;
 
+	ret = strlen(src) + 1;
+	if (copy_to_user((void __user *)value, src, ret))
+		ret = -EFAULT;
 	return ret;
 }
 
 static int acc_open(struct inode *ip, struct file *fp)
 {
 	printk(KERN_INFO "acc_open\n");
-	if (_lock(&_acc_dev->open_excl))
+	if (atomic_xchg(&_acc_dev->open_excl, 1))
 		return -EBUSY;
 
 	fp->private_data = _acc_dev;
@@ -535,7 +527,8 @@ static int acc_release(struct inode *ip, struct file *fp)
 {
 	printk(KERN_INFO "acc_release\n");
 
-	_unlock(&_acc_dev->open_excl);
+	WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0));
+	_acc_dev->disconnected = 0;
 	return 0;
 }
 
@@ -626,6 +619,8 @@ static int acc_function_setup(struct usb_function *f,
 	struct acc_dev	*dev = func_to_dev(f);
 	struct usb_composite_dev *cdev = dev->cdev;
 	int	value = -EOPNOTSUPP;
+	u8 b_requestType = ctrl->bRequestType;
+	u8 b_request = ctrl->bRequest;
 	u16	w_index = le16_to_cpu(ctrl->wIndex);
 	u16	w_value = le16_to_cpu(ctrl->wValue);
 	u16	w_length = le16_to_cpu(ctrl->wLength);
@@ -633,20 +628,27 @@ static int acc_function_setup(struct usb_function *f,
 /*
 	printk(KERN_INFO "acc_function_setup "
 			"%02x.%02x v%04x i%04x l%u\n",
-			ctrl->bRequestType, ctrl->bRequest,
+			b_requestType, b_request,
 			w_value, w_index, w_length);
 */
 
-	if (dev->function.disabled && ctrl->bRequestType ==
-			(USB_DIR_OUT | USB_TYPE_VENDOR)) {
-		if (ctrl->bRequest == ACCESSORY_START) {
-			schedule_delayed_work(&dev->work, msecs_to_jiffies(10));
-			value = 0;
-		} else if (ctrl->bRequest == ACCESSORY_SEND_STRING) {
-			dev->string_index = w_index;
-			cdev->gadget->ep0->driver_data = dev;
-			cdev->req->complete = acc_complete_set_string;
-			value = w_length;
+	if (dev->function.disabled) {
+		if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) {
+			if (b_request == ACCESSORY_START) {
+				schedule_delayed_work(
+					&dev->work, msecs_to_jiffies(10));
+				value = 0;
+			} else if (b_request == ACCESSORY_SEND_STRING) {
+				dev->string_index = w_index;
+				cdev->gadget->ep0->driver_data = dev;
+				cdev->req->complete = acc_complete_set_string;
+				value = w_length;
+			}
+		} else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) {
+			if (b_request == ACCESSORY_GET_PROTOCOL) {
+				*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
+				value = sizeof(u16);
+			}
 		}
 	}
 
diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h
index 474f12eb0181fbb3a270b6e190d28bee36b8efc2..977ad0a69c7e1fcacef69c5e457f2af2d48bad65 100644
--- a/include/linux/usb/f_accessory.h
+++ b/include/linux/usb/f_accessory.h
@@ -28,9 +28,24 @@
 /* Product ID to use when in accessory mode and adb is enabled */
 #define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
 
-/*
- * Indexes for strings sent by the host to identify the accessory.
- * The host sends these as vendor requests:
+/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */
+#define ACCESSORY_STRING_MANUFACTURER   0
+#define ACCESSORY_STRING_MODEL          1
+#define ACCESSORY_STRING_DESCRIPTION    2
+#define ACCESSORY_STRING_VERSION        3
+#define ACCESSORY_STRING_URI            4
+
+/* Control request for retrieving device's protocol version (currently 1)
+ *
+ *	requestType:    USB_DIR_IN | USB_TYPE_VENDOR
+ *	request:        ACCESSORY_GET_PROTOCOL
+ *	value:          0
+ *	index:          0
+ *	data            version number (16 bits little endian)
+ */
+#define ACCESSORY_GET_PROTOCOL  51
+
+/* Control request for host to send a string to the device
  *
  *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
  *	request:        ACCESSORY_SEND_STRING
@@ -41,19 +56,24 @@
  *  The device can later retrieve these strings via the
  *  ACCESSORY_GET_STRING_* ioctls
  */
-#define ACCESSORY_STRING_MANUFACTURER   0
-#define ACCESSORY_STRING_MODEL          1
-#define ACCESSORY_STRING_TYPE           2
-#define ACCESSORY_STRING_VERSION        3
-
-/* control requests */
 #define ACCESSORY_SEND_STRING   52
+
+/* Control request for starting device in accessory mode.
+ * The host sends this after setting all its strings to the device.
+ *
+ *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
+ *	request:        ACCESSORY_START
+ *	value:          0
+ *	index:          0
+ *	data            none
+ */
 #define ACCESSORY_START         53
 
-/* Sends an event to the accessory via the interrupt endpoint */
+/* ioctls for retrieving strings set by the host */
 #define ACCESSORY_GET_STRING_MANUFACTURER   _IOW('M', 1, char[256])
 #define ACCESSORY_GET_STRING_MODEL          _IOW('M', 2, char[256])
-#define ACCESSORY_GET_STRING_TYPE           _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION    _IOW('M', 3, char[256])
 #define ACCESSORY_GET_STRING_VERSION        _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI            _IOW('M', 5, char[256])
 
 #endif /* __LINUX_USB_F_ACCESSORY_H */