diff options
40 files changed, 7080 insertions, 0 deletions
diff --git a/driver-core/driver-core-drop-__must_check-from-bus_for_each_drv.patch b/driver-core/driver-core-drop-__must_check-from-bus_for_each_drv.patch new file mode 100644 index 00000000000000..3d90edb92f1b9a --- /dev/null +++ b/driver-core/driver-core-drop-__must_check-from-bus_for_each_drv.patch @@ -0,0 +1,41 @@ +From khali@linux-fr.org Wed Jun 16 13:21:43 2010 +From: Jean Delvare <khali@linux-fr.org> +Date: Wed, 16 Jun 2010 11:44:18 +0200 +Subject: Driver core: Drop __must_check from bus_for_each_drv() +To: Andrew Morton <akpm@osdl.org>, Greg Kroah-Hartman <gregkh@suse.de> +Message-ID: <20100616114418.4a529eb4@hyperion.delvare> + + +There is little rationale for marking bus_for_each_drv() __must_check. +It is more of an iteration helper than a real function. You don't know +in advance which callback it will be used on, so you have no clue how +important it can be to check the returned value. In practice, this +helper function can be used for best-effort tasks. + +As a matter of fact, bus_for_each_dev() is not marked __must_check. +So remove it from bus_for_each_drv() as well. This is the same that +was done back in October 2006 by Russell King for +device_for_each_child(), for exactly the same reasons. + +Signed-off-by: Jean Delvare <khali@linux-fr.org> +Cc: Andrew Morton <akpm@osdl.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + include/linux/device.h | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/include/linux/device.h ++++ b/include/linux/device.h +@@ -84,9 +84,8 @@ struct device *bus_find_device_by_name(s + struct device *start, + const char *name); + +-int __must_check bus_for_each_drv(struct bus_type *bus, +- struct device_driver *start, void *data, +- int (*fn)(struct device_driver *, void *)); ++int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, ++ void *data, int (*fn)(struct device_driver *, void *)); + + void bus_sort_breadthfirst(struct bus_type *bus, + int (*compare)(const struct device *a, diff --git a/driver-core/driver-core-use-kmemdup-in-platform_device_add_resources.patch b/driver-core/driver-core-use-kmemdup-in-platform_device_add_resources.patch new file mode 100644 index 00000000000000..908e4e1620ffc1 --- /dev/null +++ b/driver-core/driver-core-use-kmemdup-in-platform_device_add_resources.patch @@ -0,0 +1,38 @@ +From u.kleine-koenig@pengutronix.de Wed Jun 16 13:51:16 2010 +From: Uwe Kleine-K�nig <u.kleine-koenig@pengutronix.de> +Date: Tue, 15 Jun 2010 10:47:55 +0200 +Subject: Driver core: use kmemdup in platform_device_add_resources +To: linux-kernel@vger.kernel.org +Cc: Greg Kroah-Hartman <gregkh@suse.de>, Magnus Damm <damm@opensource.se>, "Rafael J. Wysocki" <rjw@sisk.pl>, Paul Mundt <lethal@linux-sh.org>, Dmitry Torokhov <dtor@mail.ru> +Message-ID: <1276591677-4678-1-git-send-email-u.kleine-koenig@pengutronix.de> + + +This makes platform_device_add_resources look like +platform_device_add_data. + +Signed-off-by: Uwe Kleine-K�nig <u.kleine-koenig@pengutronix.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/base/platform.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/base/platform.c ++++ b/drivers/base/platform.c +@@ -191,13 +191,13 @@ int platform_device_add_resources(struct + { + struct resource *r; + +- r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL); ++ r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL); + if (r) { +- memcpy(r, res, sizeof(struct resource) * num); + pdev->resource = r; + pdev->num_resources = num; ++ return 0; + } +- return r ? 0 : -ENOMEM; ++ return -ENOMEM; + } + EXPORT_SYMBOL_GPL(platform_device_add_resources); + diff --git a/driver-core/firmware-loader-embed-device-into-firmware_priv-structure.patch b/driver-core/firmware-loader-embed-device-into-firmware_priv-structure.patch new file mode 100644 index 00000000000000..8d59cba6f394f2 --- /dev/null +++ b/driver-core/firmware-loader-embed-device-into-firmware_priv-structure.patch @@ -0,0 +1,468 @@ +From dmitry.torokhov@gmail.com Wed Jun 16 13:27:32 2010 +From: Dmitry Torokhov <dmitry.torokhov@gmail.com> +Date: Fri, 04 Jun 2010 00:54:43 -0700 +Subject: firmware loader: embed device into firmware_priv structure +To: Greg KH <greg@kroah.com> +Cc: linux-kernel@vger.kernel.org +Message-ID: <20100604075443.15861.45944.stgit@localhost.localdomain> + + +Both these structures have the same lifetime rules so instead of allocating +and managing them separately embed struct device into struct firmware_priv. +Also make sure to delete sysfs attributes ourselves instead of expecting +sysfs to clean up our mess. + +Signed-off-by: Dmitry Torokhov <dtor@mail.ru> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/base/firmware_class.c | 255 ++++++++++++++++++++---------------------- + 1 file changed, 122 insertions(+), 133 deletions(-) + +--- a/drivers/base/firmware_class.c ++++ b/drivers/base/firmware_class.c +@@ -93,22 +93,26 @@ struct firmware_priv { + int nr_pages; + int page_array_size; + struct timer_list timeout; ++ struct device dev; + bool nowait; + char fw_id[]; + }; + +-static void +-fw_load_abort(struct firmware_priv *fw_priv) ++static struct firmware_priv *to_firmware_priv(struct device *dev) ++{ ++ return container_of(dev, struct firmware_priv, dev); ++} ++ ++static void fw_load_abort(struct firmware_priv *fw_priv) + { + set_bit(FW_STATUS_ABORT, &fw_priv->status); + wmb(); + complete(&fw_priv->completion); + } + +-static ssize_t +-firmware_timeout_show(struct class *class, +- struct class_attribute *attr, +- char *buf) ++static ssize_t firmware_timeout_show(struct class *class, ++ struct class_attribute *attr, ++ char *buf) + { + return sprintf(buf, "%d\n", loading_timeout); + } +@@ -126,14 +130,14 @@ firmware_timeout_show(struct class *clas + * + * Note: zero means 'wait forever'. + **/ +-static ssize_t +-firmware_timeout_store(struct class *class, +- struct class_attribute *attr, +- const char *buf, size_t count) ++static ssize_t firmware_timeout_store(struct class *class, ++ struct class_attribute *attr, ++ const char *buf, size_t count) + { + loading_timeout = simple_strtol(buf, NULL, 10); + if (loading_timeout < 0) + loading_timeout = 0; ++ + return count; + } + +@@ -145,21 +149,20 @@ static struct class_attribute firmware_c + + static void fw_dev_release(struct device *dev) + { +- struct firmware_priv *fw_priv = dev_get_drvdata(dev); ++ struct firmware_priv *fw_priv = to_firmware_priv(dev); + int i; + + for (i = 0; i < fw_priv->nr_pages; i++) + __free_page(fw_priv->pages[i]); + kfree(fw_priv->pages); + kfree(fw_priv); +- kfree(dev); + + module_put(THIS_MODULE); + } + + static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) + { +- struct firmware_priv *fw_priv = dev_get_drvdata(dev); ++ struct firmware_priv *fw_priv = to_firmware_priv(dev); + + if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id)) + return -ENOMEM; +@@ -181,8 +184,9 @@ static struct class firmware_class = { + static ssize_t firmware_loading_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +- struct firmware_priv *fw_priv = dev_get_drvdata(dev); ++ struct firmware_priv *fw_priv = to_firmware_priv(dev); + int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status); ++ + return sprintf(buf, "%d\n", loading); + } + +@@ -218,7 +222,7 @@ static ssize_t firmware_loading_store(st + struct device_attribute *attr, + const char *buf, size_t count) + { +- struct firmware_priv *fw_priv = dev_get_drvdata(dev); ++ struct firmware_priv *fw_priv = to_firmware_priv(dev); + int loading = simple_strtol(buf, NULL, 10); + int i; + +@@ -276,13 +280,12 @@ static ssize_t firmware_loading_store(st + + static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); + +-static ssize_t +-firmware_data_read(struct file *filp, struct kobject *kobj, +- struct bin_attribute *bin_attr, char *buffer, loff_t offset, +- size_t count) ++static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *bin_attr, ++ char *buffer, loff_t offset, size_t count) + { + struct device *dev = to_dev(kobj); +- struct firmware_priv *fw_priv = dev_get_drvdata(dev); ++ struct firmware_priv *fw_priv = to_firmware_priv(dev); + struct firmware *fw; + ssize_t ret_count; + +@@ -321,8 +324,7 @@ out: + return ret_count; + } + +-static int +-fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) ++static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) + { + int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT; + +@@ -372,13 +374,12 @@ fw_realloc_buffer(struct firmware_priv * + * Data written to the 'data' attribute will be later handed to + * the driver as a firmware image. + **/ +-static ssize_t +-firmware_data_write(struct file* filp, struct kobject *kobj, +- struct bin_attribute *bin_attr, char *buffer, +- loff_t offset, size_t count) ++static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *bin_attr, ++ char *buffer, loff_t offset, size_t count) + { + struct device *dev = to_dev(kobj); +- struct firmware_priv *fw_priv = dev_get_drvdata(dev); ++ struct firmware_priv *fw_priv = to_firmware_priv(dev); + struct firmware *fw; + ssize_t retval; + +@@ -426,107 +427,96 @@ static struct bin_attribute firmware_att + .write = firmware_data_write, + }; + +-static void +-firmware_class_timeout(u_long data) ++static void firmware_class_timeout(u_long data) + { + struct firmware_priv *fw_priv = (struct firmware_priv *) data; ++ + fw_load_abort(fw_priv); + } + +-static int fw_register_device(struct device **dev_p, const char *fw_name, +- struct device *device) ++static struct firmware_priv * ++fw_create_instance(struct firmware *firmware, const char *fw_name, ++ struct device *device, bool uevent, bool nowait) + { +- int retval; +- struct firmware_priv *fw_priv = +- kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL); +- struct device *f_dev = kzalloc(sizeof(*f_dev), GFP_KERNEL); +- +- *dev_p = NULL; ++ struct firmware_priv *fw_priv; ++ struct device *f_dev; ++ int error; + +- if (!fw_priv || !f_dev) { ++ fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL); ++ if (!fw_priv) { + dev_err(device, "%s: kmalloc failed\n", __func__); +- retval = -ENOMEM; +- goto error_kfree; ++ error = -ENOMEM; ++ goto err_out; + } + ++ fw_priv->fw = firmware; ++ fw_priv->nowait = nowait; + strcpy(fw_priv->fw_id, fw_name); + init_completion(&fw_priv->completion); +- fw_priv->timeout.function = firmware_class_timeout; +- fw_priv->timeout.data = (u_long) fw_priv; +- init_timer(&fw_priv->timeout); ++ setup_timer(&fw_priv->timeout, ++ firmware_class_timeout, (u_long) fw_priv); + ++ f_dev = &fw_priv->dev; ++ ++ device_initialize(f_dev); + dev_set_name(f_dev, "%s", dev_name(device)); + f_dev->parent = device; + f_dev->class = &firmware_class; +- dev_set_drvdata(f_dev, fw_priv); +- dev_set_uevent_suppress(f_dev, 1); +- retval = device_register(f_dev); +- if (retval) { +- dev_err(device, "%s: device_register failed\n", __func__); +- put_device(f_dev); +- return retval; +- } +- *dev_p = f_dev; +- return 0; +- +-error_kfree: +- kfree(f_dev); +- kfree(fw_priv); +- return retval; +-} +- +-static int fw_setup_device(struct firmware *fw, struct device **dev_p, +- const char *fw_name, struct device *device, +- int uevent, bool nowait) +-{ +- struct device *f_dev; +- struct firmware_priv *fw_priv; +- int retval; + +- *dev_p = NULL; +- retval = fw_register_device(&f_dev, fw_name, device); +- if (retval) +- goto out; ++ dev_set_uevent_suppress(f_dev, true); + + /* Need to pin this module until class device is destroyed */ + __module_get(THIS_MODULE); + +- fw_priv = dev_get_drvdata(f_dev); +- +- fw_priv->nowait = nowait; ++ error = device_add(f_dev); ++ if (error) { ++ dev_err(device, "%s: device_register failed\n", __func__); ++ goto err_put_dev; ++ } + +- fw_priv->fw = fw; +- retval = sysfs_create_bin_file(&f_dev->kobj, &firmware_attr_data); +- if (retval) { ++ error = device_create_bin_file(f_dev, &firmware_attr_data); ++ if (error) { + dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__); +- goto error_unreg; ++ goto err_del_dev; + } + +- retval = device_create_file(f_dev, &dev_attr_loading); +- if (retval) { ++ error = device_create_file(f_dev, &dev_attr_loading); ++ if (error) { + dev_err(device, "%s: device_create_file failed\n", __func__); +- goto error_unreg; ++ goto err_del_bin_attr; + } + + if (uevent) +- dev_set_uevent_suppress(f_dev, 0); +- *dev_p = f_dev; +- goto out; ++ dev_set_uevent_suppress(f_dev, false); ++ ++ return fw_priv; ++ ++err_del_bin_attr: ++ device_remove_bin_file(f_dev, &firmware_attr_data); ++err_del_dev: ++ device_del(f_dev); ++err_put_dev: ++ put_device(f_dev); ++err_out: ++ return ERR_PTR(error); ++} + +-error_unreg: ++static void fw_destroy_instance(struct firmware_priv *fw_priv) ++{ ++ struct device *f_dev = &fw_priv->dev; ++ ++ device_remove_file(f_dev, &dev_attr_loading); ++ device_remove_bin_file(f_dev, &firmware_attr_data); + device_unregister(f_dev); +-out: +- return retval; + } + +-static int +-_request_firmware(const struct firmware **firmware_p, const char *name, +- struct device *device, int uevent, bool nowait) ++static int _request_firmware(const struct firmware **firmware_p, ++ const char *name, struct device *device, ++ bool uevent, bool nowait) + { +- struct device *f_dev; + struct firmware_priv *fw_priv; + struct firmware *firmware; +- int retval; ++ int retval = 0; + + if (!firmware_p) + return -EINVAL; +@@ -547,41 +537,40 @@ _request_firmware(const struct firmware + if (uevent) + dev_dbg(device, "firmware: requesting %s\n", name); + +- retval = fw_setup_device(firmware, &f_dev, name, device, +- uevent, nowait); +- if (retval) +- goto error_kfree_fw; +- +- fw_priv = dev_get_drvdata(f_dev); ++ fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); ++ if (IS_ERR(fw_priv)) { ++ retval = PTR_ERR(fw_priv); ++ goto out; ++ } + + if (uevent) { +- if (loading_timeout > 0) { +- fw_priv->timeout.expires = jiffies + loading_timeout * HZ; +- add_timer(&fw_priv->timeout); +- } ++ if (loading_timeout > 0) ++ mod_timer(&fw_priv->timeout, ++ round_jiffies_up(jiffies + ++ loading_timeout * HZ)); ++ ++ kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); ++ } + +- kobject_uevent(&f_dev->kobj, KOBJ_ADD); +- wait_for_completion(&fw_priv->completion); +- set_bit(FW_STATUS_DONE, &fw_priv->status); +- del_timer_sync(&fw_priv->timeout); +- } else +- wait_for_completion(&fw_priv->completion); ++ wait_for_completion(&fw_priv->completion); ++ ++ set_bit(FW_STATUS_DONE, &fw_priv->status); ++ del_timer_sync(&fw_priv->timeout); + + mutex_lock(&fw_lock); +- if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) { ++ if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) + retval = -ENOENT; +- release_firmware(fw_priv->fw); +- *firmware_p = NULL; +- } + fw_priv->fw = NULL; + mutex_unlock(&fw_lock); +- device_unregister(f_dev); +- goto out; + +-error_kfree_fw: +- kfree(firmware); +- *firmware_p = NULL; ++ fw_destroy_instance(fw_priv); ++ + out: ++ if (retval) { ++ release_firmware(firmware); ++ firmware_p = NULL; ++ } ++ + return retval; + } + +@@ -632,23 +621,24 @@ struct firmware_work { + int uevent; + }; + +-static int +-request_firmware_work_func(void *arg) ++static int request_firmware_work_func(void *arg) + { + struct firmware_work *fw_work = arg; + const struct firmware *fw; + int ret; ++ + if (!arg) { + WARN_ON(1); + return 0; + } +- ret = _request_firmware(&fw, fw_work->name, fw_work->device, +- fw_work->uevent, true); + ++ ret = _request_firmware(&fw, fw_work->name, fw_work->device, ++ fw_work->uevent, true); + fw_work->cont(fw, fw_work->context); + + module_put(fw_work->module); + kfree(fw_work); ++ + return ret; + } + +@@ -676,34 +666,33 @@ request_firmware_nowait( + void (*cont)(const struct firmware *fw, void *context)) + { + struct task_struct *task; +- struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work), +- gfp); ++ struct firmware_work *fw_work; + ++ fw_work = kzalloc(sizeof (struct firmware_work), gfp); + if (!fw_work) + return -ENOMEM; ++ ++ fw_work->module = module; ++ fw_work->name = name; ++ fw_work->device = device; ++ fw_work->context = context; ++ fw_work->cont = cont; ++ fw_work->uevent = uevent; ++ + if (!try_module_get(module)) { + kfree(fw_work); + return -EFAULT; + } + +- *fw_work = (struct firmware_work) { +- .module = module, +- .name = name, +- .device = device, +- .context = context, +- .cont = cont, +- .uevent = uevent, +- }; +- + task = kthread_run(request_firmware_work_func, fw_work, + "firmware/%s", name); +- + if (IS_ERR(task)) { + fw_work->cont(NULL, fw_work->context); + module_put(fw_work->module); + kfree(fw_work); + return PTR_ERR(task); + } ++ + return 0; + } + diff --git a/driver-core/firmware-loader-use-statically-initialized-data-attribute.patch b/driver-core/firmware-loader-use-statically-initialized-data-attribute.patch new file mode 100644 index 00000000000000..a6b2674340d6c9 --- /dev/null +++ b/driver-core/firmware-loader-use-statically-initialized-data-attribute.patch @@ -0,0 +1,59 @@ +From dmitry.torokhov@gmail.com Wed Jun 16 13:27:20 2010 +From: Dmitry Torokhov <dmitry.torokhov@gmail.com> +Date: Fri, 04 Jun 2010 00:54:37 -0700 +Subject: firmware loader: use statically initialized data attribute +To: Greg KH <greg@kroah.com> +Cc: linux-kernel@vger.kernel.org +Message-ID: <20100604075437.15861.14718.stgit@localhost.localdomain> + + +There is no reason why we are using a template for binary attribute +and copying it into per-firmware data before registering. Using the +original works as well. + +Signed-off-by: Dmitry Torokhov <dtor@mail.ru> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/base/firmware_class.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/base/firmware_class.c ++++ b/drivers/base/firmware_class.c +@@ -87,7 +87,6 @@ static DEFINE_MUTEX(fw_lock); + + struct firmware_priv { + struct completion completion; +- struct bin_attribute attr_data; + struct firmware *fw; + unsigned long status; + struct page **pages; +@@ -420,8 +419,8 @@ out: + return retval; + } + +-static struct bin_attribute firmware_attr_data_tmpl = { +- .attr = {.name = "data", .mode = 0644}, ++static struct bin_attribute firmware_attr_data = { ++ .attr = { .name = "data", .mode = 0644 }, + .size = 0, + .read = firmware_data_read, + .write = firmware_data_write, +@@ -452,7 +451,6 @@ static int fw_register_device(struct dev + + strcpy(fw_priv->fw_id, fw_name); + init_completion(&fw_priv->completion); +- fw_priv->attr_data = firmware_attr_data_tmpl; + fw_priv->timeout.function = firmware_class_timeout; + fw_priv->timeout.data = (u_long) fw_priv; + init_timer(&fw_priv->timeout); +@@ -498,8 +496,7 @@ static int fw_setup_device(struct firmwa + fw_priv->nowait = nowait; + + fw_priv->fw = fw; +- sysfs_bin_attr_init(&fw_priv->attr_data); +- retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data); ++ retval = sysfs_create_bin_file(&f_dev->kobj, &firmware_attr_data); + if (retval) { + dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__); + goto error_unreg; @@ -21,6 +21,9 @@ usb.current/usb-g_serial-fix-tty-cleanup-on-unload.patch usb.current/usb-gadget-g_fs-possible-invalid-pointer-reference-bug-fixed.patch usb.current/usb-xhci-fix-bug-in-link-trb-activation-change.patch usb.current/usb-r8a66597-fix-failure-in-change-of-status.patch +usb.current/usb-musb-fix-a-bug-by-making-suspend-interrupt-available-in-device-mode.patch +usb.current/usb-otg-ulpi-bail-out-on-read-errors.patch +usb.current/usb-ehci-mxc-bail-out-on-transceiver-problems.patch ################################# # Staging patches for 2.6.35 @@ -44,12 +47,20 @@ driver-core/uio-remove-irqf_disabled-from-uio_sercos3.c.patch driver-core/uio-remove-irqf_disabled-flag-from-uio_cif.c.patch driver-core/hotplug-support-kernel-hotplug-sysctl-variable-when-config_net.patch driver-core/driver-core-internal-struct-dma_coherent_mem-change-type-of-a-member.patch +driver-core/driver-core-drop-__must_check-from-bus_for_each_drv.patch +driver-core/firmware-loader-use-statically-initialized-data-attribute.patch +driver-core/firmware-loader-embed-device-into-firmware_priv-structure.patch +driver-core/driver-core-use-kmemdup-in-platform_device_add_resources.patch ##################################### # TTY patches for after 2.6.35 is out ##################################### tty/n_gsm.c-removed-duplicated-includes.patch tty/serial-there-s-no-config-console.patch +tty/vt-clean-up-the-code-use-kernel-library.patch +tty/serial-add-uart_cap_efr-and-uart_cap_sleep-flags-to-16c950-uarts-definition.patch +tty/mrst_max3110-add-uart-driver-for-max3110-on-moorestown.patch +tty/max3110-sanity-check-a-register.patch # tty bkl removal work tty/stallion-prune-lock_kernel-calls.patch @@ -66,6 +77,24 @@ tty/synclink-reworking-locking-a-bit.patch tty/tty-serial-fix-various-misuses-mishandlings-of-port-tty.patch tty/tty-serial-fix-tty-back-references-in-termios.patch tty/tty-serial-fix-tty-referencing-in-set_ldisc.patch +tty/vc-locking-clean-up.patch +tty/tty-make-vt-s-have-a-tty_port.patch +tty/tty-move-the-vt_tty-field-from-the-vc_data-into-the-standard-tty_port.patch +tty/serial-change-the-wait-for-carrier-locking.patch +tty/serial-add-port-helpers.patch +tty/serial-trim-locking-on-the-helpers.patch +tty/serial-use-block_til_ready-helper.patch +tty/tty-replace-bkl-with-a-new-tty_lock.patch +tty/tty-never-hold-btm-while-getting-tty_mutex.patch +tty/tty-fix-console_sem-lock-order.patch +tty/cdc-acm-remove-dead-code.patch +tty/tty-introduce-wait_event_interruptible_tty.patch +tty/tty-reorder-ldisc-locking.patch +tty/tty-untangle-locking-of-wait_until_sent.patch +tty/tty-remove-tty_lock_nested.patch +tty/tty-implement-btm-as-mutex-instead-of-bkl.patch +tty/tty-release-btm-while-sleeping-in-block_til_ready.patch +tty/8250-fix-set_ldisc-operation.patch ################################### # USB stuff for after 2.6.35 is out @@ -88,6 +117,16 @@ usb/usb-core-endpoint-fix-coding-styles.patch usb/usb-otg.h-fix-the-mixup-in-parameters-order.patch usb/usb-host-eliminate-null-dereference.patch usb/usb-isd200.c-remove-unnecessary-kmalloc-cast.patch +usb/usb-throw-away-custom-hex-digit-methods.patch +usb/usb-uvc-move-constants-and-structures-definitions-to-linux-usb-video.h.patch +usb/usb-ehci-ehci-1.1-addendum-preparation.patch +usb/usb-ehci-ehci-1.1-addendum-basic-lpm-feature-support.patch +usb/usb-ehci-ehci-1.1-addendum-enable-per-port-change-detect-bits.patch +usb/usb-option-remove-duplicate-amoi_vendor_id.patch +usb/revert-usb-adding-support-for-htc-smartphones-to-ipaq.patch +usb/usb-gadget-langwell_udc.c-printk-needs-a-unsigned-long-long-cast-for-a-dma_t.patch +usb/usb-conexant-fixed-spacing-and-brace-coding-style-issues.patch +usb/usb-add-a-serial-number-parameter-to-g_file_storage-module.patch # staging stuff is now in the staging-next tree on git.kernel.org diff --git a/tty/8250-fix-set_ldisc-operation.patch b/tty/8250-fix-set_ldisc-operation.patch new file mode 100644 index 00000000000000..fee1599e8cbe0f --- /dev/null +++ b/tty/8250-fix-set_ldisc-operation.patch @@ -0,0 +1,39 @@ +From arnd@arndb.de Wed Jun 16 13:48:20 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:11 +0200 +Subject: 8250: fix set_ldisc operation +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-32-git-send-email-arnd@arndb.de> + + +The ldisc number now gets passed into ->set_ldisc. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/serial/8250.c | 9 ++------- + 1 file changed, 2 insertions(+), 7 deletions(-) + +--- a/drivers/serial/8250.c ++++ b/drivers/serial/8250.c +@@ -2409,14 +2409,9 @@ serial8250_set_termios(struct uart_port + } + + static void +-serial8250_set_ldisc(struct uart_port *port) ++serial8250_set_ldisc(struct uart_port *port, int new) + { +- int line = port->line; +- +- if (line >= port->state->port.tty->driver->num) +- return; +- +- if (port->state->port.tty->ldisc->ops->num == N_PPS) { ++ if (new == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + serial8250_enable_ms(port); + } else diff --git a/tty/cdc-acm-remove-dead-code.patch b/tty/cdc-acm-remove-dead-code.patch new file mode 100644 index 00000000000000..6b587a7f169571 --- /dev/null +++ b/tty/cdc-acm-remove-dead-code.patch @@ -0,0 +1,62 @@ +From arnd@arndb.de Wed Jun 16 13:42:49 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:04 +0200 +Subject: cdc-acm: remove dead code +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-25-git-send-email-arnd@arndb.de> + + +The wait_event_interruptible_timeout in acm_port_down is +never reached. Remove it to avoid possible deadlocks +with the big tty mutex if someone were to start using +the blocking version of acm_port_down. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/class/cdc-acm.c | 12 +++--------- + 1 file changed, 3 insertions(+), 9 deletions(-) + +--- a/drivers/usb/class/cdc-acm.c ++++ b/drivers/usb/class/cdc-acm.c +@@ -636,19 +636,13 @@ static void acm_tty_unregister(struct ac + + static int acm_tty_chars_in_buffer(struct tty_struct *tty); + +-static void acm_port_down(struct acm *acm, int drain) ++static void acm_port_down(struct acm *acm) + { + int i, nr = acm->rx_buflimit; + mutex_lock(&open_mutex); + if (acm->dev) { + usb_autopm_get_interface(acm->control); + acm_set_control(acm, acm->ctrlout = 0); +- /* try letting the last writes drain naturally */ +- if (drain) { +- wait_event_interruptible_timeout(acm->drain_wait, +- (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev, +- ACM_CLOSE_TIMEOUT * HZ); +- } + usb_kill_urb(acm->ctrlurb); + for (i = 0; i < ACM_NW; i++) + usb_kill_urb(acm->wb[i].urb); +@@ -664,7 +658,7 @@ static void acm_tty_hangup(struct tty_st + { + struct acm *acm = tty->driver_data; + tty_port_hangup(&acm->port); +- acm_port_down(acm, 0); ++ acm_port_down(acm); + } + + static void acm_tty_close(struct tty_struct *tty, struct file *filp) +@@ -685,7 +679,7 @@ static void acm_tty_close(struct tty_str + mutex_unlock(&open_mutex); + return; + } +- acm_port_down(acm, 0); ++ acm_port_down(acm); + tty_port_close_end(&acm->port, tty); + tty_port_tty_set(&acm->port, NULL); + } diff --git a/tty/max3110-sanity-check-a-register.patch b/tty/max3110-sanity-check-a-register.patch new file mode 100644 index 00000000000000..3dd69b4b2095ba --- /dev/null +++ b/tty/max3110-sanity-check-a-register.patch @@ -0,0 +1,53 @@ +From alan@linux.intel.com Wed Jun 16 13:50:26 2010 +From: jianwei.yang <jianwei.yang@intel.com> +Date: Wed, 16 Jun 2010 14:46:49 +0100 +Subject: max3110 sanity check a register +To: greg@kroah.com, linux-serial@vger.kernel.org +Message-ID: <20100616134616.12686.11518.stgit@localhost.localdomain> + + +From: jianwei.yang <jianwei.yang@intel.com> + +MAX3111 is the SPI/UART IC installed on the MRST SPI Port Card as a serial +debug goal, and the SPI Port Card will be frequently mounted and unmounted +from the main board by developers depending whether debug serial is +required or not. + +As the MAX3111 has no subvendor or product id registers available, the patch +will try to access one register to decide if this IC is present or not. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/mrst_max3110.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/drivers/serial/mrst_max3110.c ++++ b/drivers/serial/mrst_max3110.c +@@ -721,7 +721,7 @@ static int serial_m3110_probe(struct spi + struct uart_max3110 *max; + int ret; + unsigned char *buffer; +- ++ u16 res; + max = kzalloc(sizeof(*max), GFP_KERNEL); + if (!max) + return -ENOMEM; +@@ -753,7 +753,16 @@ static int serial_m3110_probe(struct spi + + max->cur_conf = 0; + atomic_set(&max->irq_pending, 0); ++ /* Check if reading configuration register returns something sane */ + ++ res = RC_TAG; ++ ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0); ++ if (ret < 0 || res == 0 || res == 0xffff) { ++ printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)", ++ res); ++ ret = -ENODEV; ++ goto err_get_page; ++ } + buffer = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; diff --git a/tty/mrst_max3110-add-uart-driver-for-max3110-on-moorestown.patch b/tty/mrst_max3110-add-uart-driver-for-max3110-on-moorestown.patch new file mode 100644 index 00000000000000..011a3d5aa18780 --- /dev/null +++ b/tty/mrst_max3110-add-uart-driver-for-max3110-on-moorestown.patch @@ -0,0 +1,969 @@ +From alan@linux.intel.com Wed Jun 16 13:50:11 2010 +From: Alan Cox <alan@linux.intel.com> +Date: Wed, 16 Jun 2010 14:46:09 +0100 +Subject: mrst_max3110: add UART driver for Max3110 on Moorestown +To: greg@kroah.com, linux-serial@vger.kernel.org +Message-ID: <20100616134554.12686.94694.stgit@localhost.localdomain> + + +From: Feng Tang <feng.tang@intel.com> + +This driver enable the max3110 device, it can be used as +a system console. the IRQ needs be enabled if user want a +better performance. MRST max3110 works in 3.684MHz clock, +which supports 230400 as its maximum rate. + +Signed-off-by: Feng Tang <feng.tang@intel.com> +Signed-off-by: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/serial/Kconfig | 17 + drivers/serial/Makefile | 1 + drivers/serial/mrst_max3110.c | 844 ++++++++++++++++++++++++++++++++++++++++++ + drivers/serial/mrst_max3110.h | 59 ++ + 4 files changed, 921 insertions(+) + +--- a/drivers/serial/Kconfig ++++ b/drivers/serial/Kconfig +@@ -698,6 +698,23 @@ config SERIAL_SA1100_CONSOLE + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + ++config SERIAL_MRST_MAX3110 ++ tristate "SPI UART driver for Max3110" ++ depends on SPI_DW_PCI ++ select SERIAL_CORE ++ select SERIAL_CORE_CONSOLE ++ help ++ This is the UART protocol driver for the MAX3110 device on ++ the Intel Moorestown platform. On other systems use the max3100 ++ driver. ++ ++config MRST_MAX3110_IRQ ++ boolean "Enable GPIO IRQ for Max3110 over Moorestown" ++ default n ++ depends on SERIAL_MRST_MAX3110 && GPIO_LANGWELL ++ help ++ This has to be enabled after Moorestown GPIO driver is loaded ++ + config SERIAL_BFIN + tristate "Blackfin serial port support" + depends on BLACKFIN +--- a/drivers/serial/Makefile ++++ b/drivers/serial/Makefile +@@ -84,3 +84,4 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbu + obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o + obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o + obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o ++obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o +--- /dev/null ++++ b/drivers/serial/mrst_max3110.c +@@ -0,0 +1,844 @@ ++/* ++ * max3110.c - spi uart protocol driver for Maxim 3110 on Moorestown ++ * ++ * Copyright (C) Intel 2008 Feng Tang <feng.tang@intel.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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. ++ */ ++ ++/* ++ * Note: ++ * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has ++ * 1 word. If SPI master controller doesn't support sclk frequency change, ++ * then the char need be sent out one by one with some delay ++ * ++ * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE ++ * interrupt for a low speed UART device ++ */ ++ ++#include <linux/module.h> ++#include <linux/ioport.h> ++#include <linux/init.h> ++#include <linux/console.h> ++#include <linux/sysrq.h> ++#include <linux/platform_device.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++#include <linux/serial_core.h> ++#include <linux/serial_reg.h> ++ ++#include <linux/kthread.h> ++#include <linux/delay.h> ++#include <asm/atomic.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/dw_spi.h> ++ ++#include "mrst_max3110.h" ++ ++#define PR_FMT "mrst_max3110: " ++ ++struct uart_max3110 { ++ struct uart_port port; ++ struct spi_device *spi; ++ char *name; ++ ++ wait_queue_head_t wq; ++ struct task_struct *main_thread; ++ struct task_struct *read_thread; ++ int mthread_up; ++ spinlock_t lock; ++ ++ u32 baud; ++ u16 cur_conf; ++ u8 clock; ++ u8 parity, word_7bits; ++ ++ atomic_t uart_tx_need; ++ ++ /* console related */ ++ struct circ_buf con_xmit; ++ atomic_t con_tx_need; ++ ++ /* irq related */ ++ u16 irq; ++ atomic_t irq_pending; ++}; ++ ++/* global data structure, may need be removed */ ++struct uart_max3110 *pmax; ++static inline void receive_char(struct uart_max3110 *max, u8 ch); ++static void receive_chars(struct uart_max3110 *max, ++ unsigned char *str, int len); ++static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf); ++static void max3110_console_receive(struct uart_max3110 *max); ++ ++int max3110_write_then_read(struct uart_max3110 *max, ++ const u8 *txbuf, u8 *rxbuf, unsigned len, int always_fast) ++{ ++ struct spi_device *spi = max->spi; ++ struct spi_message message; ++ struct spi_transfer x; ++ int ret; ++ ++ if (!txbuf || !rxbuf) ++ return -EINVAL; ++ ++ spi_message_init(&message); ++ memset(&x, 0, sizeof x); ++ x.len = len; ++ x.tx_buf = txbuf; ++ x.rx_buf = rxbuf; ++ spi_message_add_tail(&x, &message); ++ ++ if (always_fast) ++ x.speed_hz = 3125000; ++ else if (max->baud) ++ x.speed_hz = max->baud; ++ ++ /* Do the i/o */ ++ ret = spi_sync(spi, &message); ++ return ret; ++} ++ ++/* Write a u16 to the device, and return one u16 read back */ ++int max3110_out(struct uart_max3110 *max, const u16 out) ++{ ++ u16 tmp; ++ int ret; ++ ++ ret = max3110_write_then_read(max, (u8 *)&out, (u8 *)&tmp, 2, 1); ++ if (ret) ++ return ret; ++ ++ /* If some valid data is read back */ ++ if (tmp & MAX3110_READ_DATA_AVAILABLE) ++ receive_char(max, (tmp & 0xff)); ++ ++ return ret; ++} ++ ++#define MAX_READ_LEN 20 ++/* ++ * This is usually used to read data from SPIC RX FIFO, which doesn't ++ * need any delay like flushing character out. It returns how many ++ * valide bytes are read back ++ */ ++static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf) ++{ ++ u16 out[MAX_READ_LEN], in[MAX_READ_LEN]; ++ u8 *pbuf, valid_str[MAX_READ_LEN]; ++ int i, j, bytelen; ++ ++ if (len > MAX_READ_LEN) { ++ pr_err(PR_FMT "read len %d is too large\n", len); ++ return 0; ++ } ++ ++ bytelen = len * 2; ++ memset(out, 0, bytelen); ++ memset(in, 0, bytelen); ++ ++ if (max3110_write_then_read(max, (u8 *)out, (u8 *)in, bytelen, 1)) ++ return 0; ++ ++ /* If caller don't provide a buffer, then handle received char */ ++ pbuf = buf ? buf : valid_str; ++ ++ for (i = 0, j = 0; i < len; i++) { ++ if (in[i] & MAX3110_READ_DATA_AVAILABLE) ++ pbuf[j++] = (u8)(in[i] & 0xff); ++ } ++ ++ if (j && (pbuf == valid_str)) ++ receive_chars(max, valid_str, j); ++ ++ return j; ++} ++ ++static void serial_m3110_con_putchar(struct uart_port *port, int ch) ++{ ++ struct uart_max3110 *max = ++ container_of(port, struct uart_max3110, port); ++ struct circ_buf *xmit = &max->con_xmit; ++ ++ if (uart_circ_chars_free(xmit)) { ++ xmit->buf[xmit->head] = (char)ch; ++ xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1); ++ } ++ ++ if (!atomic_read(&max->con_tx_need)) { ++ atomic_set(&max->con_tx_need, 1); ++ wake_up_process(max->main_thread); ++ } ++} ++ ++/* ++ * Print a string to the serial port trying not to disturb ++ * any possible real use of the port... ++ * ++ * The console_lock must be held when we get here. ++ */ ++static void serial_m3110_con_write(struct console *co, ++ const char *s, unsigned int count) ++{ ++ if (!pmax) ++ return; ++ ++ uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar); ++} ++ ++static int __init ++serial_m3110_con_setup(struct console *co, char *options) ++{ ++ struct uart_max3110 *max = pmax; ++ int baud = 115200; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ pr_info(PR_FMT "setting up console\n"); ++ ++ if (!max) { ++ pr_err(PR_FMT "pmax is NULL, return"); ++ return -ENODEV; ++ } ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ ++ return uart_set_options(&max->port, co, baud, parity, bits, flow); ++} ++ ++static struct tty_driver *serial_m3110_con_device(struct console *co, ++ int *index) ++{ ++ struct uart_driver *p = co->data; ++ *index = co->index; ++ return p->tty_driver; ++} ++ ++static struct uart_driver serial_m3110_reg; ++static struct console serial_m3110_console = { ++ .name = "ttyS", ++ .write = serial_m3110_con_write, ++ .device = serial_m3110_con_device, ++ .setup = serial_m3110_con_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++ .data = &serial_m3110_reg, ++}; ++ ++#define MRST_CONSOLE (&serial_m3110_console) ++ ++static unsigned int serial_m3110_tx_empty(struct uart_port *port) ++{ ++ return 1; ++} ++ ++static void serial_m3110_stop_tx(struct uart_port *port) ++{ ++ return; ++} ++ ++/* stop_rx will be called in spin_lock env */ ++static void serial_m3110_stop_rx(struct uart_port *port) ++{ ++ return; ++} ++ ++#define WORDS_PER_XFER 128 ++static inline void send_circ_buf(struct uart_max3110 *max, ++ struct circ_buf *xmit) ++{ ++ int len, left = 0; ++ u16 obuf[WORDS_PER_XFER], ibuf[WORDS_PER_XFER]; ++ u8 valid_str[WORDS_PER_XFER]; ++ int i, j; ++ ++ while (!uart_circ_empty(xmit)) { ++ left = uart_circ_chars_pending(xmit); ++ while (left) { ++ len = (left >= WORDS_PER_XFER) ? WORDS_PER_XFER : left; ++ ++ memset(obuf, 0, len * 2); ++ memset(ibuf, 0, len * 2); ++ for (i = 0; i < len; i++) { ++ obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG; ++ xmit->tail = (xmit->tail + 1) & ++ (UART_XMIT_SIZE - 1); ++ } ++ max3110_write_then_read(max, (u8 *)obuf, ++ (u8 *)ibuf, len * 2, 0); ++ ++ for (i = 0, j = 0; i < len; i++) { ++ if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) ++ valid_str[j++] = (u8)(ibuf[i] & 0xff); ++ } ++ ++ if (j) ++ receive_chars(max, valid_str, j); ++ ++ max->port.icount.tx += len; ++ left -= len; ++ } ++ } ++} ++ ++static void transmit_char(struct uart_max3110 *max) ++{ ++ struct uart_port *port = &max->port; ++ struct circ_buf *xmit = &port->state->xmit; ++ ++ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) ++ return; ++ ++ send_circ_buf(max, xmit); ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ if (uart_circ_empty(xmit)) ++ serial_m3110_stop_tx(port); ++} ++ ++/* This will be called by uart_write() and tty_write, can't ++ * go to sleep */ ++static void serial_m3110_start_tx(struct uart_port *port) ++{ ++ struct uart_max3110 *max = ++ container_of(port, struct uart_max3110, port); ++ ++ if (!atomic_read(&max->uart_tx_need)) { ++ atomic_set(&max->uart_tx_need, 1); ++ wake_up_process(max->main_thread); ++ } ++} ++ ++static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len) ++{ ++ struct uart_port *port = &max->port; ++ struct tty_struct *tty; ++ int usable; ++ ++ /* If uart is not opened, just return */ ++ if (!port->state) ++ return; ++ ++ tty = port->state->port.tty; ++ if (!tty) ++ return; /* receive some char before the tty is opened */ ++ ++ while (len) { ++ usable = tty_buffer_request_room(tty, len); ++ if (usable) { ++ tty_insert_flip_string(tty, str, usable); ++ str += usable; ++ port->icount.rx += usable; ++ tty_flip_buffer_push(tty); ++ } ++ len -= usable; ++ } ++} ++ ++static inline void receive_char(struct uart_max3110 *max, u8 ch) ++{ ++ receive_chars(max, &ch, 1); ++} ++ ++static void max3110_console_receive(struct uart_max3110 *max) ++{ ++ int loop = 1, num, total = 0; ++ u8 recv_buf[512], *pbuf; ++ ++ pbuf = recv_buf; ++ do { ++ num = max3110_read_multi(max, 8, pbuf); ++ ++ if (num) { ++ loop = 10; ++ pbuf += num; ++ total += num; ++ ++ if (total >= 500) { ++ receive_chars(max, recv_buf, total); ++ pbuf = recv_buf; ++ total = 0; ++ } ++ } ++ } while (--loop); ++ ++ if (total) ++ receive_chars(max, recv_buf, total); ++} ++ ++static int max3110_main_thread(void *_max) ++{ ++ struct uart_max3110 *max = _max; ++ wait_queue_head_t *wq = &max->wq; ++ int ret = 0; ++ struct circ_buf *xmit = &max->con_xmit; ++ ++ init_waitqueue_head(wq); ++ pr_info(PR_FMT "start main thread\n"); ++ ++ do { ++ wait_event_interruptible(*wq, (atomic_read(&max->irq_pending) || ++ atomic_read(&max->con_tx_need) || ++ atomic_read(&max->uart_tx_need)) || ++ kthread_should_stop()); ++ max->mthread_up = 1; ++ ++#ifdef CONFIG_MRST_MAX3110_IRQ ++ if (atomic_read(&max->irq_pending)) { ++ max3110_console_receive(max); ++ atomic_set(&max->irq_pending, 0); ++ } ++#endif ++ ++ /* first handle console output */ ++ if (atomic_read(&max->con_tx_need)) { ++ send_circ_buf(max, xmit); ++ atomic_set(&max->con_tx_need, 0); ++ } ++ ++ /* handle uart output */ ++ if (atomic_read(&max->uart_tx_need)) { ++ transmit_char(max); ++ atomic_set(&max->uart_tx_need, 0); ++ } ++ max->mthread_up = 0; ++ } while (!kthread_should_stop()); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_MRST_MAX3110_IRQ ++static irqreturn_t serial_m3110_irq(int irq, void *dev_id) ++{ ++ struct uart_max3110 *max = dev_id; ++ ++ /* max3110's irq is a falling edge, not level triggered, ++ * so no need to disable the irq */ ++ if (!atomic_read(&max->irq_pending)) { ++ atomic_inc(&max->irq_pending); ++ wake_up_process(max->main_thread); ++ } ++ return IRQ_HANDLED; ++} ++#else ++/* if don't use RX IRQ, then need a thread to polling read */ ++static int max3110_read_thread(void *_max) ++{ ++ struct uart_max3110 *max = _max; ++ ++ pr_info(PR_FMT "start read thread\n"); ++ do { ++ if (!max->mthread_up) ++ max3110_console_receive(max); ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(HZ / 20); ++ } while (!kthread_should_stop()); ++ ++ return 0; ++} ++#endif ++ ++static int serial_m3110_startup(struct uart_port *port) ++{ ++ struct uart_max3110 *max = ++ container_of(port, struct uart_max3110, port); ++ u16 config = 0; ++ int ret = 0; ++ ++ if (port->line != 0) ++ pr_err(PR_FMT "uart port startup failed\n"); ++ ++ /* firstly disable all IRQ and config it to 115200, 8n1 */ ++ config = WC_TAG | WC_FIFO_ENABLE ++ | WC_1_STOPBITS ++ | WC_8BIT_WORD ++ | WC_BAUD_DR2; ++ ret = max3110_out(max, config); ++ ++ /* as we use thread to handle tx/rx, need set low latency */ ++ port->state->port.tty->low_latency = 1; ++ ++#ifdef CONFIG_MRST_MAX3110_IRQ ++ ret = request_irq(max->irq, serial_m3110_irq, ++ IRQ_TYPE_EDGE_FALLING, "max3110", max); ++ if (ret) ++ return ret; ++ ++ /* enable RX IRQ only */ ++ config |= WC_RXA_IRQ_ENABLE; ++ max3110_out(max, config); ++#else ++ /* if IRQ is disabled, start a read thread for input data */ ++ max->read_thread = ++ kthread_run(max3110_read_thread, max, "max3110_read"); ++#endif ++ ++ max->cur_conf = config; ++ return 0; ++} ++ ++static void serial_m3110_shutdown(struct uart_port *port) ++{ ++ struct uart_max3110 *max = ++ container_of(port, struct uart_max3110, port); ++ u16 config; ++ ++ if (max->read_thread) { ++ kthread_stop(max->read_thread); ++ max->read_thread = NULL; ++ } ++ ++#ifdef CONFIG_MRST_MAX3110_IRQ ++ free_irq(max->irq, max); ++#endif ++ ++ /* Disable interrupts from this port */ ++ config = WC_TAG | WC_SW_SHDI; ++ max3110_out(max, config); ++} ++ ++static void serial_m3110_release_port(struct uart_port *port) ++{ ++} ++ ++static int serial_m3110_request_port(struct uart_port *port) ++{ ++ return 0; ++} ++ ++static void serial_m3110_config_port(struct uart_port *port, int flags) ++{ ++ /* give it fake type */ ++ port->type = PORT_PXA; ++} ++ ++static int ++serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ /* we don't want the core code to modify any port params */ ++ return -EINVAL; ++} ++ ++ ++static const char *serial_m3110_type(struct uart_port *port) ++{ ++ struct uart_max3110 *max = ++ container_of(port, struct uart_max3110, port); ++ return max->name; ++} ++ ++static void ++serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios, ++ struct ktermios *old) ++{ ++ struct uart_max3110 *max = ++ container_of(port, struct uart_max3110, port); ++ unsigned char cval; ++ unsigned int baud, parity = 0; ++ int clk_div = -1; ++ u16 new_conf = max->cur_conf; ++ ++ switch (termios->c_cflag & CSIZE) { ++ case CS7: ++ cval = UART_LCR_WLEN7; ++ new_conf |= WC_7BIT_WORD; ++ break; ++ default: ++ case CS8: ++ cval = UART_LCR_WLEN8; ++ new_conf |= WC_8BIT_WORD; ++ break; ++ } ++ ++ baud = uart_get_baud_rate(port, termios, old, 0, 230400); ++ ++ /* first calc the div for 1.8MHZ clock case */ ++ switch (baud) { ++ case 300: ++ clk_div = WC_BAUD_DR384; ++ break; ++ case 600: ++ clk_div = WC_BAUD_DR192; ++ break; ++ case 1200: ++ clk_div = WC_BAUD_DR96; ++ break; ++ case 2400: ++ clk_div = WC_BAUD_DR48; ++ break; ++ case 4800: ++ clk_div = WC_BAUD_DR24; ++ break; ++ case 9600: ++ clk_div = WC_BAUD_DR12; ++ break; ++ case 19200: ++ clk_div = WC_BAUD_DR6; ++ break; ++ case 38400: ++ clk_div = WC_BAUD_DR3; ++ break; ++ case 57600: ++ clk_div = WC_BAUD_DR2; ++ break; ++ case 115200: ++ clk_div = WC_BAUD_DR1; ++ break; ++ case 230400: ++ if (max->clock & MAX3110_HIGH_CLK) ++ break; ++ default: ++ /* pick the previous baud rate */ ++ baud = max->baud; ++ clk_div = max->cur_conf & WC_BAUD_DIV_MASK; ++ tty_termios_encode_baud_rate(termios, baud, baud); ++ } ++ ++ if (max->clock & MAX3110_HIGH_CLK) { ++ clk_div += 1; ++ /* high clk version max3110 doesn't support B300 */ ++ if (baud == 300) ++ baud = 600; ++ if (baud == 230400) ++ clk_div = WC_BAUD_DR1; ++ tty_termios_encode_baud_rate(termios, baud, baud); ++ } ++ ++ new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div; ++ if (termios->c_cflag & CSTOPB) ++ new_conf |= WC_2_STOPBITS; ++ else ++ new_conf &= ~WC_2_STOPBITS; ++ ++ if (termios->c_cflag & PARENB) { ++ new_conf |= WC_PARITY_ENABLE; ++ parity |= UART_LCR_PARITY; ++ } else ++ new_conf &= ~WC_PARITY_ENABLE; ++ ++ if (!(termios->c_cflag & PARODD)) ++ parity |= UART_LCR_EPAR; ++ max->parity = parity; ++ ++ uart_update_timeout(port, termios->c_cflag, baud); ++ ++ new_conf |= WC_TAG; ++ if (new_conf != max->cur_conf) { ++ max3110_out(max, new_conf); ++ max->cur_conf = new_conf; ++ max->baud = baud; ++ } ++} ++ ++/* don't handle hw handshaking */ ++static unsigned int serial_m3110_get_mctrl(struct uart_port *port) ++{ ++ return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR; ++} ++ ++static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++} ++ ++static void serial_m3110_break_ctl(struct uart_port *port, int break_state) ++{ ++} ++ ++static void serial_m3110_pm(struct uart_port *port, unsigned int state, ++ unsigned int oldstate) ++{ ++} ++ ++static void serial_m3110_enable_ms(struct uart_port *port) ++{ ++} ++ ++struct uart_ops serial_m3110_ops = { ++ .tx_empty = serial_m3110_tx_empty, ++ .set_mctrl = serial_m3110_set_mctrl, ++ .get_mctrl = serial_m3110_get_mctrl, ++ .stop_tx = serial_m3110_stop_tx, ++ .start_tx = serial_m3110_start_tx, ++ .stop_rx = serial_m3110_stop_rx, ++ .enable_ms = serial_m3110_enable_ms, ++ .break_ctl = serial_m3110_break_ctl, ++ .startup = serial_m3110_startup, ++ .shutdown = serial_m3110_shutdown, ++ .set_termios = serial_m3110_set_termios, /* must have */ ++ .pm = serial_m3110_pm, ++ .type = serial_m3110_type, ++ .release_port = serial_m3110_release_port, ++ .request_port = serial_m3110_request_port, ++ .config_port = serial_m3110_config_port, ++ .verify_port = serial_m3110_verify_port, ++}; ++ ++static struct uart_driver serial_m3110_reg = { ++ .owner = THIS_MODULE, ++ .driver_name = "MRST serial", ++ .dev_name = "ttyS", ++ .major = TTY_MAJOR, ++ .minor = 64, ++ .nr = 1, ++ .cons = MRST_CONSOLE, ++}; ++ ++static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) ++{ ++ return 0; ++} ++ ++static int serial_m3110_resume(struct spi_device *spi) ++{ ++ return 0; ++} ++ ++static struct dw_spi_chip spi0_uart = { ++ .poll_mode = 1, ++ .enable_dma = 0, ++ .type = SPI_FRF_SPI, ++}; ++ ++static int serial_m3110_probe(struct spi_device *spi) ++{ ++ struct uart_max3110 *max; ++ int ret; ++ unsigned char *buffer; ++ ++ max = kzalloc(sizeof(*max), GFP_KERNEL); ++ if (!max) ++ return -ENOMEM; ++ ++ /* set spi info */ ++ spi->mode = SPI_MODE_0; ++ spi->bits_per_word = 16; ++ max->clock = MAX3110_HIGH_CLK; ++ spi->controller_data = &spi0_uart; ++ ++ spi_setup(spi); ++ ++ max->port.type = PORT_PXA; /* need apply for a max3110 type */ ++ max->port.fifosize = 2; /* only have 16b buffer */ ++ max->port.ops = &serial_m3110_ops; ++ max->port.line = 0; ++ max->port.dev = &spi->dev; ++ max->port.uartclk = 115200; ++ ++ max->spi = spi; ++ max->name = spi->modalias; /* use spi name as the name */ ++ max->irq = (u16)spi->irq; ++ ++ spin_lock_init(&max->lock); ++ ++ max->word_7bits = 0; ++ max->parity = 0; ++ max->baud = 0; ++ ++ max->cur_conf = 0; ++ atomic_set(&max->irq_pending, 0); ++ ++ buffer = (unsigned char *)__get_free_page(GFP_KERNEL); ++ if (!buffer) { ++ ret = -ENOMEM; ++ goto err_get_page; ++ } ++ max->con_xmit.buf = (unsigned char *)buffer; ++ max->con_xmit.head = max->con_xmit.tail = 0; ++ ++ max->main_thread = kthread_run(max3110_main_thread, ++ max, "max3110_main"); ++ if (IS_ERR(max->main_thread)) { ++ ret = PTR_ERR(max->main_thread); ++ goto err_kthread; ++ } ++ ++ pmax = max; ++ /* give membase a psudo value to pass serial_core's check */ ++ max->port.membase = (void *)0xff110000; ++ uart_add_one_port(&serial_m3110_reg, &max->port); ++ ++ return 0; ++ ++err_kthread: ++ free_page((unsigned long)buffer); ++err_get_page: ++ pmax = NULL; ++ kfree(max); ++ return ret; ++} ++ ++static int max3110_remove(struct spi_device *dev) ++{ ++ struct uart_max3110 *max = pmax; ++ ++ if (!pmax) ++ return 0; ++ ++ pmax = NULL; ++ uart_remove_one_port(&serial_m3110_reg, &max->port); ++ ++ free_page((unsigned long)max->con_xmit.buf); ++ ++ if (max->main_thread) ++ kthread_stop(max->main_thread); ++ ++ kfree(max); ++ return 0; ++} ++ ++static struct spi_driver uart_max3110_driver = { ++ .driver = { ++ .name = "spi_max3111", ++ .bus = &spi_bus_type, ++ .owner = THIS_MODULE, ++ }, ++ .probe = serial_m3110_probe, ++ .remove = __devexit_p(max3110_remove), ++ .suspend = serial_m3110_suspend, ++ .resume = serial_m3110_resume, ++}; ++ ++ ++int __init serial_m3110_init(void) ++{ ++ int ret = 0; ++ ++ ret = uart_register_driver(&serial_m3110_reg); ++ if (ret) ++ return ret; ++ ++ ret = spi_register_driver(&uart_max3110_driver); ++ if (ret) ++ uart_unregister_driver(&serial_m3110_reg); ++ ++ return ret; ++} ++ ++void __exit serial_m3110_exit(void) ++{ ++ spi_unregister_driver(&uart_max3110_driver); ++ uart_unregister_driver(&serial_m3110_reg); ++} ++ ++module_init(serial_m3110_init); ++module_exit(serial_m3110_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("max3110-uart"); +--- /dev/null ++++ b/drivers/serial/mrst_max3110.h +@@ -0,0 +1,59 @@ ++#ifndef _MRST_MAX3110_H ++#define _MRST_MAX3110_H ++ ++#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */ ++#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */ ++ ++/* status bits for all 4 MAX3110 operate modes */ ++#define MAX3110_READ_DATA_AVAILABLE (1 << 15) ++#define MAX3110_WRITE_BUF_EMPTY (1 << 14) ++ ++#define WC_TAG (3 << 14) ++#define RC_TAG (1 << 14) ++#define WD_TAG (2 << 14) ++#define RD_TAG (0 << 14) ++ ++/* bits def for write configuration */ ++#define WC_FIFO_ENABLE_MASK (1 << 13) ++#define WC_FIFO_ENABLE (0 << 13) ++ ++#define WC_SW_SHDI (1 << 12) ++ ++#define WC_IRQ_MASK (0xF << 8) ++#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */ ++#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */ ++#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9) ++#define WC_REC_ACT_IRQ_ENABLE (1 << 8) ++ ++#define WC_IRDA_ENABLE (1 << 7) ++ ++#define WC_STOPBITS_MASK (1 << 6) ++#define WC_2_STOPBITS (1 << 6) ++#define WC_1_STOPBITS (0 << 6) ++ ++#define WC_PARITY_ENABLE_MASK (1 << 5) ++#define WC_PARITY_ENABLE (1 << 5) ++ ++#define WC_WORDLEN_MASK (1 << 4) ++#define WC_7BIT_WORD (1 << 4) ++#define WC_8BIT_WORD (0 << 4) ++ ++#define WC_BAUD_DIV_MASK (0xF) ++#define WC_BAUD_DR1 (0x0) ++#define WC_BAUD_DR2 (0x1) ++#define WC_BAUD_DR4 (0x2) ++#define WC_BAUD_DR8 (0x3) ++#define WC_BAUD_DR16 (0x4) ++#define WC_BAUD_DR32 (0x5) ++#define WC_BAUD_DR64 (0x6) ++#define WC_BAUD_DR128 (0x7) ++#define WC_BAUD_DR3 (0x8) ++#define WC_BAUD_DR6 (0x9) ++#define WC_BAUD_DR12 (0xA) ++#define WC_BAUD_DR24 (0xB) ++#define WC_BAUD_DR48 (0xC) ++#define WC_BAUD_DR96 (0xD) ++#define WC_BAUD_DR192 (0xE) ++#define WC_BAUD_DR384 (0xF) ++ ++#endif diff --git a/tty/serial-add-port-helpers.patch b/tty/serial-add-port-helpers.patch new file mode 100644 index 00000000000000..3c849d8b6c6a18 --- /dev/null +++ b/tty/serial-add-port-helpers.patch @@ -0,0 +1,117 @@ +From arnd@arndb.de Wed Jun 16 13:35:51 2010 +From: Alan Cox <alan@linux.intel.com> +Date: Tue, 1 Jun 2010 22:52:58 +0200 +Subject: serial: add port helpers +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com> +Message-ID: <1275425591-8803-19-git-send-email-arnd@arndb.de> + + +From: Alan Cox <alan@linux.intel.com> + +We can make this the same as the ones that will be needed by the tty_port +helper logic that we want to move to but still call them from the existing +code base. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/serial_core.c | 51 +++++++++++++++++++++++++++++++------------ + 1 file changed, 37 insertions(+), 14 deletions(-) + +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -1501,6 +1501,34 @@ static void uart_update_termios(struct t + } + } + ++static int uart_carrier_raised(struct tty_port *port) ++{ ++ struct uart_state *state = container_of(port, struct uart_state, port); ++ struct uart_port *uport = state->uart_port; ++ int mctrl; ++ mutex_lock(&port->mutex); ++ spin_lock_irq(&uport->lock); ++ uport->ops->enable_ms(uport); ++ mctrl = uport->ops->get_mctrl(uport); ++ spin_unlock_irq(&uport->lock); ++ mutex_unlock(&port->mutex); ++ if (mctrl & TIOCM_CAR) ++ return 1; ++ return 0; ++} ++ ++static void uart_dtr_rts(struct tty_port *port, int onoff) ++{ ++ struct uart_state *state = container_of(port, struct uart_state, port); ++ struct uart_port *uport = state->uart_port; ++ mutex_lock(&port->mutex); ++ if (onoff) ++ uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); ++ else ++ uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); ++ mutex_unlock(&port->mutex); ++} ++ + /* + * Block the open until the port is ready. We must be called with + * the per-port semaphore held. +@@ -1509,9 +1537,7 @@ static int + uart_block_til_ready(struct file *filp, struct uart_state *state) + { + DECLARE_WAITQUEUE(wait, current); +- struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; +- unsigned int mctrl; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); +@@ -1555,23 +1581,14 @@ uart_block_til_ready(struct file *filp, + * not set RTS here - we want to make sure we catch + * the data from the modem. + */ +- if (port->tty->termios->c_cflag & CBAUD) { +- mutex_lock(&port->mutex); +- uart_set_mctrl(uport, TIOCM_DTR); +- mutex_unlock(&port->mutex); +- } ++ if (port->tty->termios->c_cflag & CBAUD) ++ tty_port_raise_dtr_rts(port); + + /* + * and wait for the carrier to indicate that the + * modem is ready for us. + */ +- mutex_lock(&port->mutex); +- spin_lock_irq(&uport->lock); +- uport->ops->enable_ms(uport); +- mctrl = uport->ops->get_mctrl(uport); +- spin_unlock_irq(&uport->lock); +- mutex_unlock(&port->mutex); +- if (mctrl & TIOCM_CAR) ++ if (tty_port_carrier_raised(port)) + break; + + schedule(); +@@ -2349,6 +2366,11 @@ static const struct tty_operations uart_ + #endif + }; + ++static const struct tty_port_operations uart_port_ops = { ++ .carrier_raised = uart_carrier_raised, ++ .dtr_rts = uart_dtr_rts, ++}; ++ + /** + * uart_register_driver - register a driver with the uart core layer + * @drv: low level driver structure +@@ -2405,6 +2427,7 @@ int uart_register_driver(struct uart_dri + struct tty_port *port = &state->port; + + tty_port_init(port); ++ port->ops = &uart_port_ops; + port->close_delay = 500; /* .5 seconds */ + port->closing_wait = 30000; /* 30 seconds */ + tasklet_init(&state->tlet, uart_tasklet_action, diff --git a/tty/serial-add-uart_cap_efr-and-uart_cap_sleep-flags-to-16c950-uarts-definition.patch b/tty/serial-add-uart_cap_efr-and-uart_cap_sleep-flags-to-16c950-uarts-definition.patch new file mode 100644 index 00000000000000..86907075767144 --- /dev/null +++ b/tty/serial-add-uart_cap_efr-and-uart_cap_sleep-flags-to-16c950-uarts-definition.patch @@ -0,0 +1,33 @@ +From yegor_sub1@visionsystems.de Wed Jun 16 13:49:47 2010 +From: Yegor Yefremov <yegor_sub1@visionsystems.de> +Date: Wed, 16 Jun 2010 16:29:55 +0200 +Subject: serial: add UART_CAP_EFR and UART_CAP_SLEEP flags to 16C950 UARTs definition +To: linux-serial@vger.kernel.org +Cc: Greg KH <greg@kroah.com> +Message-ID: <4C18DFE3.2030109@visionsystems.de> + + +Adding UART_CAP_EFR and UART_CAP_SLEEP flags will enable sleep mode +and automatic CTS flow control for 16C950 UARTs. It will also avoid +capabilities detection warning like this: + +"ttyS0: detected caps 00000700 should be 00000100" + +Signed-off-by: Yegor Yefremov <yegorslists@googlemail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/8250.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/serial/8250.c ++++ b/drivers/serial/8250.c +@@ -241,7 +241,7 @@ static const struct serial8250_config ua + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, +- .flags = UART_CAP_FIFO, ++ .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16654] = { + .name = "ST16654", diff --git a/tty/serial-change-the-wait-for-carrier-locking.patch b/tty/serial-change-the-wait-for-carrier-locking.patch new file mode 100644 index 00000000000000..9ec2e928168bac --- /dev/null +++ b/tty/serial-change-the-wait-for-carrier-locking.patch @@ -0,0 +1,184 @@ +From arnd@arndb.de Wed Jun 16 13:35:17 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:52:57 +0200 +Subject: serial: Change the wait for carrier locking +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com> +Message-ID: <1275425591-8803-18-git-send-email-arnd@arndb.de> + + +From: Alan Cox <alan@linux.intel.com> + +We want to push the lock/unlock into the helper functions so that we +can prepare to move to using the tty_port helper. The expansion initially +comes out a bit ugly but its worth the temporary expansion IMHO just so +we can produce a nice testable series of changes. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/serial_core.c | 44 ++++++++++++++++++++++++++++++++++--------- + 1 file changed, 35 insertions(+), 9 deletions(-) + +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -1272,6 +1272,7 @@ static void uart_close(struct tty_struct + struct uart_state *state = tty->driver_data; + struct tty_port *port; + struct uart_port *uport; ++ unsigned long flags; + + BUG_ON(!kernel_locked()); + +@@ -1284,9 +1285,12 @@ static void uart_close(struct tty_struct + pr_debug("uart_close(%d) called\n", uport->line); + + mutex_lock(&port->mutex); ++ spin_lock_irqsave(&port->lock, flags); + +- if (tty_hung_up_p(filp)) ++ if (tty_hung_up_p(filp)) { ++ spin_unlock_irqrestore(&port->lock, flags); + goto done; ++ } + + if ((tty->count == 1) && (port->count != 1)) { + /* +@@ -1305,8 +1309,10 @@ static void uart_close(struct tty_struct + tty->name, port->count); + port->count = 0; + } +- if (port->count) ++ if (port->count) { ++ spin_unlock_irqrestore(&port->lock, flags); + goto done; ++ } + + /* + * Now we wait for the transmit buffer to clear; and we notify +@@ -1314,6 +1320,7 @@ static void uart_close(struct tty_struct + * setting tty->closing. + */ + tty->closing = 1; ++ spin_unlock_irqrestore(&port->lock, flags); + + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait)); +@@ -1340,20 +1347,26 @@ static void uart_close(struct tty_struct + + tty_ldisc_flush(tty); + +- tty->closing = 0; + tty_port_tty_set(port, NULL); ++ spin_lock_irqsave(&port->lock, flags); ++ tty->closing = 0; + + if (port->blocked_open) { ++ spin_unlock_irqrestore(&port->lock, flags); + if (port->close_delay) + msleep_interruptible(port->close_delay); ++ spin_lock_irqsave(&port->lock, flags); + } else if (!uart_console(uport)) { ++ spin_unlock_irqrestore(&port->lock, flags); + uart_change_pm(state, 3); ++ spin_lock_irqsave(&port->lock, flags); + } + + /* + * Wake up anyone trying to open this port. + */ + clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); ++ spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&port->open_wait); + + done: +@@ -1429,6 +1442,7 @@ static void uart_hangup(struct tty_struc + { + struct uart_state *state = tty->driver_data; + struct tty_port *port = &state->port; ++ unsigned long flags; + + BUG_ON(!kernel_locked()); + pr_debug("uart_hangup(%d)\n", state->uart_port->line); +@@ -1437,8 +1451,10 @@ static void uart_hangup(struct tty_struc + if (port->flags & ASYNC_NORMAL_ACTIVE) { + uart_flush_buffer(tty); + uart_shutdown(tty, state); ++ spin_lock_irqsave(&port->lock, flags); + port->count = 0; + clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); ++ spin_unlock_irqrestore(&port->lock, flags); + tty_port_tty_set(port, NULL); + wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->delta_msr_wait); +@@ -1496,9 +1512,13 @@ uart_block_til_ready(struct file *filp, + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; + unsigned int mctrl; ++ unsigned long flags; + ++ spin_lock_irqsave(&port->lock, flags); ++ if (!tty_hung_up_p(filp)) ++ port->count--; + port->blocked_open++; +- port->count--; ++ spin_unlock_irqrestore(&port->lock, flags); + + add_wait_queue(&port->open_wait, &wait); + while (1) { +@@ -1535,23 +1555,26 @@ uart_block_til_ready(struct file *filp, + * not set RTS here - we want to make sure we catch + * the data from the modem. + */ +- if (port->tty->termios->c_cflag & CBAUD) ++ if (port->tty->termios->c_cflag & CBAUD) { ++ mutex_lock(&port->mutex); + uart_set_mctrl(uport, TIOCM_DTR); ++ mutex_unlock(&port->mutex); ++ } + + /* + * and wait for the carrier to indicate that the + * modem is ready for us. + */ ++ mutex_lock(&port->mutex); + spin_lock_irq(&uport->lock); + uport->ops->enable_ms(uport); + mctrl = uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); ++ mutex_unlock(&port->mutex); + if (mctrl & TIOCM_CAR) + break; + +- mutex_unlock(&port->mutex); + schedule(); +- mutex_lock(&port->mutex); + + if (signal_pending(current)) + break; +@@ -1559,8 +1582,11 @@ uart_block_til_ready(struct file *filp, + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->open_wait, &wait); + +- port->count++; ++ spin_lock_irqsave(&port->lock, flags); ++ if (!tty_hung_up_p(filp)) ++ port->count++; + port->blocked_open--; ++ spin_unlock_irqrestore(&port->lock, flags); + + if (signal_pending(current)) + return -ERESTARTSYS; +@@ -1677,9 +1703,9 @@ static int uart_open(struct tty_struct * + /* + * If we succeeded, wait until the port is ready. + */ ++ mutex_unlock(&port->mutex); + if (retval == 0) + retval = uart_block_til_ready(filp, state); +- mutex_unlock(&port->mutex); + + /* + * If this is the first open to succeed, adjust things to suit. diff --git a/tty/serial-trim-locking-on-the-helpers.patch b/tty/serial-trim-locking-on-the-helpers.patch new file mode 100644 index 00000000000000..9f0337a4500478 --- /dev/null +++ b/tty/serial-trim-locking-on-the-helpers.patch @@ -0,0 +1,54 @@ +From arnd@arndb.de Wed Jun 16 13:36:19 2010 +From: Alan Cox <alan@linux.intel.com> +Date: Tue, 1 Jun 2010 22:52:59 +0200 +Subject: serial: trim locking on the helpers +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com> +Message-ID: <1275425591-8803-20-git-send-email-arnd@arndb.de> + + +From: Alan Cox <alan@linux.intel.com> + +The port mutex protects port->tty, but these paths never need to walk from +port->tty. They do need the low level lock as the API expects that but they +already also take it. + +Thus we can drop the extra mutex lock calls here. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/serial_core.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -1506,12 +1506,10 @@ static int uart_carrier_raised(struct tt + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + int mctrl; +- mutex_lock(&port->mutex); + spin_lock_irq(&uport->lock); + uport->ops->enable_ms(uport); + mctrl = uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); +- mutex_unlock(&port->mutex); + if (mctrl & TIOCM_CAR) + return 1; + return 0; +@@ -1521,12 +1519,11 @@ static void uart_dtr_rts(struct tty_port + { + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; +- mutex_lock(&port->mutex); ++ + if (onoff) + uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); + else + uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); +- mutex_unlock(&port->mutex); + } + + /* diff --git a/tty/serial-use-block_til_ready-helper.patch b/tty/serial-use-block_til_ready-helper.patch new file mode 100644 index 00000000000000..a87944d7935e9e --- /dev/null +++ b/tty/serial-use-block_til_ready-helper.patch @@ -0,0 +1,124 @@ +From arnd@arndb.de Wed Jun 16 13:36:52 2010 +From: Alan Cox <alan@linux.intel.com> +Date: Tue, 1 Jun 2010 22:53:00 +0200 +Subject: serial: Use block_til_ready helper +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com> +Message-ID: <1275425591-8803-21-git-send-email-arnd@arndb.de> + + +From: Alan Cox <alan@linux.intel.com> + +Our code now rather closely resembles the helper, so switch to it. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/serial_core.c | 87 ------------------------------------------- + 1 file changed, 1 insertion(+), 86 deletions(-) + +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -1526,91 +1526,6 @@ static void uart_dtr_rts(struct tty_port + uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); + } + +-/* +- * Block the open until the port is ready. We must be called with +- * the per-port semaphore held. +- */ +-static int +-uart_block_til_ready(struct file *filp, struct uart_state *state) +-{ +- DECLARE_WAITQUEUE(wait, current); +- struct tty_port *port = &state->port; +- unsigned long flags; +- +- spin_lock_irqsave(&port->lock, flags); +- if (!tty_hung_up_p(filp)) +- port->count--; +- port->blocked_open++; +- spin_unlock_irqrestore(&port->lock, flags); +- +- add_wait_queue(&port->open_wait, &wait); +- while (1) { +- set_current_state(TASK_INTERRUPTIBLE); +- +- /* +- * If we have been hung up, tell userspace/restart open. +- */ +- if (tty_hung_up_p(filp) || port->tty == NULL) +- break; +- +- /* +- * If the port has been closed, tell userspace/restart open. +- */ +- if (!(port->flags & ASYNC_INITIALIZED)) +- break; +- +- /* +- * If non-blocking mode is set, or CLOCAL mode is set, +- * we don't want to wait for the modem status lines to +- * indicate that the port is ready. +- * +- * Also, if the port is not enabled/configured, we want +- * to allow the open to succeed here. Note that we will +- * have set TTY_IO_ERROR for a non-existant port. +- */ +- if ((filp->f_flags & O_NONBLOCK) || +- (port->tty->termios->c_cflag & CLOCAL) || +- (port->tty->flags & (1 << TTY_IO_ERROR))) +- break; +- +- /* +- * Set DTR to allow modem to know we're waiting. Do +- * not set RTS here - we want to make sure we catch +- * the data from the modem. +- */ +- if (port->tty->termios->c_cflag & CBAUD) +- tty_port_raise_dtr_rts(port); +- +- /* +- * and wait for the carrier to indicate that the +- * modem is ready for us. +- */ +- if (tty_port_carrier_raised(port)) +- break; +- +- schedule(); +- +- if (signal_pending(current)) +- break; +- } +- set_current_state(TASK_RUNNING); +- remove_wait_queue(&port->open_wait, &wait); +- +- spin_lock_irqsave(&port->lock, flags); +- if (!tty_hung_up_p(filp)) +- port->count++; +- port->blocked_open--; +- spin_unlock_irqrestore(&port->lock, flags); +- +- if (signal_pending(current)) +- return -ERESTARTSYS; +- +- if (!port->tty || tty_hung_up_p(filp)) +- return -EAGAIN; +- +- return 0; +-} +- + static struct uart_state *uart_get(struct uart_driver *drv, int line) + { + struct uart_state *state; +@@ -1719,7 +1634,7 @@ static int uart_open(struct tty_struct * + */ + mutex_unlock(&port->mutex); + if (retval == 0) +- retval = uart_block_til_ready(filp, state); ++ retval = tty_port_block_til_ready(port, tty, filp); + + /* + * If this is the first open to succeed, adjust things to suit. diff --git a/tty/tty-fix-console_sem-lock-order.patch b/tty/tty-fix-console_sem-lock-order.patch new file mode 100644 index 00000000000000..2a79928346d3d4 --- /dev/null +++ b/tty/tty-fix-console_sem-lock-order.patch @@ -0,0 +1,42 @@ +From arnd@arndb.de Wed Jun 16 13:42:16 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:03 +0200 +Subject: tty: fix console_sem lock order +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-24-git-send-email-arnd@arndb.de> + + +vgacon_do_font_op releases and reacquires the BTM while holding +console_sem. This violates the rule that BTM has to be the +outer lock whenever we hold both. + +There does not seem to be any reason to give up the BTM here, +so just stop doing that. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/video/console/vgacon.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/video/console/vgacon.c ++++ b/drivers/video/console/vgacon.c +@@ -1108,7 +1108,6 @@ static int vgacon_do_font_op(struct vgas + charmap += 4 * cmapsz; + #endif + +- tty_unlock(); + spin_lock_irq(&vga_lock); + /* First, the Sequencer */ + vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1); +@@ -1192,7 +1191,6 @@ static int vgacon_do_font_op(struct vgas + vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0); + } + spin_unlock_irq(&vga_lock); +- tty_lock(); + return 0; + } + diff --git a/tty/tty-implement-btm-as-mutex-instead-of-bkl.patch b/tty/tty-implement-btm-as-mutex-instead-of-bkl.patch new file mode 100644 index 00000000000000..ce84ae064e6b9a --- /dev/null +++ b/tty/tty-implement-btm-as-mutex-instead-of-bkl.patch @@ -0,0 +1,139 @@ +From arnd@arndb.de Wed Jun 16 13:46:10 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:09 +0200 +Subject: tty: implement BTM as mutex instead of BKL +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-30-git-send-email-arnd@arndb.de> + + +The tty locking now follows the rules for mutexes, so +we can replace the BKL usage with a new subsystem +wide mutex. + +This patch for now makes the new behaviour an optional +experimental feature that can be enabled for testing +purposes. + +Using a regular mutex here will change the behaviour +when blocked on the BTM from spinning to sleeping, +but that should not be visible to the user. + +Using the mutex also means that all the BTM is now +covered by lockdep. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/Makefile | 1 + + drivers/char/tty_mutex.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/tty.h | 8 ++++++++ + lib/Kconfig.debug | 10 ++++++++++ + 4 files changed, 66 insertions(+) + +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -9,6 +9,7 @@ FONTMAPFILE = cp437.uni + + obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o + ++obj-$(CONFIG_TTY_MUTEX) += tty_mutex.o + obj-$(CONFIG_LEGACY_PTYS) += pty.o + obj-$(CONFIG_UNIX98_PTYS) += pty.o + obj-y += misc.o +--- /dev/null ++++ b/drivers/char/tty_mutex.c +@@ -0,0 +1,47 @@ ++/* ++ * drivers/char/tty_lock.c ++ */ ++#include <linux/tty.h> ++#include <linux/module.h> ++#include <linux/kallsyms.h> ++#include <linux/semaphore.h> ++#include <linux/sched.h> ++ ++/* ++ * The 'big tty mutex' ++ * ++ * This mutex is taken and released by tty_lock() and tty_unlock(), ++ * replacing the older big kernel lock. ++ * It can no longer be taken recursively, and does not get ++ * released implicitly while sleeping. ++ * ++ * Don't use in new code. ++ */ ++static DEFINE_MUTEX(big_tty_mutex); ++struct task_struct *__big_tty_mutex_owner; ++EXPORT_SYMBOL_GPL(__big_tty_mutex_owner); ++ ++/* ++ * Getting the big tty mutex. ++ */ ++void __lockfunc tty_lock(void) ++{ ++ struct task_struct *task = current; ++ ++ WARN_ON(__big_tty_mutex_owner == task); ++ ++ mutex_lock(&big_tty_mutex); ++ __big_tty_mutex_owner = task; ++} ++EXPORT_SYMBOL(tty_lock); ++ ++void __lockfunc tty_unlock(void) ++{ ++ struct task_struct *task = current; ++ ++ WARN_ON(__big_tty_mutex_owner != task); ++ __big_tty_mutex_owner = NULL; ++ ++ mutex_unlock(&big_tty_mutex); ++} ++EXPORT_SYMBOL(tty_unlock); +--- a/include/linux/tty.h ++++ b/include/linux/tty.h +@@ -574,7 +574,14 @@ extern int vt_ioctl(struct tty_struct *t + extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg); + ++/* tty_mutex.c */ + /* functions for preparation of BKL removal */ ++#ifdef CONFIG_TTY_MUTEX ++extern void __lockfunc tty_lock(void) __acquires(tty_lock); ++extern void __lockfunc tty_unlock(void) __releases(tty_lock); ++extern struct task_struct *__big_tty_mutex_owner; ++#define tty_locked() (current == __big_tty_mutex_owner) ++#else + static inline void tty_lock(void) __acquires(kernel_lock) + { + #ifdef CONFIG_LOCK_KERNEL +@@ -588,6 +595,7 @@ static inline void tty_unlock(void) __re + unlock_kernel(); + } + #define tty_locked() (kernel_locked()) ++#endif + + /* + * wait_event_interruptible_tty -- wait for a condition with the tty lock held +--- a/lib/Kconfig.debug ++++ b/lib/Kconfig.debug +@@ -428,6 +428,16 @@ config RT_MUTEX_TESTER + help + This option enables a rt-mutex tester. + ++config TTY_MUTEX ++ bool "Use a mutex instead of BKL for TTY locking" ++ depends on EXPERIMENTAL && SMP ++ help ++ The TTY subsystem traditionally depends on the big kernel lock ++ for serialization. Saying Y here replaces the BKL with the Big ++ TTY Mutex (BTM). ++ Building a kernel without the BKL is only possible with TTY_MUTEX ++ enabled. ++ + config DEBUG_SPINLOCK + bool "Spinlock and rw-lock debugging: basic checks" + depends on DEBUG_KERNEL diff --git a/tty/tty-introduce-wait_event_interruptible_tty.patch b/tty/tty-introduce-wait_event_interruptible_tty.patch new file mode 100644 index 00000000000000..47f854759382de --- /dev/null +++ b/tty/tty-introduce-wait_event_interruptible_tty.patch @@ -0,0 +1,191 @@ +From arnd@arndb.de Wed Jun 16 13:43:07 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:05 +0200 +Subject: tty: introduce wait_event_interruptible_tty +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-26-git-send-email-arnd@arndb.de> + + +Calling wait_event_interruptible implicitly +releases the BKL when it sleeps, but we need +to do this explcitly when we have converted +it to a mutex. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/cyclades.c | 2 +- + drivers/char/istallion.c | 12 ++++++++---- + drivers/char/n_r3964.c | 2 +- + drivers/char/tty_port.c | 2 +- + drivers/char/vt_ioctl.c | 2 +- + drivers/serial/crisv10.c | 4 ++-- + include/linux/tty.h | 42 ++++++++++++++++++++++++++++++++++++++++++ + 7 files changed, 56 insertions(+), 10 deletions(-) + +--- a/drivers/char/cyclades.c ++++ b/drivers/char/cyclades.c +@@ -1607,7 +1607,7 @@ static int cy_open(struct tty_struct *tt + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { +- wait_event_interruptible(info->port.close_wait, ++ wait_event_interruptible_tty(info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); + return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; + } +--- a/drivers/char/istallion.c ++++ b/drivers/char/istallion.c +@@ -954,7 +954,7 @@ static int stli_rawopen(struct stlibrd * + * order of opens and closes may not be preserved across shared + * memory, so we must wait until it is complete. + */ +- wait_event_interruptible(portp->raw_wait, ++ wait_event_interruptible_tty(portp->raw_wait, + !test_bit(ST_CLOSING, &portp->state)); + if (signal_pending(current)) { + return -ERESTARTSYS; +@@ -989,7 +989,7 @@ static int stli_rawopen(struct stlibrd * + set_bit(ST_OPENING, &portp->state); + spin_unlock_irqrestore(&brd_lock, flags); + +- wait_event_interruptible(portp->raw_wait, ++ wait_event_interruptible_tty(portp->raw_wait, + !test_bit(ST_OPENING, &portp->state)); + if (signal_pending(current)) + rc = -ERESTARTSYS; +@@ -1020,7 +1020,7 @@ static int stli_rawclose(struct stlibrd + * occurs on this port. + */ + if (wait) { +- wait_event_interruptible(portp->raw_wait, ++ wait_event_interruptible_tty(portp->raw_wait, + !test_bit(ST_CLOSING, &portp->state)); + if (signal_pending(current)) { + return -ERESTARTSYS; +@@ -1052,7 +1052,7 @@ static int stli_rawclose(struct stlibrd + * to come back. + */ + rc = 0; +- wait_event_interruptible(portp->raw_wait, ++ wait_event_interruptible_tty(portp->raw_wait, + !test_bit(ST_CLOSING, &portp->state)); + if (signal_pending(current)) + rc = -ERESTARTSYS; +@@ -1073,6 +1073,10 @@ static int stli_rawclose(struct stlibrd + + static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback) + { ++ /* ++ * no need for wait_event_tty because clearing ST_CMDING cannot block ++ * on BTM ++ */ + wait_event_interruptible(portp->raw_wait, + !test_bit(ST_CMDING, &portp->state)); + if (signal_pending(current)) +--- a/drivers/char/n_r3964.c ++++ b/drivers/char/n_r3964.c +@@ -1079,7 +1079,7 @@ static ssize_t r3964_read(struct tty_str + goto unlock; + } + /* block until there is a message: */ +- wait_event_interruptible(pInfo->read_wait, ++ wait_event_interruptible_tty(pInfo->read_wait, + (pMsg = remove_msg(pInfo, pClient))); + } + +--- a/drivers/char/tty_port.c ++++ b/drivers/char/tty_port.c +@@ -231,7 +231,7 @@ int tty_port_block_til_ready(struct tty_ + + /* block if port is in the process of being closed */ + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { +- wait_event_interruptible(port->close_wait, ++ wait_event_interruptible_tty(port->close_wait, + !(port->flags & ASYNC_CLOSING)); + if (port->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; +--- a/drivers/char/vt_ioctl.c ++++ b/drivers/char/vt_ioctl.c +@@ -133,7 +133,7 @@ static void vt_event_wait(struct vt_even + list_add(&vw->list, &vt_events); + spin_unlock_irqrestore(&vt_event_lock, flags); + /* Wait for it to pass */ +- wait_event_interruptible(vt_event_waitqueue, vw->done); ++ wait_event_interruptible_tty(vt_event_waitqueue, vw->done); + /* Dequeue it */ + spin_lock_irqsave(&vt_event_lock, flags); + list_del(&vw->list); +--- a/drivers/serial/crisv10.c ++++ b/drivers/serial/crisv10.c +@@ -3981,7 +3981,7 @@ block_til_ready(struct tty_struct *tty, + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { +- wait_event_interruptible(info->close_wait, ++ wait_event_interruptible_tty(info->close_wait, + !(info->flags & ASYNC_CLOSING)); + #ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) +@@ -4139,7 +4139,7 @@ rs_open(struct tty_struct *tty, struct f + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { +- wait_event_interruptible(info->close_wait, ++ wait_event_interruptible_tty(info->close_wait, + !(info->flags & ASYNC_CLOSING)); + #ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? +--- a/include/linux/tty.h ++++ b/include/linux/tty.h +@@ -603,5 +603,47 @@ static inline void tty_unlock(void) __re + } + #define tty_locked() (kernel_locked()) + ++/* ++ * wait_event_interruptible_tty -- wait for a condition with the tty lock held ++ * ++ * The condition we are waiting for might take a long time to ++ * become true, or might depend on another thread taking the ++ * BTM. In either case, we need to drop the BTM to guarantee ++ * forward progress. This is a leftover from the conversion ++ * from the BKL and should eventually get removed as the BTM ++ * falls out of use. ++ * ++ * Do not use in new code. ++ */ ++#define wait_event_interruptible_tty(wq, condition) \ ++({ \ ++ int __ret = 0; \ ++ if (!(condition)) { \ ++ __wait_event_interruptible_tty(wq, condition, __ret); \ ++ } \ ++ __ret; \ ++}) ++ ++#define __wait_event_interruptible_tty(wq, condition, ret) \ ++do { \ ++ DEFINE_WAIT(__wait); \ ++ \ ++ for (;;) { \ ++ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ ++ if (condition) \ ++ break; \ ++ if (!signal_pending(current)) { \ ++ tty_unlock(); \ ++ schedule(); \ ++ tty_lock(); \ ++ continue; \ ++ } \ ++ ret = -ERESTARTSYS; \ ++ break; \ ++ } \ ++ finish_wait(&wq, &__wait); \ ++} while (0) ++ ++ + #endif /* __KERNEL__ */ + #endif diff --git a/tty/tty-make-vt-s-have-a-tty_port.patch b/tty/tty-make-vt-s-have-a-tty_port.patch new file mode 100644 index 00000000000000..2ae2becfbe6431 --- /dev/null +++ b/tty/tty-make-vt-s-have-a-tty_port.patch @@ -0,0 +1,53 @@ +From arnd@arndb.de Wed Jun 16 13:31:12 2010 +From: Alan Cox <alan@linux.intel.com> +Date: Tue, 1 Jun 2010 22:52:55 +0200 +Subject: tty: Make vt's have a tty_port +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com> +Message-ID: <1275425591-8803-16-git-send-email-arnd@arndb.de> + + +From: Alan Cox <alan@linux.intel.com> + +The vt layer isn't safely handling reference counts to tty object on the input +side. Add a tty port structure to the vt layer in order to implement this using +the standard helpers. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/vt.c | 2 ++ + include/linux/console_struct.h | 2 ++ + 2 files changed, 4 insertions(+) + +--- a/drivers/char/vt.c ++++ b/drivers/char/vt.c +@@ -773,6 +773,7 @@ int vc_allocate(unsigned int currcons) / + if (!vc) + return -ENOMEM; + vc_cons[currcons].d = vc; ++ tty_port_init(&vc->port); + INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); + visual_init(vc, currcons, 1); + if (!*vc->vc_uni_pagedir_loc) +@@ -2910,6 +2911,7 @@ static int __init con_init(void) + for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) { + vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT); + INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); ++ tty_port_init(&vc->port); + visual_init(vc, currcons, 1); + vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT); + vc_init(vc, vc->vc_rows, vc->vc_cols, +--- a/include/linux/console_struct.h ++++ b/include/linux/console_struct.h +@@ -21,6 +21,8 @@ struct vt_struct; + #define NPAR 16 + + struct vc_data { ++ struct tty_port port; /* Upper level data */ ++ + unsigned short vc_num; /* Console number */ + unsigned int vc_cols; /* [#] Console size */ + unsigned int vc_rows; diff --git a/tty/tty-move-the-vt_tty-field-from-the-vc_data-into-the-standard-tty_port.patch b/tty/tty-move-the-vt_tty-field-from-the-vc_data-into-the-standard-tty_port.patch new file mode 100644 index 00000000000000..47781353c35c88 --- /dev/null +++ b/tty/tty-move-the-vt_tty-field-from-the-vc_data-into-the-standard-tty_port.patch @@ -0,0 +1,134 @@ +From arnd@arndb.de Wed Jun 16 13:33:03 2010 +From: Alan Cox <alan@linux.intel.com> +Date: Tue, 1 Jun 2010 22:52:56 +0200 +Subject: tty: Move the vt_tty field from the vc_data into the standard tty_port +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com> +Message-ID: <1275425591-8803-17-git-send-email-arnd@arndb.de> + + +From: Alan Cox <alan@linux.intel.com> + +This takes all the tty references through the expected interface points so +we can refcount them. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/keyboard.c | 10 +++++----- + drivers/char/vt.c | 10 +++++----- + drivers/char/vt_ioctl.c | 2 +- + include/linux/console_struct.h | 1 - + 4 files changed, 11 insertions(+), 12 deletions(-) + +--- a/drivers/char/keyboard.c ++++ b/drivers/char/keyboard.c +@@ -299,7 +299,7 @@ int kbd_rate(struct kbd_repeat *rep) + */ + static void put_queue(struct vc_data *vc, int ch) + { +- struct tty_struct *tty = vc->vc_tty; ++ struct tty_struct *tty = vc->port.tty; + + if (tty) { + tty_insert_flip_char(tty, ch, 0); +@@ -309,7 +309,7 @@ static void put_queue(struct vc_data *vc + + static void puts_queue(struct vc_data *vc, char *cp) + { +- struct tty_struct *tty = vc->vc_tty; ++ struct tty_struct *tty = vc->port.tty; + + if (!tty) + return; +@@ -485,7 +485,7 @@ static void fn_show_ptregs(struct vc_dat + + static void fn_hold(struct vc_data *vc) + { +- struct tty_struct *tty = vc->vc_tty; ++ struct tty_struct *tty = vc->port.tty; + + if (rep || !tty) + return; +@@ -563,7 +563,7 @@ static void fn_inc_console(struct vc_dat + + static void fn_send_intr(struct vc_data *vc) + { +- struct tty_struct *tty = vc->vc_tty; ++ struct tty_struct *tty = vc->port.tty; + + if (!tty) + return; +@@ -1162,7 +1162,7 @@ static void kbd_keycode(unsigned int key + struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down }; + int rc; + +- tty = vc->vc_tty; ++ tty = vc->port.tty; + + if (tty && (!tty->driver_data)) { + /* No driver data? Strange. Okay we fix it then. */ +--- a/drivers/char/vt.c ++++ b/drivers/char/vt.c +@@ -962,12 +962,12 @@ static int vc_do_resize(struct tty_struc + * Resize a virtual console as seen from the console end of things. We + * use the common vc_do_resize methods to update the structures. The + * caller must hold the console sem to protect console internals and +- * vc->vc_tty ++ * vc->port.tty + */ + + int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) + { +- return vc_do_resize(vc->vc_tty, vc, cols, rows); ++ return vc_do_resize(vc->port.tty, vc, cols, rows); + } + + /** +@@ -2796,12 +2796,12 @@ static int con_open(struct tty_struct *t + struct vc_data *vc = vc_cons[currcons].d; + + /* Still being freed */ +- if (vc->vc_tty) { ++ if (vc->port.tty) { + release_console_sem(); + return -ERESTARTSYS; + } + tty->driver_data = vc; +- vc->vc_tty = tty; ++ vc->port.tty = tty; + + if (!tty->winsize.ws_row && !tty->winsize.ws_col) { + tty->winsize.ws_row = vc_cons[currcons].d->vc_rows; +@@ -2829,7 +2829,7 @@ static void con_shutdown(struct tty_stru + struct vc_data *vc = tty->driver_data; + BUG_ON(vc == NULL); + acquire_console_sem(); +- vc->vc_tty = NULL; ++ vc->port.tty = NULL; + release_console_sem(); + tty_shutdown(tty); + } +--- a/drivers/char/vt_ioctl.c ++++ b/drivers/char/vt_ioctl.c +@@ -1369,7 +1369,7 @@ void vc_SAK(struct work_struct *work) + acquire_console_sem(); + vc = vc_con->d; + if (vc) { +- tty = vc->vc_tty; ++ tty = vc->port.tty; + /* + * SAK should also work in all raw modes and reset + * them properly. +--- a/include/linux/console_struct.h ++++ b/include/linux/console_struct.h +@@ -58,7 +58,6 @@ struct vc_data { + /* VT terminal data */ + unsigned int vc_state; /* Escape sequence parser state */ + unsigned int vc_npar,vc_par[NPAR]; /* Parameters of current escape sequence */ +- struct tty_struct *vc_tty; /* TTY we are attached to */ + /* data for manual vt switching */ + struct vt_mode vt_mode; + struct pid *vt_pid; diff --git a/tty/tty-never-hold-btm-while-getting-tty_mutex.patch b/tty/tty-never-hold-btm-while-getting-tty_mutex.patch new file mode 100644 index 00000000000000..568d2747ee81ca --- /dev/null +++ b/tty/tty-never-hold-btm-while-getting-tty_mutex.patch @@ -0,0 +1,111 @@ +From arnd@arndb.de Wed Jun 16 13:41:48 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:02 +0200 +Subject: tty: never hold BTM while getting tty_mutex +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-23-git-send-email-arnd@arndb.de> + + +tty_mutex is never taken with the BTM held, except for +two corner cases that are worked around here. +We give up the BTM before calling tty_release() in the +error path of tty_open(). +Similarly, we reorder the locking in ptmx_open() +to get tty_mutex before the BTM. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/pty.c | 24 +++++++++++------------- + drivers/char/tty_io.c | 12 ++++++------ + 2 files changed, 17 insertions(+), 19 deletions(-) + +--- a/drivers/char/pty.c ++++ b/drivers/char/pty.c +@@ -626,7 +626,7 @@ static const struct tty_operations pty_u + * allocated_ptys_lock handles the list of free pty numbers + */ + +-static int __ptmx_open(struct inode *inode, struct file *filp) ++static int ptmx_open(struct inode *inode, struct file *filp) + { + struct tty_struct *tty; + int retval; +@@ -635,11 +635,14 @@ static int __ptmx_open(struct inode *ino + nonseekable_open(inode, filp); + + /* find a device that is not in use. */ ++ tty_lock(); + index = devpts_new_index(inode); ++ tty_unlock(); + if (index < 0) + return index; + + mutex_lock(&tty_mutex); ++ tty_lock(); + tty = tty_init_dev(ptm_driver, index, 1); + mutex_unlock(&tty_mutex); + +@@ -657,24 +660,19 @@ static int __ptmx_open(struct inode *ino + goto out1; + + retval = ptm_driver->ops->open(tty, filp); +- if (!retval) +- return 0; ++ if (retval) ++ goto out2; + out1: ++ tty_unlock(); ++ return retval; ++out2: ++ tty_unlock(); + tty_release(inode, filp); + return retval; + out: + devpts_kill_index(inode, index); +- return retval; +-} +- +-static int ptmx_open(struct inode *inode, struct file *filp) +-{ +- int ret; +- +- tty_lock(); +- ret = __ptmx_open(inode, filp); + tty_unlock(); +- return ret; ++ return retval; + } + + static struct file_operations ptmx_fops; +--- a/drivers/char/tty_io.c ++++ b/drivers/char/tty_io.c +@@ -1866,19 +1866,19 @@ got_driver: + printk(KERN_DEBUG "error %d in opening %s...", retval, + tty->name); + #endif ++ tty_unlock(); /* need to call tty_release without BTM */ + tty_release(inode, filp); +- if (retval != -ERESTARTSYS) { +- tty_unlock(); ++ if (retval != -ERESTARTSYS) + return retval; +- } +- if (signal_pending(current)) { +- tty_unlock(); ++ ++ if (signal_pending(current)) + return retval; +- } ++ + schedule(); + /* + * Need to reset f_op in case a hangup happened. + */ ++ tty_lock(); + if (filp->f_op == &hung_up_tty_fops) + filp->f_op = &tty_fops; + tty_unlock(); diff --git a/tty/tty-release-btm-while-sleeping-in-block_til_ready.patch b/tty/tty-release-btm-while-sleeping-in-block_til_ready.patch new file mode 100644 index 00000000000000..6d7656ab1da5ad --- /dev/null +++ b/tty/tty-release-btm-while-sleeping-in-block_til_ready.patch @@ -0,0 +1,175 @@ +From arnd@arndb.de Wed Jun 16 13:47:56 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:10 +0200 +Subject: tty: release BTM while sleeping in block_til_ready +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-31-git-send-email-arnd@arndb.de> + + +Most tty drivers may block while opening a device. +Since this possibly depends on another thread +closing it first and both threads may need the BTM, +we need to release it here. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/amiserial.c | 2 ++ + drivers/char/ip2/ip2main.c | 4 ++++ + drivers/char/serial167.c | 4 +++- + drivers/char/specialix.c | 2 ++ + drivers/char/synclink.c | 2 ++ + drivers/char/synclink_gt.c | 2 ++ + drivers/char/synclinkmp.c | 2 ++ + drivers/char/tty_port.c | 2 ++ + drivers/serial/68328serial.c | 2 ++ + drivers/serial/68360serial.c | 2 ++ + drivers/serial/crisv10.c | 2 ++ + 11 files changed, 25 insertions(+), 1 deletion(-) + +--- a/drivers/char/amiserial.c ++++ b/drivers/char/amiserial.c +@@ -1710,7 +1710,9 @@ static int block_til_ready(struct tty_st + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); + #endif ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); +--- a/drivers/char/ip2/ip2main.c ++++ b/drivers/char/ip2/ip2main.c +@@ -1486,7 +1486,9 @@ ip2_open( PTTY tty, struct file *pFile ) + + if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) { + if ( pCh->flags & ASYNC_CLOSING ) { ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + if ( tty_hung_up_p(pFile) ) { + set_current_state( TASK_RUNNING ); +@@ -1548,7 +1550,9 @@ ip2_open( PTTY tty, struct file *pFile ) + rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS); + break; + } ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + set_current_state( TASK_RUNNING ); + remove_wait_queue(&pCh->open_wait, &wait); +--- a/drivers/char/serial167.c ++++ b/drivers/char/serial167.c +@@ -1786,7 +1786,9 @@ block_til_ready(struct tty_struct *tty, + tty->name, info->count); + /**/ + #endif +- schedule(); ++ tty_unlock(); ++ schedule(); ++ tty_lock(); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); +--- a/drivers/char/specialix.c ++++ b/drivers/char/specialix.c +@@ -1365,7 +1365,9 @@ static int block_til_ready(struct tty_st + retval = -ERESTARTSYS; + break; + } ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + + set_current_state(TASK_RUNNING); +--- a/drivers/char/synclink.c ++++ b/drivers/char/synclink.c +@@ -3349,7 +3349,9 @@ static int block_til_ready(struct tty_st + printk("%s(%d):block_til_ready blocking on %s count=%d\n", + __FILE__,__LINE__, tty->driver->name, port->count ); + ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + + set_current_state(TASK_RUNNING); +--- a/drivers/char/synclink_gt.c ++++ b/drivers/char/synclink_gt.c +@@ -3244,7 +3244,9 @@ static int block_til_ready(struct tty_st + } + + DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + + set_current_state(TASK_RUNNING); +--- a/drivers/char/synclinkmp.c ++++ b/drivers/char/synclinkmp.c +@@ -3365,7 +3365,9 @@ static int block_til_ready(struct tty_st + printk("%s(%d):%s block_til_ready() count=%d\n", + __FILE__,__LINE__, tty->driver->name, port->count ); + ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + + set_current_state(TASK_RUNNING); +--- a/drivers/char/tty_port.c ++++ b/drivers/char/tty_port.c +@@ -294,7 +294,9 @@ int tty_port_block_til_ready(struct tty_ + retval = -ERESTARTSYS; + break; + } ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + finish_wait(&port->open_wait, &wait); + +--- a/drivers/serial/68328serial.c ++++ b/drivers/serial/68328serial.c +@@ -1235,7 +1235,9 @@ static int block_til_ready(struct tty_st + retval = -ERESTARTSYS; + break; + } ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); +--- a/drivers/serial/68360serial.c ++++ b/drivers/serial/68360serial.c +@@ -1860,7 +1860,9 @@ static int block_til_ready(struct tty_st + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); + #endif ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); +--- a/drivers/serial/crisv10.c ++++ b/drivers/serial/crisv10.c +@@ -4055,7 +4055,9 @@ block_til_ready(struct tty_struct *tty, + printk("block_til_ready blocking: ttyS%d, count = %d\n", + info->line, info->count); + #endif ++ tty_unlock(); + schedule(); ++ tty_lock(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); diff --git a/tty/tty-remove-tty_lock_nested.patch b/tty/tty-remove-tty_lock_nested.patch new file mode 100644 index 00000000000000..544676d2f12f76 --- /dev/null +++ b/tty/tty-remove-tty_lock_nested.patch @@ -0,0 +1,219 @@ +From arnd@arndb.de Wed Jun 16 13:45:36 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:08 +0200 +Subject: tty: remove tty_lock_nested +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-29-git-send-email-arnd@arndb.de> + + +This changes all remaining users of tty_lock_nested +to be non-recursive, which lets us kill this function. +As a consequence, we won't need to keep the lock count +any more, which allows more simplifications later. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/pty.c | 2 +- + drivers/char/selection.c | 4 ++-- + drivers/char/tty_io.c | 41 ++++++++++++++++++++--------------------- + drivers/char/tty_ldisc.c | 3 +-- + include/linux/tty.h | 16 +--------------- + 5 files changed, 25 insertions(+), 41 deletions(-) + +--- a/drivers/char/pty.c ++++ b/drivers/char/pty.c +@@ -62,7 +62,7 @@ static void pty_close(struct tty_struct + if (tty->driver == ptm_driver) + devpts_pty_kill(tty->link); + #endif +- tty_vhangup(tty->link); ++ tty_vhangup_locked(tty->link); + } + } + +--- a/drivers/char/selection.c ++++ b/drivers/char/selection.c +@@ -313,7 +313,8 @@ int paste_selection(struct tty_struct *t + struct tty_ldisc *ld; + DECLARE_WAITQUEUE(wait, current); + +- tty_lock_nested(); /* always called with BTM from vt_ioctl */ ++ /* always called with BTM from vt_ioctl */ ++ WARN_ON(!tty_locked()); + + acquire_console_sem(); + poke_blanked_console(); +@@ -343,6 +344,5 @@ int paste_selection(struct tty_struct *t + __set_current_state(TASK_RUNNING); + + tty_ldisc_deref(ld); +- tty_unlock(); + return 0; + } +--- a/drivers/char/tty_io.c ++++ b/drivers/char/tty_io.c +@@ -492,10 +492,8 @@ EXPORT_SYMBOL_GPL(tty_wakeup); + * tasklist_lock to walk task list for hangup event + * ->siglock to protect ->signal/->sighand + */ +-static void do_tty_hangup(struct work_struct *work) ++void tty_vhangup_locked(struct tty_struct *tty) + { +- struct tty_struct *tty = +- container_of(work, struct tty_struct, hangup_work); + struct file *cons_filp = NULL; + struct file *filp, *f = NULL; + struct task_struct *p; +@@ -517,8 +515,6 @@ static void do_tty_hangup(struct work_st + /* inuse_filps is protected by the single tty lock, + this really needs to change if we want to flush the + workqueue with the lock held */ +- tty_lock_nested(); /* called with BTM held from pty_close and +- others */ + check_tty_count(tty, "do_tty_hangup"); + + file_list_lock(); +@@ -598,11 +594,20 @@ static void do_tty_hangup(struct work_st + */ + set_bit(TTY_HUPPED, &tty->flags); + tty_ldisc_enable(tty); +- tty_unlock(); + if (f) + fput(f); + } + ++static void do_tty_hangup(struct work_struct *work) ++{ ++ struct tty_struct *tty = ++ container_of(work, struct tty_struct, hangup_work); ++ ++ tty_lock(); ++ tty_vhangup_locked(tty); ++ tty_unlock(); ++} ++ + /** + * tty_hangup - trigger a hangup event + * @tty: tty to hangup +@@ -638,7 +643,9 @@ void tty_vhangup(struct tty_struct *tty) + + printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); + #endif +- do_tty_hangup(&tty->hangup_work); ++ tty_lock(); ++ tty_vhangup_locked(tty); ++ tty_unlock(); + } + + EXPORT_SYMBOL(tty_vhangup); +@@ -719,10 +726,12 @@ void disassociate_ctty(int on_exit) + tty = get_current_tty(); + if (tty) { + tty_pgrp = get_pid(tty->pgrp); +- tty_lock_nested(); /* see above */ +- if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) +- tty_vhangup(tty); +- tty_unlock(); ++ if (on_exit) { ++ tty_lock(); ++ if (tty->driver->type != TTY_DRIVER_TYPE_PTY) ++ tty_vhangup_locked(tty); ++ tty_unlock(); ++ } + tty_kref_put(tty); + } else if (on_exit) { + struct pid *old_pgrp; +@@ -1213,18 +1222,14 @@ static int tty_driver_install_tty(struct + int ret; + + if (driver->ops->install) { +- tty_lock_nested(); /* already called with BTM held */ + ret = driver->ops->install(driver, tty); +- tty_unlock(); + return ret; + } + + if (tty_init_termios(tty) == 0) { +- tty_lock_nested(); + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; +- tty_unlock(); + return 0; + } + return -ENOMEM; +@@ -1317,15 +1322,11 @@ struct tty_struct *tty_init_dev(struct t + struct tty_struct *tty; + int retval; + +- tty_lock_nested(); /* always called with tty lock held already */ +- + /* Check if pty master is being opened multiple times */ + if (driver->subtype == PTY_TYPE_MASTER && + (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) { +- tty_unlock(); + return ERR_PTR(-EIO); + } +- tty_unlock(); + + /* + * First time open is complex, especially for PTY devices. +@@ -1369,9 +1370,7 @@ release_mem_out: + if (printk_ratelimit()) + printk(KERN_INFO "tty_init_dev: ldisc open failed, " + "clearing slot %d\n", idx); +- tty_lock_nested(); + release_tty(tty, idx); +- tty_unlock(); + return ERR_PTR(retval); + } + +--- a/drivers/char/tty_ldisc.c ++++ b/drivers/char/tty_ldisc.c +@@ -450,9 +450,8 @@ static int tty_ldisc_open(struct tty_str + if (ld->ops->open) { + int ret; + /* BTM here locks versus a hangup event */ +- tty_lock_nested(); /* always held here already */ ++ WARN_ON(!tty_locked()); + ret = ld->ops->open(tty); +- tty_unlock(); + return ret; + } + return 0; +--- a/include/linux/tty.h ++++ b/include/linux/tty.h +@@ -416,6 +416,7 @@ extern int is_ignored(int sig); + extern int tty_signal(int sig, struct tty_struct *tty); + extern void tty_hangup(struct tty_struct *tty); + extern void tty_vhangup(struct tty_struct *tty); ++extern void tty_vhangup_locked(struct tty_struct *tty); + extern void tty_vhangup_self(void); + extern void tty_unhangup(struct file *filp); + extern int tty_hung_up_p(struct file *filp); +@@ -574,21 +575,6 @@ extern long vt_compat_ioctl(struct tty_s + unsigned int cmd, unsigned long arg); + + /* functions for preparation of BKL removal */ +- +-/* +- * tty_lock_nested get the tty_lock while potentially holding it +- * +- * The Big TTY Mutex is a recursive lock, meaning you can take it +- * from a thread that is already holding it. +- * This is bad for a number of reasons, so tty_lock_nested should +- * really be used as rarely as possible. If a code location can +- * be shown to never get called with this held already, it should +- * use tty_lock() instead. +- */ +-static inline void __lockfunc tty_lock_nested(void) __acquires(kernel_lock) +-{ +- lock_kernel(); +-} + static inline void tty_lock(void) __acquires(kernel_lock) + { + #ifdef CONFIG_LOCK_KERNEL diff --git a/tty/tty-reorder-ldisc-locking.patch b/tty/tty-reorder-ldisc-locking.patch new file mode 100644 index 00000000000000..0ef070703e46b4 --- /dev/null +++ b/tty/tty-reorder-ldisc-locking.patch @@ -0,0 +1,117 @@ +From arnd@arndb.de Wed Jun 16 13:44:56 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:06 +0200 +Subject: tty: reorder ldisc locking +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-27-git-send-email-arnd@arndb.de> + + +We need to release the BTM in paste_selection() when +sleeping in tty_ldisc_ref_wait to avoid deadlocks +with tty_ldisc_enable. + +In tty_set_ldisc, we now always grab the BTM before +taking the ldisc_mutex in order to avoid AB-BA +deadlocks between the two. + +tty_ldisc_halt potentially blocks on a workqueue +function that takes the BTM, so we must release +the BTM before calling it. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/selection.c | 9 +++++++-- + drivers/char/tty_ldisc.c | 24 ++++++++++++++++++++---- + 2 files changed, 27 insertions(+), 6 deletions(-) + +--- a/drivers/char/selection.c ++++ b/drivers/char/selection.c +@@ -319,8 +319,13 @@ int paste_selection(struct tty_struct *t + poke_blanked_console(); + release_console_sem(); + +- ld = tty_ldisc_ref_wait(tty); +- ++ ld = tty_ldisc_ref(tty); ++ if (!ld) { ++ tty_unlock(); ++ ld = tty_ldisc_ref_wait(tty); ++ tty_lock(); ++ } ++ + add_wait_queue(&vc->paste_wait, &wait); + while (sel_buffer && sel_buffer_lth > pasted) { + set_current_state(TASK_INTERRUPTIBLE); +--- a/drivers/char/tty_ldisc.c ++++ b/drivers/char/tty_ldisc.c +@@ -582,6 +582,7 @@ int tty_set_ldisc(struct tty_struct *tty + + tty_wait_until_sent(tty, 0); + ++ tty_lock(); + mutex_lock(&tty->ldisc_mutex); + + /* +@@ -591,13 +592,13 @@ int tty_set_ldisc(struct tty_struct *tty + + while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { + mutex_unlock(&tty->ldisc_mutex); ++ tty_unlock(); + wait_event(tty_ldisc_wait, + test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); ++ tty_lock(); + mutex_lock(&tty->ldisc_mutex); + } + +- tty_lock(); +- + set_bit(TTY_LDISC_CHANGING, &tty->flags); + + /* +@@ -634,8 +635,8 @@ int tty_set_ldisc(struct tty_struct *tty + + flush_scheduled_work(); + +- mutex_lock(&tty->ldisc_mutex); + tty_lock(); ++ mutex_lock(&tty->ldisc_mutex); + if (test_bit(TTY_HUPPED, &tty->flags)) { + /* We were raced by the hangup method. It will have stomped + the ldisc data and closed the ldisc down */ +@@ -782,7 +783,20 @@ void tty_ldisc_hangup(struct tty_struct + * Avoid racing set_ldisc or tty_ldisc_release + */ + mutex_lock(&tty->ldisc_mutex); +- tty_ldisc_halt(tty); ++ ++ /* ++ * this is like tty_ldisc_halt, but we need to give up ++ * the BTM before calling cancel_delayed_work_sync, ++ * which may need to wait for another function taking the BTM ++ */ ++ clear_bit(TTY_LDISC, &tty->flags); ++ tty_unlock(); ++ cancel_delayed_work_sync(&tty->buf.work); ++ mutex_unlock(&tty->ldisc_mutex); ++ ++ tty_lock(); ++ mutex_lock(&tty->ldisc_mutex); ++ + /* At this point we have a closed ldisc and we want to + reopen it. We could defer this to the next open but + it means auditing a lot of other paths so this is +@@ -853,8 +867,10 @@ void tty_ldisc_release(struct tty_struct + * race with the set_ldisc code path. + */ + ++ tty_unlock(); + tty_ldisc_halt(tty); + flush_scheduled_work(); ++ tty_lock(); + + mutex_lock(&tty->ldisc_mutex); + /* diff --git a/tty/tty-replace-bkl-with-a-new-tty_lock.patch b/tty/tty-replace-bkl-with-a-new-tty_lock.patch new file mode 100644 index 00000000000000..2f262e2222f876 --- /dev/null +++ b/tty/tty-replace-bkl-with-a-new-tty_lock.patch @@ -0,0 +1,1041 @@ +From arnd@arndb.de Wed Jun 16 13:40:23 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:01 +0200 +Subject: tty: replace BKL with a new tty_lock +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-22-git-send-email-arnd@arndb.de> + + +As a preparation for replacing the big kernel lock +in the TTY layer, wrap all the callers in new +macros tty_lock, tty_lock_nested and tty_unlock. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/amiserial.c | 16 ++--- + drivers/char/briq_panel.c | 6 +- + drivers/char/n_hdlc.c | 16 ++--- + drivers/char/n_r3964.c | 8 +- + drivers/char/pty.c | 4 - + drivers/char/selection.c | 4 - + drivers/char/serial167.c | 4 - + drivers/char/sx.c | 12 ++-- + drivers/char/tty_io.c | 115 ++++++++++++++++++++++------------------- + drivers/char/tty_ldisc.c | 24 ++++---- + drivers/char/vc_screen.c | 4 - + drivers/char/vt_ioctl.c | 10 +-- + drivers/serial/68360serial.c | 4 - + drivers/serial/crisv10.c | 4 - + drivers/serial/serial_core.c | 10 +-- + drivers/video/console/vgacon.c | 4 - + include/linux/tty.h | 31 +++++++++++ + 17 files changed, 161 insertions(+), 115 deletions(-) + +--- a/drivers/char/amiserial.c ++++ b/drivers/char/amiserial.c +@@ -1072,7 +1072,7 @@ static int get_serial_info(struct async_ + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); +- lock_kernel(); ++ tty_lock(); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; +@@ -1083,7 +1083,7 @@ static int get_serial_info(struct async_ + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; +- unlock_kernel(); ++ tty_unlock(); + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +@@ -1100,14 +1100,14 @@ static int set_serial_info(struct async_ + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + +- lock_kernel(); ++ tty_lock(); + state = info->state; + old_state = *state; + + change_irq = new_serial.irq != state->irq; + change_port = (new_serial.port != state->port); + if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) { +- unlock_kernel(); ++ tty_unlock(); + return -EINVAL; + } + +@@ -1127,7 +1127,7 @@ static int set_serial_info(struct async_ + } + + if (new_serial.baud_base < 9600) { +- unlock_kernel(); ++ tty_unlock(); + return -EINVAL; + } + +@@ -1163,7 +1163,7 @@ check_and_exit: + } + } else + retval = startup(info); +- unlock_kernel(); ++ tty_unlock(); + return retval; + } + +@@ -1538,7 +1538,7 @@ static void rs_wait_until_sent(struct tt + + orig_jiffies = jiffies; + +- lock_kernel(); ++ tty_lock_nested(); /* tty_wait_until_sent is called from lots of places */ + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check +@@ -1579,7 +1579,7 @@ static void rs_wait_until_sent(struct tt + break; + } + __set_current_state(TASK_RUNNING); +- unlock_kernel(); ++ tty_unlock(); + #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); + #endif +--- a/drivers/char/briq_panel.c ++++ b/drivers/char/briq_panel.c +@@ -67,15 +67,15 @@ static void set_led(char state) + + static int briq_panel_open(struct inode *ino, struct file *filep) + { +- lock_kernel(); ++ tty_lock(); + /* enforce single access, vfd_is_open is protected by BKL */ + if (vfd_is_open) { +- unlock_kernel(); ++ tty_unlock(); + return -EBUSY; + } + vfd_is_open = 1; + +- unlock_kernel(); ++ tty_unlock(); + return 0; + } + +--- a/drivers/char/n_hdlc.c ++++ b/drivers/char/n_hdlc.c +@@ -598,18 +598,18 @@ static ssize_t n_hdlc_tty_read(struct tt + return -EFAULT; + } + +- lock_kernel(); ++ tty_lock(); + + for (;;) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { +- unlock_kernel(); ++ tty_unlock(); + return -EIO; + } + + n_hdlc = tty2n_hdlc (tty); + if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || + tty != n_hdlc->tty) { +- unlock_kernel(); ++ tty_unlock(); + return 0; + } + +@@ -619,13 +619,13 @@ static ssize_t n_hdlc_tty_read(struct tt + + /* no data */ + if (file->f_flags & O_NONBLOCK) { +- unlock_kernel(); ++ tty_unlock(); + return -EAGAIN; + } + + interruptible_sleep_on (&tty->read_wait); + if (signal_pending(current)) { +- unlock_kernel(); ++ tty_unlock(); + return -EINTR; + } + } +@@ -648,7 +648,7 @@ static ssize_t n_hdlc_tty_read(struct tt + kfree(rbuf); + else + n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf); +- unlock_kernel(); ++ tty_unlock(); + return ret; + + } /* end of n_hdlc_tty_read() */ +@@ -691,7 +691,7 @@ static ssize_t n_hdlc_tty_write(struct t + count = maxframe; + } + +- lock_kernel(); ++ tty_lock(); + + add_wait_queue(&tty->write_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); +@@ -731,7 +731,7 @@ static ssize_t n_hdlc_tty_write(struct t + n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf); + n_hdlc_send_frames(n_hdlc,tty); + } +- unlock_kernel(); ++ tty_unlock(); + return error; + + } /* end of n_hdlc_tty_write() */ +--- a/drivers/char/n_r3964.c ++++ b/drivers/char/n_r3964.c +@@ -1067,7 +1067,7 @@ static ssize_t r3964_read(struct tty_str + + TRACE_L("read()"); + +- lock_kernel(); ++ tty_lock(); + + pClient = findClient(pInfo, task_pid(current)); + if (pClient) { +@@ -1109,7 +1109,7 @@ static ssize_t r3964_read(struct tty_str + } + ret = -EPERM; + unlock: +- unlock_kernel(); ++ tty_unlock(); + return ret; + } + +@@ -1158,7 +1158,7 @@ static ssize_t r3964_write(struct tty_st + pHeader->locks = 0; + pHeader->owner = NULL; + +- lock_kernel(); ++ tty_lock(); + + pClient = findClient(pInfo, task_pid(current)); + if (pClient) { +@@ -1177,7 +1177,7 @@ static ssize_t r3964_write(struct tty_st + add_tx_queue(pInfo, pHeader); + trigger_transmit(pInfo); + +- unlock_kernel(); ++ tty_unlock(); + + return 0; + } +--- a/drivers/char/pty.c ++++ b/drivers/char/pty.c +@@ -671,9 +671,9 @@ static int ptmx_open(struct inode *inode + { + int ret; + +- lock_kernel(); ++ tty_lock(); + ret = __ptmx_open(inode, filp); +- unlock_kernel(); ++ tty_unlock(); + return ret; + } + +--- a/drivers/char/selection.c ++++ b/drivers/char/selection.c +@@ -313,7 +313,7 @@ int paste_selection(struct tty_struct *t + struct tty_ldisc *ld; + DECLARE_WAITQUEUE(wait, current); + +- lock_kernel(); ++ tty_lock_nested(); /* always called with BTM from vt_ioctl */ + + acquire_console_sem(); + poke_blanked_console(); +@@ -338,6 +338,6 @@ int paste_selection(struct tty_struct *t + __set_current_state(TASK_RUNNING); + + tty_ldisc_deref(ld); +- unlock_kernel(); ++ tty_unlock(); + return 0; + } +--- a/drivers/char/serial167.c ++++ b/drivers/char/serial167.c +@@ -1505,7 +1505,7 @@ cy_ioctl(struct tty_struct *tty, struct + printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg); /* */ + #endif + +- lock_kernel(); ++ tty_lock(); + + switch (cmd) { + case CYGETMON: +@@ -1561,7 +1561,7 @@ cy_ioctl(struct tty_struct *tty, struct + default: + ret_val = -ENOIOCTLCMD; + } +- unlock_kernel(); ++ tty_unlock(); + + #ifdef SERIAL_DEBUG_OTHER + printk("cy_ioctl done\n"); +--- a/drivers/char/sx.c ++++ b/drivers/char/sx.c +@@ -1699,7 +1699,7 @@ static long sx_fw_ioctl(struct file *fil + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + +- lock_kernel(); ++ tty_lock(); + + sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg); + +@@ -1848,7 +1848,7 @@ static long sx_fw_ioctl(struct file *fil + break; + } + out: +- unlock_kernel(); ++ tty_unlock(); + func_exit(); + return rc; + } +@@ -1859,7 +1859,7 @@ static int sx_break(struct tty_struct *t + int rv; + + func_enter(); +- lock_kernel(); ++ tty_lock(); + + if (flag) + rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK); +@@ -1868,7 +1868,7 @@ static int sx_break(struct tty_struct *t + if (rv != 1) + printk(KERN_ERR "sx: couldn't send break (%x).\n", + read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat))); +- unlock_kernel(); ++ tty_unlock(); + func_exit(); + return 0; + } +@@ -1909,7 +1909,7 @@ static int sx_ioctl(struct tty_struct *t + /* func_enter2(); */ + + rc = 0; +- lock_kernel(); ++ tty_lock(); + switch (cmd) { + case TIOCGSERIAL: + rc = gs_getserial(&port->gs, argp); +@@ -1921,7 +1921,7 @@ static int sx_ioctl(struct tty_struct *t + rc = -ENOIOCTLCMD; + break; + } +- unlock_kernel(); ++ tty_unlock(); + + /* func_exit(); */ + return rc; +--- a/drivers/char/tty_io.c ++++ b/drivers/char/tty_io.c +@@ -149,6 +149,7 @@ static long tty_compat_ioctl(struct file + #else + #define tty_compat_ioctl NULL + #endif ++static int __tty_fasync(int fd, struct file *filp, int on); + static int tty_fasync(int fd, struct file *filp, int on); + static void release_tty(struct tty_struct *tty, int idx); + static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); +@@ -483,7 +484,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup); + * remains intact. + * + * Locking: +- * BKL ++ * BTM + * redirect lock for undoing redirection + * file list lock for manipulating list of ttys + * tty_ldisc_lock from called functions +@@ -513,8 +514,11 @@ static void do_tty_hangup(struct work_st + } + spin_unlock(&redirect_lock); + +- /* inuse_filps is protected by the single kernel lock */ +- lock_kernel(); ++ /* inuse_filps is protected by the single tty lock, ++ this really needs to change if we want to flush the ++ workqueue with the lock held */ ++ tty_lock_nested(); /* called with BTM held from pty_close and ++ others */ + check_tty_count(tty, "do_tty_hangup"); + + file_list_lock(); +@@ -525,7 +529,7 @@ static void do_tty_hangup(struct work_st + if (filp->f_op->write != tty_write) + continue; + closecount++; +- tty_fasync(-1, filp, 0); /* can't block */ ++ __tty_fasync(-1, filp, 0); /* can't block */ + filp->f_op = &hung_up_tty_fops; + } + file_list_unlock(); +@@ -594,7 +598,7 @@ static void do_tty_hangup(struct work_st + */ + set_bit(TTY_HUPPED, &tty->flags); + tty_ldisc_enable(tty); +- unlock_kernel(); ++ tty_unlock(); + if (f) + fput(f); + } +@@ -696,7 +700,8 @@ static void session_clear_tty(struct pid + * exiting; it is 0 if called by the ioctl TIOCNOTTY. + * + * Locking: +- * BKL is taken for hysterical raisins ++ * BTM is taken for hysterical raisins, and held when ++ * called from no_tty(). + * tty_mutex is taken to protect tty + * ->siglock is taken to protect ->signal/->sighand + * tasklist_lock is taken to walk process list for sessions +@@ -714,10 +719,10 @@ void disassociate_ctty(int on_exit) + tty = get_current_tty(); + if (tty) { + tty_pgrp = get_pid(tty->pgrp); +- lock_kernel(); ++ tty_lock_nested(); /* see above */ + if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) + tty_vhangup(tty); +- unlock_kernel(); ++ tty_unlock(); + tty_kref_put(tty); + } else if (on_exit) { + struct pid *old_pgrp; +@@ -774,9 +779,9 @@ void disassociate_ctty(int on_exit) + void no_tty(void) + { + struct task_struct *tsk = current; +- lock_kernel(); ++ tty_lock(); + disassociate_ctty(0); +- unlock_kernel(); ++ tty_unlock(); + proc_clear_tty(tsk); + } + +@@ -1013,19 +1018,19 @@ out: + * We don't put it into the syslog queue right now maybe in the future if + * really needed. + * +- * We must still hold the BKL and test the CLOSING flag for the moment. ++ * We must still hold the BTM and test the CLOSING flag for the moment. + */ + + void tty_write_message(struct tty_struct *tty, char *msg) + { + if (tty) { + mutex_lock(&tty->atomic_write_lock); +- lock_kernel(); ++ tty_lock(); + if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { +- unlock_kernel(); ++ tty_unlock(); + tty->ops->write(tty, msg, strlen(msg)); + } else +- unlock_kernel(); ++ tty_unlock(); + tty_write_unlock(tty); + } + return; +@@ -1208,18 +1213,18 @@ static int tty_driver_install_tty(struct + int ret; + + if (driver->ops->install) { +- lock_kernel(); ++ tty_lock_nested(); /* already called with BTM held */ + ret = driver->ops->install(driver, tty); +- unlock_kernel(); ++ tty_unlock(); + return ret; + } + + if (tty_init_termios(tty) == 0) { +- lock_kernel(); ++ tty_lock_nested(); + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; +- unlock_kernel(); ++ tty_unlock(); + return 0; + } + return -ENOMEM; +@@ -1312,14 +1317,15 @@ struct tty_struct *tty_init_dev(struct t + struct tty_struct *tty; + int retval; + +- lock_kernel(); ++ tty_lock_nested(); /* always called with tty lock held already */ ++ + /* Check if pty master is being opened multiple times */ + if (driver->subtype == PTY_TYPE_MASTER && + (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) { +- unlock_kernel(); ++ tty_unlock(); + return ERR_PTR(-EIO); + } +- unlock_kernel(); ++ tty_unlock(); + + /* + * First time open is complex, especially for PTY devices. +@@ -1363,9 +1369,9 @@ release_mem_out: + if (printk_ratelimit()) + printk(KERN_INFO "tty_init_dev: ldisc open failed, " + "clearing slot %d\n", idx); +- lock_kernel(); ++ tty_lock_nested(); + release_tty(tty, idx); +- unlock_kernel(); ++ tty_unlock(); + return ERR_PTR(retval); + } + +@@ -1512,10 +1518,10 @@ int tty_release(struct inode *inode, str + if (tty_paranoia_check(tty, inode, "tty_release_dev")) + return 0; + +- lock_kernel(); ++ tty_lock(); + check_tty_count(tty, "tty_release_dev"); + +- tty_fasync(-1, filp, 0); ++ __tty_fasync(-1, filp, 0); + + idx = tty->index; + pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && +@@ -1527,18 +1533,18 @@ int tty_release(struct inode *inode, str + if (idx < 0 || idx >= tty->driver->num) { + printk(KERN_DEBUG "tty_release_dev: bad idx when trying to " + "free (%s)\n", tty->name); +- unlock_kernel(); ++ tty_unlock(); + return 0; + } + if (!devpts) { + if (tty != tty->driver->ttys[idx]) { +- unlock_kernel(); ++ tty_unlock(); + printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " + "for (%s)\n", idx, tty->name); + return 0; + } + if (tty->termios != tty->driver->termios[idx]) { +- unlock_kernel(); ++ tty_unlock(); + printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " + "for (%s)\n", + idx, tty->name); +@@ -1556,21 +1562,21 @@ int tty_release(struct inode *inode, str + if (tty->driver->other && + !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { + if (o_tty != tty->driver->other->ttys[idx]) { +- unlock_kernel(); ++ tty_unlock(); + printk(KERN_DEBUG "tty_release_dev: other->table[%d] " + "not o_tty for (%s)\n", + idx, tty->name); + return 0 ; + } + if (o_tty->termios != tty->driver->other->termios[idx]) { +- unlock_kernel(); ++ tty_unlock(); + printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " + "not o_termios for (%s)\n", + idx, tty->name); + return 0; + } + if (o_tty->link != tty) { +- unlock_kernel(); ++ tty_unlock(); + printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); + return 0; + } +@@ -1579,7 +1585,7 @@ int tty_release(struct inode *inode, str + if (tty->ops->close) + tty->ops->close(tty, filp); + +- unlock_kernel(); ++ tty_unlock(); + /* + * Sanity check: if tty->count is going to zero, there shouldn't be + * any waiters on tty->read_wait or tty->write_wait. We test the +@@ -1602,7 +1608,7 @@ int tty_release(struct inode *inode, str + opens on /dev/tty */ + + mutex_lock(&tty_mutex); +- lock_kernel(); ++ tty_lock(); + tty_closing = tty->count <= 1; + o_tty_closing = o_tty && + (o_tty->count <= (pty_master ? 1 : 0)); +@@ -1633,7 +1639,7 @@ int tty_release(struct inode *inode, str + + printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue " + "active!\n", tty_name(tty, buf)); +- unlock_kernel(); ++ tty_unlock(); + mutex_unlock(&tty_mutex); + schedule(); + } +@@ -1698,7 +1704,7 @@ int tty_release(struct inode *inode, str + + /* check whether both sides are closing ... */ + if (!tty_closing || (o_tty && !o_tty_closing)) { +- unlock_kernel(); ++ tty_unlock(); + return 0; + } + +@@ -1718,7 +1724,7 @@ int tty_release(struct inode *inode, str + /* Make this pty number available for reallocation */ + if (devpts) + devpts_kill_index(inode, idx); +- unlock_kernel(); ++ tty_unlock(); + return 0; + } + +@@ -1760,12 +1766,12 @@ retry_open: + retval = 0; + + mutex_lock(&tty_mutex); +- lock_kernel(); ++ tty_lock(); + + if (device == MKDEV(TTYAUX_MAJOR, 0)) { + tty = get_current_tty(); + if (!tty) { +- unlock_kernel(); ++ tty_unlock(); + mutex_unlock(&tty_mutex); + return -ENXIO; + } +@@ -1797,14 +1803,14 @@ retry_open: + goto got_driver; + } + } +- unlock_kernel(); ++ tty_unlock(); + mutex_unlock(&tty_mutex); + return -ENODEV; + } + + driver = get_tty_driver(device, &index); + if (!driver) { +- unlock_kernel(); ++ tty_unlock(); + mutex_unlock(&tty_mutex); + return -ENODEV; + } +@@ -1814,7 +1820,7 @@ got_driver: + tty = tty_driver_lookup_tty(driver, inode, index); + + if (IS_ERR(tty)) { +- unlock_kernel(); ++ tty_unlock(); + mutex_unlock(&tty_mutex); + return PTR_ERR(tty); + } +@@ -1830,7 +1836,7 @@ got_driver: + mutex_unlock(&tty_mutex); + tty_driver_kref_put(driver); + if (IS_ERR(tty)) { +- unlock_kernel(); ++ tty_unlock(); + return PTR_ERR(tty); + } + +@@ -1862,11 +1868,11 @@ got_driver: + #endif + tty_release(inode, filp); + if (retval != -ERESTARTSYS) { +- unlock_kernel(); ++ tty_unlock(); + return retval; + } + if (signal_pending(current)) { +- unlock_kernel(); ++ tty_unlock(); + return retval; + } + schedule(); +@@ -1875,14 +1881,14 @@ got_driver: + */ + if (filp->f_op == &hung_up_tty_fops) + filp->f_op = &tty_fops; +- unlock_kernel(); ++ tty_unlock(); + goto retry_open; + } +- unlock_kernel(); ++ tty_unlock(); + + + mutex_lock(&tty_mutex); +- lock_kernel(); ++ tty_lock(); + spin_lock_irq(¤t->sighand->siglock); + if (!noctty && + current->signal->leader && +@@ -1890,7 +1896,7 @@ got_driver: + tty->session == NULL) + __proc_set_tty(current, tty); + spin_unlock_irq(¤t->sighand->siglock); +- unlock_kernel(); ++ tty_unlock(); + mutex_unlock(&tty_mutex); + return 0; + } +@@ -1926,13 +1932,12 @@ static unsigned int tty_poll(struct file + return ret; + } + +-static int tty_fasync(int fd, struct file *filp, int on) ++static int __tty_fasync(int fd, struct file *filp, int on) + { + struct tty_struct *tty; + unsigned long flags; + int retval = 0; + +- lock_kernel(); + tty = (struct tty_struct *)filp->private_data; + if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync")) + goto out; +@@ -1966,7 +1971,15 @@ static int tty_fasync(int fd, struct fil + } + retval = 0; + out: +- unlock_kernel(); ++ return retval; ++} ++ ++static int tty_fasync(int fd, struct file *filp, int on) ++{ ++ int retval; ++ tty_lock(); ++ retval = __tty_fasync(fd, filp, on); ++ tty_unlock(); + return retval; + } + +--- a/drivers/char/tty_ldisc.c ++++ b/drivers/char/tty_ldisc.c +@@ -440,6 +440,8 @@ static void tty_set_termios_ldisc(struct + * + * A helper opening method. Also a convenient debugging and check + * point. ++ * ++ * Locking: always called with BTM already held. + */ + + static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) +@@ -447,10 +449,10 @@ static int tty_ldisc_open(struct tty_str + WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags)); + if (ld->ops->open) { + int ret; +- /* BKL here locks verus a hangup event */ +- lock_kernel(); ++ /* BTM here locks versus a hangup event */ ++ tty_lock_nested(); /* always held here already */ + ret = ld->ops->open(tty); +- unlock_kernel(); ++ tty_unlock(); + return ret; + } + return 0; +@@ -553,7 +555,7 @@ int tty_set_ldisc(struct tty_struct *tty + if (IS_ERR(new_ldisc)) + return PTR_ERR(new_ldisc); + +- lock_kernel(); ++ tty_lock(); + /* + * We need to look at the tty locking here for pty/tty pairs + * when both sides try to change in parallel. +@@ -567,12 +569,12 @@ int tty_set_ldisc(struct tty_struct *tty + */ + + if (tty->ldisc->ops->num == ldisc) { +- unlock_kernel(); ++ tty_unlock(); + tty_ldisc_put(new_ldisc); + return 0; + } + +- unlock_kernel(); ++ tty_unlock(); + /* + * Problem: What do we do if this blocks ? + * We could deadlock here +@@ -594,7 +596,7 @@ int tty_set_ldisc(struct tty_struct *tty + mutex_lock(&tty->ldisc_mutex); + } + +- lock_kernel(); ++ tty_lock(); + + set_bit(TTY_LDISC_CHANGING, &tty->flags); + +@@ -607,7 +609,7 @@ int tty_set_ldisc(struct tty_struct *tty + + o_ldisc = tty->ldisc; + +- unlock_kernel(); ++ tty_unlock(); + /* + * Make sure we don't change while someone holds a + * reference to the line discipline. The TTY_LDISC bit +@@ -633,14 +635,14 @@ int tty_set_ldisc(struct tty_struct *tty + flush_scheduled_work(); + + mutex_lock(&tty->ldisc_mutex); +- lock_kernel(); ++ tty_lock(); + if (test_bit(TTY_HUPPED, &tty->flags)) { + /* We were raced by the hangup method. It will have stomped + the ldisc data and closed the ldisc down */ + clear_bit(TTY_LDISC_CHANGING, &tty->flags); + mutex_unlock(&tty->ldisc_mutex); + tty_ldisc_put(new_ldisc); +- unlock_kernel(); ++ tty_unlock(); + return -EIO; + } + +@@ -682,7 +684,7 @@ int tty_set_ldisc(struct tty_struct *tty + if (o_work) + schedule_delayed_work(&o_tty->buf.work, 1); + mutex_unlock(&tty->ldisc_mutex); +- unlock_kernel(); ++ tty_unlock(); + return retval; + } + +--- a/drivers/char/vc_screen.c ++++ b/drivers/char/vc_screen.c +@@ -463,10 +463,10 @@ vcs_open(struct inode *inode, struct fil + unsigned int currcons = iminor(inode) & 127; + int ret = 0; + +- lock_kernel(); ++ tty_lock(); + if(currcons && !vc_cons_allocated(currcons-1)) + ret = -ENXIO; +- unlock_kernel(); ++ tty_unlock(); + return ret; + } + +--- a/drivers/char/vt_ioctl.c ++++ b/drivers/char/vt_ioctl.c +@@ -509,7 +509,7 @@ int vt_ioctl(struct tty_struct *tty, str + + console = vc->vc_num; + +- lock_kernel(); ++ tty_lock(); + + if (!vc_cons_allocated(console)) { /* impossible? */ + ret = -ENOIOCTLCMD; +@@ -1336,7 +1336,7 @@ int vt_ioctl(struct tty_struct *tty, str + ret = -ENOIOCTLCMD; + } + out: +- unlock_kernel(); ++ tty_unlock(); + return ret; + eperm: + ret = -EPERM; +@@ -1503,7 +1503,7 @@ long vt_compat_ioctl(struct tty_struct * + + console = vc->vc_num; + +- lock_kernel(); ++ tty_lock(); + + if (!vc_cons_allocated(console)) { /* impossible? */ + ret = -ENOIOCTLCMD; +@@ -1571,11 +1571,11 @@ long vt_compat_ioctl(struct tty_struct * + goto fallback; + } + out: +- unlock_kernel(); ++ tty_unlock(); + return ret; + + fallback: +- unlock_kernel(); ++ tty_unlock(); + return vt_ioctl(tty, file, cmd, arg); + } + +--- a/drivers/serial/68360serial.c ++++ b/drivers/serial/68360serial.c +@@ -1705,7 +1705,7 @@ static void rs_360_wait_until_sent(struc + printk("jiff=%lu...", jiffies); + #endif + +- lock_kernel(); ++ tty_lock_nested(); /* always held already since we come from ->close */ + /* We go through the loop at least once because we can't tell + * exactly when the last character exits the shifter. There can + * be at least two characters waiting to be sent after the buffers +@@ -1734,7 +1734,7 @@ static void rs_360_wait_until_sent(struc + bdp--; + } while (bdp->status & BD_SC_READY); + current->state = TASK_RUNNING; +- unlock_kernel(); ++ tty_unlock(); + #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); + #endif +--- a/drivers/serial/crisv10.c ++++ b/drivers/serial/crisv10.c +@@ -3924,7 +3924,7 @@ static void rs_wait_until_sent(struct tt + * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO + * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k) + */ +- lock_kernel(); ++ tty_lock_nested(); /* locked already when coming from close */ + orig_jiffies = jiffies; + while (info->xmit.head != info->xmit.tail || /* More in send queue */ + (*info->ostatusadr & 0x007f) || /* more in FIFO */ +@@ -3941,7 +3941,7 @@ static void rs_wait_until_sent(struct tt + curr_time_usec - info->last_tx_active_usec; + } + set_current_state(TASK_RUNNING); +- unlock_kernel(); ++ tty_unlock(); + } + + /* +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -1274,7 +1274,7 @@ static void uart_close(struct tty_struct + struct uart_port *uport; + unsigned long flags; + +- BUG_ON(!kernel_locked()); ++ BUG_ON(!tty_locked()); + + if (!state) + return; +@@ -1382,7 +1382,7 @@ static void uart_wait_until_sent(struct + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + +- lock_kernel(); ++ tty_lock_nested(); /* already locked when coming from close */ + + /* + * Set the check interval to be 1/5 of the estimated time to +@@ -1429,7 +1429,7 @@ static void uart_wait_until_sent(struct + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +- unlock_kernel(); ++ tty_unlock(); + } + + /* +@@ -1444,7 +1444,7 @@ static void uart_hangup(struct tty_struc + struct tty_port *port = &state->port; + unsigned long flags; + +- BUG_ON(!kernel_locked()); ++ BUG_ON(!tty_locked()); + pr_debug("uart_hangup(%d)\n", state->uart_port->line); + + mutex_lock(&port->mutex); +@@ -1570,7 +1570,7 @@ static int uart_open(struct tty_struct * + struct tty_port *port; + int retval, line = tty->index; + +- BUG_ON(!kernel_locked()); ++ BUG_ON(!tty_locked()); + pr_debug("uart_open(%d) called\n", line); + + /* +--- a/drivers/video/console/vgacon.c ++++ b/drivers/video/console/vgacon.c +@@ -1108,7 +1108,7 @@ static int vgacon_do_font_op(struct vgas + charmap += 4 * cmapsz; + #endif + +- unlock_kernel(); ++ tty_unlock(); + spin_lock_irq(&vga_lock); + /* First, the Sequencer */ + vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1); +@@ -1192,7 +1192,7 @@ static int vgacon_do_font_op(struct vgas + vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0); + } + spin_unlock_irq(&vga_lock); +- lock_kernel(); ++ tty_lock(); + return 0; + } + +--- a/include/linux/tty.h ++++ b/include/linux/tty.h +@@ -13,6 +13,7 @@ + #include <linux/tty_driver.h> + #include <linux/tty_ldisc.h> + #include <linux/mutex.h> ++#include <linux/smp_lock.h> + + #include <asm/system.h> + +@@ -572,5 +573,35 @@ extern int vt_ioctl(struct tty_struct *t + extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg); + ++/* functions for preparation of BKL removal */ ++ ++/* ++ * tty_lock_nested get the tty_lock while potentially holding it ++ * ++ * The Big TTY Mutex is a recursive lock, meaning you can take it ++ * from a thread that is already holding it. ++ * This is bad for a number of reasons, so tty_lock_nested should ++ * really be used as rarely as possible. If a code location can ++ * be shown to never get called with this held already, it should ++ * use tty_lock() instead. ++ */ ++static inline void __lockfunc tty_lock_nested(void) __acquires(kernel_lock) ++{ ++ lock_kernel(); ++} ++static inline void tty_lock(void) __acquires(kernel_lock) ++{ ++#ifdef CONFIG_LOCK_KERNEL ++ /* kernel_locked is 1 for !CONFIG_LOCK_KERNEL */ ++ WARN_ON(kernel_locked()); ++#endif ++ lock_kernel(); ++} ++static inline void tty_unlock(void) __releases(kernel_lock) ++{ ++ unlock_kernel(); ++} ++#define tty_locked() (kernel_locked()) ++ + #endif /* __KERNEL__ */ + #endif diff --git a/tty/tty-untangle-locking-of-wait_until_sent.patch b/tty/tty-untangle-locking-of-wait_until_sent.patch new file mode 100644 index 00000000000000..da72b6ed9d6e02 --- /dev/null +++ b/tty/tty-untangle-locking-of-wait_until_sent.patch @@ -0,0 +1,175 @@ +From arnd@arndb.de Wed Jun 16 13:45:16 2010 +From: Arnd Bergmann <arnd@arndb.de> +Date: Tue, 1 Jun 2010 22:53:07 +0200 +Subject: tty: untangle locking of wait_until_sent +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com> +Message-ID: <1275425591-8803-28-git-send-email-arnd@arndb.de> + + +Some wait_until_sent versions require the big +tty mutex, others don't and some callers of +wait_until_sent already hold it while other don't. +That leads to recursive use of the BTM in these +functions, which we're trying to get rid of. + +This turns all cleans up the locking there so +that the driver's wait_until_sent function +never takes the BTM itself if it is already +called with that lock held. + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/amiserial.c | 11 +++++++++-- + drivers/serial/68360serial.c | 2 -- + drivers/serial/crisv10.c | 2 -- + drivers/serial/serial_core.c | 31 ++++++++++++++++++++++--------- + 4 files changed, 31 insertions(+), 15 deletions(-) + +--- a/drivers/char/amiserial.c ++++ b/drivers/char/amiserial.c +@@ -1528,6 +1528,7 @@ static void rs_wait_until_sent(struct tt + { + struct async_struct * info = tty->driver_data; + unsigned long orig_jiffies, char_time; ++ int tty_was_locked = tty_locked(); + int lsr; + + if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) +@@ -1538,7 +1539,12 @@ static void rs_wait_until_sent(struct tt + + orig_jiffies = jiffies; + +- tty_lock_nested(); /* tty_wait_until_sent is called from lots of places */ ++ /* ++ * tty_wait_until_sent is called from lots of places, ++ * with or without the BTM. ++ */ ++ if (!tty_was_locked) ++ tty_lock(); + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check +@@ -1579,7 +1585,8 @@ static void rs_wait_until_sent(struct tt + break; + } + __set_current_state(TASK_RUNNING); +- tty_unlock(); ++ if (!tty_was_locked) ++ tty_unlock(); + #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); + #endif +--- a/drivers/serial/68360serial.c ++++ b/drivers/serial/68360serial.c +@@ -1705,7 +1705,6 @@ static void rs_360_wait_until_sent(struc + printk("jiff=%lu...", jiffies); + #endif + +- tty_lock_nested(); /* always held already since we come from ->close */ + /* We go through the loop at least once because we can't tell + * exactly when the last character exits the shifter. There can + * be at least two characters waiting to be sent after the buffers +@@ -1734,7 +1733,6 @@ static void rs_360_wait_until_sent(struc + bdp--; + } while (bdp->status & BD_SC_READY); + current->state = TASK_RUNNING; +- tty_unlock(); + #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); + #endif +--- a/drivers/serial/crisv10.c ++++ b/drivers/serial/crisv10.c +@@ -3924,7 +3924,6 @@ static void rs_wait_until_sent(struct tt + * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO + * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k) + */ +- tty_lock_nested(); /* locked already when coming from close */ + orig_jiffies = jiffies; + while (info->xmit.head != info->xmit.tail || /* More in send queue */ + (*info->ostatusadr & 0x007f) || /* more in FIFO */ +@@ -3941,7 +3940,6 @@ static void rs_wait_until_sent(struct tt + curr_time_usec - info->last_tx_active_usec; + } + set_current_state(TASK_RUNNING); +- tty_unlock(); + } + + /* +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -60,7 +60,7 @@ static struct lock_class_key port_lock_k + + static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, + struct ktermios *old_termios); +-static void uart_wait_until_sent(struct tty_struct *tty, int timeout); ++static void __uart_wait_until_sent(struct uart_port *port, int timeout); + static void uart_change_pm(struct uart_state *state, int pm_state); + + /* +@@ -1322,8 +1322,16 @@ static void uart_close(struct tty_struct + tty->closing = 1; + spin_unlock_irqrestore(&port->lock, flags); + +- if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) +- tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait)); ++ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) { ++ /* ++ * hack: open-coded tty_wait_until_sent to avoid ++ * recursive tty_lock ++ */ ++ long timeout = msecs_to_jiffies(port->closing_wait); ++ if (wait_event_interruptible_timeout(tty->write_wait, ++ !tty_chars_in_buffer(tty), timeout) >= 0) ++ __uart_wait_until_sent(uport, timeout); ++ } + + /* + * At this point, we stop accepting input. To do this, we +@@ -1339,7 +1347,7 @@ static void uart_close(struct tty_struct + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ +- uart_wait_until_sent(tty, uport->timeout); ++ __uart_wait_until_sent(uport, uport->timeout); + } + + uart_shutdown(tty, state); +@@ -1373,17 +1381,13 @@ done: + mutex_unlock(&port->mutex); + } + +-static void uart_wait_until_sent(struct tty_struct *tty, int timeout) ++static void __uart_wait_until_sent(struct uart_port *port, int timeout) + { +- struct uart_state *state = tty->driver_data; +- struct uart_port *port = state->uart_port; + unsigned long char_time, expire; + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + +- tty_lock_nested(); /* already locked when coming from close */ +- + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check +@@ -1429,6 +1433,15 @@ static void uart_wait_until_sent(struct + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ ++} ++ ++static void uart_wait_until_sent(struct tty_struct *tty, int timeout) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->uart_port; ++ ++ tty_lock(); ++ __uart_wait_until_sent(port, timeout); + tty_unlock(); + } + diff --git a/tty/vc-locking-clean-up.patch b/tty/vc-locking-clean-up.patch new file mode 100644 index 00000000000000..866122ce1597f2 --- /dev/null +++ b/tty/vc-locking-clean-up.patch @@ -0,0 +1,81 @@ +From arnd@arndb.de Wed Jun 16 13:30:44 2010 +From: Alan Cox <alan@linux.intel.com> +Date: Tue, 1 Jun 2010 22:52:54 +0200 +Subject: vc: Locking clean up +To: Greg KH <gregkh@suse.de> +Cc: linux-kernel@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Alan Cox <alan@lxorguk.ukuu.org.uk>, Frederic Weisbecker <fweisbec@gmail.com>, John Kacur <jkacur@redhat.com>, Alan Cox <alan@linux.intel.com> +Message-ID: <1275425591-8803-15-git-send-email-arnd@arndb.de> + + +From: Alan Cox <alan@linux.intel.com> + +The virtual console layer uses the BKL for various things that don't really +need it. Clean them out. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/selection.c | 4 ++++ + drivers/char/vt.c | 7 ++++--- + 2 files changed, 8 insertions(+), 3 deletions(-) + +--- a/drivers/char/selection.c ++++ b/drivers/char/selection.c +@@ -26,6 +26,7 @@ + #include <linux/selection.h> + #include <linux/tiocl.h> + #include <linux/console.h> ++#include <linux/smp_lock.h> + + /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */ + #define isspace(c) ((c) == ' ') +@@ -312,6 +313,8 @@ int paste_selection(struct tty_struct *t + struct tty_ldisc *ld; + DECLARE_WAITQUEUE(wait, current); + ++ lock_kernel(); ++ + acquire_console_sem(); + poke_blanked_console(); + release_console_sem(); +@@ -335,5 +338,6 @@ int paste_selection(struct tty_struct *t + __set_current_state(TASK_RUNNING); + + tty_ldisc_deref(ld); ++ unlock_kernel(); + return 0; + } +--- a/drivers/char/vt.c ++++ b/drivers/char/vt.c +@@ -281,8 +281,12 @@ static inline unsigned short *screenpos( + return p; + } + ++/* Called from the keyboard irq path.. */ + static inline void scrolldelta(int lines) + { ++ /* FIXME */ ++ /* scrolldelta needs some kind of consistency lock, but the BKL was ++ and still is not protecting versus the scheduled back end */ + scrollback_delta += lines; + schedule_console_callback(); + } +@@ -2605,8 +2609,6 @@ int tioclinux(struct tty_struct *tty, un + return -EFAULT; + ret = 0; + +- lock_kernel(); +- + switch (type) + { + case TIOCL_SETSEL: +@@ -2681,7 +2683,6 @@ int tioclinux(struct tty_struct *tty, un + ret = -EINVAL; + break; + } +- unlock_kernel(); + return ret; + } + diff --git a/tty/vt-clean-up-the-code-use-kernel-library.patch b/tty/vt-clean-up-the-code-use-kernel-library.patch new file mode 100644 index 00000000000000..ebdb7353978d2b --- /dev/null +++ b/tty/vt-clean-up-the-code-use-kernel-library.patch @@ -0,0 +1,41 @@ +From andy.shevchenko@gmail.com Wed Jun 16 13:22:07 2010 +From: Andy Shevchenko <andy.shevchenko@gmail.com> +Date: Tue, 15 Jun 2010 17:24:16 +0300 +Subject: vt: clean up the code - use kernel library +To: linux-kernel@vger.kernel.org +Cc: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>, Andrew Morton <akpm@linux-foundation.org>, Greg Kroah-Hartman <gregkh@suse.de>, Alan Cox <alan@linux.intel.com> +Message-ID: <1276611856-28232-1-git-send-email-andy.shevchenko@gmail.com> + + +From: Andy Shevchenko <ext-andriy.shevchenko@nokia.com> + +Signed-off-by: Andy Shevchenko <ext-andriy.shevchenko@nokia.com> +Cc: Andrew Morton <akpm@linux-foundation.org> +Cc: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/char/vt.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/char/vt.c ++++ b/drivers/char/vt.c +@@ -104,6 +104,7 @@ + #include <linux/io.h> + #include <asm/system.h> + #include <linux/uaccess.h> ++#include <linux/ctype.h> + + #define MAX_NR_CON_DRIVER 16 + +@@ -1789,8 +1790,8 @@ static void do_con_trol(struct tty_struc + vc->vc_state = ESnormal; + return; + case ESpalette: +- if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { +- vc->vc_par[vc->vc_npar++] = (c > '9' ? (c & 0xDF) - 'A' + 10 : c - '0'); ++ if (isxdigit(c)) { ++ vc->vc_par[vc->vc_npar++] = hex_to_bin(c); + if (vc->vc_npar == 7) { + int i = vc->vc_par[0] * 3, j = 1; + vc->vc_palette[i] = 16 * vc->vc_par[j++]; diff --git a/usb.current/usb-ehci-mxc-bail-out-on-transceiver-problems.patch b/usb.current/usb-ehci-mxc-bail-out-on-transceiver-problems.patch new file mode 100644 index 00000000000000..1d6323fefa3830 --- /dev/null +++ b/usb.current/usb-ehci-mxc-bail-out-on-transceiver-problems.patch @@ -0,0 +1,69 @@ +From w.sang@pengutronix.de Wed Jun 16 13:24:59 2010 +From: Wolfram Sang <w.sang@pengutronix.de> +Date: Tue, 15 Jun 2010 12:34:23 +0200 +Subject: USB: ehci-mxc: bail out on transceiver problems +To: linux-usb@vger.kernel.org +Cc: linux-arm-kernel@lists.infradead.org, Wolfram Sang <w.sang@pengutronix.de>, Sascha Hauer <s.hauer@pengutronix.de>, Daniel Mack <daniel@caiaq.de>, Greg KH <gregkh@suse.de>, stable@kernel.org +Message-ID: <1276598063-3956-2-git-send-email-w.sang@pengutronix.de> + + +The old code registered the hcd even if there were no transceivers +detected, leading to oopses like this if we try to probe a non-existant +ULPI: + +[ 2.730000] mxc-ehci mxc-ehci.0: unable to init transceiver +[ 2.740000] timeout polling for ULPI device +[ 2.740000] timeout polling for ULPI device +[ 2.750000] mxc-ehci mxc-ehci.0: unable to enable vbus on transceiver +[ 2.750000] mxc-ehci mxc-ehci.0: Freescale On-Chip EHCI Host Controller +[ 2.760000] mxc-ehci mxc-ehci.0: new USB bus registered, assigned bus number 2 +[ 2.770000] Unhandled fault: external abort on non-linefetch (0x808) at 0xc4876184 +[ 2.770000] Internal error: : 808 [#1] PREEMPT +[ 2.770000] last sysfs file: +[ 2.770000] Modules linked in: +[ 2.770000] CPU: 0 Not tainted (2.6.33.5 #5) +[ 2.770000] PC is at ehci_hub_control+0x4d4/0x8f8 +[ 2.770000] LR is at ehci_mxc_setup+0xbc/0xdc +[ 2.770000] pc : [<c0196dfc>] lr : [<c019bc8c>] psr: 00000093 +[ 2.770000] sp : c3815e40 ip : 00000001 fp : 60000013 +[ 2.770000] r10: c4876184 r9 : 00000000 r8 : c3814000 +[ 2.770000] r7 : c391d2cc r6 : 00000001 r5 : 00000001 r4 : 00000000 +[ 2.770000] r3 : 80000000 r2 : 00000007 r1 : 80000000 r0 : c4876184 +[ 2.770000] Flags: nzcv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel +[ 2.770000] Control: 0005317f Table: a0004000 DAC: 00000017 +[ 2.770000] Process swapper (pid: 1, stack limit = 0xc3814270) +... + +Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> +Cc: Sascha Hauer <s.hauer@pengutronix.de> +Cc: stable <stable@kernel.org> +Acked-by: Daniel Mack <daniel@caiaq.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-mxc.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +--- a/drivers/usb/host/ehci-mxc.c ++++ b/drivers/usb/host/ehci-mxc.c +@@ -207,10 +207,17 @@ static int ehci_mxc_drv_probe(struct pla + /* Initialize the transceiver */ + if (pdata->otg) { + pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET; +- if (otg_init(pdata->otg) != 0) +- dev_err(dev, "unable to init transceiver\n"); +- else if (otg_set_vbus(pdata->otg, 1) != 0) ++ ret = otg_init(pdata->otg); ++ if (ret) { ++ dev_err(dev, "unable to init transceiver, probably missing\n"); ++ ret = -ENODEV; ++ goto err_add; ++ } ++ ret = otg_set_vbus(pdata->otg, 1); ++ if (ret) { + dev_err(dev, "unable to enable vbus on transceiver\n"); ++ goto err_add; ++ } + } + + priv->hcd = hcd; diff --git a/usb.current/usb-musb-fix-a-bug-by-making-suspend-interrupt-available-in-device-mode.patch b/usb.current/usb-musb-fix-a-bug-by-making-suspend-interrupt-available-in-device-mode.patch new file mode 100644 index 00000000000000..bca576b6d5aa45 --- /dev/null +++ b/usb.current/usb-musb-fix-a-bug-by-making-suspend-interrupt-available-in-device-mode.patch @@ -0,0 +1,49 @@ +From x0082077@ti.com Wed Jun 16 13:23:21 2010 +From: Maulik Mankad <x0082077@ti.com> +Date: Tue, 15 Jun 2010 14:40:27 +0530 +Subject: usb: musb: Fix a bug by making suspend interrupt available in device mode +To: linux-usb@vger.kernel.org +Cc: Maulik Mankad <x0082077@ti.com>, David Brownell <david-b@pacbell.net>, Felipe Balbi <felipe.balbi@nokia.com> +Message-ID: <1276593027-30168-1-git-send-email-x0082077@ti.com> + + +As a part of aligning the ISR code for MUSB with the specs, the +ISR code was re-written. + +See Commit 1c25fda4a09e8229800979986ef399401053b46e (usb: musb: handle +irqs in the order dictated by programming guide) + +With this the suspend interrupt came accidently under CONFIG_USB_MUSB_HDRC_HCD. + +The fix brings suspend interrupt handling outside +CONFIG_USB_MUSB_HDRC_HCD. + +Signed-off-by: Maulik Mankad <x0082077@ti.com> +Cc: David Brownell <david-b@pacbell.net> +Acked-by: Felipe Balbi <felipe.balbi@nokia.com> +Cc: stable <stable@kernel.org> [.34] +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/musb_core.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/usb/musb/musb_core.c ++++ b/drivers/usb/musb/musb_core.c +@@ -642,7 +642,7 @@ static irqreturn_t musb_stage0_irq(struc + handled = IRQ_HANDLED; + } + +- ++#endif + if (int_usb & MUSB_INTR_SUSPEND) { + DBG(1, "SUSPEND (%s) devctl %02x power %02x\n", + otg_state_string(musb), devctl, power); +@@ -705,6 +705,7 @@ static irqreturn_t musb_stage0_irq(struc + } + } + ++#ifdef CONFIG_USB_MUSB_HDRC_HCD + if (int_usb & MUSB_INTR_CONNECT) { + struct usb_hcd *hcd = musb_to_hcd(musb); + void __iomem *mbase = musb->mregs; diff --git a/usb.current/usb-otg-ulpi-bail-out-on-read-errors.patch b/usb.current/usb-otg-ulpi-bail-out-on-read-errors.patch new file mode 100644 index 00000000000000..733b614dacb4dc --- /dev/null +++ b/usb.current/usb-otg-ulpi-bail-out-on-read-errors.patch @@ -0,0 +1,46 @@ +From w.sang@pengutronix.de Wed Jun 16 13:24:08 2010 +From: Wolfram Sang <w.sang@pengutronix.de> +Date: Tue, 15 Jun 2010 12:34:22 +0200 +Subject: USB: otg/ulpi: bail out on read errors +To: linux-usb@vger.kernel.org +Cc: linux-arm-kernel@lists.infradead.org, Wolfram Sang <w.sang@pengutronix.de>, Sascha Hauer <s.hauer@pengutronix.de>, Daniel Mack <daniel@caiaq.de>, Greg KH <gregkh@suse.de> +Message-ID: <1276598063-3956-1-git-send-email-w.sang@pengutronix.de> + +otg_read may return errnos, so bail out correctly to prevent bogus +ID-numbers. + +Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> +Cc: Sascha Hauer <s.hauer@pengutronix.de> +Acked-by: Daniel Mack <daniel@caiaq.de> +Cc: stable <stable@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/otg/ulpi.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +--- a/drivers/usb/otg/ulpi.c ++++ b/drivers/usb/otg/ulpi.c +@@ -59,12 +59,17 @@ static int ulpi_set_flags(struct otg_tra + + static int ulpi_init(struct otg_transceiver *otg) + { +- int i, vid, pid; ++ int i, vid, pid, ret; ++ u32 ulpi_id = 0; + +- vid = (otg_io_read(otg, ULPI_VENDOR_ID_HIGH) << 8) | +- otg_io_read(otg, ULPI_VENDOR_ID_LOW); +- pid = (otg_io_read(otg, ULPI_PRODUCT_ID_HIGH) << 8) | +- otg_io_read(otg, ULPI_PRODUCT_ID_LOW); ++ for (i = 0; i < 4; i++) { ++ ret = otg_io_read(otg, ULPI_PRODUCT_ID_HIGH - i); ++ if (ret < 0) ++ return ret; ++ ulpi_id = (ulpi_id << 8) | ret; ++ } ++ vid = ulpi_id & 0xffff; ++ pid = ulpi_id >> 16; + + pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); + diff --git a/usb/revert-usb-adding-support-for-htc-smartphones-to-ipaq.patch b/usb/revert-usb-adding-support-for-htc-smartphones-to-ipaq.patch new file mode 100644 index 00000000000000..840cce4f42565f --- /dev/null +++ b/usb/revert-usb-adding-support-for-htc-smartphones-to-ipaq.patch @@ -0,0 +1,35 @@ +From leann.ogasawara@canonical.com Wed Jun 16 13:29:49 2010 +From: Leann Ogasawara <leann.ogasawara@canonical.com> +Date: Thu, 10 Jun 2010 15:49:24 -0700 +Subject: Revert "USB: Adding support for HTC Smartphones to ipaq" +To: gregkh <gregkh@suse.de> +Cc: linux-usb <linux-usb@vger.kernel.org> +Message-ID: <1276210164.1221.3656.camel@emiko> + + +ipaq already had this device id defined: + +{ USB_DEVICE(0x0BB4, 0x00CF) }, /* HTC USB Modem */ + +Revert the commit which adds the duplicate entry. + +This reverts commit 04cab1329336d4577d6638360c905e360934b425. + +Originally-by: Ben Collins <ben.collins@canonical.com> +Signed-off-by: Leann Ogasawara <leann.ogasawara@canonical.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/ipaq.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/usb/serial/ipaq.c ++++ b/drivers/usb/serial/ipaq.c +@@ -534,7 +534,6 @@ static struct usb_device_id ipaq_id_tabl + { USB_DEVICE(0x413C, 0x4009) }, /* Dell Axim USB Sync */ + { USB_DEVICE(0x4505, 0x0010) }, /* Smartphone */ + { USB_DEVICE(0x5E04, 0xCE00) }, /* SAGEM Wireless Assistant */ +- { USB_DEVICE(0x0BB4, 0x00CF) }, /* HTC smartphone modems */ + { } /* Terminating entry */ + }; + diff --git a/usb/usb-add-a-serial-number-parameter-to-g_file_storage-module.patch b/usb/usb-add-a-serial-number-parameter-to-g_file_storage-module.patch new file mode 100644 index 00000000000000..5202a997ee66c7 --- /dev/null +++ b/usb/usb-add-a-serial-number-parameter-to-g_file_storage-module.patch @@ -0,0 +1,149 @@ +From yann.cantin@laposte.net Wed Jun 16 13:53:56 2010 +From: Yann Cantin <yann.cantin@laposte.net> +Date: Sat, 5 Jun 2010 23:06:31 +0200 +Subject: USB: Add a serial number parameter to g_file_storage module +To: dbrownell@users.sourceforge.net +Cc: linux-usb@vger.kernel.org, Yann Cantin <yann.cantin@laposte.net> +Message-ID: <1275771991-26392-1-git-send-email-yann.cantin@laposte.net> + + +This patch add a serial number parameter to the g_file_storage +module. There's validity checks against the string passed to comply +with the specs. + +Signed-off-by: Yann Cantin <yann.cantin@laposte.net> +Cc: Michał Nazarewicz <m.nazarewicz@samsung.com> +Cc: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/file_storage.c | 69 +++++++++++++++++++++++++++++++------- + 1 file changed, 58 insertions(+), 11 deletions(-) + +--- a/drivers/usb/gadget/file_storage.c ++++ b/drivers/usb/gadget/file_storage.c +@@ -56,7 +56,7 @@ + * following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03), + * UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by + * the optional "protocol" module parameter. In addition, the default +- * Vendor ID, Product ID, and release number can be overridden. ++ * Vendor ID, Product ID, release number and serial number can be overridden. + * + * There is support for multiple logical units (LUNs), each of which has + * its own backing file. The number of LUNs can be set using the optional +@@ -106,6 +106,7 @@ + * vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID + * product=0xPPPP Default 0xa4a5 (FSG), USB Product ID + * release=0xRRRR Override the USB release number (bcdDevice) ++ * serial=HHHH... Override serial number (string of hex chars) + * buflen=N Default N=16384, buffer size used (will be + * rounded down to a multiple of + * PAGE_CACHE_SIZE) +@@ -270,6 +271,8 @@ + + #define DRIVER_DESC "File-backed Storage Gadget" + #define DRIVER_NAME "g_file_storage" ++/* DRIVER_VERSION must be at least 6 characters long, as it is used ++ * to generate a fallback serial number. */ + #define DRIVER_VERSION "20 November 2008" + + static char fsg_string_manufacturer[64]; +@@ -314,6 +317,7 @@ static struct { + unsigned short vendor; + unsigned short product; + unsigned short release; ++ char *serial_parm; + unsigned int buflen; + + int transport_type; +@@ -374,6 +378,9 @@ MODULE_PARM_DESC(product, "USB Product I + module_param_named(release, mod_data.release, ushort, S_IRUGO); + MODULE_PARM_DESC(release, "USB release number"); + ++module_param_named(serial, mod_data.serial_parm, charp, S_IRUGO); ++MODULE_PARM_DESC(serial, "USB serial number"); ++ + module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); + MODULE_PARM_DESC(buflen, "I/O buffer size"); + +@@ -3197,6 +3204,7 @@ static int __init check_parameters(struc + { + int prot; + int gcnum; ++ int i; + + /* Store the default values */ + mod_data.transport_type = USB_PR_BULK; +@@ -3272,6 +3280,55 @@ static int __init check_parameters(struc + ERROR(fsg, "invalid buflen\n"); + return -ETOOSMALL; + } ++ ++ /* Serial string handling. ++ * On a real device, the serial string would be loaded ++ * from permanent storage. */ ++ if (mod_data.serial_parm) { ++ const char *ch; ++ unsigned len = 0; ++ ++ /* Sanity check : ++ * The CB[I] specification limits the serial string to ++ * 12 uppercase hexadecimal characters. ++ * BBB need at least 12 uppercase hexadecimal characters, ++ * with a maximum of 126. */ ++ for (ch = mod_data.serial_parm; *ch; ++ch) { ++ ++len; ++ if ((*ch < '0' || *ch > '9') && ++ (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */ ++ WARNING(fsg, ++ "Invalid serial string character: %c; " ++ "Failing back to default\n", ++ *ch); ++ goto fill_serial; ++ } ++ } ++ if (len > 126 || ++ (mod_data.transport_type == USB_PR_BULK && len < 12) || ++ (mod_data.transport_type != USB_PR_BULK && len > 12)) { ++ WARNING(fsg, ++ "Invalid serial string length; " ++ "Failing back to default\n"); ++ goto fill_serial; ++ } ++ fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial_parm; ++ } else { ++fill_serial: ++ /* Serial number not specified or invalid, make our own. ++ * We just encode it from the driver version string, ++ * 12 characters to comply with both CB[I] and BBB spec. ++ * Warning : Two devices running the same kernel will have ++ * the same fallback serial number. */ ++ for (i = 0; i < 12; i += 2) { ++ unsigned char c = DRIVER_VERSION[i / 2]; ++ ++ if (!c) ++ break; ++ sprintf(&fsg_string_serial[i], "%02X", c); ++ } ++ } ++ + #endif /* CONFIG_USB_FILE_STORAGE_TEST */ + + return 0; +@@ -3447,16 +3504,6 @@ static int __init fsg_bind(struct usb_ga + init_utsname()->sysname, init_utsname()->release, + gadget->name); + +- /* On a real device, serial[] would be loaded from permanent +- * storage. We just encode it from the driver version string. */ +- for (i = 0; i < sizeof fsg_string_serial - 2; i += 2) { +- unsigned char c = DRIVER_VERSION[i / 2]; +- +- if (!c) +- break; +- sprintf(&fsg_string_serial[i], "%02X", c); +- } +- + fsg->thread_task = kthread_create(fsg_main_thread, fsg, + "file-storage-gadget"); + if (IS_ERR(fsg->thread_task)) { diff --git a/usb/usb-conexant-fixed-spacing-and-brace-coding-style-issues.patch b/usb/usb-conexant-fixed-spacing-and-brace-coding-style-issues.patch new file mode 100644 index 00000000000000..59a8ad701cd57e --- /dev/null +++ b/usb/usb-conexant-fixed-spacing-and-brace-coding-style-issues.patch @@ -0,0 +1,66 @@ +From nikai@nikai.net Wed Jun 16 13:49:22 2010 +From: Nicolas Kaiser <nikai@nikai.net> +Date: Wed, 16 Jun 2010 18:56:05 +0200 +Subject: usb: conexant: fixed spacing and brace coding style issues +To: Simon Arlott <cxacru@fire.lp0.eu> +Cc: Greg Kroah-Hartman <gregkh@suse.de>, accessrunner-general@lists.sourceforge.net, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org +Message-ID: <20100616185605.00ce6f31@absol.kitzblitz> + + +Fixed spacing and brace coding style issues. + +Signed-off-by: Nicolas Kaiser <nikai@nikai.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/atm/cxacru.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +--- a/drivers/usb/atm/cxacru.c ++++ b/drivers/usb/atm/cxacru.c +@@ -564,7 +564,7 @@ static void cxacru_timeout_kill(unsigned + } + + static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, +- int* actual_length) ++ int *actual_length) + { + struct timer_list timer; + +@@ -952,7 +952,7 @@ static int cxacru_fw(struct usb_device * + put_unaligned(cpu_to_le32(addr), (__le32 *)(buf + offb)); + offb += 4; + addr += l; +- if(l) ++ if (l) + memcpy(buf + offb, data + offd, l); + if (l < stride) + memset(buf + offb + l, 0, stride - l); +@@ -967,7 +967,7 @@ static int cxacru_fw(struct usb_device * + } + offb = 0; + } +- } while(offd < size); ++ } while (offd < size); + dbg("sent fw %#x", fw); + + ret = 0; +@@ -1043,8 +1043,7 @@ static void cxacru_upload_firmware(struc + if (instance->modem_type->boot_rom_patch) { + val = cpu_to_le32(BR_ADDR); + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4); +- } +- else { ++ } else { + ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0); + } + if (ret) { +@@ -1068,7 +1067,7 @@ static void cxacru_upload_firmware(struc + } + + static int cxacru_find_firmware(struct cxacru_data *instance, +- char* phase, const struct firmware **fw_p) ++ char *phase, const struct firmware **fw_p) + { + struct usbatm_data *usbatm = instance->usbatm; + struct device *dev = &usbatm->usb_intf->dev; diff --git a/usb/usb-ehci-ehci-1.1-addendum-basic-lpm-feature-support.patch b/usb/usb-ehci-ehci-1.1-addendum-basic-lpm-feature-support.patch new file mode 100644 index 00000000000000..95fac8396679ba --- /dev/null +++ b/usb/usb-ehci-ehci-1.1-addendum-basic-lpm-feature-support.patch @@ -0,0 +1,243 @@ +From alek.du@intel.com Wed Jun 16 13:28:26 2010 +From: Alek Du <alek.du@intel.com> +Date: Fri, 4 Jun 2010 15:47:55 +0800 +Subject: USB: EHCI: EHCI 1.1 addendum: Basic LPM feature support +To: greg@kroah.com +Cc: david-b@pacbell.net, linux-usb@vger.kernel.org, oneukum@suse.de, Alek Du <alek.du@intel.com>, Jacob Pan <jacob.jun.pan@intel.com> +Message-ID: <1275637676-24880-3-git-send-email-alek.du@intel.com> + + +From: Alek Du <alek.du@intel.com> + +With this patch, the LPM capable EHCI host controller can put device +into L1 sleep state which is a mode that can enter/exit quickly, and +reduce power consumption. + +Signed-off-by: Jacob Pan <jacob.jun.pan@intel.com> +Signed-off-by: Alek Du <alek.du@intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/core/hub.c | 4 +- + drivers/usb/host/ehci-hcd.c | 17 +++++++++ + drivers/usb/host/ehci-hub.c | 5 ++ + drivers/usb/host/ehci-lpm.c | 83 ++++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/host/ehci-pci.c | 21 +++++++++++ + drivers/usb/host/ehci.h | 2 - + include/linux/usb/hcd.h | 4 ++ + 7 files changed, 134 insertions(+), 2 deletions(-) + +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -2878,7 +2878,9 @@ hub_port_init (struct usb_hub *hub, stru + } + + retval = 0; +- ++ /* notify HCD that we have a device connected and addressed */ ++ if (hcd->driver->update_device) ++ hcd->driver->update_device(hcd, udev); + fail: + if (retval) { + hub_port_disable(hub, port1, 0); +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -100,6 +100,11 @@ static int ignore_oc = 0; + module_param (ignore_oc, bool, S_IRUGO); + MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); + ++/* for link power management(LPM) feature */ ++static unsigned int hird; ++module_param(hird, int, S_IRUGO); ++MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); ++ + #define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) + + /*-------------------------------------------------------------------------*/ +@@ -304,6 +309,7 @@ static void end_unlink_async(struct ehci + static void ehci_work(struct ehci_hcd *ehci); + + #include "ehci-hub.c" ++#include "ehci-lpm.c" + #include "ehci-mem.c" + #include "ehci-q.c" + #include "ehci-sched.c" +@@ -603,6 +609,17 @@ static int ehci_init(struct usb_hcd *hcd + default: BUG(); + } + } ++ if (HCC_LPM(hcc_params)) { ++ /* support link power management EHCI 1.1 addendum */ ++ ehci_dbg(ehci, "support lpm\n"); ++ ehci->has_lpm = 1; ++ if (hird > 0xf) { ++ ehci_dbg(ehci, "hird %d invalid, use default 0", ++ hird); ++ hird = 0; ++ } ++ temp |= hird << 24; ++ } + ehci->command = temp; + + /* Accept arbitrarily long scatter-gather lists */ +--- a/drivers/usb/host/ehci-hub.c ++++ b/drivers/usb/host/ehci-hub.c +@@ -790,6 +790,11 @@ static int ehci_hub_control ( + status_reg); + break; + case USB_PORT_FEAT_C_CONNECTION: ++ if (ehci->has_lpm) { ++ /* clear PORTSC bits on disconnect */ ++ temp &= ~PORT_LPM; ++ temp &= ~PORT_DEV_ADDR; ++ } + ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC, + status_reg); + break; +--- /dev/null ++++ b/drivers/usb/host/ehci-lpm.c +@@ -0,0 +1,83 @@ ++/* ehci-lpm.c EHCI HCD LPM support code ++ * Copyright (c) 2008 - 2010, Intel Corporation. ++ * Author: Jacob Pan <jacob.jun.pan@intel.com> ++ * ++ * 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. ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* this file is part of ehci-hcd.c */ ++static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num) ++{ ++ u32 __iomem portsc; ++ ++ ehci_dbg(ehci, "set dev address %d for port %d\n", dev_addr, port_num); ++ if (port_num > HCS_N_PORTS(ehci->hcs_params)) { ++ ehci_dbg(ehci, "invalid port number %d\n", port_num); ++ return -ENODEV; ++ } ++ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_num-1]); ++ portsc &= ~PORT_DEV_ADDR; ++ portsc |= dev_addr<<25; ++ ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]); ++ return 0; ++} ++ ++/* ++ * this function is used to check if the device support LPM ++ * if yes, mark the PORTSC register with PORT_LPM bit ++ */ ++static int ehci_lpm_check(struct ehci_hcd *ehci, int port) ++{ ++ u32 __iomem *portsc ; ++ u32 val32; ++ int retval; ++ ++ portsc = &ehci->regs->port_status[port-1]; ++ val32 = ehci_readl(ehci, portsc); ++ if (!(val32 & PORT_DEV_ADDR)) { ++ ehci_dbg(ehci, "LPM: no device attached\n"); ++ return -ENODEV; ++ } ++ val32 |= PORT_LPM; ++ ehci_writel(ehci, val32, portsc); ++ msleep(5); ++ val32 |= PORT_SUSPEND; ++ ehci_dbg(ehci, "Sending LPM 0x%08x to port %d\n", val32, port); ++ ehci_writel(ehci, val32, portsc); ++ /* wait for ACK */ ++ msleep(10); ++ retval = handshake(ehci, &ehci->regs->port_status[port-1], PORT_SSTS, ++ PORTSC_SUSPEND_STS_ACK, 125); ++ dbg_port(ehci, "LPM", port, val32); ++ if (retval != -ETIMEDOUT) { ++ ehci_dbg(ehci, "LPM: device ACK for LPM\n"); ++ val32 |= PORT_LPM; ++ /* ++ * now device should be in L1 sleep, let's wake up the device ++ * so that we can complete enumeration. ++ */ ++ ehci_writel(ehci, val32, portsc); ++ msleep(10); ++ val32 |= PORT_RESUME; ++ ehci_writel(ehci, val32, portsc); ++ } else { ++ ehci_dbg(ehci, "LPM: device does not ACK, disable LPM %d\n", ++ retval); ++ val32 &= ~PORT_LPM; ++ retval = -ETIMEDOUT; ++ ehci_writel(ehci, val32, portsc); ++ } ++ ++ return retval; ++} +--- a/drivers/usb/host/ehci-pci.c ++++ b/drivers/usb/host/ehci-pci.c +@@ -361,6 +361,22 @@ static int ehci_pci_resume(struct usb_hc + } + #endif + ++static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev) ++{ ++ struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ int rc = 0; ++ ++ if (!udev->parent) /* udev is root hub itself, impossible */ ++ rc = -1; ++ /* we only support lpm device connected to root hub yet */ ++ if (ehci->has_lpm && !udev->parent->parent) { ++ rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum); ++ if (!rc) ++ rc = ehci_lpm_check(ehci, udev->portnum); ++ } ++ return rc; ++} ++ + static const struct hc_driver ehci_pci_hc_driver = { + .description = hcd_name, + .product_desc = "EHCI Host Controller", +@@ -407,6 +423,11 @@ static const struct hc_driver ehci_pci_h + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + ++ /* ++ * call back when device connected and addressed ++ */ ++ .update_device = ehci_update_device, ++ + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + }; + +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -140,7 +140,7 @@ struct ehci_hcd { /* one per controlle + #define OHCI_HCCTRL_LEN 0x4 + __hc32 *ohci_hcctrl_reg; + unsigned has_hostpc:1; +- ++ unsigned has_lpm:1; /* support link power management */ + u8 sbrn; /* packed release number */ + + /* irq statistics */ +--- a/include/linux/usb/hcd.h ++++ b/include/linux/usb/hcd.h +@@ -300,6 +300,10 @@ struct hc_driver { + int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); + int (*reset_device)(struct usb_hcd *, struct usb_device *); ++ /* Notifies the HCD after a device is connected and its ++ * address is set ++ */ ++ int (*update_device)(struct usb_hcd *, struct usb_device *); + }; + + extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); diff --git a/usb/usb-ehci-ehci-1.1-addendum-enable-per-port-change-detect-bits.patch b/usb/usb-ehci-ehci-1.1-addendum-enable-per-port-change-detect-bits.patch new file mode 100644 index 00000000000000..2aa59fb010338c --- /dev/null +++ b/usb/usb-ehci-ehci-1.1-addendum-enable-per-port-change-detect-bits.patch @@ -0,0 +1,104 @@ +From alek.du@intel.com Wed Jun 16 13:29:07 2010 +From: Alek Du <alek.du@intel.com> +Date: Fri, 4 Jun 2010 15:47:56 +0800 +Subject: USB: EHCI: EHCI 1.1 addendum: Enable Per-port change detect bits +To: greg@kroah.com +Cc: david-b@pacbell.net, linux-usb@vger.kernel.org, oneukum@suse.de, Alek Du <alek.du@intel.com> +Message-ID: <1275637676-24880-4-git-send-email-alek.du@intel.com> + + +From: Alek Du <alek.du@intel.com> + +This patch will enable Per-port event feature defined in EHCI 1.1 +addendum. This feature addresses an issue where HCD is currently +required to read and parse PORTSC for all enabled root hub ports. With +this patch, the overhead will be reduced. + +Signed-off-by: Alek Du <alek.du@intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-hcd.c | 19 +++++++++++++++++-- + drivers/usb/host/ehci-hub.c | 9 +++++++++ + drivers/usb/host/ehci.h | 1 + + 3 files changed, 27 insertions(+), 2 deletions(-) + +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -583,6 +583,11 @@ static int ehci_init(struct usb_hcd *hcd + if (log2_irq_thresh < 0 || log2_irq_thresh > 6) + log2_irq_thresh = 0; + temp = 1 << (16 + log2_irq_thresh); ++ if (HCC_PER_PORT_CHANGE_EVENT(hcc_params)) { ++ ehci->has_ppcd = 1; ++ ehci_dbg(ehci, "enable per-port change event\n"); ++ temp |= CMD_PPCEE; ++ } + if (HCC_CANPARK(hcc_params)) { + /* HW default park == 3, on hardware that supports it (like + * NVidia and ALI silicon), maximizes throughput on the async +@@ -781,6 +786,7 @@ static irqreturn_t ehci_irq (struct usb_ + /* remote wakeup [4.3.1] */ + if (status & STS_PCD) { + unsigned i = HCS_N_PORTS (ehci->hcs_params); ++ u32 ppcd = 0; + + /* kick root hub later */ + pcd_status = status; +@@ -789,9 +795,18 @@ static irqreturn_t ehci_irq (struct usb_ + if (!(cmd & CMD_RUN)) + usb_hcd_resume_root_hub(hcd); + ++ /* get per-port change detect bits */ ++ if (ehci->has_ppcd) ++ ppcd = status >> 16; ++ + while (i--) { +- int pstatus = ehci_readl(ehci, +- &ehci->regs->port_status [i]); ++ int pstatus; ++ ++ /* leverage per-port change bits feature */ ++ if (ehci->has_ppcd && !(ppcd & (1 << i))) ++ continue; ++ pstatus = ehci_readl(ehci, ++ &ehci->regs->port_status[i]); + + if (pstatus & PORT_OWNER) + continue; +--- a/drivers/usb/host/ehci-hub.c ++++ b/drivers/usb/host/ehci-hub.c +@@ -603,6 +603,7 @@ ehci_hub_status_data (struct usb_hcd *hc + u32 mask; + int ports, i, retval = 1; + unsigned long flags; ++ u32 ppcd = 0; + + /* if !USB_SUSPEND, root hub timers won't get shut down ... */ + if (!HC_IS_RUNNING(hcd->state)) +@@ -632,7 +633,15 @@ ehci_hub_status_data (struct usb_hcd *hc + + /* port N changes (bit N)? */ + spin_lock_irqsave (&ehci->lock, flags); ++ ++ /* get per-port change detect bits */ ++ if (ehci->has_ppcd) ++ ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16; ++ + for (i = 0; i < ports; i++) { ++ /* leverage per-port change bits feature */ ++ if (ehci->has_ppcd && !(ppcd & (1 << i))) ++ continue; + temp = ehci_readl(ehci, &ehci->regs->port_status [i]); + + /* +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -141,6 +141,7 @@ struct ehci_hcd { /* one per controlle + __hc32 *ohci_hcctrl_reg; + unsigned has_hostpc:1; + unsigned has_lpm:1; /* support link power management */ ++ unsigned has_ppcd:1; /* support per-port change bits */ + u8 sbrn; /* packed release number */ + + /* irq statistics */ diff --git a/usb/usb-ehci-ehci-1.1-addendum-preparation.patch b/usb/usb-ehci-ehci-1.1-addendum-preparation.patch new file mode 100644 index 00000000000000..21ad12d6232f20 --- /dev/null +++ b/usb/usb-ehci-ehci-1.1-addendum-preparation.patch @@ -0,0 +1,323 @@ +From alek.du@intel.com Wed Jun 16 13:27:54 2010 +From: Alek Du <alek.du@intel.com> +Date: Fri, 4 Jun 2010 15:47:54 +0800 +Subject: USB: EHCI: EHCI 1.1 addendum: preparation +To: greg@kroah.com +Cc: david-b@pacbell.net, linux-usb@vger.kernel.org, oneukum@suse.de, Alek Du <alek.du@intel.com>, Jacob Pan <jacob.jun.pan@intel.com> +Message-ID: <1275637676-24880-2-git-send-email-alek.du@intel.com> + + +From: Alek Du <alek.du@intel.com> + +EHCI 1.1 addendum introduced several energy efficiency extensions for +EHCI USB host controllers: +1. LPM (link power management) +2. Per-port change +3. Shorter periodic frame list +4. Hardware prefetching + +This patch is intended to define the HW bits and debug interface for +EHCI 1.1 addendum. The LPM and Per-port change patches will be sent out +after this patch. + +Signed-off-by: Jacob Pan <jacob.jun.pan@intel.com> +Signed-off-by: Alek Du <alek.du@intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-dbg.c | 144 ++++++++++++++++++++++++++++++++++++++++--- + drivers/usb/host/ehci.h | 1 + include/linux/usb/ehci_def.h | 23 ++++++ + 3 files changed, 161 insertions(+), 7 deletions(-) + +--- a/drivers/usb/host/ehci-dbg.c ++++ b/drivers/usb/host/ehci-dbg.c +@@ -98,13 +98,18 @@ static void dbg_hcc_params (struct ehci_ + HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); + } else { + ehci_dbg (ehci, +- "%s hcc_params %04x thresh %d uframes %s%s%s\n", ++ "%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n", + label, + params, + HCC_ISOC_THRES(params), + HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", + HCC_CANPARK(params) ? " park" : "", +- HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); ++ HCC_64BIT_ADDR(params) ? " 64 bit addr" : "", ++ HCC_LPM(params) ? " LPM" : "", ++ HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "", ++ HCC_HW_PREFETCH(params) ? " hw prefetch" : "", ++ HCC_32FRAME_PERIODIC_LIST(params) ? ++ " 32 peridic list" : ""); + } + } + #else +@@ -191,8 +196,9 @@ static int __maybe_unused + dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) + { + return scnprintf (buf, len, +- "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", ++ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", status, ++ (status & STS_PPCE_MASK) ? " PPCE" : "", + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", +@@ -210,8 +216,9 @@ static int __maybe_unused + dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) + { + return scnprintf (buf, len, +- "%s%sintrenable %02x%s%s%s%s%s%s", ++ "%s%sintrenable %02x%s%s%s%s%s%s%s", + label, label [0] ? " " : "", enable, ++ (enable & STS_PPCE_MASK) ? " PPCE" : "", + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", +@@ -228,9 +235,15 @@ static int + dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) + { + return scnprintf (buf, len, +- "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", ++ "%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s " ++ "period=%s%s %s", + label, label [0] ? " " : "", command, +- (command & CMD_PARK) ? "park" : "(park)", ++ (command & CMD_HIRD) ? " HIRD" : "", ++ (command & CMD_PPCEE) ? " PPCEE" : "", ++ (command & CMD_FSP) ? " FSP" : "", ++ (command & CMD_ASPE) ? " ASPE" : "", ++ (command & CMD_PSPE) ? " PSPE" : "", ++ (command & CMD_PARK) ? " park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", +@@ -257,11 +270,22 @@ dbg_port_buf (char *buf, unsigned len, c + } + + return scnprintf (buf, len, +- "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", ++ "%s%sport:%d status %06x %d %s%s%s%s%s%s " ++ "sig=%s%s%s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", port, status, ++ status>>25,/*device address */ ++ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ? ++ " ACK" : "", ++ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ? ++ " NYET" : "", ++ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ? ++ " STALL" : "", ++ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ? ++ " ERR" : "", + (status & PORT_POWER) ? " POWER" : "", + (status & PORT_OWNER) ? " OWNER" : "", + sig, ++ (status & PORT_LPM) ? " LPM" : "", + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", +@@ -330,6 +354,13 @@ static int debug_async_open(struct inode + static int debug_periodic_open(struct inode *, struct file *); + static int debug_registers_open(struct inode *, struct file *); + static int debug_async_open(struct inode *, struct file *); ++static int debug_lpm_open(struct inode *, struct file *); ++static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos); ++static ssize_t debug_lpm_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos); ++static int debug_lpm_close(struct inode *inode, struct file *file); ++ + static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); + static int debug_close(struct inode *, struct file *); + +@@ -351,6 +382,13 @@ static const struct file_operations debu + .read = debug_output, + .release = debug_close, + }; ++static const struct file_operations debug_lpm_fops = { ++ .owner = THIS_MODULE, ++ .open = debug_lpm_open, ++ .read = debug_lpm_read, ++ .write = debug_lpm_write, ++ .release = debug_lpm_close, ++}; + + static struct dentry *ehci_debug_root; + +@@ -917,6 +955,94 @@ static int debug_registers_open(struct i + return file->private_data ? 0 : -ENOMEM; + } + ++static int debug_lpm_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->i_private; ++ return 0; ++} ++ ++static int debug_lpm_close(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ /* TODO: show lpm stats */ ++ return 0; ++} ++ ++static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct usb_hcd *hcd; ++ struct ehci_hcd *ehci; ++ char buf[50]; ++ size_t len; ++ u32 temp; ++ unsigned long port; ++ u32 __iomem *portsc ; ++ u32 params; ++ ++ hcd = bus_to_hcd(file->private_data); ++ ehci = hcd_to_ehci(hcd); ++ ++ len = min(count, sizeof(buf) - 1); ++ if (copy_from_user(buf, user_buf, len)) ++ return -EFAULT; ++ buf[len] = '\0'; ++ if (len > 0 && buf[len - 1] == '\n') ++ buf[len - 1] = '\0'; ++ ++ if (strncmp(buf, "enable", 5) == 0) { ++ if (strict_strtoul(buf + 7, 10, &port)) ++ return -EINVAL; ++ params = ehci_readl(ehci, &ehci->caps->hcs_params); ++ if (port > HCS_N_PORTS(params)) { ++ ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port); ++ return -ENODEV; ++ } ++ portsc = &ehci->regs->port_status[port-1]; ++ temp = ehci_readl(ehci, portsc); ++ if (!(temp & PORT_DEV_ADDR)) { ++ ehci_dbg(ehci, "LPM: no device attached\n"); ++ return -ENODEV; ++ } ++ temp |= PORT_LPM; ++ ehci_writel(ehci, temp, portsc); ++ printk(KERN_INFO "force enable LPM for port %lu\n", port); ++ } else if (strncmp(buf, "hird=", 5) == 0) { ++ unsigned long hird; ++ if (strict_strtoul(buf + 5, 16, &hird)) ++ return -EINVAL; ++ printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird); ++ temp = ehci_readl(ehci, &ehci->regs->command); ++ temp &= ~CMD_HIRD; ++ temp |= hird << 24; ++ ehci_writel(ehci, temp, &ehci->regs->command); ++ } else if (strncmp(buf, "disable", 7) == 0) { ++ if (strict_strtoul(buf + 8, 10, &port)) ++ return -EINVAL; ++ params = ehci_readl(ehci, &ehci->caps->hcs_params); ++ if (port > HCS_N_PORTS(params)) { ++ ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port); ++ return -ENODEV; ++ } ++ portsc = &ehci->regs->port_status[port-1]; ++ temp = ehci_readl(ehci, portsc); ++ if (!(temp & PORT_DEV_ADDR)) { ++ ehci_dbg(ehci, "ERR: no device attached\n"); ++ return -ENODEV; ++ } ++ temp &= ~PORT_LPM; ++ ehci_writel(ehci, temp, portsc); ++ printk(KERN_INFO "disabled LPM for port %lu\n", port); ++ } else ++ return -EOPNOTSUPP; ++ return count; ++} ++ + static inline void create_debug_files (struct ehci_hcd *ehci) + { + struct usb_bus *bus = &ehci_to_hcd(ehci)->self; +@@ -940,6 +1066,10 @@ static inline void create_debug_files (s + ehci->debug_registers = debugfs_create_file("registers", S_IRUGO, + ehci->debug_dir, bus, + &debug_registers_fops); ++ ++ ehci->debug_registers = debugfs_create_file("lpm", S_IRUGO|S_IWUGO, ++ ehci->debug_dir, bus, ++ &debug_lpm_fops); + if (!ehci->debug_registers) + goto registers_error; + return; +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -157,6 +157,7 @@ struct ehci_hcd { /* one per controlle + struct dentry *debug_async; + struct dentry *debug_periodic; + struct dentry *debug_registers; ++ struct dentry *debug_lpm; + #endif + }; + +--- a/include/linux/usb/ehci_def.h ++++ b/include/linux/usb/ehci_def.h +@@ -39,6 +39,12 @@ struct ehci_caps { + #define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ ++/* EHCI 1.1 addendum */ ++#define HCC_32FRAME_PERIODIC_LIST(p) ((p)&(1 << 19)) ++#define HCC_PER_PORT_CHANGE_EVENT(p) ((p)&(1 << 18)) ++#define HCC_LPM(p) ((p)&(1 << 17)) ++#define HCC_HW_PREFETCH(p) ((p)&(1 << 16)) ++ + #define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ + #define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ + #define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +@@ -54,6 +60,13 @@ struct ehci_regs { + + /* USBCMD: offset 0x00 */ + u32 command; ++ ++/* EHCI 1.1 addendum */ ++#define CMD_HIRD (0xf<<24) /* host initiated resume duration */ ++#define CMD_PPCEE (1<<15) /* per port change event enable */ ++#define CMD_FSP (1<<14) /* fully synchronized prefetch */ ++#define CMD_ASPE (1<<13) /* async schedule prefetch enable */ ++#define CMD_PSPE (1<<12) /* periodic schedule prefetch enable */ + /* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ + #define CMD_PARK (1<<11) /* enable "park" on async qh */ + #define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +@@ -67,6 +80,7 @@ struct ehci_regs { + + /* USBSTS: offset 0x04 */ + u32 status; ++#define STS_PPCE_MASK (0xff<<16) /* Per-Port change event 1-16 */ + #define STS_ASS (1<<15) /* Async Schedule Status */ + #define STS_PSS (1<<14) /* Periodic Schedule Status */ + #define STS_RECL (1<<13) /* Reclamation */ +@@ -100,6 +114,14 @@ struct ehci_regs { + + /* PORTSC: offset 0x44 */ + u32 port_status[0]; /* up to N_PORTS */ ++/* EHCI 1.1 addendum */ ++#define PORTSC_SUSPEND_STS_ACK 0 ++#define PORTSC_SUSPEND_STS_NYET 1 ++#define PORTSC_SUSPEND_STS_STALL 2 ++#define PORTSC_SUSPEND_STS_ERR 3 ++ ++#define PORT_DEV_ADDR (0x7f<<25) /* device address */ ++#define PORT_SSTS (0x3<<23) /* suspend status */ + /* 31:23 reserved */ + #define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ + #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +@@ -115,6 +137,7 @@ struct ehci_regs { + #define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ + /* 11:10 for detecting lowspeed devices (reset vs release ownership) */ + /* 9 reserved */ ++#define PORT_LPM (1<<9) /* LPM transaction */ + #define PORT_RESET (1<<8) /* reset port */ + #define PORT_SUSPEND (1<<7) /* suspend port */ + #define PORT_RESUME (1<<6) /* resume it */ diff --git a/usb/usb-gadget-langwell_udc.c-printk-needs-a-unsigned-long-long-cast-for-a-dma_t.patch b/usb/usb-gadget-langwell_udc.c-printk-needs-a-unsigned-long-long-cast-for-a-dma_t.patch new file mode 100644 index 00000000000000..154221af9463b0 --- /dev/null +++ b/usb/usb-gadget-langwell_udc.c-printk-needs-a-unsigned-long-long-cast-for-a-dma_t.patch @@ -0,0 +1,31 @@ +From joe@perches.com Wed Jun 16 13:30:08 2010 +From: Joe Perches <joe@perches.com> +Date: Thu, 10 Jun 2010 19:20:43 -0700 +Subject: USB: gadget: langwell_udc.c: printk needs a (unsigned long long) cast for a dma_t +To: Xiaochen Shen <xiaochen.shen@intel.com> +Cc: David Brownell <dbrownell@users.sourceforge.net>, Greg Kroah-Hartman <gregkh@suse.de>, linux-usb <linux-usb@vger.kernel.org>, LKML <linux-kernel@vger.kernel.org> +Message-ID: <1276222843.1556.438.camel@Joe-Laptop.home> + + +Signed-off-by: Joe Perches <joe@perches.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/langwell_udc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/usb/gadget/langwell_udc.c ++++ b/drivers/usb/gadget/langwell_udc.c +@@ -842,9 +842,9 @@ static int langwell_ep_queue(struct usb_ + VDBG(dev, "req->mapped = 0\n"); + } + +- DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n", +- _ep->name, +- _req, _req->length, _req->buf, _req->dma); ++ DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08llx\n", ++ _ep->name, ++ _req, _req->length, _req->buf, (unsigned long long)_req->dma); + + _req->status = -EINPROGRESS; + _req->actual = 0; diff --git a/usb/usb-option-remove-duplicate-amoi_vendor_id.patch b/usb/usb-option-remove-duplicate-amoi_vendor_id.patch new file mode 100644 index 00000000000000..2d86097f361c0a --- /dev/null +++ b/usb/usb-option-remove-duplicate-amoi_vendor_id.patch @@ -0,0 +1,41 @@ +From leann.ogasawara@canonical.com Wed Jun 16 13:29:36 2010 +From: Leann Ogasawara <leann.ogasawara@canonical.com> +Date: Thu, 10 Jun 2010 14:51:51 -0700 +Subject: USB: option: Remove duplicate AMOI_VENDOR_ID +To: smurf@smurf.noris.de +Cc: linux-usb@vger.kernel.org +Message-ID: <1276206711.1221.3631.camel@emiko> + + +AMOI_VENDOR_ID is defined twice. Remove the duplicate entry and move +the AMOI_PRODUCT_9508 definition to be grouped with the other AMOI +product definitions. + +Originally-by: Ben Collins <ben.collins@ubuntu.com> +Signed-off-by: Leann Ogasawara <leann.ogasawara@canonical.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/option.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -206,6 +206,7 @@ static void option_instat_callback(struc + #define AMOI_PRODUCT_H01 0x0800 + #define AMOI_PRODUCT_H01A 0x7002 + #define AMOI_PRODUCT_H02 0x0802 ++#define AMOI_PRODUCT_9508 0x0800 + + #define DELL_VENDOR_ID 0x413C + +@@ -263,9 +264,6 @@ static void option_instat_callback(struc + #define BANDRICH_PRODUCT_1011 0x1011 + #define BANDRICH_PRODUCT_1012 0x1012 + +-#define AMOI_VENDOR_ID 0x1614 +-#define AMOI_PRODUCT_9508 0x0800 +- + #define QUALCOMM_VENDOR_ID 0x05C6 + + #define CMOTECH_VENDOR_ID 0x16d8 diff --git a/usb/usb-throw-away-custom-hex-digit-methods.patch b/usb/usb-throw-away-custom-hex-digit-methods.patch new file mode 100644 index 00000000000000..8738f9fba0667e --- /dev/null +++ b/usb/usb-throw-away-custom-hex-digit-methods.patch @@ -0,0 +1,81 @@ +From andy.shevchenko@gmail.com Wed Jun 16 13:22:24 2010 +From: Andy Shevchenko <andy.shevchenko@gmail.com> +Date: Tue, 15 Jun 2010 17:04:44 +0300 +Subject: usb: throw away custom hex digit methods +To: linux-kernel@vger.kernel.org +Cc: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>, Greg Kroah-Hartman <gregkh@suse.de>, David Brownell <dbrownell@users.sourceforge.net>, linux-usb@vger.kernel.org +Message-ID: <1276610684-26335-1-git-send-email-andy.shevchenko@gmail.com> + + +From: Andy Shevchenko <ext-andriy.shevchenko@nokia.com> + +Recent kernel has common method to convert hex digit to its value. + +Signed-off-by: Andy Shevchenko <ext-andriy.shevchenko@nokia.com> +Cc: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/atm/ueagle-atm.c | 5 +++-- + drivers/usb/gadget/u_ether.c | 15 ++------------- + 2 files changed, 5 insertions(+), 15 deletions(-) + +--- a/drivers/usb/atm/ueagle-atm.c ++++ b/drivers/usb/atm/ueagle-atm.c +@@ -67,6 +67,7 @@ + #include <linux/mutex.h> + #include <linux/freezer.h> + #include <linux/slab.h> ++#include <linux/kernel.h> + + #include <asm/unaligned.h> + +@@ -2429,7 +2430,6 @@ UEA_ATTR(firmid, 0); + + /* Retrieve the device End System Identifier (MAC) */ + +-#define htoi(x) (isdigit(x) ? x-'0' : toupper(x)-'A'+10) + static int uea_getesi(struct uea_softc *sc, u_char * esi) + { + unsigned char mac_str[2 * ETH_ALEN + 1]; +@@ -2440,7 +2440,8 @@ static int uea_getesi(struct uea_softc * + return 1; + + for (i = 0; i < ETH_ALEN; i++) +- esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]); ++ esi[i] = hex_to_bin(mac_str[2 * i]) * 16 + ++ hex_to_bin(mac_str[2 * i + 1]); + + return 0; + } +--- a/drivers/usb/gadget/u_ether.c ++++ b/drivers/usb/gadget/u_ether.c +@@ -704,17 +704,6 @@ static char *host_addr; + module_param(host_addr, charp, S_IRUGO); + MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); + +- +-static u8 __init nibble(unsigned char c) +-{ +- if (isdigit(c)) +- return c - '0'; +- c = toupper(c); +- if (isxdigit(c)) +- return 10 + c - 'A'; +- return 0; +-} +- + static int get_ether_addr(const char *str, u8 *dev_addr) + { + if (str) { +@@ -725,8 +714,8 @@ static int get_ether_addr(const char *st + + if ((*str == '.') || (*str == ':')) + str++; +- num = nibble(*str++) << 4; +- num |= (nibble(*str++)); ++ num = hex_to_bin(*str++) << 4; ++ num |= hex_to_bin(*str++); + dev_addr [i] = num; + } + if (is_valid_ether_addr(dev_addr)) diff --git a/usb/usb-uvc-move-constants-and-structures-definitions-to-linux-usb-video.h.patch b/usb/usb-uvc-move-constants-and-structures-definitions-to-linux-usb-video.h.patch new file mode 100644 index 00000000000000..59c6f4834cb351 --- /dev/null +++ b/usb/usb-uvc-move-constants-and-structures-definitions-to-linux-usb-video.h.patch @@ -0,0 +1,1044 @@ +From laurent.pinchart@ideasonboard.com Wed Jun 16 13:25:39 2010 +From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +Date: Mon, 7 Jun 2010 13:09:47 +0200 +Subject: USB: uvc: Move constants and structures definitions to linux/usb/video.h +To: linux-usb@vger.kernel.org +Message-ID: <1275908987-5774-1-git-send-email-laurent.pinchart@ideasonboard.com> + + +The UVC host and gadget drivers both define constants and structures in +private header files. Move all those definitions to linux/usb/video.h +where they can be shared by the two drivers (and be available for +userspace applications). + +Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/media/video/uvc/uvcvideo.h | 19 - + drivers/usb/gadget/f_uvc.c | 16 - + drivers/usb/gadget/f_uvc.h | 352 -------------------------------- + drivers/usb/gadget/uvc.h | 36 --- + drivers/usb/gadget/webcam.c | 24 +- + include/linux/usb/video.h | 397 +++++++++++++++++++++++++++++++++++++ + 6 files changed, 418 insertions(+), 426 deletions(-) + +--- a/drivers/media/video/uvc/uvcvideo.h ++++ b/drivers/media/video/uvc/uvcvideo.h +@@ -179,25 +179,6 @@ struct uvc_device; + /* TODO: Put the most frequently accessed fields at the beginning of + * structures to maximize cache efficiency. + */ +-struct uvc_streaming_control { +- __u16 bmHint; +- __u8 bFormatIndex; +- __u8 bFrameIndex; +- __u32 dwFrameInterval; +- __u16 wKeyFrameRate; +- __u16 wPFrameRate; +- __u16 wCompQuality; +- __u16 wCompWindowSize; +- __u16 wDelay; +- __u32 dwMaxVideoFrameSize; +- __u32 dwMaxPayloadTransferSize; +- __u32 dwClockFrequency; +- __u8 bmFramingInfo; +- __u8 bPreferedVersion; +- __u8 bMinVersion; +- __u8 bMaxVersion; +-}; +- + struct uvc_menu_info { + __u32 value; + __u8 name[32]; +--- a/drivers/usb/gadget/f_uvc.c ++++ b/drivers/usb/gadget/f_uvc.c +@@ -61,12 +61,12 @@ static struct usb_gadget_strings *uvc_fu + #define UVC_INTF_VIDEO_STREAMING 1 + + static struct usb_interface_assoc_descriptor uvc_iad __initdata = { +- .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, ++ .bLength = sizeof(uvc_iad), + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 0, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_VIDEO, +- .bFunctionSubClass = 0x03, ++ .bFunctionSubClass = UVC_SC_VIDEO_INTERFACE_COLLECTION, + .bFunctionProtocol = 0x00, + .iFunction = 0, + }; +@@ -78,7 +78,7 @@ static struct usb_interface_descriptor u + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_VIDEO, +- .bInterfaceSubClass = 0x01, ++ .bInterfaceSubClass = UVC_SC_VIDEOCONTROL, + .bInterfaceProtocol = 0x00, + .iInterface = 0, + }; +@@ -106,7 +106,7 @@ static struct usb_interface_descriptor u + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_VIDEO, +- .bInterfaceSubClass = 0x02, ++ .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, + .bInterfaceProtocol = 0x00, + .iInterface = 0, + }; +@@ -118,7 +118,7 @@ static struct usb_interface_descriptor u + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_VIDEO, +- .bInterfaceSubClass = 0x02, ++ .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, + .bInterfaceProtocol = 0x00, + .iInterface = 0, + }; +@@ -603,15 +603,15 @@ uvc_bind_config(struct usb_configuration + + /* Validate the descriptors. */ + if (control == NULL || control[0] == NULL || +- control[0]->bDescriptorSubType != UVC_DT_HEADER) ++ control[0]->bDescriptorSubType != UVC_VC_HEADER) + goto error; + + if (fs_streaming == NULL || fs_streaming[0] == NULL || +- fs_streaming[0]->bDescriptorSubType != UVC_DT_INPUT_HEADER) ++ fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + goto error; + + if (hs_streaming == NULL || hs_streaming[0] == NULL || +- hs_streaming[0]->bDescriptorSubType != UVC_DT_INPUT_HEADER) ++ hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + goto error; + + uvc->desc.control = control; +--- a/drivers/usb/gadget/f_uvc.h ++++ b/drivers/usb/gadget/f_uvc.h +@@ -15,357 +15,7 @@ + #define _F_UVC_H_ + + #include <linux/usb/composite.h> +- +-#define USB_CLASS_VIDEO_CONTROL 1 +-#define USB_CLASS_VIDEO_STREAMING 2 +- +-struct uvc_descriptor_header { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +-} __attribute__ ((packed)); +- +-struct uvc_header_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u16 bcdUVC; +- __u16 wTotalLength; +- __u32 dwClockFrequency; +- __u8 bInCollection; +- __u8 baInterfaceNr[]; +-} __attribute__((__packed__)); +- +-#define UVC_HEADER_DESCRIPTOR(n) uvc_header_descriptor_##n +- +-#define DECLARE_UVC_HEADER_DESCRIPTOR(n) \ +-struct UVC_HEADER_DESCRIPTOR(n) { \ +- __u8 bLength; \ +- __u8 bDescriptorType; \ +- __u8 bDescriptorSubType; \ +- __u16 bcdUVC; \ +- __u16 wTotalLength; \ +- __u32 dwClockFrequency; \ +- __u8 bInCollection; \ +- __u8 baInterfaceNr[n]; \ +-} __attribute__ ((packed)) +- +-struct uvc_input_terminal_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bTerminalID; +- __u16 wTerminalType; +- __u8 bAssocTerminal; +- __u8 iTerminal; +-} __attribute__((__packed__)); +- +-struct uvc_output_terminal_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bTerminalID; +- __u16 wTerminalType; +- __u8 bAssocTerminal; +- __u8 bSourceID; +- __u8 iTerminal; +-} __attribute__((__packed__)); +- +-struct uvc_camera_terminal_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bTerminalID; +- __u16 wTerminalType; +- __u8 bAssocTerminal; +- __u8 iTerminal; +- __u16 wObjectiveFocalLengthMin; +- __u16 wObjectiveFocalLengthMax; +- __u16 wOcularFocalLength; +- __u8 bControlSize; +- __u8 bmControls[3]; +-} __attribute__((__packed__)); +- +-struct uvc_selector_unit_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bUnitID; +- __u8 bNrInPins; +- __u8 baSourceID[0]; +- __u8 iSelector; +-} __attribute__((__packed__)); +- +-#define UVC_SELECTOR_UNIT_DESCRIPTOR(n) \ +- uvc_selector_unit_descriptor_##n +- +-#define DECLARE_UVC_SELECTOR_UNIT_DESCRIPTOR(n) \ +-struct UVC_SELECTOR_UNIT_DESCRIPTOR(n) { \ +- __u8 bLength; \ +- __u8 bDescriptorType; \ +- __u8 bDescriptorSubType; \ +- __u8 bUnitID; \ +- __u8 bNrInPins; \ +- __u8 baSourceID[n]; \ +- __u8 iSelector; \ +-} __attribute__ ((packed)) +- +-struct uvc_processing_unit_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bUnitID; +- __u8 bSourceID; +- __u16 wMaxMultiplier; +- __u8 bControlSize; +- __u8 bmControls[2]; +- __u8 iProcessing; +-} __attribute__((__packed__)); +- +-struct uvc_extension_unit_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bUnitID; +- __u8 guidExtensionCode[16]; +- __u8 bNumControls; +- __u8 bNrInPins; +- __u8 baSourceID[0]; +- __u8 bControlSize; +- __u8 bmControls[0]; +- __u8 iExtension; +-} __attribute__((__packed__)); +- +-#define UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \ +- uvc_extension_unit_descriptor_##p_##n +- +-#define DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \ +-struct UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) { \ +- __u8 bLength; \ +- __u8 bDescriptorType; \ +- __u8 bDescriptorSubType; \ +- __u8 bUnitID; \ +- __u8 guidExtensionCode[16]; \ +- __u8 bNumControls; \ +- __u8 bNrInPins; \ +- __u8 baSourceID[p]; \ +- __u8 bControlSize; \ +- __u8 bmControls[n]; \ +- __u8 iExtension; \ +-} __attribute__ ((packed)) +- +-struct uvc_control_endpoint_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u16 wMaxTransferSize; +-} __attribute__((__packed__)); +- +-#define UVC_DT_HEADER 1 +-#define UVC_DT_INPUT_TERMINAL 2 +-#define UVC_DT_OUTPUT_TERMINAL 3 +-#define UVC_DT_SELECTOR_UNIT 4 +-#define UVC_DT_PROCESSING_UNIT 5 +-#define UVC_DT_EXTENSION_UNIT 6 +- +-#define UVC_DT_HEADER_SIZE(n) (12+(n)) +-#define UVC_DT_INPUT_TERMINAL_SIZE 8 +-#define UVC_DT_OUTPUT_TERMINAL_SIZE 9 +-#define UVC_DT_CAMERA_TERMINAL_SIZE(n) (15+(n)) +-#define UVC_DT_SELECTOR_UNIT_SIZE(n) (6+(n)) +-#define UVC_DT_PROCESSING_UNIT_SIZE(n) (9+(n)) +-#define UVC_DT_EXTENSION_UNIT_SIZE(p,n) (24+(p)+(n)) +-#define UVC_DT_CONTROL_ENDPOINT_SIZE 5 +- +-struct uvc_input_header_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bNumFormats; +- __u16 wTotalLength; +- __u8 bEndpointAddress; +- __u8 bmInfo; +- __u8 bTerminalLink; +- __u8 bStillCaptureMethod; +- __u8 bTriggerSupport; +- __u8 bTriggerUsage; +- __u8 bControlSize; +- __u8 bmaControls[]; +-} __attribute__((__packed__)); +- +-#define UVC_INPUT_HEADER_DESCRIPTOR(n, p) \ +- uvc_input_header_descriptor_##n_##p +- +-#define DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(n, p) \ +-struct UVC_INPUT_HEADER_DESCRIPTOR(n, p) { \ +- __u8 bLength; \ +- __u8 bDescriptorType; \ +- __u8 bDescriptorSubType; \ +- __u8 bNumFormats; \ +- __u16 wTotalLength; \ +- __u8 bEndpointAddress; \ +- __u8 bmInfo; \ +- __u8 bTerminalLink; \ +- __u8 bStillCaptureMethod; \ +- __u8 bTriggerSupport; \ +- __u8 bTriggerUsage; \ +- __u8 bControlSize; \ +- __u8 bmaControls[p][n]; \ +-} __attribute__ ((packed)) +- +-struct uvc_output_header_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bNumFormats; +- __u16 wTotalLength; +- __u8 bEndpointAddress; +- __u8 bTerminalLink; +- __u8 bControlSize; +- __u8 bmaControls[]; +-} __attribute__((__packed__)); +- +-#define UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \ +- uvc_output_header_descriptor_##n_##p +- +-#define DECLARE_UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \ +-struct UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) { \ +- __u8 bLength; \ +- __u8 bDescriptorType; \ +- __u8 bDescriptorSubType; \ +- __u8 bNumFormats; \ +- __u16 wTotalLength; \ +- __u8 bEndpointAddress; \ +- __u8 bTerminalLink; \ +- __u8 bControlSize; \ +- __u8 bmaControls[p][n]; \ +-} __attribute__ ((packed)) +- +-struct uvc_format_uncompressed { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bFormatIndex; +- __u8 bNumFrameDescriptors; +- __u8 guidFormat[16]; +- __u8 bBitsPerPixel; +- __u8 bDefaultFrameIndex; +- __u8 bAspectRatioX; +- __u8 bAspectRatioY; +- __u8 bmInterfaceFlags; +- __u8 bCopyProtect; +-} __attribute__((__packed__)); +- +-struct uvc_frame_uncompressed { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bFrameIndex; +- __u8 bmCapabilities; +- __u16 wWidth; +- __u16 wHeight; +- __u32 dwMinBitRate; +- __u32 dwMaxBitRate; +- __u32 dwMaxVideoFrameBufferSize; +- __u32 dwDefaultFrameInterval; +- __u8 bFrameIntervalType; +- __u32 dwFrameInterval[]; +-} __attribute__((__packed__)); +- +-#define UVC_FRAME_UNCOMPRESSED(n) \ +- uvc_frame_uncompressed_##n +- +-#define DECLARE_UVC_FRAME_UNCOMPRESSED(n) \ +-struct UVC_FRAME_UNCOMPRESSED(n) { \ +- __u8 bLength; \ +- __u8 bDescriptorType; \ +- __u8 bDescriptorSubType; \ +- __u8 bFrameIndex; \ +- __u8 bmCapabilities; \ +- __u16 wWidth; \ +- __u16 wHeight; \ +- __u32 dwMinBitRate; \ +- __u32 dwMaxBitRate; \ +- __u32 dwMaxVideoFrameBufferSize; \ +- __u32 dwDefaultFrameInterval; \ +- __u8 bFrameIntervalType; \ +- __u32 dwFrameInterval[n]; \ +-} __attribute__ ((packed)) +- +-struct uvc_format_mjpeg { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bFormatIndex; +- __u8 bNumFrameDescriptors; +- __u8 bmFlags; +- __u8 bDefaultFrameIndex; +- __u8 bAspectRatioX; +- __u8 bAspectRatioY; +- __u8 bmInterfaceFlags; +- __u8 bCopyProtect; +-} __attribute__((__packed__)); +- +-struct uvc_frame_mjpeg { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bFrameIndex; +- __u8 bmCapabilities; +- __u16 wWidth; +- __u16 wHeight; +- __u32 dwMinBitRate; +- __u32 dwMaxBitRate; +- __u32 dwMaxVideoFrameBufferSize; +- __u32 dwDefaultFrameInterval; +- __u8 bFrameIntervalType; +- __u32 dwFrameInterval[]; +-} __attribute__((__packed__)); +- +-#define UVC_FRAME_MJPEG(n) \ +- uvc_frame_mjpeg_##n +- +-#define DECLARE_UVC_FRAME_MJPEG(n) \ +-struct UVC_FRAME_MJPEG(n) { \ +- __u8 bLength; \ +- __u8 bDescriptorType; \ +- __u8 bDescriptorSubType; \ +- __u8 bFrameIndex; \ +- __u8 bmCapabilities; \ +- __u16 wWidth; \ +- __u16 wHeight; \ +- __u32 dwMinBitRate; \ +- __u32 dwMaxBitRate; \ +- __u32 dwMaxVideoFrameBufferSize; \ +- __u32 dwDefaultFrameInterval; \ +- __u8 bFrameIntervalType; \ +- __u32 dwFrameInterval[n]; \ +-} __attribute__ ((packed)) +- +-struct uvc_color_matching_descriptor { +- __u8 bLength; +- __u8 bDescriptorType; +- __u8 bDescriptorSubType; +- __u8 bColorPrimaries; +- __u8 bTransferCharacteristics; +- __u8 bMatrixCoefficients; +-} __attribute__((__packed__)); +- +-#define UVC_DT_INPUT_HEADER 1 +-#define UVC_DT_OUTPUT_HEADER 2 +-#define UVC_DT_FORMAT_UNCOMPRESSED 4 +-#define UVC_DT_FRAME_UNCOMPRESSED 5 +-#define UVC_DT_FORMAT_MJPEG 6 +-#define UVC_DT_FRAME_MJPEG 7 +-#define UVC_DT_COLOR_MATCHING 13 +- +-#define UVC_DT_INPUT_HEADER_SIZE(n, p) (13+(n*p)) +-#define UVC_DT_OUTPUT_HEADER_SIZE(n, p) (9+(n*p)) +-#define UVC_DT_FORMAT_UNCOMPRESSED_SIZE 27 +-#define UVC_DT_FRAME_UNCOMPRESSED_SIZE(n) (26+4*(n)) +-#define UVC_DT_FORMAT_MJPEG_SIZE 11 +-#define UVC_DT_FRAME_MJPEG_SIZE(n) (26+4*(n)) +-#define UVC_DT_COLOR_MATCHING_SIZE 6 ++#include <linux/usb/video.h> + + extern int uvc_bind_config(struct usb_configuration *c, + const struct uvc_descriptor_header * const *control, +--- a/drivers/usb/gadget/uvc.h ++++ b/drivers/usb/gadget/uvc.h +@@ -48,39 +48,6 @@ struct uvc_event + #define UVC_INTF_STREAMING 1 + + /* ------------------------------------------------------------------------ +- * UVC constants & structures +- */ +- +-/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +-#define UVC_STREAM_EOH (1 << 7) +-#define UVC_STREAM_ERR (1 << 6) +-#define UVC_STREAM_STI (1 << 5) +-#define UVC_STREAM_RES (1 << 4) +-#define UVC_STREAM_SCR (1 << 3) +-#define UVC_STREAM_PTS (1 << 2) +-#define UVC_STREAM_EOF (1 << 1) +-#define UVC_STREAM_FID (1 << 0) +- +-struct uvc_streaming_control { +- __u16 bmHint; +- __u8 bFormatIndex; +- __u8 bFrameIndex; +- __u32 dwFrameInterval; +- __u16 wKeyFrameRate; +- __u16 wPFrameRate; +- __u16 wCompQuality; +- __u16 wCompWindowSize; +- __u16 wDelay; +- __u32 dwMaxVideoFrameSize; +- __u32 dwMaxPayloadTransferSize; +- __u32 dwClockFrequency; +- __u8 bmFramingInfo; +- __u8 bPreferedVersion; +- __u8 bMinVersion; +- __u8 bMaxVersion; +-} __attribute__((__packed__)); +- +-/* ------------------------------------------------------------------------ + * Debugging, printing and logging + */ + +@@ -137,9 +104,6 @@ extern unsigned int uvc_trace_param; + #define UVC_MAX_REQUEST_SIZE 64 + #define UVC_MAX_EVENTS 4 + +-#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8 +-#define USB_CLASS_MISC 0xef +- + /* ------------------------------------------------------------------------ + * Structures + */ +--- a/drivers/usb/gadget/webcam.c ++++ b/drivers/usb/gadget/webcam.c +@@ -90,7 +90,7 @@ DECLARE_UVC_HEADER_DESCRIPTOR(1); + static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { + .bLength = UVC_DT_HEADER_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_HEADER, ++ .bDescriptorSubType = UVC_VC_HEADER, + .bcdUVC = cpu_to_le16(0x0100), + .wTotalLength = 0, /* dynamic */ + .dwClockFrequency = cpu_to_le32(48000000), +@@ -101,7 +101,7 @@ static const struct UVC_HEADER_DESCRIPTO + static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = { + .bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3), + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_INPUT_TERMINAL, ++ .bDescriptorSubType = UVC_VC_INPUT_TERMINAL, + .bTerminalID = 1, + .wTerminalType = cpu_to_le16(0x0201), + .bAssocTerminal = 0, +@@ -118,7 +118,7 @@ static const struct uvc_camera_terminal_ + static const struct uvc_processing_unit_descriptor uvc_processing = { + .bLength = UVC_DT_PROCESSING_UNIT_SIZE(2), + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_PROCESSING_UNIT, ++ .bDescriptorSubType = UVC_VC_PROCESSING_UNIT, + .bUnitID = 2, + .bSourceID = 1, + .wMaxMultiplier = cpu_to_le16(16*1024), +@@ -131,7 +131,7 @@ static const struct uvc_processing_unit_ + static const struct uvc_output_terminal_descriptor uvc_output_terminal = { + .bLength = UVC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_OUTPUT_TERMINAL, ++ .bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL, + .bTerminalID = 3, + .wTerminalType = cpu_to_le16(0x0101), + .bAssocTerminal = 0, +@@ -144,7 +144,7 @@ DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2 + static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = { + .bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2), + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_INPUT_HEADER, ++ .bDescriptorSubType = UVC_VS_INPUT_HEADER, + .bNumFormats = 2, + .wTotalLength = 0, /* dynamic */ + .bEndpointAddress = 0, /* dynamic */ +@@ -161,7 +161,7 @@ static const struct UVC_INPUT_HEADER_DES + static const struct uvc_format_uncompressed uvc_format_yuv = { + .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_FORMAT_UNCOMPRESSED, ++ .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, + .bFormatIndex = 1, + .bNumFrameDescriptors = 2, + .guidFormat = +@@ -181,7 +181,7 @@ DECLARE_UVC_FRAME_UNCOMPRESSED(3); + static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { + .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_FRAME_UNCOMPRESSED, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, + .bFrameIndex = 1, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(640), +@@ -199,7 +199,7 @@ static const struct UVC_FRAME_UNCOMPRESS + static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { + .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_FRAME_UNCOMPRESSED, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, + .bFrameIndex = 2, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(1280), +@@ -215,7 +215,7 @@ static const struct UVC_FRAME_UNCOMPRESS + static const struct uvc_format_mjpeg uvc_format_mjpg = { + .bLength = UVC_DT_FORMAT_MJPEG_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_FORMAT_MJPEG, ++ .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, + .bFormatIndex = 2, + .bNumFrameDescriptors = 2, + .bmFlags = 0, +@@ -232,7 +232,7 @@ DECLARE_UVC_FRAME_MJPEG(3); + static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = { + .bLength = UVC_DT_FRAME_MJPEG_SIZE(3), + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_FRAME_MJPEG, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, + .bFrameIndex = 1, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(640), +@@ -250,7 +250,7 @@ static const struct UVC_FRAME_MJPEG(3) u + static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { + .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_FRAME_MJPEG, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, + .bFrameIndex = 2, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(1280), +@@ -266,7 +266,7 @@ static const struct UVC_FRAME_MJPEG(1) u + static const struct uvc_color_matching_descriptor uvc_color_matching = { + .bLength = UVC_DT_COLOR_MATCHING_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubType = UVC_DT_COLOR_MATCHING, ++ .bDescriptorSubType = UVC_VS_COLORFORMAT, + .bColorPrimaries = 1, + .bTransferCharacteristics = 1, + .bMatrixCoefficients = 4, +--- a/include/linux/usb/video.h ++++ b/include/linux/usb/video.h +@@ -160,5 +160,402 @@ + #define UVC_STATUS_TYPE_CONTROL 1 + #define UVC_STATUS_TYPE_STREAMING 2 + ++/* 2.4.3.3. Payload Header Information */ ++#define UVC_STREAM_EOH (1 << 7) ++#define UVC_STREAM_ERR (1 << 6) ++#define UVC_STREAM_STI (1 << 5) ++#define UVC_STREAM_RES (1 << 4) ++#define UVC_STREAM_SCR (1 << 3) ++#define UVC_STREAM_PTS (1 << 2) ++#define UVC_STREAM_EOF (1 << 1) ++#define UVC_STREAM_FID (1 << 0) ++ ++/* ------------------------------------------------------------------------ ++ * UVC structures ++ */ ++ ++/* All UVC descriptors have these 3 fields at the beginning */ ++struct uvc_descriptor_header { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++} __attribute__((packed)); ++ ++/* 3.7.2. Video Control Interface Header Descriptor */ ++struct uvc_header_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u16 bcdUVC; ++ __u16 wTotalLength; ++ __u32 dwClockFrequency; ++ __u8 bInCollection; ++ __u8 baInterfaceNr[]; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_HEADER_SIZE(n) (12+(n)) ++ ++#define UVC_HEADER_DESCRIPTOR(n) \ ++ uvc_header_descriptor_##n ++ ++#define DECLARE_UVC_HEADER_DESCRIPTOR(n) \ ++struct UVC_HEADER_DESCRIPTOR(n) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u16 bcdUVC; \ ++ __u16 wTotalLength; \ ++ __u32 dwClockFrequency; \ ++ __u8 bInCollection; \ ++ __u8 baInterfaceNr[n]; \ ++} __attribute__ ((packed)) ++ ++/* 3.7.2.1. Input Terminal Descriptor */ ++struct uvc_input_terminal_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bTerminalID; ++ __u16 wTerminalType; ++ __u8 bAssocTerminal; ++ __u8 iTerminal; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_INPUT_TERMINAL_SIZE 8 ++ ++/* 3.7.2.2. Output Terminal Descriptor */ ++struct uvc_output_terminal_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bTerminalID; ++ __u16 wTerminalType; ++ __u8 bAssocTerminal; ++ __u8 bSourceID; ++ __u8 iTerminal; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_OUTPUT_TERMINAL_SIZE 9 ++ ++/* 3.7.2.3. Camera Terminal Descriptor */ ++struct uvc_camera_terminal_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bTerminalID; ++ __u16 wTerminalType; ++ __u8 bAssocTerminal; ++ __u8 iTerminal; ++ __u16 wObjectiveFocalLengthMin; ++ __u16 wObjectiveFocalLengthMax; ++ __u16 wOcularFocalLength; ++ __u8 bControlSize; ++ __u8 bmControls[3]; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_CAMERA_TERMINAL_SIZE(n) (15+(n)) ++ ++/* 3.7.2.4. Selector Unit Descriptor */ ++struct uvc_selector_unit_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bUnitID; ++ __u8 bNrInPins; ++ __u8 baSourceID[0]; ++ __u8 iSelector; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_SELECTOR_UNIT_SIZE(n) (6+(n)) ++ ++#define UVC_SELECTOR_UNIT_DESCRIPTOR(n) \ ++ uvc_selector_unit_descriptor_##n ++ ++#define DECLARE_UVC_SELECTOR_UNIT_DESCRIPTOR(n) \ ++struct UVC_SELECTOR_UNIT_DESCRIPTOR(n) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u8 bUnitID; \ ++ __u8 bNrInPins; \ ++ __u8 baSourceID[n]; \ ++ __u8 iSelector; \ ++} __attribute__ ((packed)) ++ ++/* 3.7.2.5. Processing Unit Descriptor */ ++struct uvc_processing_unit_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bUnitID; ++ __u8 bSourceID; ++ __u16 wMaxMultiplier; ++ __u8 bControlSize; ++ __u8 bmControls[2]; ++ __u8 iProcessing; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_PROCESSING_UNIT_SIZE(n) (9+(n)) ++ ++/* 3.7.2.6. Extension Unit Descriptor */ ++struct uvc_extension_unit_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bUnitID; ++ __u8 guidExtensionCode[16]; ++ __u8 bNumControls; ++ __u8 bNrInPins; ++ __u8 baSourceID[0]; ++ __u8 bControlSize; ++ __u8 bmControls[0]; ++ __u8 iExtension; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_EXTENSION_UNIT_SIZE(p, n) (24+(p)+(n)) ++ ++#define UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \ ++ uvc_extension_unit_descriptor_##p_##n ++ ++#define DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \ ++struct UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u8 bUnitID; \ ++ __u8 guidExtensionCode[16]; \ ++ __u8 bNumControls; \ ++ __u8 bNrInPins; \ ++ __u8 baSourceID[p]; \ ++ __u8 bControlSize; \ ++ __u8 bmControls[n]; \ ++ __u8 iExtension; \ ++} __attribute__ ((packed)) ++ ++/* 3.8.2.2. Video Control Interrupt Endpoint Descriptor */ ++struct uvc_control_endpoint_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u16 wMaxTransferSize; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_CONTROL_ENDPOINT_SIZE 5 ++ ++/* 3.9.2.1. Input Header Descriptor */ ++struct uvc_input_header_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bNumFormats; ++ __u16 wTotalLength; ++ __u8 bEndpointAddress; ++ __u8 bmInfo; ++ __u8 bTerminalLink; ++ __u8 bStillCaptureMethod; ++ __u8 bTriggerSupport; ++ __u8 bTriggerUsage; ++ __u8 bControlSize; ++ __u8 bmaControls[]; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_INPUT_HEADER_SIZE(n, p) (13+(n*p)) ++ ++#define UVC_INPUT_HEADER_DESCRIPTOR(n, p) \ ++ uvc_input_header_descriptor_##n_##p ++ ++#define DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(n, p) \ ++struct UVC_INPUT_HEADER_DESCRIPTOR(n, p) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u8 bNumFormats; \ ++ __u16 wTotalLength; \ ++ __u8 bEndpointAddress; \ ++ __u8 bmInfo; \ ++ __u8 bTerminalLink; \ ++ __u8 bStillCaptureMethod; \ ++ __u8 bTriggerSupport; \ ++ __u8 bTriggerUsage; \ ++ __u8 bControlSize; \ ++ __u8 bmaControls[p][n]; \ ++} __attribute__ ((packed)) ++ ++/* 3.9.2.2. Output Header Descriptor */ ++struct uvc_output_header_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bNumFormats; ++ __u16 wTotalLength; ++ __u8 bEndpointAddress; ++ __u8 bTerminalLink; ++ __u8 bControlSize; ++ __u8 bmaControls[]; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_OUTPUT_HEADER_SIZE(n, p) (9+(n*p)) ++ ++#define UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \ ++ uvc_output_header_descriptor_##n_##p ++ ++#define DECLARE_UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \ ++struct UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u8 bNumFormats; \ ++ __u16 wTotalLength; \ ++ __u8 bEndpointAddress; \ ++ __u8 bTerminalLink; \ ++ __u8 bControlSize; \ ++ __u8 bmaControls[p][n]; \ ++} __attribute__ ((packed)) ++ ++/* 3.9.2.6. Color matching descriptor */ ++struct uvc_color_matching_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bColorPrimaries; ++ __u8 bTransferCharacteristics; ++ __u8 bMatrixCoefficients; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_COLOR_MATCHING_SIZE 6 ++ ++/* 4.3.1.1. Video Probe and Commit Controls */ ++struct uvc_streaming_control { ++ __u16 bmHint; ++ __u8 bFormatIndex; ++ __u8 bFrameIndex; ++ __u32 dwFrameInterval; ++ __u16 wKeyFrameRate; ++ __u16 wPFrameRate; ++ __u16 wCompQuality; ++ __u16 wCompWindowSize; ++ __u16 wDelay; ++ __u32 dwMaxVideoFrameSize; ++ __u32 dwMaxPayloadTransferSize; ++ __u32 dwClockFrequency; ++ __u8 bmFramingInfo; ++ __u8 bPreferedVersion; ++ __u8 bMinVersion; ++ __u8 bMaxVersion; ++} __attribute__((__packed__)); ++ ++/* Uncompressed Payload - 3.1.1. Uncompressed Video Format Descriptor */ ++struct uvc_format_uncompressed { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bFormatIndex; ++ __u8 bNumFrameDescriptors; ++ __u8 guidFormat[16]; ++ __u8 bBitsPerPixel; ++ __u8 bDefaultFrameIndex; ++ __u8 bAspectRatioX; ++ __u8 bAspectRatioY; ++ __u8 bmInterfaceFlags; ++ __u8 bCopyProtect; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_FORMAT_UNCOMPRESSED_SIZE 27 ++ ++/* Uncompressed Payload - 3.1.2. Uncompressed Video Frame Descriptor */ ++struct uvc_frame_uncompressed { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bFrameIndex; ++ __u8 bmCapabilities; ++ __u16 wWidth; ++ __u16 wHeight; ++ __u32 dwMinBitRate; ++ __u32 dwMaxBitRate; ++ __u32 dwMaxVideoFrameBufferSize; ++ __u32 dwDefaultFrameInterval; ++ __u8 bFrameIntervalType; ++ __u32 dwFrameInterval[]; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_FRAME_UNCOMPRESSED_SIZE(n) (26+4*(n)) ++ ++#define UVC_FRAME_UNCOMPRESSED(n) \ ++ uvc_frame_uncompressed_##n ++ ++#define DECLARE_UVC_FRAME_UNCOMPRESSED(n) \ ++struct UVC_FRAME_UNCOMPRESSED(n) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u8 bFrameIndex; \ ++ __u8 bmCapabilities; \ ++ __u16 wWidth; \ ++ __u16 wHeight; \ ++ __u32 dwMinBitRate; \ ++ __u32 dwMaxBitRate; \ ++ __u32 dwMaxVideoFrameBufferSize; \ ++ __u32 dwDefaultFrameInterval; \ ++ __u8 bFrameIntervalType; \ ++ __u32 dwFrameInterval[n]; \ ++} __attribute__ ((packed)) ++ ++/* MJPEG Payload - 3.1.1. MJPEG Video Format Descriptor */ ++struct uvc_format_mjpeg { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bFormatIndex; ++ __u8 bNumFrameDescriptors; ++ __u8 bmFlags; ++ __u8 bDefaultFrameIndex; ++ __u8 bAspectRatioX; ++ __u8 bAspectRatioY; ++ __u8 bmInterfaceFlags; ++ __u8 bCopyProtect; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_FORMAT_MJPEG_SIZE 11 ++ ++/* MJPEG Payload - 3.1.2. MJPEG Video Frame Descriptor */ ++struct uvc_frame_mjpeg { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bFrameIndex; ++ __u8 bmCapabilities; ++ __u16 wWidth; ++ __u16 wHeight; ++ __u32 dwMinBitRate; ++ __u32 dwMaxBitRate; ++ __u32 dwMaxVideoFrameBufferSize; ++ __u32 dwDefaultFrameInterval; ++ __u8 bFrameIntervalType; ++ __u32 dwFrameInterval[]; ++} __attribute__((__packed__)); ++ ++#define UVC_DT_FRAME_MJPEG_SIZE(n) (26+4*(n)) ++ ++#define UVC_FRAME_MJPEG(n) \ ++ uvc_frame_mjpeg_##n ++ ++#define DECLARE_UVC_FRAME_MJPEG(n) \ ++struct UVC_FRAME_MJPEG(n) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u8 bFrameIndex; \ ++ __u8 bmCapabilities; \ ++ __u16 wWidth; \ ++ __u16 wHeight; \ ++ __u32 dwMinBitRate; \ ++ __u32 dwMaxBitRate; \ ++ __u32 dwMaxVideoFrameBufferSize; \ ++ __u32 dwDefaultFrameInterval; \ ++ __u8 bFrameIntervalType; \ ++ __u32 dwFrameInterval[n]; \ ++} __attribute__ ((packed)) ++ + #endif /* __LINUX_USB_VIDEO_H */ + |
