There are only 254 USB composite gadget string_ids available.
When switching gadget mode such as mtp and acm repeatedly,
they will be exhausted.
This bug has been brought up since android composite driver
introduced a way to switch gadget modes while the composite
driver is still holding its bind.
Fix this by reset next_string_id and clean up gstrings when
android gadgets are disabled. Also by removing the condition
comparing gadgets' string id to 0 because gadget string id
has to be re-assigned whenever the string count is reset.
The codes removed the condition check will work as the same
as before they have changed if the gadgets are used by other
composite drivers other than android since all of them call
bind only once and never unbind it hence no side effects considered.
Ported from https://android-review.googlesource.com/#/c/95366/
Bug
200001941
Change-Id: I1e2fbe0f59fe05b89052db62e0b61b074d8f032b
Signed-off-by: Rakesh Bodla <rbodla@nvidia.com>
Reviewed-on: http://git-master/r/425165
Reviewed-by: Mitch Luban <mluban@nvidia.com>
bool sw_connected;
struct work_struct work;
char ffs_aliases[256];
+ unsigned short ffs_string_ids;
};
static struct class *android_class;
.bConfigurationValue = 1,
};
+static void android_gstring_cleanup(struct android_dev *dev)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_gadget_string_container *uc, *tmp;
+
+ list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) {
+ list_del(&uc->list);
+ kfree(uc);
+ }
+ /* reserve unfreed string ids */
+ cdev->next_string_id = ARRAY_SIZE(strings_dev) +
+ dev->ffs_string_ids - 1;
+}
+
static void android_work(struct work_struct *data)
{
struct android_dev *dev = container_of(data, struct android_dev, work);
/* Cancel pending control requests */
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
usb_remove_config(cdev, &android_config_driver);
+ android_gstring_cleanup(dev);
}
}
config->data = ffs;
config->opened = true;
+ dev->ffs_string_ids = ffs->strings_count;
if (config->enabled)
android_enable(dev);
config->data = NULL;
functionfs_unbind(ffs);
+ dev->ffs_string_ids = 0;
mutex_unlock(&dev->mutex);
}
printk(KERN_INFO "acc_bind_config\n");
/* allocate a string ID for our interface */
- if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) {
- ret = usb_string_id(c->cdev);
- if (ret < 0)
- return ret;
- acc_string_defs[INTERFACE_STRING_INDEX].id = ret;
- acc_interface_desc.iInterface = ret;
- }
+ ret = usb_string_id(c->cdev);
+ if (ret < 0)
+ return ret;
+ acc_string_defs[INTERFACE_STRING_INDEX].id = ret;
+ acc_interface_desc.iInterface = ret;
dev->cdev = c->cdev;
dev->function.name = "accessory";
common->ep0req = cdev->req;
common->cdev = cdev;
- /* Maybe allocate device-global string IDs, and patch descriptors */
- if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
- rc = usb_string_id(cdev);
- if (unlikely(rc < 0))
- goto error_release;
- fsg_strings[FSG_STRING_INTERFACE].id = rc;
- fsg_intf_desc.iInterface = rc;
- }
-
/*
* Create the LUNs, open their backing files, and register the
* LUN devices in sysfs.
struct fsg_dev *fsg;
int rc;
+ /* allocate device-global string IDs, and patch descriptors */
+ rc = usb_string_id(cdev);
+ if (unlikely(rc < 0))
+ return rc;
+ fsg_strings[FSG_STRING_INTERFACE].id = rc;
+ fsg_intf_desc.iInterface = rc;
+
fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
if (unlikely(!fsg))
return -ENOMEM;
printk(KERN_INFO "mtp_bind_config\n");
- /* allocate a string ID for our interface */
- if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) {
- ret = usb_string_id(c->cdev);
- if (ret < 0)
- return ret;
- mtp_string_defs[INTERFACE_STRING_INDEX].id = ret;
- mtp_interface_desc.iInterface = ret;
- }
+ ret = usb_string_id(c->cdev);
+ if (ret < 0)
+ return ret;
+ mtp_string_defs[INTERFACE_STRING_INDEX].id = ret;
+ mtp_interface_desc.iInterface = ret;
dev->cdev = c->cdev;
dev->function.name = "mtp";
if (status < 0)
return status;
- if (rndis_string_defs[0].id == 0) {
- status = usb_string_ids_tab(c->cdev, rndis_string_defs);
- if (status)
- return status;
-
- rndis_control_intf.iInterface = rndis_string_defs[0].id;
- rndis_data_intf.iInterface = rndis_string_defs[1].id;
- rndis_iad_descriptor.iFunction = rndis_string_defs[2].id;
- }
+ status = usb_string_ids_tab(c->cdev, rndis_string_defs);
+ if (status)
+ return status;
+
+ rndis_control_intf.iInterface = rndis_string_defs[0].id;
+ rndis_data_intf.iInterface = rndis_string_defs[1].id;
+ rndis_iad_descriptor.iFunction = rndis_string_defs[2].id;
/* allocate and initialize one new instance */
status = -ENOMEM;