diff options
47 files changed, 7889 insertions, 205 deletions
diff --git a/bus_id-arm.patch b/bus_id-arm.patch deleted file mode 100644 index f5b876f8fd8599..00000000000000 --- a/bus_id-arm.patch +++ /dev/null @@ -1,160 +0,0 @@ -From kay.sievers@vrfy.org Fri May 30 10:26:49 2008 -From: Kay Sievers <kay.sievers@vrfy.org> -Subject: arm: convert to new driver core device name api -Date: Fri, 30 May 2008 17:42:11 +0200 -Message-Id: <1212162131.2418.5.camel@linux.site> -Mime-Version: 1.0 -Content-Transfer-Encoding: 7bit -Status: RO -Content-Length: 8395 - -Signed-off-by: Kay Sievers <kay.sievers@vrfy.org> ---- - ---- - arch/arm/mach-aaec2000/core.c | 2 +- - arch/arm/mach-ep93xx/core.c | 6 +++--- - arch/arm/mach-integrator/core.c | 10 +++++----- - arch/arm/mach-integrator/integrator_cp.c | 6 +++--- - arch/arm/mach-lh7a40x/clcd.c | 2 +- - arch/arm/mach-netx/fb.c | 2 +- - 6 files changed, 14 insertions(+), 14 deletions(-) - ---- a/arch/arm/mach-aaec2000/core.c -+++ b/arch/arm/mach-aaec2000/core.c -@@ -212,7 +212,7 @@ static struct clcd_board clcd_plat_data - - static struct amba_device clcd_device = { - .dev = { -- .bus_id = "mb:16", -+ .init_name = "mb:16", - .coherent_dma_mask = ~0, - .platform_data = &clcd_plat_data, - }, ---- a/arch/arm/mach-ep93xx/core.c -+++ b/arch/arm/mach-ep93xx/core.c -@@ -389,7 +389,7 @@ static struct amba_pl010_data ep93xx_uar - - static struct amba_device uart1_device = { - .dev = { -- .bus_id = "apb:uart1", -+ .init_name = "apb:uart1", - .platform_data = &ep93xx_uart_data, - }, - .res = { -@@ -403,7 +403,7 @@ static struct amba_device uart1_device = - - static struct amba_device uart2_device = { - .dev = { -- .bus_id = "apb:uart2", -+ .init_name = "apb:uart2", - .platform_data = &ep93xx_uart_data, - }, - .res = { -@@ -417,7 +417,7 @@ static struct amba_device uart2_device = - - static struct amba_device uart3_device = { - .dev = { -- .bus_id = "apb:uart3", -+ .init_name = "apb:uart3", - .platform_data = &ep93xx_uart_data, - }, - .res = { ---- a/arch/arm/mach-integrator/core.c -+++ b/arch/arm/mach-integrator/core.c -@@ -35,7 +35,7 @@ static struct amba_pl010_data integrator - - static struct amba_device rtc_device = { - .dev = { -- .bus_id = "mb:15", -+ .init_name = "mb:15", - }, - .res = { - .start = INTEGRATOR_RTC_BASE, -@@ -48,7 +48,7 @@ static struct amba_device rtc_device = { - - static struct amba_device uart0_device = { - .dev = { -- .bus_id = "mb:16", -+ .init_name = "mb:16", - .platform_data = &integrator_uart_data, - }, - .res = { -@@ -62,7 +62,7 @@ static struct amba_device uart0_device = - - static struct amba_device uart1_device = { - .dev = { -- .bus_id = "mb:17", -+ .init_name = "mb:17", - .platform_data = &integrator_uart_data, - }, - .res = { -@@ -76,7 +76,7 @@ static struct amba_device uart1_device = - - static struct amba_device kmi0_device = { - .dev = { -- .bus_id = "mb:18", -+ .init_name = "mb:18", - }, - .res = { - .start = KMI0_BASE, -@@ -89,7 +89,7 @@ static struct amba_device kmi0_device = - - static struct amba_device kmi1_device = { - .dev = { -- .bus_id = "mb:19", -+ .init_name = "mb:19", - }, - .res = { - .start = KMI1_BASE, ---- a/arch/arm/mach-integrator/integrator_cp.c -+++ b/arch/arm/mach-integrator/integrator_cp.c -@@ -406,7 +406,7 @@ static struct mmc_platform_data mmc_data - - static struct amba_device mmc_device = { - .dev = { -- .bus_id = "mb:1c", -+ .init_name = "mb:1c", - .platform_data = &mmc_data, - }, - .res = { -@@ -420,7 +420,7 @@ static struct amba_device mmc_device = { - - static struct amba_device aaci_device = { - .dev = { -- .bus_id = "mb:1d", -+ .init_name = "mb:1d", - }, - .res = { - .start = INTCP_PA_AACI_BASE, -@@ -531,7 +531,7 @@ static struct clcd_board clcd_data = { - - static struct amba_device clcd_device = { - .dev = { -- .bus_id = "mb:c0", -+ .init_name = "mb:c0", - .coherent_dma_mask = ~0, - .platform_data = &clcd_data, - }, ---- a/arch/arm/mach-lh7a40x/clcd.c -+++ b/arch/arm/mach-lh7a40x/clcd.c -@@ -208,7 +208,7 @@ static struct clcd_board clcd_platform_d - static struct amba_device name##_device = { \ - .dev = { \ - .coherent_dma_mask = ~0, \ -- .bus_id = busid, \ -+ .init_name = busid, \ - .platform_data = plat, \ - }, \ - .res = { \ ---- a/arch/arm/mach-netx/fb.c -+++ b/arch/arm/mach-netx/fb.c -@@ -94,7 +94,7 @@ void clk_put(struct clk *clk) - - static struct amba_device fb_device = { - .dev = { -- .bus_id = "fb", -+ .init_name = "fb", - .coherent_dma_mask = ~0, - }, - .res = { diff --git a/driver-core-provide-a-dev_set_name-that-handles-names-longer-than-20-chars.patch b/driver-core-provide-a-dev_set_name-that-handles-names-longer-than-20-chars.patch index c660648061e67e..67ef1e909b5f6c 100644 --- a/driver-core-provide-a-dev_set_name-that-handles-names-longer-than-20-chars.patch +++ b/driver-core-provide-a-dev_set_name-that-handles-names-longer-than-20-chars.patch @@ -13,9 +13,9 @@ Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- - drivers/base/core.c | 89 +++++++++++++++++++++++++++++++------------------ + drivers/base/core.c | 90 +++++++++++++++++++++++++++++++------------------ include/linux/device.h | 10 +++-- - 2 files changed, 64 insertions(+), 35 deletions(-) + 2 files changed, 64 insertions(+), 36 deletions(-) --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -29,12 +29,13 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> if (dev->release) dev->release(dev); else if (dev->type && dev->type->release) -@@ -767,21 +770,6 @@ static void device_remove_class_symlinks +@@ -767,22 +770,6 @@ static void device_remove_class_symlinks } /** - * dev_set_name - set a device name - * @dev: device +- * @fmt: format string for the device's name - */ -int dev_set_name(struct device *dev, const char *fmt, ...) -{ @@ -51,7 +52,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> * device_to_dev_kobj - select a /sys/dev/ directory for the device * @dev: device * -@@ -829,6 +817,47 @@ static void device_remove_sys_dev_entry( +@@ -830,6 +817,47 @@ static void device_remove_sys_dev_entry( } } @@ -99,7 +100,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /** * device_add - add device to device hierarchy. * @dev: device. -@@ -850,12 +879,7 @@ int device_add(struct device *dev) +@@ -851,12 +879,7 @@ int device_add(struct device *dev) if (!dev) goto done; @@ -113,7 +114,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> goto done; pr_debug("device: '%s': %s\n", dev_name(dev), __func__); -@@ -867,12 +891,15 @@ int device_add(struct device *dev) +@@ -868,12 +891,15 @@ int device_add(struct device *dev) if (parent) set_dev_node(dev, dev_to_node(parent)); @@ -132,7 +133,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> if (platform_notify) platform_notify(dev); -@@ -927,6 +954,7 @@ int device_add(struct device *dev) +@@ -928,6 +954,7 @@ int device_add(struct device *dev) done: put_device(dev); return error; @@ -140,7 +141,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> PMError: bus_remove_device(dev); BusError: -@@ -1233,7 +1261,9 @@ struct device *device_create_vargs(struc +@@ -1234,7 +1261,9 @@ struct device *device_create_vargs(struc dev->release = device_create_release; dev_set_drvdata(dev, drvdata); @@ -151,7 +152,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> retval = device_register(dev); if (retval) goto error; -@@ -1241,6 +1271,7 @@ struct device *device_create_vargs(struc +@@ -1242,6 +1271,7 @@ struct device *device_create_vargs(struc return dev; error: @@ -159,7 +160,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> kfree(dev); return ERR_PTR(retval); } -@@ -1340,19 +1371,15 @@ int device_rename(struct device *dev, ch +@@ -1341,19 +1371,15 @@ int device_rename(struct device *dev, ch old_class_name = make_class_name(dev->class->name, &dev->kobj); #endif diff --git a/driver-core.current/kobject-fix-kobject_rename-and-config_sysfs.patch b/driver-core.current/kobject-fix-kobject_rename-and-config_sysfs.patch index ed37ca3b705902..bad0705f2c2096 100644 --- a/driver-core.current/kobject-fix-kobject_rename-and-config_sysfs.patch +++ b/driver-core.current/kobject-fix-kobject_rename-and-config_sysfs.patch @@ -67,7 +67,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> incorrect and needs to be fixed. --- a/drivers/base/core.c +++ b/drivers/base/core.c -@@ -1259,6 +1259,11 @@ EXPORT_SYMBOL_GPL(device_destroy); +@@ -1260,6 +1260,11 @@ EXPORT_SYMBOL_GPL(device_destroy); * device_rename - renames a device * @dev: the pointer to the struct device to be renamed * @new_name: the new name of the device diff --git a/driver-core/class-change-internal-semaphore-to-a-mutex.patch b/driver-core/class-change-internal-semaphore-to-a-mutex.patch index 7fc9e1b79a3df2..27d7c0d8d2455e 100644 --- a/driver-core/class-change-internal-semaphore-to-a-mutex.patch +++ b/driver-core/class-change-internal-semaphore-to-a-mutex.patch @@ -160,7 +160,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> #include "base.h" #include "power/power.h" -@@ -906,7 +907,7 @@ int device_add(struct device *dev) +@@ -907,7 +908,7 @@ int device_add(struct device *dev) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { @@ -169,7 +169,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /* tie the class to the device */ list_add_tail(&dev->node, &dev->class->p->class_devices); -@@ -915,7 +916,7 @@ int device_add(struct device *dev) +@@ -916,7 +917,7 @@ int device_add(struct device *dev) &dev->class->p->class_interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); @@ -178,7 +178,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> } Done: put_device(dev); -@@ -1016,7 +1017,7 @@ void device_del(struct device *dev) +@@ -1017,7 +1018,7 @@ void device_del(struct device *dev) if (dev->class) { device_remove_class_symlinks(dev); @@ -187,7 +187,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /* notify any interfaces that the device is now gone */ list_for_each_entry(class_intf, &dev->class->p->class_interfaces, node) -@@ -1024,7 +1025,7 @@ void device_del(struct device *dev) +@@ -1025,7 +1026,7 @@ void device_del(struct device *dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ list_del_init(&dev->node); diff --git a/driver-core/class-move-driver-core-specific-parts-to-a-private-structure.patch b/driver-core/class-move-driver-core-specific-parts-to-a-private-structure.patch index 79ea9af6875299..4d2d549cded627 100644 --- a/driver-core/class-move-driver-core-specific-parts-to-a-private-structure.patch +++ b/driver-core/class-move-driver-core-specific-parts-to-a-private-structure.patch @@ -389,7 +389,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> #endif sysfs_remove_link(&dev->kobj, "subsystem"); -@@ -903,15 +903,16 @@ int device_add(struct device *dev) +@@ -904,15 +904,16 @@ int device_add(struct device *dev) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { @@ -410,7 +410,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> } Done: put_device(dev); -@@ -1012,14 +1013,15 @@ void device_del(struct device *dev) +@@ -1013,14 +1014,15 @@ void device_del(struct device *dev) if (dev->class) { device_remove_class_symlinks(dev); @@ -429,7 +429,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> } device_remove_file(dev, &uevent_attr); device_remove_attrs(dev); -@@ -1355,11 +1357,12 @@ int device_rename(struct device *dev, ch +@@ -1356,11 +1358,12 @@ int device_rename(struct device *dev, ch } #else if (dev->class) { diff --git a/driver-core/class-rename-devices-to-class_devices-in-internal-class-structure.patch b/driver-core/class-rename-devices-to-class_devices-in-internal-class-structure.patch index 8cc09659207848..e11b765b780f34 100644 --- a/driver-core/class-rename-devices-to-class_devices-in-internal-class-structure.patch +++ b/driver-core/class-rename-devices-to-class_devices-in-internal-class-structure.patch @@ -87,7 +87,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> up(&parent->p->sem); --- a/drivers/base/core.c +++ b/drivers/base/core.c -@@ -905,7 +905,7 @@ int device_add(struct device *dev) +@@ -906,7 +906,7 @@ int device_add(struct device *dev) if (dev->class) { down(&dev->class->p->sem); /* tie the class to the device */ diff --git a/driver-core/class-rename-interfaces-to-class_interfaces-in-internal-class-structure.patch b/driver-core/class-rename-interfaces-to-class_interfaces-in-internal-class-structure.patch index 92492131b8d9f7..40ebaf6f5b57ca 100644 --- a/driver-core/class-rename-interfaces-to-class_interfaces-in-internal-class-structure.patch +++ b/driver-core/class-rename-interfaces-to-class_interfaces-in-internal-class-structure.patch @@ -61,7 +61,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> class_intf->add_dev(dev, class_intf); --- a/drivers/base/core.c +++ b/drivers/base/core.c -@@ -908,8 +908,8 @@ int device_add(struct device *dev) +@@ -909,8 +909,8 @@ int device_add(struct device *dev) list_add_tail(&dev->node, &dev->class->p->class_devices); /* notify any interfaces that the device is here */ @@ -72,7 +72,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); up(&dev->class->p->sem); -@@ -1015,8 +1015,8 @@ void device_del(struct device *dev) +@@ -1016,8 +1016,8 @@ void device_del(struct device *dev) down(&dev->class->p->sem); /* notify any interfaces that the device is now gone */ diff --git a/driver-core/class-rename-sem-to-class_sem-in-internal-class-structure.patch b/driver-core/class-rename-sem-to-class_sem-in-internal-class-structure.patch index cf1fb7164c8b29..b1804938f47d8e 100644 --- a/driver-core/class-rename-sem-to-class_sem-in-internal-class-structure.patch +++ b/driver-core/class-rename-sem-to-class_sem-in-internal-class-structure.patch @@ -137,7 +137,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> } --- a/drivers/base/core.c +++ b/drivers/base/core.c -@@ -906,7 +906,7 @@ int device_add(struct device *dev) +@@ -907,7 +907,7 @@ int device_add(struct device *dev) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { @@ -146,7 +146,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /* tie the class to the device */ list_add_tail(&dev->node, &dev->class->p->class_devices); -@@ -915,7 +915,7 @@ int device_add(struct device *dev) +@@ -916,7 +916,7 @@ int device_add(struct device *dev) &dev->class->p->class_interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); @@ -155,7 +155,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> } Done: put_device(dev); -@@ -1016,7 +1016,7 @@ void device_del(struct device *dev) +@@ -1017,7 +1017,7 @@ void device_del(struct device *dev) if (dev->class) { device_remove_class_symlinks(dev); @@ -164,7 +164,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /* notify any interfaces that the device is now gone */ list_for_each_entry(class_intf, &dev->class->p->class_interfaces, node) -@@ -1024,7 +1024,7 @@ void device_del(struct device *dev) +@@ -1025,7 +1025,7 @@ void device_del(struct device *dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ list_del_init(&dev->node); diff --git a/driver-core/class-rename-subsys-to-class_subsys-in-internal-class-structure.patch b/driver-core/class-rename-subsys-to-class_subsys-in-internal-class-structure.patch index b492ceca1b931e..e397de2e7f13a7 100644 --- a/driver-core/class-rename-subsys-to-class_subsys-in-internal-class-structure.patch +++ b/driver-core/class-rename-subsys-to-class_subsys-in-internal-class-structure.patch @@ -209,7 +209,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> #endif sysfs_remove_link(&dev->kobj, "subsystem"); -@@ -1357,11 +1360,11 @@ int device_rename(struct device *dev, ch +@@ -1358,11 +1361,11 @@ int device_rename(struct device *dev, ch } #else if (dev->class) { diff --git a/driver-core/driver-core-add-ability-for-class_find_device-to-start-in-middle-of-list.patch b/driver-core/driver-core-add-ability-for-class_find_device-to-start-in-middle-of-list.patch index 0df91b0a195741..2c71eb7414af7c 100644 --- a/driver-core/driver-core-add-ability-for-class_find_device-to-start-in-middle-of-list.patch +++ b/driver-core/driver-core-add-ability-for-class_find_device-to-start-in-middle-of-list.patch @@ -75,7 +75,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- a/drivers/base/core.c +++ b/drivers/base/core.c -@@ -1291,7 +1291,7 @@ void device_destroy(struct class *class, +@@ -1292,7 +1292,7 @@ void device_destroy(struct class *class, { struct device *dev; diff --git a/driver-core/driver-core-add-init_name-to-struct-device.patch b/driver-core/driver-core-add-init_name-to-struct-device.patch index 6924da0d6ef07a..0f8d1718c40e59 100644 --- a/driver-core/driver-core-add-init_name-to-struct-device.patch +++ b/driver-core/driver-core-add-init_name-to-struct-device.patch @@ -17,7 +17,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- a/drivers/base/core.c +++ b/drivers/base/core.c -@@ -843,13 +843,19 @@ int device_add(struct device *dev) +@@ -844,13 +844,19 @@ int device_add(struct device *dev) { struct device *parent = NULL; struct class_interface *class_intf; @@ -42,7 +42,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> pr_debug("device: '%s': %s\n", dev_name(dev), __func__); -@@ -917,7 +923,7 @@ int device_add(struct device *dev) +@@ -918,7 +924,7 @@ int device_add(struct device *dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->class_mutex); } @@ -51,7 +51,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> put_device(dev); return error; PMError: -@@ -944,7 +950,7 @@ int device_add(struct device *dev) +@@ -945,7 +951,7 @@ int device_add(struct device *dev) cleanup_device_parent(dev); if (parent) put_device(parent); diff --git a/driver-core/driver-core-prepare-for-removal-of-20-char-limit-from-struct-device.patch b/driver-core/driver-core-prepare-for-removal-of-20-char-limit-from-struct-device.patch index 5152db7e2ffc72..455245051a6f57 100644 --- a/driver-core/driver-core-prepare-for-removal-of-20-char-limit-from-struct-device.patch +++ b/driver-core/driver-core-prepare-for-removal-of-20-char-limit-from-struct-device.patch @@ -749,7 +749,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> #endif sysfs_remove_link(&dev->kobj, "subsystem"); -@@ -852,7 +851,7 @@ int device_add(struct device *dev) +@@ -853,7 +852,7 @@ int device_add(struct device *dev) goto Done; } @@ -758,7 +758,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> parent = get_device(dev->parent); setup_parent(dev, parent); -@@ -862,7 +861,7 @@ int device_add(struct device *dev) +@@ -863,7 +862,7 @@ int device_add(struct device *dev) set_dev_node(dev, dev_to_node(parent)); /* first, register with generic layer. */ @@ -767,7 +767,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> if (error) goto Error; -@@ -1065,7 +1064,7 @@ void device_del(struct device *dev) +@@ -1066,7 +1065,7 @@ void device_del(struct device *dev) */ void device_unregister(struct device *dev) { @@ -776,7 +776,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> device_del(dev); put_device(dev); } -@@ -1178,7 +1177,7 @@ EXPORT_SYMBOL_GPL(device_remove_file); +@@ -1179,7 +1178,7 @@ EXPORT_SYMBOL_GPL(device_remove_file); static void device_create_release(struct device *dev) { @@ -785,7 +785,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> kfree(dev); } -@@ -1326,7 +1325,7 @@ int device_rename(struct device *dev, ch +@@ -1327,7 +1326,7 @@ int device_rename(struct device *dev, ch if (!dev) return -EINVAL; @@ -794,7 +794,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> __func__, new_name); #ifdef CONFIG_SYSFS_DEPRECATED -@@ -1339,7 +1338,7 @@ int device_rename(struct device *dev, ch +@@ -1340,7 +1339,7 @@ int device_rename(struct device *dev, ch error = -ENOMEM; goto out; } @@ -803,7 +803,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> strlcpy(dev->bus_id, new_name, BUS_ID_SIZE); error = kobject_rename(&dev->kobj, new_name); -@@ -1362,7 +1361,7 @@ int device_rename(struct device *dev, ch +@@ -1363,7 +1362,7 @@ int device_rename(struct device *dev, ch #else if (dev->class) { error = sysfs_create_link(&dev->class->p->class_subsys.kobj, @@ -812,7 +812,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> if (error) goto out; sysfs_remove_link(&dev->class->p->class_subsys.kobj, -@@ -1440,8 +1439,8 @@ int device_move(struct device *dev, stru +@@ -1441,8 +1440,8 @@ int device_move(struct device *dev, stru new_parent = get_device(new_parent); new_parent_kobj = get_device_parent(dev, new_parent); diff --git a/driver-core/driver-core-remove-device_create.patch b/driver-core/driver-core-remove-device_create.patch index 222f1f96b5f862..9b40ea46ddf84d 100644 --- a/driver-core/driver-core-remove-device_create.patch +++ b/driver-core/driver-core-remove-device_create.patch @@ -16,7 +16,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- a/drivers/base/core.c +++ b/drivers/base/core.c -@@ -1272,40 +1272,6 @@ struct device *device_create_drvdata(str +@@ -1273,40 +1273,6 @@ struct device *device_create_drvdata(str } EXPORT_SYMBOL_GPL(device_create_drvdata); diff --git a/driver-core/kobject-reorder-kobject-to-save-space-on-64-bit-builds.patch b/driver-core/kobject-reorder-kobject-to-save-space-on-64-bit-builds.patch new file mode 100644 index 00000000000000..3888347a92376d --- /dev/null +++ b/driver-core/kobject-reorder-kobject-to-save-space-on-64-bit-builds.patch @@ -0,0 +1,35 @@ +From richard@rsk.demon.co.uk Fri Jun 6 15:22:07 2008 +From: Richard Kennedy <richard@rsk.demon.co.uk> +Date: Mon, 02 Jun 2008 11:07:25 +0100 +Subject: kobject: reorder kobject to save space on 64 bit builds +To: gregkh <gregkh@suse.de> +Message-ID: <1212401245.2966.8.camel@castor.localdomain> + + +reorder kobject to save space on 64 bit builds. +shrinks from 72 to 64 bytes & moves allocated kobject to a smaller +slab. + +Signed-off-by: Richard Kennedy <richard@rsk.demon.co.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + include/linux/kobject.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/kobject.h ++++ b/include/linux/kobject.h +@@ -58,12 +58,12 @@ enum kobject_action { + + struct kobject { + const char *name; +- struct kref kref; + struct list_head entry; + struct kobject *parent; + struct kset *kset; + struct kobj_type *ktype; + struct sysfs_dirent *sd; ++ struct kref kref; + unsigned int state_initialized:1; + unsigned int state_in_sysfs:1; + unsigned int state_add_uevent_sent:1; diff --git a/driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch b/driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch index ed9a41a564f73d..dada3dd9cf7d04 100644 --- a/driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch +++ b/driver-core/sysfs-add-sys-dev-char-block-to-lookup-sysfs-path-by-major-minor.patch @@ -134,7 +134,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> #ifdef CONFIG_BLOCK static inline int device_is_not_partition(struct device *dev) -@@ -775,6 +778,54 @@ int dev_set_name(struct device *dev, con +@@ -776,6 +779,54 @@ int dev_set_name(struct device *dev, con EXPORT_SYMBOL_GPL(dev_set_name); /** @@ -189,7 +189,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> * device_add - add device to device hierarchy. * @dev: device. * -@@ -828,6 +879,10 @@ int device_add(struct device *dev) +@@ -829,6 +880,10 @@ int device_add(struct device *dev) error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; @@ -200,7 +200,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> } error = device_add_class_symlinks(dev); -@@ -872,6 +927,9 @@ int device_add(struct device *dev) +@@ -873,6 +928,9 @@ int device_add(struct device *dev) device_remove_class_symlinks(dev); SymlinkError: if (MAJOR(dev->devt)) @@ -210,7 +210,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> device_remove_file(dev, &devt_attr); ueventattrError: device_remove_file(dev, &uevent_attr); -@@ -947,8 +1005,10 @@ void device_del(struct device *dev) +@@ -948,8 +1006,10 @@ void device_del(struct device *dev) device_pm_remove(dev); if (parent) klist_del(&dev->knode_parent); @@ -222,7 +222,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> if (dev->class) { device_remove_class_symlinks(dev); -@@ -1073,7 +1133,25 @@ int __init devices_init(void) +@@ -1074,7 +1134,25 @@ int __init devices_init(void) devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); if (!devices_kset) return -ENOMEM; @@ -248,7 +248,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> } EXPORT_SYMBOL_GPL(device_for_each_child); -@@ -1451,4 +1529,7 @@ void device_shutdown(void) +@@ -1452,4 +1530,7 @@ void device_shutdown(void) dev->driver->shutdown(dev); } } diff --git a/driver-core/uio-add-generic-uio-platform-driver.patch b/driver-core/uio-add-generic-uio-platform-driver.patch new file mode 100644 index 00000000000000..2d487467b6eeaa --- /dev/null +++ b/driver-core/uio-add-generic-uio-platform-driver.patch @@ -0,0 +1,168 @@ +From hjk@linutronix.de Sat May 31 02:37:42 2008 +From: Uwe Kleine-K�nig <Uwe.Kleine-Koenig@digi.com> +Date: Sat, 31 May 2008 11:37:27 +0200 +Subject: UIO: add generic UIO platform driver +To: linux-kernel@vger.kernel.org +Cc: Hans J. Koch <hjk@linutronix.de>, Greg Kroah-Hartman <gregkh@suse.de> + + +This patch adds a generic UIO platform driver. It eliminates the need for a +dedicated kernel module for simple platform devices. Users only need to +implement their irq handler in platform code and fill a struct uio_info +there. This helps avoiding code duplication as UIO platform drivers often +share a lot of common code. + +Signed-off-by: Uwe Kleine-K�nig <Uwe.Kleine-Koenig@digi.com> +Signed-off-by: Hans J. Koch <hjk@linutronix.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/uio/Kconfig | 7 ++ + drivers/uio/Makefile | 1 + drivers/uio/uio_pdrv.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 126 insertions(+) + +--- a/drivers/uio/Kconfig ++++ b/drivers/uio/Kconfig +@@ -26,6 +26,13 @@ config UIO_CIF + To compile this driver as a module, choose M here: the module + will be called uio_cif. + ++config UIO_PDRV ++ tristate "Userspace I/O platform driver" ++ help ++ Generic platform driver for Userspace I/O devices. ++ ++ If you don't know what to do here, say N. ++ + config UIO_SMX + tristate "SMX cryptengine UIO interface" + default n +--- a/drivers/uio/Makefile ++++ b/drivers/uio/Makefile +@@ -1,3 +1,4 @@ + obj-$(CONFIG_UIO) += uio.o + obj-$(CONFIG_UIO_CIF) += uio_cif.o ++obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o + obj-$(CONFIG_UIO_SMX) += uio_smx.o +--- /dev/null ++++ b/drivers/uio/uio_pdrv.c +@@ -0,0 +1,118 @@ ++/* ++ * drivers/uio/uio_pdrv.c ++ * ++ * Copyright (C) 2008 by Digi International Inc. ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++#include <linux/platform_device.h> ++#include <linux/uio_driver.h> ++#include <linux/stringify.h> ++ ++#define DRIVER_NAME "uio" ++ ++struct uio_platdata { ++ struct uio_info *uioinfo; ++}; ++ ++static int uio_pdrv_probe(struct platform_device *pdev) ++{ ++ struct uio_info *uioinfo = pdev->dev.platform_data; ++ struct uio_platdata *pdata; ++ struct uio_mem *uiomem; ++ int ret = -ENODEV; ++ int i; ++ ++ if (!uioinfo || !uioinfo->name || !uioinfo->version) { ++ dev_dbg(&pdev->dev, "%s: err_uioinfo\n", __func__); ++ goto err_uioinfo; ++ } ++ ++ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) { ++ ret = -ENOMEM; ++ dev_dbg(&pdev->dev, "%s: err_alloc_pdata\n", __func__); ++ goto err_alloc_pdata; ++ } ++ ++ pdata->uioinfo = uioinfo; ++ ++ uiomem = &uioinfo->mem[0]; ++ ++ for (i = 0; i < pdev->num_resources; ++i) { ++ struct resource *r = &pdev->resource[i]; ++ ++ if (r->flags != IORESOURCE_MEM) ++ continue; ++ ++ if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) { ++ dev_warn(&pdev->dev, "device has more than " ++ __stringify(MAX_UIO_MAPS) ++ " I/O memory resources.\n"); ++ break; ++ } ++ ++ uiomem->memtype = UIO_MEM_PHYS; ++ uiomem->addr = r->start; ++ uiomem->size = r->end - r->start + 1; ++ ++uiomem; ++ } ++ ++ while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) { ++ uiomem->size = 0; ++ ++uiomem; ++ } ++ ++ pdata->uioinfo->priv = pdata; ++ ++ ret = uio_register_device(&pdev->dev, pdata->uioinfo); ++ ++ if (ret) { ++ kfree(pdata); ++err_alloc_pdata: ++err_uioinfo: ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, pdata); ++ ++ return 0; ++} ++ ++static int uio_pdrv_remove(struct platform_device *pdev) ++{ ++ struct uio_platdata *pdata = platform_get_drvdata(pdev); ++ ++ uio_unregister_device(pdata->uioinfo); ++ ++ return 0; ++} ++ ++static struct platform_driver uio_pdrv = { ++ .probe = uio_pdrv_probe, ++ .remove = uio_pdrv_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init uio_pdrv_init(void) ++{ ++ return platform_driver_register(&uio_pdrv); ++} ++ ++static void __exit uio_pdrv_exit(void) ++{ ++ platform_driver_unregister(&uio_pdrv); ++} ++module_init(uio_pdrv_init); ++module_exit(uio_pdrv_exit); ++ ++MODULE_AUTHOR("Uwe Kleine-Koenig"); ++MODULE_DESCRIPTION("Userspace I/O platform driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/driver-core/uio-add-write-function-to-allow-irq-masking.patch b/driver-core/uio-add-write-function-to-allow-irq-masking.patch new file mode 100644 index 00000000000000..bf3f1f446c1604 --- /dev/null +++ b/driver-core/uio-add-write-function-to-allow-irq-masking.patch @@ -0,0 +1,155 @@ +From hjk@linutronix.de Sat May 31 02:37:42 2008 +From: Hans J. Koch <hjk@linutronix.de> +Date: Sat, 31 May 2008 11:37:27 +0200 +Date: Thu, Fri, 23 May 2008 13:50:14 +0200 +Subject: UIO: Add write function to allow irq masking +To: linux-kernel@vger.kernel.org +Cc: Greg Kroah-Hartman <gregkh@suse.de>, Jan Altenberg <jan.altenberg@linutronix.de>, Thomas Gleixner <tglx@linutronix.de>, Uwe Kleine-K�nig <Uwe.Kleine-Koenig@digi.com>, Magnus Damm <magnus.damm@gmail.com> + + +Sometimes it is necessary to enable/disable the interrupt of a UIO device +from the userspace part of the driver. With this patch, the UIO kernel driver +can implement an "irqcontrol()" function that does this. Userspace can write +an s32 value to /dev/uioX (usually 0 or 1 to turn the irq off or on). The +UIO core will then call the driver's irqcontrol function. + +Signed-off-by: Hans J. Koch <hjk@linutronix.de> +Acked-by: Uwe Kleine-K�nig <Uwe.Kleine-Koenig@digi.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/DocBook/uio-howto.tmpl | 40 ++++++++++++++++++++++++++++++++++- + drivers/uio/uio.c | 26 ++++++++++++++++++++++ + include/linux/uio_driver.h | 2 + + 3 files changed, 67 insertions(+), 1 deletion(-) + +--- a/Documentation/DocBook/uio-howto.tmpl ++++ b/Documentation/DocBook/uio-howto.tmpl +@@ -30,6 +30,12 @@ + + <revhistory> + <revision> ++ <revnumber>0.5</revnumber> ++ <date>2008-05-22</date> ++ <authorinitials>hjk</authorinitials> ++ <revremark>Added description of write() function.</revremark> ++ </revision> ++ <revision> + <revnumber>0.4</revnumber> + <date>2007-11-26</date> + <authorinitials>hjk</authorinitials> +@@ -64,7 +70,7 @@ + <?dbhtml filename="copyright.html"?> + <title>Copyright and License</title> + <para> +- Copyright (c) 2006 by Hans-Jürgen Koch.</para> ++ Copyright (c) 2006-2008 by Hans-Jürgen Koch.</para> + <para> + This documentation is Free Software licensed under the terms of the + GPL version 2. +@@ -189,6 +195,30 @@ interested in translating it, please ema + represents the total interrupt count. You can use this number + to figure out if you missed some interrupts. + </para> ++ <para> ++ For some hardware that has more than one interrupt source internally, ++ but not separate IRQ mask and status registers, there might be ++ situations where userspace cannot determine what the interrupt source ++ was if the kernel handler disables them by writing to the chip's IRQ ++ register. In such a case, the kernel has to disable the IRQ completely ++ to leave the chip's register untouched. Now the userspace part can ++ determine the cause of the interrupt, but it cannot re-enable ++ interrupts. Another cornercase is chips where re-enabling interrupts ++ is a read-modify-write operation to a combined IRQ status/acknowledge ++ register. This would be racy if a new interrupt occurred ++ simultaneously. ++ </para> ++ <para> ++ To address these problems, UIO also implements a write() function. It ++ is normally not used and can be ignored for hardware that has only a ++ single interrupt source or has separate IRQ mask and status registers. ++ If you need it, however, a write to <filename>/dev/uioX</filename> ++ will call the <function>irqcontrol()</function> function implemented ++ by the driver. You have to write a 32-bit value that is usually either ++ 0 or 1 to disable or enable interrupts. If a driver does not implement ++ <function>irqcontrol()</function>, <function>write()</function> will ++ return with <varname>-ENOSYS</varname>. ++ </para> + + <para> + To handle interrupts properly, your custom kernel module can +@@ -362,6 +392,14 @@ device is actually used. + <function>open()</function>, you will probably also want a custom + <function>release()</function> function. + </para></listitem> ++ ++<listitem><para> ++<varname>int (*irqcontrol)(struct uio_info *info, s32 irq_on) ++</varname>: Optional. If you need to be able to enable or disable ++interrupts from userspace by writing to <filename>/dev/uioX</filename>, ++you can implement this function. The parameter <varname>irq_on</varname> ++will be 0 to disable interrupts and 1 to enable them. ++</para></listitem> + </itemizedlist> + + <para> +--- a/drivers/uio/uio.c ++++ b/drivers/uio/uio.c +@@ -420,6 +420,31 @@ static ssize_t uio_read(struct file *fil + return retval; + } + ++static ssize_t uio_write(struct file *filep, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct uio_listener *listener = filep->private_data; ++ struct uio_device *idev = listener->dev; ++ ssize_t retval; ++ s32 irq_on; ++ ++ if (idev->info->irq == UIO_IRQ_NONE) ++ return -EIO; ++ ++ if (count != sizeof(s32)) ++ return -EINVAL; ++ ++ if (!idev->info->irqcontrol) ++ return -ENOSYS; ++ ++ if (copy_from_user(&irq_on, buf, count)) ++ return -EFAULT; ++ ++ retval = idev->info->irqcontrol(idev->info, irq_on); ++ ++ return retval ? retval : sizeof(s32); ++} ++ + static int uio_find_mem_index(struct vm_area_struct *vma) + { + int mi; +@@ -539,6 +564,7 @@ static const struct file_operations uio_ + .open = uio_open, + .release = uio_release, + .read = uio_read, ++ .write = uio_write, + .mmap = uio_mmap, + .poll = uio_poll, + .fasync = uio_fasync, +--- a/include/linux/uio_driver.h ++++ b/include/linux/uio_driver.h +@@ -53,6 +53,7 @@ struct uio_device; + * @mmap: mmap operation for this uio device + * @open: open operation for this uio device + * @release: release operation for this uio device ++ * @irqcontrol: disable/enable irqs when 0/1 is written to /dev/uioX + */ + struct uio_info { + struct uio_device *uio_dev; +@@ -66,6 +67,7 @@ struct uio_info { + int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); + int (*open)(struct uio_info *info, struct inode *inode); + int (*release)(struct uio_info *info, struct inode *inode); ++ int (*irqcontrol)(struct uio_info *info, s32 irq_on); + }; + + extern int __must_check diff --git a/driver-core/uio-fix-uio-kconfig-dependancies.patch b/driver-core/uio-fix-uio-kconfig-dependancies.patch new file mode 100644 index 00000000000000..6c0772aa11f954 --- /dev/null +++ b/driver-core/uio-fix-uio-kconfig-dependancies.patch @@ -0,0 +1,39 @@ +From hjk@linutronix.de Sat May 31 02:37:42 2008 +From: Uwe Kleine-K�nig <Uwe.Kleine-Koenig@digi.com> +Date: Sat, 31 May 2008 11:37:27 +0200 +Subject: UIO: fix UIO Kconfig dependancies +To: linux-kernel@vger.kernel.org +Cc: Hans J. Koch <hjk@linutronix.de>, Greg Kroah-Hartman <gregkh@suse.de> + + +ae210f188614bb3d1ee3f19c64e28e3cdd44877c introduced a big "if UIO"/"endif" +where all uio drivers are defined. So know there is no need for them to +depend explicitly on UIO. + +Signed-off-by: Uwe Kleine-K�nig <Uwe.Kleine-Koenig@digi.com> +Signed-off-by: Hans J. Koch <hjk@linutronix.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/uio/Kconfig | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/uio/Kconfig ++++ b/drivers/uio/Kconfig +@@ -15,7 +15,7 @@ if UIO + + config UIO_CIF + tristate "generic Hilscher CIF Card driver" +- depends on UIO && PCI ++ depends on PCI + default n + help + Driver for Hilscher CIF DeviceNet and Profibus cards. This +@@ -28,7 +28,6 @@ config UIO_CIF + + config UIO_SMX + tristate "SMX cryptengine UIO interface" +- depends on UIO + default n + help + Userspace IO interface to the Cryptography engine found on the @@ -21,6 +21,7 @@ driver-core.current/kobject-fix-kobject_rename-and-config_sysfs.patch ################################# usb.current/usb-fix-build-bug-in-usb_isightfw.patch usb.current/isight_firmware-avoid-crash-on-loading-invalid-firmware.patch +usb.current/usb-isp1760-assign-resource-fields-before-adding-hcd.patch ##################################################################### # Stuff to be merged after 2.6.26 is out @@ -91,6 +92,10 @@ driver-core/driver-core-remove-device_id_size-define.patch driver-core/driver-core-fix-a-lot-of-printk-usages-of-bus_id.patch driver-core/pnp-add-acpi-modalias-entries.patch +driver-core/uio-fix-uio-kconfig-dependancies.patch +driver-core/uio-add-write-function-to-allow-irq-masking.patch +driver-core/uio-add-generic-uio-platform-driver.patch +driver-core/kobject-reorder-kobject-to-save-space-on-64-bit-builds.patch # bus_id fun driver-core/pci-make-pci_name-use-dev_name.patch @@ -171,6 +176,32 @@ usb/usb-remove-documentation-usb-uhci.txt.patch usb/usb-serial-gadget-modular-tty-glue.patch usb/usb-serial-gadget-use-new-tty-glue.patch usb/usb-rndis-switch-to-seq_files.patch +usb/usb-at91_udc-updated-fifo-sizes.patch +usb/usb-gadget-support-descriptor-copying.patch +usb/usb-gadget-composite-gadget-framework.patch +usb/usb-gadget-zero-sourcesink-config-driver.patch +usb/usb-gadget-zero-loopback-function-driver.patch +usb/usb-gadget-zero-use-updated-framework.patch +usb/usb-serial-gadget-cdc-acm-function-driver.patch +usb/usb-serial-gadget-generic-serial-function-driver.patch +usb/usb-serial-gadget-use-updated-framework.patch +usb/usb-composite-support-16-interfaces-by-default.patch +usb/usb-gadget-push-bkl-down-into-drivers.patch +usb/usb-ftdi_usb-eliminate-ioctl-and-bkl-ioctl-use.patch +usb/usb-usblcd-push-down-bkl-into-driver.patch +usb/usb-iowarrior-push-down-bkl.patch +usb/usb-hiddev-switch-to-unlocked_ioctl.patch +usb/usb-auerwald-push-down-the-bkl-into-the-driver.patch +usb/usb-rio100-push-down-the-bkl.patch +usb/usb-sisusb-push-down-the-bkl.patch +usb/usb-ohci-ppc-of-use-linux-of_platform.h-instead-of-asm.patch +usb/usb-digi_accelport.c-trivial-sparse-lock-annotation.patch +usb/usb-cp2101.c-fix-sparse-signedness-mismatch-warnings.patch +usb/usb-speedtch.c-fix-sparse-shadowed-variable-warning.patch +usb/ohci-fix-toggle-bit-desynchronization-when-canceling-urbs.patch +usb/usb-missing-usb_put_hcd-to-ohci-at91.patch +usb/usb-make-sa1111-ohci-driver-sa11x0-specific.patch +usb/usb-ohci_hcd-hang-submit-vs.-rmmod-race.patch # wireless usb, still a work in progress usb/bitmap-add-bitmap_copy_le.patch @@ -207,6 +238,7 @@ usb/wusb-add-the-wire-adapter-core.patch usb/wusb-add-hwa-hc-wireless-host-controller-driver.patch usb/wusb-fix-error-path-for-wusb_set_dev_addr.patch usb/uwb-disable-command-event-filtering-for-DUB-1210.patch +usb/wusb-drivers-uwb-wlp-sysfs.c-move-misplaced-debug-statement.patch # my ols tutorial driver, never in mainline usb/usb-gotemp.patch diff --git a/usb.current/usb-isp1760-assign-resource-fields-before-adding-hcd.patch b/usb.current/usb-isp1760-assign-resource-fields-before-adding-hcd.patch new file mode 100644 index 00000000000000..d99624262e7287 --- /dev/null +++ b/usb.current/usb-isp1760-assign-resource-fields-before-adding-hcd.patch @@ -0,0 +1,42 @@ +From ncase@xes-inc.com Fri Jun 6 15:07:08 2008 +From: Nate Case <ncase@xes-inc.com> +Date: Wed, 21 May 2008 16:28:20 -0500 +Subject: USB: isp1760: Assign resource fields before adding hcd +To: Sebastian Siewior <bigeasy@linutronix.de> +Cc: Greg Kroah-Hartman <gregkh@suse.de>, linux-usb@vger.kernel.org, linuxppc-dev <linuxppc-dev@ozlabs.org> +Message-ID: <1211405300.13845.627.camel@localhost.localdomain> + + +This fixes the bogus "io mem 0x00000000" message printed +during driver init due to hcd->rsrc_start being assigned after +the call to usb_add_hcd(). + +Signed-off-by: Nate Case <ncase@xes-inc.com> +Acked-by: Sebastian Siewior <bigeasy@linutronix.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/isp1760-hcd.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/usb/host/isp1760-hcd.c ++++ b/drivers/usb/host/isp1760-hcd.c +@@ -2207,14 +2207,14 @@ struct usb_hcd *isp1760_register(u64 res + goto err_put; + } + +- ret = usb_add_hcd(hcd, irq, irqflags); +- if (ret) +- goto err_unmap; +- + hcd->irq = irq; + hcd->rsrc_start = res_start; + hcd->rsrc_len = res_len; + ++ ret = usb_add_hcd(hcd, irq, irqflags); ++ if (ret) ++ goto err_unmap; ++ + return hcd; + + err_unmap: diff --git a/usb/ohci-fix-toggle-bit-desynchronization-when-canceling-urbs.patch b/usb/ohci-fix-toggle-bit-desynchronization-when-canceling-urbs.patch new file mode 100644 index 00000000000000..42a59cd13d9cf5 --- /dev/null +++ b/usb/ohci-fix-toggle-bit-desynchronization-when-canceling-urbs.patch @@ -0,0 +1,58 @@ +From leonidv11@gmail.com Fri Jun 6 15:17:55 2008 +From: Leonid <leonidv11@gmail.com> +Date: Fri, 30 May 2008 11:59:01 -0700 +Subject: OHCI: fix toggle bit desynchronization when canceling URBs +To: "USB list" <linux-usb@vger.kernel.org> +Cc: "Greg KH" <greg@kroah.com>, "David Brownell" <david-b@pacbell.net> +Message-ID: <efc7ee340805301159k30108141h6ceec6f4cefe840c@mail.gmail.com> +Content-Disposition: inline + + +This patch fixes a problem where canceling (bulk or interrupts) URBs +through the OHCI controller can result in a desynchronization of the +toggle bits between the HC and the peripheral. The reason for this +issue stems from the fact that during the cancellation process the TD +is never officially "retired" by the HC and, therefore, as per the +OHCI specification, the TD's internal toggle state does not get passed +to the ED's toggle state. Future TDs that obtain their toggle state +from the ED would then be using an incorrect toggle state. + +The fix is simple: the toggle state of any canceled TDs are +propagated back to the ED in the finish_unlinks function. + +Signed-off-by: Leonid <leonidv11@gmail.com> +Acked-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ohci-q.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/usb/host/ohci-q.c ++++ b/drivers/usb/host/ohci-q.c +@@ -952,6 +952,7 @@ rescan_this: + struct urb *urb; + urb_priv_t *urb_priv; + __hc32 savebits; ++ u32 tdINFO; + + td = list_entry (entry, struct td, td_list); + urb = td->urb; +@@ -966,6 +967,17 @@ rescan_this: + savebits = *prev & ~cpu_to_hc32 (ohci, TD_MASK); + *prev = td->hwNextTD | savebits; + ++ /* If this was unlinked, the TD may not have been ++ * retired ... so manually save the data toggle. ++ * When this is ISO (no toggle), the value we save ++ * will be ignored by the controller. ++ */ ++ tdINFO = hc32_to_cpup (ohci, &td->hwINFO); ++ if ((tdINFO & TD_T) == TD_T_DATA0) ++ ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_C); ++ else if ((tdINFO & TD_T) == TD_T_DATA1) ++ ed->hwHeadP |= cpu_to_hc32(ohci, ED_C); ++ + /* HC may have partly processed this TD */ + td_done (ohci, urb, td); + urb_priv->td_cnt++; diff --git a/usb/usb-at91_udc-updated-fifo-sizes.patch b/usb/usb-at91_udc-updated-fifo-sizes.patch new file mode 100644 index 00000000000000..cd1a477417e5ef --- /dev/null +++ b/usb/usb-at91_udc-updated-fifo-sizes.patch @@ -0,0 +1,44 @@ +From david-b@pacbell.net Fri Jun 6 15:06:20 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 27 May 2008 19:24:20 -0700 +Subject: USB: at91_udc: updated fifo sizes +To: linux-usb@vger.kernel.org +Cc: Greg KH <greg@kroah.com>, Andrew Victor <linux@maxim.org.za> +Message-ID: <200805271924.21005.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +It turns out newer versions of the AT91 UDC hardware have increased +sizes of some of the FIFOs. Reporting that is a Good Thing. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/at91_udc.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/usb/gadget/at91_udc.c ++++ b/drivers/usb/gadget/at91_udc.c +@@ -1687,6 +1687,19 @@ static int __init at91udc_probe(struct p + udc->board.pullup_active_low); + } + ++ /* newer chips have more FIFO memory than rm9200 */ ++ if (cpu_is_at91sam9260()) { ++ udc->ep[0].maxpacket = 64; ++ udc->ep[3].maxpacket = 64; ++ udc->ep[4].maxpacket = 512; ++ udc->ep[5].maxpacket = 512; ++ } else if (cpu_is_at91sam9261()) { ++ udc->ep[3].maxpacket = 64; ++ } else if (cpu_is_at91sam9263()) { ++ udc->ep[0].maxpacket = 64; ++ udc->ep[3].maxpacket = 64; ++ } ++ + udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1); + if (!udc->udp_baseaddr) { + retval = -ENOMEM; diff --git a/usb/usb-auerwald-push-down-the-bkl-into-the-driver.patch b/usb/usb-auerwald-push-down-the-bkl-into-the-driver.patch new file mode 100644 index 00000000000000..cd09705f3286dc --- /dev/null +++ b/usb/usb-auerwald-push-down-the-bkl-into-the-driver.patch @@ -0,0 +1,59 @@ +From alan@lxorguk.ukuu.org.uk Fri Jun 6 15:15:02 2008 +From: Alan Cox <alan@lxorguk.ukuu.org.uk> +Date: Thu, 22 May 2008 22:46:25 +0100 +Subject: USB: auerwald: Push down the BKL into the driver +To: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org +Message-ID: <20080522224625.6fe88245@core> + + +Also fix the unknown ioctl return code + +Signed-off-by: Alan Cox <alan@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/misc/auerswald.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/usb/misc/auerswald.c ++++ b/drivers/usb/misc/auerswald.c +@@ -1421,7 +1421,8 @@ ofail: mutex_unlock(&cp->mutex); + + + /* IOCTL functions */ +-static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++static long auerchar_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) + { + pauerchar_t ccp = (pauerchar_t) file->private_data; + int ret = 0; +@@ -1452,7 +1453,7 @@ static int auerchar_ioctl (struct inode + mutex_unlock(&ccp->mutex); + return -ENODEV; + } +- ++ lock_kernel(); + switch (cmd) { + + /* return != 0 if Transmitt channel ready to send */ +@@ -1547,9 +1548,10 @@ static int auerchar_ioctl (struct inode + + default: + dbg ("IOCTL_AU_UNKNOWN"); +- ret = -ENOIOCTLCMD; ++ ret = -ENOTTY; + break; + } ++ unlock_kernel(); + /* release the mutexes */ + mutex_unlock(&cp->mutex); + mutex_unlock(&ccp->mutex); +@@ -1860,7 +1862,7 @@ static const struct file_operations auer + .llseek = no_llseek, + .read = auerchar_read, + .write = auerchar_write, +- .ioctl = auerchar_ioctl, ++ .unlocked_ioctl = auerchar_ioctl, + .open = auerchar_open, + .release = auerchar_release, + }; diff --git a/usb/usb-composite-support-16-interfaces-by-default.patch b/usb/usb-composite-support-16-interfaces-by-default.patch new file mode 100644 index 00000000000000..30070e710c8891 --- /dev/null +++ b/usb/usb-composite-support-16-interfaces-by-default.patch @@ -0,0 +1,31 @@ +From felipe.balbi@nokia.com Fri Jun 6 15:12:15 2008 +From: Felipe Balbi <felipe.balbi@nokia.com> +Date: Thu, 22 May 2008 02:45:11 +0300 +Subject: USB: COMPOSITE: Support 16 interfaces by default +To: linux-usb@vger.kernel.org +Cc: David Brownell <dbrownell@sourceforge.users.net>, Felipe Balbi <felipe.balbi@nokia.com> +Message-ID: <1211413513-1320-2-git-send-email-felipe.balbi@nokia.com> + + +8 interfaces is not enough if we try to use +3 instances of obex function driver. + +Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> +Acked-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + include/linux/usb/composite.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/usb/composite.h ++++ b/include/linux/usb/composite.h +@@ -147,7 +147,7 @@ ep_choose(struct usb_gadget *g, struct u + return fs; + } + +-#define MAX_CONFIG_INTERFACES 8 /* arbitrary; max 255 */ ++#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ + + /** + * struct usb_configuration - represents one gadget configuration diff --git a/usb/usb-cp2101.c-fix-sparse-signedness-mismatch-warnings.patch b/usb/usb-cp2101.c-fix-sparse-signedness-mismatch-warnings.patch new file mode 100644 index 00000000000000..04281707dffc02 --- /dev/null +++ b/usb/usb-cp2101.c-fix-sparse-signedness-mismatch-warnings.patch @@ -0,0 +1,128 @@ +From harvey.harrison@gmail.com Fri Jun 6 15:17:09 2008 +From: Harvey Harrison <harvey.harrison@gmail.com> +Date: Fri, 30 May 2008 10:29:55 -0700 +Subject: USB: cp2101.c fix sparse signedness mismatch warnings +To: Greg KH <gregkh@suse.de> +Cc: Andrew Morton <akpm@linux-foundation.org> +Message-ID: <1212168595.28403.198.camel@brick> + + +The get/set 2101_config helpers take an unsigned int rather than an +int. It is safe to change these in each case and may even produce +better code as it will be an unsigned divide rather than a signed +divide in places. All other manipulation was setting/masking bits +which will not be affected by the sign change. + +Fixes the following sparse warnings: +drivers/usb/serial/cp2101.c:378:44: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:378:44: expected unsigned int *data +drivers/usb/serial/cp2101.c:378:44: got int *<noident> +drivers/usb/serial/cp2101.c:388:40: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:388:40: expected unsigned int *data +drivers/usb/serial/cp2101.c:388:40: got int *<noident> +drivers/usb/serial/cp2101.c:413:42: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:413:42: expected unsigned int *data +drivers/usb/serial/cp2101.c:413:42: got int *<noident> +drivers/usb/serial/cp2101.c:421:42: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:421:42: expected unsigned int *data +drivers/usb/serial/cp2101.c:421:42: got int *<noident> +drivers/usb/serial/cp2101.c:444:42: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:444:42: expected unsigned int *data +drivers/usb/serial/cp2101.c:444:42: got int *<noident> +drivers/usb/serial/cp2101.c:451:42: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:451:42: expected unsigned int *data +drivers/usb/serial/cp2101.c:451:42: got int *<noident> +drivers/usb/serial/cp2101.c:458:42: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:458:42: expected unsigned int *data +drivers/usb/serial/cp2101.c:458:42: got int *<noident> +drivers/usb/serial/cp2101.c:471:42: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:471:42: expected unsigned int *data +drivers/usb/serial/cp2101.c:471:42: got int *<noident> +drivers/usb/serial/cp2101.c:481:42: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:481:42: expected unsigned int *data +drivers/usb/serial/cp2101.c:481:42: got int *<noident> +drivers/usb/serial/cp2101.c:561:41: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:561:41: expected unsigned int *data +drivers/usb/serial/cp2101.c:561:41: got int *<noident> +drivers/usb/serial/cp2101.c:591:45: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:591:45: expected unsigned int *data +drivers/usb/serial/cp2101.c:591:45: got int *<noident> +drivers/usb/serial/cp2101.c:597:41: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:597:41: expected unsigned int *data +drivers/usb/serial/cp2101.c:597:41: got int *<noident> +drivers/usb/serial/cp2101.c:608:45: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:608:45: expected unsigned int *data +drivers/usb/serial/cp2101.c:608:45: got int *<noident> +drivers/usb/serial/cp2101.c:614:41: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:614:41: expected unsigned int *data +drivers/usb/serial/cp2101.c:614:41: got int *<noident> +drivers/usb/serial/cp2101.c:623:45: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:623:45: expected unsigned int *data +drivers/usb/serial/cp2101.c:623:45: got int *<noident> +drivers/usb/serial/cp2101.c:680:50: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:680:50: expected unsigned int *data +drivers/usb/serial/cp2101.c:680:50: got int *<noident> +drivers/usb/serial/cp2101.c:690:43: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:690:43: expected unsigned int *data +drivers/usb/serial/cp2101.c:690:43: got int *<noident> +drivers/usb/serial/cp2101.c:715:41: warning: incorrect type in argument 3 (different signedness) +drivers/usb/serial/cp2101.c:715:41: expected unsigned int *data +drivers/usb/serial/cp2101.c:715:41: got int *<noident> + +Signed-off-by: Harvey Harrison <harvey.harrison@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/cp2101.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +--- a/drivers/usb/serial/cp2101.c ++++ b/drivers/usb/serial/cp2101.c +@@ -365,8 +365,8 @@ static void cp2101_close (struct usb_ser + static void cp2101_get_termios (struct usb_serial_port *port) + { + unsigned int cflag, modem_ctl[4]; +- int baud; +- int bits; ++ unsigned int baud; ++ unsigned int bits; + + dbg("%s - port %d", __func__, port->number); + +@@ -498,7 +498,7 @@ static void cp2101_set_termios (struct u + struct ktermios *old_termios) + { + unsigned int cflag, old_cflag; +- int baud=0, bits; ++ unsigned int baud = 0, bits; + unsigned int modem_ctl[4]; + + dbg("%s - port %d", __func__, port->number); +@@ -654,7 +654,7 @@ static void cp2101_set_termios (struct u + static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear) + { +- int control = 0; ++ unsigned int control = 0; + + dbg("%s - port %d", __func__, port->number); + +@@ -683,7 +683,8 @@ static int cp2101_tiocmset (struct usb_s + + static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) + { +- int control, result; ++ unsigned int control; ++ int result; + + dbg("%s - port %d", __func__, port->number); + +@@ -703,7 +704,7 @@ static int cp2101_tiocmget (struct usb_s + + static void cp2101_break_ctl (struct usb_serial_port *port, int break_state) + { +- int state; ++ unsigned int state; + + dbg("%s - port %d", __func__, port->number); + if (break_state == 0) diff --git a/usb/usb-digi_accelport.c-trivial-sparse-lock-annotation.patch b/usb/usb-digi_accelport.c-trivial-sparse-lock-annotation.patch new file mode 100644 index 00000000000000..7c37e2cfda7a6b --- /dev/null +++ b/usb/usb-digi_accelport.c-trivial-sparse-lock-annotation.patch @@ -0,0 +1,26 @@ +From harvey.harrison@gmail.com Fri Jun 6 15:16:54 2008 +From: Harvey Harrison <harvey.harrison@gmail.com> +Date: Fri, 30 May 2008 10:18:53 -0700 +Subject: USB: digi_accelport.c trivial sparse lock annotation +To: Greg KH <gregkh@suse.de> +Cc: LKML <linux-kernel@vger.kernel.org>, Al Viro <viro@ZenIV.linux.org.uk> +Message-ID: <1212167934.28403.196.camel@brick> + + +Signed-off-by: Harvey Harrison <harvey.harrison@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/digi_acceleport.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/usb/serial/digi_acceleport.c ++++ b/drivers/usb/serial/digi_acceleport.c +@@ -571,6 +571,7 @@ static struct usb_serial_driver digi_acc + static long cond_wait_interruptible_timeout_irqrestore( + wait_queue_head_t *q, long timeout, + spinlock_t *lock, unsigned long flags) ++__releases(lock) + { + DEFINE_WAIT(wait); + diff --git a/usb/usb-ftdi_usb-eliminate-ioctl-and-bkl-ioctl-use.patch b/usb/usb-ftdi_usb-eliminate-ioctl-and-bkl-ioctl-use.patch new file mode 100644 index 00000000000000..1cf45ee232bbc9 --- /dev/null +++ b/usb/usb-ftdi_usb-eliminate-ioctl-and-bkl-ioctl-use.patch @@ -0,0 +1,57 @@ +From alan@lxorguk.ukuu.org.uk Fri Jun 6 15:13:59 2008 +From: Alan Cox <alan@lxorguk.ukuu.org.uk> +Date: Thu, 22 May 2008 22:04:48 +0100 +Subject: USB: ftdi_usb: Eliminate ioctl and BKL ioctl use +To: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org +Message-ID: <20080522220448.7d77a7db@core> + + +ftdi has one ioctl, which is buggy and for debugging. Kill it off + +Signed-off-by: Alan Cox <alan@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/misc/ftdi-elan.c | 24 ------------------------ + 1 file changed, 24 deletions(-) + +--- a/drivers/usb/misc/ftdi-elan.c ++++ b/drivers/usb/misc/ftdi-elan.c +@@ -656,29 +656,6 @@ static int ftdi_elan_release(struct inod + } + + +-#define FTDI_ELAN_IOC_MAGIC 0xA1 +-#define FTDI_ELAN_IOCDEBUG _IOC(_IOC_WRITE, FTDI_ELAN_IOC_MAGIC, 1, 132) +-static int ftdi_elan_ioctl(struct inode *inode, struct file *file, +- unsigned int cmd, unsigned long arg) +-{ +- switch (cmd) { +- case FTDI_ELAN_IOCDEBUG:{ +- char line[132]; +- int size = strncpy_from_user(line, +- (const char __user *)arg, sizeof(line)); +- if (size < 0) { +- return -EINVAL; +- } else { +- printk(KERN_ERR "TODO: ioctl %s\n", line); +- return 0; +- } +- } +- default: +- return -EFAULT; +- } +-} +- +- + /* + * + * blocking bulk reads are used to get data from the device +@@ -1222,7 +1199,6 @@ error_1: + static const struct file_operations ftdi_elan_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, +- .ioctl = ftdi_elan_ioctl, + .read = ftdi_elan_read, + .write = ftdi_elan_write, + .open = ftdi_elan_open, diff --git a/usb/usb-gadget-composite-gadget-framework.patch b/usb/usb-gadget-composite-gadget-framework.patch new file mode 100644 index 00000000000000..51a992806b9803 --- /dev/null +++ b/usb/usb-gadget-composite-gadget-framework.patch @@ -0,0 +1,1477 @@ +From david-b@pacbell.net Fri Jun 6 15:10:24 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 20 May 2008 11:04:46 -0700 +Subject: usb gadget: composite gadget framework +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> +Message-ID: <200805201104.46710.david-b@pacbell.net> +Content-Disposition: inline + + +Add <linux/usb/composite.h> interfaces for composite gadget drivers, and +basic implementation support behind it: + + - struct usb_function ... groups one or more interfaces into a function + managed as one unit within a configuration, to which it's added by + usb_add_function(). + + - struct usb_configuration ... groups one or more such functions into + a configuration managed as one unit by a driver, to which it's added + by usb_add_config(). These operate at either high or full/low speeds + and at a given bMaxPower. + + - struct usb_composite_driver ... groups one or more such configurations + into a gadget driver, which may be registered or unregistered. + + - struct usb_composite_dev ... a usb_composite_driver manages this; it + wraps the usb_gadget exposed by the controller driver. + +This also includes some basic kerneldoc. + +How to use it (the short version): provide a usb_composite_driver with a +bind() that calls usb_add_config() for each of the needed configurations. +The configurations in turn have bind() calls, which will usb_add_function() +for each function required. Each function's bind() allocates resources +needed to perform its tasks, like endpoints; sometimes configurations will +allocate resources too. + +Separate patches will convert most gadget drivers to this infrastructure. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/DocBook/gadget.tmpl | 35 + + drivers/usb/gadget/composite.c | 1041 ++++++++++++++++++++++++++++++++++++++ + include/linux/usb/composite.h | 338 ++++++++++++ + 3 files changed, 1414 insertions(+) + +--- a/Documentation/DocBook/gadget.tmpl ++++ b/Documentation/DocBook/gadget.tmpl +@@ -524,6 +524,41 @@ These utilities include endpoint autocon + <!-- !Edrivers/usb/gadget/epautoconf.c --> + </sect1> + ++<sect1 id="composite"><title>Composite Device Framework</title> ++ ++<para>The core API is sufficient for writing drivers for composite ++USB devices (with more than one function in a given configuration), ++and also multi-configuration devices (also more than one function, ++but not necessarily sharing a given configuration). ++There is however an optional framework which makes it easier to ++reuse and combine functions. ++</para> ++ ++<para>Devices using this framework provide a <emphasis>struct ++usb_composite_driver</emphasis>, which in turn provides one or ++more <emphasis>struct usb_configuration</emphasis> instances. ++Each such configuration includes at least one ++<emphasis>struct usb_function</emphasis>, which packages a user ++visible role such as "network link" or "mass storage device". ++Management functions may also exist, such as "Device Firmware ++Upgrade". ++</para> ++ ++!Iinclude/linux/usb/composite.h ++!Edrivers/usb/gadget/composite.c ++ ++</sect1> ++ ++<sect1 id="functions"><title>Composite Device Functions</title> ++ ++<para>At this writing, a few of the current gadget drivers have ++been converted to this framework. ++Near-term plans include converting all of them, except for "gadgetfs". ++</para> ++ ++</sect1> ++ ++ + </chapter> + + <chapter id="controllers"><title>Peripheral Controller Drivers</title> +--- /dev/null ++++ b/drivers/usb/gadget/composite.c +@@ -0,0 +1,1041 @@ ++/* ++ * composite.c - infrastructure for Composite USB Gadgets ++ * ++ * Copyright (C) 2006-2008 David Brownell ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kallsyms.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/device.h> ++ ++#include <linux/usb/composite.h> ++ ++ ++/* ++ * The code in this file is utility code, used to build a gadget driver ++ * from one or more "function" drivers, one or more "configuration" ++ * objects, and a "usb_composite_driver" by gluing them together along ++ * with the relevant device-wide data. ++ */ ++ ++/* big enough to hold our biggest descriptor */ ++#define USB_BUFSIZ 512 ++ ++static struct usb_composite_driver *composite; ++ ++/* Some systems will need runtime overrides for the product identifers ++ * published in the device descriptor, either numbers or strings or both. ++ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). ++ */ ++ ++static ushort idVendor; ++module_param(idVendor, ushort, 0); ++MODULE_PARM_DESC(idVendor, "USB Vendor ID"); ++ ++static ushort idProduct; ++module_param(idProduct, ushort, 0); ++MODULE_PARM_DESC(idProduct, "USB Product ID"); ++ ++static ushort bcdDevice; ++module_param(bcdDevice, ushort, 0); ++MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); ++ ++static char *iManufacturer; ++module_param(iManufacturer, charp, 0); ++MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); ++ ++static char *iProduct; ++module_param(iProduct, charp, 0); ++MODULE_PARM_DESC(iProduct, "USB Product string"); ++ ++static char *iSerialNumber; ++module_param(iSerialNumber, charp, 0); ++MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/** ++ * usb_add_function() - add a function to a configuration ++ * @config: the configuration ++ * @function: the function being added ++ * Context: single threaded during gadget setup ++ * ++ * After initialization, each configuration must have one or more ++ * functions added to it. Adding a function involves calling its @bind() ++ * method to allocate resources such as interface and string identifiers ++ * and endpoints. ++ * ++ * This function returns the value of the function's bind(), which is ++ * zero for success else a negative errno value. ++ */ ++int __init usb_add_function(struct usb_configuration *config, ++ struct usb_function *function) ++{ ++ int value = -EINVAL; ++ ++ DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", ++ function->name, function, ++ config->label, config); ++ ++ if (!function->set_alt || !function->disable) ++ goto done; ++ ++ function->config = config; ++ list_add_tail(&function->list, &config->functions); ++ ++ /* REVISIT *require* function->bind? */ ++ if (function->bind) { ++ value = function->bind(config, function); ++ if (value < 0) { ++ list_del(&function->list); ++ function->config = NULL; ++ } ++ } else ++ value = 0; ++ ++ /* We allow configurations that don't work at both speeds. ++ * If we run into a lowspeed Linux system, treat it the same ++ * as full speed ... it's the function drivers that will need ++ * to avoid bulk and ISO transfers. ++ */ ++ if (!config->fullspeed && function->descriptors) ++ config->fullspeed = true; ++ if (!config->highspeed && function->hs_descriptors) ++ config->highspeed = true; ++ ++done: ++ if (value) ++ DBG(config->cdev, "adding '%s'/%p --> %d\n", ++ function->name, function, value); ++ return value; ++} ++ ++/** ++ * usb_interface_id() - allocate an unused interface ID ++ * @config: configuration associated with the interface ++ * @function: function handling the interface ++ * Context: single threaded during gadget setup ++ * ++ * usb_interface_id() is called from usb_function.bind() callbacks to ++ * allocate new interface IDs. The function driver will then store that ++ * ID in interface, association, CDC union, and other descriptors. It ++ * will also handle any control requests targetted at that interface, ++ * particularly changing its altsetting via set_alt(). There may ++ * also be class-specific or vendor-specific requests to handle. ++ * ++ * All interface identifier should be allocated using this routine, to ++ * ensure that for example different functions don't wrongly assign ++ * different meanings to the same identifier. Note that since interface ++ * identifers are configuration-specific, functions used in more than ++ * one configuration (or more than once in a given configuration) need ++ * multiple versions of the relevant descriptors. ++ * ++ * Returns the interface ID which was allocated; or -ENODEV if no ++ * more interface IDs can be allocated. ++ */ ++int __init usb_interface_id(struct usb_configuration *config, ++ struct usb_function *function) ++{ ++ unsigned id = config->next_interface_id; ++ ++ if (id < MAX_CONFIG_INTERFACES) { ++ config->interface[id] = function; ++ config->next_interface_id = id + 1; ++ return id; ++ } ++ return -ENODEV; ++} ++ ++static int config_buf(struct usb_configuration *config, ++ enum usb_device_speed speed, void *buf, u8 type) ++{ ++ struct usb_config_descriptor *c = buf; ++ void *next = buf + USB_DT_CONFIG_SIZE; ++ int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; ++ struct usb_function *f; ++ int status; ++ ++ /* write the config descriptor */ ++ c = buf; ++ c->bLength = USB_DT_CONFIG_SIZE; ++ c->bDescriptorType = type; ++ /* wTotalLength is written later */ ++ c->bNumInterfaces = config->next_interface_id; ++ c->bConfigurationValue = config->bConfigurationValue; ++ c->iConfiguration = config->iConfiguration; ++ c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; ++ c->bMaxPower = config->bMaxPower; ++ ++ /* There may be e.g. OTG descriptors */ ++ if (config->descriptors) { ++ status = usb_descriptor_fillbuf(next, len, ++ config->descriptors); ++ if (status < 0) ++ return status; ++ len -= status; ++ next += status; ++ } ++ ++ /* add each function's descriptors */ ++ list_for_each_entry(f, &config->functions, list) { ++ struct usb_descriptor_header **descriptors; ++ ++ if (speed == USB_SPEED_HIGH) ++ descriptors = f->hs_descriptors; ++ else ++ descriptors = f->descriptors; ++ if (!descriptors) ++ continue; ++ status = usb_descriptor_fillbuf(next, len, ++ (const struct usb_descriptor_header **) descriptors); ++ if (status < 0) ++ return status; ++ len -= status; ++ next += status; ++ } ++ ++ len = next - buf; ++ c->wTotalLength = cpu_to_le16(len); ++ return len; ++} ++ ++static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) ++{ ++ struct usb_gadget *gadget = cdev->gadget; ++ struct usb_configuration *c; ++ u8 type = w_value >> 8; ++ enum usb_device_speed speed = USB_SPEED_UNKNOWN; ++ ++ if (gadget_is_dualspeed(gadget)) { ++ int hs = 0; ++ ++ if (gadget->speed == USB_SPEED_HIGH) ++ hs = 1; ++ if (type == USB_DT_OTHER_SPEED_CONFIG) ++ hs = !hs; ++ if (hs) ++ speed = USB_SPEED_HIGH; ++ ++ } ++ ++ /* This is a lookup by config *INDEX* */ ++ w_value &= 0xff; ++ list_for_each_entry(c, &cdev->configs, list) { ++ /* ignore configs that won't work at this speed */ ++ if (speed == USB_SPEED_HIGH) { ++ if (!c->highspeed) ++ continue; ++ } else { ++ if (!c->fullspeed) ++ continue; ++ } ++ if (w_value == 0) ++ return config_buf(c, speed, cdev->req->buf, type); ++ w_value--; ++ } ++ return -EINVAL; ++} ++ ++static int count_configs(struct usb_composite_dev *cdev, unsigned type) ++{ ++ struct usb_gadget *gadget = cdev->gadget; ++ struct usb_configuration *c; ++ unsigned count = 0; ++ int hs = 0; ++ ++ if (gadget_is_dualspeed(gadget)) { ++ if (gadget->speed == USB_SPEED_HIGH) ++ hs = 1; ++ if (type == USB_DT_DEVICE_QUALIFIER) ++ hs = !hs; ++ } ++ list_for_each_entry(c, &cdev->configs, list) { ++ /* ignore configs that won't work at this speed */ ++ if (hs) { ++ if (!c->highspeed) ++ continue; ++ } else { ++ if (!c->fullspeed) ++ continue; ++ } ++ count++; ++ } ++ return count; ++} ++ ++static void device_qual(struct usb_composite_dev *cdev) ++{ ++ struct usb_qualifier_descriptor *qual = cdev->req->buf; ++ ++ qual->bLength = sizeof(*qual); ++ qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; ++ /* POLICY: same bcdUSB and device type info at both speeds */ ++ qual->bcdUSB = cdev->desc.bcdUSB; ++ qual->bDeviceClass = cdev->desc.bDeviceClass; ++ qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; ++ qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; ++ /* ASSUME same EP0 fifo size at both speeds */ ++ qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; ++ qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void reset_config(struct usb_composite_dev *cdev) ++{ ++ struct usb_function *f; ++ ++ DBG(cdev, "reset config\n"); ++ ++ list_for_each_entry(f, &cdev->config->functions, list) { ++ if (f->disable) ++ f->disable(f); ++ } ++ cdev->config = NULL; ++} ++ ++static int set_config(struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *ctrl, unsigned number) ++{ ++ struct usb_gadget *gadget = cdev->gadget; ++ struct usb_configuration *c = NULL; ++ int result = -EINVAL; ++ unsigned power = gadget_is_otg(gadget) ? 8 : 100; ++ int tmp; ++ ++ if (cdev->config) ++ reset_config(cdev); ++ ++ if (number) { ++ list_for_each_entry(c, &cdev->configs, list) { ++ if (c->bConfigurationValue == number) { ++ result = 0; ++ break; ++ } ++ } ++ if (result < 0) ++ goto done; ++ } else ++ result = 0; ++ ++ INFO(cdev, "%s speed config #%d: %s\n", ++ ({ char *speed; ++ switch (gadget->speed) { ++ case USB_SPEED_LOW: speed = "low"; break; ++ case USB_SPEED_FULL: speed = "full"; break; ++ case USB_SPEED_HIGH: speed = "high"; break; ++ default: speed = "?"; break; ++ } ; speed; }), number, c ? c->label : ""); ++ ++ if (!c) ++ goto done; ++ ++ cdev->config = c; ++ ++ /* Initialize all interfaces by setting them to altsetting zero. */ ++ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { ++ struct usb_function *f = c->interface[tmp]; ++ ++ if (!f) ++ break; ++ ++ result = f->set_alt(f, tmp, 0); ++ if (result < 0) { ++ DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", ++ tmp, f->name, f, result); ++ ++ reset_config(cdev); ++ goto done; ++ } ++ } ++ ++ /* when we return, be sure our power usage is valid */ ++ power = 2 * c->bMaxPower; ++done: ++ usb_gadget_vbus_draw(gadget, power); ++ return result; ++} ++ ++/** ++ * usb_add_config() - add a configuration to a device. ++ * @cdev: wraps the USB gadget ++ * @config: the configuration, with bConfigurationValue assigned ++ * Context: single threaded during gadget setup ++ * ++ * One of the main tasks of a composite driver's bind() routine is to ++ * add each of the configurations it supports, using this routine. ++ * ++ * This function returns the value of the configuration's bind(), which ++ * is zero for success else a negative errno value. Binding configurations ++ * assigns global resources including string IDs, and per-configuration ++ * resources such as interface IDs and endpoints. ++ */ ++int __init usb_add_config(struct usb_composite_dev *cdev, ++ struct usb_configuration *config) ++{ ++ int status = -EINVAL; ++ struct usb_configuration *c; ++ ++ DBG(cdev, "adding config #%u '%s'/%p\n", ++ config->bConfigurationValue, ++ config->label, config); ++ ++ if (!config->bConfigurationValue || !config->bind) ++ goto done; ++ ++ /* Prevent duplicate configuration identifiers */ ++ list_for_each_entry(c, &cdev->configs, list) { ++ if (c->bConfigurationValue == config->bConfigurationValue) { ++ status = -EBUSY; ++ goto done; ++ } ++ } ++ ++ config->cdev = cdev; ++ list_add_tail(&config->list, &cdev->configs); ++ ++ INIT_LIST_HEAD(&config->functions); ++ config->next_interface_id = 0; ++ ++ status = config->bind(config); ++ if (status < 0) { ++ list_del(&config->list); ++ config->cdev = NULL; ++ } else { ++ unsigned i; ++ ++ DBG(cdev, "cfg %d/%p speeds:%s%s\n", ++ config->bConfigurationValue, config, ++ config->highspeed ? " high" : "", ++ config->fullspeed ++ ? (gadget_is_dualspeed(cdev->gadget) ++ ? " full" ++ : " full/low") ++ : ""); ++ ++ for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { ++ struct usb_function *f = config->interface[i]; ++ ++ if (!f) ++ continue; ++ DBG(cdev, " interface %d = %s/%p\n", ++ i, f->name, f); ++ } ++ } ++ ++ /* set_alt(), or next config->bind(), sets up ++ * ep->driver_data as needed. ++ */ ++ usb_ep_autoconfig_reset(cdev->gadget); ++ ++done: ++ if (status) ++ DBG(cdev, "added config '%s'/%u --> %d\n", config->label, ++ config->bConfigurationValue, status); ++ return status; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* We support strings in multiple languages ... string descriptor zero ++ * says which languages are supported. The typical case will be that ++ * only one language (probably English) is used, with I18N handled on ++ * the host side. ++ */ ++ ++static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) ++{ ++ const struct usb_gadget_strings *s; ++ u16 language; ++ __le16 *tmp; ++ ++ while (*sp) { ++ s = *sp; ++ language = cpu_to_le16(s->language); ++ for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { ++ if (*tmp == language) ++ goto repeat; ++ } ++ *tmp++ = language; ++repeat: ++ sp++; ++ } ++} ++ ++static int lookup_string( ++ struct usb_gadget_strings **sp, ++ void *buf, ++ u16 language, ++ int id ++) ++{ ++ struct usb_gadget_strings *s; ++ int value; ++ ++ while (*sp) { ++ s = *sp++; ++ if (s->language != language) ++ continue; ++ value = usb_gadget_get_string(s, id, buf); ++ if (value > 0) ++ return value; ++ } ++ return -EINVAL; ++} ++ ++static int get_string(struct usb_composite_dev *cdev, ++ void *buf, u16 language, int id) ++{ ++ struct usb_configuration *c; ++ struct usb_function *f; ++ int len; ++ ++ /* Yes, not only is USB's I18N support probably more than most ++ * folk will ever care about ... also, it's all supported here. ++ * (Except for UTF8 support for Unicode's "Astral Planes".) ++ */ ++ ++ /* 0 == report all available language codes */ ++ if (id == 0) { ++ struct usb_string_descriptor *s = buf; ++ struct usb_gadget_strings **sp; ++ ++ memset(s, 0, 256); ++ s->bDescriptorType = USB_DT_STRING; ++ ++ sp = composite->strings; ++ if (sp) ++ collect_langs(sp, s->wData); ++ ++ list_for_each_entry(c, &cdev->configs, list) { ++ sp = c->strings; ++ if (sp) ++ collect_langs(sp, s->wData); ++ ++ list_for_each_entry(f, &c->functions, list) { ++ sp = f->strings; ++ if (sp) ++ collect_langs(sp, s->wData); ++ } ++ } ++ ++ for (len = 0; s->wData[len] && len <= 126; len++) ++ continue; ++ if (!len) ++ return -EINVAL; ++ ++ s->bLength = 2 * (len + 1); ++ return s->bLength; ++ } ++ ++ /* Otherwise, look up and return a specified string. String IDs ++ * are device-scoped, so we look up each string table we're told ++ * about. These lookups are infrequent; simpler-is-better here. ++ */ ++ if (composite->strings) { ++ len = lookup_string(composite->strings, buf, language, id); ++ if (len > 0) ++ return len; ++ } ++ list_for_each_entry(c, &cdev->configs, list) { ++ if (c->strings) { ++ len = lookup_string(c->strings, buf, language, id); ++ if (len > 0) ++ return len; ++ } ++ list_for_each_entry(f, &c->functions, list) { ++ if (!f->strings) ++ continue; ++ len = lookup_string(f->strings, buf, language, id); ++ if (len > 0) ++ return len; ++ } ++ } ++ return -EINVAL; ++} ++ ++/** ++ * usb_string_id() - allocate an unused string ID ++ * @cdev: the device whose string descriptor IDs are being allocated ++ * Context: single threaded during gadget setup ++ * ++ * @usb_string_id() is called from bind() callbacks to allocate ++ * string IDs. Drivers for functions, configurations, or gadgets will ++ * then store that ID in the appropriate descriptors and string table. ++ * ++ * All string identifier should be allocated using this routine, to ++ * ensure that for example different functions don't wrongly assign ++ * different meanings to the same identifier. ++ */ ++int __init usb_string_id(struct usb_composite_dev *cdev) ++{ ++ if (cdev->next_string_id < 254) { ++ /* string id 0 is reserved */ ++ cdev->next_string_id++; ++ return cdev->next_string_id; ++ } ++ return -ENODEV; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ if (req->status || req->actual != req->length) ++ DBG((struct usb_composite_dev *) ep->driver_data, ++ "setup complete --> %d, %d/%d\n", ++ req->status, req->actual, req->length); ++} ++ ++/* ++ * The setup() callback implements all the ep0 functionality that's ++ * not handled lower down, in hardware or the hardware driver(like ++ * device and endpoint feature flags, and their status). It's all ++ * housekeeping for the gadget function we're implementing. Most of ++ * the work is in config and function specific setup. ++ */ ++static int ++composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ struct usb_function *f = NULL; ++ ++ /* partial re-init of the response message; the function or the ++ * gadget might need to intercept e.g. a control-OUT completion ++ * when we delegate to it. ++ */ ++ req->zero = 0; ++ req->complete = composite_setup_complete; ++ req->length = USB_BUFSIZ; ++ gadget->ep0->driver_data = cdev; ++ ++ switch (ctrl->bRequest) { ++ ++ /* we handle all standard USB descriptors */ ++ case USB_REQ_GET_DESCRIPTOR: ++ if (ctrl->bRequestType != USB_DIR_IN) ++ goto unknown; ++ switch (w_value >> 8) { ++ ++ case USB_DT_DEVICE: ++ cdev->desc.bNumConfigurations = ++ count_configs(cdev, USB_DT_DEVICE); ++ value = min(w_length, (u16) sizeof cdev->desc); ++ memcpy(req->buf, &cdev->desc, value); ++ break; ++ case USB_DT_DEVICE_QUALIFIER: ++ if (!gadget_is_dualspeed(gadget)) ++ break; ++ device_qual(cdev); ++ value = min_t(int, w_length, ++ sizeof(struct usb_qualifier_descriptor)); ++ break; ++ case USB_DT_OTHER_SPEED_CONFIG: ++ if (!gadget_is_dualspeed(gadget)) ++ break; ++ /* FALLTHROUGH */ ++ case USB_DT_CONFIG: ++ value = config_desc(cdev, w_value); ++ if (value >= 0) ++ value = min(w_length, (u16) value); ++ break; ++ case USB_DT_STRING: ++ value = get_string(cdev, req->buf, ++ w_index, w_value & 0xff); ++ if (value >= 0) ++ value = min(w_length, (u16) value); ++ break; ++ } ++ break; ++ ++ /* any number of configs can work */ ++ case USB_REQ_SET_CONFIGURATION: ++ if (ctrl->bRequestType != 0) ++ goto unknown; ++ if (gadget_is_otg(gadget)) { ++ if (gadget->a_hnp_support) ++ DBG(cdev, "HNP available\n"); ++ else if (gadget->a_alt_hnp_support) ++ DBG(cdev, "HNP on another port\n"); ++ else ++ VDBG(cdev, "HNP inactive\n"); ++ } ++ spin_lock(&cdev->lock); ++ value = set_config(cdev, ctrl, w_value); ++ spin_unlock(&cdev->lock); ++ break; ++ case USB_REQ_GET_CONFIGURATION: ++ if (ctrl->bRequestType != USB_DIR_IN) ++ goto unknown; ++ if (cdev->config) ++ *(u8 *)req->buf = cdev->config->bConfigurationValue; ++ else ++ *(u8 *)req->buf = 0; ++ value = min(w_length, (u16) 1); ++ break; ++ ++ /* function drivers must handle get/set altsetting; if there's ++ * no get() method, we know only altsetting zero works. ++ */ ++ case USB_REQ_SET_INTERFACE: ++ if (ctrl->bRequestType != USB_RECIP_INTERFACE) ++ goto unknown; ++ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) ++ break; ++ f = cdev->config->interface[w_index]; ++ if (!f) ++ break; ++ if (w_value && !f->get_alt) ++ break; ++ value = f->set_alt(f, w_index, w_value); ++ break; ++ case USB_REQ_GET_INTERFACE: ++ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) ++ goto unknown; ++ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) ++ break; ++ f = cdev->config->interface[w_index]; ++ if (!f) ++ break; ++ /* lots of interfaces only need altsetting zero... */ ++ value = f->get_alt ? f->get_alt(f, w_index) : 0; ++ if (value < 0) ++ break; ++ *((u8 *)req->buf) = value; ++ value = min(w_length, (u16) 1); ++ break; ++ default: ++unknown: ++ VDBG(cdev, ++ "non-core control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ ++ /* functions always handle their interfaces ... punt other ++ * recipients (endpoint, other, WUSB, ...) to the current ++ * configuration code. ++ * ++ * REVISIT it could make sense to let the composite device ++ * take such requests too, if that's ever needed: to work ++ * in config 0, etc. ++ */ ++ if ((ctrl->bRequestType & USB_RECIP_MASK) ++ == USB_RECIP_INTERFACE) { ++ f = cdev->config->interface[w_index]; ++ if (f && f->setup) ++ value = f->setup(f, ctrl); ++ else ++ f = NULL; ++ } ++ if (value < 0 && !f) { ++ struct usb_configuration *c; ++ ++ c = cdev->config; ++ if (c && c->setup) ++ value = c->setup(c, ctrl); ++ } ++ ++ goto done; ++ } ++ ++ /* respond with data transfer before status phase? */ ++ if (value >= 0) { ++ req->length = value; ++ req->zero = value < w_length; ++ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) { ++ DBG(cdev, "ep_queue --> %d\n", value); ++ req->status = 0; ++ composite_setup_complete(gadget->ep0, req); ++ } ++ } ++ ++done: ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static void composite_disconnect(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ unsigned long flags; ++ ++ /* REVISIT: should we have config and device level ++ * disconnect callbacks? ++ */ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (cdev->config) ++ reset_config(cdev); ++ spin_unlock_irqrestore(&cdev->lock, flags); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void /* __init_or_exit */ ++composite_unbind(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ unsigned long flags; ++ ++ /* composite_disconnect() must already have been called ++ * by the underlying peripheral controller driver! ++ */ ++ WARN_ON(cdev->config); ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ while (!list_empty(&cdev->configs)) { ++ struct usb_configuration *c; ++ ++ c = list_first_entry(&cdev->configs, ++ struct usb_configuration, list); ++ while (!list_empty(&c->functions)) { ++ struct usb_function *f; ++ ++ f = list_first_entry(&c->functions, ++ struct usb_function, list); ++ list_del(&f->list); ++ if (f->unbind) { ++ DBG(cdev, "unbind function '%s'/%p\n", ++ f->name, f); ++ f->unbind(c, f); ++ /* may free memory for "f" */ ++ } ++ } ++ list_del(&c->list); ++ if (c->unbind) { ++ DBG(cdev, "unbind config '%s'/%p\n", c->label, c); ++ c->unbind(c); ++ /* may free memory for "c" */ ++ } ++ } ++ if (composite->unbind) ++ composite->unbind(cdev); ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ ++ if (cdev->req) { ++ kfree(cdev->req->buf); ++ usb_ep_free_request(gadget->ep0, cdev->req); ++ } ++ kfree(cdev); ++ set_gadget_data(gadget, NULL); ++ composite = NULL; ++} ++ ++static void __init ++string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) ++{ ++ struct usb_string *str = tab->strings; ++ ++ for (str = tab->strings; str->s; str++) { ++ if (str->id == id) { ++ str->s = s; ++ return; ++ } ++ } ++} ++ ++static void __init ++string_override(struct usb_gadget_strings **tab, u8 id, const char *s) ++{ ++ while (*tab) { ++ string_override_one(*tab, id, s); ++ tab++; ++ } ++} ++ ++static int __init composite_bind(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev; ++ int status = -ENOMEM; ++ ++ cdev = kzalloc(sizeof *cdev, GFP_KERNEL); ++ if (!cdev) ++ return status; ++ ++ spin_lock_init(&cdev->lock); ++ cdev->gadget = gadget; ++ set_gadget_data(gadget, cdev); ++ INIT_LIST_HEAD(&cdev->configs); ++ ++ /* preallocate control response and buffer */ ++ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); ++ if (!cdev->req) ++ goto fail; ++ cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); ++ if (!cdev->req->buf) ++ goto fail; ++ cdev->req->complete = composite_setup_complete; ++ gadget->ep0->driver_data = cdev; ++ ++ cdev->bufsiz = USB_BUFSIZ; ++ cdev->driver = composite; ++ ++ usb_gadget_set_selfpowered(gadget); ++ ++ /* interface and string IDs start at zero via kzalloc. ++ * we force endpoints to start unassigned; few controller ++ * drivers will zero ep->driver_data. ++ */ ++ usb_ep_autoconfig_reset(cdev->gadget); ++ ++ /* composite gadget needs to assign strings for whole device (like ++ * serial number), register function drivers, potentially update ++ * power state and consumption, etc ++ */ ++ status = composite->bind(cdev); ++ if (status < 0) ++ goto fail; ++ ++ cdev->desc = *composite->dev; ++ cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; ++ ++ /* standardized runtime overrides for device ID data */ ++ if (idVendor) ++ cdev->desc.idVendor = cpu_to_le16(idVendor); ++ if (idProduct) ++ cdev->desc.idProduct = cpu_to_le16(idProduct); ++ if (bcdDevice) ++ cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); ++ ++ /* strings can't be assigned before bind() allocates the ++ * releavnt identifiers ++ */ ++ if (cdev->desc.iManufacturer && iManufacturer) ++ string_override(composite->strings, ++ cdev->desc.iManufacturer, iManufacturer); ++ if (cdev->desc.iProduct && iProduct) ++ string_override(composite->strings, ++ cdev->desc.iProduct, iProduct); ++ if (cdev->desc.iSerialNumber && iSerialNumber) ++ string_override(composite->strings, ++ cdev->desc.iSerialNumber, iSerialNumber); ++ ++ INFO(cdev, "%s ready\n", composite->name); ++ return 0; ++ ++fail: ++ composite_unbind(gadget); ++ return status; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ++composite_suspend(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct usb_function *f; ++ ++ /* REVISIT: should we have config and device level ++ * suspend/resume callbacks? ++ */ ++ DBG(cdev, "suspend\n"); ++ if (cdev->config) { ++ list_for_each_entry(f, &cdev->config->functions, list) { ++ if (f->suspend) ++ f->suspend(f); ++ } ++ } ++} ++ ++static void ++composite_resume(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct usb_function *f; ++ ++ /* REVISIT: should we have config and device level ++ * suspend/resume callbacks? ++ */ ++ DBG(cdev, "resume\n"); ++ if (cdev->config) { ++ list_for_each_entry(f, &cdev->config->functions, list) { ++ if (f->resume) ++ f->resume(f); ++ } ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_gadget_driver composite_driver = { ++ .speed = USB_SPEED_HIGH, ++ ++ .bind = composite_bind, ++ .unbind = __exit_p(composite_unbind), ++ ++ .setup = composite_setup, ++ .disconnect = composite_disconnect, ++ ++ .suspend = composite_suspend, ++ .resume = composite_resume, ++ ++ .driver = { ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/** ++ * usb_composite_register() - register a composite driver ++ * @driver: the driver to register ++ * Context: single threaded during gadget setup ++ * ++ * This function is used to register drivers using the composite driver ++ * framework. The return value is zero, or a negative errno value. ++ * Those values normally come from the driver's @bind method, which does ++ * all the work of setting up the driver to match the hardware. ++ * ++ * On successful return, the gadget is ready to respond to requests from ++ * the host, unless one of its components invokes usb_gadget_disconnect() ++ * while it was binding. That would usually be done in order to wait for ++ * some userspace participation. ++ */ ++int __init usb_composite_register(struct usb_composite_driver *driver) ++{ ++ if (!driver || !driver->dev || !driver->bind || composite) ++ return -EINVAL; ++ ++ if (!driver->name) ++ driver->name = "composite"; ++ composite_driver.function = (char *) driver->name; ++ composite_driver.driver.name = driver->name; ++ composite = driver; ++ ++ return usb_gadget_register_driver(&composite_driver); ++} ++ ++/** ++ * usb_composite_unregister() - unregister a composite driver ++ * @driver: the driver to unregister ++ * ++ * This function is used to unregister drivers using the composite ++ * driver framework. ++ */ ++void __exit usb_composite_unregister(struct usb_composite_driver *driver) ++{ ++ if (composite != driver) ++ return; ++ usb_gadget_unregister_driver(&composite_driver); ++} +--- /dev/null ++++ b/include/linux/usb/composite.h +@@ -0,0 +1,338 @@ ++/* ++ * composite.h -- framework for usb gadgets which are composite devices ++ * ++ * Copyright (C) 2006-2008 David Brownell ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef __LINUX_USB_COMPOSITE_H ++#define __LINUX_USB_COMPOSITE_H ++ ++/* ++ * This framework is an optional layer on top of the USB Gadget interface, ++ * making it easier to build (a) Composite devices, supporting multiple ++ * functions within any single configuration, and (b) Multi-configuration ++ * devices, also supporting multiple functions but without necessarily ++ * having more than one function per configuration. ++ * ++ * Example: a device with a single configuration supporting both network ++ * link and mass storage functions is a composite device. Those functions ++ * might alternatively be packaged in individual configurations, but in ++ * the composite model the host can use both functions at the same time. ++ */ ++ ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++ ++ ++struct usb_configuration; ++ ++/** ++ * struct usb_function - describes one function of a configuration ++ * @name: For diagnostics, identifies the function. ++ * @strings: tables of strings, keyed by identifiers assigned during bind() ++ * and by language IDs provided in control requests ++ * @descriptors: Table of full (or low) speed descriptors, using interface and ++ * string identifiers assigned during @bind(). If this pointer is null, ++ * the function will not be available at full speed (or at low speed). ++ * @hs_descriptors: Table of high speed descriptors, using interface and ++ * string identifiers assigned during @bind(). If this pointer is null, ++ * the function will not be available at high speed. ++ * @config: assigned when @usb_add_function() is called; this is the ++ * configuration with which this function is associated. ++ * @bind: Before the gadget can register, all of its functions bind() to the ++ * available resources including string and interface identifiers used ++ * in interface or class descriptors; endpoints; I/O buffers; and so on. ++ * @unbind: Reverses @bind; called as a side effect of unregistering the ++ * driver which added this function. ++ * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may ++ * initialize usb_ep.driver data at this time (when it is used). ++ * Note that setting an interface to its current altsetting resets ++ * interface state, and that all interfaces have a disabled state. ++ * @get_alt: Returns the active altsetting. If this is not provided, ++ * then only altsetting zero is supported. ++ * @disable: (REQUIRED) Indicates the function should be disabled. Reasons ++ * include host resetting or reconfiguring the gadget, and disconnection. ++ * @setup: Used for interface-specific control requests. ++ * @suspend: Notifies functions when the host stops sending USB traffic. ++ * @resume: Notifies functions when the host restarts USB traffic. ++ * ++ * A single USB function uses one or more interfaces, and should in most ++ * cases support operation at both full and high speeds. Each function is ++ * associated by @usb_add_function() with a one configuration; that function ++ * causes @bind() to be called so resources can be allocated as part of ++ * setting up a gadget driver. Those resources include endpoints, which ++ * should be allocated using @usb_ep_autoconfig(). ++ * ++ * To support dual speed operation, a function driver provides descriptors ++ * for both high and full speed operation. Except in rare cases that don't ++ * involve bulk endpoints, each speed needs different endpoint descriptors. ++ * ++ * Function drivers choose their own strategies for managing instance data. ++ * The simplest strategy just declares it "static', which means the function ++ * can only be activated once. If the function needs to be exposed in more ++ * than one configuration at a given speed, it needs to support multiple ++ * usb_function structures (one for each configuration). ++ * ++ * A more complex strategy might encapsulate a @usb_function structure inside ++ * a driver-specific instance structure to allows multiple activations. An ++ * example of multiple activations might be a CDC ACM function that supports ++ * two or more distinct instances within the same configuration, providing ++ * several independent logical data links to a USB host. ++ */ ++struct usb_function { ++ const char *name; ++ struct usb_gadget_strings **strings; ++ struct usb_descriptor_header **descriptors; ++ struct usb_descriptor_header **hs_descriptors; ++ ++ struct usb_configuration *config; ++ ++ /* REVISIT: bind() functions can be marked __init, which ++ * makes trouble for section mismatch analysis. See if ++ * we can't restructure things to avoid mismatching. ++ * Related: unbind() may kfree() but bind() won't... ++ */ ++ ++ /* configuration management: bind/unbind */ ++ int (*bind)(struct usb_configuration *, ++ struct usb_function *); ++ void (*unbind)(struct usb_configuration *, ++ struct usb_function *); ++ ++ /* runtime state management */ ++ int (*set_alt)(struct usb_function *, ++ unsigned interface, unsigned alt); ++ int (*get_alt)(struct usb_function *, ++ unsigned interface); ++ void (*disable)(struct usb_function *); ++ int (*setup)(struct usb_function *, ++ const struct usb_ctrlrequest *); ++ void (*suspend)(struct usb_function *); ++ void (*resume)(struct usb_function *); ++ ++ /* internals */ ++ struct list_head list; ++}; ++ ++int usb_add_function(struct usb_configuration *, struct usb_function *); ++ ++int usb_interface_id(struct usb_configuration *, struct usb_function *); ++ ++/** ++ * ep_choose - select descriptor endpoint at current device speed ++ * @g: gadget, connected and running at some speed ++ * @hs: descriptor to use for high speed operation ++ * @fs: descriptor to use for full or low speed operation ++ */ ++static inline struct usb_endpoint_descriptor * ++ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, ++ struct usb_endpoint_descriptor *fs) ++{ ++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) ++ return hs; ++ return fs; ++} ++ ++#define MAX_CONFIG_INTERFACES 8 /* arbitrary; max 255 */ ++ ++/** ++ * struct usb_configuration - represents one gadget configuration ++ * @label: For diagnostics, describes the configuration. ++ * @strings: Tables of strings, keyed by identifiers assigned during @bind() ++ * and by language IDs provided in control requests. ++ * @descriptors: Table of descriptors preceding all function descriptors. ++ * Examples include OTG and vendor-specific descriptors. ++ * @bind: Called from @usb_add_config() to allocate resources unique to this ++ * configuration and to call @usb_add_function() for each function used. ++ * @unbind: Reverses @bind; called as a side effect of unregistering the ++ * driver which added this configuration. ++ * @setup: Used to delegate control requests that aren't handled by standard ++ * device infrastructure or directed at a specific interface. ++ * @bConfigurationValue: Copied into configuration descriptor. ++ * @iConfiguration: Copied into configuration descriptor. ++ * @bmAttributes: Copied into configuration descriptor. ++ * @bMaxPower: Copied into configuration descriptor. ++ * @cdev: assigned by @usb_add_config() before calling @bind(); this is ++ * the device associated with this configuration. ++ * ++ * Configurations are building blocks for gadget drivers structured around ++ * function drivers. Simple USB gadgets require only one function and one ++ * configuration, and handle dual-speed hardware by always providing the same ++ * functionality. Slightly more complex gadgets may have more than one ++ * single-function configuration at a given speed; or have configurations ++ * that only work at one speed. ++ * ++ * Composite devices are, by definition, ones with configurations which ++ * include more than one function. ++ * ++ * The lifecycle of a usb_configuration includes allocation, initialization ++ * of the fields described above, and calling @usb_add_config() to set up ++ * internal data and bind it to a specific device. The configuration's ++ * @bind() method is then used to initialize all the functions and then ++ * call @usb_add_function() for them. ++ * ++ * Those functions would normally be independant of each other, but that's ++ * not mandatory. CDC WMC devices are an example where functions often ++ * depend on other functions, with some functions subsidiary to others. ++ * Such interdependency may be managed in any way, so long as all of the ++ * descriptors complete by the time the composite driver returns from ++ * its bind() routine. ++ */ ++struct usb_configuration { ++ const char *label; ++ struct usb_gadget_strings **strings; ++ const struct usb_descriptor_header **descriptors; ++ ++ /* REVISIT: bind() functions can be marked __init, which ++ * makes trouble for section mismatch analysis. See if ++ * we can't restructure things to avoid mismatching... ++ */ ++ ++ /* configuration management: bind/unbind */ ++ int (*bind)(struct usb_configuration *); ++ void (*unbind)(struct usb_configuration *); ++ int (*setup)(struct usb_configuration *, ++ const struct usb_ctrlrequest *); ++ ++ /* fields in the config descriptor */ ++ u8 bConfigurationValue; ++ u8 iConfiguration; ++ u8 bmAttributes; ++ u8 bMaxPower; ++ ++ struct usb_composite_dev *cdev; ++ ++ /* internals */ ++ struct list_head list; ++ struct list_head functions; ++ u8 next_interface_id; ++ unsigned highspeed:1; ++ unsigned fullspeed:1; ++ struct usb_function *interface[MAX_CONFIG_INTERFACES]; ++}; ++ ++int usb_add_config(struct usb_composite_dev *, ++ struct usb_configuration *); ++ ++/** ++ * struct usb_composite_driver - groups configurations into a gadget ++ * @name: For diagnostics, identifies the driver. ++ * @dev: Template descriptor for the device, including default device ++ * identifiers. ++ * @strings: tables of strings, keyed by identifiers assigned during bind() ++ * and language IDs provided in control requests ++ * @bind: (REQUIRED) Used to allocate resources that are shared across the ++ * whole device, such as string IDs, and add its configurations using ++ * @usb_add_config(). This may fail by returning a negative errno ++ * value; it should return zero on successful initialization. ++ * @unbind: Reverses @bind(); called as a side effect of unregistering ++ * this driver. ++ * ++ * Devices default to reporting self powered operation. Devices which rely ++ * on bus powered operation should report this in their @bind() method. ++ * ++ * Before returning from @bind, various fields in the template descriptor ++ * may be overridden. These include the idVendor/idProduct/bcdDevice values ++ * normally to bind the appropriate host side driver, and the three strings ++ * (iManufacturer, iProduct, iSerialNumber) normally used to provide user ++ * meaningful device identifiers. (The strings will not be defined unless ++ * they are defined in @dev and @strings.) The correct ep0 maxpacket size ++ * is also reported, as defined by the underlying controller driver. ++ */ ++struct usb_composite_driver { ++ const char *name; ++ const struct usb_device_descriptor *dev; ++ struct usb_gadget_strings **strings; ++ ++ /* REVISIT: bind() functions can be marked __init, which ++ * makes trouble for section mismatch analysis. See if ++ * we can't restructure things to avoid mismatching... ++ */ ++ ++ int (*bind)(struct usb_composite_dev *); ++ int (*unbind)(struct usb_composite_dev *); ++}; ++ ++extern int usb_composite_register(struct usb_composite_driver *); ++extern void usb_composite_unregister(struct usb_composite_driver *); ++ ++ ++/** ++ * struct usb_composite_device - represents one composite usb gadget ++ * @gadget: read-only, abstracts the gadget's usb peripheral controller ++ * @req: used for control responses; buffer is pre-allocated ++ * @bufsiz: size of buffer pre-allocated in @req ++ * @config: the currently active configuration ++ * ++ * One of these devices is allocated and initialized before the ++ * associated device driver's bind() is called. ++ * ++ * OPEN ISSUE: it appears that some WUSB devices will need to be ++ * built by combining a normal (wired) gadget with a wireless one. ++ * This revision of the gadget framework should probably try to make ++ * sure doing that won't hurt too much. ++ * ++ * One notion for how to handle Wireless USB devices involves: ++ * (a) a second gadget here, discovery mechanism TBD, but likely ++ * needing separate "register/unregister WUSB gadget" calls; ++ * (b) updates to usb_gadget to include flags "is it wireless", ++ * "is it wired", plus (presumably in a wrapper structure) ++ * bandgroup and PHY info; ++ * (c) presumably a wireless_ep wrapping a usb_ep, and reporting ++ * wireless-specific parameters like maxburst and maxsequence; ++ * (d) configurations that are specific to wireless links; ++ * (e) function drivers that understand wireless configs and will ++ * support wireless for (additional) function instances; ++ * (f) a function to support association setup (like CBAF), not ++ * necessarily requiring a wireless adapter; ++ * (g) composite device setup that can create one or more wireless ++ * configs, including appropriate association setup support; ++ * (h) more, TBD. ++ */ ++struct usb_composite_dev { ++ struct usb_gadget *gadget; ++ struct usb_request *req; ++ unsigned bufsiz; ++ ++ struct usb_configuration *config; ++ ++ /* internals */ ++ struct usb_device_descriptor desc; ++ struct list_head configs; ++ struct usb_composite_driver *driver; ++ u8 next_string_id; ++ ++ spinlock_t lock; ++ ++ /* REVISIT use and existence of lock ... */ ++}; ++ ++extern int usb_string_id(struct usb_composite_dev *c); ++ ++/* messaging utils */ ++#define DBG(d, fmt, args...) \ ++ dev_dbg(&(d)->gadget->dev , fmt , ## args) ++#define VDBG(d, fmt, args...) \ ++ dev_vdbg(&(d)->gadget->dev , fmt , ## args) ++#define ERROR(d, fmt, args...) \ ++ dev_err(&(d)->gadget->dev , fmt , ## args) ++#define WARN(d, fmt, args...) \ ++ dev_warn(&(d)->gadget->dev , fmt , ## args) ++#define INFO(d, fmt, args...) \ ++ dev_info(&(d)->gadget->dev , fmt , ## args) ++ ++#endif /* __LINUX_USB_COMPOSITE_H */ diff --git a/usb/usb-gadget-push-bkl-down-into-drivers.patch b/usb/usb-gadget-push-bkl-down-into-drivers.patch new file mode 100644 index 00000000000000..3b29204bd98237 --- /dev/null +++ b/usb/usb-gadget-push-bkl-down-into-drivers.patch @@ -0,0 +1,113 @@ +From alan@lxorguk.ukuu.org.uk Fri Jun 6 15:13:46 2008 +From: Alan Cox <alan@lxorguk.ukuu.org.uk> +Date: Thu, 22 May 2008 22:03:27 +0100 +Subject: USB: gadget: Push BKL down into drivers +To: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org +Message-ID: <20080522220327.38e2347d@core> + + +This keeps the gadget ioctl method wrapped but pushes the BKL down into +the gadget code so we can use unlocked_ioctl(). + +Signed-off-by: Alan Cox <alan@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/inode.c | 23 +++++++++++++---------- + drivers/usb/gadget/printer.c | 7 +++---- + 2 files changed, 16 insertions(+), 14 deletions(-) + +--- a/drivers/usb/gadget/inode.c ++++ b/drivers/usb/gadget/inode.c +@@ -32,6 +32,7 @@ + #include <asm/uaccess.h> + #include <linux/slab.h> + #include <linux/poll.h> ++#include <linux/smp_lock.h> + + #include <linux/device.h> + #include <linux/moduleparam.h> +@@ -483,8 +484,7 @@ ep_release (struct inode *inode, struct + return 0; + } + +-static int ep_ioctl (struct inode *inode, struct file *fd, +- unsigned code, unsigned long value) ++static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) + { + struct ep_data *data = fd->private_data; + int status; +@@ -740,7 +740,7 @@ static const struct file_operations ep_i + + .read = ep_read, + .write = ep_write, +- .ioctl = ep_ioctl, ++ .unlocked_ioctl = ep_ioctl, + .release = ep_release, + + .aio_read = ep_aio_read, +@@ -1294,15 +1294,18 @@ out: + return mask; + } + +-static int dev_ioctl (struct inode *inode, struct file *fd, +- unsigned code, unsigned long value) ++static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) + { + struct dev_data *dev = fd->private_data; + struct usb_gadget *gadget = dev->gadget; ++ long ret = -ENOTTY; + +- if (gadget->ops->ioctl) +- return gadget->ops->ioctl (gadget, code, value); +- return -ENOTTY; ++ if (gadget->ops->ioctl) { ++ lock_kernel(); ++ ret = gadget->ops->ioctl (gadget, code, value); ++ unlock_kernel(); ++ } ++ return ret; + } + + /* used after device configuration */ +@@ -1314,7 +1317,7 @@ static const struct file_operations ep0_ + .write = ep0_write, + .fasync = ep0_fasync, + .poll = ep0_poll, +- .ioctl = dev_ioctl, ++ .unlocked_ioctl = dev_ioctl, + .release = dev_release, + }; + +@@ -1964,7 +1967,7 @@ static const struct file_operations dev_ + .open = dev_open, + .write = dev_config, + .fasync = ep0_fasync, +- .ioctl = dev_ioctl, ++ .unlocked_ioctl = dev_ioctl, + .release = dev_release, + }; + +--- a/drivers/usb/gadget/printer.c ++++ b/drivers/usb/gadget/printer.c +@@ -827,9 +827,8 @@ printer_poll(struct file *fd, poll_table + return status; + } + +-static int +-printer_ioctl(struct inode *inode, struct file *fd, unsigned int code, +- unsigned long arg) ++static long ++printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) + { + struct printer_dev *dev = fd->private_data; + unsigned long flags; +@@ -868,7 +867,7 @@ static struct file_operations printer_io + .write = printer_write, + .fsync = printer_fsync, + .poll = printer_poll, +- .ioctl = printer_ioctl, ++ .unlocked_ioctl = printer_ioctl, + .release = printer_close + }; + diff --git a/usb/usb-gadget-support-descriptor-copying.patch b/usb/usb-gadget-support-descriptor-copying.patch new file mode 100644 index 00000000000000..996eac0c45d9ec --- /dev/null +++ b/usb/usb-gadget-support-descriptor-copying.patch @@ -0,0 +1,171 @@ +From david-b@pacbell.net Fri Jun 6 15:10:05 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 20 May 2008 11:02:47 -0700 +Subject: USB: gadget support descriptor copying +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> +Message-ID: <200805201102.47771.david-b@pacbell.net> +Content-Disposition: inline + + +Define three new descriptor manipulation utilities, for use when +setting up functions that may have multiple instances: + + usb_copy_descriptors() to copy a vector of descriptors + usb_free_descriptors() to free the copy + usb_find_endpoint() to find a copied version + +These will be used as follows. Functions will continue to have static +tables of descriptors they update, now used as __initdata templates. + +When a function creates a new instance, it patches those tables with +relevant interface and string IDs, plus endpoint assignments. Then it +makes a copy of those morphed descriptors and associates that with the +new function instance, and records the endpoint descriptors to use when +activating the endpoints. When initialization is done, only the copies +remain in memory. The copies are freed on driver removal. + +This ensures that each instances has descriptors which hold the right +instance-specific data. Two instances in the same configuration will +obviously never share the same interface IDs or use the same endpoints, +and instances in different configuration mostly wouldn't do so either. + +This also includes a bugfix to the epautoconf code that shows up with +this usage model. It must replace the previous endpoint number when +it updates descriptors, not just mask in a few more bits. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/config.c | 75 +++++++++++++++++++++++++++++++++++++++- + drivers/usb/gadget/epautoconf.c | 1 + include/linux/usb/gadget.h | 19 ++++++++++ + 3 files changed, 94 insertions(+), 1 deletion(-) + +--- a/drivers/usb/gadget/config.c ++++ b/drivers/usb/gadget/config.c +@@ -96,7 +96,7 @@ int usb_gadget_config_buf( + /* config descriptor first */ + if (length < USB_DT_CONFIG_SIZE || !desc) + return -EINVAL; +- *cp = *config; ++ *cp = *config; + + /* then interface/endpoint/class/vendor/... */ + len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, +@@ -115,3 +115,76 @@ int usb_gadget_config_buf( + return len; + } + ++/** ++ * usb_copy_descriptors - copy a vector of USB descriptors ++ * @src: null-terminated vector to copy ++ * Context: initialization code, which may sleep ++ * ++ * This makes a copy of a vector of USB descriptors. Its primary use ++ * is to support usb_function objects which can have multiple copies, ++ * each needing different descriptors. Functions may have static ++ * tables of descriptors, which are used as templates and customized ++ * with identifiers (for interfaces, strings, endpoints, and more) ++ * as needed by a given function instance. ++ */ ++struct usb_descriptor_header **__init ++usb_copy_descriptors(struct usb_descriptor_header **src) ++{ ++ struct usb_descriptor_header **tmp; ++ unsigned bytes; ++ unsigned n_desc; ++ void *mem; ++ struct usb_descriptor_header **ret; ++ ++ /* count descriptors and their sizes; then add vector size */ ++ for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) ++ bytes += (*tmp)->bLength; ++ bytes += (n_desc + 1) * sizeof(*tmp); ++ ++ mem = kmalloc(bytes, GFP_KERNEL); ++ if (!mem) ++ return NULL; ++ ++ /* fill in pointers starting at "tmp", ++ * to descriptors copied starting at "mem"; ++ * and return "ret" ++ */ ++ ret = tmp = mem; ++ mem += (n_desc + 1) * sizeof(*tmp); ++ while (*src) { ++ memcpy(mem, *src, (*src)->bLength); ++ *tmp = mem; ++ tmp++; ++ mem += (*src)->bLength; ++ src++; ++ } ++ *tmp = NULL; ++ ++ return ret; ++} ++ ++/** ++ * usb_find_endpoint - find a copy of an endpoint descriptor ++ * @src: original vector of descriptors ++ * @copy: copy of @src ++ * @ep: endpoint descriptor found in @src ++ * ++ * This returns the copy of the @match descriptor made for @copy. Its ++ * intended use is to help remembering the endpoint descriptor to use ++ * when enabling a given endpoint. ++ */ ++struct usb_endpoint_descriptor *__init ++usb_find_endpoint( ++ struct usb_descriptor_header **src, ++ struct usb_descriptor_header **copy, ++ struct usb_endpoint_descriptor *match ++) ++{ ++ while (*src) { ++ if (*src == (void *) match) ++ return (void *)*copy; ++ src++; ++ copy++; ++ } ++ return NULL; ++} +--- a/drivers/usb/gadget/epautoconf.c ++++ b/drivers/usb/gadget/epautoconf.c +@@ -159,6 +159,7 @@ ep_matches ( + /* MATCH!! */ + + /* report address */ ++ desc->bEndpointAddress &= USB_DIR_IN; + if (isdigit (ep->name [2])) { + u8 num = simple_strtol (&ep->name [2], NULL, 10); + desc->bEndpointAddress |= num; +--- a/include/linux/usb/gadget.h ++++ b/include/linux/usb/gadget.h +@@ -858,6 +858,25 @@ int usb_descriptor_fillbuf(void *, unsig + int usb_gadget_config_buf(const struct usb_config_descriptor *config, + void *buf, unsigned buflen, const struct usb_descriptor_header **desc); + ++/* copy a NULL-terminated vector of descriptors */ ++struct usb_descriptor_header **usb_copy_descriptors( ++ struct usb_descriptor_header **); ++ ++/* return copy of endpoint descriptor given original descriptor set */ ++struct usb_endpoint_descriptor *usb_find_endpoint( ++ struct usb_descriptor_header **src, ++ struct usb_descriptor_header **copy, ++ struct usb_endpoint_descriptor *match); ++ ++/** ++ * usb_free_descriptors - free descriptors returned by usb_copy_descriptors() ++ * @v: vector of descriptors ++ */ ++static inline void usb_free_descriptors(struct usb_descriptor_header **v) ++{ ++ kfree(v); ++} ++ + /*-------------------------------------------------------------------------*/ + + /* utility wrapping a simple endpoint selection policy */ diff --git a/usb/usb-gadget-zero-loopback-function-driver.patch b/usb/usb-gadget-zero-loopback-function-driver.patch new file mode 100644 index 00000000000000..ee2af7a0a1d4a4 --- /dev/null +++ b/usb/usb-gadget-zero-loopback-function-driver.patch @@ -0,0 +1,426 @@ +From david-b@pacbell.net Fri Jun 6 15:10:56 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 20 May 2008 11:07:08 -0700 +Subject: usb gadget zero: loopback function driver +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> +Message-ID: <200805201107.08935.david-b@pacbell.net> +Content-Disposition: inline + + +This splits the gadget zero "loopback" configuration into a standalone +"configuration driver", building on the composite gadget framework code. +It doesn't yet pull the original code out of gadget zero or update how +that driver is built. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/f_loopback.c | 383 ++++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/g_zero.h | 2 + 2 files changed, 385 insertions(+) + +--- /dev/null ++++ b/drivers/usb/gadget/f_loopback.c +@@ -0,0 +1,383 @@ ++/* ++ * f_loopback.c - USB peripheral loopback configuration driver ++ * ++ * Copyright (C) 2003-2008 David Brownell ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/device.h> ++ ++#include "g_zero.h" ++#include "gadget_chips.h" ++ ++ ++/* ++ * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, ++ * ++ * This takes messages of various sizes written OUT to a device, and loops ++ * them back so they can be read IN from it. It has been used by certain ++ * test applications. It supports limited testing of data queueing logic. ++ * ++ * ++ * This is currently packaged as a configuration driver, which can't be ++ * combined with other functions to make composite devices. However, it ++ * can be combined with other independent configurations. ++ */ ++struct f_loopback { ++ struct usb_function function; ++ ++ struct usb_ep *in_ep; ++ struct usb_ep *out_ep; ++}; ++ ++static inline struct f_loopback *func_to_loop(struct usb_function *f) ++{ ++ return container_of(f, struct f_loopback, function); ++} ++ ++static unsigned qlen = 32; ++module_param(qlen, uint, 0); ++MODULE_PARM_DESC(qlenn, "depth of loopback queue"); ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_interface_descriptor loopback_intf = { ++ .bLength = sizeof loopback_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_source_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_sink_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_loopback_descs[] = { ++ (struct usb_descriptor_header *) &loopback_intf, ++ (struct usb_descriptor_header *) &fs_sink_desc, ++ (struct usb_descriptor_header *) &fs_source_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_source_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_sink_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *hs_loopback_descs[] = { ++ (struct usb_descriptor_header *) &loopback_intf, ++ (struct usb_descriptor_header *) &hs_source_desc, ++ (struct usb_descriptor_header *) &hs_sink_desc, ++ NULL, ++}; ++ ++/* function-specific strings: */ ++ ++static struct usb_string strings_loopback[] = { ++ [0].s = "loop input to output", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_loop = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_loopback, ++}; ++ ++static struct usb_gadget_strings *loopback_strings[] = { ++ &stringtab_loop, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init ++loopback_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_loopback *loop = func_to_loop(f); ++ int id; ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ loopback_intf.bInterfaceNumber = id; ++ ++ /* allocate endpoints */ ++ ++ loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); ++ if (!loop->in_ep) { ++autoconf_fail: ++ ERROR(cdev, "%s: can't autoconfigure on %s\n", ++ f->name, cdev->gadget->name); ++ return -ENODEV; ++ } ++ loop->in_ep->driver_data = cdev; /* claim */ ++ ++ loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); ++ if (!loop->out_ep) ++ goto autoconf_fail; ++ loop->out_ep->driver_data = cdev; /* claim */ ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_source_desc.bEndpointAddress = ++ fs_source_desc.bEndpointAddress; ++ hs_sink_desc.bEndpointAddress = ++ fs_sink_desc.bEndpointAddress; ++ f->hs_descriptors = hs_loopback_descs; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, loop->in_ep->name, loop->out_ep->name); ++ return 0; ++} ++ ++static void ++loopback_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ kfree(func_to_loop(f)); ++} ++ ++static void loopback_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_loopback *loop = ep->driver_data; ++ struct usb_composite_dev *cdev = loop->function.config->cdev; ++ int status = req->status; ++ ++ switch (status) { ++ ++ case 0: /* normal completion? */ ++ if (ep == loop->out_ep) { ++ /* loop this OUT packet back IN to the host */ ++ req->zero = (req->actual < req->length); ++ req->length = req->actual; ++ status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC); ++ if (status == 0) ++ return; ++ ++ /* "should never get here" */ ++ ERROR(cdev, "can't loop %s to %s: %d\n", ++ ep->name, loop->in_ep->name, ++ status); ++ } ++ ++ /* queue the buffer for some later OUT packet */ ++ req->length = buflen; ++ status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); ++ if (status == 0) ++ return; ++ ++ /* "should never get here" */ ++ /* FALLTHROUGH */ ++ ++ default: ++ ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, ++ status, req->actual, req->length); ++ /* FALLTHROUGH */ ++ ++ /* NOTE: since this driver doesn't maintain an explicit record ++ * of requests it submitted (just maintains qlen count), we ++ * rely on the hardware driver to clean up on disconnect or ++ * endpoint disable. ++ */ ++ case -ECONNABORTED: /* hardware forced ep reset */ ++ case -ECONNRESET: /* request dequeued */ ++ case -ESHUTDOWN: /* disconnect from host */ ++ free_ep_req(ep, req); ++ return; ++ } ++} ++ ++static void disable_loopback(struct f_loopback *loop) ++{ ++ struct usb_composite_dev *cdev; ++ ++ cdev = loop->function.config->cdev; ++ disable_endpoints(cdev, loop->in_ep, loop->out_ep); ++ VDBG(cdev, "%s disabled\n", loop->function.name); ++} ++ ++static int ++enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) ++{ ++ int result = 0; ++ const struct usb_endpoint_descriptor *src, *sink; ++ struct usb_ep *ep; ++ struct usb_request *req; ++ unsigned i; ++ ++ src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); ++ sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); ++ ++ /* one endpoint writes data back IN to the host */ ++ ep = loop->in_ep; ++ result = usb_ep_enable(ep, src); ++ if (result < 0) ++ return result; ++ ep->driver_data = loop; ++ ++ /* one endpoint just reads OUT packets */ ++ ep = loop->out_ep; ++ result = usb_ep_enable(ep, sink); ++ if (result < 0) { ++fail0: ++ ep = loop->in_ep; ++ usb_ep_disable(ep); ++ ep->driver_data = NULL; ++ return result; ++ } ++ ep->driver_data = loop; ++ ++ /* allocate a bunch of read buffers and queue them all at once. ++ * we buffer at most 'qlen' transfers; fewer if any need more ++ * than 'buflen' bytes each. ++ */ ++ for (i = 0; i < qlen && result == 0; i++) { ++ req = alloc_ep_req(ep); ++ if (req) { ++ req->complete = loopback_complete; ++ result = usb_ep_queue(ep, req, GFP_ATOMIC); ++ if (result) ++ ERROR(cdev, "%s queue req --> %d\n", ++ ep->name, result); ++ } else { ++ usb_ep_disable(ep); ++ ep->driver_data = NULL; ++ result = -ENOMEM; ++ goto fail0; ++ } ++ } ++ ++ DBG(cdev, "%s enabled\n", loop->function.name); ++ return result; ++} ++ ++static int loopback_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct f_loopback *loop = func_to_loop(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt is zero */ ++ if (loop->in_ep->driver_data) ++ disable_loopback(loop); ++ return enable_loopback(cdev, loop); ++} ++ ++static void loopback_disable(struct usb_function *f) ++{ ++ struct f_loopback *loop = func_to_loop(f); ++ ++ disable_loopback(loop); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init loopback_bind_config(struct usb_configuration *c) ++{ ++ struct f_loopback *loop; ++ int status; ++ ++ loop = kzalloc(sizeof *loop, GFP_KERNEL); ++ if (!loop) ++ return -ENOMEM; ++ ++ loop->function.name = "loopback"; ++ loop->function.descriptors = fs_loopback_descs; ++ loop->function.bind = loopback_bind; ++ loop->function.unbind = loopback_unbind; ++ loop->function.set_alt = loopback_set_alt; ++ loop->function.disable = loopback_disable; ++ ++ status = usb_add_function(c, &loop->function); ++ if (status) ++ kfree(loop); ++ return status; ++} ++ ++static struct usb_configuration loopback_driver = { ++ .label = "loopback", ++ .strings = loopback_strings, ++ .bind = loopback_bind_config, ++ .bConfigurationValue = 2, ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++ /* .iConfiguration = DYNAMIC */ ++}; ++ ++/** ++ * loopback_add - add a loopback testing configuration to a device ++ * @cdev: the device to support the loopback configuration ++ */ ++int __init loopback_add(struct usb_composite_dev *cdev) ++{ ++ int id; ++ ++ /* allocate string ID(s) */ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_loopback[0].id = id; ++ ++ loopback_intf.iInterface = id; ++ loopback_driver.iConfiguration = id; ++ ++ /* FIXME pass in bConfigurationValue and OTG stuff */ ++ ++ /* support OTG systems */ ++ if (gadget_is_otg(cdev->gadget)) { ++ loopback_driver.descriptors = otg_desc; ++ loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ return usb_add_config(cdev, &loopback_driver); ++} +--- a/drivers/usb/gadget/g_zero.h ++++ b/drivers/usb/gadget/g_zero.h +@@ -9,6 +9,7 @@ + #include <linux/usb/composite.h> + + /* global state */ ++extern unsigned buflen; + extern const struct usb_descriptor_header **otg_desc; + + /* common utilities */ +@@ -19,5 +20,6 @@ void disable_endpoints(struct usb_compos + + /* configuration-specific linkup */ + int sourcesink_add(struct usb_composite_dev *cdev); ++int loopback_add(struct usb_composite_dev *cdev); + + #endif /* __G_ZERO_H */ diff --git a/usb/usb-gadget-zero-sourcesink-config-driver.patch b/usb/usb-gadget-zero-sourcesink-config-driver.patch new file mode 100644 index 00000000000000..970deb13e6df92 --- /dev/null +++ b/usb/usb-gadget-zero-sourcesink-config-driver.patch @@ -0,0 +1,645 @@ +From david-b@pacbell.net Fri Jun 6 15:10:42 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 20 May 2008 11:06:24 -0700 +Subject: usb gadget zero: sourcesink config driver +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> +Message-ID: <200805201106.24630.david-b@pacbell.net> +Content-Disposition: inline + + +This splits the gadget zero "source/sink" configuration into a standalone +"configuration driver", building on the composite gadget framework code. +It doesn't yet pull the original code out of gadget zero or update how +that driver is built. + +Neither this, nor its sibling "loopback" configuration, is a function +driver that can be combined with other functions. However the code does +become simpler because of this conversion, so it's a net win. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/f_sourcesink.c | 589 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/g_zero.h | 23 + + 2 files changed, 612 insertions(+) + +--- /dev/null ++++ b/drivers/usb/gadget/f_sourcesink.c +@@ -0,0 +1,589 @@ ++/* ++ * f_sourcesink.c - USB peripheral source/sink configuration driver ++ * ++ * Copyright (C) 2003-2008 David Brownell ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/device.h> ++ ++#include "g_zero.h" ++#include "gadget_chips.h" ++ ++ ++/* ++ * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral ++ * controller drivers. ++ * ++ * This just sinks bulk packets OUT to the peripheral and sources them IN ++ * to the host, optionally with specific data patterns for integrity tests. ++ * As such it supports basic functionality and load tests. ++ * ++ * In terms of control messaging, this supports all the standard requests ++ * plus two that support control-OUT tests. If the optional "autoresume" ++ * mode is enabled, it provides good functional coverage for the "USBCV" ++ * test harness from USB-IF. ++ * ++ * Note that because this doesn't queue more than one request at a time, ++ * some other function must be used to test queueing logic. The network ++ * link (g_ether) is the best overall option for that, since its TX and RX ++ * queues are relatively independent, will receive a range of packet sizes, ++ * and can often be made to run out completely. Those issues are important ++ * when stress testing peripheral controller drivers. ++ * ++ * ++ * This is currently packaged as a configuration driver, which can't be ++ * combined with other functions to make composite devices. However, it ++ * can be combined with other independent configurations. ++ */ ++struct f_sourcesink { ++ struct usb_function function; ++ ++ struct usb_ep *in_ep; ++ struct usb_ep *out_ep; ++ struct timer_list resume; ++}; ++ ++static inline struct f_sourcesink *func_to_ss(struct usb_function *f) ++{ ++ return container_of(f, struct f_sourcesink, function); ++} ++ ++static unsigned autoresume; ++module_param(autoresume, uint, 0); ++MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); ++ ++static unsigned pattern; ++module_param(pattern, uint, 0); ++MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 "); ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_interface_descriptor source_sink_intf = { ++ .bLength = sizeof source_sink_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_source_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_sink_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_source_sink_descs[] = { ++ (struct usb_descriptor_header *) &source_sink_intf, ++ (struct usb_descriptor_header *) &fs_sink_desc, ++ (struct usb_descriptor_header *) &fs_source_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_source_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_sink_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *hs_source_sink_descs[] = { ++ (struct usb_descriptor_header *) &source_sink_intf, ++ (struct usb_descriptor_header *) &hs_source_desc, ++ (struct usb_descriptor_header *) &hs_sink_desc, ++ NULL, ++}; ++ ++/* function-specific strings: */ ++ ++static struct usb_string strings_sourcesink[] = { ++ [0].s = "source and sink data", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_sourcesink = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_sourcesink, ++}; ++ ++static struct usb_gadget_strings *sourcesink_strings[] = { ++ &stringtab_sourcesink, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void sourcesink_autoresume(unsigned long _c) ++{ ++ struct usb_composite_dev *cdev = (void *)_c; ++ struct usb_gadget *g = cdev->gadget; ++ ++ /* Normally the host would be woken up for something ++ * more significant than just a timer firing; likely ++ * because of some direct user request. ++ */ ++ if (g->speed != USB_SPEED_UNKNOWN) { ++ int status = usb_gadget_wakeup(g); ++ DBG(cdev, "%s --> %d\n", __func__, status); ++ } ++} ++ ++static int __init ++sourcesink_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_sourcesink *ss = func_to_ss(f); ++ int id; ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ source_sink_intf.bInterfaceNumber = id; ++ ++ /* allocate endpoints */ ++ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); ++ if (!ss->in_ep) { ++autoconf_fail: ++ ERROR(cdev, "%s: can't autoconfigure on %s\n", ++ f->name, cdev->gadget->name); ++ return -ENODEV; ++ } ++ ss->in_ep->driver_data = cdev; /* claim */ ++ ++ ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); ++ if (!ss->out_ep) ++ goto autoconf_fail; ++ ss->out_ep->driver_data = cdev; /* claim */ ++ ++ setup_timer(&ss->resume, sourcesink_autoresume, ++ (unsigned long) c->cdev); ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_source_desc.bEndpointAddress = ++ fs_source_desc.bEndpointAddress; ++ hs_sink_desc.bEndpointAddress = ++ fs_sink_desc.bEndpointAddress; ++ f->hs_descriptors = hs_source_sink_descs; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, ss->in_ep->name, ss->out_ep->name); ++ return 0; ++} ++ ++static void ++sourcesink_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ kfree(func_to_ss(f)); ++} ++ ++/* optionally require specific source/sink data patterns */ ++static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) ++{ ++ unsigned i; ++ u8 *buf = req->buf; ++ struct usb_composite_dev *cdev = ss->function.config->cdev; ++ ++ for (i = 0; i < req->actual; i++, buf++) { ++ switch (pattern) { ++ ++ /* all-zeroes has no synchronization issues */ ++ case 0: ++ if (*buf == 0) ++ continue; ++ break; ++ ++ /* "mod63" stays in sync with short-terminated transfers, ++ * OR otherwise when host and gadget agree on how large ++ * each usb transfer request should be. Resync is done ++ * with set_interface or set_config. (We *WANT* it to ++ * get quickly out of sync if controllers or their drivers ++ * stutter for any reason, including buffer duplcation...) ++ */ ++ case 1: ++ if (*buf == (u8)(i % 63)) ++ continue; ++ break; ++ } ++ ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf); ++ usb_ep_set_halt(ss->out_ep); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) ++{ ++ unsigned i; ++ u8 *buf = req->buf; ++ ++ switch (pattern) { ++ case 0: ++ memset(req->buf, 0, req->length); ++ break; ++ case 1: ++ for (i = 0; i < req->length; i++) ++ *buf++ = (u8) (i % 63); ++ break; ++ } ++} ++ ++static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_sourcesink *ss = ep->driver_data; ++ struct usb_composite_dev *cdev = ss->function.config->cdev; ++ int status = req->status; ++ ++ switch (status) { ++ ++ case 0: /* normal completion? */ ++ if (ep == ss->out_ep) { ++ check_read_data(ss, req); ++ memset(req->buf, 0x55, req->length); ++ } else ++ reinit_write_data(ep, req); ++ break; ++ ++ /* this endpoint is normally active while we're configured */ ++ case -ECONNABORTED: /* hardware forced ep reset */ ++ case -ECONNRESET: /* request dequeued */ ++ case -ESHUTDOWN: /* disconnect from host */ ++ VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, ++ req->actual, req->length); ++ if (ep == ss->out_ep) ++ check_read_data(ss, req); ++ free_ep_req(ep, req); ++ return; ++ ++ case -EOVERFLOW: /* buffer overrun on read means that ++ * we didn't provide a big enough ++ * buffer. ++ */ ++ default: ++#if 1 ++ DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, ++ status, req->actual, req->length); ++#endif ++ case -EREMOTEIO: /* short read */ ++ break; ++ } ++ ++ status = usb_ep_queue(ep, req, GFP_ATOMIC); ++ if (status) { ++ ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", ++ ep->name, req->length, status); ++ usb_ep_set_halt(ep); ++ /* FIXME recover later ... somehow */ ++ } ++} ++ ++static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in) ++{ ++ struct usb_ep *ep; ++ struct usb_request *req; ++ int status; ++ ++ ep = is_in ? ss->in_ep : ss->out_ep; ++ req = alloc_ep_req(ep); ++ if (!req) ++ return -ENOMEM; ++ ++ req->complete = source_sink_complete; ++ if (is_in) ++ reinit_write_data(ep, req); ++ else ++ memset(req->buf, 0x55, req->length); ++ ++ status = usb_ep_queue(ep, req, GFP_ATOMIC); ++ if (status) { ++ struct usb_composite_dev *cdev; ++ ++ cdev = ss->function.config->cdev; ++ ERROR(cdev, "start %s %s --> %d\n", ++ is_in ? "IN" : "OUT", ++ ep->name, status); ++ free_ep_req(ep, req); ++ } ++ ++ return status; ++} ++ ++static void disable_source_sink(struct f_sourcesink *ss) ++{ ++ struct usb_composite_dev *cdev; ++ ++ cdev = ss->function.config->cdev; ++ disable_endpoints(cdev, ss->in_ep, ss->out_ep); ++ del_timer(&ss->resume); ++ VDBG(cdev, "%s disabled\n", ss->function.name); ++} ++ ++static int ++enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss) ++{ ++ int result = 0; ++ const struct usb_endpoint_descriptor *src, *sink; ++ struct usb_ep *ep; ++ ++ src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); ++ sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); ++ ++ /* one endpoint writes (sources) zeroes IN (to the host) */ ++ ep = ss->in_ep; ++ result = usb_ep_enable(ep, src); ++ if (result < 0) ++ return result; ++ ep->driver_data = ss; ++ ++ result = source_sink_start_ep(ss, true); ++ if (result < 0) { ++fail: ++ ep = ss->in_ep; ++ usb_ep_disable(ep); ++ ep->driver_data = NULL; ++ return result; ++ } ++ ++ /* one endpoint reads (sinks) anything OUT (from the host) */ ++ ep = ss->out_ep; ++ result = usb_ep_enable(ep, sink); ++ if (result < 0) ++ goto fail; ++ ep->driver_data = ss; ++ ++ result = source_sink_start_ep(ss, false); ++ if (result < 0) { ++ usb_ep_disable(ep); ++ ep->driver_data = NULL; ++ goto fail; ++ } ++ ++ DBG(cdev, "%s enabled\n", ss->function.name); ++ return result; ++} ++ ++static int sourcesink_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct f_sourcesink *ss = func_to_ss(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt is zero */ ++ if (ss->in_ep->driver_data) ++ disable_source_sink(ss); ++ return enable_source_sink(cdev, ss); ++} ++ ++static void sourcesink_disable(struct usb_function *f) ++{ ++ struct f_sourcesink *ss = func_to_ss(f); ++ ++ disable_source_sink(ss); ++} ++ ++static void sourcesink_suspend(struct usb_function *f) ++{ ++ struct f_sourcesink *ss = func_to_ss(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ if (cdev->gadget->speed == USB_SPEED_UNKNOWN) ++ return; ++ ++ if (autoresume) { ++ mod_timer(&ss->resume, jiffies + (HZ * autoresume)); ++ DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume); ++ } else ++ DBG(cdev, "%s\n", __func__); ++} ++ ++static void sourcesink_resume(struct usb_function *f) ++{ ++ struct f_sourcesink *ss = func_to_ss(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "%s\n", __func__); ++ del_timer(&ss->resume); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init sourcesink_bind_config(struct usb_configuration *c) ++{ ++ struct f_sourcesink *ss; ++ int status; ++ ++ ss = kzalloc(sizeof *ss, GFP_KERNEL); ++ if (!ss) ++ return -ENOMEM; ++ ++ ss->function.name = "source/sink"; ++ ss->function.descriptors = fs_source_sink_descs; ++ ss->function.bind = sourcesink_bind; ++ ss->function.unbind = sourcesink_unbind; ++ ss->function.set_alt = sourcesink_set_alt; ++ ss->function.disable = sourcesink_disable; ++ ss->function.suspend = sourcesink_suspend; ++ ss->function.resume = sourcesink_resume; ++ ++ status = usb_add_function(c, &ss->function); ++ if (status) ++ kfree(ss); ++ return status; ++} ++ ++static int sourcesink_setup(struct usb_configuration *c, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ struct usb_request *req = c->cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything except ++ * the two control test requests. ++ */ ++ switch (ctrl->bRequest) { ++ ++ /* ++ * These are the same vendor-specific requests supported by ++ * Intel's USB 2.0 compliance test devices. We exceed that ++ * device spec by allowing multiple-packet requests. ++ * ++ * NOTE: the Control-OUT data stays in req->buf ... better ++ * would be copying it into a scratch buffer, so that other ++ * requests may safely intervene. ++ */ ++ case 0x5b: /* control WRITE test -- fill the buffer */ ++ if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) ++ goto unknown; ++ if (w_value || w_index) ++ break; ++ /* just read that many bytes into the buffer */ ++ if (w_length > req->length) ++ break; ++ value = w_length; ++ break; ++ case 0x5c: /* control READ test -- return the buffer */ ++ if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) ++ goto unknown; ++ if (w_value || w_index) ++ break; ++ /* expect those bytes are still in the buffer; send back */ ++ if (w_length > req->length) ++ break; ++ value = w_length; ++ break; ++ ++ default: ++unknown: ++ VDBG(c->cdev, ++ "unknown control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(c->cdev, "source/sinkc response, err %d\n", ++ value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static struct usb_configuration sourcesink_driver = { ++ .label = "source/sink", ++ .strings = sourcesink_strings, ++ .bind = sourcesink_bind_config, ++ .setup = sourcesink_setup, ++ .bConfigurationValue = 3, ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++ /* .iConfiguration = DYNAMIC */ ++}; ++ ++/** ++ * sourcesink_add - add a source/sink testing configuration to a device ++ * @cdev: the device to support the configuration ++ */ ++int __init sourcesink_add(struct usb_composite_dev *cdev) ++{ ++ int id; ++ ++ /* allocate string ID(s) */ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_sourcesink[0].id = id; ++ ++ source_sink_intf.iInterface = id; ++ sourcesink_driver.iConfiguration = id; ++ ++ /* FIXME pass in bConfigurationValue and OTG stuff */ ++ ++ /* support autoresume for remote wakeup testing */ ++ if (autoresume) ++ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ ++ /* support OTG systems */ ++ if (gadget_is_otg(cdev->gadget)) { ++ sourcesink_driver.descriptors = otg_desc; ++ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ return usb_add_config(cdev, &sourcesink_driver); ++} +--- /dev/null ++++ b/drivers/usb/gadget/g_zero.h +@@ -0,0 +1,23 @@ ++/* ++ * This header declares the utility functions used by "Gadget Zero", plus ++ * interfaces to its two single-configuration function drivers. ++ */ ++ ++#ifndef __G_ZERO_H ++#define __G_ZERO_H ++ ++#include <linux/usb/composite.h> ++ ++/* global state */ ++extern const struct usb_descriptor_header **otg_desc; ++ ++/* common utilities */ ++struct usb_request *alloc_ep_req(struct usb_ep *ep); ++void free_ep_req(struct usb_ep *ep, struct usb_request *req); ++void disable_endpoints(struct usb_composite_dev *cdev, ++ struct usb_ep *in, struct usb_ep *out); ++ ++/* configuration-specific linkup */ ++int sourcesink_add(struct usb_composite_dev *cdev); ++ ++#endif /* __G_ZERO_H */ diff --git a/usb/usb-gadget-zero-use-updated-framework.patch b/usb/usb-gadget-zero-use-updated-framework.patch new file mode 100644 index 00000000000000..f0bbcc562c2f05 --- /dev/null +++ b/usb/usb-gadget-zero-use-updated-framework.patch @@ -0,0 +1,1346 @@ +From david-b@pacbell.net Fri Jun 6 15:11:08 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 20 May 2008 11:07:49 -0700 +Subject: usb gadget zero: use updated framework +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> +Message-ID: <200805201107.49807.david-b@pacbell.net> +Content-Disposition: inline + + +Update Gadget Zero to use the more modular versions of the loopback +and source/sink configuration drivers which build on the new gadget +framework code. + +The core code is a LOT simpler, and it should be much easier now to +understand how the parts fit together. The conversion is an overall +source shrink in terms of this gadget, since it uses more midlayer +support. However, it's an overall increase in object size because +there's less sharing between the two configurations (improves code +clarity) and because the midlayer is a bit more functional than this +driver actually needs. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Makefile | 4 + drivers/usb/gadget/zero.c | 1158 +++----------------------------------------- + 2 files changed, 95 insertions(+), 1067 deletions(-) + +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -22,7 +22,9 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o + # + # USB gadget drivers + # +-g_zero-objs := zero.o usbstring.o config.o epautoconf.o ++C_UTILS = composite.o usbstring.o config.o epautoconf.o ++ ++g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) + g_ether-objs := ether.o usbstring.o config.o epautoconf.o + g_serial-objs := serial.o u_serial.o usbstring.o config.o epautoconf.o + g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o +--- a/drivers/usb/gadget/zero.c ++++ b/drivers/usb/gadget/zero.c +@@ -30,12 +30,7 @@ + * + * It supports two similar configurations. One sinks whatever the usb host + * writes, and in return sources zeroes. The other loops whatever the host +- * writes back, so the host can read it. Module options include: +- * +- * buflen=N default N=4096, buffer size used +- * qlen=N default N=32, how many buffers in the loopback queue +- * loopdefault default false, list loopback config first +- * autoresume=N default N=0, seconds before triggering remote wakeup ++ * writes back, so the host can read it. + * + * Many drivers will only have one configuration, letting them be much + * simpler if they also don't support high speed operation (like this +@@ -47,94 +42,35 @@ + * work with low capability USB controllers without four bulk endpoints. + */ + ++/* ++ * driver assumes self-powered hardware, and ++ * has no way for users to trigger remote wakeup. ++ */ ++ + /* #define VERBOSE_DEBUG */ + + #include <linux/kernel.h> + #include <linux/utsname.h> + #include <linux/device.h> + +-#include <linux/usb/ch9.h> +-#include <linux/usb/gadget.h> +- ++#include "g_zero.h" + #include "gadget_chips.h" + + + /*-------------------------------------------------------------------------*/ + +-#define DRIVER_VERSION "Earth Day 2008" ++#define DRIVER_VERSION "Cinco de Mayo 2008" + +-static const char shortname[] = "zero"; + static const char longname[] = "Gadget Zero"; + +-static const char source_sink[] = "source and sink data"; +-static const char loopback[] = "loop input to output"; +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * driver assumes self-powered hardware, and +- * has no way for users to trigger remote wakeup. +- * +- * this version autoconfigures as much as possible, +- * which is reasonable for most "bulk-only" drivers. +- */ +-static const char *EP_IN_NAME; /* source */ +-static const char *EP_OUT_NAME; /* sink */ +- +-/*-------------------------------------------------------------------------*/ +- +-/* big enough to hold our biggest descriptor */ +-#define USB_BUFSIZ 256 +- +-struct zero_dev { +- spinlock_t lock; +- struct usb_gadget *gadget; +- struct usb_request *req; /* for control responses */ +- +- /* when configured, we have one of two configs: +- * - source data (in to host) and sink it (out from host) +- * - or loop it back (out from host back in to host) +- */ +- u8 config; +- struct usb_ep *in_ep, *out_ep; +- +- /* autoresume timer */ +- struct timer_list resume; +-}; +- +-#define DBG(d, fmt, args...) \ +- dev_dbg(&(d)->gadget->dev , fmt , ## args) +-#define VDBG(d, fmt, args...) \ +- dev_vdbg(&(d)->gadget->dev , fmt , ## args) +-#define ERROR(d, fmt, args...) \ +- dev_err(&(d)->gadget->dev , fmt , ## args) +-#define WARN(d, fmt, args...) \ +- dev_warn(&(d)->gadget->dev , fmt , ## args) +-#define INFO(d, fmt, args...) \ +- dev_info(&(d)->gadget->dev , fmt , ## args) +- +-/*-------------------------------------------------------------------------*/ +- +-static unsigned buflen = 4096; +-static unsigned qlen = 32; +-static unsigned pattern = 0; +- +-module_param(buflen, uint, S_IRUGO); +-module_param(qlen, uint, S_IRUGO); +-module_param(pattern, uint, S_IRUGO|S_IWUSR); +- +-/* +- * if it's nonzero, autoresume says how many seconds to wait +- * before trying to wake up the host after suspend. +- */ +-static unsigned autoresume = 0; +-module_param(autoresume, uint, 0); ++unsigned buflen = 4096; ++module_param(buflen, uint, 0); + + /* + * Normally the "loopback" configuration is second (index 1) so + * it's not the default. Here's where to change that order, to +- * work better with hosts where config changes are problematic. +- * Or controllers (like superh) that only support one config. ++ * work better with hosts where config changes are problematic or ++ * controllers (like original superh) that only support one config. + */ + static int loopdefault = 0; + module_param(loopdefault, bool, S_IRUGO|S_IWUSR); +@@ -156,24 +92,6 @@ module_param(loopdefault, bool, S_IRUGO| + + /*-------------------------------------------------------------------------*/ + +-/* +- * DESCRIPTORS ... most are static, but strings and (full) +- * configuration descriptors are built on demand. +- */ +- +-#define STRING_MANUFACTURER 25 +-#define STRING_PRODUCT 42 +-#define STRING_SERIAL 101 +-#define STRING_SOURCE_SINK 250 +-#define STRING_LOOPBACK 251 +- +-/* +- * This device advertises two configurations; these numbers work +- * on a pxa250 as well as more flexible hardware. +- */ +-#define CONFIG_SOURCE_SINK 3 +-#define CONFIG_LOOPBACK 2 +- + static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, +@@ -183,248 +101,64 @@ static struct usb_device_descriptor devi + + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), +- .iManufacturer = STRING_MANUFACTURER, +- .iProduct = STRING_PRODUCT, +- .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 2, + }; + +-static struct usb_config_descriptor source_sink_config = { +- .bLength = sizeof source_sink_config, +- .bDescriptorType = USB_DT_CONFIG, +- +- /* compute wTotalLength on the fly */ +- .bNumInterfaces = 1, +- .bConfigurationValue = CONFIG_SOURCE_SINK, +- .iConfiguration = STRING_SOURCE_SINK, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 1, /* self-powered */ +-}; +- +-static struct usb_config_descriptor loopback_config = { +- .bLength = sizeof loopback_config, +- .bDescriptorType = USB_DT_CONFIG, +- +- /* compute wTotalLength on the fly */ +- .bNumInterfaces = 1, +- .bConfigurationValue = CONFIG_LOOPBACK, +- .iConfiguration = STRING_LOOPBACK, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 1, /* self-powered */ +-}; +- ++#ifdef CONFIG_USB_OTG + static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + +- .bmAttributes = USB_OTG_SRP, +-}; +- +-/* one interface in each configuration */ +- +-static const struct usb_interface_descriptor source_sink_intf = { +- .bLength = sizeof source_sink_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_VENDOR_SPEC, +- .iInterface = STRING_SOURCE_SINK, +-}; +- +-static const struct usb_interface_descriptor loopback_intf = { +- .bLength = sizeof loopback_intf, +- .bDescriptorType = USB_DT_INTERFACE, +- +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_VENDOR_SPEC, +- .iInterface = STRING_LOOPBACK, +-}; +- +-/* two full speed bulk endpoints; their use is config-dependent */ +- +-static struct usb_endpoint_descriptor fs_source_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; +- +-static struct usb_endpoint_descriptor fs_sink_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bEndpointAddress = USB_DIR_OUT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; +- +-static const struct usb_descriptor_header *fs_source_sink_function[] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- (struct usb_descriptor_header *) &source_sink_intf, +- (struct usb_descriptor_header *) &fs_sink_desc, +- (struct usb_descriptor_header *) &fs_source_desc, +- NULL, +-}; +- +-static const struct usb_descriptor_header *fs_loopback_function[] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- (struct usb_descriptor_header *) &loopback_intf, +- (struct usb_descriptor_header *) &fs_sink_desc, +- (struct usb_descriptor_header *) &fs_source_desc, +- NULL, +-}; +- +-/* +- * usb 2.0 devices need to expose both high speed and full speed +- * descriptors, unless they only run at full speed. +- * +- * that means alternate endpoint descriptors (bigger packets) +- * and a "device qualifier" ... plus more construction options +- * for the config descriptor. +- */ +- +-static struct usb_endpoint_descriptor hs_source_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16(512), +-}; +- +-static struct usb_endpoint_descriptor hs_sink_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16(512), +-}; +- +-static struct usb_qualifier_descriptor dev_qualifier = { +- .bLength = sizeof dev_qualifier, +- .bDescriptorType = USB_DT_DEVICE_QUALIFIER, +- +- .bcdUSB = __constant_cpu_to_le16(0x0200), +- .bDeviceClass = USB_CLASS_VENDOR_SPEC, +- +- .bNumConfigurations = 2, ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, + }; + +-static const struct usb_descriptor_header *hs_source_sink_function[] = { ++const struct usb_descriptor_header **otg_desc = { + (struct usb_descriptor_header *) &otg_descriptor, +- (struct usb_descriptor_header *) &source_sink_intf, +- (struct usb_descriptor_header *) &hs_source_desc, +- (struct usb_descriptor_header *) &hs_sink_desc, + NULL, + }; ++#endif + +-static const struct usb_descriptor_header *hs_loopback_function[] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- (struct usb_descriptor_header *) &loopback_intf, +- (struct usb_descriptor_header *) &hs_source_desc, +- (struct usb_descriptor_header *) &hs_sink_desc, +- NULL, +-}; ++/* string IDs are assigned dynamically */ + +-/* maxpacket and other transfer characteristics vary by speed. */ +-static inline struct usb_endpoint_descriptor * +-ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, +- struct usb_endpoint_descriptor *fs) +-{ +- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) +- return hs; +- return fs; +-} ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++#define STRING_SERIAL_IDX 2 + + static char manufacturer[50]; + + /* default serial number takes at least two packets */ + static char serial[] = "0123456789.0123456789.0123456789"; + +- +-/* static strings, in UTF-8 */ +-static struct usb_string strings[] = { +- { STRING_MANUFACTURER, manufacturer, }, +- { STRING_PRODUCT, longname, }, +- { STRING_SERIAL, serial, }, +- { STRING_LOOPBACK, loopback, }, +- { STRING_SOURCE_SINK, source_sink, }, ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = longname, ++ [STRING_SERIAL_IDX].s = serial, + { } /* end of list */ + }; + +-static struct usb_gadget_strings stringtab = { ++static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ +- .strings = strings, ++ .strings = strings_dev, + }; + +-/* +- * config descriptors are also handcrafted. these must agree with code +- * that sets configurations, and with code managing interfaces and their +- * altsettings. other complexity may come from: +- * +- * - high speed support, including "other speed config" rules +- * - multiple configurations +- * - interfaces with alternate settings +- * - embedded class or vendor-specific descriptors +- * +- * this handles high speed, and has a second config that could as easily +- * have been an alternate interface setting (on most hardware). +- * +- * NOTE: to demonstrate (and test) more USB capabilities, this driver +- * should include an altsetting to test interrupt transfers, including +- * high bandwidth modes at high speed. (Maybe work like Intel's test +- * device?) +- */ +-static int config_buf(struct usb_gadget *gadget, +- u8 *buf, u8 type, unsigned index) +-{ +- int is_source_sink; +- int len; +- const struct usb_descriptor_header **function; +- int hs = 0; +- +- /* two configurations will always be index 0 and index 1 */ +- if (index > 1) +- return -EINVAL; +- is_source_sink = loopdefault ? (index == 1) : (index == 0); +- +- if (gadget_is_dualspeed(gadget)) { +- hs = (gadget->speed == USB_SPEED_HIGH); +- if (type == USB_DT_OTHER_SPEED_CONFIG) +- hs = !hs; +- } +- if (hs) +- function = is_source_sink +- ? hs_source_sink_function +- : hs_loopback_function; +- else +- function = is_source_sink +- ? fs_source_sink_function +- : fs_loopback_function; +- +- /* for now, don't advertise srp-only devices */ +- if (!gadget_is_otg(gadget)) +- function++; +- +- len = usb_gadget_config_buf(is_source_sink +- ? &source_sink_config +- : &loopback_config, +- buf, USB_BUFSIZ, function); +- if (len < 0) +- return len; +- ((struct usb_config_descriptor *) buf)->bDescriptorType = type; +- return len; +-} ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; + + /*-------------------------------------------------------------------------*/ + +-static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) ++struct usb_request *alloc_ep_req(struct usb_ep *ep) + { + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { +- req->length = length; +- req->buf = kmalloc(length, GFP_ATOMIC); ++ req->length = buflen; ++ req->buf = kmalloc(buflen, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; +@@ -433,681 +167,73 @@ static struct usb_request *alloc_ep_req( + return req; + } + +-static void free_ep_req(struct usb_ep *ep, struct usb_request *req) ++void free_ep_req(struct usb_ep *ep, struct usb_request *req) + { + kfree(req->buf); + usb_ep_free_request(ep, req); + } + +-/*-------------------------------------------------------------------------*/ +- +-/* +- * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripherals, +- * this just sinks bulk packets OUT to the peripheral and sources them IN +- * to the host, optionally with specific data patterns. +- * +- * In terms of control messaging, this supports all the standard requests +- * plus two that support control-OUT tests. +- * +- * Note that because this doesn't queue more than one request at a time, +- * some other function must be used to test queueing logic. The network +- * link (g_ether) is probably the best option for that. +- */ +- +-/* optionally require specific source/sink data patterns */ +- +-static int +-check_read_data( +- struct zero_dev *dev, +- struct usb_ep *ep, +- struct usb_request *req +-) +-{ +- unsigned i; +- u8 *buf = req->buf; +- +- for (i = 0; i < req->actual; i++, buf++) { +- switch (pattern) { +- /* all-zeroes has no synchronization issues */ +- case 0: +- if (*buf == 0) +- continue; +- break; +- /* mod63 stays in sync with short-terminated transfers, +- * or otherwise when host and gadget agree on how large +- * each usb transfer request should be. resync is done +- * with set_interface or set_config. +- */ +- case 1: +- if (*buf == (u8)(i % 63)) +- continue; +- break; +- } +- ERROR(dev, "bad OUT byte, buf[%d] = %d\n", i, *buf); +- usb_ep_set_halt(ep); +- return -EINVAL; +- } +- return 0; +-} +- +-static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) +-{ +- unsigned i; +- u8 *buf = req->buf; +- +- switch (pattern) { +- case 0: +- memset(req->buf, 0, req->length); +- break; +- case 1: +- for (i = 0; i < req->length; i++) +- *buf++ = (u8) (i % 63); +- break; +- } +-} +- +-/* if there is only one request in the queue, there'll always be an +- * irq delay between end of one request and start of the next. +- * that prevents using hardware dma queues. +- */ +-static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) ++static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) + { +- struct zero_dev *dev = ep->driver_data; +- int status = req->status; +- +- switch (status) { +- +- case 0: /* normal completion? */ +- if (ep == dev->out_ep) { +- check_read_data(dev, ep, req); +- memset(req->buf, 0x55, req->length); +- } else +- reinit_write_data(ep, req); +- break; +- +- /* this endpoint is normally active while we're configured */ +- case -ECONNABORTED: /* hardware forced ep reset */ +- case -ECONNRESET: /* request dequeued */ +- case -ESHUTDOWN: /* disconnect from host */ +- VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status, +- req->actual, req->length); +- if (ep == dev->out_ep) +- check_read_data(dev, ep, req); +- free_ep_req(ep, req); +- return; +- +- case -EOVERFLOW: /* buffer overrun on read means that +- * we didn't provide a big enough +- * buffer. +- */ +- default: +-#if 1 +- DBG(dev, "%s complete --> %d, %d/%d\n", ep->name, +- status, req->actual, req->length); +-#endif +- case -EREMOTEIO: /* short read */ +- break; +- } ++ int value; + +- status = usb_ep_queue(ep, req, GFP_ATOMIC); +- if (status) { +- ERROR(dev, "kill %s: resubmit %d bytes --> %d\n", +- ep->name, req->length, status); +- usb_ep_set_halt(ep); +- /* FIXME recover later ... somehow */ ++ if (ep->driver_data) { ++ value = usb_ep_disable(ep); ++ if (value < 0) ++ DBG(cdev, "disable %s --> %d\n", ++ ep->name, value); ++ ep->driver_data = NULL; + } + } + +-static struct usb_request *source_sink_start_ep(struct usb_ep *ep) ++void disable_endpoints(struct usb_composite_dev *cdev, ++ struct usb_ep *in, struct usb_ep *out) + { +- struct usb_request *req; +- int status; +- +- req = alloc_ep_req(ep, buflen); +- if (!req) +- return NULL; +- +- memset(req->buf, 0, req->length); +- req->complete = source_sink_complete; +- +- if (strcmp(ep->name, EP_IN_NAME) == 0) +- reinit_write_data(ep, req); +- else +- memset(req->buf, 0x55, req->length); +- +- status = usb_ep_queue(ep, req, GFP_ATOMIC); +- if (status) { +- struct zero_dev *dev = ep->driver_data; +- +- ERROR(dev, "start %s --> %d\n", ep->name, status); +- free_ep_req(ep, req); +- req = NULL; +- } +- +- return req; +-} +- +-static int set_source_sink_config(struct zero_dev *dev) +-{ +- int result = 0; +- struct usb_ep *ep; +- struct usb_gadget *gadget = dev->gadget; +- +- gadget_for_each_ep(ep, gadget) { +- const struct usb_endpoint_descriptor *d; +- +- /* one endpoint writes (sources) zeroes in (to the host) */ +- if (strcmp(ep->name, EP_IN_NAME) == 0) { +- d = ep_desc(gadget, &hs_source_desc, &fs_source_desc); +- result = usb_ep_enable(ep, d); +- if (result == 0) { +- ep->driver_data = dev; +- if (source_sink_start_ep(ep) != NULL) { +- dev->in_ep = ep; +- continue; +- } +- usb_ep_disable(ep); +- result = -EIO; +- } +- +- /* one endpoint reads (sinks) anything out (from the host) */ +- } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { +- d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); +- result = usb_ep_enable(ep, d); +- if (result == 0) { +- ep->driver_data = dev; +- if (source_sink_start_ep(ep) != NULL) { +- dev->out_ep = ep; +- continue; +- } +- usb_ep_disable(ep); +- result = -EIO; +- } +- +- /* ignore any other endpoints */ +- } else +- continue; +- +- /* stop on error */ +- ERROR(dev, "can't start %s, result %d\n", ep->name, result); +- break; +- } +- if (result == 0) +- DBG(dev, "buflen %d\n", buflen); +- +- /* caller is responsible for cleanup on error */ +- return result; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void loopback_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- struct zero_dev *dev = ep->driver_data; +- int status = req->status; +- +- switch (status) { +- +- case 0: /* normal completion? */ +- if (ep == dev->out_ep) { +- /* loop this OUT packet back IN to the host */ +- req->zero = (req->actual < req->length); +- req->length = req->actual; +- status = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC); +- if (status == 0) +- return; +- +- /* "should never get here" */ +- ERROR(dev, "can't loop %s to %s: %d\n", +- ep->name, dev->in_ep->name, +- status); +- } +- +- /* queue the buffer for some later OUT packet */ +- req->length = buflen; +- status = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); +- if (status == 0) +- return; +- +- /* "should never get here" */ +- /* FALLTHROUGH */ +- +- default: +- ERROR(dev, "%s loop complete --> %d, %d/%d\n", ep->name, +- status, req->actual, req->length); +- /* FALLTHROUGH */ +- +- /* NOTE: since this driver doesn't maintain an explicit record +- * of requests it submitted (just maintains qlen count), we +- * rely on the hardware driver to clean up on disconnect or +- * endpoint disable. +- */ +- case -ECONNABORTED: /* hardware forced ep reset */ +- case -ECONNRESET: /* request dequeued */ +- case -ESHUTDOWN: /* disconnect from host */ +- free_ep_req(ep, req); +- return; +- } +-} +- +-static int set_loopback_config(struct zero_dev *dev) +-{ +- int result = 0; +- struct usb_ep *ep; +- struct usb_gadget *gadget = dev->gadget; +- +- gadget_for_each_ep(ep, gadget) { +- const struct usb_endpoint_descriptor *d; +- +- /* one endpoint writes data back IN to the host */ +- if (strcmp(ep->name, EP_IN_NAME) == 0) { +- d = ep_desc(gadget, &hs_source_desc, &fs_source_desc); +- result = usb_ep_enable(ep, d); +- if (result == 0) { +- ep->driver_data = dev; +- dev->in_ep = ep; +- continue; +- } +- +- /* one endpoint just reads OUT packets */ +- } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { +- d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); +- result = usb_ep_enable(ep, d); +- if (result == 0) { +- ep->driver_data = dev; +- dev->out_ep = ep; +- continue; +- } +- +- /* ignore any other endpoints */ +- } else +- continue; +- +- /* stop on error */ +- ERROR(dev, "can't enable %s, result %d\n", ep->name, result); +- break; +- } +- +- /* allocate a bunch of read buffers and queue them all at once. +- * we buffer at most 'qlen' transfers; fewer if any need more +- * than 'buflen' bytes each. +- */ +- if (result == 0) { +- struct usb_request *req; +- unsigned i; +- +- ep = dev->out_ep; +- for (i = 0; i < qlen && result == 0; i++) { +- req = alloc_ep_req(ep, buflen); +- if (req) { +- req->complete = loopback_complete; +- result = usb_ep_queue(ep, req, GFP_ATOMIC); +- if (result) +- DBG(dev, "%s queue req --> %d\n", +- ep->name, result); +- } else +- result = -ENOMEM; +- } +- } +- if (result == 0) +- DBG(dev, "qlen %d, buflen %d\n", qlen, buflen); +- +- /* caller is responsible for cleanup on error */ +- return result; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void zero_reset_config(struct zero_dev *dev) +-{ +- if (dev->config == 0) +- return; +- +- DBG(dev, "reset config\n"); +- +- /* just disable endpoints, forcing completion of pending i/o. +- * all our completion handlers free their requests in this case. +- */ +- if (dev->in_ep) { +- usb_ep_disable(dev->in_ep); +- dev->in_ep = NULL; +- } +- if (dev->out_ep) { +- usb_ep_disable(dev->out_ep); +- dev->out_ep = NULL; +- } +- dev->config = 0; +- del_timer(&dev->resume); +-} +- +-/* change our operational config. this code must agree with the code +- * that returns config descriptors, and altsetting code. +- * +- * it's also responsible for power management interactions. some +- * configurations might not work with our current power sources. +- * +- * note that some device controller hardware will constrain what this +- * code can do, perhaps by disallowing more than one configuration or +- * by limiting configuration choices (like the pxa2xx). +- */ +-static int zero_set_config(struct zero_dev *dev, unsigned number) +-{ +- int result = 0; +- struct usb_gadget *gadget = dev->gadget; +- +- if (number == dev->config) +- return 0; +- +- if (gadget_is_sa1100(gadget) && dev->config) { +- /* tx fifo is full, but we can't clear it...*/ +- ERROR(dev, "can't change configurations\n"); +- return -ESPIPE; +- } +- zero_reset_config(dev); +- +- switch (number) { +- case CONFIG_SOURCE_SINK: +- result = set_source_sink_config(dev); +- break; +- case CONFIG_LOOPBACK: +- result = set_loopback_config(dev); +- break; +- default: +- result = -EINVAL; +- /* FALL THROUGH */ +- case 0: +- return result; +- } +- +- if (!result && (!dev->in_ep || !dev->out_ep)) +- result = -ENODEV; +- if (result) +- zero_reset_config(dev); +- else { +- char *speed; +- +- switch (gadget->speed) { +- case USB_SPEED_LOW: speed = "low"; break; +- case USB_SPEED_FULL: speed = "full"; break; +- case USB_SPEED_HIGH: speed = "high"; break; +- default: speed = "?"; break; +- } +- +- dev->config = number; +- INFO(dev, "%s speed config #%d: %s\n", speed, number, +- (number == CONFIG_SOURCE_SINK) +- ? source_sink : loopback); +- } +- return result; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- if (req->status || req->actual != req->length) +- DBG((struct zero_dev *) ep->driver_data, +- "setup complete --> %d, %d/%d\n", +- req->status, req->actual, req->length); +-} +- +-/* +- * The setup() callback implements all the ep0 functionality that's +- * not handled lower down, in hardware or the hardware driver (like +- * device and endpoint feature flags, and their status). It's all +- * housekeeping for the gadget function we're implementing. Most of +- * the work is in config-specific setup. +- */ +-static int +-zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- struct usb_request *req = dev->req; +- int value = -EOPNOTSUPP; +- u16 w_index = le16_to_cpu(ctrl->wIndex); +- u16 w_value = le16_to_cpu(ctrl->wValue); +- u16 w_length = le16_to_cpu(ctrl->wLength); +- +- /* usually this stores reply data in the pre-allocated ep0 buffer, +- * but config change events will reconfigure hardware. +- */ +- req->zero = 0; +- switch (ctrl->bRequest) { +- +- case USB_REQ_GET_DESCRIPTOR: +- if (ctrl->bRequestType != USB_DIR_IN) +- goto unknown; +- switch (w_value >> 8) { +- +- case USB_DT_DEVICE: +- value = min(w_length, (u16) sizeof device_desc); +- memcpy(req->buf, &device_desc, value); +- break; +- case USB_DT_DEVICE_QUALIFIER: +- if (!gadget_is_dualspeed(gadget)) +- break; +- value = min(w_length, (u16) sizeof dev_qualifier); +- memcpy(req->buf, &dev_qualifier, value); +- break; +- +- case USB_DT_OTHER_SPEED_CONFIG: +- if (!gadget_is_dualspeed(gadget)) +- break; +- // FALLTHROUGH +- case USB_DT_CONFIG: +- value = config_buf(gadget, req->buf, +- w_value >> 8, +- w_value & 0xff); +- if (value >= 0) +- value = min(w_length, (u16) value); +- break; +- +- case USB_DT_STRING: +- /* wIndex == language code. +- * this driver only handles one language, you can +- * add string tables for other languages, using +- * any UTF-8 characters +- */ +- value = usb_gadget_get_string(&stringtab, +- w_value & 0xff, req->buf); +- if (value >= 0) +- value = min(w_length, (u16) value); +- break; +- } +- break; +- +- /* currently two configs, two speeds */ +- case USB_REQ_SET_CONFIGURATION: +- if (ctrl->bRequestType != 0) +- goto unknown; +- if (gadget->a_hnp_support) +- DBG(dev, "HNP available\n"); +- else if (gadget->a_alt_hnp_support) +- DBG(dev, "HNP needs a different root port\n"); +- else +- VDBG(dev, "HNP inactive\n"); +- spin_lock(&dev->lock); +- value = zero_set_config(dev, w_value); +- spin_unlock(&dev->lock); +- break; +- case USB_REQ_GET_CONFIGURATION: +- if (ctrl->bRequestType != USB_DIR_IN) +- goto unknown; +- *(u8 *)req->buf = dev->config; +- value = min(w_length, (u16) 1); +- break; +- +- /* until we add altsetting support, or other interfaces, +- * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) +- * and already killed pending endpoint I/O. +- */ +- case USB_REQ_SET_INTERFACE: +- if (ctrl->bRequestType != USB_RECIP_INTERFACE) +- goto unknown; +- spin_lock(&dev->lock); +- if (dev->config && w_index == 0 && w_value == 0) { +- u8 config = dev->config; +- +- /* resets interface configuration, forgets about +- * previous transaction state (queued bufs, etc) +- * and re-inits endpoint state (toggle etc) +- * no response queued, just zero status == success. +- * if we had more than one interface we couldn't +- * use this "reset the config" shortcut. +- */ +- zero_reset_config(dev); +- zero_set_config(dev, config); +- value = 0; +- } +- spin_unlock(&dev->lock); +- break; +- case USB_REQ_GET_INTERFACE: +- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) +- goto unknown; +- if (!dev->config) +- break; +- if (w_index != 0) { +- value = -EDOM; +- break; +- } +- *(u8 *)req->buf = 0; +- value = min(w_length, (u16) 1); +- break; +- +- /* +- * These are the same vendor-specific requests supported by +- * Intel's USB 2.0 compliance test devices. We exceed that +- * device spec by allowing multiple-packet requests. +- */ +- case 0x5b: /* control WRITE test -- fill the buffer */ +- if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) +- goto unknown; +- if (w_value || w_index) +- break; +- /* just read that many bytes into the buffer */ +- if (w_length > USB_BUFSIZ) +- break; +- value = w_length; +- break; +- case 0x5c: /* control READ test -- return the buffer */ +- if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) +- goto unknown; +- if (w_value || w_index) +- break; +- /* expect those bytes are still in the buffer; send back */ +- if (w_length > USB_BUFSIZ +- || w_length != req->length) +- break; +- value = w_length; +- break; +- +- default: +-unknown: +- VDBG(dev, +- "unknown control req%02x.%02x v%04x i%04x l%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- w_value, w_index, w_length); +- } +- +- /* respond with data transfer before status phase? */ +- if (value >= 0) { +- req->length = value; +- req->zero = value < w_length; +- value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); +- if (value < 0) { +- DBG(dev, "ep_queue --> %d\n", value); +- req->status = 0; +- zero_setup_complete(gadget->ep0, req); +- } +- } +- +- /* device either stalls (value < 0) or reports success */ +- return value; +-} +- +-static void zero_disconnect(struct usb_gadget *gadget) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- unsigned long flags; +- +- spin_lock_irqsave(&dev->lock, flags); +- zero_reset_config(dev); +- +- /* a more significant application might have some non-usb +- * activities to quiesce here, saving resources like power +- * or pushing the notification up a network stack. +- */ +- spin_unlock_irqrestore(&dev->lock, flags); +- +- /* next we may get setup() calls to enumerate new connections; +- * or an unbind() during shutdown (including removing module). +- */ +-} +- +-static void zero_autoresume(unsigned long _dev) +-{ +- struct zero_dev *dev = (struct zero_dev *) _dev; +- int status; +- +- /* normally the host would be woken up for something +- * more significant than just a timer firing... +- */ +- if (dev->gadget->speed != USB_SPEED_UNKNOWN) { +- status = usb_gadget_wakeup(dev->gadget); +- DBG(dev, "wakeup --> %d\n", status); +- } ++ disable_ep(cdev, in); ++ disable_ep(cdev, out); + } + + /*-------------------------------------------------------------------------*/ + +-static void zero_unbind(struct usb_gadget *gadget) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- +- DBG(dev, "unbind\n"); +- +- /* we've already been disconnected ... no i/o is active */ +- if (dev->req) { +- dev->req->length = USB_BUFSIZ; +- free_ep_req(gadget->ep0, dev->req); +- } +- del_timer_sync(&dev->resume); +- kfree(dev); +- set_gadget_data(gadget, NULL); +-} +- +-static int __init zero_bind(struct usb_gadget *gadget) ++static int __init zero_bind(struct usb_composite_dev *cdev) + { +- struct zero_dev *dev; +- struct usb_ep *ep; + int gcnum; ++ struct usb_gadget *gadget = cdev->gadget; ++ int id; + +- /* FIXME this can't yet work right with SH ... it has only +- * one configuration, numbered one. +- */ +- if (gadget_is_sh(gadget)) +- return -ENODEV; +- +- /* Bulk-only drivers like this one SHOULD be able to +- * autoconfigure on any sane usb controller driver, +- * but there may also be important quirks to address. ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. + */ +- usb_ep_autoconfig_reset(gadget); +- ep = usb_ep_autoconfig(gadget, &fs_source_desc); +- if (!ep) { +-autoconf_fail: +- pr_err("%s: can't autoconfigure on %s\n", +- shortname, gadget->name); +- return -ENODEV; ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_MANUFACTURER_IDX].id = id; ++ device_desc.iManufacturer = id; ++ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_PRODUCT_IDX].id = id; ++ device_desc.iProduct = id; ++ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_SERIAL_IDX].id = id; ++ device_desc.iSerialNumber = id; ++ ++ /* Register primary, then secondary configuration. Note that ++ * SH3 only allows one config... ++ */ ++ if (loopdefault) { ++ loopback_add(cdev); ++ if (!gadget_is_sh(gadget)) ++ sourcesink_add(cdev); ++ } else { ++ sourcesink_add(cdev); ++ if (!gadget_is_sh(gadget)) ++ loopback_add(cdev); + } +- EP_IN_NAME = ep->name; +- ep->driver_data = ep; /* claim */ +- +- ep = usb_ep_autoconfig(gadget, &fs_sink_desc); +- if (!ep) +- goto autoconf_fail; +- EP_OUT_NAME = ep->name; +- ep->driver_data = ep; /* claim */ + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) +@@ -1115,144 +241,44 @@ autoconf_fail: + else { + /* gadget zero is so simple (for now, no altsettings) that + * it SHOULD NOT have problems with bulk-capable hardware. +- * so warn about unrcognized controllers, don't panic. ++ * so just warn about unrcognized controllers -- don't panic. + * + * things like configuration and altsetting numbering + * can need hardware-specific attention though. + */ + pr_warning("%s: controller '%s' not recognized\n", +- shortname, gadget->name); ++ longname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + +- /* ok, we made sense of the hardware ... */ +- dev = kzalloc(sizeof(*dev), GFP_KERNEL); +- if (!dev) +- return -ENOMEM; +- spin_lock_init(&dev->lock); +- dev->gadget = gadget; +- set_gadget_data(gadget, dev); +- +- init_timer(&dev->resume); +- dev->resume.function = zero_autoresume; +- dev->resume.data = (unsigned long) dev; +- +- /* preallocate control response and buffer */ +- dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); +- if (!dev->req) +- goto enomem; +- dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); +- if (!dev->req->buf) +- goto enomem; +- +- dev->req->complete = zero_setup_complete; +- +- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; +- +- if (gadget_is_dualspeed(gadget)) { +- /* assume ep0 uses the same value for both speeds ... */ +- dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; +- +- /* and that all endpoints are dual-speed */ +- hs_source_desc.bEndpointAddress = +- fs_source_desc.bEndpointAddress; +- hs_sink_desc.bEndpointAddress = +- fs_sink_desc.bEndpointAddress; +- } +- +- if (gadget_is_otg(gadget)) { +- otg_descriptor.bmAttributes |= USB_OTG_HNP, +- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- } +- +- usb_gadget_set_selfpowered(gadget); +- +- if (autoresume) { +- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- } +- +- gadget->ep0->driver_data = dev; +- +- INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); +- INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, +- EP_OUT_NAME, EP_IN_NAME); ++ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); + + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + + return 0; +- +-enomem: +- zero_unbind(gadget); +- return -ENOMEM; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void zero_suspend(struct usb_gadget *gadget) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- +- if (gadget->speed == USB_SPEED_UNKNOWN) +- return; +- +- if (autoresume) { +- mod_timer(&dev->resume, jiffies + (HZ * autoresume)); +- DBG(dev, "suspend, wakeup in %d seconds\n", autoresume); +- } else +- DBG(dev, "suspend\n"); +-} +- +-static void zero_resume(struct usb_gadget *gadget) +-{ +- struct zero_dev *dev = get_gadget_data(gadget); +- +- DBG(dev, "resume\n"); +- del_timer(&dev->resume); + } + +- +-/*-------------------------------------------------------------------------*/ +- +-static struct usb_gadget_driver zero_driver = { +-#ifdef CONFIG_USB_GADGET_DUALSPEED +- .speed = USB_SPEED_HIGH, +-#else +- .speed = USB_SPEED_FULL, +-#endif +- .function = (char *) longname, ++static struct usb_composite_driver zero_driver = { ++ .name = "zero", ++ .dev = &device_desc, ++ .strings = dev_strings, + .bind = zero_bind, +- .unbind = __exit_p(zero_unbind), +- +- .setup = zero_setup, +- .disconnect = zero_disconnect, +- +- .suspend = zero_suspend, +- .resume = zero_resume, +- +- .driver = { +- .name = (char *) shortname, +- .owner = THIS_MODULE, +- }, + }; + + MODULE_AUTHOR("David Brownell"); + MODULE_LICENSE("GPL"); + +- + static int __init init(void) + { +- return usb_gadget_register_driver(&zero_driver); ++ return usb_composite_register(&zero_driver); + } + module_init(init); + + static void __exit cleanup(void) + { +- usb_gadget_unregister_driver(&zero_driver); ++ usb_composite_unregister(&zero_driver); + } + module_exit(cleanup); +- diff --git a/usb/usb-hiddev-switch-to-unlocked_ioctl.patch b/usb/usb-hiddev-switch-to-unlocked_ioctl.patch new file mode 100644 index 00000000000000..e9bcea751f06e6 --- /dev/null +++ b/usb/usb-hiddev-switch-to-unlocked_ioctl.patch @@ -0,0 +1,74 @@ +From alan@lxorguk.ukuu.org.uk Fri Jun 6 15:14:50 2008 +From: Alan Cox <alan@lxorguk.ukuu.org.uk> +Date: Thu, 22 May 2008 22:22:17 +0100 +Subject: USB: hiddev: Switch to unlocked_ioctl +To: linux-kernel@vger.kernel.org, dmitry.torokhov@gmail.com, linux-usb@vger.kernel.org +Message-ID: <20080522222217.3a785487@core> + + +Push down the BKL. In some cases compat_ioctl already doesn't take the +BKL so we don't either. Some of the locking here seems already dubious +and object lifetimes want documenting + +Signed-off-by: Alan Cox <alan@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/hid/usbhid/hiddev.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/drivers/hid/usbhid/hiddev.c ++++ b/drivers/hid/usbhid/hiddev.c +@@ -406,6 +406,7 @@ static noinline int hiddev_ioctl_usage(s + uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); + if (!uref_multi) + return -ENOMEM; ++ lock_kernel(); + uref = &uref_multi->uref; + if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { + if (copy_from_user(uref_multi, user_arg, +@@ -501,12 +502,15 @@ static noinline int hiddev_ioctl_usage(s + } + + goodreturn: ++ unlock_kernel(); + kfree(uref_multi); + return 0; + fault: ++ unlock_kernel(); + kfree(uref_multi); + return -EFAULT; + inval: ++ unlock_kernel(); + kfree(uref_multi); + return -EINVAL; + } +@@ -540,7 +544,7 @@ static noinline int hiddev_ioctl_string( + return len; + } + +-static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { + struct hiddev_list *list = file->private_data; + struct hiddev *hiddev = list->hiddev; +@@ -555,7 +559,10 @@ static int hiddev_ioctl(struct inode *in + struct usbhid_device *usbhid = hid->driver_data; + void __user *user_arg = (void __user *)arg; + int i; ++ ++ /* Called without BKL by compat methods so no BKL taken */ + ++ /* FIXME: Who or what stop this racing with a disconnect ?? */ + if (!hiddev->exist) + return -EIO; + +@@ -768,7 +775,7 @@ static const struct file_operations hidd + .poll = hiddev_poll, + .open = hiddev_open, + .release = hiddev_release, +- .ioctl = hiddev_ioctl, ++ .unlocked_ioctl = hiddev_ioctl, + .fasync = hiddev_fasync, + #ifdef CONFIG_COMPAT + .compat_ioctl = hiddev_compat_ioctl, diff --git a/usb/usb-iowarrior-push-down-bkl.patch b/usb/usb-iowarrior-push-down-bkl.patch new file mode 100644 index 00000000000000..d98ecba9b12531 --- /dev/null +++ b/usb/usb-iowarrior-push-down-bkl.patch @@ -0,0 +1,57 @@ +From alan@lxorguk.ukuu.org.uk Fri Jun 6 15:14:34 2008 +From: Alan Cox <alan@lxorguk.ukuu.org.uk> +Date: Thu, 22 May 2008 22:06:02 +0100 +Subject: USB: iowarrior: Push down BKL +To: linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org +Message-ID: <20080522220602.31397da8@core> + + +I'm pretty sure the mutex is sufficient for all locking but will come +back to that later if the USB folks don't beat me to it. For now get rid +of the old BKL ioctl method and wrap the ioctl handler + +Signed-off-by: Alan Cox <alan@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/misc/iowarrior.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/usb/misc/iowarrior.c ++++ b/drivers/usb/misc/iowarrior.c +@@ -474,8 +474,8 @@ exit: + /** + * iowarrior_ioctl + */ +-static int iowarrior_ioctl(struct inode *inode, struct file *file, +- unsigned int cmd, unsigned long arg) ++static long iowarrior_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) + { + struct iowarrior *dev = NULL; + __u8 *buffer; +@@ -493,6 +493,7 @@ static int iowarrior_ioctl(struct inode + return -ENOMEM; + + /* lock this object */ ++ lock_kernel(); + mutex_lock(&dev->mutex); + + /* verify that the device wasn't unplugged */ +@@ -584,6 +585,7 @@ static int iowarrior_ioctl(struct inode + error_out: + /* unlock the device */ + mutex_unlock(&dev->mutex); ++ unlock_kernel(); + kfree(buffer); + return retval; + } +@@ -719,7 +721,7 @@ static const struct file_operations iowa + .owner = THIS_MODULE, + .write = iowarrior_write, + .read = iowarrior_read, +- .ioctl = iowarrior_ioctl, ++ .unlocked_ioctl = iowarrior_ioctl, + .open = iowarrior_open, + .release = iowarrior_release, + .poll = iowarrior_poll, diff --git a/usb/usb-make-sa1111-ohci-driver-sa11x0-specific.patch b/usb/usb-make-sa1111-ohci-driver-sa11x0-specific.patch new file mode 100644 index 00000000000000..9d317c1ddc4713 --- /dev/null +++ b/usb/usb-make-sa1111-ohci-driver-sa11x0-specific.patch @@ -0,0 +1,35 @@ +From eric.y.miao@gmail.com Fri Jun 6 15:19:15 2008 +From: Eric Miao <eric.y.miao@gmail.com> +Date: Mon, 02 Jun 2008 10:05:30 +0800 +Subject: USB: make SA1111 OHCI driver SA11x0 specific +To: linux-usb@vger.kernel.org +Cc: David Brownell <david-b@pacbell.net>, Russell King - ARM Linux <linux@arm.linux.org.uk> +Message-ID: <4843556A.5080905@gmail.com> + + + +As RMK pointed out, considering the fact that the _only_ platform with +a PXA and SA1111 is the Lubbock, and that SA1111 DMA doesn't work there, +(i.e. the SA1111 OHCI doesn't work there) the SA1111 OHCI driver should +really be made SA11x0 specific. + +Signed-off-by: Eric Miao <eric.miao@marvell.com> +Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> +Acked-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ohci-hcd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -974,7 +974,7 @@ MODULE_LICENSE ("GPL"); + #define PCI_DRIVER ohci_pci_driver + #endif + +-#ifdef CONFIG_SA1111 ++#if defined(CONFIG_ARCH_SA1100) && defined(CONFIG_SA1111) + #include "ohci-sa1111.c" + #define SA1111_DRIVER ohci_hcd_sa1111_driver + #endif diff --git a/usb/usb-missing-usb_put_hcd-to-ohci-at91.patch b/usb/usb-missing-usb_put_hcd-to-ohci-at91.patch new file mode 100644 index 00000000000000..1cbcbb251ad28e --- /dev/null +++ b/usb/usb-missing-usb_put_hcd-to-ohci-at91.patch @@ -0,0 +1,65 @@ +From zaitcev@redhat.com Fri Jun 6 15:18:50 2008 +From: Pete Zaitcev <zaitcev@redhat.com> +Date: Sun, 1 Jun 2008 14:38:43 -0700 +Subject: USB: missing usb_put_hcd to ohci-at91 +To: linux-usb@vger.kernel.org +Cc: linux@maxim.org.za, zaitcev@redhat.com +Message-ID: <20080601143843.5ba394db.zaitcev@redhat.com> + + +Looks like usb_put_hcd was missing. Also, make an always-zero function +return void. + +Signed-off-by: Pete Zaitcev <zaitcev@yahoo.com> +Acked-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ohci-at91.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/usb/host/ohci-at91.c ++++ b/drivers/usb/host/ohci-at91.c +@@ -91,7 +91,7 @@ static void at91_stop_hc(struct platform + + /*-------------------------------------------------------------------------*/ + +-static int usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); ++static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); + + /* configure so an HC device and id are always provided */ + /* always called with process context; sleeping is OK */ +@@ -184,13 +184,14 @@ static int usb_hcd_at91_probe(const stru + * context, "rmmod" or something similar. + * + */ +-static int usb_hcd_at91_remove(struct usb_hcd *hcd, ++static void usb_hcd_at91_remove(struct usb_hcd *hcd, + struct platform_device *pdev) + { + usb_remove_hcd(hcd); + at91_stop_hc(pdev); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); + + if (cpu_is_at91sam9261()) + clk_put(hclk); +@@ -199,7 +200,6 @@ static int usb_hcd_at91_remove(struct us + fclk = iclk = hclk = NULL; + + dev_set_drvdata(&pdev->dev, NULL); +- return 0; + } + + /*-------------------------------------------------------------------------*/ +@@ -308,7 +308,8 @@ static int ohci_hcd_at91_drv_remove(stru + } + + device_init_wakeup(&pdev->dev, 0); +- return usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev); ++ usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev); ++ return 0; + } + + #ifdef CONFIG_PM diff --git a/usb/usb-ohci-ppc-of-use-linux-of_platform.h-instead-of-asm.patch b/usb/usb-ohci-ppc-of-use-linux-of_platform.h-instead-of-asm.patch new file mode 100644 index 00000000000000..b182fb85b74d3b --- /dev/null +++ b/usb/usb-ohci-ppc-of-use-linux-of_platform.h-instead-of-asm.patch @@ -0,0 +1,29 @@ +From sfr@canb.auug.org.au Fri Jun 6 15:16:28 2008 +From: Stephen Rothwell <sfr@canb.auug.org.au> +Date: Fri, 23 May 2008 16:37:58 +1000 +Subject: USB: ohci-ppc-of: use linux/of_platform.h instead of asm +To: Sylvain Munaut <tnt@246tNt.com> +Cc: ppc-dev <linuxppc-dev@ozlabs.org>, Greg KH <greg@kroah.com> +Message-ID: <20080523163758.4edcdef4.sfr@canb.auug.org.au> + + + +Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ohci-ppc-of.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/host/ohci-ppc-of.c ++++ b/drivers/usb/host/ohci-ppc-of.c +@@ -14,8 +14,8 @@ + */ + + #include <linux/signal.h> ++#include <linux/of_platform.h> + +-#include <asm/of_platform.h> + #include <asm/prom.h> + + diff --git a/usb/usb-ohci_hcd-hang-submit-vs.-rmmod-race.patch b/usb/usb-ohci_hcd-hang-submit-vs.-rmmod-race.patch new file mode 100644 index 00000000000000..53adab2165ee65 --- /dev/null +++ b/usb/usb-ohci_hcd-hang-submit-vs.-rmmod-race.patch @@ -0,0 +1,49 @@ +From zaitcev@redhat.com Fri Jun 6 15:19:36 2008 +From: Pete Zaitcev <zaitcev@redhat.com> +Date: Sun, 1 Jun 2008 21:23:07 -0700 +Subject: USB: ohci_hcd hang: submit vs. rmmod race +To: greg@kroah.com +Cc: linux-usb@vger.kernel.org, zaitcev@redhat.com +Message-ID: <20080601212307.f01b8089.zaitcev@redhat.com> + + +If we do rmmod ohci_hcd while an application is doing something, the +following may happen: + +- a control URB completes (in finish_urb) and the ohci's endpoint is + set into ED_UNLINK in ed_deschedule +- same URB is (re)submitted because of the open/close loop or other + such application behaviour +- rmmod sets the state to HC_STATE_QUESCING +- finish_unlinks happens at next SOF; normally it would set ed into + ED_IDLE and immediately call ed_schedule (since URB had extra TDs + queued), which sets it into ED_OPER. But the check in ed_schedule + makes it fail with -EAGAIN (which is ignored) +- from now on we have a dead URB stuck; it cannot even be unlinked + because the ed status is not ED_OPER, and thus start_ed_unlink is + not invoked. + +This patch removes the check. In 2.6.25, all callers check for +__ACTIVE bit before invoking ed_schedule, which is more appropriate. + +Alan Stern and David Brownell approved of this (cautiously). + +Signed-off-by: Pete Zaitcev <zaitcev@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ohci-q.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/usb/host/ohci-q.c ++++ b/drivers/usb/host/ohci-q.c +@@ -159,9 +159,6 @@ static int ed_schedule (struct ohci_hcd + { + int branch; + +- if (ohci_to_hcd(ohci)->state == HC_STATE_QUIESCING) +- return -EAGAIN; +- + ed->state = ED_OPER; + ed->ed_prev = NULL; + ed->ed_next = NULL; diff --git a/usb/usb-rio100-push-down-the-bkl.patch b/usb/usb-rio100-push-down-the-bkl.patch new file mode 100644 index 00000000000000..941e20ed384b79 --- /dev/null +++ b/usb/usb-rio100-push-down-the-bkl.patch @@ -0,0 +1,56 @@ +From alan@lxorguk.ukuu.org.uk Fri Jun 6 15:15:16 2008 +From: Alan Cox <alan@lxorguk.ukuu.org.uk> +Date: Thu, 22 May 2008 22:47:31 +0100 +Subject: USB: rio100: Push down the BKL +To: linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org +Message-ID: <20080522224731.10cdac01@core> + + +The BKL is actually probably not needed as the mutex seems sufficient. If +so then a further patch to drop it would be a good followup. + +Signed-off-by: Alan Cox <alan@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/misc/rio500.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/usb/misc/rio500.c ++++ b/drivers/usb/misc/rio500.c +@@ -104,9 +104,7 @@ static int close_rio(struct inode *inode + return 0; + } + +-static int +-ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd, +- unsigned long arg) ++static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg) + { + struct RioCommand rio_cmd; + struct rio_usb_data *rio = &rio_instance; +@@ -116,6 +114,7 @@ ioctl_rio(struct inode *inode, struct fi + int retries; + int retval=0; + ++ lock_kernel(); + mutex_lock(&(rio->lock)); + /* Sanity check to make sure rio is connected, powered, etc */ + if (rio->present == 0 || rio->rio_dev == NULL) { +@@ -254,6 +253,7 @@ ioctl_rio(struct inode *inode, struct fi + + err_out: + mutex_unlock(&(rio->lock)); ++ unlock_kernel(); + return retval; + } + +@@ -433,7 +433,7 @@ file_operations usb_rio_fops = { + .owner = THIS_MODULE, + .read = read_rio, + .write = write_rio, +- .ioctl = ioctl_rio, ++ .unlocked_ioctl = ioctl_rio, + .open = open_rio, + .release = close_rio, + }; diff --git a/usb/usb-serial-gadget-cdc-acm-function-driver.patch b/usb/usb-serial-gadget-cdc-acm-function-driver.patch new file mode 100644 index 00000000000000..f9b59ca1746408 --- /dev/null +++ b/usb/usb-serial-gadget-cdc-acm-function-driver.patch @@ -0,0 +1,681 @@ +From david-b@pacbell.net Fri Jun 6 15:11:23 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 20 May 2008 11:10:52 -0700 +Subject: usb serial gadget: cdc acm function driver +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> +Message-ID: <200805201110.52928.david-b@pacbell.net> +Content-Disposition: inline + + +Split out CDC ACM parts of "gadget serial" to a "function driver". +Some key structural differences from the previous ACM support, shared +with with the generic serial function (next patch): + + - As a function driver, it can be combined with other functions. + One gadget configuration could offer both serial and network + links, as an example. + + - One serial port can be exposed in multiple configurations; + the /dev/ttyGS0 node could be exposed regardless of which + config the host selected. + + - One configuration can expose multiple serial ports, such as + ttyGS0, ttyGS1, ttyGS2, and ttyGS3. + +This code should be a lot easier to understand than the previous +all-in-one-big-file version of the driver. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/DocBook/gadget.tmpl | 2 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_acm.c | 587 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/u_serial.h | 10 + 4 files changed, 595 insertions(+), 6 deletions(-) + +--- a/Documentation/DocBook/gadget.tmpl ++++ b/Documentation/DocBook/gadget.tmpl +@@ -556,6 +556,8 @@ been converted to this framework. + Near-term plans include converting all of them, except for "gadgetfs". + </para> + ++!Edrivers/usb/gadget/f_acm.c ++ + </sect1> + + +--- /dev/null ++++ b/drivers/usb/gadget/f_acm.c +@@ -0,0 +1,587 @@ ++/* ++ * f_acm.c -- USB CDC ACM function driver ++ * ++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) ++ * Copyright (C) 2008 by David Brownell ++ * ++ * This software is distributed under the terms of the GNU General ++ * Public License ("GPL") as published by the Free Software Foundation, ++ * either version 2 of that License or (at your option) any later version. ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/device.h> ++ ++#include "u_serial.h" ++#include "gadget_chips.h" ++ ++ ++/* ++ * This CDC ACM function support just wraps control functions and ++ * notifications around the generic serial-over-usb code. ++ * ++ * Because CDC ACM is standardized by the USB-IF, many host operating ++ * systems have drivers for it. Accordingly, ACM is the preferred ++ * interop solution for serial-port type connections. The control ++ * models are often not necessary, and in any case don't do much in ++ * this bare-bones implementation. ++ */ ++ ++struct acm_ep_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++ struct usb_endpoint_descriptor *notify; ++}; ++ ++struct f_acm { ++ struct gserial port; ++ u8 ctrl_id, data_id; ++ u8 port_num; ++ ++ struct usb_descriptor_header **fs_function; ++ struct acm_ep_descs fs; ++ struct usb_descriptor_header **hs_function; ++ struct acm_ep_descs hs; ++ ++ struct usb_ep *notify; ++ struct usb_endpoint_descriptor *notify_desc; ++ ++ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ ++ u16 port_handshake_bits; ++#define RS232_RTS (1 << 1) /* unused with full duplex */ ++#define RS232_DTR (1 << 0) /* host is ready for data r/w */ ++}; ++ ++static inline struct f_acm *func_to_acm(struct usb_function *f) ++{ ++ return container_of(f, struct f_acm, port.func); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* notification endpoint uses smallish and infrequent fixed-size messages */ ++ ++#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ ++#define GS_NOTIFY_MAXPACKET 8 ++ ++/* interface and class descriptors: */ ++ ++static struct usb_interface_descriptor acm_control_interface_desc __initdata = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, ++ .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_interface_descriptor acm_data_interface_desc __initdata = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_CDC_DATA, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_header_desc acm_header_desc __initdata = { ++ .bLength = sizeof(acm_header_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_HEADER_TYPE, ++ .bcdCDC = __constant_cpu_to_le16(0x0110), ++}; ++ ++static struct usb_cdc_call_mgmt_descriptor ++acm_call_mgmt_descriptor __initdata = { ++ .bLength = sizeof(acm_call_mgmt_descriptor), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, ++ .bmCapabilities = 0, ++ /* .bDataInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { ++ .bLength = sizeof(acm_descriptor), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_ACM_TYPE, ++ .bmCapabilities = (1 << 1), ++}; ++ ++static struct usb_cdc_union_desc acm_union_desc __initdata = { ++ .bLength = sizeof(acm_union_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_UNION_TYPE, ++ /* .bMasterInterface0 = DYNAMIC */ ++ /* .bSlaveInterface0 = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), ++ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, ++}; ++ ++static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *acm_fs_function[] __initdata = { ++ (struct usb_descriptor_header *) &acm_control_interface_desc, ++ (struct usb_descriptor_header *) &acm_header_desc, ++ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, ++ (struct usb_descriptor_header *) &acm_descriptor, ++ (struct usb_descriptor_header *) &acm_union_desc, ++ (struct usb_descriptor_header *) &acm_fs_notify_desc, ++ (struct usb_descriptor_header *) &acm_data_interface_desc, ++ (struct usb_descriptor_header *) &acm_fs_in_desc, ++ (struct usb_descriptor_header *) &acm_fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), ++ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, ++}; ++ ++static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *acm_hs_function[] __initdata = { ++ (struct usb_descriptor_header *) &acm_control_interface_desc, ++ (struct usb_descriptor_header *) &acm_header_desc, ++ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, ++ (struct usb_descriptor_header *) &acm_descriptor, ++ (struct usb_descriptor_header *) &acm_union_desc, ++ (struct usb_descriptor_header *) &acm_hs_notify_desc, ++ (struct usb_descriptor_header *) &acm_data_interface_desc, ++ (struct usb_descriptor_header *) &acm_hs_in_desc, ++ (struct usb_descriptor_header *) &acm_hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++#define ACM_CTRL_IDX 0 ++#define ACM_DATA_IDX 1 ++ ++/* static strings, in UTF-8 */ ++static struct usb_string acm_string_defs[] = { ++ [ACM_CTRL_IDX].s = "CDC ACM Control", ++ [ACM_DATA_IDX].s = "CDC ACM Data", ++ { /* ZEROES END LIST */ }, ++}; ++ ++static struct usb_gadget_strings acm_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = acm_string_defs, ++}; ++ ++static struct usb_gadget_strings *acm_strings[] = { ++ &acm_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ACM control ... data handling is delegated to tty library code. ++ * The main task of this function is to activate and deactivate ++ * that code based on device state; track parameters like line ++ * speed, handshake state, and so on; and issue notifications. ++ */ ++ ++static void acm_complete_set_line_coding(struct usb_ep *ep, ++ struct usb_request *req) ++{ ++ struct f_acm *acm = ep->driver_data; ++ struct usb_composite_dev *cdev = acm->port.func.config->cdev; ++ ++ if (req->status != 0) { ++ DBG(cdev, "acm ttyGS%d completion, err %d\n", ++ acm->port_num, req->status); ++ return; ++ } ++ ++ /* normal completion */ ++ if (req->actual != sizeof(acm->port_line_coding)) { ++ DBG(cdev, "acm ttyGS%d short resp, len %d\n", ++ acm->port_num, req->actual); ++ usb_ep_set_halt(ep); ++ } else { ++ struct usb_cdc_line_coding *value = req->buf; ++ ++ /* REVISIT: we currently just remember this data. ++ * If we change that, (a) validate it first, then ++ * (b) update whatever hardware needs updating, ++ * (c) worry about locking. This is information on ++ * the order of 9600-8-N-1 ... most of which means ++ * nothing unless we control a real RS232 line. ++ */ ++ acm->port_line_coding = *value; ++ } ++} ++ ++static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) ++{ ++ struct f_acm *acm = func_to_acm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything except ++ * CDC class messages; interface activation uses set_alt(). ++ */ ++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { ++ ++ /* SET_LINE_CODING ... just read and save what the host sends */ ++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_REQ_SET_LINE_CODING: ++ if (w_length != sizeof(struct usb_cdc_line_coding) ++ || w_index != acm->ctrl_id) ++ goto invalid; ++ ++ value = w_length; ++ cdev->gadget->ep0->driver_data = acm; ++ req->complete = acm_complete_set_line_coding; ++ break; ++ ++ /* GET_LINE_CODING ... return what host sent, or initial value */ ++ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_REQ_GET_LINE_CODING: ++ if (w_index != acm->ctrl_id) ++ goto invalid; ++ ++ value = min_t(unsigned, w_length, ++ sizeof(struct usb_cdc_line_coding)); ++ memcpy(req->buf, &acm->port_line_coding, value); ++ break; ++ ++ /* SET_CONTROL_LINE_STATE ... save what the host sent */ ++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_REQ_SET_CONTROL_LINE_STATE: ++ if (w_index != acm->ctrl_id) ++ goto invalid; ++ ++ value = 0; ++ ++ /* FIXME we should not allow data to flow until the ++ * host sets the RS232_DTR bit; and when it clears ++ * that bit, we should return to that no-flow state. ++ */ ++ acm->port_handshake_bits = w_value; ++ break; ++ ++ default: ++invalid: ++ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", ++ acm->port_num, ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "acm response on ttyGS%d, err %d\n", ++ acm->port_num, value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_acm *acm = func_to_acm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt == 0, so this is an activation or a reset */ ++ ++ /* REVISIT hardware on PXA handles SET_INTERFACE; ++ * this is probably doing resets wrong ... ++ */ ++ ++ if (intf == acm->ctrl_id) { ++ /* REVISIT this may need more work when we start to ++ * send notifications ... ++ */ ++ if (acm->notify->driver_data) { ++ VDBG(cdev, "reset acm control interface %d\n", intf); ++ usb_ep_disable(acm->notify); ++ } else { ++ VDBG(cdev, "init acm ctrl interface %d\n", intf); ++ acm->notify_desc = ep_choose(cdev->gadget, ++ acm->hs.notify, ++ acm->fs.notify); ++ } ++ usb_ep_enable(acm->notify, acm->notify_desc); ++ acm->notify->driver_data = acm; ++ ++ } else if (intf == acm->data_id) { ++ if (acm->port.in->driver_data) { ++ DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); ++ gserial_disconnect(&acm->port); ++ } else { ++ DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); ++ acm->port.in_desc = ep_choose(cdev->gadget, ++ acm->hs.in, acm->fs.in); ++ acm->port.out_desc = ep_choose(cdev->gadget, ++ acm->hs.out, acm->fs.out); ++ } ++ gserial_connect(&acm->port, acm->port_num); ++ ++ } else ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static void acm_disable(struct usb_function *f) ++{ ++ struct f_acm *acm = func_to_acm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); ++ gserial_disconnect(&acm->port); ++ usb_ep_disable(acm->notify); ++ acm->notify->driver_data = NULL; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ACM function driver setup/binding */ ++static int __init ++acm_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_acm *acm = func_to_acm(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs, and patch descriptors */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ acm->ctrl_id = status; ++ ++ acm_control_interface_desc.bInterfaceNumber = status; ++ acm_union_desc .bMasterInterface0 = status; ++ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ acm->data_id = status; ++ ++ acm_data_interface_desc.bInterfaceNumber = status; ++ acm_union_desc.bSlaveInterface0 = status; ++ acm_call_mgmt_descriptor.bDataInterface = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); ++ if (!ep) ++ goto fail; ++ acm->port.in = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); ++ if (!ep) ++ goto fail; ++ acm->port.out = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); ++ if (!ep) ++ goto fail; ++ acm->notify = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(acm_fs_function); ++ ++ acm->fs.in = usb_find_endpoint(acm_fs_function, ++ f->descriptors, &acm_fs_in_desc); ++ acm->fs.out = usb_find_endpoint(acm_fs_function, ++ f->descriptors, &acm_fs_out_desc); ++ acm->fs.notify = usb_find_endpoint(acm_fs_function, ++ f->descriptors, &acm_fs_notify_desc); ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ acm_hs_in_desc.bEndpointAddress = ++ acm_fs_in_desc.bEndpointAddress; ++ acm_hs_out_desc.bEndpointAddress = ++ acm_fs_out_desc.bEndpointAddress; ++ acm_hs_notify_desc.bEndpointAddress = ++ acm_fs_notify_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(acm_hs_function); ++ ++ acm->hs.in = usb_find_endpoint(acm_hs_function, ++ f->hs_descriptors, &acm_hs_in_desc); ++ acm->hs.out = usb_find_endpoint(acm_hs_function, ++ f->hs_descriptors, &acm_hs_out_desc); ++ acm->hs.notify = usb_find_endpoint(acm_hs_function, ++ f->hs_descriptors, &acm_hs_notify_desc); ++ } ++ ++ /* FIXME provide a callback for triggering notifications */ ++ ++ DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", ++ acm->port_num, ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ acm->port.in->name, acm->port.out->name, ++ acm->notify->name); ++ return 0; ++ ++fail: ++ /* we might as well release our claims on endpoints */ ++ if (acm->notify) ++ acm->notify->driver_data = NULL; ++ if (acm->port.out) ++ acm->port.out->driver_data = NULL; ++ if (acm->port.in) ++ acm->port.in->driver_data = NULL; ++ ++ ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); ++ ++ return status; ++} ++ ++static void ++acm_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ kfree(func_to_acm(f)); ++} ++ ++/* Some controllers can't support CDC ACM ... */ ++static inline bool can_support_cdc(struct usb_configuration *c) ++{ ++ /* SH3 doesn't support multiple interfaces */ ++ if (gadget_is_sh(c->cdev->gadget)) ++ return false; ++ ++ /* sa1100 doesn't have a third interrupt endpoint */ ++ if (gadget_is_sa1100(c->cdev->gadget)) ++ return false; ++ ++ /* everything else is *probably* fine ... */ ++ return true; ++} ++ ++/** ++ * acm_bind_config - add a CDC ACM function to a configuration ++ * @c: the configuration to support the CDC ACM instance ++ * @port_num: /dev/ttyGS* port this interface will use ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gserial_setup() with enough ports to ++ * handle all the ones it binds. Caller is also responsible ++ * for calling @gserial_cleanup() before module unload. ++ */ ++int __init acm_bind_config(struct usb_configuration *c, u8 port_num) ++{ ++ struct f_acm *acm; ++ int status; ++ ++ if (!can_support_cdc(c)) ++ return -EINVAL; ++ ++ /* REVISIT might want instance-specific strings to help ++ * distinguish instances ... ++ */ ++ ++ /* maybe allocate device-global string IDs, and patch descriptors */ ++ if (acm_string_defs[ACM_CTRL_IDX].id == 0) { ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ acm_string_defs[ACM_CTRL_IDX].id = status; ++ ++ acm_control_interface_desc.iInterface = status; ++ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ acm_string_defs[ACM_DATA_IDX].id = status; ++ ++ acm_data_interface_desc.iInterface = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ acm = kzalloc(sizeof *acm, GFP_KERNEL); ++ if (!acm) ++ return -ENOMEM; ++ ++ acm->port_num = port_num; ++ ++ acm->port.func.name = "acm"; ++ acm->port.func.strings = acm_strings; ++ /* descriptors are per-instance copies */ ++ acm->port.func.bind = acm_bind; ++ acm->port.func.unbind = acm_unbind; ++ acm->port.func.set_alt = acm_set_alt; ++ acm->port.func.setup = acm_setup; ++ acm->port.func.disable = acm_disable; ++ ++ status = usb_add_function(c, &acm->port.func); ++ if (status) ++ kfree(acm); ++ return status; ++} +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -26,7 +26,7 @@ C_UTILS = composite.o usbstring.o config + + g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) + g_ether-objs := ether.o usbstring.o config.o epautoconf.o +-g_serial-objs := serial.o u_serial.o usbstring.o config.o epautoconf.o ++g_serial-objs := serial.o u_serial.o f_acm.o $(C_UTILS) + g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o + gadgetfs-objs := inode.o + g_file_storage-objs := file_storage.o usbstring.o config.o \ +--- a/drivers/usb/gadget/u_serial.h ++++ b/drivers/usb/gadget/u_serial.h +@@ -1,10 +1,7 @@ + #ifndef __U_SERIAL_H + #define __U_SERIAL_H + +-/* #include <linux/usb/composite.h> */ +-#include <linux/usb/ch9.h> +-#include <linux/usb/gadget.h> +- ++#include <linux/usb/composite.h> + #include <linux/usb/cdc.h> + + /* +@@ -21,7 +18,7 @@ + * REVISIT someday, allow multiplexing several TTYs over these endpoints. + */ + struct gserial { +- /* struct usb_function func; */ ++ struct usb_function func; + + /* port is managed by gserial_{connect,disconnect} */ + struct gs_port *ioport; +@@ -48,4 +45,7 @@ void gserial_cleanup(void); + int gserial_connect(struct gserial *, u8 port_num); + void gserial_disconnect(struct gserial *); + ++/* functions are bound to configurations by a config or gadget driver */ ++int acm_bind_config(struct usb_configuration *c, u8 port_num); ++ + #endif /* __U_SERIAL_H */ diff --git a/usb/usb-serial-gadget-generic-serial-function-driver.patch b/usb/usb-serial-gadget-generic-serial-function-driver.patch new file mode 100644 index 00000000000000..3aa638262d159e --- /dev/null +++ b/usb/usb-serial-gadget-generic-serial-function-driver.patch @@ -0,0 +1,354 @@ +From david-b@pacbell.net Fri Jun 6 15:11:41 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 20 May 2008 11:11:31 -0700 +Subject: usb serial gadget: generic serial function driver +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> +Message-ID: <200805201111.31815.david-b@pacbell.net> +Content-Disposition: inline + + +Split out the generic serial support into a "function driver". This +closely mimics the ACM support, but with a MUCH simpler control model. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/DocBook/gadget.tmpl | 1 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_serial.c | 298 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/u_serial.h | 1 + 4 files changed, 301 insertions(+), 1 deletion(-) + +--- a/Documentation/DocBook/gadget.tmpl ++++ b/Documentation/DocBook/gadget.tmpl +@@ -557,6 +557,7 @@ Near-term plans include converting all o + </para> + + !Edrivers/usb/gadget/f_acm.c ++!Edrivers/usb/gadget/f_serial.c + + </sect1> + +--- /dev/null ++++ b/drivers/usb/gadget/f_serial.c +@@ -0,0 +1,298 @@ ++/* ++ * f_serial.c - generic USB serial function ++ * ++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) ++ * Copyright (C) 2008 by David Brownell ++ * ++ * This software is distributed under the terms of the GNU General ++ * Public License ("GPL") as published by the Free Software Foundation, ++ * either version 2 of that License or (at your option) any later version. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++ ++#include "u_serial.h" ++#include "gadget_chips.h" ++ ++ ++/* ++ * This function packages a simple "generic serial" port with no real ++ * control mechanisms, just raw data transfer over two bulk endpoints. ++ * ++ * Because it's not standardized, this isn't as interoperable as the ++ * CDC ACM driver. However, for many purposes it's just as functional ++ * if you can arrange appropriate host side drivers. ++ */ ++ ++struct gser_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++}; ++ ++struct f_gser { ++ struct gserial port; ++ u8 data_id; ++ u8 port_num; ++ ++ struct usb_descriptor_header **fs_function; ++ struct gser_descs fs; ++ struct usb_descriptor_header **hs_function; ++ struct gser_descs hs; ++}; ++ ++static inline struct f_gser *func_to_gser(struct usb_function *f) ++{ ++ return container_of(f, struct f_gser, port.func); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* interface descriptor: */ ++ ++static struct usb_interface_descriptor gser_interface_desc __initdata = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *gser_fs_function[] __initdata = { ++ (struct usb_descriptor_header *) &gser_interface_desc, ++ (struct usb_descriptor_header *) &gser_fs_in_desc, ++ (struct usb_descriptor_header *) &gser_fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *gser_hs_function[] __initdata = { ++ (struct usb_descriptor_header *) &gser_interface_desc, ++ (struct usb_descriptor_header *) &gser_hs_in_desc, ++ (struct usb_descriptor_header *) &gser_hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++static struct usb_string gser_string_defs[] = { ++ [0].s = "Generic Serial", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings gser_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = gser_string_defs, ++}; ++ ++static struct usb_gadget_strings *gser_strings[] = { ++ &gser_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_gser *gser = func_to_gser(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt == 0, so this is an activation or a reset */ ++ ++ /* REVISIT hardware on PXA handles SET_INTERFACE; ++ * this is probably doing resets wrong ... ++ */ ++ if (gser->port.in->driver_data) { ++ DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); ++ gserial_disconnect(&gser->port); ++ } else { ++ DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); ++ gser->port.in_desc = ep_choose(cdev->gadget, ++ gser->hs.in, gser->fs.in); ++ gser->port.out_desc = ep_choose(cdev->gadget, ++ gser->hs.out, gser->fs.out); ++ } ++ gserial_connect(&gser->port, gser->port_num); ++ return 0; ++} ++ ++static void gser_disable(struct usb_function *f) ++{ ++ struct f_gser *gser = func_to_gser(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); ++ gserial_disconnect(&gser->port); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* serial function driver setup/binding */ ++ ++static int __init ++gser_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_gser *gser = func_to_gser(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ gser->data_id = status; ++ gser_interface_desc.bInterfaceNumber = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc); ++ if (!ep) ++ goto fail; ++ gser->port.in = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); ++ if (!ep) ++ goto fail; ++ gser->port.out = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(gser_fs_function); ++ ++ gser->fs.in = usb_find_endpoint(gser_fs_function, ++ f->descriptors, &gser_fs_in_desc); ++ gser->fs.out = usb_find_endpoint(gser_fs_function, ++ f->descriptors, &gser_fs_out_desc); ++ ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ gser_hs_in_desc.bEndpointAddress = ++ gser_fs_in_desc.bEndpointAddress; ++ gser_hs_out_desc.bEndpointAddress = ++ gser_fs_out_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(gser_hs_function); ++ ++ gser->hs.in = usb_find_endpoint(gser_hs_function, ++ f->hs_descriptors, &gser_hs_in_desc); ++ gser->hs.out = usb_find_endpoint(gser_hs_function, ++ f->hs_descriptors, &gser_hs_out_desc); ++ } ++ ++ DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", ++ gser->port_num, ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ gser->port.in->name, gser->port.out->name); ++ return 0; ++ ++fail: ++ /* we might as well release our claims on endpoints */ ++ if (gser->port.out) ++ gser->port.out->driver_data = NULL; ++ if (gser->port.in) ++ gser->port.in->driver_data = NULL; ++ ++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); ++ ++ return status; ++} ++ ++static void ++gser_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ kfree(func_to_gser(f)); ++} ++ ++/** ++ * gser_bind_config - add a generic serial function to a configuration ++ * @c: the configuration to support the serial instance ++ * @port_num: /dev/ttyGS* port this interface will use ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gserial_setup() with enough ports to ++ * handle all the ones it binds. Caller is also responsible ++ * for calling @gserial_cleanup() before module unload. ++ */ ++int __init gser_bind_config(struct usb_configuration *c, u8 port_num) ++{ ++ struct f_gser *gser; ++ int status; ++ ++ /* REVISIT might want instance-specific strings to help ++ * distinguish instances ... ++ */ ++ ++ /* maybe allocate device-global string ID */ ++ if (gser_string_defs[0].id == 0) { ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ gser_string_defs[0].id = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ gser = kzalloc(sizeof *gser, GFP_KERNEL); ++ if (!gser) ++ return -ENOMEM; ++ ++ gser->port_num = port_num; ++ ++ gser->port.func.name = "gser"; ++ gser->port.func.strings = gser_strings; ++ gser->port.func.bind = gser_bind; ++ gser->port.func.unbind = gser_unbind; ++ gser->port.func.set_alt = gser_set_alt; ++ gser->port.func.disable = gser_disable; ++ ++ status = usb_add_function(c, &gser->port.func); ++ if (status) ++ kfree(gser); ++ return status; ++} +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -26,7 +26,7 @@ C_UTILS = composite.o usbstring.o config + + g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) + g_ether-objs := ether.o usbstring.o config.o epautoconf.o +-g_serial-objs := serial.o u_serial.o f_acm.o $(C_UTILS) ++g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS) + g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o + gadgetfs-objs := inode.o + g_file_storage-objs := file_storage.o usbstring.o config.o \ +--- a/drivers/usb/gadget/u_serial.h ++++ b/drivers/usb/gadget/u_serial.h +@@ -47,5 +47,6 @@ void gserial_disconnect(struct gserial * + + /* functions are bound to configurations by a config or gadget driver */ + int acm_bind_config(struct usb_configuration *c, u8 port_num); ++int gser_bind_config(struct usb_configuration *c, u8 port_num); + + #endif /* __U_SERIAL_H */ diff --git a/usb/usb-serial-gadget-use-updated-framework.patch b/usb/usb-serial-gadget-use-updated-framework.patch new file mode 100644 index 00000000000000..e92e015abf1e6e --- /dev/null +++ b/usb/usb-serial-gadget-use-updated-framework.patch @@ -0,0 +1,1205 @@ +From david-b@pacbell.net Fri Jun 6 15:11:53 2008 +From: David Brownell <david-b@pacbell.net> +Date: Tue, 20 May 2008 11:21:06 -0700 +Subject: usb serial gadget: use updated framework +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> +Message-ID: <200805201121.06576.david-b@pacbell.net> +Content-Disposition: inline + + +This switches the serial gadget over to using the new "function" +versions of the serial port interfacing code. The remaining code +in this module is quite small... + +Note one new feature added by this patch: a new module parameter +saying how many instances to create, defaulting to "one". So for +hardware which allows it, you can "modprobe g_serial n_ports=2" +(or more) to get two ACM ports. + +Also, ACM is the (more interoperable) default now, with the use_acm +parameter switched to be a boolean. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/serial.c | 1073 +++++------------------------------------- + drivers/usb/gadget/u_serial.h | 5 + 2 files changed, 144 insertions(+), 934 deletions(-) + +--- a/drivers/usb/gadget/serial.c ++++ b/drivers/usb/gadget/serial.c +@@ -21,314 +21,78 @@ + + /* Defines */ + +-#define GS_VERSION_STR "v2.3" +-#define GS_VERSION_NUM 0x2300 ++#define GS_VERSION_STR "v2.4" ++#define GS_VERSION_NUM 0x2400 + + #define GS_LONG_NAME "Gadget Serial" +-#define GS_SHORT_NAME "g_serial" +- + #define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR + +- +-/* REVISIT only one port is supported for now; +- * see gs_{send,recv}_packet() ... no multiplexing, +- * and no support for multiple ACM devices. +- */ +-#define GS_NUM_PORTS 1 +- +-#define GS_NUM_CONFIGS 1 +-#define GS_NO_CONFIG_ID 0 +-#define GS_BULK_CONFIG_ID 1 +-#define GS_ACM_CONFIG_ID 2 +- +-#define GS_MAX_NUM_INTERFACES 2 +-#define GS_BULK_INTERFACE_ID 0 +-#define GS_CONTROL_INTERFACE_ID 0 +-#define GS_DATA_INTERFACE_ID 1 +- +-#define GS_MAX_DESC_LEN 256 +- +-#define GS_DEFAULT_USE_ACM 0 +- +- +-/* maxpacket and other transfer characteristics vary by speed. */ +-static inline struct usb_endpoint_descriptor * +-choose_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, +- struct usb_endpoint_descriptor *fs) +-{ +- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) +- return hs; +- return fs; +-} +- ++/*-------------------------------------------------------------------------*/ + + /* Thanks to NetChip Technologies for donating this product ID. +- * +- * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! +- * Instead: allocate your own, using normal USB-IF procedures. +- */ ++* ++* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! ++* Instead: allocate your own, using normal USB-IF procedures. ++*/ + #define GS_VENDOR_ID 0x0525 /* NetChip */ + #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ + #define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ + +-#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ +-#define GS_NOTIFY_MAXPACKET 8 +- +-/* the device structure holds info for the USB device */ +-struct gs_dev { +- struct usb_gadget *dev_gadget; /* gadget device pointer */ +- spinlock_t dev_lock; /* lock for set/reset config */ +- int dev_config; /* configuration number */ +- struct usb_request *dev_ctrl_req; /* control request */ +- +- struct gserial gser; /* serial/tty port */ +-}; +- +- +-/* Functions */ ++/* string IDs are assigned dynamically */ + +-/* gadget driver internals */ +-static int gs_set_config(struct gs_dev *dev, unsigned config); +-static void gs_reset_config(struct gs_dev *dev); +-static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, +- u8 type, unsigned int index, int is_otg); +- +-static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, +- gfp_t kmalloc_flags); +-static void gs_free_req(struct usb_ep *ep, struct usb_request *req); ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++#define STRING_DESCRIPTION_IDX 2 + +-/*-------------------------------------------------------------------------*/ +- +-/* USB descriptors */ +- +-#define GS_MANUFACTURER_STR_ID 1 +-#define GS_PRODUCT_STR_ID 2 +-#define GS_SERIAL_STR_ID 3 +-#define GS_BULK_CONFIG_STR_ID 4 +-#define GS_ACM_CONFIG_STR_ID 5 +-#define GS_CONTROL_STR_ID 6 +-#define GS_DATA_STR_ID 7 +- +-/* static strings, in UTF-8 */ + static char manufacturer[50]; +-static struct usb_string gs_strings[] = { +- { GS_MANUFACTURER_STR_ID, manufacturer }, +- { GS_PRODUCT_STR_ID, GS_VERSION_NAME }, +- { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" }, +- { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" }, +- { GS_CONTROL_STR_ID, "Gadget Serial Control" }, +- { GS_DATA_STR_ID, "Gadget Serial Data" }, ++ ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = GS_VERSION_NAME, ++ [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, + { } /* end of list */ + }; + +-static struct usb_gadget_strings gs_string_table = { +- .language = 0x0409, /* en-us */ +- .strings = gs_strings, ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, + }; + +-static struct usb_device_descriptor gs_device_desc = { ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; ++ ++static struct usb_device_descriptor device_desc = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), ++ /* .bDeviceClass = f(use_acm) */ + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, ++ /* .bMaxPacketSize0 = f(hardware) */ + .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID), +- .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), +- .iManufacturer = GS_MANUFACTURER_STR_ID, +- .iProduct = GS_PRODUCT_STR_ID, +- .bNumConfigurations = GS_NUM_CONFIGS, ++ /* .idProduct = f(use_acm) */ ++ /* .iManufacturer = DYNAMIC */ ++ /* .iProduct = DYNAMIC */ ++ .bNumConfigurations = 1, + }; + +-static struct usb_otg_descriptor gs_otg_descriptor = { +- .bLength = sizeof(gs_otg_descriptor), ++static struct usb_otg_descriptor otg_descriptor = { ++ .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, +- .bmAttributes = USB_OTG_SRP, +-}; +- +-static struct usb_config_descriptor gs_bulk_config_desc = { +- .bLength = USB_DT_CONFIG_SIZE, +- .bDescriptorType = USB_DT_CONFIG, +- /* .wTotalLength computed dynamically */ +- .bNumInterfaces = 1, +- .bConfigurationValue = GS_BULK_CONFIG_ID, +- .iConfiguration = GS_BULK_CONFIG_STR_ID, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 1, +-}; +- +-static struct usb_config_descriptor gs_acm_config_desc = { +- .bLength = USB_DT_CONFIG_SIZE, +- .bDescriptorType = USB_DT_CONFIG, +- /* .wTotalLength computed dynamically */ +- .bNumInterfaces = 2, +- .bConfigurationValue = GS_ACM_CONFIG_ID, +- .iConfiguration = GS_ACM_CONFIG_STR_ID, +- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +- .bMaxPower = 1, +-}; +- +-static const struct usb_interface_descriptor gs_bulk_interface_desc = { +- .bLength = USB_DT_INTERFACE_SIZE, +- .bDescriptorType = USB_DT_INTERFACE, +- .bInterfaceNumber = GS_BULK_INTERFACE_ID, +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_VENDOR_SPEC, +- .bInterfaceSubClass = 0, +- .bInterfaceProtocol = 0, +- .iInterface = GS_DATA_STR_ID, +-}; +- +-static const struct usb_interface_descriptor gs_control_interface_desc = { +- .bLength = USB_DT_INTERFACE_SIZE, +- .bDescriptorType = USB_DT_INTERFACE, +- .bInterfaceNumber = GS_CONTROL_INTERFACE_ID, +- .bNumEndpoints = 1, +- .bInterfaceClass = USB_CLASS_COMM, +- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, +- .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, +- .iInterface = GS_CONTROL_STR_ID, +-}; +- +-static const struct usb_interface_descriptor gs_data_interface_desc = { +- .bLength = USB_DT_INTERFACE_SIZE, +- .bDescriptorType = USB_DT_INTERFACE, +- .bInterfaceNumber = GS_DATA_INTERFACE_ID, +- .bNumEndpoints = 2, +- .bInterfaceClass = USB_CLASS_CDC_DATA, +- .bInterfaceSubClass = 0, +- .bInterfaceProtocol = 0, +- .iInterface = GS_DATA_STR_ID, +-}; +- +-static const struct usb_cdc_header_desc gs_header_desc = { +- .bLength = sizeof(gs_header_desc), +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_HEADER_TYPE, +- .bcdCDC = __constant_cpu_to_le16(0x0110), +-}; +- +-static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = { +- .bLength = sizeof(gs_call_mgmt_descriptor), +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, +- .bmCapabilities = 0, +- .bDataInterface = 1, /* index of data interface */ +-}; +- +-static struct usb_cdc_acm_descriptor gs_acm_descriptor = { +- .bLength = sizeof(gs_acm_descriptor), +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_ACM_TYPE, +- .bmCapabilities = (1 << 1), +-}; +- +-static const struct usb_cdc_union_desc gs_union_desc = { +- .bLength = sizeof(gs_union_desc), +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = USB_CDC_UNION_TYPE, +- .bMasterInterface0 = 0, /* index of control interface */ +- .bSlaveInterface0 = 1, /* index of data interface */ +-}; +- +-static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), +- .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, +-}; +- +-static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; + +-static struct usb_endpoint_descriptor gs_fullspeed_out_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bEndpointAddress = USB_DIR_OUT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +-}; +- +-static const struct usb_descriptor_header *gs_bulk_fullspeed_function[] = { +- (struct usb_descriptor_header *) &gs_otg_descriptor, +- (struct usb_descriptor_header *) &gs_bulk_interface_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_in_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_out_desc, +- NULL, +-}; +- +-static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = { +- (struct usb_descriptor_header *) &gs_otg_descriptor, +- (struct usb_descriptor_header *) &gs_control_interface_desc, +- (struct usb_descriptor_header *) &gs_header_desc, +- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, +- (struct usb_descriptor_header *) &gs_acm_descriptor, +- (struct usb_descriptor_header *) &gs_union_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_notify_desc, +- (struct usb_descriptor_header *) &gs_data_interface_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_in_desc, +- (struct usb_descriptor_header *) &gs_fullspeed_out_desc, +- NULL, +-}; +- +-static struct usb_endpoint_descriptor gs_highspeed_notify_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bEndpointAddress = USB_DIR_IN, +- .bmAttributes = USB_ENDPOINT_XFER_INT, +- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), +- .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, +-}; +- +-static struct usb_endpoint_descriptor gs_highspeed_in_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16(512), +-}; +- +-static struct usb_endpoint_descriptor gs_highspeed_out_desc = { +- .bLength = USB_DT_ENDPOINT_SIZE, +- .bDescriptorType = USB_DT_ENDPOINT, +- .bmAttributes = USB_ENDPOINT_XFER_BULK, +- .wMaxPacketSize = __constant_cpu_to_le16(512), +-}; +- +-static struct usb_qualifier_descriptor gs_qualifier_desc = { +- .bLength = sizeof(struct usb_qualifier_descriptor), +- .bDescriptorType = USB_DT_DEVICE_QUALIFIER, +- .bcdUSB = __constant_cpu_to_le16 (0x0200), +- /* assumes ep0 uses the same value for both speeds ... */ +- .bNumConfigurations = GS_NUM_CONFIGS, +-}; +- +-static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = { +- (struct usb_descriptor_header *) &gs_otg_descriptor, +- (struct usb_descriptor_header *) &gs_bulk_interface_desc, +- (struct usb_descriptor_header *) &gs_highspeed_in_desc, +- (struct usb_descriptor_header *) &gs_highspeed_out_desc, +- NULL, ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, + }; + +-static const struct usb_descriptor_header *gs_acm_highspeed_function[] = { +- (struct usb_descriptor_header *) &gs_otg_descriptor, +- (struct usb_descriptor_header *) &gs_control_interface_desc, +- (struct usb_descriptor_header *) &gs_header_desc, +- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, +- (struct usb_descriptor_header *) &gs_acm_descriptor, +- (struct usb_descriptor_header *) &gs_union_desc, +- (struct usb_descriptor_header *) &gs_highspeed_notify_desc, +- (struct usb_descriptor_header *) &gs_data_interface_desc, +- (struct usb_descriptor_header *) &gs_highspeed_in_desc, +- (struct usb_descriptor_header *) &gs_highspeed_out_desc, ++static const struct usb_descriptor_header *otg_desc[] = { ++ (struct usb_descriptor_header *) &otg_descriptor, + NULL, + }; + +- + /*-------------------------------------------------------------------------*/ + + /* Module */ +@@ -337,699 +101,150 @@ MODULE_AUTHOR("Al Borchers"); + MODULE_AUTHOR("David Brownell"); + MODULE_LICENSE("GPL"); + +-static unsigned int use_acm = GS_DEFAULT_USE_ACM; +-module_param(use_acm, uint, S_IRUGO); +-MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no"); ++static int use_acm = true; ++module_param(use_acm, bool, 0); ++MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); ++ ++static unsigned n_ports = 1; ++module_param(n_ports, uint, 0); ++MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); + + /*-------------------------------------------------------------------------*/ + +-/* Gadget Driver */ +- +-/* +- * gs_unbind +- * +- * Called on module unload. Frees the control request and device +- * structure. +- */ +-static void __exit gs_unbind(struct usb_gadget *gadget) ++static int __init serial_bind_config(struct usb_configuration *c) + { +- struct gs_dev *dev = get_gadget_data(gadget); ++ unsigned i; ++ int status = 0; + +- /* read/write requests already freed, only control request remains */ +- if (dev != NULL) { +- if (dev->dev_ctrl_req != NULL) { +- gs_free_req(gadget->ep0, dev->dev_ctrl_req); +- dev->dev_ctrl_req = NULL; +- } +- gs_reset_config(dev); +- kfree(dev); +- set_gadget_data(gadget, NULL); ++ for (i = 0; i < n_ports && status == 0; i++) { ++ if (use_acm) ++ status = acm_bind_config(c, i); ++ else ++ status = gser_bind_config(c, i); + } +- +- pr_info("gs_unbind: %s unbound\n", GS_VERSION_NAME); +- +- gserial_cleanup(); ++ return status; + } + +-/* +- * gs_bind +- * +- * Called on module load. Allocates and initializes the device +- * structure and a control request. +- */ +-static int __init gs_bind(struct usb_gadget *gadget) +-{ +- int ret; +- struct usb_ep *ep; +- struct gs_dev *dev; +- int gcnum; +- +- ret = gserial_setup(gadget, GS_NUM_PORTS); +- if (ret < 0) +- return ret; +- +- /* Some controllers can't support CDC ACM: +- * - sh doesn't support multiple interfaces or configs; +- * - sa1100 doesn't have a third interrupt endpoint +- */ +- if (gadget_is_sh(gadget) || gadget_is_sa1100(gadget)) +- use_acm = 0; +- +- gcnum = usb_gadget_controller_number(gadget); +- if (gcnum >= 0) +- gs_device_desc.bcdDevice = +- cpu_to_le16(GS_VERSION_NUM | gcnum); +- else { +- pr_warning("gs_bind: controller '%s' not recognized\n", +- gadget->name); +- /* unrecognized, but safe unless bulk is REALLY quirky */ +- gs_device_desc.bcdDevice = +- __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); +- } +- +- dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); +- if (dev == NULL) { +- ret = -ENOMEM; +- goto autoconf_fail; +- } +- +- usb_ep_autoconfig_reset(gadget); +- ret = -ENXIO; +- +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); +- if (!ep) +- goto autoconf_fail; +- dev->gser.in = ep; +- ep->driver_data = dev; /* claim the endpoint */ +- +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); +- if (!ep) +- goto autoconf_fail; +- dev->gser.out = ep; +- ep->driver_data = dev; /* claim the endpoint */ +- +- if (use_acm) { +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); +- if (!ep) { +- pr_err("gs_bind: cannot run ACM on %s\n", gadget->name); +- goto autoconf_fail; +- } +- gs_device_desc.idProduct = __constant_cpu_to_le16( +- GS_CDC_PRODUCT_ID), +- dev->gser.notify = ep; +- ep->driver_data = dev; /* claim the endpoint */ +- } ++static struct usb_configuration serial_config_driver = { ++ /* .label = f(use_acm) */ ++ .bind = serial_bind_config, ++ /* .bConfigurationValue = f(use_acm) */ ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++}; + +- gs_device_desc.bDeviceClass = use_acm +- ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; +- gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; +- +- if (gadget_is_dualspeed(gadget)) { +- gs_qualifier_desc.bDeviceClass = use_acm +- ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; +- /* assume ep0 uses the same packet size for both speeds */ +- gs_qualifier_desc.bMaxPacketSize0 = +- gs_device_desc.bMaxPacketSize0; +- /* assume endpoints are dual-speed */ +- gs_highspeed_notify_desc.bEndpointAddress = +- gs_fullspeed_notify_desc.bEndpointAddress; +- gs_highspeed_in_desc.bEndpointAddress = +- gs_fullspeed_in_desc.bEndpointAddress; +- gs_highspeed_out_desc.bEndpointAddress = +- gs_fullspeed_out_desc.bEndpointAddress; +- } ++static int __init gs_bind(struct usb_composite_dev *cdev) ++{ ++ int gcnum; ++ struct usb_gadget *gadget = cdev->gadget; ++ int status; + +- usb_gadget_set_selfpowered(gadget); ++ status = gserial_setup(cdev->gadget, n_ports); ++ if (status < 0) ++ return status; + +- if (gadget_is_otg(gadget)) { +- gs_otg_descriptor.bmAttributes |= USB_OTG_HNP, +- gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +- } ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. ++ */ + +- snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", ++ /* device description: manufacturer, product */ ++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail; ++ strings_dev[STRING_MANUFACTURER_IDX].id = status; + +- dev->dev_gadget = gadget; +- spin_lock_init(&dev->dev_lock); +- set_gadget_data(gadget, dev); +- +- /* preallocate control response and buffer */ +- dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN, +- GFP_KERNEL); +- if (dev->dev_ctrl_req == NULL) { +- ret = -ENOMEM; +- goto autoconf_fail; +- } +- gadget->ep0->driver_data = dev; ++ device_desc.iManufacturer = status; + +- pr_info("gs_bind: %s bound\n", GS_VERSION_NAME); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail; ++ strings_dev[STRING_PRODUCT_IDX].id = status; + +- return 0; ++ device_desc.iProduct = status; + +-autoconf_fail: +- kfree(dev); +- gserial_cleanup(); +- pr_err("gs_bind: to %s, err %d\n", gadget->name, ret); +- return ret; +-} ++ /* config description */ ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail; ++ strings_dev[STRING_DESCRIPTION_IDX].id = status; + +-static int gs_setup_standard(struct usb_gadget *gadget, +- const struct usb_ctrlrequest *ctrl) +-{ +- int ret = -EOPNOTSUPP; +- struct gs_dev *dev = get_gadget_data(gadget); +- struct usb_request *req = dev->dev_ctrl_req; +- u16 wIndex = le16_to_cpu(ctrl->wIndex); +- u16 wValue = le16_to_cpu(ctrl->wValue); +- u16 wLength = le16_to_cpu(ctrl->wLength); +- +- switch (ctrl->bRequest) { +- case USB_REQ_GET_DESCRIPTOR: +- if (ctrl->bRequestType != USB_DIR_IN) +- break; +- +- switch (wValue >> 8) { +- case USB_DT_DEVICE: +- ret = min(wLength, +- (u16)sizeof(struct usb_device_descriptor)); +- memcpy(req->buf, &gs_device_desc, ret); +- break; +- +- case USB_DT_DEVICE_QUALIFIER: +- if (!gadget_is_dualspeed(gadget)) +- break; +- ret = min(wLength, +- (u16)sizeof(struct usb_qualifier_descriptor)); +- memcpy(req->buf, &gs_qualifier_desc, ret); +- break; +- +- case USB_DT_OTHER_SPEED_CONFIG: +- if (!gadget_is_dualspeed(gadget)) +- break; +- /* fall through */ +- case USB_DT_CONFIG: +- ret = gs_build_config_buf(req->buf, gadget, +- wValue >> 8, wValue & 0xff, +- gadget_is_otg(gadget)); +- if (ret >= 0) +- ret = min(wLength, (u16)ret); +- break; +- +- case USB_DT_STRING: +- /* wIndex == language code. */ +- ret = usb_gadget_get_string(&gs_string_table, +- wValue & 0xff, req->buf); +- if (ret >= 0) +- ret = min(wLength, (u16)ret); +- break; +- } +- break; +- +- case USB_REQ_SET_CONFIGURATION: +- if (ctrl->bRequestType != 0) +- break; +- spin_lock(&dev->dev_lock); +- ret = gs_set_config(dev, wValue); +- spin_unlock(&dev->dev_lock); +- break; +- +- case USB_REQ_GET_CONFIGURATION: +- if (ctrl->bRequestType != USB_DIR_IN) +- break; +- *(u8 *)req->buf = dev->dev_config; +- ret = min(wLength, (u16)1); +- break; +- +- case USB_REQ_SET_INTERFACE: +- if (ctrl->bRequestType != USB_RECIP_INTERFACE +- || !dev->dev_config +- || wIndex >= GS_MAX_NUM_INTERFACES) +- break; +- if (dev->dev_config == GS_BULK_CONFIG_ID +- && wIndex != GS_BULK_INTERFACE_ID) +- break; +- /* no alternate interface settings */ +- if (wValue != 0) +- break; +- spin_lock(&dev->dev_lock); +- /* PXA hardware partially handles SET_INTERFACE; +- * we need to kluge around that interference. */ +- if (gadget_is_pxa(gadget)) { +- ret = gs_set_config(dev, use_acm ? +- GS_ACM_CONFIG_ID : GS_BULK_CONFIG_ID); +- goto set_interface_done; +- } +- if (dev->dev_config != GS_BULK_CONFIG_ID +- && wIndex == GS_CONTROL_INTERFACE_ID) { +- if (dev->gser.notify) { +- usb_ep_disable(dev->gser.notify); +- usb_ep_enable(dev->gser.notify, +- dev->gser.notify_desc); +- } +- } else { +- gserial_connect(&dev->gser, 0); +- gserial_disconnect(&dev->gser); +- } +- ret = 0; +-set_interface_done: +- spin_unlock(&dev->dev_lock); +- break; +- +- case USB_REQ_GET_INTERFACE: +- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) +- || dev->dev_config == GS_NO_CONFIG_ID) +- break; +- if (wIndex >= GS_MAX_NUM_INTERFACES +- || (dev->dev_config == GS_BULK_CONFIG_ID +- && wIndex != GS_BULK_INTERFACE_ID)) { +- ret = -EDOM; +- break; +- } +- /* no alternate interface settings */ +- *(u8 *)req->buf = 0; +- ret = min(wLength, (u16)1); +- break; +- +- default: +- pr_err("gs_setup: unknown standard request, type=%02x, " +- "request=%02x, value=%04x, index=%04x, length=%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- wValue, wIndex, wLength); +- break; +- } ++ serial_config_driver.iConfiguration = status; + +- return ret; +-} +- +-static void gs_setup_complete_set_line_coding(struct usb_ep *ep, +- struct usb_request *req) +-{ +- struct gs_dev *dev = ep->driver_data; +- +- switch (req->status) { +- case 0: +- /* normal completion */ +- if (req->actual != sizeof(dev->gser.port_line_coding)) +- usb_ep_set_halt(ep); +- else { +- struct usb_cdc_line_coding *value = req->buf; +- +- /* REVISIT: we currently just remember this data. +- * If we change that, (a) validate it first, then +- * (b) update whatever hardware needs updating. +- */ +- spin_lock(&dev->dev_lock); +- dev->gser.port_line_coding = *value; +- spin_unlock(&dev->dev_lock); +- } +- break; +- +- case -ESHUTDOWN: +- /* disconnect */ +- gs_free_req(ep, req); +- break; +- +- default: +- /* unexpected */ +- break; +- } +- return; +-} +- +-static int gs_setup_class(struct usb_gadget *gadget, +- const struct usb_ctrlrequest *ctrl) +-{ +- int ret = -EOPNOTSUPP; +- struct gs_dev *dev = get_gadget_data(gadget); +- struct usb_request *req = dev->dev_ctrl_req; +- u16 wIndex = le16_to_cpu(ctrl->wIndex); +- u16 wValue = le16_to_cpu(ctrl->wValue); +- u16 wLength = le16_to_cpu(ctrl->wLength); +- +- switch (ctrl->bRequest) { +- case USB_CDC_REQ_SET_LINE_CODING: +- if (wLength != sizeof(struct usb_cdc_line_coding)) +- break; +- ret = wLength; +- req->complete = gs_setup_complete_set_line_coding; +- break; +- +- case USB_CDC_REQ_GET_LINE_CODING: +- ret = min_t(int, wLength, sizeof(struct usb_cdc_line_coding)); +- spin_lock(&dev->dev_lock); +- memcpy(req->buf, &dev->gser.port_line_coding, ret); +- spin_unlock(&dev->dev_lock); +- break; +- +- case USB_CDC_REQ_SET_CONTROL_LINE_STATE: +- if (wLength != 0) +- break; +- ret = 0; +- /* REVISIT: we currently just remember this data. +- * If we change that, update whatever hardware needs +- * updating. +- */ +- spin_lock(&dev->dev_lock); +- dev->gser.port_handshake_bits = wValue; +- spin_unlock(&dev->dev_lock); +- break; +- +- default: +- /* NOTE: strictly speaking, we should accept AT-commands +- * using SEND_ENCPSULATED_COMMAND/GET_ENCAPSULATED_RESPONSE. +- * But our call management descriptor says we don't handle +- * call management, so we should be able to get by without +- * handling those "required" commands (except by stalling). ++ /* set up other descriptors */ ++ gcnum = usb_gadget_controller_number(gadget); ++ if (gcnum >= 0) ++ device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum); ++ else { ++ /* this is so simple (for now, no altsettings) that it ++ * SHOULD NOT have problems with bulk-capable hardware. ++ * so warn about unrcognized controllers -- don't panic. ++ * ++ * things like configuration and altsetting numbering ++ * can need hardware-specific attention though. + */ +- pr_err("gs_setup: unknown class request, " +- "type=%02x, request=%02x, value=%04x, " +- "index=%04x, length=%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- wValue, wIndex, wLength); +- break; +- } +- +- return ret; +-} +- +-/* +- * gs_setup_complete +- */ +-static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- if (req->status || req->actual != req->length) { +- pr_err("gs_setup_complete: status error, status=%d, " +- "actual=%d, length=%d\n", +- req->status, req->actual, req->length); +- } +-} +- +-/* +- * gs_setup +- * +- * Implements all the control endpoint functionality that's not +- * handled in hardware or the hardware driver. +- * +- * Returns the size of the data sent to the host, or a negative +- * error number. +- */ +-static int gs_setup(struct usb_gadget *gadget, +- const struct usb_ctrlrequest *ctrl) +-{ +- int ret = -EOPNOTSUPP; +- struct gs_dev *dev = get_gadget_data(gadget); +- struct usb_request *req = dev->dev_ctrl_req; +- u16 wIndex = le16_to_cpu(ctrl->wIndex); +- u16 wValue = le16_to_cpu(ctrl->wValue); +- u16 wLength = le16_to_cpu(ctrl->wLength); +- +- req->complete = gs_setup_complete; +- +- switch (ctrl->bRequestType & USB_TYPE_MASK) { +- case USB_TYPE_STANDARD: +- ret = gs_setup_standard(gadget, ctrl); +- break; +- +- case USB_TYPE_CLASS: +- ret = gs_setup_class(gadget, ctrl); +- break; +- +- default: +- pr_err("gs_setup: unknown request, type=%02x, request=%02x, " +- "value=%04x, index=%04x, length=%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- wValue, wIndex, wLength); +- break; ++ pr_warning("gs_bind: controller '%s' not recognized\n", ++ gadget->name); ++ device_desc.bcdDevice = ++ __constant_cpu_to_le16(GS_VERSION_NUM | 0x0099); + } + +- /* respond with data transfer before status phase? */ +- if (ret >= 0) { +- req->length = ret; +- req->zero = ret < wLength +- && (ret % gadget->ep0->maxpacket) == 0; +- ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); +- if (ret < 0) { +- pr_err("gs_setup: cannot queue response, ret=%d\n", +- ret); +- req->status = 0; +- gs_setup_complete(gadget->ep0, req); +- } ++ if (gadget_is_otg(cdev->gadget)) { ++ serial_config_driver.descriptors = otg_desc; ++ serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + +- /* device either stalls (ret < 0) or reports success */ +- return ret; +-} ++ /* register our configuration */ ++ status = usb_add_config(cdev, &serial_config_driver); ++ if (status < 0) ++ goto fail; + +-/* +- * gs_disconnect +- * +- * Called when the device is disconnected. Frees the closed +- * ports and disconnects open ports. Open ports will be freed +- * on close. Then reallocates the ports for the next connection. +- */ +-static void gs_disconnect(struct usb_gadget *gadget) +-{ +- unsigned long flags; +- struct gs_dev *dev = get_gadget_data(gadget); ++ INFO(cdev, "%s\n", GS_VERSION_NAME); + +- spin_lock_irqsave(&dev->dev_lock, flags); +- gs_reset_config(dev); +- spin_unlock_irqrestore(&dev->dev_lock, flags); ++ return 0; + +- pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME); ++fail: ++ gserial_cleanup(); ++ return status; + } + +-static struct usb_gadget_driver gs_gadget_driver = { +-#ifdef CONFIG_USB_GADGET_DUALSPEED +- .speed = USB_SPEED_HIGH, +-#else +- .speed = USB_SPEED_FULL, +-#endif /* CONFIG_USB_GADGET_DUALSPEED */ +- .function = GS_LONG_NAME, +- .bind = gs_bind, +- .unbind = gs_unbind, +- .setup = gs_setup, +- .disconnect = gs_disconnect, +- .driver = { +- .name = GS_SHORT_NAME, +- .owner = THIS_MODULE, +- }, ++static struct usb_composite_driver gserial_driver = { ++ .name = "g_serial", ++ .dev = &device_desc, ++ .strings = dev_strings, ++ .bind = gs_bind, + }; + +-/* +- * gs_set_config +- * +- * Configures the device by enabling device specific +- * optimizations, setting up the endpoints, allocating +- * read and write requests and queuing read requests. +- * +- * The device lock must be held when calling this function. +- */ +-static int gs_set_config(struct gs_dev *dev, unsigned config) ++static int __init init(void) + { +- int ret = 0; +- struct usb_gadget *gadget = dev->dev_gadget; +- +- if (config == dev->dev_config) +- return 0; +- +- gs_reset_config(dev); +- +- switch (config) { +- case GS_NO_CONFIG_ID: +- return 0; +- case GS_BULK_CONFIG_ID: +- if (use_acm) +- return -EINVAL; +- break; +- case GS_ACM_CONFIG_ID: +- if (!use_acm) +- return -EINVAL; +- break; +- default: +- return -EINVAL; +- } +- +- dev->gser.in_desc = choose_ep_desc(gadget, +- &gs_highspeed_in_desc, +- &gs_fullspeed_in_desc); +- dev->gser.out_desc = choose_ep_desc(gadget, +- &gs_highspeed_out_desc, +- &gs_fullspeed_out_desc); +- dev->gser.notify_desc = dev->gser.notify +- ? choose_ep_desc(gadget, +- &gs_highspeed_notify_desc, +- &gs_fullspeed_notify_desc) +- : NULL; +- +- /* only support one "serial" port for now */ +- if (dev->gser.notify) { +- ret = usb_ep_enable(dev->gser.notify, dev->gser.notify_desc); +- if (ret < 0) +- return ret; +- dev->gser.notify->driver_data = dev; +- } +- +- ret = gserial_connect(&dev->gser, 0); +- if (ret < 0) { +- if (dev->gser.notify) { +- usb_ep_disable(dev->gser.notify); +- dev->gser.notify->driver_data = NULL; +- } +- return ret; +- } +- +- dev->dev_config = config; +- +- /* REVISIT the ACM mode should be able to actually *issue* some +- * notifications, for at least serial state change events if +- * not also for network connection; say so in bmCapabilities. ++ /* We *could* export two configs; that'd be much cleaner... ++ * but neither of these product IDs was defined that way. + */ +- +- pr_info("gs_set_config: %s configured, %s speed %s config\n", +- GS_LONG_NAME, +- gadget->speed == USB_SPEED_HIGH ? "high" : "full", +- config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM"); +- +- return 0; +-} +- +-/* +- * gs_reset_config +- * +- * Mark the device as not configured, disable all endpoints, +- * which forces completion of pending I/O and frees queued +- * requests, and free the remaining write requests on the +- * free list. +- * +- * The device lock must be held when calling this function. +- */ +-static void gs_reset_config(struct gs_dev *dev) +-{ +- if (dev->dev_config == GS_NO_CONFIG_ID) +- return; +- +- dev->dev_config = GS_NO_CONFIG_ID; +- +- gserial_disconnect(&dev->gser); +- if (dev->gser.notify) { +- usb_ep_disable(dev->gser.notify); +- dev->gser.notify->driver_data = NULL; +- } +-} +- +-/* +- * gs_build_config_buf +- * +- * Builds the config descriptors in the given buffer and returns the +- * length, or a negative error number. +- */ +-static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, +- u8 type, unsigned int index, int is_otg) +-{ +- int len; +- int high_speed = 0; +- const struct usb_config_descriptor *config_desc; +- const struct usb_descriptor_header **function; +- +- if (index >= gs_device_desc.bNumConfigurations) +- return -EINVAL; +- +- /* other speed switches high and full speed */ +- if (gadget_is_dualspeed(g)) { +- high_speed = (g->speed == USB_SPEED_HIGH); +- if (type == USB_DT_OTHER_SPEED_CONFIG) +- high_speed = !high_speed; +- } +- + if (use_acm) { +- config_desc = &gs_acm_config_desc; +- function = high_speed +- ? gs_acm_highspeed_function +- : gs_acm_fullspeed_function; ++ serial_config_driver.label = "CDC ACM config"; ++ serial_config_driver.bConfigurationValue = 2; ++ device_desc.bDeviceClass = USB_CLASS_COMM; ++ device_desc.idProduct = ++ __constant_cpu_to_le16(GS_CDC_PRODUCT_ID); + } else { +- config_desc = &gs_bulk_config_desc; +- function = high_speed +- ? gs_bulk_highspeed_function +- : gs_bulk_fullspeed_function; ++ serial_config_driver.label = "Generic Serial config"; ++ serial_config_driver.bConfigurationValue = 1; ++ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; ++ device_desc.idProduct = ++ __constant_cpu_to_le16(GS_PRODUCT_ID); + } ++ strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; + +- /* for now, don't advertise srp-only devices */ +- if (!is_otg) +- function++; +- +- len = usb_gadget_config_buf(config_desc, buf, GS_MAX_DESC_LEN, function); +- if (len < 0) +- return len; +- +- ((struct usb_config_descriptor *)buf)->bDescriptorType = type; +- +- return len; +-} +- +-/* +- * gs_alloc_req +- * +- * Allocate a usb_request and its buffer. Returns a pointer to the +- * usb_request or NULL if there is an error. +- */ +-static struct usb_request * +-gs_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t kmalloc_flags) +-{ +- struct usb_request *req; +- +- if (ep == NULL) +- return NULL; +- +- req = usb_ep_alloc_request(ep, kmalloc_flags); +- +- if (req != NULL) { +- req->length = len; +- req->buf = kmalloc(len, kmalloc_flags); +- if (req->buf == NULL) { +- usb_ep_free_request(ep, req); +- return NULL; +- } +- } +- +- return req; ++ return usb_composite_register(&gserial_driver); + } ++module_init(init); + +-/* +- * gs_free_req +- * +- * Free a usb_request and its buffer. +- */ +-static void gs_free_req(struct usb_ep *ep, struct usb_request *req) ++static void __exit cleanup(void) + { +- if (ep != NULL && req != NULL) { +- kfree(req->buf); +- usb_ep_free_request(ep, req); +- } +-} +- +-/*-------------------------------------------------------------------------*/ +- +-/* +- * gs_module_init +- * +- * Register as a USB gadget driver and a tty driver. +- */ +-static int __init gs_module_init(void) +-{ +- return usb_gadget_register_driver(&gs_gadget_driver); +-} +-module_init(gs_module_init); +- +-/* +- * gs_module_exit +- * +- * Unregister as a tty driver and a USB gadget driver. +- */ +-static void __exit gs_module_exit(void) +-{ +- usb_gadget_unregister_driver(&gs_gadget_driver); ++ usb_composite_unregister(&gserial_driver); ++ gserial_cleanup(); + } +-module_exit(gs_module_exit); ++module_exit(cleanup); +--- a/drivers/usb/gadget/u_serial.h ++++ b/drivers/usb/gadget/u_serial.h +@@ -30,11 +30,6 @@ struct gserial { + + /* REVISIT avoid this CDC-ACM support harder ... */ + struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */ +- +- /* FIXME remove these when we switch to acm_bind_config... */ +- struct usb_ep *notify; +- struct usb_endpoint_descriptor *notify_desc; +- u16 port_handshake_bits; + }; + + /* port setup/teardown is handled by gadget driver */ diff --git a/usb/usb-sisusb-push-down-the-bkl.patch b/usb/usb-sisusb-push-down-the-bkl.patch new file mode 100644 index 00000000000000..88e1e1f4dd97ce --- /dev/null +++ b/usb/usb-sisusb-push-down-the-bkl.patch @@ -0,0 +1,68 @@ +From alan@lxorguk.ukuu.org.uk Fri Jun 6 15:15:32 2008 +From: Alan Cox <alan@lxorguk.ukuu.org.uk> +Date: Thu, 22 May 2008 22:48:48 +0100 +Subject: USB: sisusb: Push down the BKL +To: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org +Message-ID: <20080522224848.318ea9a9@core> + + +This is another case where the lock_kernel appears to be unneccessary and +could be removed with a bit more investigative work + +Signed-off-by: Alan Cox <alan@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/misc/sisusbvga/sisusb.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +--- a/drivers/usb/misc/sisusbvga/sisusb.c ++++ b/drivers/usb/misc/sisusbvga/sisusb.c +@@ -2982,9 +2982,8 @@ sisusb_handle_command(struct sisusb_usb_ + return retval; + } + +-static int +-sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, +- unsigned long arg) ++static long ++sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { + struct sisusb_usb_data *sisusb; + struct sisusb_info x; +@@ -2995,6 +2994,7 @@ sisusb_ioctl(struct inode *inode, struct + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + ++ lock_kernel(); + mutex_lock(&sisusb->lock); + + /* Sanity check */ +@@ -3053,6 +3053,7 @@ sisusb_ioctl(struct inode *inode, struct + + err_out: + mutex_unlock(&sisusb->lock); ++ unlock_kernel(); + return retval; + } + +@@ -3066,9 +3067,7 @@ sisusb_compat_ioctl(struct file *f, unsi + case SISUSB_GET_CONFIG_SIZE: + case SISUSB_GET_CONFIG: + case SISUSB_COMMAND: +- lock_kernel(); +- retval = sisusb_ioctl(f->f_path.dentry->d_inode, f, cmd, arg); +- unlock_kernel(); ++ retval = sisusb_ioctl(f, cmd, arg); + return retval; + + default: +@@ -3087,7 +3086,7 @@ static const struct file_operations usb_ + #ifdef SISUSB_NEW_CONFIG_COMPAT + .compat_ioctl = sisusb_compat_ioctl, + #endif +- .ioctl = sisusb_ioctl ++ .unlocked_ioctl = sisusb_ioctl + }; + + static struct usb_class_driver usb_sisusb_class = { diff --git a/usb/usb-speedtch.c-fix-sparse-shadowed-variable-warning.patch b/usb/usb-speedtch.c-fix-sparse-shadowed-variable-warning.patch new file mode 100644 index 00000000000000..2a39454197af32 --- /dev/null +++ b/usb/usb-speedtch.c-fix-sparse-shadowed-variable-warning.patch @@ -0,0 +1,30 @@ +From harvey.harrison@gmail.com Fri Jun 6 15:17:29 2008 +From: Harvey Harrison <harvey.harrison@gmail.com> +Date: Fri, 30 May 2008 10:39:04 -0700 +Subject: USB: speedtch.c fix sparse shadowed variable warning +To: Greg KH <gregkh@suse.de> +Cc: Andrew Morton <akpm@linux-foundation.org> +Message-ID: <1212169144.28403.200.camel@brick> + + +i is used only as a for-loop index no need to declare another. +drivers/usb/atm/speedtch.c:832:7: warning: symbol 'i' shadows an earlier one +drivers/usb/atm/speedtch.c:766:6: originally declared here + +Signed-off-by: Harvey Harrison <harvey.harrison@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/atm/speedtch.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/usb/atm/speedtch.c ++++ b/drivers/usb/atm/speedtch.c +@@ -829,7 +829,6 @@ static int speedtch_bind(struct usbatm_d + if (use_isoc) { + const struct usb_host_interface *desc = data_intf->cur_altsetting; + const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in; +- int i; + + use_isoc = 0; /* fall back to bulk if endpoint not found */ + diff --git a/usb/usb-usblcd-push-down-bkl-into-driver.patch b/usb/usb-usblcd-push-down-bkl-into-driver.patch new file mode 100644 index 00000000000000..b48e17bc18e15c --- /dev/null +++ b/usb/usb-usblcd-push-down-bkl-into-driver.patch @@ -0,0 +1,55 @@ +From alan@lxorguk.ukuu.org.uk Fri Jun 6 15:14:12 2008 +From: Alan Cox <alan@lxorguk.ukuu.org.uk> +Date: Thu, 22 May 2008 22:07:51 +0100 +Subject: USB: usblcd: Push down BKL into driver +To: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org +Message-ID: <20080522220751.599d73ee@core> + + +I'm pretty sure this can be eliminated however I couldn't prove (or find) +what stopped the device vanishing mid IOCTL_GET_HARD_VERSION. Perhaps a +USB wizard could double check that and see if the lock_kernel can go +entirely. + +Signed-off-by: Alan Cox <alan@redhat.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/misc/usblcd.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/usb/misc/usblcd.c ++++ b/drivers/usb/misc/usblcd.c +@@ -146,7 +146,7 @@ static ssize_t lcd_read(struct file *fil + return retval; + } + +-static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { + struct usb_lcd *dev; + u16 bcdDevice; +@@ -158,12 +158,14 @@ static int lcd_ioctl(struct inode *inode + + switch (cmd) { + case IOCTL_GET_HARD_VERSION: ++ lock_kernel(); + bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); + sprintf(buf,"%1d%1d.%1d%1d", + (bcdDevice & 0xF000)>>12, + (bcdDevice & 0xF00)>>8, + (bcdDevice & 0xF0)>>4, + (bcdDevice & 0xF)); ++ unlock_kernel(); + if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) + return -EFAULT; + break; +@@ -272,7 +274,7 @@ static const struct file_operations lcd_ + .read = lcd_read, + .write = lcd_write, + .open = lcd_open, +- .ioctl = lcd_ioctl, ++ .unlocked_ioctl = lcd_ioctl, + .release = lcd_release, + }; + diff --git a/usb/wusb-drivers-uwb-wlp-sysfs.c-move-misplaced-debug-statement.patch b/usb/wusb-drivers-uwb-wlp-sysfs.c-move-misplaced-debug-statement.patch new file mode 100644 index 00000000000000..7c06068322fed2 --- /dev/null +++ b/usb/wusb-drivers-uwb-wlp-sysfs.c-move-misplaced-debug-statement.patch @@ -0,0 +1,33 @@ +From akpm@linux-foundation.org Fri Jun 6 15:09:20 2008 +From: akpm@linux-foundation.org +Date: Thu, 15 May 2008 12:35:11 -0700 +Subject: WUSB: drivers/uwb/wlp/sysfs.c: move misplaced debug statement +To: greg@kroah.com +Cc: akpm@linux-foundation.org, anderson.lizardo@gmail.com +Message-ID: <200805151935.m4FJZBpa006765@imap1.linux-foundation.org> + + +From: Andrew Morton <akpm@linux-foundation.org> + +drivers/uwb/wlp/sysfs.c:142: warning: control may reach end of non-void function 'wlp_wss_neighborhood_print_remove' being inlined + +Cc: "Anderson Lizardo" <anderson.lizardo@gmail.com> +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/uwb/wlp/sysfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/uwb/wlp/sysfs.c ++++ b/drivers/uwb/wlp/sysfs.c +@@ -125,8 +125,8 @@ ssize_t wlp_wss_neighborhood_print_remov + + out: + mutex_unlock(&wlp->nbmutex); +- return used; + d_fnend(6, dev, "wlp %p\n", wlp); ++ return used; + } + + |
