From 594ae88fa39586012229a6f2d9f57dbe9323e869 Mon Sep 17 00:00:00 2001 From: Roland Koebler Date: Mon, 5 Oct 2009 19:58:56 +0200 Subject: USB: serial: ftdi_sio: add space/mark parity Add mark and space parity, since the device supports it. Signed-off-by: Roland Koebler Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ebcc6d0e2e91..a8103e0347ee 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2195,15 +2195,21 @@ static void ftdi_set_termios(struct tty_struct *tty, /* Set number of data bits, parity, stop bits */ - termios->c_cflag &= ~CMSPAR; - urb_value = 0; urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 : FTDI_SIO_SET_DATA_STOP_BITS_1); - urb_value |= (cflag & PARENB ? - (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD : - FTDI_SIO_SET_DATA_PARITY_EVEN) : - FTDI_SIO_SET_DATA_PARITY_NONE); + if (cflag & PARENB) { + if (cflag & CMSPAR) + urb_value |= cflag & PARODD ? + FTDI_SIO_SET_DATA_PARITY_MARK : + FTDI_SIO_SET_DATA_PARITY_SPACE; + else + urb_value |= cflag & PARODD ? + FTDI_SIO_SET_DATA_PARITY_ODD : + FTDI_SIO_SET_DATA_PARITY_EVEN; + } else { + urb_value |= FTDI_SIO_SET_DATA_PARITY_NONE; + } if (cflag & CSIZE) { switch (cflag & CSIZE) { case CS5: urb_value |= 5; dbg("Setting CS5"); break; -- cgit v1.2.3 From c207fa10a5e44d9a0de0505d9f7c50fba3bf24fb Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Wed, 23 Sep 2009 18:32:44 -0500 Subject: USB: ehci-hub: Remove redundant ehci->debug check No need to check ehci->debug twice. Found-by: Sergei Shtylyov sshtylyov@ru.mvista.com Signed-off-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 1b6f1c0e5cee..2c6571c05f35 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -236,7 +236,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) } if (unlikely(ehci->debug)) { - if (ehci->debug && !dbgp_reset_prep()) + if (!dbgp_reset_prep()) ehci->debug = NULL; else dbgp_external_startup(); -- cgit v1.2.3 From abc101091dbe7fb1db9b5cfc2c07b42fc10e22a9 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 24 Sep 2009 16:18:27 -0600 Subject: USB: Convert a dev_info to a dev_dbg Knowing which configuration was chosen is a debugging aid more than it is informational. Signed-off-by: Matthew Wilcox Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 05e6d313961e..bdf87a8414a1 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -139,7 +139,7 @@ int usb_choose_configuration(struct usb_device *udev) if (best) { i = best->desc.bConfigurationValue; - dev_info(&udev->dev, + dev_dbg(&udev->dev, "configuration #%d chosen from %d choice%s\n", i, num_configs, plural(num_configs)); } else { -- cgit v1.2.3 From 76d49dcad6a5987547cbf09a717f605ad5a7df61 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 24 Sep 2009 16:19:11 -0600 Subject: USB: usb-storage: Associate the name of the interface with the scsi host Instead of reporting "SCSI emulation for USB Mass Storage devices", report "usb-storage 1-4:1.0". Signed-off-by: Matthew Wilcox Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 3 ++- drivers/usb/storage/usb.c | 2 ++ drivers/usb/storage/usb.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index cfa26d56ce60..e5e6df39e737 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -73,7 +73,8 @@ static const char* host_info(struct Scsi_Host *host) { - return "SCSI emulation for USB Mass Storage devices"; + struct us_data *us = host_to_us(host); + return us->scsi_name; } static int slave_alloc (struct scsi_device *sdev) diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 8060b85fe1a3..783548485868 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -929,6 +929,8 @@ int usb_stor_probe2(struct us_data *us) result = usb_stor_acquire_resources(us); if (result) goto BadDevice; + snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s", + dev_name(&us->pusb_intf->dev)); result = scsi_add_host(us_to_host(us), &us->pusb_intf->dev); if (result) { printk(KERN_WARNING USB_STORAGE diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 2609efb2bd7e..69717134231b 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -132,6 +132,7 @@ struct us_data { /* SCSI interfaces */ struct scsi_cmnd *srb; /* current srb */ unsigned int tag; /* current dCBWTag */ + char scsi_name[32]; /* scsi_host name */ /* control and bulk communications data */ struct urb *current_urb; /* USB requests */ -- cgit v1.2.3 From 07e23f3090d97dc5e4086bdbcf7f2a98a88208fd Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 24 Sep 2009 16:19:49 -0600 Subject: USB Storage: Make driver less chatty when it finds a new device Use dev_dbg() instead of an unconditional printk(KERN_DEBUG). This has two benefits; one is that it identifies the USB device which the messages related to, and the other is that the messages won't be produced unless debug is turned on. Enable the debug messages when CONFIG_USB_STORAGE_DEBUG is set. Signed-off-by: Matthew Wilcox Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 783548485868..01e43a13a6bf 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -45,6 +45,10 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef CONFIG_USB_STORAGE_DEBUG +#define DEBUG +#endif + #include #include #include @@ -808,14 +812,13 @@ static int usb_stor_scan_thread(void * __us) { struct us_data *us = (struct us_data *)__us; - printk(KERN_DEBUG - "usb-storage: device found at %d\n", us->pusb_dev->devnum); + dev_dbg(&us->pusb_intf->dev, "device found\n"); set_freezable(); /* Wait for the timeout to expire or for a disconnect */ if (delay_use > 0) { - printk(KERN_DEBUG "usb-storage: waiting for device " - "to settle before scanning\n"); + dev_dbg(&us->pusb_intf->dev, "waiting for device to settle " + "before scanning\n"); wait_event_freezable_timeout(us->delay_wait, test_bit(US_FLIDX_DONT_SCAN, &us->dflags), delay_use * HZ); @@ -832,7 +835,7 @@ static int usb_stor_scan_thread(void * __us) mutex_unlock(&us->dev_mutex); } scsi_scan_host(us_to_host(us)); - printk(KERN_DEBUG "usb-storage: device scan complete\n"); + dev_dbg(&us->pusb_intf->dev, "scan complete\n"); /* Should we unbind if no devices were detected? */ } -- cgit v1.2.3 From fc25268228a929cf19cc9a39d00b103a700a9db1 Mon Sep 17 00:00:00 2001 From: Julie Zhu Date: Mon, 21 Sep 2009 16:08:19 -0600 Subject: USB: Add support for Xilinx USB host controller Add bus glue driver for Xilinx USB host controller. The controller can be configured as HS only or HS/FS hybrid. The driver uses the device tree file to configure the driver according to the setting in the hardware system. This driver has been tested with usbtest using the NET2280 PCI card. Signed-off-by: Julie Zhu Signed-off-by: John Linn Acked-by: Grant Likely Signed-off-by: Greg Kroah-Hartman --- Documentation/powerpc/dts-bindings/xilinx.txt | 11 + drivers/usb/host/Kconfig | 15 +- drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-xilinx-of.c | 300 ++++++++++++++++++++++++++ 4 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/host/ehci-xilinx-of.c diff --git a/Documentation/powerpc/dts-bindings/xilinx.txt b/Documentation/powerpc/dts-bindings/xilinx.txt index 80339fe4300b..ea68046bb9cb 100644 --- a/Documentation/powerpc/dts-bindings/xilinx.txt +++ b/Documentation/powerpc/dts-bindings/xilinx.txt @@ -292,4 +292,15 @@ - reg-offset : A value of 3 is required - reg-shift : A value of 2 is required + vii) Xilinx USB Host controller + + The Xilinx USB host controller is EHCI compatible but with a different + base address for the EHCI registers, and it is always a big-endian + USB Host controller. The hardware can be configured as high speed only, + or high speed/full speed hybrid. + + Required properties: + - xlnx,support-usb-fs: A value 0 means the core is built as high speed + only. A value 1 means the core also supports + full speed devices. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 9b43b226817f..6d97e039cdbb 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -90,14 +90,25 @@ config USB_EHCI_TT_NEWSCHED config USB_EHCI_BIG_ENDIAN_MMIO bool - depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX) + depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) default y config USB_EHCI_BIG_ENDIAN_DESC bool - depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX) + depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) default y +config XPS_USB_HCD_XILINX + bool "Use Xilinx usb host EHCI controller core" + depends on USB_EHCI_HCD && (PPC32 || MICROBLAZE) + select USB_EHCI_BIG_ENDIAN_DESC + select USB_EHCI_BIG_ENDIAN_MMIO + ---help--- + Xilinx xps USB host controller core is EHCI compilant and has + transaction translator built-in. It can be configured to either + support both high speed and full speed devices, or high speed + devices only. + config USB_EHCI_FSL bool "Support for Freescale on-chip EHCI USB controller" depends on USB_EHCI_HCD && FSL_SOC diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 9835e0713943..57f916cff872 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1118,6 +1118,11 @@ MODULE_LICENSE ("GPL"); #define OF_PLATFORM_DRIVER ehci_hcd_ppc_of_driver #endif +#ifdef CONFIG_XPS_USB_HCD_XILINX +#include "ehci-xilinx-of.c" +#define OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver +#endif + #ifdef CONFIG_PLAT_ORION #include "ehci-orion.c" #define PLATFORM_DRIVER ehci_orion_driver diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c new file mode 100644 index 000000000000..a5861531ad3e --- /dev/null +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -0,0 +1,300 @@ +/* + * EHCI HCD (Host Controller Driver) for USB. + * + * Bus Glue for Xilinx EHCI core on the of_platform bus + * + * Copyright (c) 2009 Xilinx, Inc. + * + * Based on "ehci-ppc-of.c" by Valentine Barshak + * and "ehci-ppc-soc.c" by Stefan Roese + * and "ohci-ppc-of.c" by Sylvain Munaut + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#include +#include + +/** + * ehci_xilinx_of_setup - Initialize the device for ehci_reset() + * @hcd: Pointer to the usb_hcd device to which the host controller bound + * + * called during probe() after chip reset completes. + */ +static int ehci_xilinx_of_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->sbrn = 0x20; + + return ehci_reset(ehci); +} + +/** + * ehci_xilinx_port_handed_over - hand the port out if failed to enable it + * @hcd: Pointer to the usb_hcd device to which the host controller bound + * @portnum:Port number to which the device is attached. + * + * This function is used as a place to tell the user that the Xilinx USB host + * controller does support LS devices. And in an HS only configuration, it + * does not support FS devices either. It is hoped that this can help a + * confused user. + * + * There are cases when the host controller fails to enable the port due to, + * for example, insufficient power that can be supplied to the device from + * the USB bus. In those cases, the messages printed here are not helpful. + */ +static int ehci_xilinx_port_handed_over(struct usb_hcd *hcd, int portnum) +{ + dev_warn(hcd->self.controller, "port %d cannot be enabled\n", portnum); + if (hcd->has_tt) { + dev_warn(hcd->self.controller, + "Maybe you have connected a low speed device?\n"); + + dev_warn(hcd->self.controller, + "We do not support low speed devices\n"); + } else { + dev_warn(hcd->self.controller, + "Maybe your device is not a high speed device?\n"); + dev_warn(hcd->self.controller, + "The USB host controller does not support full speed " + "nor low speed devices\n"); + dev_warn(hcd->self.controller, + "You can reconfigure the host controller to have " + "full speed support\n"); + } + + return 0; +} + + +static const struct hc_driver ehci_xilinx_of_hc_driver = { + .description = hcd_name, + .product_desc = "OF EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_xilinx_of_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = NULL, + .port_handed_over = ehci_xilinx_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +/** + * ehci_hcd_xilinx_of_probe - Probe method for the USB host controller + * @op: pointer to the of_device to which the host controller bound + * @match: pointer to of_device_id structure, not used + * + * This function requests resources and sets up appropriate properties for the + * host controller. Because the Xilinx USB host controller can be configured + * as HS only or HS/FS only, it checks the configuration in the device tree + * entry, and sets an appropriate value for hcd->has_tt. + */ +static int __devinit +ehci_hcd_xilinx_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct device_node *dn = op->node; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct resource res; + int irq; + int rv; + int *value; + + if (usb_disabled()) + return -ENODEV; + + dev_dbg(&op->dev, "initializing XILINX-OF USB Controller\n"); + + rv = of_address_to_resource(dn, 0, &res); + if (rv) + return rv; + + hcd = usb_create_hcd(&ehci_xilinx_of_hc_driver, &op->dev, + "XILINX-OF USB"); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res.start; + hcd->rsrc_len = res.end - res.start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + printk(KERN_ERR __FILE__ ": request_mem_region failed\n"); + rv = -EBUSY; + goto err_rmr; + } + + irq = irq_of_parse_and_map(dn, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n"); + rv = -EBUSY; + goto err_irq; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + printk(KERN_ERR __FILE__ ": ioremap failed\n"); + rv = -ENOMEM; + goto err_ioremap; + } + + ehci = hcd_to_ehci(hcd); + + /* This core always has big-endian register interface and uses + * big-endian memory descriptors. + */ + ehci->big_endian_mmio = 1; + ehci->big_endian_desc = 1; + + /* Check whether the FS support option is selected in the hardware. + */ + value = (int *)of_get_property(dn, "xlnx,support-usb-fs", NULL); + if (value && (*value == 1)) { + ehci_dbg(ehci, "USB host controller supports FS devices\n"); + hcd->has_tt = 1; + } else { + ehci_dbg(ehci, + "USB host controller is HS only\n"); + hcd->has_tt = 0; + } + + /* Debug registers are at the first 0x100 region + */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + rv = usb_add_hcd(hcd, irq, 0); + if (rv == 0) + return 0; + + iounmap(hcd->regs); + +err_ioremap: +err_irq: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_rmr: + usb_put_hcd(hcd); + + return rv; +} + +/** + * ehci_hcd_xilinx_of_remove - shutdown hcd and release resources + * @op: pointer to of_device structure that is to be removed + * + * Remove the hcd structure, and release resources that has been requested + * during probe. + */ +static int ehci_hcd_xilinx_of_remove(struct of_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + dev_set_drvdata(&op->dev, NULL); + + dev_dbg(&op->dev, "stopping XILINX-OF USB Controller\n"); + + usb_remove_hcd(hcd); + + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + usb_put_hcd(hcd); + + return 0; +} + +/** + * ehci_hcd_xilinx_of_shutdown - shutdown the hcd + * @op: pointer to of_device structure that is to be removed + * + * Properly shutdown the hcd, call driver's shutdown routine. + */ +static int ehci_hcd_xilinx_of_shutdown(struct of_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); + + return 0; +} + + +static struct of_device_id ehci_hcd_xilinx_of_match[] = { + {.compatible = "xlnx,xps-usb-host-1.00.a",}, + {}, +}; +MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match); + +static struct of_platform_driver ehci_hcd_xilinx_of_driver = { + .name = "xilinx-of-ehci", + .match_table = ehci_hcd_xilinx_of_match, + .probe = ehci_hcd_xilinx_of_probe, + .remove = ehci_hcd_xilinx_of_remove, + .shutdown = ehci_hcd_xilinx_of_shutdown, + .driver = { + .name = "xilinx-of-ehci", + .owner = THIS_MODULE, + }, +}; -- cgit v1.2.3 From 32b4ecbf12d4bc193007dc466a22e84546e34dd9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 1 Oct 2009 19:03:13 +0200 Subject: USB: Add missing static markers to ohci-pnx4008 I can't see any reason why these would not be static. Signed-off-by: Jean Delvare Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pnx4008.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 100bf3d8437c..2769326da42e 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -98,8 +98,8 @@ #define ISP1301_I2C_INTERRUPT_RISING 0xE #define ISP1301_I2C_REG_CLEAR_ADDR 1 -struct i2c_driver isp1301_driver; -struct i2c_client *isp1301_i2c_client; +static struct i2c_driver isp1301_driver; +static struct i2c_client *isp1301_i2c_client; extern int usb_disabled(void); extern int ocpi_enable(void); @@ -120,12 +120,12 @@ static int isp1301_remove(struct i2c_client *client) return 0; } -const struct i2c_device_id isp1301_id[] = { +static const struct i2c_device_id isp1301_id[] = { { "isp1301_pnx", 0 }, { } }; -struct i2c_driver isp1301_driver = { +static struct i2c_driver isp1301_driver = { .driver = { .name = "isp1301_pnx", }, -- cgit v1.2.3 From 77dc82b0f747d46c74c43f74a8115929d3daa02e Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 24 Aug 2009 14:44:30 +0100 Subject: USB: make urb scatter-gather support more generic The WHCI HCD will also support urbs with scatter-gather lists. Add a usb_bus field to indicated how many sg list elements are supported by the HCD. Use this to decide whether to pass the scatter-list to the HCD or not. Make the usb-storage driver use this new field. Signed-off-by: David Vrabel Cc: Alan Stern Cc: Sarah Sharp Cc: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 8 +------- drivers/usb/host/xhci-pci.c | 2 ++ drivers/usb/storage/usb.c | 10 ++++++++++ include/linux/usb.h | 1 + 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index da718e84d58d..feafd28a0428 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -393,13 +393,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, if (io->entries <= 0) return io->entries; - /* If we're running on an xHCI host controller, queue the whole scatter - * gather list with one call to urb_enqueue(). This is only for bulk, - * as that endpoint type does not care how the data gets broken up - * across frames. - */ - if (usb_pipebulk(pipe) && - bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) { + if (dev->bus->sg_tablesize > 0) { io->urbs = kmalloc(sizeof *io->urbs, mem_flags); use_sg = true; } else { diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 06595ec27bb7..e097008d6fb1 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -54,6 +54,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd) struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval; + hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 1; + xhci->cap_regs = hcd->regs; xhci->op_regs = hcd->regs + HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 01e43a13a6bf..1599d86154c4 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -843,6 +843,15 @@ static int usb_stor_scan_thread(void * __us) complete_and_exit(&us->scanning_done, 0); } +static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + + if (usb_dev->bus->sg_tablesize) { + return usb_dev->bus->sg_tablesize; + } + return SG_ALL; +} /* First part of general USB mass-storage probing */ int usb_stor_probe1(struct us_data **pus, @@ -871,6 +880,7 @@ int usb_stor_probe1(struct us_data **pus, * Allow 16-byte CDBs and thus > 2TB */ host->max_cmd_len = 16; + host->sg_tablesize = usb_stor_sg_tablesize(intf); *pus = us = host_to_us(host); memset(us, 0, sizeof(struct us_data)); mutex_init(&(us->dev_mutex)); diff --git a/include/linux/usb.h b/include/linux/usb.h index a34fa89f1474..6e91ee4f5b81 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -331,6 +331,7 @@ struct usb_bus { u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ + unsigned sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in * round-robin allocation */ -- cgit v1.2.3 From 903008a50d559a0b288440d1e2f25e9f15d081f6 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 24 Aug 2009 15:02:27 +0100 Subject: USB: whci-hcd: support urbs with scatter-gather lists Support urbs with scatter-gather lists by trying to fit sg list elements into page lists in one or more qTDs. qTDs must end on a wMaxPacketSize boundary so if this isn't possible the urb's sg list must be copied into bounce buffers. Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/whci/hcd.c | 1 + drivers/usb/host/whci/qset.c | 348 ++++++++++++++++++++++++++++++++++++---- drivers/usb/host/whci/whcd.h | 9 ++ drivers/usb/host/whci/whci-hc.h | 5 +- 4 files changed, 332 insertions(+), 31 deletions(-) diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index 687b622a1612..e0d3401285c8 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -250,6 +250,7 @@ static int whc_probe(struct umc_dev *umc) } usb_hcd->wireless = 1; + usb_hcd->self.sg_tablesize = 2048; /* somewhat arbitrary */ wusbhc = usb_hcd_to_wusbhc(usb_hcd); whc = wusbhc_to_whc(wusbhc); diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index 1b9dc1571570..88e51ea8620b 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -57,8 +57,9 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) is_out = usb_pipeout(urb->pipe); - epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; + qset->max_packet = le16_to_cpu(urb->ep->desc.wMaxPacketSize); + epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; if (epcd) { qset->max_seq = epcd->bMaxSequence; qset->max_burst = epcd->bMaxBurst; @@ -72,7 +73,7 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN) | usb_pipe_to_qh_type(urb->pipe) | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum)) - | QH_INFO1_MAX_PKT_LEN(usb_maxpacket(urb->dev, urb->pipe, is_out)) + | QH_INFO1_MAX_PKT_LEN(qset->max_packet) ); qset->qh.info2 = cpu_to_le32( QH_INFO2_BURST(qset->max_burst) @@ -241,6 +242,36 @@ static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset) qset->ntds--; } +static void qset_copy_bounce_to_sg(struct whc *whc, struct whc_std *std) +{ + struct scatterlist *sg; + void *bounce; + size_t remaining, offset; + + bounce = std->bounce_buf; + remaining = std->len; + + sg = std->bounce_sg; + offset = std->bounce_offset; + + while (remaining) { + size_t len; + + len = min(sg->length - offset, remaining); + memcpy(sg_virt(sg) + offset, bounce, len); + + bounce += len; + remaining -= len; + + offset += len; + if (offset >= sg->length) { + sg = sg_next(sg); + offset = 0; + } + } + +} + /** * qset_free_std - remove an sTD and free it. * @whc: the WHCI host controller @@ -249,13 +280,29 @@ static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset) void qset_free_std(struct whc *whc, struct whc_std *std) { list_del(&std->list_node); - if (std->num_pointers) { - dma_unmap_single(whc->wusbhc.dev, std->dma_addr, - std->num_pointers * sizeof(struct whc_page_list_entry), - DMA_TO_DEVICE); + if (std->bounce_buf) { + bool is_out = usb_pipeout(std->urb->pipe); + dma_addr_t dma_addr; + + if (std->num_pointers) + dma_addr = le64_to_cpu(std->pl_virt[0].buf_ptr); + else + dma_addr = std->dma_addr; + + dma_unmap_single(whc->wusbhc.dev, dma_addr, + std->len, is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (!is_out) + qset_copy_bounce_to_sg(whc, std); + kfree(std->bounce_buf); + } + if (std->pl_virt) { + if (std->dma_addr) + dma_unmap_single(whc->wusbhc.dev, std->dma_addr, + std->num_pointers * sizeof(struct whc_page_list_entry), + DMA_TO_DEVICE); kfree(std->pl_virt); + std->pl_virt = NULL; } - kfree(std); } @@ -293,12 +340,17 @@ static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_f { dma_addr_t dma_addr = std->dma_addr; dma_addr_t sp, ep; - size_t std_len = std->len; size_t pl_len; int p; - sp = ALIGN(dma_addr, WHCI_PAGE_SIZE); - ep = dma_addr + std_len; + /* Short buffers don't need a page list. */ + if (std->len <= WHCI_PAGE_SIZE) { + std->num_pointers = 0; + return 0; + } + + sp = dma_addr & ~(WHCI_PAGE_SIZE-1); + ep = dma_addr + std->len; std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); @@ -309,7 +361,7 @@ static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_f for (p = 0; p < std->num_pointers; p++) { std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); - dma_addr = ALIGN(dma_addr + WHCI_PAGE_SIZE, WHCI_PAGE_SIZE); + dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); } return 0; @@ -339,6 +391,238 @@ static void urb_dequeue_work(struct work_struct *work) spin_unlock_irqrestore(&whc->lock, flags); } +static struct whc_std *qset_new_std(struct whc *whc, struct whc_qset *qset, + struct urb *urb, gfp_t mem_flags) +{ + struct whc_std *std; + + std = kzalloc(sizeof(struct whc_std), mem_flags); + if (std == NULL) + return NULL; + + std->urb = urb; + std->qtd = NULL; + + INIT_LIST_HEAD(&std->list_node); + list_add_tail(&std->list_node, &qset->stds); + + return std; +} + +static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *urb, + gfp_t mem_flags) +{ + size_t remaining; + struct scatterlist *sg; + int i; + int ntds = 0; + struct whc_std *std = NULL; + struct whc_page_list_entry *entry; + dma_addr_t prev_end = 0; + size_t pl_len; + int p = 0; + + dev_dbg(&whc->umc->dev, "adding urb w/ sg of length %d\n", urb->transfer_buffer_length); + + remaining = urb->transfer_buffer_length; + + for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) { + dma_addr_t dma_addr; + size_t dma_remaining; + dma_addr_t sp, ep; + int num_pointers; + + if (remaining == 0) { + break; + } + + dma_addr = sg_dma_address(sg); + dma_remaining = min(sg_dma_len(sg), remaining); + + dev_dbg(&whc->umc->dev, "adding sg[%d] %08x %d\n", i, (unsigned)dma_addr, + dma_remaining); + + while (dma_remaining) { + size_t dma_len; + + /* + * We can use the previous std (if it exists) provided that: + * - the previous one ended on a page boundary. + * - the current one begins on a page boundary. + * - the previous one isn't full. + * + * If a new std is needed but the previous one + * did not end on a wMaxPacketSize boundary + * then this sg list cannot be mapped onto + * multiple qTDs. Return an error and let the + * caller sort it out. + */ + if (!std + || (prev_end & (WHCI_PAGE_SIZE-1)) + || (dma_addr & (WHCI_PAGE_SIZE-1)) + || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { + if (prev_end % qset->max_packet != 0) + return -EINVAL; + dev_dbg(&whc->umc->dev, "need new std\n"); + std = qset_new_std(whc, qset, urb, mem_flags); + if (std == NULL) { + return -ENOMEM; + } + ntds++; + p = 0; + } + + dma_len = dma_remaining; + + /* + * If the remainder in this element doesn't + * fit in a single qTD, end the qTD on a + * wMaxPacketSize boundary. + */ + if (std->len + dma_len > QTD_MAX_XFER_SIZE) { + dma_len = QTD_MAX_XFER_SIZE - std->len; + ep = ((dma_addr + dma_len) / qset->max_packet) * qset->max_packet; + dma_len = ep - dma_addr; + } + + dev_dbg(&whc->umc->dev, "adding %d\n", dma_len); + + std->len += dma_len; + std->ntds_remaining = -1; /* filled in later */ + + sp = dma_addr & ~(WHCI_PAGE_SIZE-1); + ep = dma_addr + dma_len; + num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); + std->num_pointers += num_pointers; + + dev_dbg(&whc->umc->dev, "need %d more (%d total) page pointers\n", + num_pointers, std->num_pointers); + + pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); + + std->pl_virt = krealloc(std->pl_virt, pl_len, mem_flags); + if (std->pl_virt == NULL) { + return -ENOMEM; + } + + for (;p < std->num_pointers; p++, entry++) { + dev_dbg(&whc->umc->dev, "e[%d] %08x\n", p, dma_addr); + std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); + dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); + } + + prev_end = dma_addr = ep; + dma_remaining -= dma_len; + remaining -= dma_len; + } + } + + dev_dbg(&whc->umc->dev, "used %d tds\n", ntds); + + /* Now the number of stds is know, go back and fill in + std->ntds_remaining. */ + list_for_each_entry(std, &qset->stds, list_node) { + if (std->ntds_remaining == -1) { + pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); + std->ntds_remaining = ntds--; + std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, + pl_len, DMA_TO_DEVICE); + } + } + return 0; +} + +/** + * qset_add_urb_sg_linearize - add an urb with sg list, copying the data + * + * If the URB contains an sg list whose elements cannot be directly + * mapped to qTDs then the data must be transferred via bounce + * buffers. + */ +static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, + struct urb *urb, gfp_t mem_flags) +{ + bool is_out = usb_pipeout(urb->pipe); + size_t max_std_len; + size_t remaining; + int ntds = 0; + struct whc_std *std = NULL; + void *bounce = NULL; + struct scatterlist *sg; + int i; + + /* limit maximum bounce buffer to 16 * 3.5 KiB ~= 28 k */ + max_std_len = qset->max_burst * qset->max_packet; + + remaining = urb->transfer_buffer_length; + + for_each_sg(urb->sg->sg, sg, urb->sg->nents, i) { + size_t len; + size_t sg_remaining; + void *orig; + + if (remaining == 0) { + break; + } + + sg_remaining = min(remaining, sg->length); + orig = sg_virt(sg); + + dev_dbg(&whc->umc->dev, "adding sg[%d] %d\n", i, sg_remaining); + + while (sg_remaining) { + if (!std || std->len == max_std_len) { + dev_dbg(&whc->umc->dev, "need new std\n"); + std = qset_new_std(whc, qset, urb, mem_flags); + if (std == NULL) + return -ENOMEM; + std->bounce_buf = kmalloc(max_std_len, mem_flags); + if (std->bounce_buf == NULL) + return -ENOMEM; + std->bounce_sg = sg; + std->bounce_offset = orig - sg_virt(sg); + bounce = std->bounce_buf; + ntds++; + } + + len = min(sg_remaining, max_std_len - std->len); + + dev_dbg(&whc->umc->dev, "added %d from sg[%d] @ offset %d\n", + len, i, orig - sg_virt(sg)); + + if (is_out) + memcpy(bounce, orig, len); + + std->len += len; + std->ntds_remaining = -1; /* filled in later */ + + bounce += len; + orig += len; + sg_remaining -= len; + remaining -= len; + } + } + + /* + * For each of the new sTDs, map the bounce buffers, create + * page lists (if necessary), and fill in std->ntds_remaining. + */ + list_for_each_entry(std, &qset->stds, list_node) { + if (std->ntds_remaining != -1) + continue; + + std->dma_addr = dma_map_single(&whc->umc->dev, std->bounce_buf, std->len, + is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (qset_fill_page_list(whc, std, mem_flags) < 0) + return -ENOMEM; + + std->ntds_remaining = ntds--; + } + + return 0; +} + /** * qset_add_urb - add an urb to the qset's queue. * @@ -353,10 +637,7 @@ int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb, int remaining = urb->transfer_buffer_length; u64 transfer_dma = urb->transfer_dma; int ntds_remaining; - - ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); - if (ntds_remaining == 0) - ntds_remaining = 1; + int ret; wurb = kzalloc(sizeof(struct whc_urb), mem_flags); if (wurb == NULL) @@ -366,32 +647,41 @@ int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb, wurb->urb = urb; INIT_WORK(&wurb->dequeue_work, urb_dequeue_work); + if (urb->sg) { + ret = qset_add_urb_sg(whc, qset, urb, mem_flags); + if (ret == -EINVAL) { + dev_dbg(&whc->umc->dev, "linearizing %d octet urb\n", + urb->transfer_buffer_length); + qset_free_stds(qset, urb); + ret = qset_add_urb_sg_linearize(whc, qset, urb, mem_flags); + } + if (ret < 0) + goto err_no_mem; + return 0; + } + + ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); + if (ntds_remaining == 0) + ntds_remaining = 1; + while (ntds_remaining) { struct whc_std *std; size_t std_len; - std = kmalloc(sizeof(struct whc_std), mem_flags); - if (std == NULL) - goto err_no_mem; - std_len = remaining; if (std_len > QTD_MAX_XFER_SIZE) std_len = QTD_MAX_XFER_SIZE; - std->urb = urb; + std = qset_new_std(whc, qset, urb, mem_flags); + if (std == NULL) + goto err_no_mem; + std->dma_addr = transfer_dma; std->len = std_len; std->ntds_remaining = ntds_remaining; - std->qtd = NULL; - INIT_LIST_HEAD(&std->list_node); - list_add_tail(&std->list_node, &qset->stds); - - if (std_len > WHCI_PAGE_SIZE) { - if (qset_fill_page_list(whc, std, mem_flags) < 0) - goto err_no_mem; - } else - std->num_pointers = 0; + if (qset_fill_page_list(whc, std, mem_flags) < 0) + goto err_no_mem; ntds_remaining--; remaining -= std_len; diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h index 24e94d983c5e..c80c7d93bc4a 100644 --- a/drivers/usb/host/whci/whcd.h +++ b/drivers/usb/host/whci/whcd.h @@ -84,6 +84,11 @@ struct whc { * @len: the length of data in the associated TD. * @ntds_remaining: number of TDs (starting from this one) in this transfer. * + * @bounce_buf: a bounce buffer if the std was from an urb with a sg + * list that could not be mapped to qTDs directly. + * @bounce_sg: the first scatterlist element bounce_buf is for. + * @bounce_offset: the offset into bounce_sg for the start of bounce_buf. + * * Queued URBs may require more TDs than are available in a qset so we * use a list of these "software TDs" (sTDs) to hold per-TD data. */ @@ -97,6 +102,10 @@ struct whc_std { int num_pointers; dma_addr_t dma_addr; struct whc_page_list_entry *pl_virt; + + void *bounce_buf; + struct scatterlist *bounce_sg; + unsigned bounce_offset; }; /** diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h index e8d0001605be..d5e5c3aacced 100644 --- a/drivers/usb/host/whci/whci-hc.h +++ b/drivers/usb/host/whci/whci-hc.h @@ -267,8 +267,9 @@ struct whc_qset { unsigned reset:1; struct urb *pause_after_urb; struct completion remove_complete; - int max_burst; - int max_seq; + uint16_t max_packet; + uint8_t max_burst; + uint8_t max_seq; }; static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target) -- cgit v1.2.3 From dab5940ae6ba64dee738f15ac0ce14804bf09eaa Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Tue, 18 Aug 2009 16:11:24 +0100 Subject: USB: allow interrupt transfers to WUSB devices Check urb->interval on interrupt transfers and allow those with valid values (6 <= interval <= 16). Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 0885d4abdc62..e7cae1334693 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -429,8 +429,16 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: /* too small? */ - if (urb->interval <= 0) - return -EINVAL; + switch (dev->speed) { + case USB_SPEED_VARIABLE: + if (urb->interval < 6) + return -EINVAL; + break; + default: + if (urb->interval <= 0) + return -EINVAL; + break; + } /* too big? */ switch (dev->speed) { case USB_SPEED_SUPER: /* units are 125us */ @@ -438,6 +446,10 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (urb->interval > (1 << 15)) return -EINVAL; max = 1 << 15; + case USB_SPEED_VARIABLE: + if (urb->interval > 16) + return -EINVAL; + break; case USB_SPEED_HIGH: /* units are microframes */ /* NOTE usb handles 2^15 */ if (urb->interval > (1024 * 8)) @@ -461,8 +473,10 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) default: return -EINVAL; } - /* Round down to a power of 2, no more than max */ - urb->interval = min(max, 1 << ilog2(urb->interval)); + if (dev->speed != USB_SPEED_VARIABLE) { + /* Round down to a power of 2, no more than max */ + urb->interval = min(max, 1 << ilog2(urb->interval)); + } } return usb_hcd_submit_urb(urb, mem_flags); -- cgit v1.2.3 From 544ac059b934362ba530f5e28d642662137f3ea9 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Wed, 14 Oct 2009 13:23:55 +0000 Subject: USB: whci-hcd: fix type and format warnings in sg code Fix type and format warning in the new sg code. Remove the very chatty debug messages that were left in by mistake and use min_t() as required (no one seems to agree on a type for buffer sizes). Reported-by: Randy Dunlap Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/whci/qset.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index 88e51ea8620b..08280869ed1c 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -422,8 +422,6 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u size_t pl_len; int p = 0; - dev_dbg(&whc->umc->dev, "adding urb w/ sg of length %d\n", urb->transfer_buffer_length); - remaining = urb->transfer_buffer_length; for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) { @@ -437,10 +435,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u } dma_addr = sg_dma_address(sg); - dma_remaining = min(sg_dma_len(sg), remaining); - - dev_dbg(&whc->umc->dev, "adding sg[%d] %08x %d\n", i, (unsigned)dma_addr, - dma_remaining); + dma_remaining = min_t(size_t, sg_dma_len(sg), remaining); while (dma_remaining) { size_t dma_len; @@ -463,7 +458,6 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { if (prev_end % qset->max_packet != 0) return -EINVAL; - dev_dbg(&whc->umc->dev, "need new std\n"); std = qset_new_std(whc, qset, urb, mem_flags); if (std == NULL) { return -ENOMEM; @@ -485,8 +479,6 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u dma_len = ep - dma_addr; } - dev_dbg(&whc->umc->dev, "adding %d\n", dma_len); - std->len += dma_len; std->ntds_remaining = -1; /* filled in later */ @@ -495,9 +487,6 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); std->num_pointers += num_pointers; - dev_dbg(&whc->umc->dev, "need %d more (%d total) page pointers\n", - num_pointers, std->num_pointers); - pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); std->pl_virt = krealloc(std->pl_virt, pl_len, mem_flags); @@ -506,7 +495,6 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u } for (;p < std->num_pointers; p++, entry++) { - dev_dbg(&whc->umc->dev, "e[%d] %08x\n", p, dma_addr); std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); } @@ -517,8 +505,6 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u } } - dev_dbg(&whc->umc->dev, "used %d tds\n", ntds); - /* Now the number of stds is know, go back and fill in std->ntds_remaining. */ list_for_each_entry(std, &qset->stds, list_node) { @@ -565,14 +551,11 @@ static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, break; } - sg_remaining = min(remaining, sg->length); + sg_remaining = min_t(size_t, remaining, sg->length); orig = sg_virt(sg); - dev_dbg(&whc->umc->dev, "adding sg[%d] %d\n", i, sg_remaining); - while (sg_remaining) { if (!std || std->len == max_std_len) { - dev_dbg(&whc->umc->dev, "need new std\n"); std = qset_new_std(whc, qset, urb, mem_flags); if (std == NULL) return -ENOMEM; @@ -587,9 +570,6 @@ static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, len = min(sg_remaining, max_std_len - std->len); - dev_dbg(&whc->umc->dev, "added %d from sg[%d] @ offset %d\n", - len, i, orig - sg_virt(sg)); - if (is_out) memcpy(bounce, orig, len); @@ -650,8 +630,6 @@ int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb, if (urb->sg) { ret = qset_add_urb_sg(whc, qset, urb, mem_flags); if (ret == -EINVAL) { - dev_dbg(&whc->umc->dev, "linearizing %d octet urb\n", - urb->transfer_buffer_length); qset_free_stds(qset, urb); ret = qset_add_urb_sg_linearize(whc, qset, urb, mem_flags); } -- cgit v1.2.3 From 0c36457fb6f1a88d36978904c0cbbc952bc76786 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 19 Sep 2009 09:13:43 +0200 Subject: USB: skeleton: Correct use of ! and & Correct priority problem in the use of ! and &. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression E; constant C; @@ - !E & C + !(E & C) // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usb-skeleton.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index b62f2bc064f6..b1e579c5c97c 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -358,7 +358,7 @@ retry: rv = skel_do_read_io(dev, count); if (rv < 0) goto exit; - else if (!file->f_flags & O_NONBLOCK) + else if (!(file->f_flags & O_NONBLOCK)) goto retry; rv = -EAGAIN; } @@ -411,7 +411,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, * limit the number of URBs in flight to stop a user from using up all * RAM */ - if (!file->f_flags & O_NONBLOCK) { + if (!(file->f_flags & O_NONBLOCK)) { if (down_interruptible(&dev->limit_sem)) { retval = -ERESTARTSYS; goto exit; -- cgit v1.2.3 From d4c310899c0f6b972d062d63b2a46cfc71786cbd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Oct 2009 00:03:38 +0200 Subject: USB gadget: Handle endpoint requests at the function level Control requests targeted at an endpoint (that is sent to EP0 but specifying the target endpoint address in wIndex) are dispatched to the current configuration's setup callback, requiring all gadget drivers to dispatch the requests to the correct function driver. To avoid this, record which endpoints are used by each function in the composite driver SET CONFIGURATION handler and dispatch requests targeted at endpoints to the correct function. Signed-off-by: Laurent Pinchart Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 54 +++++++++++++++++++++++++++++++++++------- include/linux/usb/composite.h | 1 + 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index d05397ec8a18..8498f1a114d5 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -373,6 +373,8 @@ static void reset_config(struct usb_composite_dev *cdev) list_for_each_entry(f, &cdev->config->functions, list) { if (f->disable) f->disable(f); + + bitmap_zero(f->endpoints, 32); } cdev->config = NULL; } @@ -418,10 +420,35 @@ static int set_config(struct usb_composite_dev *cdev, /* Initialize all interfaces by setting them to altsetting zero. */ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { struct usb_function *f = c->interface[tmp]; + struct usb_descriptor_header **descriptors; if (!f) break; + /* + * Record which endpoints are used by the function. This is used + * to dispatch control requests targeted at that endpoint to the + * function's setup callback instead of the current + * configuration's setup callback. + */ + if (gadget->speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + + for (; *descriptors; ++descriptors) { + struct usb_endpoint_descriptor *ep; + int addr; + + if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT) + continue; + + ep = (struct usb_endpoint_descriptor *)*descriptors; + addr = ((ep->bEndpointAddress & 0x80) >> 3) + | (ep->bEndpointAddress & 0x0f); + set_bit(addr, f->endpoints); + } + result = f->set_alt(f, tmp, 0); if (result < 0) { DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", @@ -688,6 +715,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; + u8 endp; /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion @@ -800,23 +828,33 @@ unknown: ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); - /* functions always handle their interfaces ... punt other - * recipients (endpoint, other, WUSB, ...) to the current + /* functions always handle their interfaces and endpoints... + * punt other recipients (other, WUSB, ...) to the current * configuration code. * * REVISIT it could make sense to let the composite device * take such requests too, if that's ever needed: to work * in config 0, etc. */ - if ((ctrl->bRequestType & USB_RECIP_MASK) - == USB_RECIP_INTERFACE) { + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: f = cdev->config->interface[intf]; - if (f && f->setup) - value = f->setup(f, ctrl); - else + break; + + case USB_RECIP_ENDPOINT: + endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); + list_for_each_entry(f, &cdev->config->functions, list) { + if (test_bit(endp, f->endpoints)) + break; + } + if (&f->list == &cdev->config->functions) f = NULL; + break; } - if (value < 0 && !f) { + + if (f && f->setup) + value = f->setup(f, ctrl); + else { struct usb_configuration *c; c = cdev->config; diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 4f6bb3d2160e..738ea1a691cb 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -127,6 +127,7 @@ struct usb_function { /* private: */ /* internals */ struct list_head list; + DECLARE_BITMAP(endpoints, 32); }; int usb_add_function(struct usb_configuration *, struct usb_function *); -- cgit v1.2.3 From f4572316f72fdc4f30c9cf0c5598048f85daafd7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Oct 2009 00:03:39 +0200 Subject: USB audio gadget: handle endpoint control requests at the function level Now that control requests targeted at an endpoint can be handled at the function level, move the UAC-specific control request handling code from the audio gadget driver to the audio function driver. Signed-off-by: Laurent Pinchart Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/audio.c | 115 ------------------------------------------- drivers/usb/gadget/f_audio.c | 76 +++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 117 deletions(-) diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index a3a0f4a27ef0..58f220323847 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -89,120 +89,6 @@ static const struct usb_descriptor_header *otg_desc[] = { /*-------------------------------------------------------------------------*/ -/** - * Handle USB audio endpoint set/get command in setup class request - */ - -static int audio_set_endpoint_req(struct usb_configuration *c, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = c->cdev; - int value = -EOPNOTSUPP; - u16 ep = le16_to_cpu(ctrl->wIndex); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); - - switch (ctrl->bRequest) { - case UAC_SET_CUR: - value = 0; - break; - - case UAC_SET_MIN: - break; - - case UAC_SET_MAX: - break; - - case UAC_SET_RES: - break; - - case UAC_SET_MEM: - break; - - default: - break; - } - - return value; -} - -static int audio_get_endpoint_req(struct usb_configuration *c, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = c->cdev; - int value = -EOPNOTSUPP; - u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); - - switch (ctrl->bRequest) { - case UAC_GET_CUR: - case UAC_GET_MIN: - case UAC_GET_MAX: - case UAC_GET_RES: - value = 3; - break; - case UAC_GET_MEM: - break; - default: - break; - } - - return value; -} - -static int -audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = c->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - /* composite driver infrastructure handles everything except - * Audio class messages; interface activation uses set_alt(). - */ - switch (ctrl->bRequestType) { - case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: - value = audio_set_endpoint_req(c, ctrl); - break; - - case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: - value = audio_get_endpoint_req(c, ctrl); - break; - - default: - ERROR(cdev, "Invalid control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer or status phase? */ - if (value >= 0) { - DBG(cdev, "Audio req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - req->zero = 0; - req->length = value; - value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (value < 0) - ERROR(cdev, "Audio response on err %d\n", value); - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -/*-------------------------------------------------------------------------*/ - static int __init audio_do_config(struct usb_configuration *c) { /* FIXME alloc iConfiguration string, set it in c->strings */ @@ -220,7 +106,6 @@ static int __init audio_do_config(struct usb_configuration *c) static struct usb_configuration audio_config_driver = { .label = DRIVER_DESC, .bind = audio_do_config, - .setup = audio_setup, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index 98e9bb977291..c43c89ffa2c8 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -445,6 +445,70 @@ static int audio_get_intf_req(struct usb_function *f, return len; } +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + value = 0; + break; + + case UAC_SET_MIN: + break; + + case UAC_SET_MAX: + break; + + case UAC_SET_RES: + break; + + case UAC_SET_MEM: + break; + + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + value = 3; + break; + case UAC_GET_MEM: + break; + default: + break; + } + + return value; +} + static int f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -455,8 +519,8 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); - /* composite driver infrastructure handles everything except - * Audio class messages; interface activation uses set_alt(). + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). */ switch (ctrl->bRequestType) { case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: @@ -467,6 +531,14 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) value = audio_get_intf_req(f, ctrl); break; + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + default: ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, -- cgit v1.2.3 From df970c6ccf99c9573f6ebf5b0f37df797f2be9c7 Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Fri, 25 Sep 2009 12:24:12 +0200 Subject: USB: modifications for at91sam9g10 Modify both host and gadget USB drivers for at91sam9g10. This add a clock management equivalent to at91sam9261 on usb drivers. It also add the way of handling gadget pull-ups (like the at91sam9261). Signed-off-by: Hong Xu Signed-off-by: Nicolas Ferre --- drivers/usb/gadget/at91_udc.c | 6 +++--- drivers/usb/host/ohci-at91.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 66450a1abc22..043e04db2a05 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -892,7 +892,7 @@ static void pullup(struct at91_udc *udc, int is_on) txvc |= AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { u32 usbpucr; usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); @@ -910,7 +910,7 @@ static void pullup(struct at91_udc *udc, int is_on) txvc &= ~AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { u32 usbpucr; usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); @@ -1692,7 +1692,7 @@ static int __init at91udc_probe(struct platform_device *pdev) udc->ep[3].maxpacket = 64; udc->ep[4].maxpacket = 512; udc->ep[5].maxpacket = 512; - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { udc->ep[3].maxpacket = 64; } else if (cpu_is_at91sam9263()) { udc->ep[0].maxpacket = 64; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 7ccffcbe7b6f..68b83ab70719 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -35,7 +35,7 @@ extern int usb_disabled(void); static void at91_start_clock(void) { - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) clk_enable(hclk); clk_enable(iclk); clk_enable(fclk); @@ -46,7 +46,7 @@ static void at91_stop_clock(void) { clk_disable(fclk); clk_disable(iclk); - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) clk_disable(hclk); clocked = 0; } @@ -142,7 +142,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, iclk = clk_get(&pdev->dev, "ohci_clk"); fclk = clk_get(&pdev->dev, "uhpck"); - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) hclk = clk_get(&pdev->dev, "hck0"); at91_start_hc(pdev); @@ -155,7 +155,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, /* Error handling */ at91_stop_hc(pdev); - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) clk_put(hclk); clk_put(fclk); clk_put(iclk); @@ -192,7 +192,7 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) clk_put(hclk); clk_put(fclk); clk_put(iclk); -- cgit v1.2.3 From bb3d4db6a9c4ab13ce5730b6d1a9876258fc5a5a Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 24 Sep 2009 00:33:45 +0200 Subject: USB: usbtmc: minor formatting cleanups Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 2473cf0c6b1d..574ede15de85 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1109,13 +1109,13 @@ static void usbtmc_disconnect(struct usb_interface *intf) kref_put(&data->kref, usbtmc_delete); } -static int usbtmc_suspend (struct usb_interface *intf, pm_message_t message) +static int usbtmc_suspend(struct usb_interface *intf, pm_message_t message) { /* this driver does not have pending URBs */ return 0; } -static int usbtmc_resume (struct usb_interface *intf) +static int usbtmc_resume(struct usb_interface *intf) { return 0; } -- cgit v1.2.3 From 5c85ebc6ec046e3e3a4d765101527f2907fe417c Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 12 Oct 2009 15:45:17 +0000 Subject: usb: whci-hcd: decode more QHead fields in the debug files Print ep number, direction and type; and current window in asl and pzl debugfs files. Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/whci/debug.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c index 2273c815941f..8c1c610c9513 100644 --- a/drivers/usb/host/whci/debug.c +++ b/drivers/usb/host/whci/debug.c @@ -31,17 +31,29 @@ struct whc_dbg { void qset_print(struct seq_file *s, struct whc_qset *qset) { + static const char *qh_type[] = { + "ctrl", "isoc", "bulk", "intr", "rsvd", "rsvd", "rsvd", "lpintr", }; struct whc_std *std; struct urb *urb = NULL; int i; - seq_printf(s, "qset %08x\n", (u32)qset->qset_dma); + seq_printf(s, "qset %08x", (u32)qset->qset_dma); + if (&qset->list_node == qset->whc->async_list.prev) { + seq_printf(s, " (dummy)\n"); + } else { + seq_printf(s, " ep%d%s-%s maxpkt: %d\n", + qset->qh.info1 & 0x0f, + (qset->qh.info1 >> 4) & 0x1 ? "in" : "out", + qh_type[(qset->qh.info1 >> 5) & 0x7], + (qset->qh.info1 >> 16) & 0xffff); + } seq_printf(s, " -> %08x\n", (u32)qset->qh.link); seq_printf(s, " info: %08x %08x %08x\n", - qset->qh.info1, qset->qh.info2, qset->qh.info3); - seq_printf(s, " sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count); + qset->qh.info1, qset->qh.info2, qset->qh.info3); + seq_printf(s, " sts: %04x errs: %d curwin: %08x\n", + qset->qh.status, qset->qh.err_count, qset->qh.cur_window); seq_printf(s, " TD: sts: %08x opts: %08x\n", - qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options); + qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options); for (i = 0; i < WHCI_QSET_TD_MAX; i++) { seq_printf(s, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n", -- cgit v1.2.3 From 2ccaac1a90a517a13b7f91289970faa2b7ee74cf Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 12 Oct 2009 15:45:18 +0000 Subject: USB: wusb: add wusb_phy_rate sysfs file to host controllers Add the wusb_phy_rate sysfs file to Wireless USB host controllers. This sets the maximum PHY rate that will be used for all connected devices. Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-class-uwb_rc-wusbhc | 13 +++++++++ drivers/usb/host/whci/qset.c | 24 ++++++++++++++-- drivers/usb/host/whci/whci-hc.h | 9 +----- drivers/usb/wusbcore/wusbhc.c | 32 ++++++++++++++++++++++ drivers/usb/wusbcore/wusbhc.h | 1 + 5 files changed, 68 insertions(+), 11 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc b/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc index 4e8106f7cfd9..25b1e751b777 100644 --- a/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc +++ b/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc @@ -23,3 +23,16 @@ Description: Since this relates to security (specifically, the lifetime of PTKs and GTKs) it should not be changed from the default. + +What: /sys/class/uwb_rc/uwbN/wusbhc/wusb_phy_rate +Date: August 2009 +KernelVersion: 2.6.32 +Contact: David Vrabel +Description: + The maximum PHY rate to use for all connected devices. + This is only of limited use for testing and + development as the hardware's automatic rate + adaptation is better then this simple control. + + Refer to [ECMA-368] section 10.3.1.1 for the value to + use. diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index 08280869ed1c..39e855a55c63 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -49,11 +49,13 @@ struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags) * state * @urb: an urb for a transfer to this endpoint */ -static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) +static void qset_fill_qh(struct whc *whc, struct whc_qset *qset, struct urb *urb) { struct usb_device *usb_dev = urb->dev; + struct wusb_dev *wusb_dev = usb_dev->wusb_dev; struct usb_wireless_ep_comp_descriptor *epcd; bool is_out; + uint8_t phy_rate; is_out = usb_pipeout(urb->pipe); @@ -68,6 +70,22 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) qset->max_burst = 1; } + /* + * Initial PHY rate is 53.3 Mbit/s for control endpoints or + * the maximum supported by the device for other endpoints + * (unless limited by the user). + */ + if (usb_pipecontrol(urb->pipe)) + phy_rate = UWB_PHY_RATE_53; + else { + uint16_t phy_rates; + + phy_rates = le16_to_cpu(wusb_dev->wusb_cap_descr->wPHYRates); + phy_rate = fls(phy_rates) - 1; + if (phy_rate > whc->wusbhc.phy_rate) + phy_rate = whc->wusbhc.phy_rate; + } + qset->qh.info1 = cpu_to_le32( QH_INFO1_EP(usb_pipeendpoint(urb->pipe)) | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN) @@ -87,7 +105,7 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) * strength and can presumably guess the Tx power required * from that? */ qset->qh.info3 = cpu_to_le32( - QH_INFO3_TX_RATE_53_3 + QH_INFO3_TX_RATE(phy_rate) | QH_INFO3_TX_PWR(0) /* 0 == max power */ ); @@ -149,7 +167,7 @@ struct whc_qset *get_qset(struct whc *whc, struct urb *urb, qset->ep = urb->ep; urb->ep->hcpriv = qset; - qset_fill_qh(qset, urb); + qset_fill_qh(whc, qset, urb); } return qset; } diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h index d5e5c3aacced..4d4cbc0730bf 100644 --- a/drivers/usb/host/whci/whci-hc.h +++ b/drivers/usb/host/whci/whci-hc.h @@ -172,14 +172,7 @@ struct whc_qhead { #define QH_INFO3_MAX_DELAY(d) ((d) << 0) /* maximum stream delay in 125 us units (isoc only) */ #define QH_INFO3_INTERVAL(i) ((i) << 16) /* segment interval in 125 us units (isoc only) */ -#define QH_INFO3_TX_RATE_53_3 (0 << 24) -#define QH_INFO3_TX_RATE_80 (1 << 24) -#define QH_INFO3_TX_RATE_106_7 (2 << 24) -#define QH_INFO3_TX_RATE_160 (3 << 24) -#define QH_INFO3_TX_RATE_200 (4 << 24) -#define QH_INFO3_TX_RATE_320 (5 << 24) -#define QH_INFO3_TX_RATE_400 (6 << 24) -#define QH_INFO3_TX_RATE_480 (7 << 24) +#define QH_INFO3_TX_RATE(r) ((r) << 24) /* PHY rate (see [ECMA-368] section 10.3.1.1) */ #define QH_INFO3_TX_PWR(p) ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */ #define QH_STATUS_FLOW_CTRL (1 << 15) diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c index ee6256f23636..eab86e4bc770 100644 --- a/drivers/usb/wusbcore/wusbhc.c +++ b/drivers/usb/wusbcore/wusbhc.c @@ -147,10 +147,40 @@ static ssize_t wusb_chid_store(struct device *dev, } static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store); + +static ssize_t wusb_phy_rate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); + + return sprintf(buf, "%d\n", wusbhc->phy_rate); +} + +static ssize_t wusb_phy_rate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); + uint8_t phy_rate; + ssize_t result; + + result = sscanf(buf, "%hhu", &phy_rate); + if (result != 1) + return -EINVAL; + if (phy_rate >= UWB_PHY_RATE_INVALID) + return -EINVAL; + + wusbhc->phy_rate = phy_rate; + return size; +} +static DEVICE_ATTR(wusb_phy_rate, 0644, wusb_phy_rate_show, wusb_phy_rate_store); + /* Group all the WUSBHC attributes */ static struct attribute *wusbhc_attrs[] = { &dev_attr_wusb_trust_timeout.attr, &dev_attr_wusb_chid.attr, + &dev_attr_wusb_phy_rate.attr, NULL, }; @@ -177,6 +207,8 @@ int wusbhc_create(struct wusbhc *wusbhc) int result = 0; wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS; + wusbhc->phy_rate = UWB_PHY_RATE_INVALID - 1; + mutex_init(&wusbhc->mutex); result = wusbhc_mmcie_create(wusbhc); if (result < 0) diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index 797c2453a35b..fd2fd4e277e1 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h @@ -253,6 +253,7 @@ struct wusbhc { unsigned trust_timeout; /* in jiffies */ struct wusb_ckhdid chid; + uint8_t phy_rate; struct wuie_host_info *wuie_host_info; struct mutex mutex; /* locks everything else */ -- cgit v1.2.3 From 7c3950f89124a5588274859b1b3b9d81785c9d52 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 15 Oct 2009 17:09:34 +0300 Subject: USB OTG: add support for ulpi connected external transceivers This adds support for OTG transceivers directly connected to the ULPI interface. In particular, the following details are added - a struct for low level io functions (read/write) - a priv field to be used as 'viewport' by low level access functions - an (*init) and (*shutdown) callbacks, along with static inline helpers - a (*set_vbus) callback to switch the port power on and off - a flags field for per-transceiver settings - some defines for the flags bitmask to configure platform specific details Signed-off-by: Daniel Mack Cc: Heikki Krogerus Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/otg.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 2443c0e7a80c..52bb917641f0 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -33,6 +33,23 @@ enum usb_otg_state { OTG_STATE_A_VBUS_ERR, }; +#define USB_OTG_PULLUP_ID (1 << 0) +#define USB_OTG_PULLDOWN_DP (1 << 1) +#define USB_OTG_PULLDOWN_DM (1 << 2) +#define USB_OTG_EXT_VBUS_INDICATOR (1 << 3) +#define USB_OTG_DRV_VBUS (1 << 4) +#define USB_OTG_DRV_VBUS_EXT (1 << 5) + +struct otg_transceiver; + +/* for transceivers connected thru an ULPI interface, the user must + * provide access ops + */ +struct otg_io_access_ops { + int (*read)(struct otg_transceiver *otg, u32 reg); + int (*write)(struct otg_transceiver *otg, u32 val, u32 reg); +}; + /* * the otg driver needs to interact with both device side and host side * usb controllers. it decides which controller is active at a given @@ -42,6 +59,7 @@ enum usb_otg_state { struct otg_transceiver { struct device *dev; const char *label; + unsigned int flags; u8 default_a; enum usb_otg_state state; @@ -49,10 +67,17 @@ struct otg_transceiver { struct usb_bus *host; struct usb_gadget *gadget; + struct otg_io_access_ops *io_ops; + void __iomem *io_priv; + /* to pass extra port status to the root hub */ u16 port_status; u16 port_change; + /* initialize/shutdown the OTG controller */ + int (*init)(struct otg_transceiver *otg); + void (*shutdown)(struct otg_transceiver *otg); + /* bind/unbind the host controller */ int (*set_host)(struct otg_transceiver *otg, struct usb_bus *host); @@ -65,6 +90,10 @@ struct otg_transceiver { int (*set_power)(struct otg_transceiver *otg, unsigned mA); + /* effective for A-peripheral, ignored for B devices */ + int (*set_vbus)(struct otg_transceiver *otg, + bool enabled); + /* for non-OTG B devices: set transceiver into suspend mode */ int (*set_suspend)(struct otg_transceiver *otg, int suspend); @@ -85,6 +114,38 @@ extern int otg_set_transceiver(struct otg_transceiver *); extern void usb_nop_xceiv_register(void); extern void usb_nop_xceiv_unregister(void); +/* helpers for direct access thru low-level io interface */ +static inline int otg_io_read(struct otg_transceiver *otg, u32 reg) +{ + if (otg->io_ops && otg->io_ops->read) + return otg->io_ops->read(otg, reg); + + return -EINVAL; +} + +static inline int otg_io_write(struct otg_transceiver *otg, u32 reg, u32 val) +{ + if (otg->io_ops && otg->io_ops->write) + return otg->io_ops->write(otg, reg, val); + + return -EINVAL; +} + +static inline int +otg_init(struct otg_transceiver *otg) +{ + if (otg->init) + return otg->init(otg); + + return 0; +} + +static inline void +otg_shutdown(struct otg_transceiver *otg) +{ + if (otg->shutdown) + otg->shutdown(otg); +} /* for usb host and peripheral controller drivers */ extern struct otg_transceiver *otg_get_transceiver(void); @@ -97,6 +158,12 @@ otg_start_hnp(struct otg_transceiver *otg) return otg->start_hnp(otg); } +/* Context: can sleep */ +static inline int +otg_set_vbus(struct otg_transceiver *otg, bool enabled) +{ + return otg->set_vbus(otg, enabled); +} /* for HCDs */ static inline int @@ -105,7 +172,6 @@ otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) return otg->set_host(otg, host); } - /* for usb peripheral controller drivers */ /* Context: can sleep */ -- cgit v1.2.3 From eea6799cd658aaaef7301b44b5ac0a2c04064825 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 15 Oct 2009 17:09:35 +0300 Subject: USB OTG: Add generic driver for ULPI OTG transceiver This adds a minimal generic driver for ULPI connected transceivers, using the OTG framework functions recently introduced. The driver got a table to match the ULPI chips, which currently only has one entry for NXP's ISP 1504 transceiver. Signed-off-by: Daniel Mack Cc: Heikki Krogerus Cc: David Brownell Cc: Sascha Hauer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 2 + drivers/usb/otg/Kconfig | 9 ++++ drivers/usb/otg/Makefile | 1 + drivers/usb/otg/ulpi.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/ulpi.h | 7 +++ 5 files changed, 155 insertions(+) create mode 100644 drivers/usb/otg/ulpi.c create mode 100644 include/linux/usb/ulpi.h diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index be3c9b80bc9f..473aa1a20de9 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -44,3 +44,5 @@ obj-y += early/ obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ + +obj-$(CONFIG_USB_ULPI) += otg/ diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index aa884d072f0b..de56b3d743d7 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -41,6 +41,15 @@ config ISP1301_OMAP This driver can also be built as a module. If so, the module will be called isp1301_omap. +config USB_ULPI + bool "Generic ULPI Transceiver Driver" + depends on ARM + help + Enable this to support ULPI connected USB OTG transceivers which + are likely found on embedded boards. + + The only chip currently supported is NXP's ISP1504 + config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 208167856529..aeb49a8ec412 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o +obj-$(CONFIG_USB_ULPI) += ulpi.o ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c new file mode 100644 index 000000000000..896527456b7e --- /dev/null +++ b/drivers/usb/otg/ulpi.c @@ -0,0 +1,136 @@ +/* + * Generic ULPI USB transceiver support + * + * Copyright (C) 2009 Daniel Mack + * + * Based on sources from + * + * Sascha Hauer + * Freescale Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +/* ULPI register addresses */ +#define ULPI_VID_LOW 0x00 /* Vendor ID low */ +#define ULPI_VID_HIGH 0x01 /* Vendor ID high */ +#define ULPI_PID_LOW 0x02 /* Product ID low */ +#define ULPI_PID_HIGH 0x03 /* Product ID high */ +#define ULPI_ITFCTL 0x07 /* Interface Control */ +#define ULPI_OTGCTL 0x0A /* OTG Control */ + +/* add to above register address to access Set/Clear functions */ +#define ULPI_REG_SET 0x01 +#define ULPI_REG_CLEAR 0x02 + +/* ULPI OTG Control Register bits */ +#define ID_PULL_UP (1 << 0) /* enable ID Pull Up */ +#define DP_PULL_DOWN (1 << 1) /* enable DP Pull Down */ +#define DM_PULL_DOWN (1 << 2) /* enable DM Pull Down */ +#define DISCHRG_VBUS (1 << 3) /* Discharge Vbus */ +#define CHRG_VBUS (1 << 4) /* Charge Vbus */ +#define DRV_VBUS (1 << 5) /* Drive Vbus */ +#define DRV_VBUS_EXT (1 << 6) /* Drive Vbus external */ +#define USE_EXT_VBUS_IND (1 << 7) /* Use ext. Vbus indicator */ + +#define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) + +#define TR_FLAG(flags, a, b) (((flags) & a) ? b : 0) + +/* ULPI hardcoded IDs, used for probing */ +static unsigned int ulpi_ids[] = { + ULPI_ID(0x04cc, 0x1504), /* NXP ISP1504 */ +}; + +static int ulpi_set_flags(struct otg_transceiver *otg) +{ + unsigned int flags = 0; + + if (otg->flags & USB_OTG_PULLUP_ID) + flags |= ID_PULL_UP; + + if (otg->flags & USB_OTG_PULLDOWN_DM) + flags |= DM_PULL_DOWN; + + if (otg->flags & USB_OTG_PULLDOWN_DP) + flags |= DP_PULL_DOWN; + + if (otg->flags & USB_OTG_EXT_VBUS_INDICATOR) + flags |= USE_EXT_VBUS_IND; + + return otg_io_write(otg, flags, ULPI_OTGCTL + ULPI_REG_SET); +} + +static int ulpi_init(struct otg_transceiver *otg) +{ + int i, vid, pid; + + vid = (otg_io_read(otg, ULPI_VID_HIGH) << 8) | + otg_io_read(otg, ULPI_VID_LOW); + pid = (otg_io_read(otg, ULPI_PID_HIGH) << 8) | + otg_io_read(otg, ULPI_PID_LOW); + + pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); + + for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) + if (ulpi_ids[i] == ULPI_ID(vid, pid)) + return ulpi_set_flags(otg); + + pr_err("ULPI ID does not match any known transceiver.\n"); + return -ENODEV; +} + +static int ulpi_set_vbus(struct otg_transceiver *otg, bool on) +{ + unsigned int flags = otg_io_read(otg, ULPI_OTGCTL); + + flags &= ~(DRV_VBUS | DRV_VBUS_EXT); + + if (on) { + if (otg->flags & USB_OTG_DRV_VBUS) + flags |= DRV_VBUS; + + if (otg->flags & USB_OTG_DRV_VBUS_EXT) + flags |= DRV_VBUS_EXT; + } + + return otg_io_write(otg, flags, ULPI_OTGCTL + ULPI_REG_SET); +} + +struct otg_transceiver * +otg_ulpi_create(struct otg_io_access_ops *ops, + unsigned int flags) +{ + struct otg_transceiver *otg; + + otg = kzalloc(sizeof(*otg), GFP_KERNEL); + if (!otg) + return NULL; + + otg->label = "ULPI"; + otg->flags = flags; + otg->io_ops = ops; + otg->init = ulpi_init; + otg->set_vbus = ulpi_set_vbus; + + return otg; +} +EXPORT_SYMBOL_GPL(otg_ulpi_create); + diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h new file mode 100644 index 000000000000..20675c6ebc4d --- /dev/null +++ b/include/linux/usb/ulpi.h @@ -0,0 +1,7 @@ +#ifndef __LINUX_USB_ULPI_H +#define __LINUX_USB_ULPI_H + +struct otg_transceiver *otg_ulpi_create(struct otg_io_access_ops *ops, + unsigned int flags); + +#endif /* __LINUX_USB_ULPI_H */ -- cgit v1.2.3 From 045cebe9ccf3f7d418ccc2337b4f339dac923f2a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 14 Oct 2009 11:44:14 +0300 Subject: USB: host: ehci: introduce omap ehci-hcd driver this driver has been sitting in linux-omap tree for quite some time. It adds support for omap's ehci controller. Signed-off-by: Felipe Balbi Signed-off-by: Vikram Pandita Signed-off-by: Ajay Kumar Gupta Signed-off-by: Anand Gadiyar Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-omap.c | 755 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 760 insertions(+) create mode 100644 drivers/usb/host/ehci-omap.c diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 57f916cff872..a7649b586a92 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1108,6 +1108,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_hcd_au1xxx_driver #endif +#ifdef CONFIG_ARCH_OMAP34XX +#include "ehci-omap.c" +#define PLATFORM_DRIVER ehci_hcd_omap_driver +#endif + #ifdef CONFIG_PPC_PS3 #include "ehci-ps3.c" #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c new file mode 100644 index 000000000000..7ba8df3c6d78 --- /dev/null +++ b/drivers/usb/host/ehci-omap.c @@ -0,0 +1,755 @@ +/* + * ehci-omap.c - driver for USBHOST on OMAP 34xx processor + * + * Bus Glue for OMAP34xx USBHOST 3 port EHCI controller + * Tested on OMAP3430 ES2.0 SDP + * + * Copyright (C) 2007-2008 Texas Instruments, Inc. + * Author: Vikram Pandita + * + * Copyright (C) 2009 Nokia Corporation + * Contact: Felipe Balbi + * + * Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TODO (last updated Feb 23rd, 2009): + * - add kernel-doc + * - enable AUTOIDLE + * - move DPLL5 programming to clock fw + * - add suspend/resume + * - move workarounds to board-files + */ + +#include +#include +#include +#include + +/* + * OMAP USBHOST Register addresses: VIRTUAL ADDRESSES + * Use ehci_omap_readl()/ehci_omap_writel() functions + */ + +/* TLL Register Set */ +#define OMAP_USBTLL_REVISION (0x00) +#define OMAP_USBTLL_SYSCONFIG (0x10) +#define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8) +#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3) +#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0) + +#define OMAP_USBTLL_SYSSTATUS (0x14) +#define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP_USBTLL_IRQSTATUS (0x18) +#define OMAP_USBTLL_IRQENABLE (0x1C) + +#define OMAP_TLL_SHARED_CONF (0x30) +#define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6) +#define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5) +#define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2) +#define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1) +#define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0) + +#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) +#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) +#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) +#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) +#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) +#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) + +#define OMAP_TLL_ULPI_FUNCTION_CTRL(num) (0x804 + 0x100 * num) +#define OMAP_TLL_ULPI_INTERFACE_CTRL(num) (0x807 + 0x100 * num) +#define OMAP_TLL_ULPI_OTG_CTRL(num) (0x80A + 0x100 * num) +#define OMAP_TLL_ULPI_INT_EN_RISE(num) (0x80D + 0x100 * num) +#define OMAP_TLL_ULPI_INT_EN_FALL(num) (0x810 + 0x100 * num) +#define OMAP_TLL_ULPI_INT_STATUS(num) (0x813 + 0x100 * num) +#define OMAP_TLL_ULPI_INT_LATCH(num) (0x814 + 0x100 * num) +#define OMAP_TLL_ULPI_DEBUG(num) (0x815 + 0x100 * num) +#define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num) + +#define OMAP_TLL_CHANNEL_COUNT 3 +#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 1) +#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 2) +#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 4) + +/* UHH Register Set */ +#define OMAP_UHH_REVISION (0x00) +#define OMAP_UHH_SYSCONFIG (0x10) +#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12) +#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8) +#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3) +#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0) + +#define OMAP_UHH_SYSSTATUS (0x14) +#define OMAP_UHH_HOSTCONFIG (0x40) +#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0) +#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0) +#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11) +#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12) +#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2) +#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3) +#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4) +#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5) +#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8) +#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9) +#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10) + +#define OMAP_UHH_DEBUG_CSR (0x44) + +/* EHCI Register Set */ +#define EHCI_INSNREG05_ULPI (0xA4) +#define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31 +#define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24 +#define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22 +#define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16 +#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 +#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 + +/*-------------------------------------------------------------------------*/ + +static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val) +{ + __raw_writel(val, base + reg); +} + +static inline u32 ehci_omap_readl(void __iomem *base, u32 reg) +{ + return __raw_readl(base + reg); +} + +static inline void ehci_omap_writeb(void __iomem *base, u8 reg, u8 val) +{ + __raw_writeb(val, base + reg); +} + +static inline u8 ehci_omap_readb(void __iomem *base, u8 reg) +{ + return __raw_readb(base + reg); +} + +/*-------------------------------------------------------------------------*/ + +struct ehci_hcd_omap { + struct ehci_hcd *ehci; + struct device *dev; + + struct clk *usbhost_ick; + struct clk *usbhost2_120m_fck; + struct clk *usbhost1_48m_fck; + struct clk *usbtll_fck; + struct clk *usbtll_ick; + + /* FIXME the following two workarounds are + * board specific not silicon-specific so these + * should be moved to board-file instead. + * + * Maybe someone from TI will know better which + * board is affected and needs the workarounds + * to be applied + */ + + /* gpio for resetting phy */ + int reset_gpio_port[OMAP3_HS_USB_PORTS]; + + /* phy reset workaround */ + int phy_reset; + + /* desired phy_mode: TLL, PHY */ + enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS]; + + void __iomem *uhh_base; + void __iomem *tll_base; + void __iomem *ehci_base; +}; + +/*-------------------------------------------------------------------------*/ + +static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask) +{ + unsigned reg; + int i; + + /* Program the 3 TLL channels upfront */ + for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { + reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); + + /* Disable AutoIdle, BitStuffing and use SDR Mode */ + reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE + | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF + | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); + ehci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg); + } + + /* Program Common TLL register */ + reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_SHARED_CONF); + reg |= (OMAP_TLL_SHARED_CONF_FCLK_IS_ON + | OMAP_TLL_SHARED_CONF_USB_DIVRATION + | OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN); + reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN; + + ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg); + + /* Enable channels now */ + for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { + reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); + + /* Enable only the reg that is needed */ + if (!(tll_channel_mask & 1<tll_base, OMAP_TLL_CHANNEL_CONF(i), reg); + + ehci_omap_writeb(omap->tll_base, + OMAP_TLL_ULPI_SCRATCH_REGISTER(i), 0xbe); + dev_dbg(omap->dev, "ULPI_SCRATCH_REG[ch=%d]= 0x%02x\n", + i+1, ehci_omap_readb(omap->tll_base, + OMAP_TLL_ULPI_SCRATCH_REGISTER(i))); + } +} + +/*-------------------------------------------------------------------------*/ + +/* omap_start_ehc + * - Start the TI USBHOST controller + */ +static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + u8 tll_ch_mask = 0; + unsigned reg = 0; + int ret = 0; + + dev_dbg(omap->dev, "starting TI EHCI USB Controller\n"); + + /* Enable Clocks for USBHOST */ + omap->usbhost_ick = clk_get(omap->dev, "usbhost_ick"); + if (IS_ERR(omap->usbhost_ick)) { + ret = PTR_ERR(omap->usbhost_ick); + goto err_host_ick; + } + clk_enable(omap->usbhost_ick); + + omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck"); + if (IS_ERR(omap->usbhost2_120m_fck)) { + ret = PTR_ERR(omap->usbhost2_120m_fck); + goto err_host_120m_fck; + } + clk_enable(omap->usbhost2_120m_fck); + + omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck"); + if (IS_ERR(omap->usbhost1_48m_fck)) { + ret = PTR_ERR(omap->usbhost1_48m_fck); + goto err_host_48m_fck; + } + clk_enable(omap->usbhost1_48m_fck); + + if (omap->phy_reset) { + /* Refer: ISSUE1 */ + if (gpio_is_valid(omap->reset_gpio_port[0])) { + gpio_request(omap->reset_gpio_port[0], + "USB1 PHY reset"); + gpio_direction_output(omap->reset_gpio_port[0], 0); + } + + if (gpio_is_valid(omap->reset_gpio_port[1])) { + gpio_request(omap->reset_gpio_port[1], + "USB2 PHY reset"); + gpio_direction_output(omap->reset_gpio_port[1], 0); + } + + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(10); + } + + /* Configure TLL for 60Mhz clk for ULPI */ + omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck"); + if (IS_ERR(omap->usbtll_fck)) { + ret = PTR_ERR(omap->usbtll_fck); + goto err_tll_fck; + } + clk_enable(omap->usbtll_fck); + + omap->usbtll_ick = clk_get(omap->dev, "usbtll_ick"); + if (IS_ERR(omap->usbtll_ick)) { + ret = PTR_ERR(omap->usbtll_ick); + goto err_tll_ick; + } + clk_enable(omap->usbtll_ick); + + /* perform TLL soft reset, and wait until reset is complete */ + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, + OMAP_USBTLL_SYSCONFIG_SOFTRESET); + + /* Wait for TLL reset to complete */ + while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS) + & OMAP_USBTLL_SYSSTATUS_RESETDONE)) { + cpu_relax(); + + if (time_after(jiffies, timeout)) { + dev_dbg(omap->dev, "operation timed out\n"); + ret = -EINVAL; + goto err_sys_status; + } + } + + dev_dbg(omap->dev, "TLL RESET DONE\n"); + + /* (1<<3) = no idle mode only for initial debugging */ + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, + OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | + OMAP_USBTLL_SYSCONFIG_SIDLEMODE | + OMAP_USBTLL_SYSCONFIG_CACTIVITY); + + + /* Put UHH in NoIdle/NoStandby mode */ + reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG); + reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP + | OMAP_UHH_SYSCONFIG_SIDLEMODE + | OMAP_UHH_SYSCONFIG_CACTIVITY + | OMAP_UHH_SYSCONFIG_MIDLEMODE); + reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; + + ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); + + reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG); + + /* setup ULPI bypass and burst configurations */ + reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN + | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN + | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); + reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; + + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; + if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; + + /* Bypass the TLL module for PHY mode operation */ + if (omap_rev() <= OMAP3430_REV_ES2_1) { + dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1 \n"); + if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || + (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || + (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + } else { + dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + + if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + + } + ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); + dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg); + + + if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) || + (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) || + (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) { + + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= OMAP_TLL_CHANNEL_1_EN_MASK; + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= OMAP_TLL_CHANNEL_2_EN_MASK; + if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK; + + /* Enable UTMI mode for required TLL channels */ + omap_usb_utmi_init(omap, tll_ch_mask); + } + + if (omap->phy_reset) { + /* Refer ISSUE1: + * Hold the PHY in RESET for enough time till + * PHY is settled and ready + */ + udelay(10); + + if (gpio_is_valid(omap->reset_gpio_port[0])) + gpio_set_value(omap->reset_gpio_port[0], 1); + + if (gpio_is_valid(omap->reset_gpio_port[1])) + gpio_set_value(omap->reset_gpio_port[1], 1); + } + + return 0; + +err_sys_status: + clk_disable(omap->usbtll_ick); + clk_put(omap->usbtll_ick); + +err_tll_ick: + clk_disable(omap->usbtll_fck); + clk_put(omap->usbtll_fck); + +err_tll_fck: + clk_disable(omap->usbhost1_48m_fck); + clk_put(omap->usbhost1_48m_fck); + + if (omap->phy_reset) { + if (gpio_is_valid(omap->reset_gpio_port[0])) + gpio_free(omap->reset_gpio_port[0]); + + if (gpio_is_valid(omap->reset_gpio_port[1])) + gpio_free(omap->reset_gpio_port[1]); + } + +err_host_48m_fck: + clk_disable(omap->usbhost2_120m_fck); + clk_put(omap->usbhost2_120m_fck); + +err_host_120m_fck: + clk_disable(omap->usbhost_ick); + clk_put(omap->usbhost_ick); + +err_host_ick: + return ret; +} + +static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + + dev_dbg(omap->dev, "stopping TI EHCI USB Controller\n"); + + /* Reset OMAP modules for insmod/rmmod to work */ + ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, + OMAP_UHH_SYSCONFIG_SOFTRESET); + while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 0))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(omap->dev, "operation timed out\n"); + } + + while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 1))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(omap->dev, "operation timed out\n"); + } + + while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 2))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(omap->dev, "operation timed out\n"); + } + + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1)); + + while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS) + & (1 << 0))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(omap->dev, "operation timed out\n"); + } + + if (omap->usbtll_fck != NULL) { + clk_disable(omap->usbtll_fck); + clk_put(omap->usbtll_fck); + omap->usbtll_fck = NULL; + } + + if (omap->usbhost_ick != NULL) { + clk_disable(omap->usbhost_ick); + clk_put(omap->usbhost_ick); + omap->usbhost_ick = NULL; + } + + if (omap->usbhost1_48m_fck != NULL) { + clk_disable(omap->usbhost1_48m_fck); + clk_put(omap->usbhost1_48m_fck); + omap->usbhost1_48m_fck = NULL; + } + + if (omap->usbhost2_120m_fck != NULL) { + clk_disable(omap->usbhost2_120m_fck); + clk_put(omap->usbhost2_120m_fck); + omap->usbhost2_120m_fck = NULL; + } + + if (omap->usbtll_ick != NULL) { + clk_disable(omap->usbtll_ick); + clk_put(omap->usbtll_ick); + omap->usbtll_ick = NULL; + } + + if (omap->phy_reset) { + if (gpio_is_valid(omap->reset_gpio_port[0])) + gpio_free(omap->reset_gpio_port[0]); + + if (gpio_is_valid(omap->reset_gpio_port[1])) + gpio_free(omap->reset_gpio_port[1]); + } + + dev_dbg(omap->dev, "Clock to USB host has been disabled\n"); +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ehci_omap_hc_driver; + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + +/** + * ehci_hcd_omap_probe - initialize TI-based HCDs + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +static int ehci_hcd_omap_probe(struct platform_device *pdev) +{ + struct ehci_hcd_omap_platform_data *pdata = pdev->dev.platform_data; + struct ehci_hcd_omap *omap; + struct resource *res; + struct usb_hcd *hcd; + + int irq = platform_get_irq(pdev, 0); + int ret = -ENODEV; + + if (!pdata) { + dev_dbg(&pdev->dev, "missing platform_data\n"); + goto err_pdata; + } + + if (usb_disabled()) + goto err_disabled; + + omap = kzalloc(sizeof(*omap), GFP_KERNEL); + if (!omap) { + ret = -ENOMEM; + goto err_create_hcd; + } + + hcd = usb_create_hcd(&ehci_omap_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) { + dev_dbg(&pdev->dev, "failed to create hcd with err %d\n", ret); + ret = -ENOMEM; + goto err_create_hcd; + } + + platform_set_drvdata(pdev, omap); + omap->dev = &pdev->dev; + omap->phy_reset = pdata->phy_reset; + omap->reset_gpio_port[0] = pdata->reset_gpio_port[0]; + omap->reset_gpio_port[1] = pdata->reset_gpio_port[1]; + omap->reset_gpio_port[2] = pdata->reset_gpio_port[2]; + omap->port_mode[0] = pdata->port_mode[0]; + omap->port_mode[1] = pdata->port_mode[1]; + omap->port_mode[2] = pdata->port_mode[2]; + omap->ehci = hcd_to_ehci(hcd); + omap->ehci->sbrn = 0x20; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(&pdev->dev, "EHCI ioremap failed\n"); + ret = -ENOMEM; + goto err_ioremap; + } + + /* we know this is the memory we want, no need to ioremap again */ + omap->ehci->caps = hcd->regs; + omap->ehci_base = hcd->regs; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + omap->uhh_base = ioremap(res->start, resource_size(res)); + if (!omap->uhh_base) { + dev_err(&pdev->dev, "UHH ioremap failed\n"); + ret = -ENOMEM; + goto err_uhh_ioremap; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + omap->tll_base = ioremap(res->start, resource_size(res)); + if (!omap->tll_base) { + dev_err(&pdev->dev, "TLL ioremap failed\n"); + ret = -ENOMEM; + goto err_tll_ioremap; + } + + ret = omap_start_ehc(omap, hcd); + if (ret) { + dev_dbg(&pdev->dev, "failed to start ehci\n"); + goto err_start; + } + + omap->ehci->regs = hcd->regs + + HC_LENGTH(readl(&omap->ehci->caps->hc_capbase)); + + /* cache this readonly data; minimize chip reads */ + omap->ehci->hcs_params = readl(&omap->ehci->caps->hcs_params); + + /* SET 1 micro-frame Interrupt interval */ + writel(readl(&omap->ehci->regs->command) | (1 << 16), + &omap->ehci->regs->command); + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (ret) { + dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); + goto err_add_hcd; + } + + return 0; + +err_add_hcd: + omap_stop_ehc(omap, hcd); + +err_start: + iounmap(omap->tll_base); + +err_tll_ioremap: + iounmap(omap->uhh_base); + +err_uhh_ioremap: + iounmap(hcd->regs); + +err_ioremap: + usb_put_hcd(hcd); + +err_create_hcd: +err_disabled: +err_pdata: + return ret; +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * ehci_hcd_omap_remove - shutdown processing for EHCI HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + */ +static int ehci_hcd_omap_remove(struct platform_device *pdev) +{ + struct ehci_hcd_omap *omap = platform_get_drvdata(pdev); + struct usb_hcd *hcd = ehci_to_hcd(omap->ehci); + + usb_remove_hcd(hcd); + omap_stop_ehc(omap, hcd); + iounmap(hcd->regs); + iounmap(omap->tll_base); + iounmap(omap->uhh_base); + usb_put_hcd(hcd); + + return 0; +} + +static void ehci_hcd_omap_shutdown(struct platform_device *pdev) +{ + struct ehci_hcd_omap *omap = platform_get_drvdata(pdev); + struct usb_hcd *hcd = ehci_to_hcd(omap->ehci); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +static struct platform_driver ehci_hcd_omap_driver = { + .probe = ehci_hcd_omap_probe, + .remove = ehci_hcd_omap_remove, + .shutdown = ehci_hcd_omap_shutdown, + /*.suspend = ehci_hcd_omap_suspend, */ + /*.resume = ehci_hcd_omap_resume, */ + .driver = { + .name = "ehci-omap", + } +}; + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ehci_omap_hc_driver = { + .description = hcd_name, + .product_desc = "OMAP-EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +MODULE_ALIAS("platform:omap-ehci"); +MODULE_AUTHOR("Texas Instruments, Inc."); +MODULE_AUTHOR("Felipe Balbi "); + -- cgit v1.2.3 From 650f470b333a42936056f73e7cadad9a17e5825b Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 19 Oct 2009 13:19:41 +0200 Subject: USB: improved error handling in usb_port_suspend() usb: better error handling in usb_port_suspend - disable remote wakeup only if it was enabled - refuse to autosuspend if remote wakeup fails to be enabled Signed-off-by: Oliver Neukum Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0f857e645058..2ac115015229 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2123,9 +2123,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (status) + if (status) { dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); + /* bail if autosuspend is requested */ + if (msg.event & PM_EVENT_AUTO) + return status; + } } /* see 7.1.7.6 */ @@ -2134,7 +2138,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ - (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + if (udev->do_remote_wakeup) + (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, -- cgit v1.2.3 From 7778ea8ff33d42fd72a0d6fa6877f0ca94164d85 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 27 Oct 2009 10:55:52 -0700 Subject: USB: xhci: Handle URB cancel, complete and resubmit race. In the old code, there was a race condition between the stop endpoint command and the URB submission process. When the stop endpoint command is handled by the event handler, the endpoint ring is assumed to be stopped. When a stop endpoint command is queued, URB submissions are to not ring the doorbell. The old code would check the number of pending URBs to be canceled, and would not ring the doorbell if it was non-zero. However, the following race condition could occur with the old code: 1. Cancel an URB, add it to the list of URBs to be canceled, queue the stop endpoint command, and increment ep->cancels_pending to 1. 2. The URB finishes on the HW, and an event is enqueued to the event ring (at the same time as 1). 3. The stop endpoint command finishes, and the endpoint is halted. An event is queued to the event ring. 4. The event handler sees the finished URB, notices it was to be canceled, decrements ep->cancels_pending to 0, and removes it from the to be canceled list. 5. The event handler drops the lock and gives back the URB. The completion handler requeues the URB (or a different driver enqueues a new URB). This causes the endpoint's doorbell to be rung, since ep->cancels_pending == 0. The endpoint is now running. 6. A second URB is canceled, and it's added to the canceled list. Since ep->cancels_pending == 0, a new stop endpoint command is queued, and ep->cancels_pending is incremented to 1. 7. The event handler then sees the completed stop endpoint command. The handler assumes the endpoint is stopped, but it isn't. It attempts to move the dequeue pointer or change TDs to cancel the second URB, while the hardware is actively accessing the endpoint ring. To eliminate this race condition, a new endpoint state bit is introduced, EP_HALT_PENDING. When this bit is set, a stop endpoint command has been queued, and the command handler has not begun to process the URB cancellation list yet. The endpoint doorbell should not be rung when this is set. Set this when a stop endpoint command is queued, clear it when the handler for that command runs, and check if it's set before ringing a doorbell. ep->cancels_pending is eliminated, because it is no longer used. Make sure to ring the doorbell for an endpoint when the stop endpoint command handler runs, even if the canceled URB list is empty. All canceled URBs could have completed and new URBs could have been enqueued without the doorbell being rung before the command was handled. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hcd.c | 4 ++-- drivers/usb/host/xhci-ring.c | 14 ++++++++------ drivers/usb/host/xhci.h | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 932f99938481..3a30db6d6abe 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -817,12 +817,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) xhci_debug_ring(xhci, ep_ring); td = (struct xhci_td *) urb->hcpriv; - ep->cancels_pending++; list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); /* Queue a stop endpoint command, but only if this is * the first cancellation to be handled. */ - if (ep->cancels_pending == 1) { + if (!(ep->ep_state & EP_HALT_PENDING)) { + ep->ep_state |= EP_HALT_PENDING; xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index); xhci_ring_cmd_db(xhci); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 821b7b4709de..184e8b6f30b2 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -306,7 +306,7 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci, /* Don't ring the doorbell for this endpoint if there are pending * cancellations because the we don't want to interrupt processing. */ - if (!ep->cancels_pending && !(ep_state & SET_DEQ_PENDING) + if (!(ep_state & EP_HALT_PENDING) && !(ep_state & SET_DEQ_PENDING) && !(ep_state & EP_HALTED)) { field = xhci_readl(xhci, db_addr) & DB_MASK; xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr); @@ -507,8 +507,11 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, ep = &xhci->devs[slot_id]->eps[ep_index]; ep_ring = ep->ring; - if (list_empty(&ep->cancelled_td_list)) + if (list_empty(&ep->cancelled_td_list)) { + ep->ep_state &= ~EP_HALT_PENDING; + ring_ep_doorbell(xhci, slot_id, ep_index); return; + } /* Fix up the ep ring first, so HW stops executing cancelled TDs. * We have the xHCI lock, so nothing can modify this list until we drop @@ -535,9 +538,9 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, * the cancelled TD list for URB completion later. */ list_del(&cur_td->td_list); - ep->cancels_pending--; } last_unlinked_td = cur_td; + ep->ep_state &= ~EP_HALT_PENDING; /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */ if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { @@ -1249,10 +1252,9 @@ td_cleanup: } list_del(&td->td_list); /* Was this TD slated to be cancelled but completed anyway? */ - if (!list_empty(&td->cancelled_td_list)) { + if (!list_empty(&td->cancelled_td_list)) list_del(&td->cancelled_td_list); - ep->cancels_pending--; - } + /* Leave the TD around for the reset endpoint function to use * (but only if it's not a control endpoint, since we already * queued the Set TR dequeue pointer command for stalled diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 4b254b6fa245..b173fd96dceb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -652,10 +652,10 @@ struct xhci_virt_ep { struct xhci_ring *new_ring; unsigned int ep_state; #define SET_DEQ_PENDING (1 << 0) -#define EP_HALTED (1 << 1) +#define EP_HALTED (1 << 1) /* For stall handling */ +#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */ /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; - unsigned int cancels_pending; /* The TRB that was last reported in a stopped endpoint ring */ union xhci_trb *stopped_trb; struct xhci_td *stopped_td; -- cgit v1.2.3 From 1656142aa10531bed4d8cd3abcd1f1105f6b1c31 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 27 Oct 2009 10:56:33 -0700 Subject: USB: xhci: Re-purpose xhci_quiesce(). xhci_quiesce() is basically a no-op right now. It's only called if HC_IS_RUNNING() is true, and the body of the function consists of a BUG_ON if HC_IS_RUNNING() is false. For the new xHCI watchdog timer, we need a new function that clears the xHCI running bit in the command register, but doesn't wait for the halt status to show up in the status register. Re-purpose xhci_quiesce() to do that. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hcd.c | 51 +++++++++++++++------------------------------ drivers/usb/host/xhci.h | 1 + 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 3a30db6d6abe..5839453d342b 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -67,22 +67,14 @@ static int handshake(struct xhci_hcd *xhci, void __iomem *ptr, } /* - * Force HC into halt state. - * - * Disable any IRQs and clear the run/stop bit. - * HC will complete any current and actively pipelined transactions, and - * should halt within 16 microframes of the run/stop bit being cleared. - * Read HC Halted bit in the status register to see when the HC is finished. - * XXX: shouldn't we set HC_STATE_HALT here somewhere? + * Disable interrupts and begin the xHCI halting process. */ -int xhci_halt(struct xhci_hcd *xhci) +void xhci_quiesce(struct xhci_hcd *xhci) { u32 halted; u32 cmd; u32 mask; - xhci_dbg(xhci, "// Halt the HC\n"); - /* Disable all interrupts from the host controller */ mask = ~(XHCI_IRQS); halted = xhci_readl(xhci, &xhci->op_regs->status) & STS_HALT; if (!halted) @@ -91,6 +83,21 @@ int xhci_halt(struct xhci_hcd *xhci) cmd = xhci_readl(xhci, &xhci->op_regs->command); cmd &= mask; xhci_writel(xhci, cmd, &xhci->op_regs->command); +} + +/* + * Force HC into halt state. + * + * Disable any IRQs and clear the run/stop bit. + * HC will complete any current and actively pipelined transactions, and + * should halt within 16 microframes of the run/stop bit being cleared. + * Read HC Halted bit in the status register to see when the HC is finished. + * XXX: shouldn't we set HC_STATE_HALT here somewhere? + */ +int xhci_halt(struct xhci_hcd *xhci) +{ + xhci_dbg(xhci, "// Halt the HC\n"); + xhci_quiesce(xhci); return handshake(xhci, &xhci->op_regs->status, STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); @@ -124,28 +131,6 @@ int xhci_reset(struct xhci_hcd *xhci) return handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 250 * 1000); } -/* - * Stop the HC from processing the endpoint queues. - */ -static void xhci_quiesce(struct xhci_hcd *xhci) -{ - /* - * Queues are per endpoint, so we need to disable an endpoint or slot. - * - * To disable a slot, we need to insert a disable slot command on the - * command ring and ring the doorbell. This will also free any internal - * resources associated with the slot (which might not be what we want). - * - * A Release Endpoint command sounds better - doesn't free internal HC - * memory, but removes the endpoints from the schedule and releases the - * bandwidth, disables the doorbells, and clears the endpoint enable - * flag. Usually used prior to a set interface command. - * - * TODO: Implement after command ring code is done. - */ - BUG_ON(!HC_IS_RUNNING(xhci_to_hcd(xhci)->state)); - xhci_dbg(xhci, "Finished quiescing -- code not written yet\n"); -} #if 0 /* Set up MSI-X table for entry 0 (may claim other entries later) */ @@ -490,8 +475,6 @@ void xhci_stop(struct usb_hcd *hcd) struct xhci_hcd *xhci = hcd_to_xhci(hcd); spin_lock_irq(&xhci->lock); - if (HC_IS_RUNNING(hcd->state)) - xhci_quiesce(xhci); xhci_halt(xhci); xhci_reset(xhci); spin_unlock_irq(&xhci->lock); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b173fd96dceb..af3c5638526c 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1223,6 +1223,7 @@ void xhci_unregister_pci(void); #endif /* xHCI host controller glue */ +void xhci_quiesce(struct xhci_hcd *xhci); int xhci_halt(struct xhci_hcd *xhci); int xhci_reset(struct xhci_hcd *xhci); int xhci_init(struct usb_hcd *hcd); -- cgit v1.2.3 From 01a5479f8c9e28e6bd1359b966dd81396fcb1732 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 27 Oct 2009 10:57:01 -0700 Subject: USB: xhci: Add watchdog timer for URB cancellation. In order to giveback a canceled URB, we must ensure that the xHCI hardware will not access the buffer in an URB. We can't modify the buffer pointers on endpoint rings without issuing and waiting for a stop endpoint command. Since URBs can be canceled in interrupt context, we can't wait on that command. The old code trusted that the host controller would respond to the command, and would giveback the URBs in the event handler. If the hardware never responds to the stop endpoint command, the URBs will never be completed, and we might hang the USB subsystem. Implement a watchdog timer that is spawned whenever a stop endpoint command is queued. If a stop endpoint command event is found on the event ring during an interrupt, we need to stop the watchdog timer with del_timer(). Since del_timer() can fail if the timer is running and waiting on the xHCI lock, we need a way to signal to the timer that everything is fine and it should exit. If we simply clear EP_HALT_PENDING, a new stop endpoint command could sneak in and set it before the watchdog timer can grab the lock. Instead we use a combination of the EP_HALT_PENDING flag and a counter for the number of pending stop endpoint commands (xhci_virt_ep->stop_cmds_pending). If we need to cancel the watchdog timer and del_timer() succeeds, we decrement the number of pending stop endpoint commands. If del_timer() fails, we leave the number of pending stop endpoint commands alone. In either case, we clear the EP_HALT_PENDING flag. The timer will decrement the number of pending stop endpoint commands once it obtains the lock. If the timer is the tail end of the last stop endpoint command (xhci_virt_ep->stop_cmds_pending == 0), and the endpoint's command is still pending (EP_HALT_PENDING is set), we assume the host is dying. The watchdog timer will set XHCI_STATE_DYING, try to halt the xHCI host, and give back all pending URBs. Various other places in the driver need to check whether the xHCI host is dying. If the interrupt handler ever notices, it should immediately stop processing events. The URB enqueue function should also return -ESHUTDOWN. The URB dequeue function should simply return the value of usb_hcd_check_unlink_urb() and the watchdog timer will take care of giving the URB back. When a device is disconnected, the xHCI hardware structures should be freed without issuing a disable slot command (since the hardware probably won't respond to it anyway). The debugging polling loop should stop polling if the host is dying. When a device is disconnected, any pending watchdog timers are killed with del_timer_sync(). It must be synchronous so that the watchdog timer doesn't attempt to access the freed endpoint structures. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hcd.c | 52 ++++++++++++- drivers/usb/host/xhci-mem.c | 15 +++- drivers/usb/host/xhci-ring.c | 170 ++++++++++++++++++++++++++++++++++++++++--- drivers/usb/host/xhci.h | 22 ++++++ 4 files changed, 243 insertions(+), 16 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 5839453d342b..0d5a8564ed17 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -246,8 +246,14 @@ static void xhci_work(struct xhci_hcd *xhci) /* Flush posted writes */ xhci_readl(xhci, &xhci->ir_set->irq_pending); - /* FIXME this should be a delayed service routine that clears the EHB */ - xhci_handle_event(xhci); + if (xhci->xhc_state & XHCI_STATE_DYING) + xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " + "Shouldn't IRQs be disabled?\n"); + else + /* FIXME this should be a delayed service routine + * that clears the EHB. + */ + xhci_handle_event(xhci); /* Clear the event handler busy flag (RW1C); the event ring should be empty. */ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); @@ -320,7 +326,7 @@ void xhci_event_ring_work(unsigned long arg) spin_lock_irqsave(&xhci->lock, flags); temp = xhci_readl(xhci, &xhci->op_regs->status); xhci_dbg(xhci, "op reg status = 0x%x\n", temp); - if (temp == 0xffffffff) { + if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) { xhci_dbg(xhci, "HW died, polling stopped.\n"); spin_unlock_irqrestore(&xhci->lock, flags); return; @@ -710,16 +716,22 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) * atomic context to this function, which may allocate memory. */ spin_lock_irqsave(&xhci->lock, flags); + if (xhci->xhc_state & XHCI_STATE_DYING) + goto dying; ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); spin_unlock_irqrestore(&xhci->lock, flags); } else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) { spin_lock_irqsave(&xhci->lock, flags); + if (xhci->xhc_state & XHCI_STATE_DYING) + goto dying; ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); spin_unlock_irqrestore(&xhci->lock, flags); } else if (usb_endpoint_xfer_int(&urb->ep->desc)) { spin_lock_irqsave(&xhci->lock, flags); + if (xhci->xhc_state & XHCI_STATE_DYING) + goto dying; ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); spin_unlock_irqrestore(&xhci->lock, flags); @@ -728,6 +740,12 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) } exit: return ret; +dying: + xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for " + "non-responsive xHCI host.\n", + urb->ep->desc.bEndpointAddress, urb); + spin_unlock_irqrestore(&xhci->lock, flags); + return -ESHUTDOWN; } /* @@ -789,6 +807,17 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) kfree(td); return ret; } + if (xhci->xhc_state & XHCI_STATE_DYING) { + xhci_dbg(xhci, "Ep 0x%x: URB %p to be canceled on " + "non-responsive xHCI host.\n", + urb->ep->desc.bEndpointAddress, urb); + /* Let the stop endpoint command watchdog timer (which set this + * state) finish cleaning up the endpoint TD lists. We must + * have caught it in the middle of dropping a lock and giving + * back an URB. + */ + goto done; + } xhci_dbg(xhci, "Cancel URB %p\n", urb); xhci_dbg(xhci, "Event ring:\n"); @@ -806,6 +835,10 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) */ if (!(ep->ep_state & EP_HALT_PENDING)) { ep->ep_state |= EP_HALT_PENDING; + ep->stop_cmds_pending++; + ep->stop_cmd_timer.expires = jiffies + + XHCI_STOP_EP_CMD_TIMEOUT * HZ; + add_timer(&ep->stop_cmd_timer); xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index); xhci_ring_cmd_db(xhci); } @@ -1410,16 +1443,27 @@ void xhci_endpoint_reset(struct usb_hcd *hcd, void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_virt_device *virt_dev; unsigned long flags; u32 state; + int i; if (udev->slot_id == 0) return; + virt_dev = xhci->devs[udev->slot_id]; + if (!virt_dev) + return; + + /* Stop any wayward timer functions (which may grab the lock) */ + for (i = 0; i < 31; ++i) { + virt_dev->eps[i].ep_state &= ~EP_HALT_PENDING; + del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); + } spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = xhci_readl(xhci, &xhci->op_regs->status); - if (state == 0xffffffff) { + if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) { xhci_free_virt_device(xhci, udev->slot_id); spin_unlock_irqrestore(&xhci->lock, flags); return; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index b8fd270a8b0d..fd05247b7bb1 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -248,6 +248,15 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, (ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params))); } +static void xhci_init_endpoint_timer(struct xhci_hcd *xhci, + struct xhci_virt_ep *ep) +{ + init_timer(&ep->stop_cmd_timer); + ep->stop_cmd_timer.data = (unsigned long) ep; + ep->stop_cmd_timer.function = xhci_stop_endpoint_command_watchdog; + ep->xhci = xhci; +} + /* All the xhci_tds in the ring's TD list should be freed at this point */ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) { @@ -309,9 +318,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id, (unsigned long long)dev->in_ctx->dma); - /* Initialize the cancellation list for each endpoint */ - for (i = 0; i < 31; i++) + /* Initialize the cancellation list and watchdog timers for each ep */ + for (i = 0; i < 31; i++) { + xhci_init_endpoint_timer(xhci, &dev->eps[i]); INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); + } /* Allocate endpoint 0 ring */ dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, flags); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 184e8b6f30b2..9541e88df68f 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -475,6 +475,35 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, ep->ep_state |= SET_DEQ_PENDING; } +static inline void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci, + struct xhci_virt_ep *ep) +{ + ep->ep_state &= ~EP_HALT_PENDING; + /* Can't del_timer_sync in interrupt, so we attempt to cancel. If the + * timer is running on another CPU, we don't decrement stop_cmds_pending + * (since we didn't successfully stop the watchdog timer). + */ + if (del_timer(&ep->stop_cmd_timer)) + ep->stop_cmds_pending--; +} + +/* Must be called with xhci->lock held in interrupt context */ +static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, + struct xhci_td *cur_td, int status, char *adjective) +{ + struct usb_hcd *hcd = xhci_to_hcd(xhci); + + cur_td->urb->hcpriv = NULL; + usb_hcd_unlink_urb_from_ep(hcd, cur_td->urb); + xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, cur_td->urb); + + spin_unlock(&xhci->lock); + usb_hcd_giveback_urb(hcd, cur_td->urb, status); + kfree(cur_td); + spin_lock(&xhci->lock); + xhci_dbg(xhci, "%s URB given back\n", adjective); +} + /* * When we get a command completion for a Stop Endpoint Command, we need to * unlink any cancelled TDs from the ring. There are two ways to do that: @@ -508,7 +537,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, ep_ring = ep->ring; if (list_empty(&ep->cancelled_td_list)) { - ep->ep_state &= ~EP_HALT_PENDING; + xhci_stop_watchdog_timer_in_irq(xhci, ep); ring_ep_doorbell(xhci, slot_id, ep_index); return; } @@ -540,7 +569,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, list_del(&cur_td->td_list); } last_unlinked_td = cur_td; - ep->ep_state &= ~EP_HALT_PENDING; + xhci_stop_watchdog_timer_in_irq(xhci, ep); /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */ if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { @@ -568,23 +597,136 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, hcd_stat_update(xhci->tp_stat, cur_td->urb->actual_length, ktime_sub(stop_time, cur_td->start_time)); #endif - cur_td->urb->hcpriv = NULL; - usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), cur_td->urb); - - xhci_dbg(xhci, "Giveback cancelled URB %p\n", cur_td->urb); - spin_unlock(&xhci->lock); /* Doesn't matter what we pass for status, since the core will * just overwrite it (because the URB has been unlinked). */ - usb_hcd_giveback_urb(xhci_to_hcd(xhci), cur_td->urb, 0); - kfree(cur_td); + xhci_giveback_urb_in_irq(xhci, cur_td, 0, "cancelled"); - spin_lock(&xhci->lock); + /* Stop processing the cancelled list if the watchdog timer is + * running. + */ + if (xhci->xhc_state & XHCI_STATE_DYING) + return; } while (cur_td != last_unlinked_td); /* Return to the event handler with xhci->lock re-acquired */ } +/* Watchdog timer function for when a stop endpoint command fails to complete. + * In this case, we assume the host controller is broken or dying or dead. The + * host may still be completing some other events, so we have to be careful to + * let the event ring handler and the URB dequeueing/enqueueing functions know + * through xhci->state. + * + * The timer may also fire if the host takes a very long time to respond to the + * command, and the stop endpoint command completion handler cannot delete the + * timer before the timer function is called. Another endpoint cancellation may + * sneak in before the timer function can grab the lock, and that may queue + * another stop endpoint command and add the timer back. So we cannot use a + * simple flag to say whether there is a pending stop endpoint command for a + * particular endpoint. + * + * Instead we use a combination of that flag and a counter for the number of + * pending stop endpoint commands. If the timer is the tail end of the last + * stop endpoint command, and the endpoint's command is still pending, we assume + * the host is dying. + */ +void xhci_stop_endpoint_command_watchdog(unsigned long arg) +{ + struct xhci_hcd *xhci; + struct xhci_virt_ep *ep; + struct xhci_virt_ep *temp_ep; + struct xhci_ring *ring; + struct xhci_td *cur_td; + int ret, i, j; + + ep = (struct xhci_virt_ep *) arg; + xhci = ep->xhci; + + spin_lock(&xhci->lock); + + ep->stop_cmds_pending--; + if (xhci->xhc_state & XHCI_STATE_DYING) { + xhci_dbg(xhci, "Stop EP timer ran, but another timer marked " + "xHCI as DYING, exiting.\n"); + spin_unlock(&xhci->lock); + return; + } + if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) { + xhci_dbg(xhci, "Stop EP timer ran, but no command pending, " + "exiting.\n"); + spin_unlock(&xhci->lock); + return; + } + + xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n"); + xhci_warn(xhci, "Assuming host is dying, halting host.\n"); + /* Oops, HC is dead or dying or at least not responding to the stop + * endpoint command. + */ + xhci->xhc_state |= XHCI_STATE_DYING; + /* Disable interrupts from the host controller and start halting it */ + xhci_quiesce(xhci); + spin_unlock(&xhci->lock); + + ret = xhci_halt(xhci); + + spin_lock(&xhci->lock); + if (ret < 0) { + /* This is bad; the host is not responding to commands and it's + * not allowing itself to be halted. At least interrupts are + * disabled, so we can set HC_STATE_HALT and notify the + * USB core. But if we call usb_hc_died(), it will attempt to + * disconnect all device drivers under this host. Those + * disconnect() methods will wait for all URBs to be unlinked, + * so we must complete them. + */ + xhci_warn(xhci, "Non-responsive xHCI host is not halting.\n"); + xhci_warn(xhci, "Completing active URBs anyway.\n"); + /* We could turn all TDs on the rings to no-ops. This won't + * help if the host has cached part of the ring, and is slow if + * we want to preserve the cycle bit. Skip it and hope the host + * doesn't touch the memory. + */ + } + for (i = 0; i < MAX_HC_SLOTS; i++) { + if (!xhci->devs[i]) + continue; + for (j = 0; j < 31; j++) { + temp_ep = &xhci->devs[i]->eps[j]; + ring = temp_ep->ring; + if (!ring) + continue; + xhci_dbg(xhci, "Killing URBs for slot ID %u, " + "ep index %u\n", i, j); + while (!list_empty(&ring->td_list)) { + cur_td = list_first_entry(&ring->td_list, + struct xhci_td, + td_list); + list_del(&cur_td->td_list); + if (!list_empty(&cur_td->cancelled_td_list)) + list_del(&cur_td->cancelled_td_list); + xhci_giveback_urb_in_irq(xhci, cur_td, + -ESHUTDOWN, "killed"); + } + while (!list_empty(&temp_ep->cancelled_td_list)) { + cur_td = list_first_entry( + &temp_ep->cancelled_td_list, + struct xhci_td, + cancelled_td_list); + list_del(&cur_td->cancelled_td_list); + xhci_giveback_urb_in_irq(xhci, cur_td, + -ESHUTDOWN, "killed"); + } + } + } + spin_unlock(&xhci->lock); + xhci_to_hcd(xhci)->state = HC_STATE_HALT; + xhci_dbg(xhci, "Calling usb_hc_died()\n"); + usb_hc_died(xhci_to_hcd(xhci)); + xhci_dbg(xhci, "xHCI host controller is dead.\n"); +} + /* * When we get a completion for a Set Transfer Ring Dequeue Pointer command, * we need to clear the set deq pending flag in the endpoint ring state, so that @@ -1333,6 +1475,14 @@ void xhci_handle_event(struct xhci_hcd *xhci) default: xhci->error_bitmask |= 1 << 3; } + /* Any of the above functions may drop and re-acquire the lock, so check + * to make sure a watchdog timer didn't mark the host as non-responsive. + */ + if (xhci->xhc_state & XHCI_STATE_DYING) { + xhci_dbg(xhci, "xHCI host dying, returning from " + "event handler.\n"); + return; + } if (update_ptrs) { /* Update SW and HC event ring dequeue pointer */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index af3c5638526c..c92f84154fb5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -659,6 +659,10 @@ struct xhci_virt_ep { /* The TRB that was last reported in a stopped endpoint ring */ union xhci_trb *stopped_trb; struct xhci_td *stopped_td; + /* Watchdog timer for stop endpoint command to cancel URBs */ + struct timer_list stop_cmd_timer; + int stop_cmds_pending; + struct xhci_hcd *xhci; }; struct xhci_virt_device { @@ -1022,6 +1026,8 @@ struct xhci_scratchpad { #define ERST_ENTRIES 1 /* Poll every 60 seconds */ #define POLL_TIMEOUT 60 +/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */ +#define XHCI_STOP_EP_CMD_TIMEOUT 5 /* XXX: Make these module parameters */ @@ -1083,6 +1089,21 @@ struct xhci_hcd { struct timer_list event_ring_timer; int zombie; #endif + /* Host controller watchdog timer structures */ + unsigned int xhc_state; +/* Host controller is dying - not responding to commands. "I'm not dead yet!" + * + * xHC interrupts have been disabled and a watchdog timer will (or has already) + * halt the xHCI host, and complete all URBs with an -ESHUTDOWN code. Any code + * that sees this status (other than the timer that set it) should stop touching + * hardware immediately. Interrupt handlers should return immediately when + * they see this status (any time they drop and re-acquire xhci->lock). + * xhci_urb_dequeue() should call usb_hcd_check_unlink_urb() and return without + * putting the TD on the canceled list, etc. + * + * There are no reports of xHCI host controllers that display this issue. + */ +#define XHCI_STATE_DYING (1 << 0) /* Statistics */ int noops_submitted; int noops_handled; @@ -1279,6 +1300,7 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_dequeue_state *deq_state); +void xhci_stop_endpoint_command_watchdog(unsigned long arg); /* xHCI roothub code */ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, -- cgit v1.2.3 From 1978628089d0bf5e560c8de0eb2b1492771c1b12 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 3 Nov 2009 13:06:40 -0800 Subject: USB: xhci: Remove unused HCD statistics code. CONFIG_USB_HCD_STAT was used in an abandoned patch to track host controller throughput statistics. Since CONFIG_USB_HCD_STAT will never be defined, remove code that can never run. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 9541e88df68f..aaed076bae37 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -526,9 +526,6 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, struct xhci_td *last_unlinked_td; struct xhci_dequeue_state deq_state; -#ifdef CONFIG_USB_HCD_STAT - ktime_t stop_time = ktime_get(); -#endif memset(&deq_state, 0, sizeof(deq_state)); slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); @@ -593,10 +590,6 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, list_del(&cur_td->cancelled_td_list); /* Clean up the cancelled URB */ -#ifdef CONFIG_USB_HCD_STAT - hcd_stat_update(xhci->tp_stat, cur_td->urb->actual_length, - ktime_sub(stop_time, cur_td->start_time)); -#endif /* Doesn't matter what we pass for status, since the core will * just overwrite it (because the URB has been unlinked). */ -- cgit v1.2.3 From e14196b442dcfb1e146fa0730b9f950706d2f6b3 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 27 Oct 2009 10:54:49 -0700 Subject: USB: ehci: Minor constant fix for SCHEDULE_SLOP. Change the constant SCHEDULE_SLOP to be 80 microframes, instead of 10 frames. It was always multiplied by 8 to convert frames to microframes. SCHEDULE_SLOP is only used in ehci-sched.c. Signed-off-by: Sarah Sharp Cc: David Brownell Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index b25cdea93a1f..9fa5614b8ef6 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1373,7 +1373,7 @@ sitd_slot_ok ( * given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler! */ -#define SCHEDULE_SLOP 10 /* frames */ +#define SCHEDULE_SLOP 80 /* microframes */ static int iso_stream_schedule ( @@ -1387,7 +1387,7 @@ iso_stream_schedule ( unsigned mod = ehci->periodic_size << 3; struct ehci_iso_sched *sched = urb->hcpriv; - if (sched->span > (mod - 8 * SCHEDULE_SLOP)) { + if (sched->span > (mod - SCHEDULE_SLOP)) { ehci_dbg (ehci, "iso request %p too long\n", urb); status = -EFBIG; goto fail; @@ -1420,7 +1420,7 @@ iso_stream_schedule ( start += mod; /* Fell behind (by up to twice the slop amount)? */ - if (start >= max - 2 * 8 * SCHEDULE_SLOP) + if (start >= max - 2 * SCHEDULE_SLOP) start += period * DIV_ROUND_UP( max - start, period) - mod; @@ -1439,7 +1439,7 @@ iso_stream_schedule ( * can also help high bandwidth if the dma and irq loads don't * jump until after the queue is primed. */ - start = SCHEDULE_SLOP * 8 + (now & ~0x07); + start = SCHEDULE_SLOP + (now & ~0x07); start %= mod; stream->next_uframe = start; -- cgit v1.2.3 From 3c47c01a8f6243bfacfbf2c08273695e3db4d28c Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 27 Oct 2009 10:55:05 -0700 Subject: USB: ehci: Respect IST when scheduling new split iTDs. The EHCI specification says that an EHCI host controller may cache part of the isochronous schedule. The EHCI controller must advertise how much it caches in the schedule through the HCCPARAMS register isochronous scheduling threshold (IST) bits. In theory, adding new iTDs within the IST should be harmless. The HW will follow the old cached linked list and miss the new iTD. SW will notice HW missed the iTD and return 0 for the transfer length. However, Intel ICH9 chipsets (and some later chipsets) have issues when SW attempts to schedule a split transaction within the IST. All transfers will cease being sent out that port, and the drivers will see isochronous packets complete with a length of zero. Start of frames may or may not also disappear, causing the device to go into auto-suspend. This "bus stall" will continue until a control or bulk transfer is queued to a device under that roothub. Most drivers will never cause this behavior, because they use multiple URBs with multiple packets to keep the bus busy. If you limit the number of URBs to one, you may be able to hit this bug. Make sure the EHCI driver does not schedule full-speed transfers within the IST under an Intel chipset. Make sure that when we fall behind the current microframe plus IST, we schedule the new transfer at the next periodic interval after the IST. Don't change the scheduling for new transfers, since the schedule slop will always be greater than the IST. Allow high speed isochronous transfers to be scheduled within the IST, since this doesn't trigger the Intel chipset bug. Make sure that if the host caches the full frame, the EHCI driver's internal isochronous threshold (ehci->i_thresh) is set to 8 microframes + 2 microframes wiggle room. This is similar to what is done in the case where the host caches less than the full frame. Signed-off-by: Sarah Sharp Cc: Alan Stern Cc: David Brownell Cc: Clemens Ladisch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-sched.c | 30 ++++++++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index a7649b586a92..70c81fb9b22c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -548,7 +548,7 @@ static int ehci_init(struct usb_hcd *hcd) /* controllers may cache some of the periodic schedule ... */ hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); if (HCC_ISOC_CACHE(hcc_params)) // full frame cache - ehci->i_thresh = 8; + ehci->i_thresh = 2 + 8; else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 9fa5614b8ef6..470e8106c7d1 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1382,10 +1382,11 @@ iso_stream_schedule ( struct ehci_iso_stream *stream ) { - u32 now, start, max, period; + u32 now, next, start, period; int status; unsigned mod = ehci->periodic_size << 3; struct ehci_iso_sched *sched = urb->hcpriv; + struct pci_dev *pdev; if (sched->span > (mod - SCHEDULE_SLOP)) { ehci_dbg (ehci, "iso request %p too long\n", urb); @@ -1406,26 +1407,35 @@ iso_stream_schedule ( now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; - /* when's the last uframe this urb could start? */ - max = now + mod; - /* Typical case: reuse current schedule, stream is still active. * Hopefully there are no gaps from the host falling behind * (irq delays etc), but if there are we'll take the next * slot in the schedule, implicitly assuming URB_ISO_ASAP. */ if (likely (!list_empty (&stream->td_list))) { + pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller); start = stream->next_uframe; - if (start < now) - start += mod; + + /* For high speed devices, allow scheduling within the + * isochronous scheduling threshold. For full speed devices, + * don't. (Work around for Intel ICH9 bug.) + */ + if (!stream->highspeed && + pdev->vendor == PCI_VENDOR_ID_INTEL) + next = now + ehci->i_thresh; + else + next = now; /* Fell behind (by up to twice the slop amount)? */ - if (start >= max - 2 * SCHEDULE_SLOP) + if (((start - next) & (mod - 1)) >= + mod - 2 * SCHEDULE_SLOP) start += period * DIV_ROUND_UP( - max - start, period) - mod; + (next - start) & (mod - 1), + period); /* Tried to schedule too far into the future? */ - if (unlikely((start + sched->span) >= max)) { + if (unlikely(((start - now) & (mod - 1)) + sched->span + >= mod - 2 * SCHEDULE_SLOP)) { status = -EFBIG; goto fail; } @@ -1470,7 +1480,7 @@ iso_stream_schedule ( /* no room in the schedule */ ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n", list_empty (&stream->td_list) ? "" : "re", - urb, now, max); + urb, now, now + mod); status = -ENOSPC; fail: -- cgit v1.2.3 From 742937a24b6103efd7db3a38debf24157c19f385 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 27 Oct 2009 15:18:46 -0400 Subject: USB: don't use a fixed DMA mapping for hub status URBs This patch (as1296) gets rid of the fixed DMA-buffer mapping used by the hub driver for its status URB. This URB doesn't get used much -- mainly when a device is plugged in or unplugged -- so the dynamic mapping overhead is minimal. And most systems have many fewer external hubs than root hubs, which don't need a mapped buffer anyway. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2ac115015229..708c63826100 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -45,7 +45,6 @@ struct usb_hub { /* buffer for urb ... with extra space in case of babble */ char (*buffer)[8]; - dma_addr_t buffer_dma; /* DMA address for buffer */ union { struct usb_hub_status hub; struct usb_port_status port; @@ -869,8 +868,7 @@ static int hub_configure(struct usb_hub *hub, int maxp, ret; char *message = "out of memory"; - hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL, - &hub->buffer_dma); + hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL); if (!hub->buffer) { ret = -ENOMEM; goto fail; @@ -1111,8 +1109,6 @@ static int hub_configure(struct usb_hub *hub, usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval); - hub->urb->transfer_dma = hub->buffer_dma; - hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* maybe cycle the hub leds */ if (hub->has_indicators && blinkenlights) @@ -1162,8 +1158,7 @@ static void hub_disconnect(struct usb_interface *intf) kfree(hub->port_owners); kfree(hub->descriptor); kfree(hub->status); - usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer, - hub->buffer_dma); + kfree(hub->buffer); kref_put(&hub->kref, hub_release); } -- cgit v1.2.3 From c2f14071343339737ab4c8a1dab01df22ddfdf47 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 27 Oct 2009 15:20:13 -0400 Subject: USB: add a "remove hardware" sysfs attribute This patch (as1297) adds a "remove" attribute to each USB device's directory in sysfs. Writing to this attribute causes the device to be deconfigured (the same as writing 0 to the "bConfigurationValue" attribute) and then tells the hub driver to disable the device's upstream port. The device remains locked during these activities so there is no possibility of it getting reconfigured in between. The port will remain disabled until after the device is unplugged. The purpose of this is to provide a means for user programs to imitate the "Safely remove hardware" applet in Windows. Some devices do expect their ports to be disabled before they are unplugged, and they provide visual feedback to users indicating when they can safely be unplugged. The security implications are minimal. Writing to the "remove" attribute is no more dangerous than writing to the "bConfigurationValue" attribute. Signed-off-by: Alan Stern Cc: David Zeuthen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/core/sysfs.c | 23 ++++++++++++++++++++++ drivers/usb/core/usb.h | 1 + 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 708c63826100..5413d712cae0 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -60,6 +60,8 @@ struct usb_hub { status change */ unsigned long busy_bits[1]; /* ports being reset or resumed */ + unsigned long removed_bits[1]; /* ports with a "removed" + device present */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif @@ -635,6 +637,33 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) kick_khubd(hub); } +/** + * usb_remove_device - disable a device's port on its parent hub + * @udev: device to be disabled and removed + * Context: @udev locked, must be able to sleep. + * + * After @udev's port has been disabled, khubd is notified and it will + * see that the device has been disconnected. When the device is + * physically unplugged and something is plugged in, the events will + * be received and processed normally. + */ +int usb_remove_device(struct usb_device *udev) +{ + struct usb_hub *hub; + struct usb_interface *intf; + + if (!udev->parent) /* Can't remove a root hub */ + return -EINVAL; + hub = hdev_to_hub(udev->parent); + intf = to_usb_interface(hub->intfdev); + + usb_autopm_get_interface(intf); + set_bit(udev->portnum, hub->removed_bits); + hub_port_logical_disconnect(hub, udev->portnum); + usb_autopm_put_interface(intf); + return 0; +} + enum hub_activation_type { HUB_INIT, HUB_INIT2, HUB_INIT3, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME, @@ -730,6 +759,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) USB_PORT_FEAT_C_ENABLE); } + /* We can forget about a "removed" device when there's a + * physical disconnect or the connect status changes. + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + (portchange & USB_PORT_STAT_C_CONNECTION)) + clear_bit(port1, hub->removed_bits); + if (!udev || udev->state == USB_STATE_NOTATTACHED) { /* Tell khubd to disconnect the device or * check for a new connection @@ -2965,6 +3001,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, usb_disconnect(&hdev->children[port1-1]); clear_bit(port1, hub->change_bits); + /* We can forget about a "removed" device when there's a physical + * disconnect or the connect status changes. + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + (portchange & USB_PORT_STAT_C_CONNECTION)) + clear_bit(port1, hub->removed_bits); + if (portchange & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE)) { status = hub_port_debounce(hub, port1); @@ -2978,8 +3021,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } } - /* Return now if debouncing failed or nothing is connected */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) { + /* Return now if debouncing failed or nothing is connected or + * the device was "removed". + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + test_bit(port1, hub->removed_bits)) { /* maybe switch power back on (e.g. root hub was reset) */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 7ec3041ae79e..470e2413a9cf 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -508,6 +508,28 @@ static ssize_t usb_dev_authorized_store(struct device *dev, static DEVICE_ATTR(authorized, 0644, usb_dev_authorized_show, usb_dev_authorized_store); +/* "Safely remove a device" */ +static ssize_t usb_remove_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int rc = 0; + + usb_lock_device(udev); + if (udev->state != USB_STATE_NOTATTACHED) { + + /* To avoid races, first unconfigure and then remove */ + usb_set_configuration(udev, -1); + rc = usb_remove_device(udev); + } + if (rc == 0) + rc = count; + usb_unlock_device(udev); + return rc; +} +static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store); + static struct attribute *dev_attrs[] = { /* current configuration's attributes */ @@ -533,6 +555,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_maxchild.attr, &dev_attr_quirks.attr, &dev_attr_authorized.attr, + &dev_attr_remove.attr, NULL, }; static struct attribute_group dev_attr_grp = { diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 9a8b15e6377a..4c36c7f512a0 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -24,6 +24,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern int usb_deauthorize_device(struct usb_device *); extern int usb_authorize_device(struct usb_device *); extern void usb_detect_quirks(struct usb_device *udev); +extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); -- cgit v1.2.3 From 56cdc8e122f9e0c8392cc4ccfa13a72ca7a0dceb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 27 Oct 2009 15:26:50 -0400 Subject: USB: fix a bug in the scatter-gather library This patch (as1298) fixes a bug in the new scatter-gather URB facility. If an URB uses a scatterlist then it should not have the URB_NO_INTERRUPT flag set; otherwise the system won't be notified when the transfer completes. Signed-off-by: Alan Stern Acked-by: David Vrabel CC: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index feafd28a0428..72e7b3a43ef4 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -403,7 +403,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, if (!io->urbs) goto nomem; - urb_flags = URB_NO_INTERRUPT; + urb_flags = 0; if (dma) urb_flags |= URB_NO_TRANSFER_DMA_MAP; if (usb_pipein(pipe)) @@ -435,6 +435,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, io->urbs[0]->num_sgs = io->entries; io->entries = 1; } else { + urb_flags |= URB_NO_INTERRUPT; for_each_sg(sg, sg, io->entries, i) { unsigned len; -- cgit v1.2.3 From 5a0a4e37b5f5143f8db80893c2fe073c95976042 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 28 Oct 2009 01:14:59 +0100 Subject: USB: Add EHCI support for MX27 and MX31 based boards The Freescale MX27 and MX31 SoCs have a EHCI controller onboard. The controller is capable of USB on the go. This patch adds a driver to support all three of them. Users have to pass details about serial interface configuration in the platform data. The USB OTG core used here is the ARC core, so the driver should be renamed and probably be merged with ehci-fsl.c eventually. Signed-off-by: Sascha Hauer Signed-off-by: Daniel Mack Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- arch/arm/plat-mxc/Makefile | 1 + arch/arm/plat-mxc/ehci.c | 92 ++++++++++ arch/arm/plat-mxc/include/mach/mxc_ehci.h | 37 ++++ drivers/usb/Kconfig | 1 + drivers/usb/host/Kconfig | 7 + drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-mxc.c | 296 ++++++++++++++++++++++++++++++ 7 files changed, 439 insertions(+) create mode 100644 arch/arm/plat-mxc/ehci.c create mode 100644 arch/arm/plat-mxc/include/mach/mxc_ehci.h create mode 100644 drivers/usb/host/ehci-mxc.c diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile index e3212c8ff421..e35f9bd32624 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_ARCH_MX1) += iomux-mx1-mx2.o dma-mx1-mx2.o obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o dma-mx1-mx2.o obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o obj-$(CONFIG_MXC_PWM) += pwm.o +obj-$(CONFIG_USB_EHCI_MXC) += ehci.o diff --git a/arch/arm/plat-mxc/ehci.c b/arch/arm/plat-mxc/ehci.c new file mode 100644 index 000000000000..41599be882e8 --- /dev/null +++ b/arch/arm/plat-mxc/ehci.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009 Daniel Mack + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include +#include + +#define USBCTRL_OTGBASE_OFFSET 0x600 + +#define MX31_OTG_SIC_SHIFT 29 +#define MX31_OTG_SIC_MASK (0xf << MX31_OTG_SIC_SHIFT) +#define MX31_OTG_PM_BIT (1 << 24) + +#define MX31_H2_SIC_SHIFT 21 +#define MX31_H2_SIC_MASK (0xf << MX31_H2_SIC_SHIFT) +#define MX31_H2_PM_BIT (1 << 16) +#define MX31_H2_DT_BIT (1 << 5) + +#define MX31_H1_SIC_SHIFT 13 +#define MX31_H1_SIC_MASK (0xf << MX31_H1_SIC_SHIFT) +#define MX31_H1_PM_BIT (1 << 8) +#define MX31_H1_DT_BIT (1 << 4) + +int mxc_set_usbcontrol(int port, unsigned int flags) +{ + unsigned int v; + + if (cpu_is_mx31()) { + v = readl(IO_ADDRESS(MX31_OTG_BASE_ADDR + + USBCTRL_OTGBASE_OFFSET)); + + switch (port) { + case 0: /* OTG port */ + v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) + << MX31_OTG_SIC_SHIFT; + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v |= MX31_OTG_PM_BIT; + + break; + case 1: /* H1 port */ + v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) + << MX31_H1_SIC_SHIFT; + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v |= MX31_H1_PM_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX31_H1_DT_BIT; + + break; + case 2: /* H2 port */ + v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) + << MX31_H2_SIC_SHIFT; + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX31_H2_PM_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX31_H2_DT_BIT; + + break; + } + + writel(v, IO_ADDRESS(MX31_OTG_BASE_ADDR + + USBCTRL_OTGBASE_OFFSET)); + return 0; + } + + printk(KERN_WARNING + "%s() unable to setup USBCONTROL for this CPU\n", __func__); + return -EINVAL; +} +EXPORT_SYMBOL(mxc_set_usbcontrol); + diff --git a/arch/arm/plat-mxc/include/mach/mxc_ehci.h b/arch/arm/plat-mxc/include/mach/mxc_ehci.h new file mode 100644 index 000000000000..8f796239393e --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/mxc_ehci.h @@ -0,0 +1,37 @@ +#ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H +#define __INCLUDE_ASM_ARCH_MXC_EHCI_H + +/* values for portsc field */ +#define MXC_EHCI_PHY_LOW_POWER_SUSPEND (1 << 23) +#define MXC_EHCI_FORCE_FS (1 << 24) +#define MXC_EHCI_UTMI_8BIT (0 << 28) +#define MXC_EHCI_UTMI_16BIT (1 << 28) +#define MXC_EHCI_SERIAL (1 << 29) +#define MXC_EHCI_MODE_UTMI (0 << 30) +#define MXC_EHCI_MODE_PHILIPS (1 << 30) +#define MXC_EHCI_MODE_ULPI (2 << 30) +#define MXC_EHCI_MODE_SERIAL (3 << 30) + +/* values for flags field */ +#define MXC_EHCI_INTERFACE_DIFF_UNI (0 << 0) +#define MXC_EHCI_INTERFACE_DIFF_BI (1 << 0) +#define MXC_EHCI_INTERFACE_SINGLE_UNI (2 << 0) +#define MXC_EHCI_INTERFACE_SINGLE_BI (3 << 0) +#define MXC_EHCI_INTERFACE_MASK (0xf) + +#define MXC_EHCI_POWER_PINS_ENABLED (1 << 5) +#define MXC_EHCI_TTL_ENABLED (1 << 6) + +struct mxc_usbh_platform_data { + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); + + unsigned int portsc; + unsigned int flags; + struct otg_transceiver *otg; +}; + +int mxc_set_usbcontrol(int port, unsigned int flags); + +#endif /* __INCLUDE_ASM_ARCH_MXC_EHCI_H */ + diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 240750881d28..a3b7caf265d4 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -60,6 +60,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_IXP4XX default y if ARCH_W90X900 default y if ARCH_AT91SAM9G45 + default y if ARCH_MXC default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6d97e039cdbb..2678a1624fcc 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -116,6 +116,13 @@ config USB_EHCI_FSL ---help--- Variation of ARC USB block used in some Freescale chips. +config USB_EHCI_MXC + bool "Support for Freescale on-chip EHCI USB controller" + depends on USB_EHCI_HCD && ARCH_MXC + select USB_EHCI_ROOT_HUB_TT + ---help--- + Variation of ARC USB block used in some Freescale chips. + config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" depends on USB_EHCI_HCD && PPC_OF diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 70c81fb9b22c..0090f163b160 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1103,6 +1103,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_fsl_driver #endif +#ifdef CONFIG_USB_EHCI_MXC +#include "ehci-mxc.c" +#define PLATFORM_DRIVER ehci_mxc_driver +#endif + #ifdef CONFIG_SOC_AU1200 #include "ehci-au1xxx.c" #define PLATFORM_DRIVER ehci_hcd_au1xxx_driver diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c new file mode 100644 index 000000000000..35c56f40bdbb --- /dev/null +++ b/drivers/usb/host/ehci-mxc.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2008 Sascha Hauer , Pengutronix + * Copyright (c) 2009 Daniel Mack + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include + +#define ULPI_VIEWPORT_OFFSET 0x170 +#define PORTSC_OFFSET 0x184 +#define USBMODE_OFFSET 0x1a8 +#define USBMODE_CM_HOST 3 + +struct ehci_mxc_priv { + struct clk *usbclk, *ahbclk; + struct usb_hcd *hcd; +}; + +/* called during probe() after chip reset completes */ +static int ehci_mxc_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + hcd->has_tt = 1; + + ehci->sbrn = 0x20; + + ehci_reset(ehci); + + ehci_port_power(ehci, 0); + return 0; +} + +static const struct hc_driver ehci_mxc_hc_driver = { + .description = hcd_name, + .product_desc = "Freescale On-Chip EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = ehci_mxc_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int ehci_mxc_drv_probe(struct platform_device *pdev) +{ + struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; + struct usb_hcd *hcd; + struct resource *res; + int irq, ret, temp; + struct ehci_mxc_priv *priv; + struct device *dev = &pdev->dev; + + dev_info(&pdev->dev, "initializing i.MX USB Controller\n"); + + if (!pdata) { + dev_err(dev, "No platform data given, bailing out.\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + + hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev)); + if (!hcd) + return -ENOMEM; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto err_alloc; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Found HC with no register addr. Check setup!\n"); + ret = -ENODEV; + goto err_get_resource; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(dev, "controller already in use\n"); + ret = -EBUSY; + goto err_request_mem; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(dev, "error mapping memory\n"); + ret = -EFAULT; + goto err_ioremap; + } + + /* enable clocks */ + priv->usbclk = clk_get(dev, "usb"); + if (IS_ERR(priv->usbclk)) { + ret = PTR_ERR(priv->usbclk); + goto err_clk; + } + clk_enable(priv->usbclk); + + if (!cpu_is_mx35()) { + priv->ahbclk = clk_get(dev, "usb_ahb"); + if (IS_ERR(priv->ahbclk)) { + ret = PTR_ERR(priv->ahbclk); + goto err_clk_ahb; + } + clk_enable(priv->ahbclk); + } + + /* set USBMODE to host mode */ + temp = readl(hcd->regs + USBMODE_OFFSET); + writel(temp | USBMODE_CM_HOST, hcd->regs + USBMODE_OFFSET); + + /* set up the PORTSCx register */ + writel(pdata->portsc, hcd->regs + PORTSC_OFFSET); + mdelay(10); + + /* setup USBCONTROL. */ + ret = mxc_set_usbcontrol(pdev->id, pdata->flags); + if (ret < 0) + goto err_init; + + /* call platform specific init function */ + if (pdata->init) { + ret = pdata->init(pdev); + if (ret) { + dev_err(dev, "platform init failed\n"); + goto err_init; + } + } + + /* most platforms need some time to settle changed IO settings */ + mdelay(10); + + /* 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) + dev_err(dev, "unable to enable vbus on transceiver\n"); + } + + priv->hcd = hcd; + platform_set_drvdata(pdev, priv); + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (ret) + goto err_add; + + return 0; + +err_add: + if (pdata && pdata->exit) + pdata->exit(pdev); +err_init: + if (priv->ahbclk) { + clk_disable(priv->ahbclk); + clk_put(priv->ahbclk); + } +err_clk_ahb: + clk_disable(priv->usbclk); + clk_put(priv->usbclk); +err_clk: + iounmap(hcd->regs); +err_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_request_mem: +err_get_resource: + kfree(priv); +err_alloc: + usb_put_hcd(hcd); + return ret; +} + +static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) +{ + struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; + struct ehci_mxc_priv *priv = platform_get_drvdata(pdev); + struct usb_hcd *hcd = priv->hcd; + + if (pdata && pdata->exit) + pdata->exit(pdev); + + if (pdata->otg) + otg_shutdown(pdata->otg); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + clk_disable(priv->usbclk); + clk_put(priv->usbclk); + if (priv->ahbclk) { + clk_disable(priv->ahbclk); + clk_put(priv->ahbclk); + } + + kfree(priv); + + return 0; +} + +static void ehci_mxc_drv_shutdown(struct platform_device *pdev) +{ + struct ehci_mxc_priv *priv = platform_get_drvdata(pdev); + struct usb_hcd *hcd = priv->hcd; + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +MODULE_ALIAS("platform:mxc-ehci"); + +static struct platform_driver ehci_mxc_driver = { + .probe = ehci_mxc_drv_probe, + .remove = __exit_p(ehci_mxc_drv_remove), + .shutdown = ehci_mxc_drv_shutdown, + .driver = { + .name = "mxc-ehci", + }, +}; -- cgit v1.2.3 From 8845c05ecb5ba3c65d77757a2b4ef27eca96d6f7 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:14 +0100 Subject: USB: g_file_storage: parts of file_storage.c moved to separate file Moved parts of the file_storage.c file which do not reference fsg_dev structure to newly created storage_common.c file. dump_msg() and dump_cdb() have been changed to macros to remove fsg_dev reference. Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 572 +----------------------------------- drivers/usb/gadget/storage_common.c | 567 +++++++++++++++++++++++++++++++++++ 2 files changed, 572 insertions(+), 567 deletions(-) create mode 100644 drivers/usb/gadget/storage_common.c diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 1e6aa504d58a..332599c11987 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -298,46 +298,6 @@ MODULE_LICENSE("Dual BSD/GPL"); /*-------------------------------------------------------------------------*/ -#define LDBG(lun,fmt,args...) \ - dev_dbg(&(lun)->dev , fmt , ## args) -#define MDBG(fmt,args...) \ - pr_debug(DRIVER_NAME ": " fmt , ## args) - -#ifndef DEBUG -#undef VERBOSE_DEBUG -#undef DUMP_MSGS -#endif /* !DEBUG */ - -#ifdef VERBOSE_DEBUG -#define VLDBG LDBG -#else -#define VLDBG(lun,fmt,args...) \ - do { } while (0) -#endif /* VERBOSE_DEBUG */ - -#define LERROR(lun,fmt,args...) \ - dev_err(&(lun)->dev , fmt , ## args) -#define LWARN(lun,fmt,args...) \ - dev_warn(&(lun)->dev , fmt , ## args) -#define LINFO(lun,fmt,args...) \ - dev_info(&(lun)->dev , fmt , ## args) - -#define MINFO(fmt,args...) \ - pr_info(DRIVER_NAME ": " fmt , ## args) - -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARNING(d, fmt, args...) \ - dev_warn(&(d)->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->gadget->dev , fmt , ## args) - - -/*-------------------------------------------------------------------------*/ /* Encapsulate the module parameter settings */ @@ -425,125 +385,6 @@ MODULE_PARM_DESC(buflen, "I/O buffer size"); #endif /* CONFIG_USB_FILE_STORAGE_TEST */ -/*-------------------------------------------------------------------------*/ - -/* SCSI device types */ -#define TYPE_DISK 0x00 -#define TYPE_CDROM 0x05 - -/* USB protocol value = the transport method */ -#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt -#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt -#define USB_PR_BULK 0x50 // Bulk-only - -/* USB subclass value = the protocol encapsulation */ -#define USB_SC_RBC 0x01 // Reduced Block Commands (flash) -#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM) -#define USB_SC_QIC 0x03 // QIC-157 (tape) -#define USB_SC_UFI 0x04 // UFI (floppy) -#define USB_SC_8070 0x05 // SFF-8070i (removable) -#define USB_SC_SCSI 0x06 // Transparent SCSI - -/* Bulk-only data structures */ - -/* Command Block Wrapper */ -struct bulk_cb_wrap { - __le32 Signature; // Contains 'USBC' - u32 Tag; // Unique per command id - __le32 DataTransferLength; // Size of the data - u8 Flags; // Direction in bit 7 - u8 Lun; // LUN (normally 0) - u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE - u8 CDB[16]; // Command Data Block -}; - -#define USB_BULK_CB_WRAP_LEN 31 -#define USB_BULK_CB_SIG 0x43425355 // Spells out USBC -#define USB_BULK_IN_FLAG 0x80 - -/* Command Status Wrapper */ -struct bulk_cs_wrap { - __le32 Signature; // Should = 'USBS' - u32 Tag; // Same as original command - __le32 Residue; // Amount not transferred - u8 Status; // See below -}; - -#define USB_BULK_CS_WRAP_LEN 13 -#define USB_BULK_CS_SIG 0x53425355 // Spells out 'USBS' -#define USB_STATUS_PASS 0 -#define USB_STATUS_FAIL 1 -#define USB_STATUS_PHASE_ERROR 2 - -/* Bulk-only class specific requests */ -#define USB_BULK_RESET_REQUEST 0xff -#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe - - -/* CBI Interrupt data structure */ -struct interrupt_data { - u8 bType; - u8 bValue; -}; - -#define CBI_INTERRUPT_DATA_LEN 2 - -/* CBI Accept Device-Specific Command request */ -#define USB_CBI_ADSC_REQUEST 0x00 - - -#define MAX_COMMAND_SIZE 16 // Length of a SCSI Command Data Block - -/* SCSI commands that we recognize */ -#define SC_FORMAT_UNIT 0x04 -#define SC_INQUIRY 0x12 -#define SC_MODE_SELECT_6 0x15 -#define SC_MODE_SELECT_10 0x55 -#define SC_MODE_SENSE_6 0x1a -#define SC_MODE_SENSE_10 0x5a -#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e -#define SC_READ_6 0x08 -#define SC_READ_10 0x28 -#define SC_READ_12 0xa8 -#define SC_READ_CAPACITY 0x25 -#define SC_READ_FORMAT_CAPACITIES 0x23 -#define SC_READ_HEADER 0x44 -#define SC_READ_TOC 0x43 -#define SC_RELEASE 0x17 -#define SC_REQUEST_SENSE 0x03 -#define SC_RESERVE 0x16 -#define SC_SEND_DIAGNOSTIC 0x1d -#define SC_START_STOP_UNIT 0x1b -#define SC_SYNCHRONIZE_CACHE 0x35 -#define SC_TEST_UNIT_READY 0x00 -#define SC_VERIFY 0x2f -#define SC_WRITE_6 0x0a -#define SC_WRITE_10 0x2a -#define SC_WRITE_12 0xaa - -/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ -#define SS_NO_SENSE 0 -#define SS_COMMUNICATION_FAILURE 0x040800 -#define SS_INVALID_COMMAND 0x052000 -#define SS_INVALID_FIELD_IN_CDB 0x052400 -#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 -#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 -#define SS_MEDIUM_NOT_PRESENT 0x023a00 -#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 -#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 -#define SS_RESET_OCCURRED 0x062900 -#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 -#define SS_UNRECOVERED_READ_ERROR 0x031100 -#define SS_WRITE_ERROR 0x030c02 -#define SS_WRITE_PROTECTED 0x072700 - -#define SK(x) ((u8) ((x) >> 16)) // Sense Key byte, etc. -#define ASC(x) ((u8) ((x) >> 8)) -#define ASCQ(x) ((u8) (x)) - - -/*-------------------------------------------------------------------------*/ - /* * These definitions will permit the compiler to avoid generating code for * parts of the driver that aren't used in the non-TEST version. Even gcc @@ -566,81 +407,12 @@ struct interrupt_data { #endif /* CONFIG_USB_FILE_STORAGE_TEST */ -struct lun { - struct file *filp; - loff_t file_length; - loff_t num_sectors; - - unsigned int ro : 1; - unsigned int prevent_medium_removal : 1; - unsigned int registered : 1; - unsigned int info_valid : 1; - - u32 sense_data; - u32 sense_data_info; - u32 unit_attention_data; - - struct device dev; -}; - -#define backing_file_is_open(curlun) ((curlun)->filp != NULL) - -static struct lun *dev_to_lun(struct device *dev) -{ - return container_of(dev, struct lun, dev); -} - - -/* Big enough to hold our biggest descriptor */ -#define EP0_BUFSIZE 256 -#define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value - -/* Number of buffers we will use. 2 is enough for double-buffering */ -#define NUM_BUFFERS 2 - -enum fsg_buffer_state { - BUF_STATE_EMPTY = 0, - BUF_STATE_FULL, - BUF_STATE_BUSY -}; - -struct fsg_buffhd { - void *buf; - enum fsg_buffer_state state; - struct fsg_buffhd *next; - - /* The NetChip 2280 is faster, and handles some protocol faults - * better, if we don't submit any short bulk-out read requests. - * So we will record the intended request length here. */ - unsigned int bulk_out_intended_length; +/*-------------------------------------------------------------------------*/ - struct usb_request *inreq; - int inreq_busy; - struct usb_request *outreq; - int outreq_busy; -}; +#include "storage_common.c" -enum fsg_state { - FSG_STATE_COMMAND_PHASE = -10, // This one isn't used anywhere - FSG_STATE_DATA_PHASE, - FSG_STATE_STATUS_PHASE, - - FSG_STATE_IDLE = 0, - FSG_STATE_ABORT_BULK_OUT, - FSG_STATE_RESET, - FSG_STATE_INTERFACE_CHANGE, - FSG_STATE_CONFIG_CHANGE, - FSG_STATE_DISCONNECT, - FSG_STATE_EXIT, - FSG_STATE_TERMINATED -}; +/*-------------------------------------------------------------------------*/ -enum data_direction { - DATA_DIR_UNKNOWN = 0, - DATA_DIR_FROM_HOST, - DATA_DIR_TO_HOST, - DATA_DIR_NONE -}; struct fsg_dev { /* lock protects: state, all the req_busy's, and cbbuf_cmnd */ @@ -739,49 +511,9 @@ static void set_bulk_out_req_length(struct fsg_dev *fsg, static struct fsg_dev *the_fsg; static struct usb_gadget_driver fsg_driver; -static void close_backing_file(struct lun *curlun); - /*-------------------------------------------------------------------------*/ -#ifdef DUMP_MSGS - -static void dump_msg(struct fsg_dev *fsg, const char *label, - const u8 *buf, unsigned int length) -{ - if (length < 512) { - DBG(fsg, "%s, length %u:\n", label, length); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, - 16, 1, buf, length, 0); - } -} - -static void dump_cdb(struct fsg_dev *fsg) -{} - -#else - -static void dump_msg(struct fsg_dev *fsg, const char *label, - const u8 *buf, unsigned int length) -{} - -#ifdef VERBOSE_DEBUG - -static void dump_cdb(struct fsg_dev *fsg) -{ - print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, - 16, 1, fsg->cmnd, fsg->cmnd_size, 0); -} - -#else - -static void dump_cdb(struct fsg_dev *fsg) -{} - -#endif /* VERBOSE_DEBUG */ -#endif /* DUMP_MSGS */ - - static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) { const char *name; @@ -797,16 +529,6 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) } -/*-------------------------------------------------------------------------*/ - -/* Routines for unaligned data access */ - -static u32 get_unaligned_be24(u8 *buf) -{ - return 0xffffff & (u32) get_unaligned_be32(buf - 1); -} - - /*-------------------------------------------------------------------------*/ /* @@ -814,11 +536,6 @@ static u32 get_unaligned_be24(u8 *buf) * descriptors are built on demand. Also the (static) config and interface * descriptors are adjusted during fsg_bind(). */ -#define STRING_MANUFACTURER 1 -#define STRING_PRODUCT 2 -#define STRING_SERIAL 3 -#define STRING_CONFIG 4 -#define STRING_INTERFACE 5 /* There is only one configuration. */ #define CONFIG_VALUE 1 @@ -855,81 +572,7 @@ config_desc = { .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, }; -static struct usb_otg_descriptor -otg_desc = { - .bLength = sizeof(otg_desc), - .bDescriptorType = USB_DT_OTG, - - .bmAttributes = USB_OTG_SRP, -}; - -/* There is only one interface. */ - -static struct usb_interface_descriptor -intf_desc = { - .bLength = sizeof intf_desc, - .bDescriptorType = USB_DT_INTERFACE, - - .bNumEndpoints = 2, // Adjusted during fsg_bind() - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind() - .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind() - .iInterface = STRING_INTERFACE, -}; - -/* Three full-speed endpoint descriptors: bulk-in, bulk-out, - * and interrupt-in. */ - -static struct usb_endpoint_descriptor -fs_bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - /* wMaxPacketSize set by autoconfiguration */ -}; -static struct usb_endpoint_descriptor -fs_bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - /* wMaxPacketSize set by autoconfiguration */ -}; - -static struct usb_endpoint_descriptor -fs_intr_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(2), - .bInterval = 32, // frames -> 32 ms -}; - -static const struct usb_descriptor_header *fs_function[] = { - (struct usb_descriptor_header *) &otg_desc, - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &fs_bulk_in_desc, - (struct usb_descriptor_header *) &fs_bulk_out_desc, - (struct usb_descriptor_header *) &fs_intr_in_desc, - NULL, -}; -#define FS_FUNCTION_PRE_EP_ENTRIES 2 - - -/* - * USB 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - * - * That means alternate endpoint descriptors (bigger packets) - * and a "device qualifier" ... plus more construction options - * for the config descriptor. - */ static struct usb_qualifier_descriptor dev_qualifier = { .bLength = sizeof dev_qualifier, @@ -941,78 +584,6 @@ dev_qualifier = { .bNumConfigurations = 1, }; -static struct usb_endpoint_descriptor -hs_bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor -hs_bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), - .bInterval = 1, // NAK every 1 uframe -}; - -static struct usb_endpoint_descriptor -hs_intr_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(2), - .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms -}; - -static const struct usb_descriptor_header *hs_function[] = { - (struct usb_descriptor_header *) &otg_desc, - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &hs_bulk_in_desc, - (struct usb_descriptor_header *) &hs_bulk_out_desc, - (struct usb_descriptor_header *) &hs_intr_in_desc, - NULL, -}; -#define HS_FUNCTION_PRE_EP_ENTRIES 2 - -/* Maxpacket and other transfer characteristics vary by speed. */ -static struct usb_endpoint_descriptor * -ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} - - -/* The CBI specification limits the serial string to 12 uppercase hexadecimal - * characters. */ -static char manufacturer[64]; -static char serial[13]; - -/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ -static struct usb_string strings[] = { - {STRING_MANUFACTURER, manufacturer}, - {STRING_PRODUCT, longname}, - {STRING_SERIAL, serial}, - {STRING_CONFIG, "Self-powered"}, - {STRING_INTERFACE, "Mass Storage"}, - {} -}; - -static struct usb_gadget_strings stringtab = { - .language = 0x0409, // en-us - .strings = strings, -}; /* @@ -1864,25 +1435,6 @@ static int do_write(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -/* Sync the file data, don't bother with the metadata. - * This code was copied from fs/buffer.c:sys_fdatasync(). */ -static int fsync_sub(struct lun *curlun) -{ - struct file *filp = curlun->filp; - - if (curlun->ro || !filp) - return 0; - return vfs_fsync(filp, filp->f_path.dentry, 1); -} - -static void fsync_all(struct fsg_dev *fsg) -{ - int i; - - for (i = 0; i < fsg->nluns; ++i) - fsync_sub(&fsg->luns[i]); -} - static int do_synchronize_cache(struct fsg_dev *fsg) { struct lun *curlun = fsg->curlun; @@ -2113,24 +1665,6 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) } -static void store_cdrom_address(u8 *dest, int msf, u32 addr) -{ - if (msf) { - /* Convert to Minutes-Seconds-Frames */ - addr >>= 2; /* Convert to 2048-byte frames */ - addr += 2*75; /* Lead-in occupies 2 seconds */ - dest[3] = addr % 75; /* Frames */ - addr /= 75; - dest[2] = addr % 60; /* Seconds */ - addr /= 60; - dest[1] = addr; /* Minutes */ - dest[0] = 0; /* Reserved */ - } else { - /* Absolute sector */ - put_unaligned_be32(addr, dest); - } -} - static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) { struct lun *curlun = fsg->curlun; @@ -3506,7 +3040,8 @@ static void handle_exception(struct fsg_dev *fsg) break; case FSG_STATE_DISCONNECT: - fsync_all(fsg); + for (i = 0; i < fsg->nluns; ++i) + fsync_sub(fsg->luns + i); do_set_config(fsg, 0); // Unconfigured state break; @@ -3595,103 +3130,6 @@ static int fsg_main_thread(void *fsg_) /*-------------------------------------------------------------------------*/ -/* If the next two routines are called while the gadget is registered, - * the caller must own fsg->filesem for writing. */ - -static int open_backing_file(struct lun *curlun, const char *filename) -{ - int ro; - struct file *filp = NULL; - int rc = -EINVAL; - struct inode *inode = NULL; - loff_t size; - loff_t num_sectors; - loff_t min_sectors; - - /* R/W if we can, R/O if we must */ - ro = curlun->ro; - if (!ro) { - filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); - if (-EROFS == PTR_ERR(filp)) - ro = 1; - } - if (ro) - filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); - if (IS_ERR(filp)) { - LINFO(curlun, "unable to open backing file: %s\n", filename); - return PTR_ERR(filp); - } - - if (!(filp->f_mode & FMODE_WRITE)) - ro = 1; - - if (filp->f_path.dentry) - inode = filp->f_path.dentry->d_inode; - if (inode && S_ISBLK(inode->i_mode)) { - if (bdev_read_only(inode->i_bdev)) - ro = 1; - } else if (!inode || !S_ISREG(inode->i_mode)) { - LINFO(curlun, "invalid file type: %s\n", filename); - goto out; - } - - /* If we can't read the file, it's no good. - * If we can't write the file, use it read-only. */ - if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { - LINFO(curlun, "file not readable: %s\n", filename); - goto out; - } - if (!(filp->f_op->write || filp->f_op->aio_write)) - ro = 1; - - size = i_size_read(inode->i_mapping->host); - if (size < 0) { - LINFO(curlun, "unable to find file size: %s\n", filename); - rc = (int) size; - goto out; - } - num_sectors = size >> 9; // File size in 512-byte blocks - min_sectors = 1; - if (mod_data.cdrom) { - num_sectors &= ~3; // Reduce to a multiple of 2048 - min_sectors = 300*4; // Smallest track is 300 frames - if (num_sectors >= 256*60*75*4) { - num_sectors = (256*60*75 - 1) * 4; - LINFO(curlun, "file too big: %s\n", filename); - LINFO(curlun, "using only first %d blocks\n", - (int) num_sectors); - } - } - if (num_sectors < min_sectors) { - LINFO(curlun, "file too small: %s\n", filename); - rc = -ETOOSMALL; - goto out; - } - - get_file(filp); - curlun->ro = ro; - curlun->filp = filp; - curlun->file_length = size; - curlun->num_sectors = num_sectors; - LDBG(curlun, "open backing file: %s\n", filename); - rc = 0; - -out: - filp_close(filp, current->files); - return rc; -} - - -static void close_backing_file(struct lun *curlun) -{ - if (curlun->filp) { - LDBG(curlun, "close backing file\n"); - fput(curlun->filp); - curlun->filp = NULL; - } -} - - static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char *buf) { struct lun *curlun = dev_to_lun(dev); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c new file mode 100644 index 000000000000..fa86fdaafc1d --- /dev/null +++ b/drivers/usb/gadget/storage_common.c @@ -0,0 +1,567 @@ +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + */ + + + +/*-------------------------------------------------------------------------*/ + + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args) +#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) +#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) +#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) + +#define DBG(d, fmt, args...) dev_dbg (&(d)->gadget->dev, fmt, ## args) +#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev, fmt, ## args) +#define ERROR(d, fmt, args...) dev_err (&(d)->gadget->dev, fmt, ## args) +#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev, fmt, ## args) +#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev, fmt, ## args) + + + +#ifdef DUMP_MSGS + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +#define dump_cdb(fsg) \ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + + + + + +/*-------------------------------------------------------------------------*/ + +/* SCSI device types */ +#define TYPE_DISK 0x00 +#define TYPE_CDROM 0x05 + +/* USB protocol value = the transport method */ +#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt +#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt +#define USB_PR_BULK 0x50 // Bulk-only + +/* USB subclass value = the protocol encapsulation */ +#define USB_SC_RBC 0x01 // Reduced Block Commands (flash) +#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM) +#define USB_SC_QIC 0x03 // QIC-157 (tape) +#define USB_SC_UFI 0x04 // UFI (floppy) +#define USB_SC_8070 0x05 // SFF-8070i (removable) +#define USB_SC_SCSI 0x06 // Transparent SCSI + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct bulk_cb_wrap { + __le32 Signature; // Contains 'USBC' + u32 Tag; // Unique per command id + __le32 DataTransferLength; // Size of the data + u8 Flags; // Direction in bit 7 + u8 Lun; // LUN (normally 0) + u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE + u8 CDB[16]; // Command Data Block +}; + +#define USB_BULK_CB_WRAP_LEN 31 +#define USB_BULK_CB_SIG 0x43425355 // Spells out USBC +#define USB_BULK_IN_FLAG 0x80 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { + __le32 Signature; // Should = 'USBS' + u32 Tag; // Same as original command + __le32 Residue; // Amount not transferred + u8 Status; // See below +}; + +#define USB_BULK_CS_WRAP_LEN 13 +#define USB_BULK_CS_SIG 0x53425355 // Spells out 'USBS' +#define USB_STATUS_PASS 0 +#define USB_STATUS_FAIL 1 +#define USB_STATUS_PHASE_ERROR 2 + +/* Bulk-only class specific requests */ +#define USB_BULK_RESET_REQUEST 0xff +#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe + + +/* CBI Interrupt data structure */ +struct interrupt_data { + u8 bType; + u8 bValue; +}; + +#define CBI_INTERRUPT_DATA_LEN 2 + +/* CBI Accept Device-Specific Command request */ +#define USB_CBI_ADSC_REQUEST 0x00 + + +#define MAX_COMMAND_SIZE 16 // Length of a SCSI Command Data Block + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT 0x04 +#define SC_INQUIRY 0x12 +#define SC_MODE_SELECT_6 0x15 +#define SC_MODE_SELECT_10 0x55 +#define SC_MODE_SENSE_6 0x1a +#define SC_MODE_SENSE_10 0x5a +#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_12 0xa8 +#define SC_READ_CAPACITY 0x25 +#define SC_READ_FORMAT_CAPACITIES 0x23 +#define SC_READ_HEADER 0x44 +#define SC_READ_TOC 0x43 +#define SC_RELEASE 0x17 +#define SC_REQUEST_SENSE 0x03 +#define SC_RESERVE 0x16 +#define SC_SEND_DIAGNOSTIC 0x1d +#define SC_START_STOP_UNIT 0x1b +#define SC_SYNCHRONIZE_CACHE 0x35 +#define SC_TEST_UNIT_READY 0x00 +#define SC_VERIFY 0x2f +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a +#define SC_WRITE_12 0xaa + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) // Sense Key byte, etc. +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + + +/*-------------------------------------------------------------------------*/ + + +struct lun { + struct file *filp; + loff_t file_length; + loff_t num_sectors; + + unsigned int ro : 1; + unsigned int prevent_medium_removal : 1; + unsigned int registered : 1; + unsigned int info_valid : 1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + struct device dev; +}; + +#define backing_file_is_open(curlun) ((curlun)->filp != NULL) + +static struct lun *dev_to_lun(struct device *dev) +{ + return container_of(dev, struct lun, dev); +} + + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE 256 +#define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value + +/* Number of buffers we will use. 2 is enough for double-buffering */ +#define NUM_BUFFERS 2 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +struct fsg_buffhd { + void *buf; + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsg_state { + FSG_STATE_COMMAND_PHASE = -10, // This one isn't used anywhere + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + + +/*-------------------------------------------------------------------------*/ + + +static inline u32 get_unaligned_be24(u8 *buf) +{ + return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + + +/*-------------------------------------------------------------------------*/ + + +#define STRING_MANUFACTURER 1 +#define STRING_PRODUCT 2 +#define STRING_SERIAL 3 +#define STRING_CONFIG 4 +#define STRING_INTERFACE 5 + + +static struct usb_otg_descriptor +otg_desc = { + .bLength = sizeof(otg_desc), + .bDescriptorType = USB_DT_OTG, + + .bmAttributes = USB_OTG_SRP, +}; + +/* There is only one interface. */ + +static struct usb_interface_descriptor +intf_desc = { + .bLength = sizeof intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, // Adjusted during fsg_bind() + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind() + .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind() + .iInterface = STRING_INTERFACE, +}; + +/* Three full-speed endpoint descriptors: bulk-in, bulk-out, + * and interrupt-in. */ + +static struct usb_endpoint_descriptor +fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +static struct usb_endpoint_descriptor +fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +static struct usb_endpoint_descriptor +fs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 32, // frames -> 32 ms +}; + +static const struct usb_descriptor_header *fs_function[] = { + (struct usb_descriptor_header *) &otg_desc, + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &fs_bulk_in_desc, + (struct usb_descriptor_header *) &fs_bulk_out_desc, + (struct usb_descriptor_header *) &fs_intr_in_desc, + NULL, +}; +#define FS_FUNCTION_PRE_EP_ENTRIES 2 + + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the config descriptor. + */ +static struct usb_endpoint_descriptor +hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor +hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 1, // NAK every 1 uframe +}; + +static struct usb_endpoint_descriptor +hs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms +}; + +static const struct usb_descriptor_header *hs_function[] = { + (struct usb_descriptor_header *) &otg_desc, + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &hs_bulk_in_desc, + (struct usb_descriptor_header *) &hs_bulk_out_desc, + (struct usb_descriptor_header *) &hs_intr_in_desc, + NULL, +}; +#define HS_FUNCTION_PRE_EP_ENTRIES 2 + +/* Maxpacket and other transfer characteristics vary by speed. */ +static struct usb_endpoint_descriptor * +ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + + +/* The CBI specification limits the serial string to 12 uppercase hexadecimal + * characters. */ +static char manufacturer[64]; +static char serial[13]; + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string strings[] = { + {STRING_MANUFACTURER, manufacturer}, + {STRING_PRODUCT, longname}, + {STRING_SERIAL, serial}, + {STRING_CONFIG, "Self-powered"}, + {STRING_INTERFACE, "Mass Storage"}, + {} +}; + +static struct usb_gadget_strings stringtab = { + .language = 0x0409, // en-us + .strings = strings, +}; + + + /*-------------------------------------------------------------------------*/ + +/* If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. */ + +static int open_backing_file(struct lun *curlun, const char *filename) +{ + int ro; + struct file *filp = NULL; + int rc = -EINVAL; + struct inode *inode = NULL; + loff_t size; + loff_t num_sectors; + loff_t min_sectors; + + /* R/W if we can, R/O if we must */ + ro = curlun->ro; + if (!ro) { + filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); + if (-EROFS == PTR_ERR(filp)) + ro = 1; + } + if (ro) + filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR(filp)) { + LINFO(curlun, "unable to open backing file: %s\n", filename); + return PTR_ERR(filp); + } + + if (!(filp->f_mode & FMODE_WRITE)) + ro = 1; + + if (filp->f_path.dentry) + inode = filp->f_path.dentry->d_inode; + if (inode && S_ISBLK(inode->i_mode)) { + if (bdev_read_only(inode->i_bdev)) + ro = 1; + } else if (!inode || !S_ISREG(inode->i_mode)) { + LINFO(curlun, "invalid file type: %s\n", filename); + goto out; + } + + /* If we can't read the file, it's no good. + * If we can't write the file, use it read-only. */ + if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { + LINFO(curlun, "file not readable: %s\n", filename); + goto out; + } + if (!(filp->f_op->write || filp->f_op->aio_write)) + ro = 1; + + size = i_size_read(inode->i_mapping->host); + if (size < 0) { + LINFO(curlun, "unable to find file size: %s\n", filename); + rc = (int) size; + goto out; + } + num_sectors = size >> 9; // File size in 512-byte blocks + min_sectors = 1; + if (mod_data.cdrom) { + num_sectors &= ~3; // Reduce to a multiple of 2048 + min_sectors = 300*4; // Smallest track is 300 frames + if (num_sectors >= 256*60*75*4) { + num_sectors = (256*60*75 - 1) * 4; + LINFO(curlun, "file too big: %s\n", filename); + LINFO(curlun, "using only first %d blocks\n", + (int) num_sectors); + } + } + if (num_sectors < min_sectors) { + LINFO(curlun, "file too small: %s\n", filename); + rc = -ETOOSMALL; + goto out; + } + + get_file(filp); + curlun->ro = ro; + curlun->filp = filp; + curlun->file_length = size; + curlun->num_sectors = num_sectors; + LDBG(curlun, "open backing file: %s\n", filename); + rc = 0; + +out: + filp_close(filp, current->files); + return rc; +} + + +static void close_backing_file(struct lun *curlun) +{ + if (curlun->filp) { + LDBG(curlun, "close backing file\n"); + fput(curlun->filp); + curlun->filp = NULL; + } +} + + +/*-------------------------------------------------------------------------*/ + +/* Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). */ +static int fsync_sub(struct lun *curlun) +{ + struct file *filp = curlun->filp; + + if (curlun->ro || !filp) + return 0; + return vfs_fsync(filp, filp->f_path.dentry, 1); +} + +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_unaligned_be32(addr, dest); + } +} -- cgit v1.2.3 From 69f9afed81a8aff23b54b0770579d5875191a048 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:15 +0100 Subject: USB: g_file_storage: "fsg_" prefix added to some identifiers Prefixed some identifiers that were defined in storage_common.c file with "fsg_". Not all identifiers were prefixed but the ones that are most likely to produce conflicts when used with other USB functions. Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 203 ++++++++++++++++++------------------ drivers/usb/gadget/storage_common.c | 132 +++++++++++++---------- 2 files changed, 183 insertions(+), 152 deletions(-) diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 332599c11987..90233f4f3601 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -248,8 +248,6 @@ #include #include -#include - #include #include @@ -274,8 +272,11 @@ #define DRIVER_NAME "g_file_storage" #define DRIVER_VERSION "20 November 2008" -static const char longname[] = DRIVER_DESC; -static const char shortname[] = DRIVER_NAME; +static char fsg_string_manufacturer[64]; +static const char fsg_string_product[] = DRIVER_DESC; +static char fsg_string_serial[13]; +static const char fsg_string_config[] = "Self-powered"; +static const char fsg_string_interface[] = "Mass Storage"; MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Alan Stern"); @@ -285,8 +286,8 @@ MODULE_LICENSE("Dual BSD/GPL"); * * DO NOT REUSE THESE IDs with any other driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ -#define DRIVER_VENDOR_ID 0x0525 // NetChip -#define DRIVER_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget +#define FSG_VENDOR_ID 0x0525 // NetChip +#define FSG_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget /* @@ -301,11 +302,11 @@ MODULE_LICENSE("Dual BSD/GPL"); /* Encapsulate the module parameter settings */ -#define MAX_LUNS 8 +#define FSG_MAX_LUNS 8 static struct { - char *file[MAX_LUNS]; - int ro[MAX_LUNS]; + char *file[FSG_MAX_LUNS]; + int ro[FSG_MAX_LUNS]; unsigned int num_filenames; unsigned int num_ros; unsigned int nluns; @@ -332,8 +333,8 @@ static struct { .removable = 0, .can_stall = 1, .cdrom = 0, - .vendor = DRIVER_VENDOR_ID, - .product = DRIVER_PRODUCT_ID, + .vendor = FSG_VENDOR_ID, + .product = FSG_PRODUCT_ID, .release = 0xffff, // Use controller chip type .buflen = 16384, }; @@ -434,7 +435,7 @@ struct fsg_dev { int intreq_busy; struct fsg_buffhd *intr_buffhd; - unsigned int bulk_out_maxpacket; + unsigned int bulk_out_maxpacket; enum fsg_state state; // For exception handling unsigned int exception_req_tag; @@ -459,7 +460,7 @@ struct fsg_dev { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[NUM_BUFFERS]; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; int thread_wakeup_needed; struct completion thread_notifier; @@ -484,8 +485,8 @@ struct fsg_dev { u8 cbbuf_cmnd[MAX_COMMAND_SIZE]; unsigned int nluns; - struct lun *luns; - struct lun *curlun; + struct fsg_lun *luns; + struct fsg_lun *curlun; }; typedef void (*fsg_routine_t)(struct fsg_dev *); @@ -549,13 +550,13 @@ device_desc = { .bDeviceClass = USB_CLASS_PER_INTERFACE, /* The next three values can be overridden by module parameters */ - .idVendor = cpu_to_le16(DRIVER_VENDOR_ID), - .idProduct = cpu_to_le16(DRIVER_PRODUCT_ID), + .idVendor = cpu_to_le16(FSG_VENDOR_ID), + .idProduct = cpu_to_le16(FSG_PRODUCT_ID), .bcdDevice = cpu_to_le16(0xffff), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIAL, + .iManufacturer = FSG_STRING_MANUFACTURER, + .iProduct = FSG_STRING_PRODUCT, + .iSerialNumber = FSG_STRING_SERIAL, .bNumConfigurations = 1, }; @@ -567,7 +568,7 @@ config_desc = { /* wTotalLength computed by usb_gadget_config_buf() */ .bNumInterfaces = 1, .bConfigurationValue = CONFIG_VALUE, - .iConfiguration = STRING_CONFIG, + .iConfiguration = FSG_STRING_CONFIG, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, }; @@ -604,9 +605,9 @@ static int populate_config_buf(struct usb_gadget *gadget, if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) - function = hs_function; + function = fsg_hs_function; else - function = fs_function; + function = fsg_fs_function; /* for now, don't advertise srp-only devices */ if (!gadget_is_otg(gadget)) @@ -957,7 +958,7 @@ get_config: VDBG(fsg, "get string descriptor\n"); /* wIndex == language code */ - value = usb_gadget_get_string(&stringtab, + value = usb_gadget_get_string(&fsg_stringtab, w_value & 0xff, req->buf); break; } @@ -1122,7 +1123,7 @@ static int sleep_thread(struct fsg_dev *fsg) static int do_read(struct fsg_dev *fsg) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; u32 lba; struct fsg_buffhd *bh; int rc; @@ -1248,7 +1249,7 @@ static int do_read(struct fsg_dev *fsg) static int do_write(struct fsg_dev *fsg) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; u32 lba; struct fsg_buffhd *bh; int get_some_more; @@ -1437,12 +1438,12 @@ static int do_write(struct fsg_dev *fsg) static int do_synchronize_cache(struct fsg_dev *fsg) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; int rc; /* We ignore the requested LBA and write out all file's * dirty data buffers. */ - rc = fsync_sub(curlun); + rc = fsg_lun_fsync_sub(curlun); if (rc) curlun->sense_data = SS_WRITE_ERROR; return 0; @@ -1451,7 +1452,7 @@ static int do_synchronize_cache(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static void invalidate_sub(struct lun *curlun) +static void invalidate_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; struct inode *inode = filp->f_path.dentry->d_inode; @@ -1463,7 +1464,7 @@ static void invalidate_sub(struct lun *curlun) static int do_verify(struct fsg_dev *fsg) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; u32 lba; u32 verification_length; struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; @@ -1496,7 +1497,7 @@ static int do_verify(struct fsg_dev *fsg) file_offset = ((loff_t) lba) << 9; /* Write out all the dirty buffers before invalidating them */ - fsync_sub(curlun); + fsg_lun_fsync_sub(curlun); if (signal_pending(current)) return -EINTR; @@ -1593,7 +1594,7 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; u8 *buf = (u8 *) bh->buf; u32 sd, sdinfo; int valid; @@ -1647,7 +1648,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; u32 lba = get_unaligned_be32(&fsg->cmnd[2]); int pmi = fsg->cmnd[8]; u8 *buf = (u8 *) bh->buf; @@ -1667,7 +1668,7 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; int msf = fsg->cmnd[1] & 0x02; u32 lba = get_unaligned_be32(&fsg->cmnd[2]); u8 *buf = (u8 *) bh->buf; @@ -1690,7 +1691,7 @@ static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; int msf = fsg->cmnd[1] & 0x02; int start_track = fsg->cmnd[6]; u8 *buf = (u8 *) bh->buf; @@ -1718,7 +1719,7 @@ static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; int mscmnd = fsg->cmnd[0]; u8 *buf = (u8 *) bh->buf; u8 *buf0 = buf; @@ -1799,7 +1800,7 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_start_stop(struct fsg_dev *fsg) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; int loej, start; if (!mod_data.removable) { @@ -1829,7 +1830,7 @@ static int do_start_stop(struct fsg_dev *fsg) if (loej) { // Simulate an unload/eject up_read(&fsg->filesem); down_write(&fsg->filesem); - close_backing_file(curlun); + fsg_lun_close(curlun); up_write(&fsg->filesem); down_read(&fsg->filesem); } @@ -1837,7 +1838,7 @@ static int do_start_stop(struct fsg_dev *fsg) /* Our emulation doesn't support mounting; the medium is * available for use as soon as it is loaded. */ - if (!backing_file_is_open(curlun)) { + if (!fsg_lun_is_open(curlun)) { curlun->sense_data = SS_MEDIUM_NOT_PRESENT; return -EINVAL; } @@ -1849,7 +1850,7 @@ static int do_start_stop(struct fsg_dev *fsg) static int do_prevent_allow(struct fsg_dev *fsg) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; int prevent; if (!mod_data.removable) { @@ -1864,7 +1865,7 @@ static int do_prevent_allow(struct fsg_dev *fsg) } if (curlun->prevent_medium_removal && !prevent) - fsync_sub(curlun); + fsg_lun_fsync_sub(curlun); curlun->prevent_medium_removal = prevent; return 0; } @@ -1873,7 +1874,7 @@ static int do_prevent_allow(struct fsg_dev *fsg) static int do_read_format_capacities(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; u8 *buf = (u8 *) bh->buf; buf[0] = buf[1] = buf[2] = 0; @@ -1890,7 +1891,7 @@ static int do_read_format_capacities(struct fsg_dev *fsg, static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; /* We don't support MODE SELECT */ curlun->sense_data = SS_INVALID_COMMAND; @@ -2133,7 +2134,7 @@ static int finish_reply(struct fsg_dev *fsg) static int send_status(struct fsg_dev *fsg) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->curlun; struct fsg_buffhd *bh; int rc; u8 status = USB_STATUS_PASS; @@ -2225,7 +2226,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, int lun = fsg->cmnd[1] >> 5; static const char dirletter[4] = {'u', 'o', 'i', 'n'}; char hdlen[20]; - struct lun *curlun; + struct fsg_lun *curlun; /* Adjust the expected cmnd_size for protocol encapsulation padding. * Transparent SCSI doesn't pad. */ @@ -2354,7 +2355,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, /* If the medium isn't mounted and the command needs to access * it, return an error. */ - if (curlun && !backing_file_is_open(curlun) && needs_medium) { + if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { curlun->sense_data = SS_MEDIUM_NOT_PRESENT; return -EINVAL; } @@ -2609,8 +2610,8 @@ static int do_scsi_command(struct fsg_dev *fsg) static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct usb_request *req = bh->outreq; - struct bulk_cb_wrap *cbw = req->buf; + struct usb_request *req = bh->outreq; + struct fsg_bulk_cb_wrap *cbw = req->buf; /* Was this a real packet? Should it be ignored? */ if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) @@ -2639,7 +2640,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) } /* Is the CBW meaningful? */ - if (cbw->Lun >= MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " "cmdlen %u\n", @@ -2772,7 +2773,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting) reset: /* Deallocate the requests */ - for (i = 0; i < NUM_BUFFERS; ++i) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; if (bh->inreq) { @@ -2810,12 +2811,14 @@ reset: DBG(fsg, "set interface %d\n", altsetting); /* Enable the endpoints */ - d = ep_desc(fsg->gadget, &fs_bulk_in_desc, &hs_bulk_in_desc); + d = fsg_ep_desc(fsg->gadget, + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) goto reset; fsg->bulk_in_enabled = 1; - d = ep_desc(fsg->gadget, &fs_bulk_out_desc, &hs_bulk_out_desc); + d = fsg_ep_desc(fsg->gadget, + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) goto reset; fsg->bulk_out_enabled = 1; @@ -2823,14 +2826,15 @@ reset: clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); if (transport_is_cbi()) { - d = ep_desc(fsg->gadget, &fs_intr_in_desc, &hs_intr_in_desc); + d = fsg_ep_desc(fsg->gadget, + &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc); if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) goto reset; fsg->intr_in_enabled = 1; } /* Allocate the requests */ - for (i = 0; i < NUM_BUFFERS; ++i) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) @@ -2906,7 +2910,7 @@ static void handle_exception(struct fsg_dev *fsg) struct fsg_buffhd *bh; enum fsg_state old_state; u8 new_config; - struct lun *curlun; + struct fsg_lun *curlun; unsigned int exception_req_tag; int rc; @@ -2926,7 +2930,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Cancel all the pending transfers */ if (fsg->intreq_busy) usb_ep_dequeue(fsg->intr_in, fsg->intreq); - for (i = 0; i < NUM_BUFFERS; ++i) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { bh = &fsg->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(fsg->bulk_in, bh->inreq); @@ -2937,7 +2941,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Wait until everything is idle */ for (;;) { num_active = fsg->intreq_busy; - for (i = 0; i < NUM_BUFFERS; ++i) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { bh = &fsg->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } @@ -2959,7 +2963,7 @@ static void handle_exception(struct fsg_dev *fsg) * state, and the exception. Then invoke the handler. */ spin_lock_irq(&fsg->lock); - for (i = 0; i < NUM_BUFFERS; ++i) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { bh = &fsg->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -3041,7 +3045,7 @@ static void handle_exception(struct fsg_dev *fsg) case FSG_STATE_DISCONNECT: for (i = 0; i < fsg->nluns; ++i) - fsync_sub(fsg->luns + i); + fsg_lun_fsync_sub(fsg->luns + i); do_set_config(fsg, 0); // Unconfigured state break; @@ -3132,7 +3136,7 @@ static int fsg_main_thread(void *fsg_) static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char *buf) { - struct lun *curlun = dev_to_lun(dev); + struct fsg_lun *curlun = fsg_lun_from_dev(dev); return sprintf(buf, "%d\n", curlun->ro); } @@ -3140,13 +3144,13 @@ static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char * static ssize_t show_file(struct device *dev, struct device_attribute *attr, char *buf) { - struct lun *curlun = dev_to_lun(dev); + struct fsg_lun *curlun = fsg_lun_from_dev(dev); struct fsg_dev *fsg = dev_get_drvdata(dev); char *p; ssize_t rc; down_read(&fsg->filesem); - if (backing_file_is_open(curlun)) { // Get the complete pathname + if (fsg_lun_is_open(curlun)) { // Get the complete pathname p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); if (IS_ERR(p)) rc = PTR_ERR(p); @@ -3169,7 +3173,7 @@ static ssize_t store_ro(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ssize_t rc = count; - struct lun *curlun = dev_to_lun(dev); + struct fsg_lun *curlun = fsg_lun_from_dev(dev); struct fsg_dev *fsg = dev_get_drvdata(dev); int i; @@ -3179,7 +3183,7 @@ static ssize_t store_ro(struct device *dev, struct device_attribute *attr, /* Allow the write-enable status to change only while the backing file * is closed. */ down_read(&fsg->filesem); - if (backing_file_is_open(curlun)) { + if (fsg_lun_is_open(curlun)) { LDBG(curlun, "read-only status change prevented\n"); rc = -EBUSY; } else { @@ -3193,11 +3197,11 @@ static ssize_t store_ro(struct device *dev, struct device_attribute *attr, static ssize_t store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct lun *curlun = dev_to_lun(dev); + struct fsg_lun *curlun = fsg_lun_from_dev(dev); struct fsg_dev *fsg = dev_get_drvdata(dev); int rc = 0; - if (curlun->prevent_medium_removal && backing_file_is_open(curlun)) { + if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { LDBG(curlun, "eject attempt prevented\n"); return -EBUSY; // "Door is locked" } @@ -3208,14 +3212,14 @@ static ssize_t store_file(struct device *dev, struct device_attribute *attr, /* Eject current medium */ down_write(&fsg->filesem); - if (backing_file_is_open(curlun)) { - close_backing_file(curlun); + if (fsg_lun_is_open(curlun)) { + fsg_lun_close(curlun); curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; } /* Load new medium */ if (count > 0 && buf[0]) { - rc = open_backing_file(curlun, buf); + rc = fsg_lun_open(curlun, buf); if (rc == 0) curlun->unit_attention_data = SS_NOT_READY_TO_READY_TRANSITION; @@ -3251,7 +3255,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) { struct fsg_dev *fsg = get_gadget_data(gadget); int i; - struct lun *curlun; + struct fsg_lun *curlun; struct usb_request *req = fsg->ep0req; DBG(fsg, "unbind\n"); @@ -3263,7 +3267,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) if (curlun->registered) { device_remove_file(&curlun->dev, &dev_attr_ro); device_remove_file(&curlun->dev, &dev_attr_file); - close_backing_file(curlun); + fsg_lun_close(curlun); device_unregister(&curlun->dev); curlun->registered = 0; } @@ -3279,7 +3283,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) } /* Free the data buffers */ - for (i = 0; i < NUM_BUFFERS; ++i) + for (i = 0; i < FSG_NUM_BUFFERS; ++i) kfree(fsg->buffhds[i].buf); /* Free the request and buffer for endpoint 0 */ @@ -3386,7 +3390,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) struct fsg_dev *fsg = the_fsg; int rc; int i; - struct lun *curlun; + struct fsg_lun *curlun; struct usb_ep *ep; struct usb_request *req; char *pathbuf, *p; @@ -3412,7 +3416,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) i = mod_data.nluns; if (i == 0) i = max(mod_data.num_filenames, 1u); - if (i > MAX_LUNS) { + if (i > FSG_MAX_LUNS) { ERROR(fsg, "invalid number of LUNs: %d\n", i); rc = -EINVAL; goto out; @@ -3420,7 +3424,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Create the LUNs, open their backing files, and register the * LUN devices in sysfs. */ - fsg->luns = kzalloc(i * sizeof(struct lun), GFP_KERNEL); + fsg->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL); if (!fsg->luns) { rc = -ENOMEM; goto out; @@ -3454,7 +3458,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) kref_get(&fsg->ref); if (mod_data.file[i] && *mod_data.file[i]) { - if ((rc = open_backing_file(curlun, + if ((rc = fsg_lun_open(curlun, mod_data.file[i])) != 0) goto out; } else if (!mod_data.removable) { @@ -3466,20 +3470,20 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Find all the endpoints we will use */ usb_ep_autoconfig_reset(gadget); - ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc); + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); if (!ep) goto autoconf_fail; ep->driver_data = fsg; // claim the endpoint fsg->bulk_in = ep; - ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc); + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); if (!ep) goto autoconf_fail; ep->driver_data = fsg; // claim the endpoint fsg->bulk_out = ep; if (transport_is_cbi()) { - ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc); + ep = usb_ep_autoconfig(gadget, &fsg_fs_intr_in_desc); if (!ep) goto autoconf_fail; ep->driver_data = fsg; // claim the endpoint @@ -3493,28 +3497,28 @@ static int __init fsg_bind(struct usb_gadget *gadget) device_desc.bcdDevice = cpu_to_le16(mod_data.release); i = (transport_is_cbi() ? 3 : 2); // Number of endpoints - intf_desc.bNumEndpoints = i; - intf_desc.bInterfaceSubClass = mod_data.protocol_type; - intf_desc.bInterfaceProtocol = mod_data.transport_type; - fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL; + fsg_intf_desc.bNumEndpoints = i; + fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type; + fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type; + fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL; if (gadget_is_dualspeed(gadget)) { - hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL; + fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL; /* Assume ep0 uses the same maxpacket value for both speeds */ dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; /* Assume endpoint addresses are the same for both speeds */ - hs_bulk_in_desc.bEndpointAddress = - fs_bulk_in_desc.bEndpointAddress; - hs_bulk_out_desc.bEndpointAddress = - fs_bulk_out_desc.bEndpointAddress; - hs_intr_in_desc.bEndpointAddress = - fs_intr_in_desc.bEndpointAddress; + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_hs_intr_in_desc.bEndpointAddress = + fsg_fs_intr_in_desc.bEndpointAddress; } if (gadget_is_otg(gadget)) - otg_desc.bmAttributes |= USB_OTG_HNP; + fsg_otg_desc.bmAttributes |= USB_OTG_HNP; rc = -ENOMEM; @@ -3528,7 +3532,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) req->complete = ep0_complete; /* Allocate the data buffers */ - for (i = 0; i < NUM_BUFFERS; ++i) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; /* Allocate for the bulk-in endpoint. We assume that @@ -3539,23 +3543,24 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; bh->next = bh + 1; } - fsg->buffhds[NUM_BUFFERS - 1].next = &fsg->buffhds[0]; + fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0]; /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer, + "%s %s with %s", 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(serial) - 2; i += 2) { + for (i = 0; i < sizeof fsg_string_serial - 2; i += 2) { unsigned char c = DRIVER_VERSION[i / 2]; if (!c) break; - sprintf(&serial[i], "%02X", c); + sprintf(&fsg_string_serial[i], "%02X", c); } fsg->thread_task = kthread_create(fsg_main_thread, fsg, @@ -3571,7 +3576,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; - if (backing_file_is_open(curlun)) { + if (fsg_lun_is_open(curlun)) { p = NULL; if (pathbuf) { p = d_path(&curlun->filp->f_path, @@ -3641,7 +3646,7 @@ static struct usb_gadget_driver fsg_driver = { #else .speed = USB_SPEED_FULL, #endif - .function = (char *) longname, + .function = (char *) fsg_string_product, .bind = fsg_bind, .unbind = fsg_unbind, .disconnect = fsg_disconnect, @@ -3650,7 +3655,7 @@ static struct usb_gadget_driver fsg_driver = { .resume = fsg_resume, .driver = { - .name = (char *) shortname, + .name = DRIVER_NAME, .owner = THIS_MODULE, // .release = ... // .suspend = ... diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index fa86fdaafc1d..19bf1e330dd0 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -4,9 +4,38 @@ * Copyright (C) 2003-2008 Alan Stern * Copyeight (C) 2009 Samsung Electronics * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + * - fsg_string_manufacturer -- name of the manufacturer + * - fsg_string_product -- name of the product + * - fsg_string_serial -- product's serial + * - fsg_string_config -- name of the configuration + * - fsg_string_interface -- name of the interface + * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS + * macro is defined prior to including this file. */ +#include + /*-------------------------------------------------------------------------*/ @@ -93,7 +122,7 @@ /* Bulk-only data structures */ /* Command Block Wrapper */ -struct bulk_cb_wrap { +struct fsg_bulk_cb_wrap { __le32 Signature; // Contains 'USBC' u32 Tag; // Unique per command id __le32 DataTransferLength; // Size of the data @@ -191,7 +220,7 @@ struct interrupt_data { /*-------------------------------------------------------------------------*/ -struct lun { +struct fsg_lun { struct file *filp; loff_t file_length; loff_t num_sectors; @@ -208,11 +237,11 @@ struct lun { struct device dev; }; -#define backing_file_is_open(curlun) ((curlun)->filp != NULL) +#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL) -static struct lun *dev_to_lun(struct device *dev) +static struct fsg_lun *fsg_lun_from_dev(struct device *dev) { - return container_of(dev, struct lun, dev); + return container_of(dev, struct fsg_lun, dev); } @@ -221,7 +250,7 @@ static struct lun *dev_to_lun(struct device *dev) #define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value /* Number of buffers we will use. 2 is enough for double-buffering */ -#define NUM_BUFFERS 2 +#define FSG_NUM_BUFFERS 2 enum fsg_buffer_state { BUF_STATE_EMPTY = 0, @@ -280,16 +309,18 @@ static inline u32 get_unaligned_be24(u8 *buf) /*-------------------------------------------------------------------------*/ -#define STRING_MANUFACTURER 1 -#define STRING_PRODUCT 2 -#define STRING_SERIAL 3 -#define STRING_CONFIG 4 -#define STRING_INTERFACE 5 +enum { + FSG_STRING_MANUFACTURER = 1, + FSG_STRING_PRODUCT, + FSG_STRING_SERIAL, + FSG_STRING_CONFIG, + FSG_STRING_INTERFACE +}; static struct usb_otg_descriptor -otg_desc = { - .bLength = sizeof(otg_desc), +fsg_otg_desc = { + .bLength = sizeof fsg_otg_desc, .bDescriptorType = USB_DT_OTG, .bmAttributes = USB_OTG_SRP, @@ -298,22 +329,22 @@ otg_desc = { /* There is only one interface. */ static struct usb_interface_descriptor -intf_desc = { - .bLength = sizeof intf_desc, +fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 2, // Adjusted during fsg_bind() .bInterfaceClass = USB_CLASS_MASS_STORAGE, .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind() .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind() - .iInterface = STRING_INTERFACE, + .iInterface = FSG_STRING_INTERFACE, }; /* Three full-speed endpoint descriptors: bulk-in, bulk-out, * and interrupt-in. */ static struct usb_endpoint_descriptor -fs_bulk_in_desc = { +fsg_fs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -323,7 +354,7 @@ fs_bulk_in_desc = { }; static struct usb_endpoint_descriptor -fs_bulk_out_desc = { +fsg_fs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -333,7 +364,7 @@ fs_bulk_out_desc = { }; static struct usb_endpoint_descriptor -fs_intr_in_desc = { +fsg_fs_intr_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -343,15 +374,15 @@ fs_intr_in_desc = { .bInterval = 32, // frames -> 32 ms }; -static const struct usb_descriptor_header *fs_function[] = { - (struct usb_descriptor_header *) &otg_desc, - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &fs_bulk_in_desc, - (struct usb_descriptor_header *) &fs_bulk_out_desc, - (struct usb_descriptor_header *) &fs_intr_in_desc, +static const struct usb_descriptor_header *fsg_fs_function[] = { + (struct usb_descriptor_header *) &fsg_otg_desc, + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_fs_intr_in_desc, NULL, }; -#define FS_FUNCTION_PRE_EP_ENTRIES 2 +#define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2 /* @@ -363,7 +394,7 @@ static const struct usb_descriptor_header *fs_function[] = { * for the config descriptor. */ static struct usb_endpoint_descriptor -hs_bulk_in_desc = { +fsg_hs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -373,7 +404,7 @@ hs_bulk_in_desc = { }; static struct usb_endpoint_descriptor -hs_bulk_out_desc = { +fsg_hs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -384,7 +415,7 @@ hs_bulk_out_desc = { }; static struct usb_endpoint_descriptor -hs_intr_in_desc = { +fsg_hs_intr_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -394,19 +425,19 @@ hs_intr_in_desc = { .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms }; -static const struct usb_descriptor_header *hs_function[] = { - (struct usb_descriptor_header *) &otg_desc, - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &hs_bulk_in_desc, - (struct usb_descriptor_header *) &hs_bulk_out_desc, - (struct usb_descriptor_header *) &hs_intr_in_desc, +static const struct usb_descriptor_header *fsg_hs_function[] = { + (struct usb_descriptor_header *) &fsg_otg_desc, + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_hs_intr_in_desc, NULL, }; -#define HS_FUNCTION_PRE_EP_ENTRIES 2 +#define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2 /* Maxpacket and other transfer characteristics vary by speed. */ static struct usb_endpoint_descriptor * -ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, struct usb_endpoint_descriptor *hs) { if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) @@ -415,24 +446,19 @@ ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, } -/* The CBI specification limits the serial string to 12 uppercase hexadecimal - * characters. */ -static char manufacturer[64]; -static char serial[13]; - /* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ -static struct usb_string strings[] = { - {STRING_MANUFACTURER, manufacturer}, - {STRING_PRODUCT, longname}, - {STRING_SERIAL, serial}, - {STRING_CONFIG, "Self-powered"}, - {STRING_INTERFACE, "Mass Storage"}, +static struct usb_string fsg_strings[] = { + {FSG_STRING_MANUFACTURER, fsg_string_manufacturer}, + {FSG_STRING_PRODUCT, fsg_string_product}, + {FSG_STRING_SERIAL, fsg_string_serial}, + {FSG_STRING_CONFIG, fsg_string_config}, + {FSG_STRING_INTERFACE, fsg_string_interface}, {} }; -static struct usb_gadget_strings stringtab = { +static struct usb_gadget_strings fsg_stringtab = { .language = 0x0409, // en-us - .strings = strings, + .strings = fsg_strings, }; @@ -441,7 +467,7 @@ static struct usb_gadget_strings stringtab = { /* If the next two routines are called while the gadget is registered, * the caller must own fsg->filesem for writing. */ -static int open_backing_file(struct lun *curlun, const char *filename) +static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) { int ro; struct file *filp = NULL; @@ -525,7 +551,7 @@ out: } -static void close_backing_file(struct lun *curlun) +static void fsg_lun_close(struct fsg_lun *curlun) { if (curlun->filp) { LDBG(curlun, "close backing file\n"); @@ -539,7 +565,7 @@ static void close_backing_file(struct lun *curlun) /* Sync the file data, don't bother with the metadata. * This code was copied from fs/buffer.c:sys_fdatasync(). */ -static int fsync_sub(struct lun *curlun) +static int fsg_lun_fsync_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; -- cgit v1.2.3 From 4d8f4ded60b4e42936fbdff2ec4c62679c53c250 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:16 +0100 Subject: USB: g_file_storage: per-LUN ro, removable and cdrom flags handling changed removable and cdrom flag has been added to the fsg_lun structure removing any references to mod_data object from storage_common.c. As of read-only flag, previously it was set if a read-only backing file was specified (which is good) and remained set even after the file has been closed (which may be considered an issue). Currently, the initial read-only flag is preserved so if it was unset each time file is opened code will try to open it read-write even if previous file was opened read-only. Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 12 ++++++++---- drivers/usb/gadget/storage_common.c | 7 +++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 90233f4f3601..ee712a5715e7 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3138,7 +3138,9 @@ static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char * { struct fsg_lun *curlun = fsg_lun_from_dev(dev); - return sprintf(buf, "%d\n", curlun->ro); + return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) + ? curlun->ro + : curlun->initially_ro); } static ssize_t show_file(struct device *dev, struct device_attribute *attr, @@ -3188,6 +3190,7 @@ static ssize_t store_ro(struct device *dev, struct device_attribute *attr, rc = -EBUSY; } else { curlun->ro = !!i; + curlun->initially_ro = !!i; LDBG(curlun, "read-only status set to %d\n", curlun->ro); } up_read(&fsg->filesem); @@ -3433,9 +3436,10 @@ static int __init fsg_bind(struct usb_gadget *gadget) for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; - curlun->ro = mod_data.ro[i]; - if (mod_data.cdrom) - curlun->ro = 1; + curlun->cdrom = !!mod_data.cdrom; + curlun->ro = mod_data.cdrom || mod_data.ro[i]; + curlun->initially_ro = curlun->ro; + curlun->removable = mod_data.removable; curlun->dev.release = lun_release; curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 19bf1e330dd0..affd23b5436f 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -225,7 +225,10 @@ struct fsg_lun { loff_t file_length; loff_t num_sectors; + unsigned int initially_ro : 1; unsigned int ro : 1; + unsigned int removable : 1; + unsigned int cdrom : 1; unsigned int prevent_medium_removal : 1; unsigned int registered : 1; unsigned int info_valid : 1; @@ -478,7 +481,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) loff_t min_sectors; /* R/W if we can, R/O if we must */ - ro = curlun->ro; + ro = curlun->initially_ro; if (!ro) { filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); if (-EROFS == PTR_ERR(filp)) @@ -521,7 +524,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) } num_sectors = size >> 9; // File size in 512-byte blocks min_sectors = 1; - if (mod_data.cdrom) { + if (curlun->cdrom) { num_sectors &= ~3; // Reduce to a multiple of 2048 min_sectors = 300*4; // Smallest track is 300 frames if (num_sectors >= 256*60*75*4) { -- cgit v1.2.3 From 01d6f17d5f4c1c5e7bd982d23efd9713ee58657f Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:17 +0100 Subject: USB: g_file_storage: more code from file_storage.c moved to storage_common.c Since storage_common.c no longer references mod_data object it is now possible to include it before mod_data object is defined. This makes it possible to move some defines that are used as default values of mod_data fields to be defined in storage_common.c file (where they should be set from the beginning). Also, show_ro(), show_file(), store_ro() and store_file() have been moved to storage_common.c with LUN's device's drvdata changed from pointing to fsg_dev to pointing to rw_semaphore (&fsg->filesem). Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 129 ++++-------------------------------- drivers/usb/gadget/storage_common.c | 116 ++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 117 deletions(-) diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index ee712a5715e7..7998402b1840 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -278,18 +278,14 @@ static char fsg_string_serial[13]; static const char fsg_string_config[] = "Self-powered"; static const char fsg_string_interface[] = "Mass Storage"; + +#include "storage_common.c" + + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Alan Stern"); MODULE_LICENSE("Dual BSD/GPL"); -/* Thanks to NetChip Technologies for donating this product ID. - * - * DO NOT REUSE THESE IDs with any other driver!! Ever!! - * Instead: allocate your own, using normal USB-IF procedures. */ -#define FSG_VENDOR_ID 0x0525 // NetChip -#define FSG_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget - - /* * This driver assumes self-powered hardware and has no way for users to * trigger remote wakeup. It uses autoconfiguration to select endpoints @@ -302,8 +298,6 @@ MODULE_LICENSE("Dual BSD/GPL"); /* Encapsulate the module parameter settings */ -#define FSG_MAX_LUNS 8 - static struct { char *file[FSG_MAX_LUNS]; int ro[FSG_MAX_LUNS]; @@ -408,10 +402,6 @@ MODULE_PARM_DESC(buflen, "I/O buffer size"); #endif /* CONFIG_USB_FILE_STORAGE_TEST */ -/*-------------------------------------------------------------------------*/ - -#include "storage_common.c" - /*-------------------------------------------------------------------------*/ @@ -3134,107 +3124,10 @@ static int fsg_main_thread(void *fsg_) /*-------------------------------------------------------------------------*/ -static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - - return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) - ? curlun->ro - : curlun->initially_ro); -} - -static ssize_t show_file(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct fsg_dev *fsg = dev_get_drvdata(dev); - char *p; - ssize_t rc; - - down_read(&fsg->filesem); - if (fsg_lun_is_open(curlun)) { // Get the complete pathname - p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); - if (IS_ERR(p)) - rc = PTR_ERR(p); - else { - rc = strlen(p); - memmove(buf, p, rc); - buf[rc] = '\n'; // Add a newline - buf[++rc] = 0; - } - } else { // No file, return 0 bytes - *buf = 0; - rc = 0; - } - up_read(&fsg->filesem); - return rc; -} - - -static ssize_t store_ro(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - ssize_t rc = count; - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct fsg_dev *fsg = dev_get_drvdata(dev); - int i; - - if (sscanf(buf, "%d", &i) != 1) - return -EINVAL; - - /* Allow the write-enable status to change only while the backing file - * is closed. */ - down_read(&fsg->filesem); - if (fsg_lun_is_open(curlun)) { - LDBG(curlun, "read-only status change prevented\n"); - rc = -EBUSY; - } else { - curlun->ro = !!i; - curlun->initially_ro = !!i; - LDBG(curlun, "read-only status set to %d\n", curlun->ro); - } - up_read(&fsg->filesem); - return rc; -} - -static ssize_t store_file(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct fsg_dev *fsg = dev_get_drvdata(dev); - int rc = 0; - - if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { - LDBG(curlun, "eject attempt prevented\n"); - return -EBUSY; // "Door is locked" - } - - /* Remove a trailing newline */ - if (count > 0 && buf[count-1] == '\n') - ((char *) buf)[count-1] = 0; // Ugh! - - /* Eject current medium */ - down_write(&fsg->filesem); - if (fsg_lun_is_open(curlun)) { - fsg_lun_close(curlun); - curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; - } - - /* Load new medium */ - if (count > 0 && buf[0]) { - rc = fsg_lun_open(curlun, buf); - if (rc == 0) - curlun->unit_attention_data = - SS_NOT_READY_TO_READY_TRANSITION; - } - up_write(&fsg->filesem); - return (rc < 0 ? rc : count); -} - /* The write permissions and store_xxx pointers are set in fsg_bind() */ -static DEVICE_ATTR(ro, 0444, show_ro, NULL); -static DEVICE_ATTR(file, 0444, show_file, NULL); +static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); +static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); /*-------------------------------------------------------------------------*/ @@ -3249,7 +3142,9 @@ static void fsg_release(struct kref *ref) static void lun_release(struct device *dev) { - struct fsg_dev *fsg = dev_get_drvdata(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + struct fsg_dev *fsg = + container_of(filesem, struct fsg_dev, filesem); kref_put(&fsg->ref, fsg_release); } @@ -3408,10 +3303,10 @@ static int __init fsg_bind(struct usb_gadget *gadget) if (mod_data.removable) { // Enable the store_xxx attributes dev_attr_file.attr.mode = 0644; - dev_attr_file.store = store_file; + dev_attr_file.store = fsg_store_file; if (!mod_data.cdrom) { dev_attr_ro.attr.mode = 0644; - dev_attr_ro.store = store_ro; + dev_attr_ro.store = fsg_store_ro; } } @@ -3443,7 +3338,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) curlun->dev.release = lun_release; curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; - dev_set_drvdata(&curlun->dev, fsg); + dev_set_drvdata(&curlun->dev, &fsg->filesem); dev_set_name(&curlun->dev,"%s-lun%d", dev_name(&gadget->dev), i); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index affd23b5436f..6523cb6f4c4d 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -37,6 +37,14 @@ #include +/* Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. */ +#define FSG_VENDOR_ID 0x0525 // NetChip +#define FSG_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget + + /*-------------------------------------------------------------------------*/ @@ -255,6 +263,12 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) /* Number of buffers we will use. 2 is enough for double-buffering */ #define FSG_NUM_BUFFERS 2 +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 8 + enum fsg_buffer_state { BUF_STATE_EMPTY = 0, BUF_STATE_FULL, @@ -594,3 +608,105 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr) put_unaligned_be32(addr, dest); } } + + +/*-------------------------------------------------------------------------*/ + + +static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) + ? curlun->ro + : curlun->initially_ro); +} + +static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + char *p; + ssize_t rc; + + down_read(filesem); + if (fsg_lun_is_open(curlun)) { // Get the complete pathname + p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) + rc = PTR_ERR(p); + else { + rc = strlen(p); + memmove(buf, p, rc); + buf[rc] = '\n'; // Add a newline + buf[++rc] = 0; + } + } else { // No file, return 0 bytes + *buf = 0; + rc = 0; + } + up_read(filesem); + return rc; +} + + +static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t rc = count; + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%d", &i) != 1) + return -EINVAL; + + /* Allow the write-enable status to change only while the backing file + * is closed. */ + down_read(filesem); + if (fsg_lun_is_open(curlun)) { + LDBG(curlun, "read-only status change prevented\n"); + rc = -EBUSY; + } else { + curlun->ro = !!i; + curlun->initially_ro = !!i; + LDBG(curlun, "read-only status set to %d\n", curlun->ro); + } + up_read(filesem); + return rc; +} + +static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + int rc = 0; + + if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { + LDBG(curlun, "eject attempt prevented\n"); + return -EBUSY; // "Door is locked" + } + + /* Remove a trailing newline */ + if (count > 0 && buf[count-1] == '\n') + ((char *) buf)[count-1] = 0; // Ugh! + + /* Eject current medium */ + down_write(filesem); + if (fsg_lun_is_open(curlun)) { + fsg_lun_close(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; + } + + /* Load new medium */ + if (count > 0 && buf[0]) { + rc = fsg_lun_open(curlun, buf); + if (rc == 0) + curlun->unit_attention_data = + SS_NOT_READY_TO_READY_TRANSITION; + } + up_write(filesem); + return (rc < 0 ? rc : count); +} -- cgit v1.2.3 From 523f8e77ff6dd775a14dc36f126053e8b344f473 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:18 +0100 Subject: USB: g_mass_storage: template f_mass_storage.c file created Copied file_storage.c to f_mass_storage.c which will be used as template for the Mass Storage composite Function. Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 3611 +++++++++++++++++++++++++++++++++++ 1 file changed, 3611 insertions(+) create mode 100644 drivers/usb/gadget/f_mass_storage.c diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c new file mode 100644 index 000000000000..7998402b1840 --- /dev/null +++ b/drivers/usb/gadget/f_mass_storage.c @@ -0,0 +1,3611 @@ +/* + * file_storage.c -- File-backed USB Storage Gadget, for USB development + * + * Copyright (C) 2003-2008 Alan Stern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * The File-backed Storage Gadget acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In addition + * to providing an example of a genuinely useful gadget driver for a USB + * device, it also illustrates a technique of double-buffering for increased + * throughput. Last but not least, it gives an easy way to probe the + * behavior of the Mass Storage drivers in a USB host. + * + * Backing storage is provided by a regular file or a block device, specified + * by the "file" module parameter. Access can be limited to read-only by + * setting the optional "ro" module parameter. (For CD-ROM emulation, + * access is always read-only.) The gadget will indicate that it has + * removable media if the optional "removable" module parameter is set. + * + * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), + * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected + * by the optional "transport" module parameter. It also supports the + * 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. + * + * 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 + * "luns" module parameter (anywhere from 1 to 8), and the corresponding + * files are specified using comma-separated lists for "file" and "ro". + * The default number of LUNs is taken from the number of "file" elements; + * it is 1 if "file" is not given. If "removable" is not set then a backing + * file must be specified for each LUN. If it is set, then an unspecified + * or empty backing filename means the LUN's medium is not loaded. Ideally + * each LUN would be settable independently as a disk drive or a CD-ROM + * drive, but currently all LUNs have to be the same type. The CD-ROM + * emulation includes a single data track and no audio tracks; hence there + * need be only one backing file per LUN. Note also that the CD-ROM block + * length is set to 512 rather than the more common value 2048. + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are + * needed (an interrupt-out endpoint is also needed for CBI). The memory + * requirement amounts to two 16K buffers, size configurable by a parameter. + * Support is included for both full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints. With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * Module options: + * + * file=filename[,filename...] + * Required if "removable" is not set, names of + * the files or block devices used for + * backing storage + * ro=b[,b...] Default false, booleans for read-only access + * removable Default false, boolean for removable media + * luns=N Default N = number of filenames, number of + * LUNs to support + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt + * bulk endpoints + * cdrom Default false, boolean for whether to emulate + * a CD-ROM drive + * transport=XXX Default BBB, transport name (CB, CBI, or BBB) + * protocol=YYY Default SCSI, protocol name (RBC, 8020 or + * ATAPI, QIC, UFI, 8070, or SCSI; + * also 1 - 6) + * vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID + * product=0xPPPP Default 0xa4a5 (FSG), USB Product ID + * release=0xRRRR Override the USB release number (bcdDevice) + * buflen=N Default N=16384, buffer size used (will be + * rounded down to a multiple of + * PAGE_CACHE_SIZE) + * + * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", + * "removable", "luns", "stall", and "cdrom" options are available; default + * values are used for everything else. + * + * The pathnames of the backing files and the ro settings are available in + * the attribute files "file" and "ro" in the lun subdirectory of the + * gadget's sysfs directory. If the "removable" option is set, writing to + * these files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab. Changes to the ro + * setting are not allowed when the medium is loaded or if CD-ROM emulation + * is being used. + * + * This gadget driver is heavily based on "Gadget Zero" by David Brownell. + * The driver's SCSI command interface was based on the "Information + * technology - Small Computer System Interface - 2" document from + * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at + * . The single exception + * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the + * "Universal Serial Bus Mass Storage Class UFI Command Specification" + * document, Revision 1.0, December 14, 1998, available at + * . + */ + + +/* + * Driver Design + * + * The FSG driver is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind(). But it can also + * exit when it receives a signal, and there's no point leaving the + * gadget running when the thread is dead. So just before the thread + * exits, it deregisters the gadget driver. This makes things a little + * tricky: The driver is deregistered at two places, and the exiting + * thread can indirectly call fsg_unbind() which in turn can tell the + * thread to exit. The first problem is resolved through the use of the + * REGISTERED atomic bitflag; the driver will only be deregistered once. + * The second problem is resolved by having fsg_unbind() check + * fsg->state; it won't try to stop the thread if the state is already + * FSG_STATE_TERMINATED. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gadget_chips.h" + + + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" + +/*-------------------------------------------------------------------------*/ + +#define DRIVER_DESC "File-backed Storage Gadget" +#define DRIVER_NAME "g_file_storage" +#define DRIVER_VERSION "20 November 2008" + +static char fsg_string_manufacturer[64]; +static const char fsg_string_product[] = DRIVER_DESC; +static char fsg_string_serial[13]; +static const char fsg_string_config[] = "Self-powered"; +static const char fsg_string_interface[] = "Mass Storage"; + + +#include "storage_common.c" + + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Alan Stern"); +MODULE_LICENSE("Dual BSD/GPL"); + +/* + * This driver assumes self-powered hardware and has no way for users to + * trigger remote wakeup. It uses autoconfiguration to select endpoints + * and endpoint addresses. + */ + + +/*-------------------------------------------------------------------------*/ + + +/* Encapsulate the module parameter settings */ + +static struct { + char *file[FSG_MAX_LUNS]; + int ro[FSG_MAX_LUNS]; + unsigned int num_filenames; + unsigned int num_ros; + unsigned int nluns; + + int removable; + int can_stall; + int cdrom; + + char *transport_parm; + char *protocol_parm; + unsigned short vendor; + unsigned short product; + unsigned short release; + unsigned int buflen; + + int transport_type; + char *transport_name; + int protocol_type; + char *protocol_name; + +} mod_data = { // Default values + .transport_parm = "BBB", + .protocol_parm = "SCSI", + .removable = 0, + .can_stall = 1, + .cdrom = 0, + .vendor = FSG_VENDOR_ID, + .product = FSG_PRODUCT_ID, + .release = 0xffff, // Use controller chip type + .buflen = 16384, + }; + + +module_param_array_named(file, mod_data.file, charp, &mod_data.num_filenames, + S_IRUGO); +MODULE_PARM_DESC(file, "names of backing files or devices"); + +module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO); +MODULE_PARM_DESC(ro, "true to force read-only"); + +module_param_named(luns, mod_data.nluns, uint, S_IRUGO); +MODULE_PARM_DESC(luns, "number of LUNs"); + +module_param_named(removable, mod_data.removable, bool, S_IRUGO); +MODULE_PARM_DESC(removable, "true to simulate removable media"); + +module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); +MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); + +module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); +MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); + + +/* In the non-TEST version, only the module parameters listed above + * are available. */ +#ifdef CONFIG_USB_FILE_STORAGE_TEST + +module_param_named(transport, mod_data.transport_parm, charp, S_IRUGO); +MODULE_PARM_DESC(transport, "type of transport (BBB, CBI, or CB)"); + +module_param_named(protocol, mod_data.protocol_parm, charp, S_IRUGO); +MODULE_PARM_DESC(protocol, "type of protocol (RBC, 8020, QIC, UFI, " + "8070, or SCSI)"); + +module_param_named(vendor, mod_data.vendor, ushort, S_IRUGO); +MODULE_PARM_DESC(vendor, "USB Vendor ID"); + +module_param_named(product, mod_data.product, ushort, S_IRUGO); +MODULE_PARM_DESC(product, "USB Product ID"); + +module_param_named(release, mod_data.release, ushort, S_IRUGO); +MODULE_PARM_DESC(release, "USB release number"); + +module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); +MODULE_PARM_DESC(buflen, "I/O buffer size"); + +#endif /* CONFIG_USB_FILE_STORAGE_TEST */ + + +/* + * These definitions will permit the compiler to avoid generating code for + * parts of the driver that aren't used in the non-TEST version. Even gcc + * can recognize when a test of a constant expression yields a dead code + * path. + */ + +#ifdef CONFIG_USB_FILE_STORAGE_TEST + +#define transport_is_bbb() (mod_data.transport_type == USB_PR_BULK) +#define transport_is_cbi() (mod_data.transport_type == USB_PR_CBI) +#define protocol_is_scsi() (mod_data.protocol_type == USB_SC_SCSI) + +#else + +#define transport_is_bbb() 1 +#define transport_is_cbi() 0 +#define protocol_is_scsi() 1 + +#endif /* CONFIG_USB_FILE_STORAGE_TEST */ + + +/*-------------------------------------------------------------------------*/ + + +struct fsg_dev { + /* lock protects: state, all the req_busy's, and cbbuf_cmnd */ + spinlock_t lock; + struct usb_gadget *gadget; + + /* filesem protects: backing files in use */ + struct rw_semaphore filesem; + + /* reference counting: wait until all LUNs are released */ + struct kref ref; + + struct usb_ep *ep0; // Handy copy of gadget->ep0 + struct usb_request *ep0req; // For control responses + unsigned int ep0_req_tag; + const char *ep0req_name; + + struct usb_request *intreq; // For interrupt responses + int intreq_busy; + struct fsg_buffhd *intr_buffhd; + + unsigned int bulk_out_maxpacket; + enum fsg_state state; // For exception handling + unsigned int exception_req_tag; + + u8 config, new_config; + + unsigned int running : 1; + unsigned int bulk_in_enabled : 1; + unsigned int bulk_out_enabled : 1; + unsigned int intr_in_enabled : 1; + unsigned int phase_error : 1; + unsigned int short_packet_received : 1; + unsigned int bad_lun_okay : 1; + + unsigned long atomic_bitflags; +#define REGISTERED 0 +#define IGNORE_BULK_OUT 1 +#define SUSPENDED 2 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; + struct usb_ep *intr_in; + + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + + int thread_wakeup_needed; + struct completion thread_notifier; + struct task_struct *thread_task; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + unsigned int lun; + u32 residue; + u32 usb_amount_left; + + /* The CB protocol offers no way for a host to know when a command + * has completed. As a result the next command may arrive early, + * and we will still have to handle it. For that reason we need + * a buffer to store new commands when using CB (or CBI, which + * does not oblige a host to wait for command completion either). */ + int cbbuf_cmnd_size; + u8 cbbuf_cmnd[MAX_COMMAND_SIZE]; + + unsigned int nluns; + struct fsg_lun *luns; + struct fsg_lun *curlun; +}; + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_dev *fsg) +{ + return (fsg->state > FSG_STATE_IDLE); +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_dev *fsg, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % fsg->bulk_out_maxpacket; + if (rem > 0) + length += fsg->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + +static struct fsg_dev *the_fsg; +static struct usb_gadget_driver fsg_driver; + + +/*-------------------------------------------------------------------------*/ + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ + const char *name; + + if (ep == fsg->bulk_in) + name = "bulk-in"; + else if (ep == fsg->bulk_out) + name = "bulk-out"; + else + name = ep->name; + DBG(fsg, "%s set halt\n", name); + return usb_ep_set_halt(ep); +} + + +/*-------------------------------------------------------------------------*/ + +/* + * DESCRIPTORS ... most are static, but strings and (full) configuration + * descriptors are built on demand. Also the (static) config and interface + * descriptors are adjusted during fsg_bind(). + */ + +/* There is only one configuration. */ +#define CONFIG_VALUE 1 + +static struct usb_device_descriptor +device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + + /* The next three values can be overridden by module parameters */ + .idVendor = cpu_to_le16(FSG_VENDOR_ID), + .idProduct = cpu_to_le16(FSG_PRODUCT_ID), + .bcdDevice = cpu_to_le16(0xffff), + + .iManufacturer = FSG_STRING_MANUFACTURER, + .iProduct = FSG_STRING_PRODUCT, + .iSerialNumber = FSG_STRING_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor +config_desc = { + .bLength = sizeof config_desc, + .bDescriptorType = USB_DT_CONFIG, + + /* wTotalLength computed by usb_gadget_config_buf() */ + .bNumInterfaces = 1, + .bConfigurationValue = CONFIG_VALUE, + .iConfiguration = FSG_STRING_CONFIG, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, +}; + + +static struct usb_qualifier_descriptor +dev_qualifier = { + .bLength = sizeof dev_qualifier, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + + .bNumConfigurations = 1, +}; + + + +/* + * Config descriptors must agree with the code that sets configurations + * and with code managing interfaces and their altsettings. They must + * also handle different speeds and other-speed requests. + */ +static int populate_config_buf(struct usb_gadget *gadget, + u8 *buf, u8 type, unsigned index) +{ + enum usb_device_speed speed = gadget->speed; + int len; + const struct usb_descriptor_header **function; + + if (index > 0) + return -EINVAL; + + if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) + speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; + if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) + function = fsg_hs_function; + else + function = fsg_fs_function; + + /* for now, don't advertise srp-only devices */ + if (!gadget_is_otg(gadget)) + function++; + + len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); + ((struct usb_config_descriptor *) buf)->bDescriptorType = type; + return len; +} + + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* Caller must hold fsg->lock */ +static void wakeup_thread(struct fsg_dev *fsg) +{ + /* Tell the main thread that something has happened */ + fsg->thread_wakeup_needed = 1; + if (fsg->thread_task) + wake_up_process(fsg->thread_task); +} + + +static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) +{ + unsigned long flags; + + /* Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. */ + spin_lock_irqsave(&fsg->lock, flags); + if (fsg->state <= new_state) { + fsg->exception_req_tag = fsg->ep0_req_tag; + fsg->state = new_state; + if (fsg->thread_task) + send_sig_info(SIGUSR1, SEND_SIG_FORCED, + fsg->thread_task); + } + spin_unlock_irqrestore(&fsg->lock, flags); +} + + +/*-------------------------------------------------------------------------*/ + +/* The disconnect callback and ep0 routines. These always run in_irq, + * except that ep0_queue() is called in the main thread to acknowledge + * completion of various requests: set config, set interface, and + * Bulk-only device reset. */ + +static void fsg_disconnect(struct usb_gadget *gadget) +{ + struct fsg_dev *fsg = get_gadget_data(gadget); + + DBG(fsg, "disconnect or port reset\n"); + raise_exception(fsg, FSG_STATE_DISCONNECT); +} + + +static int ep0_queue(struct fsg_dev *fsg) +{ + int rc; + + rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); + if (rc != 0 && rc != -ESHUTDOWN) { + + /* We can't do much more than wait for a reset */ + WARNING(fsg, "error in submission: %s --> %d\n", + fsg->ep0->name, rc); + } + return rc; +} + +static void ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_dev *fsg = ep->driver_data; + + if (req->actual > 0) + dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual); + if (req->status || req->actual != req->length) + DBG(fsg, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) // Request was cancelled + usb_ep_fifo_flush(ep); + + if (req->status == 0 && req->context) + ((fsg_routine_t) (req->context))(fsg); +} + + +/*-------------------------------------------------------------------------*/ + +/* Bulk and interrupt endpoint completion handlers. + * These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_dev *fsg = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + DBG(fsg, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) // Request was cancelled + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&fsg->lock); + bh->inreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + wakeup_thread(fsg); + spin_unlock(&fsg->lock); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_dev *fsg = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + dump_msg(fsg, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) + DBG(fsg, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, + bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) // Request was cancelled + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&fsg->lock); + bh->outreq_busy = 0; + bh->state = BUF_STATE_FULL; + wakeup_thread(fsg); + spin_unlock(&fsg->lock); +} + + +#ifdef CONFIG_USB_FILE_STORAGE_TEST +static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_dev *fsg = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + DBG(fsg, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) // Request was cancelled + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&fsg->lock); + fsg->intreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + wakeup_thread(fsg); + spin_unlock(&fsg->lock); +} + +#else +static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) +{} +#endif /* CONFIG_USB_FILE_STORAGE_TEST */ + + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers. These always run in_irq. */ + +#ifdef CONFIG_USB_FILE_STORAGE_TEST +static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = fsg->ep0req; + static u8 cbi_reset_cmnd[6] = { + SC_SEND_DIAGNOSTIC, 4, 0xff, 0xff, 0xff, 0xff}; + + /* Error in command transfer? */ + if (req->status || req->length != req->actual || + req->actual < 6 || req->actual > MAX_COMMAND_SIZE) { + + /* Not all controllers allow a protocol stall after + * receiving control-out data, but we'll try anyway. */ + fsg_set_halt(fsg, fsg->ep0); + return; // Wait for reset + } + + /* Is it the special reset command? */ + if (req->actual >= sizeof cbi_reset_cmnd && + memcmp(req->buf, cbi_reset_cmnd, + sizeof cbi_reset_cmnd) == 0) { + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "cbi reset request\n"); + raise_exception(fsg, FSG_STATE_RESET); + return; + } + + VDBG(fsg, "CB[I] accept device-specific command\n"); + spin_lock(&fsg->lock); + + /* Save the command for later */ + if (fsg->cbbuf_cmnd_size) + WARNING(fsg, "CB[I] overwriting previous command\n"); + fsg->cbbuf_cmnd_size = req->actual; + memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); + + wakeup_thread(fsg); + spin_unlock(&fsg->lock); +} + +#else +static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{} +#endif /* CONFIG_USB_FILE_STORAGE_TEST */ + + +static int class_setup_req(struct fsg_dev *fsg, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = fsg->ep0req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (!fsg->config) + return value; + + /* Handle Bulk-only class-specific requests */ + if (transport_is_bbb()) { + switch (ctrl->bRequest) { + + case USB_BULK_RESET_REQUEST: + if (ctrl->bRequestType != (USB_DIR_OUT | + USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != 0 || w_value != 0) { + value = -EDOM; + break; + } + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg, FSG_STATE_RESET); + value = DELAYED_STATUS; + break; + + case USB_BULK_GET_MAX_LUN_REQUEST: + if (ctrl->bRequestType != (USB_DIR_IN | + USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != 0 || w_value != 0) { + value = -EDOM; + break; + } + VDBG(fsg, "get max LUN\n"); + *(u8 *) req->buf = fsg->nluns - 1; + value = 1; + break; + } + } + + /* Handle CBI class-specific requests */ + else { + switch (ctrl->bRequest) { + + case USB_CBI_ADSC_REQUEST: + if (ctrl->bRequestType != (USB_DIR_OUT | + USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != 0 || w_value != 0) { + value = -EDOM; + break; + } + if (w_length > MAX_COMMAND_SIZE) { + value = -EOVERFLOW; + break; + } + value = w_length; + fsg->ep0req->context = received_cbi_adsc; + break; + } + } + + if (value == -EOPNOTSUPP) + VDBG(fsg, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + le16_to_cpu(ctrl->wValue), w_index, w_length); + return value; +} + + +/*-------------------------------------------------------------------------*/ + +/* Ep0 standard request handlers. These always run in_irq. */ + +static int standard_setup_req(struct fsg_dev *fsg, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = fsg->ep0req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + + /* Usually this just stores reply data in the pre-allocated ep0 buffer, + * but config change events will also reconfigure hardware. */ + switch (ctrl->bRequest) { + + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) + break; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + VDBG(fsg, "get device descriptor\n"); + value = sizeof device_desc; + memcpy(req->buf, &device_desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + VDBG(fsg, "get device qualifier\n"); + if (!gadget_is_dualspeed(fsg->gadget)) + break; + value = sizeof dev_qualifier; + memcpy(req->buf, &dev_qualifier, value); + break; + + case USB_DT_OTHER_SPEED_CONFIG: + VDBG(fsg, "get other-speed config descriptor\n"); + if (!gadget_is_dualspeed(fsg->gadget)) + break; + goto get_config; + case USB_DT_CONFIG: + VDBG(fsg, "get configuration descriptor\n"); +get_config: + value = populate_config_buf(fsg->gadget, + req->buf, + w_value >> 8, + w_value & 0xff); + break; + + case USB_DT_STRING: + VDBG(fsg, "get string descriptor\n"); + + /* wIndex == language code */ + value = usb_gadget_get_string(&fsg_stringtab, + w_value & 0xff, req->buf); + break; + } + break; + + /* One config, two speeds */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) + break; + VDBG(fsg, "set configuration\n"); + if (w_value == CONFIG_VALUE || w_value == 0) { + fsg->new_config = w_value; + + /* Raise an exception to wipe out previous transaction + * state (queued bufs, etc) and set the new config. */ + raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); + value = DELAYED_STATUS; + } + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) + break; + VDBG(fsg, "get configuration\n"); + *(u8 *) req->buf = fsg->config; + value = 1; + break; + + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | + USB_RECIP_INTERFACE)) + break; + if (fsg->config && w_index == 0) { + + /* Raise an exception to wipe out previous transaction + * state (queued bufs, etc) and install the new + * interface altsetting. */ + raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE); + value = DELAYED_STATUS; + } + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | + USB_RECIP_INTERFACE)) + break; + if (!fsg->config) + break; + if (w_index != 0) { + value = -EDOM; + break; + } + VDBG(fsg, "get interface\n"); + *(u8 *) req->buf = 0; + value = 1; + break; + + default: + VDBG(fsg, + "unknown control req %02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, le16_to_cpu(ctrl->wLength)); + } + + return value; +} + + +static int fsg_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + struct fsg_dev *fsg = get_gadget_data(gadget); + int rc; + int w_length = le16_to_cpu(ctrl->wLength); + + ++fsg->ep0_req_tag; // Record arrival of a new request + fsg->ep0req->context = NULL; + fsg->ep0req->length = 0; + dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) + rc = class_setup_req(fsg, ctrl); + else + rc = standard_setup_req(fsg, ctrl); + + /* Respond with data/status or defer until later? */ + if (rc >= 0 && rc != DELAYED_STATUS) { + rc = min(rc, w_length); + fsg->ep0req->length = rc; + fsg->ep0req->zero = rc < w_length; + fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? + "ep0-in" : "ep0-out"); + rc = ep0_queue(fsg); + } + + /* Device either stalls (rc < 0) or reports success */ + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req, int *pbusy, + enum fsg_buffer_state *state) +{ + int rc; + + if (ep == fsg->bulk_in) + dump_msg(fsg, "bulk-in", req->buf, req->length); + else if (ep == fsg->intr_in) + dump_msg(fsg, "intr-in", req->buf, req->length); + + spin_lock_irq(&fsg->lock); + *pbusy = 1; + *state = BUF_STATE_BUSY; + spin_unlock_irq(&fsg->lock); + rc = usb_ep_queue(ep, req, GFP_KERNEL); + if (rc != 0) { + *pbusy = 0; + *state = BUF_STATE_EMPTY; + + /* We can't do much more than wait for a reset */ + + /* Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. */ + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && + req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); + } +} + + +static int sleep_thread(struct fsg_dev *fsg) +{ + int rc = 0; + + /* Wait until a signal arrives or we are woken up */ + for (;;) { + try_to_freeze(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + if (fsg->thread_wakeup_needed) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + fsg->thread_wakeup_needed = 0; + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int do_read(struct fsg_dev *fsg) +{ + struct fsg_lun *curlun = fsg->curlun; + u32 lba; + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + loff_t file_offset, file_offset_tmp; + unsigned int amount; + unsigned int partial_page; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (fsg->cmnd[0] == SC_READ_6) + lba = get_unaligned_be24(&fsg->cmnd[1]); + else { + lba = get_unaligned_be32(&fsg->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. */ + if ((fsg->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + file_offset = ((loff_t) lba) << 9; + + /* Carry out the file reads */ + amount_left = fsg->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; // No default reply + + for (;;) { + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. */ + amount = min((unsigned int) amount_left, mod_data.buflen); + amount = min((loff_t) amount, + curlun->file_length - file_offset); + partial_page = file_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - + partial_page); + + /* Wait for the next buffer to become available */ + bh = fsg->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } + + /* If we were asked to read past the end of file, + * end with an empty buffer. */ + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file read: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); // Round down to a block + } + file_offset += nread; + amount_left -= nread; + fsg->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + /* If an error occurred, report it and its position */ + if (nread < amount) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + if (amount_left == 0) + break; // No more left to read + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + } + + return -EIO; // No default reply +} + + +/*-------------------------------------------------------------------------*/ + +static int do_write(struct fsg_dev *fsg) +{ + struct fsg_lun *curlun = fsg->curlun; + u32 lba; + struct fsg_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + loff_t usb_offset, file_offset, file_offset_tmp; + unsigned int amount; + unsigned int partial_page; + ssize_t nwritten; + int rc; + + if (curlun->ro) { + curlun->sense_data = SS_WRITE_PROTECTED; + return -EINVAL; + } + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags &= ~O_SYNC; // Default is not to wait + spin_unlock(&curlun->filp->f_lock); + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (fsg->cmnd[0] == SC_WRITE_6) + lba = get_unaligned_be24(&fsg->cmnd[1]); + else { + lba = get_unaligned_be32(&fsg->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. */ + if ((fsg->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (fsg->cmnd[1] & 0x08) { // FUA + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags |= O_SYNC; + spin_unlock(&curlun->filp->f_lock); + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Carry out the file writes */ + get_some_more = 1; + file_offset = usb_offset = ((loff_t) lba) << 9; + amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = fsg->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* Figure out how much we want to get: + * Try to get the remaining amount. + * But don't get more than the buffer size. + * And don't try to go past the end of the file. + * If we're not at a page boundary, + * don't go past the next page. + * If this means getting 0, then we were asked + * to write past the end of file. + * Finally, round down to a block boundary. */ + amount = min(amount_left_to_req, mod_data.buflen); + amount = min((loff_t) amount, curlun->file_length - + usb_offset); + partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, + (unsigned int) PAGE_CACHE_SIZE - partial_page); + + if (amount == 0) { + get_some_more = 0; + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = usb_offset >> 9; + curlun->info_valid = 1; + continue; + } + amount -= (amount & 511); + if (amount == 0) { + + /* Why were we were asked to transfer a + * partial block? */ + get_some_more = 0; + continue; + } + + /* Get the next buffer */ + usb_offset += amount; + fsg->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = bh->bulk_out_intended_length = + amount; + bh->outreq->short_not_ok = 1; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = fsg->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; // We stopped early + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + fsg->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) { + curlun->sense_data = SS_COMMUNICATION_FAILURE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + amount = bh->outreq->actual; + if (curlun->file_length - file_offset < amount) { + LERROR(curlun, + "write %u @ %llu beyond end %llu\n", + amount, (unsigned long long) file_offset, + (unsigned long long) curlun->file_length); + amount = curlun->file_length - file_offset; + } + + /* Perform the write */ + file_offset_tmp = file_offset; + nwritten = vfs_write(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nwritten); + if (signal_pending(current)) + return -EINTR; // Interrupted! + + if (nwritten < 0) { + LDBG(curlun, "error in file write: %d\n", + (int) nwritten); + nwritten = 0; + } else if (nwritten < amount) { + LDBG(curlun, "partial file write: %d/%u\n", + (int) nwritten, amount); + nwritten -= (nwritten & 511); + // Round down to a block + } + file_offset += nwritten; + amount_left_to_write -= nwritten; + fsg->residue -= nwritten; + + /* If an error occurred, report it and its position */ + if (nwritten < amount) { + curlun->sense_data = SS_WRITE_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { + fsg->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = sleep_thread(fsg); + if (rc) + return rc; + } + + return -EIO; // No default reply +} + + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_dev *fsg) +{ + struct fsg_lun *curlun = fsg->curlun; + int rc; + + /* We ignore the requested LBA and write out all file's + * dirty data buffers. */ + rc = fsg_lun_fsync_sub(curlun); + if (rc) + curlun->sense_data = SS_WRITE_ERROR; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static void invalidate_sub(struct fsg_lun *curlun) +{ + struct file *filp = curlun->filp; + struct inode *inode = filp->f_path.dentry->d_inode; + unsigned long rc; + + rc = invalidate_mapping_pages(inode->i_mapping, 0, -1); + VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc); +} + +static int do_verify(struct fsg_dev *fsg) +{ + struct fsg_lun *curlun = fsg->curlun; + u32 lba; + u32 verification_length; + struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; + loff_t file_offset, file_offset_tmp; + u32 amount_left; + unsigned int amount; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + lba = get_unaligned_be32(&fsg->cmnd[2]); + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) but we don't implement it. */ + if ((fsg->cmnd[1] & ~0x10) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + verification_length = get_unaligned_be16(&fsg->cmnd[7]); + if (unlikely(verification_length == 0)) + return -EIO; // No default reply + + /* Prepare to carry out the file verify */ + amount_left = verification_length << 9; + file_offset = ((loff_t) lba) << 9; + + /* Write out all the dirty buffers before invalidating them */ + fsg_lun_fsync_sub(curlun); + if (signal_pending(current)) + return -EINTR; + + invalidate_sub(curlun); + if (signal_pending(current)) + return -EINTR; + + /* Just try to read the requested blocks */ + while (amount_left > 0) { + + /* Figure out how much we need to read: + * Try to read the remaining amount, but not more than + * the buffer size. + * And don't try to read past the end of the file. + * If this means reading 0 then we were asked to read + * past the end of file. */ + amount = min((unsigned int) amount_left, mod_data.buflen); + amount = min((loff_t) amount, + curlun->file_length - file_offset); + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file verify: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file verify: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); // Round down to a sector + } + if (nread == 0) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + file_offset += nread; + amount_left -= nread; + } + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + u8 *buf = (u8 *) bh->buf; + + static char vendor_id[] = "Linux "; + static char product_disk_id[] = "File-Stor Gadget"; + static char product_cdrom_id[] = "File-CD Gadget "; + + if (!fsg->curlun) { // Unsupported LUNs are okay + fsg->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; // Unsupported, no device-type + buf[4] = 31; // Additional length + return 36; + } + + memset(buf, 0, 8); + buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK); + if (mod_data.removable) + buf[1] = 0x80; + buf[2] = 2; // ANSI SCSI level 2 + buf[3] = 2; // SCSI-2 INQUIRY data format + buf[4] = 31; // Additional length + // No special options + sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, + (mod_data.cdrom ? product_cdrom_id : + product_disk_id), + mod_data.release); + return 36; +} + + +static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = fsg->curlun; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo; + int valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSG normally uses option a); enable this code to use option b). + */ +#if 0 + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!curlun) { // Unsupported LUNs are okay + fsg->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; + valid = 0; + } else { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + valid = curlun->info_valid << 7; + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; // Valid, current error + buf[2] = SK(sd); + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ + buf[7] = 18 - 8; // Additional sense length + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +} + + +static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = fsg->curlun; + u32 lba = get_unaligned_be32(&fsg->cmnd[2]); + int pmi = fsg->cmnd[8]; + u8 *buf = (u8 *) bh->buf; + + /* Check the PMI and LBA fields */ + if (pmi > 1 || (pmi == 0 && lba != 0)) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); + /* Max logical block */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + return 8; +} + + +static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = fsg->curlun; + int msf = fsg->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&fsg->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + + if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + + +static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = fsg->curlun; + int msf = fsg->cmnd[1] & 0x02; + int start_track = fsg->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + return 20; +} + + +static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = fsg->curlun; + int mscmnd = fsg->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((fsg->cmnd[1] & ~0x08) != 0) { // Mask away DBD + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = fsg->cmnd[2] >> 6; + page_code = fsg->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. */ + memset(buf, 0, 8); + if (mscmnd == SC_MODE_SENSE_6) { + buf[2] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA + buf += 4; + limit = 255; + } else { // SC_MODE_SENSE_10 + buf[3] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA + buf += 8; + limit = 65535; // Should really be mod_data.buflen + } + + /* No block descriptors */ + + /* The mode pages, in numerical order. The only page we support + * is the Caching page. */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; + buf[0] = 0x08; // Page code + buf[1] = 10; // Page length + memset(buf+2, 0, 10); // None of the fields are changeable + + if (!changeable_values) { + buf[2] = 0x04; // Write cache enable, + // Read cache not disabled + // No cache retention priorities + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ + /* Minimum prefetch = 0 */ + put_unaligned_be16(0xffff, &buf[8]); + /* Maximum prefetch */ + put_unaligned_be16(0xffff, &buf[10]); + /* Maximum prefetch ceiling */ + } + buf += 12; + } + + /* Check that a valid page was requested and the mode data length + * isn't too long. */ + len = buf - buf0; + if (!valid_page || len > limit) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mscmnd == SC_MODE_SENSE_6) + buf0[0] = len - 1; + else + put_unaligned_be16(len - 2, buf0); + return len; +} + + +static int do_start_stop(struct fsg_dev *fsg) +{ + struct fsg_lun *curlun = fsg->curlun; + int loej, start; + + if (!mod_data.removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + // int immed = fsg->cmnd[1] & 0x01; + loej = fsg->cmnd[4] & 0x02; + start = fsg->cmnd[4] & 0x01; + +#ifdef CONFIG_USB_FILE_STORAGE_TEST + if ((fsg->cmnd[1] & ~0x01) != 0 || // Mask away Immed + (fsg->cmnd[4] & ~0x03) != 0) { // Mask LoEj, Start + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (!start) { + + /* Are we allowed to unload the media? */ + if (curlun->prevent_medium_removal) { + LDBG(curlun, "unload attempt prevented\n"); + curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; + return -EINVAL; + } + if (loej) { // Simulate an unload/eject + up_read(&fsg->filesem); + down_write(&fsg->filesem); + fsg_lun_close(curlun); + up_write(&fsg->filesem); + down_read(&fsg->filesem); + } + } else { + + /* Our emulation doesn't support mounting; the medium is + * available for use as soon as it is loaded. */ + if (!fsg_lun_is_open(curlun)) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + } +#endif + return 0; +} + + +static int do_prevent_allow(struct fsg_dev *fsg) +{ + struct fsg_lun *curlun = fsg->curlun; + int prevent; + + if (!mod_data.removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = fsg->cmnd[4] & 0x01; + if ((fsg->cmnd[4] & ~0x01) != 0) { // Mask away Prevent + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (curlun->prevent_medium_removal && !prevent) + fsg_lun_fsync_sub(curlun); + curlun->prevent_medium_removal = prevent; + return 0; +} + + +static int do_read_format_capacities(struct fsg_dev *fsg, + struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = fsg->curlun; + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; // Only the Current/Maximum Capacity Descriptor + buf += 4; + + put_unaligned_be32(curlun->num_sectors, &buf[0]); + /* Number of blocks */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + buf[4] = 0x02; /* Current capacity */ + return 12; +} + + +static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = fsg->curlun; + + /* We don't support MODE SELECT */ + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + rc = fsg_set_halt(fsg, fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_halt(fsg->bulk_in); + } + return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + DBG(fsg, "bulk-in set wedge\n"); + rc = usb_ep_set_wedge(fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_wedge(fsg->bulk_in); + } + return rc; +} + +static int pad_with_zeros(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; + u32 nkeep = bh->inreq->length; + u32 nsend; + int rc; + + bh->state = BUF_STATE_EMPTY; // For the first iteration + fsg->usb_amount_left = nkeep + fsg->residue; + while (fsg->usb_amount_left > 0) { + + /* Wait for the next buffer to be free */ + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } + + nsend = min(fsg->usb_amount_left, (u32) mod_data.buflen); + memset(bh->buf + nkeep, 0, nsend - nkeep); + bh->inreq->length = nsend; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + bh = fsg->next_buffhd_to_fill = bh->next; + fsg->usb_amount_left -= nsend; + nkeep = 0; + } + return 0; +} + +static int throw_away_data(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh; + u32 amount; + int rc; + + while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY || + fsg->usb_amount_left > 0) { + + /* Throw away the data in a filled buffer */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + bh->state = BUF_STATE_EMPTY; + fsg->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual != bh->outreq->length || + bh->outreq->status != 0) { + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + return -EINTR; + } + continue; + } + + /* Try to submit another request if we need one */ + bh = fsg->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { + amount = min(fsg->usb_amount_left, + (u32) mod_data.buflen); + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = bh->bulk_out_intended_length = + amount; + bh->outreq->short_not_ok = 1; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + fsg->usb_amount_left -= amount; + continue; + } + + /* Otherwise wait for something to happen */ + rc = sleep_thread(fsg); + if (rc) + return rc; + } + return 0; +} + + +static int finish_reply(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; + int rc = 0; + + switch (fsg->data_dir) { + case DATA_DIR_NONE: + break; // Nothing to send + + /* If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ + case DATA_DIR_UNKNOWN: + if (mod_data.can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + rc = halt_bulk_in_endpoint(fsg); + } + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: + if (fsg->data_size == 0) + ; // Nothing to send + + /* If there's no residue, simply send the last buffer */ + else if (fsg->residue == 0) { + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + } + + /* There is a residue. For CB and CBI, simply mark the end + * of the data with a short packet. However, if we are + * allowed to stall, there was no data at all (residue == + * data_size), and the command failed (invalid LUN or + * sense data is set), then halt the bulk-in endpoint + * instead. */ + else if (!transport_is_bbb()) { + if (mod_data.can_stall && + fsg->residue == fsg->data_size && + (!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) { + bh->state = BUF_STATE_EMPTY; + rc = halt_bulk_in_endpoint(fsg); + } else { + bh->inreq->zero = 1; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + } + } + + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ + else { + if (mod_data.can_stall) { + bh->inreq->zero = 1; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + rc = halt_bulk_in_endpoint(fsg); + } else + rc = pad_with_zeros(fsg); + } + break; + + /* We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. */ + case DATA_DIR_FROM_HOST: + if (fsg->residue == 0) + ; // Nothing to receive + + /* Did the host stop sending unexpectedly early? */ + else if (fsg->short_packet_received) { + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; + } + + /* We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. + * The controller may already have ACK'ed all the remaining + * bulk-out packets, in which case the host wouldn't see a + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ +#if 0 + else if (mod_data.can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; + } +#endif + + /* We can't stall. Read in the excess data and throw it + * all away. */ + else + rc = throw_away_data(fsg); + break; + } + return rc; +} + + +static int send_status(struct fsg_dev *fsg) +{ + struct fsg_lun *curlun = fsg->curlun; + struct fsg_buffhd *bh; + int rc; + u8 status = USB_STATUS_PASS; + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ + bh = fsg->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } + + if (curlun) { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + } else if (fsg->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + + if (fsg->phase_error) { + DBG(fsg, "sending phase-error status\n"); + status = USB_STATUS_PHASE_ERROR; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { + DBG(fsg, "sending command-failure status\n"); + status = USB_STATUS_FAIL; + VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } + + if (transport_is_bbb()) { + struct bulk_cs_wrap *csw = bh->buf; + + /* Store and send the Bulk-only CSW */ + csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); + csw->Tag = fsg->tag; + csw->Residue = cpu_to_le32(fsg->residue); + csw->Status = status; + + bh->inreq->length = USB_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + + } else if (mod_data.transport_type == USB_PR_CB) { + + /* Control-Bulk transport has no status phase! */ + return 0; + + } else { // USB_PR_CBI + struct interrupt_data *buf = bh->buf; + + /* Store and send the Interrupt data. UFI sends the ASC + * and ASCQ bytes. Everything else sends a Type (which + * is always 0) and the status Value. */ + if (mod_data.protocol_type == USB_SC_UFI) { + buf->bType = ASC(sd); + buf->bValue = ASCQ(sd); + } else { + buf->bType = 0; + buf->bValue = status; + } + fsg->intreq->length = CBI_INTERRUPT_DATA_LEN; + + fsg->intr_buffhd = bh; // Point to the right buffhd + fsg->intreq->buf = bh->inreq->buf; + fsg->intreq->context = bh; + start_transfer(fsg, fsg->intr_in, fsg->intreq, + &fsg->intreq_busy, &bh->state); + } + + fsg->next_buffhd_to_fill = bh->next; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int check_command(struct fsg_dev *fsg, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) +{ + int i; + int lun = fsg->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsg_lun *curlun; + + /* Adjust the expected cmnd_size for protocol encapsulation padding. + * Transparent SCSI doesn't pad. */ + if (protocol_is_scsi()) + ; + + /* There's some disagreement as to whether RBC pads commands or not. + * We'll play it safe and accept either form. */ + else if (mod_data.protocol_type == USB_SC_RBC) { + if (fsg->cmnd_size == 12) + cmnd_size = 12; + + /* All the other protocols pad to 12 bytes */ + } else + cmnd_size = 12; + + hdlen[0] = 0; + if (fsg->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], + fsg->data_size); + VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen); + + /* We can't reply at all until we know the correct data direction + * and size. */ + if (fsg->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; + if (fsg->data_dir == DATA_DIR_UNKNOWN) { // CB or CBI + fsg->data_dir = data_dir; + fsg->data_size = fsg->data_size_from_cmnd; + + } else { // Bulk-only + if (fsg->data_size < fsg->data_size_from_cmnd) { + + /* Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much + * as we are allowed. */ + fsg->data_size_from_cmnd = fsg->data_size; + fsg->phase_error = 1; + } + } + fsg->residue = fsg->usb_amount_left = fsg->data_size; + + /* Conflicting data directions is a phase error */ + if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) { + fsg->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ + if (cmnd_size != fsg->cmnd_size) { + + /* Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= fsg->cmnd_size) { + DBG(fsg, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, fsg->cmnd_size); + cmnd_size = fsg->cmnd_size; + } else { + fsg->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ + if (transport_is_bbb()) { + if (fsg->lun != lun) + DBG(fsg, "using LUN %d from CBW, " + "not LUN %d from CDB\n", + fsg->lun, lun); + } else + fsg->lun = lun; // Use LUN from the command + + /* Check the LUN */ + if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { + fsg->curlun = curlun = &fsg->luns[fsg->lun]; + if (fsg->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + } else { + fsg->curlun = curlun = NULL; + fsg->bad_lun_okay = 0; + + /* INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. */ + if (fsg->cmnd[0] != SC_INQUIRY && + fsg->cmnd[0] != SC_REQUEST_SENSE) { + DBG(fsg, "unsupported LUN %d\n", fsg->lun); + return -EINVAL; + } + } + + /* If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && + fsg->cmnd[0] != SC_INQUIRY && + fsg->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } + + /* Check that only command bytes listed in the mask are non-zero */ + fsg->cmnd[1] &= 0x1f; // Mask away the LUN + for (i = 1; i < cmnd_size; ++i) { + if (fsg->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + + /* If the medium isn't mounted and the command needs to access + * it, return an error. */ + if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + + return 0; +} + + +static int do_scsi_command(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh; + int rc; + int reply = -EINVAL; + int i; + static char unknown[16]; + + dump_cdb(fsg); + + /* Wait for the next buffer to become available for data or status */ + bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } + fsg->phase_error = 0; + fsg->short_packet_received = 0; + + down_read(&fsg->filesem); // We're using the backing file + switch (fsg->cmnd[0]) { + + case SC_INQUIRY: + fsg->data_size_from_cmnd = fsg->cmnd[4]; + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY")) == 0) + reply = do_inquiry(fsg, bh); + break; + + case SC_MODE_SELECT_6: + fsg->data_size_from_cmnd = fsg->cmnd[4]; + if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)")) == 0) + reply = do_mode_select(fsg, bh); + break; + + case SC_MODE_SELECT_10: + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)")) == 0) + reply = do_mode_select(fsg, bh); + break; + + case SC_MODE_SENSE_6: + fsg->data_size_from_cmnd = fsg->cmnd[4]; + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)")) == 0) + reply = do_mode_sense(fsg, bh); + break; + + case SC_MODE_SENSE_10: + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)")) == 0) + reply = do_mode_sense(fsg, bh); + break; + + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: + fsg->data_size_from_cmnd = 0; + if ((reply = check_command(fsg, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL")) == 0) + reply = do_prevent_allow(fsg); + break; + + case SC_READ_6: + i = fsg->cmnd[4]; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)")) == 0) + reply = do_read(fsg); + break; + + case SC_READ_10: + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->cmnd[7]) << 9; + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)")) == 0) + reply = do_read(fsg); + break; + + case SC_READ_12: + fsg->data_size_from_cmnd = + get_unaligned_be32(&fsg->cmnd[6]) << 9; + if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)")) == 0) + reply = do_read(fsg); + break; + + case SC_READ_CAPACITY: + fsg->data_size_from_cmnd = 8; + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY")) == 0) + reply = do_read_capacity(fsg, bh); + break; + + case SC_READ_HEADER: + if (!mod_data.cdrom) + goto unknown_cmnd; + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER")) == 0) + reply = do_read_header(fsg, bh); + break; + + case SC_READ_TOC: + if (!mod_data.cdrom) + goto unknown_cmnd; + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC")) == 0) + reply = do_read_toc(fsg, bh); + break; + + case SC_READ_FORMAT_CAPACITIES: + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES")) == 0) + reply = do_read_format_capacities(fsg, bh); + break; + + case SC_REQUEST_SENSE: + fsg->data_size_from_cmnd = fsg->cmnd[4]; + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE")) == 0) + reply = do_request_sense(fsg, bh); + break; + + case SC_START_STOP_UNIT: + fsg->data_size_from_cmnd = 0; + if ((reply = check_command(fsg, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT")) == 0) + reply = do_start_stop(fsg); + break; + + case SC_SYNCHRONIZE_CACHE: + fsg->data_size_from_cmnd = 0; + if ((reply = check_command(fsg, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE")) == 0) + reply = do_synchronize_cache(fsg); + break; + + case SC_TEST_UNIT_READY: + fsg->data_size_from_cmnd = 0; + reply = check_command(fsg, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; + + /* Although optional, this command is used by MS-Windows. We + * support a minimal version: BytChk must be 0. */ + case SC_VERIFY: + fsg->data_size_from_cmnd = 0; + if ((reply = check_command(fsg, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY")) == 0) + reply = do_verify(fsg); + break; + + case SC_WRITE_6: + i = fsg->cmnd[4]; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)")) == 0) + reply = do_write(fsg); + break; + + case SC_WRITE_10: + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->cmnd[7]) << 9; + if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)")) == 0) + reply = do_write(fsg); + break; + + case SC_WRITE_12: + fsg->data_size_from_cmnd = + get_unaligned_be32(&fsg->cmnd[6]) << 9; + if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)")) == 0) + reply = do_write(fsg); + break; + + /* Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise + * for anyone interested to implement RESERVE and RELEASE in terms + * of Posix locks. */ + case SC_FORMAT_UNIT: + case SC_RELEASE: + case SC_RESERVE: + case SC_SEND_DIAGNOSTIC: + // Fall through + + default: + unknown_cmnd: + fsg->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); + if ((reply = check_command(fsg, fsg->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) { + fsg->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } + up_read(&fsg->filesem); + + if (reply == -EINTR || signal_pending(current)) + return -EINTR; + + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) + reply = 0; // Error reply length + if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, fsg->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + fsg->residue -= reply; + } // Otherwise it's already set + + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct fsg_bulk_cb_wrap *cbw = req->buf; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) + return -EINVAL; + + /* Is the CBW valid? */ + if (req->actual != USB_BULK_CB_WRAP_LEN || + cbw->Signature != cpu_to_le32( + USB_BULK_CB_SIG)) { + DBG(fsg, "invalid CBW: len %u sig 0x%x\n", + req->actual, + le32_to_cpu(cbw->Signature)); + + /* The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. */ + wedge_bulk_in_endpoint(fsg); + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + return -EINVAL; + } + + /* Is the CBW meaningful? */ + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { + DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " + "cmdlen %u\n", + cbw->Lun, cbw->Flags, cbw->Length); + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ + if (mod_data.can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } + return -EINVAL; + } + + /* Save the command for later */ + fsg->cmnd_size = cbw->Length; + memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size); + if (cbw->Flags & USB_BULK_IN_FLAG) + fsg->data_dir = DATA_DIR_TO_HOST; + else + fsg->data_dir = DATA_DIR_FROM_HOST; + fsg->data_size = le32_to_cpu(cbw->DataTransferLength); + if (fsg->data_size == 0) + fsg->data_dir = DATA_DIR_NONE; + fsg->lun = cbw->Lun; + fsg->tag = cbw->Tag; + return 0; +} + + +static int get_next_command(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh; + int rc = 0; + + if (transport_is_bbb()) { + + /* Wait for the next buffer to become available */ + bh = fsg->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } + + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); + + /* We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. */ + + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } + smp_rmb(); + rc = received_cbw(fsg, bh); + bh->state = BUF_STATE_EMPTY; + + } else { // USB_PR_CB or USB_PR_CBI + + /* Wait for the next command to arrive */ + while (fsg->cbbuf_cmnd_size == 0) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } + + /* Is the previous status interrupt request still busy? + * The host is allowed to skip reading the status, + * so we must cancel it. */ + if (fsg->intreq_busy) + usb_ep_dequeue(fsg->intr_in, fsg->intreq); + + /* Copy the command and mark the buffer empty */ + fsg->data_dir = DATA_DIR_UNKNOWN; + spin_lock_irq(&fsg->lock); + fsg->cmnd_size = fsg->cbbuf_cmnd_size; + memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size); + fsg->cbbuf_cmnd_size = 0; + spin_unlock_irq(&fsg->lock); + } + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, + const struct usb_endpoint_descriptor *d) +{ + int rc; + + ep->driver_data = fsg; + rc = usb_ep_enable(ep, d); + if (rc) + ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); + return rc; +} + +static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (*preq) + return 0; + ERROR(fsg, "can't allocate request for %s\n", ep->name); + return -ENOMEM; +} + +/* + * Reset interface setting and re-init endpoint state (toggle etc). + * Call with altsetting < 0 to disable the interface. The only other + * available altsetting is 0, which enables the interface. + */ +static int do_set_interface(struct fsg_dev *fsg, int altsetting) +{ + int rc = 0; + int i; + const struct usb_endpoint_descriptor *d; + + if (fsg->running) + DBG(fsg, "reset interface\n"); + +reset: + /* Deallocate the requests */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } + if (fsg->intreq) { + usb_ep_free_request(fsg->intr_in, fsg->intreq); + fsg->intreq = NULL; + } + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + if (fsg->intr_in_enabled) { + usb_ep_disable(fsg->intr_in); + fsg->intr_in_enabled = 0; + } + + fsg->running = 0; + if (altsetting < 0 || rc != 0) + return rc; + + DBG(fsg, "set interface %d\n", altsetting); + + /* Enable the endpoints */ + d = fsg_ep_desc(fsg->gadget, + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); + if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) + goto reset; + fsg->bulk_in_enabled = 1; + + d = fsg_ep_desc(fsg->gadget, + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); + if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) + goto reset; + fsg->bulk_out_enabled = 1; + fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + + if (transport_is_cbi()) { + d = fsg_ep_desc(fsg->gadget, + &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc); + if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) + goto reset; + fsg->intr_in_enabled = 1; + } + + /* Allocate the requests */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) + goto reset; + if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + if (transport_is_cbi()) { + if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0) + goto reset; + fsg->intreq->complete = intr_in_complete; + } + + fsg->running = 1; + for (i = 0; i < fsg->nluns; ++i) + fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; + return rc; +} + + +/* + * Change our operational configuration. This code must agree with the code + * that returns config descriptors, and with interface altsetting code. + * + * It's also responsible for power management interactions. Some + * configurations might not work with our current power sources. + * For now we just assume the gadget is always self-powered. + */ +static int do_set_config(struct fsg_dev *fsg, u8 new_config) +{ + int rc = 0; + + /* Disable the single interface */ + if (fsg->config != 0) { + DBG(fsg, "reset config\n"); + fsg->config = 0; + rc = do_set_interface(fsg, -1); + } + + /* Enable the interface */ + if (new_config != 0) { + fsg->config = new_config; + if ((rc = do_set_interface(fsg, 0)) != 0) + fsg->config = 0; // Reset on errors + else { + char *speed; + + switch (fsg->gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } + INFO(fsg, "%s speed config #%d\n", speed, fsg->config); + } + } + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_dev *fsg) +{ + siginfo_t info; + int sig; + int i; + int num_active; + struct fsg_buffhd *bh; + enum fsg_state old_state; + u8 new_config; + struct fsg_lun *curlun; + unsigned int exception_req_tag; + int rc; + + /* Clear the existing signals. Anything but SIGUSR1 is converted + * into a high-priority EXIT exception. */ + for (;;) { + sig = dequeue_signal_lock(current, ¤t->blocked, &info); + if (!sig) + break; + if (sig != SIGUSR1) { + if (fsg->state < FSG_STATE_EXIT) + DBG(fsg, "Main thread exiting on signal\n"); + raise_exception(fsg, FSG_STATE_EXIT); + } + } + + /* Cancel all the pending transfers */ + if (fsg->intreq_busy) + usb_ep_dequeue(fsg->intr_in, fsg->intreq); + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &fsg->buffhds[i]; + if (bh->inreq_busy) + usb_ep_dequeue(fsg->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(fsg->bulk_out, bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { + num_active = fsg->intreq_busy; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &fsg->buffhds[i]; + num_active += bh->inreq_busy + bh->outreq_busy; + } + if (num_active == 0) + break; + if (sleep_thread(fsg)) + return; + } + + /* Clear out the controller's fifos */ + if (fsg->bulk_in_enabled) + usb_ep_fifo_flush(fsg->bulk_in); + if (fsg->bulk_out_enabled) + usb_ep_fifo_flush(fsg->bulk_out); + if (fsg->intr_in_enabled) + usb_ep_fifo_flush(fsg->intr_in); + + /* Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. */ + spin_lock_irq(&fsg->lock); + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &fsg->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } + fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain = + &fsg->buffhds[0]; + + exception_req_tag = fsg->exception_req_tag; + new_config = fsg->new_config; + old_state = fsg->state; + + if (old_state == FSG_STATE_ABORT_BULK_OUT) + fsg->state = FSG_STATE_STATUS_PHASE; + else { + for (i = 0; i < fsg->nluns; ++i) { + curlun = &fsg->luns[i]; + curlun->prevent_medium_removal = 0; + curlun->sense_data = curlun->unit_attention_data = + SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + fsg->state = FSG_STATE_IDLE; + } + spin_unlock_irq(&fsg->lock); + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + default: + break; + + case FSG_STATE_ABORT_BULK_OUT: + send_status(fsg); + spin_lock_irq(&fsg->lock); + if (fsg->state == FSG_STATE_STATUS_PHASE) + fsg->state = FSG_STATE_IDLE; + spin_unlock_irq(&fsg->lock); + break; + + case FSG_STATE_RESET: + /* In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ + if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) + usb_ep_clear_halt(fsg->bulk_in); + + if (transport_is_bbb()) { + if (fsg->ep0_req_tag == exception_req_tag) + ep0_queue(fsg); // Complete the status stage + + } else if (transport_is_cbi()) + send_status(fsg); // Status by interrupt pipe + + /* Technically this should go here, but it would only be + * a waste of time. Ditto for the INTERFACE_CHANGE and + * CONFIG_CHANGE cases. */ + // for (i = 0; i < fsg->nluns; ++i) + // fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; + break; + + case FSG_STATE_INTERFACE_CHANGE: + rc = do_set_interface(fsg, 0); + if (fsg->ep0_req_tag != exception_req_tag) + break; + if (rc != 0) // STALL on errors + fsg_set_halt(fsg, fsg->ep0); + else // Complete the status stage + ep0_queue(fsg); + break; + + case FSG_STATE_CONFIG_CHANGE: + rc = do_set_config(fsg, new_config); + if (fsg->ep0_req_tag != exception_req_tag) + break; + if (rc != 0) // STALL on errors + fsg_set_halt(fsg, fsg->ep0); + else // Complete the status stage + ep0_queue(fsg); + break; + + case FSG_STATE_DISCONNECT: + for (i = 0; i < fsg->nluns; ++i) + fsg_lun_fsync_sub(fsg->luns + i); + do_set_config(fsg, 0); // Unconfigured state + break; + + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: + do_set_config(fsg, 0); // Free resources + spin_lock_irq(&fsg->lock); + fsg->state = FSG_STATE_TERMINATED; // Stop the thread + spin_unlock_irq(&fsg->lock); + break; + } +} + + +/*-------------------------------------------------------------------------*/ + +static int fsg_main_thread(void *fsg_) +{ + struct fsg_dev *fsg = fsg_; + + /* Allow the thread to be killed by a signal, but set the signal mask + * to block everything but INT, TERM, KILL, and USR1. */ + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + allow_signal(SIGUSR1); + + /* Allow the thread to be frozen */ + set_freezable(); + + /* Arrange for userspace references to be interpreted as kernel + * pointers. That way we can pass a kernel pointer to a routine + * that expects a __user pointer and it will work okay. */ + set_fs(get_ds()); + + /* The main loop */ + while (fsg->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(fsg) || signal_pending(current)) { + handle_exception(fsg); + continue; + } + + if (!fsg->running) { + sleep_thread(fsg); + continue; + } + + if (get_next_command(fsg)) + continue; + + spin_lock_irq(&fsg->lock); + if (!exception_in_progress(fsg)) + fsg->state = FSG_STATE_DATA_PHASE; + spin_unlock_irq(&fsg->lock); + + if (do_scsi_command(fsg) || finish_reply(fsg)) + continue; + + spin_lock_irq(&fsg->lock); + if (!exception_in_progress(fsg)) + fsg->state = FSG_STATE_STATUS_PHASE; + spin_unlock_irq(&fsg->lock); + + if (send_status(fsg)) + continue; + + spin_lock_irq(&fsg->lock); + if (!exception_in_progress(fsg)) + fsg->state = FSG_STATE_IDLE; + spin_unlock_irq(&fsg->lock); + } + + spin_lock_irq(&fsg->lock); + fsg->thread_task = NULL; + spin_unlock_irq(&fsg->lock); + + /* If we are exiting because of a signal, unregister the + * gadget driver. */ + if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) + usb_gadget_unregister_driver(&fsg_driver); + + /* Let the unbind and cleanup routines know the thread has exited */ + complete_and_exit(&fsg->thread_notifier, 0); +} + + +/*-------------------------------------------------------------------------*/ + + +/* The write permissions and store_xxx pointers are set in fsg_bind() */ +static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); +static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); + + +/*-------------------------------------------------------------------------*/ + +static void fsg_release(struct kref *ref) +{ + struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref); + + kfree(fsg->luns); + kfree(fsg); +} + +static void lun_release(struct device *dev) +{ + struct rw_semaphore *filesem = dev_get_drvdata(dev); + struct fsg_dev *fsg = + container_of(filesem, struct fsg_dev, filesem); + + kref_put(&fsg->ref, fsg_release); +} + +static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) +{ + struct fsg_dev *fsg = get_gadget_data(gadget); + int i; + struct fsg_lun *curlun; + struct usb_request *req = fsg->ep0req; + + DBG(fsg, "unbind\n"); + clear_bit(REGISTERED, &fsg->atomic_bitflags); + + /* Unregister the sysfs attribute files and the LUNs */ + for (i = 0; i < fsg->nluns; ++i) { + curlun = &fsg->luns[i]; + if (curlun->registered) { + device_remove_file(&curlun->dev, &dev_attr_ro); + device_remove_file(&curlun->dev, &dev_attr_file); + fsg_lun_close(curlun); + device_unregister(&curlun->dev); + curlun->registered = 0; + } + } + + /* If the thread isn't already dead, tell it to exit now */ + if (fsg->state != FSG_STATE_TERMINATED) { + raise_exception(fsg, FSG_STATE_EXIT); + wait_for_completion(&fsg->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&fsg->thread_notifier); + } + + /* Free the data buffers */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) + kfree(fsg->buffhds[i].buf); + + /* Free the request and buffer for endpoint 0 */ + if (req) { + kfree(req->buf); + usb_ep_free_request(fsg->ep0, req); + } + + set_gadget_data(gadget, NULL); +} + + +static int __init check_parameters(struct fsg_dev *fsg) +{ + int prot; + int gcnum; + + /* Store the default values */ + mod_data.transport_type = USB_PR_BULK; + mod_data.transport_name = "Bulk-only"; + mod_data.protocol_type = USB_SC_SCSI; + mod_data.protocol_name = "Transparent SCSI"; + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) + mod_data.can_stall = 0; + + if (mod_data.release == 0xffff) { // Parameter wasn't set + /* The sa1100 controller is not supported */ + if (gadget_is_sa1100(fsg->gadget)) + gcnum = -1; + else + gcnum = usb_gadget_controller_number(fsg->gadget); + if (gcnum >= 0) + mod_data.release = 0x0300 + gcnum; + else { + WARNING(fsg, "controller '%s' not recognized\n", + fsg->gadget->name); + mod_data.release = 0x0399; + } + } + + prot = simple_strtol(mod_data.protocol_parm, NULL, 0); + +#ifdef CONFIG_USB_FILE_STORAGE_TEST + if (strnicmp(mod_data.transport_parm, "BBB", 10) == 0) { + ; // Use default setting + } else if (strnicmp(mod_data.transport_parm, "CB", 10) == 0) { + mod_data.transport_type = USB_PR_CB; + mod_data.transport_name = "Control-Bulk"; + } else if (strnicmp(mod_data.transport_parm, "CBI", 10) == 0) { + mod_data.transport_type = USB_PR_CBI; + mod_data.transport_name = "Control-Bulk-Interrupt"; + } else { + ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm); + return -EINVAL; + } + + if (strnicmp(mod_data.protocol_parm, "SCSI", 10) == 0 || + prot == USB_SC_SCSI) { + ; // Use default setting + } else if (strnicmp(mod_data.protocol_parm, "RBC", 10) == 0 || + prot == USB_SC_RBC) { + mod_data.protocol_type = USB_SC_RBC; + mod_data.protocol_name = "RBC"; + } else if (strnicmp(mod_data.protocol_parm, "8020", 4) == 0 || + strnicmp(mod_data.protocol_parm, "ATAPI", 10) == 0 || + prot == USB_SC_8020) { + mod_data.protocol_type = USB_SC_8020; + mod_data.protocol_name = "8020i (ATAPI)"; + } else if (strnicmp(mod_data.protocol_parm, "QIC", 3) == 0 || + prot == USB_SC_QIC) { + mod_data.protocol_type = USB_SC_QIC; + mod_data.protocol_name = "QIC-157"; + } else if (strnicmp(mod_data.protocol_parm, "UFI", 10) == 0 || + prot == USB_SC_UFI) { + mod_data.protocol_type = USB_SC_UFI; + mod_data.protocol_name = "UFI"; + } else if (strnicmp(mod_data.protocol_parm, "8070", 4) == 0 || + prot == USB_SC_8070) { + mod_data.protocol_type = USB_SC_8070; + mod_data.protocol_name = "8070i"; + } else { + ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm); + return -EINVAL; + } + + mod_data.buflen &= PAGE_CACHE_MASK; + if (mod_data.buflen <= 0) { + ERROR(fsg, "invalid buflen\n"); + return -ETOOSMALL; + } +#endif /* CONFIG_USB_FILE_STORAGE_TEST */ + + return 0; +} + + +static int __init fsg_bind(struct usb_gadget *gadget) +{ + struct fsg_dev *fsg = the_fsg; + int rc; + int i; + struct fsg_lun *curlun; + struct usb_ep *ep; + struct usb_request *req; + char *pathbuf, *p; + + fsg->gadget = gadget; + set_gadget_data(gadget, fsg); + fsg->ep0 = gadget->ep0; + fsg->ep0->driver_data = fsg; + + if ((rc = check_parameters(fsg)) != 0) + goto out; + + if (mod_data.removable) { // Enable the store_xxx attributes + dev_attr_file.attr.mode = 0644; + dev_attr_file.store = fsg_store_file; + if (!mod_data.cdrom) { + dev_attr_ro.attr.mode = 0644; + dev_attr_ro.store = fsg_store_ro; + } + } + + /* Find out how many LUNs there should be */ + i = mod_data.nluns; + if (i == 0) + i = max(mod_data.num_filenames, 1u); + if (i > FSG_MAX_LUNS) { + ERROR(fsg, "invalid number of LUNs: %d\n", i); + rc = -EINVAL; + goto out; + } + + /* Create the LUNs, open their backing files, and register the + * LUN devices in sysfs. */ + fsg->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL); + if (!fsg->luns) { + rc = -ENOMEM; + goto out; + } + fsg->nluns = i; + + for (i = 0; i < fsg->nluns; ++i) { + curlun = &fsg->luns[i]; + curlun->cdrom = !!mod_data.cdrom; + curlun->ro = mod_data.cdrom || mod_data.ro[i]; + curlun->initially_ro = curlun->ro; + curlun->removable = mod_data.removable; + curlun->dev.release = lun_release; + curlun->dev.parent = &gadget->dev; + curlun->dev.driver = &fsg_driver.driver; + dev_set_drvdata(&curlun->dev, &fsg->filesem); + dev_set_name(&curlun->dev,"%s-lun%d", + dev_name(&gadget->dev), i); + + if ((rc = device_register(&curlun->dev)) != 0) { + INFO(fsg, "failed to register LUN%d: %d\n", i, rc); + goto out; + } + if ((rc = device_create_file(&curlun->dev, + &dev_attr_ro)) != 0 || + (rc = device_create_file(&curlun->dev, + &dev_attr_file)) != 0) { + device_unregister(&curlun->dev); + goto out; + } + curlun->registered = 1; + kref_get(&fsg->ref); + + if (mod_data.file[i] && *mod_data.file[i]) { + if ((rc = fsg_lun_open(curlun, + mod_data.file[i])) != 0) + goto out; + } else if (!mod_data.removable) { + ERROR(fsg, "no file given for LUN%d\n", i); + rc = -EINVAL; + goto out; + } + } + + /* Find all the endpoints we will use */ + usb_ep_autoconfig_reset(gadget); + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint + fsg->bulk_out = ep; + + if (transport_is_cbi()) { + ep = usb_ep_autoconfig(gadget, &fsg_fs_intr_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint + fsg->intr_in = ep; + } + + /* Fix up the descriptors */ + device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; + device_desc.idVendor = cpu_to_le16(mod_data.vendor); + device_desc.idProduct = cpu_to_le16(mod_data.product); + device_desc.bcdDevice = cpu_to_le16(mod_data.release); + + i = (transport_is_cbi() ? 3 : 2); // Number of endpoints + fsg_intf_desc.bNumEndpoints = i; + fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type; + fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type; + fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL; + + if (gadget_is_dualspeed(gadget)) { + fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL; + + /* Assume ep0 uses the same maxpacket value for both speeds */ + dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; + + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_hs_intr_in_desc.bEndpointAddress = + fsg_fs_intr_in_desc.bEndpointAddress; + } + + if (gadget_is_otg(gadget)) + fsg_otg_desc.bmAttributes |= USB_OTG_HNP; + + rc = -ENOMEM; + + /* Allocate the request and buffer for endpoint 0 */ + fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); + if (!req) + goto out; + req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); + if (!req->buf) + goto out; + req->complete = ep0_complete; + + /* Allocate the data buffers */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &fsg->buffhds[i]; + + /* Allocate for the bulk-in endpoint. We assume that + * the buffer will also work with the bulk-out (and + * interrupt-in) endpoint. */ + bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL); + if (!bh->buf) + goto out; + bh->next = bh + 1; + } + fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0]; + + /* This should reflect the actual gadget power source */ + usb_gadget_set_selfpowered(gadget); + + snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer, + "%s %s with %s", + 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)) { + rc = PTR_ERR(fsg->thread_task); + goto out; + } + + INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); + INFO(fsg, "Number of LUNs=%d\n", fsg->nluns); + + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); + for (i = 0; i < fsg->nluns; ++i) { + curlun = &fsg->luns[i]; + if (fsg_lun_is_open(curlun)) { + p = NULL; + if (pathbuf) { + p = d_path(&curlun->filp->f_path, + pathbuf, PATH_MAX); + if (IS_ERR(p)) + p = NULL; + } + LINFO(curlun, "ro=%d, file: %s\n", + curlun->ro, (p ? p : "(error)")); + } + } + kfree(pathbuf); + + DBG(fsg, "transport=%s (x%02x)\n", + mod_data.transport_name, mod_data.transport_type); + DBG(fsg, "protocol=%s (x%02x)\n", + mod_data.protocol_name, mod_data.protocol_type); + DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", + mod_data.vendor, mod_data.product, mod_data.release); + DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", + mod_data.removable, mod_data.can_stall, + mod_data.cdrom, mod_data.buflen); + DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); + + set_bit(REGISTERED, &fsg->atomic_bitflags); + + /* Tell the thread to start working */ + wake_up_process(fsg->thread_task); + return 0; + +autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); + rc = -ENOTSUPP; + +out: + fsg->state = FSG_STATE_TERMINATED; // The thread is dead + fsg_unbind(gadget); + complete(&fsg->thread_notifier); + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static void fsg_suspend(struct usb_gadget *gadget) +{ + struct fsg_dev *fsg = get_gadget_data(gadget); + + DBG(fsg, "suspend\n"); + set_bit(SUSPENDED, &fsg->atomic_bitflags); +} + +static void fsg_resume(struct usb_gadget *gadget) +{ + struct fsg_dev *fsg = get_gadget_data(gadget); + + DBG(fsg, "resume\n"); + clear_bit(SUSPENDED, &fsg->atomic_bitflags); +} + + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver fsg_driver = { +#ifdef CONFIG_USB_GADGET_DUALSPEED + .speed = USB_SPEED_HIGH, +#else + .speed = USB_SPEED_FULL, +#endif + .function = (char *) fsg_string_product, + .bind = fsg_bind, + .unbind = fsg_unbind, + .disconnect = fsg_disconnect, + .setup = fsg_setup, + .suspend = fsg_suspend, + .resume = fsg_resume, + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + // .release = ... + // .suspend = ... + // .resume = ... + }, +}; + + +static int __init fsg_alloc(void) +{ + struct fsg_dev *fsg; + + fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + if (!fsg) + return -ENOMEM; + spin_lock_init(&fsg->lock); + init_rwsem(&fsg->filesem); + kref_init(&fsg->ref); + init_completion(&fsg->thread_notifier); + + the_fsg = fsg; + return 0; +} + + +static int __init fsg_init(void) +{ + int rc; + struct fsg_dev *fsg; + + if ((rc = fsg_alloc()) != 0) + return rc; + fsg = the_fsg; + if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) + kref_put(&fsg->ref, fsg_release); + return rc; +} +module_init(fsg_init); + + +static void __exit fsg_cleanup(void) +{ + struct fsg_dev *fsg = the_fsg; + + /* Unregister the driver iff the thread hasn't already done so */ + if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) + usb_gadget_unregister_driver(&fsg_driver); + + /* Wait for the thread to finish up */ + wait_for_completion(&fsg->thread_notifier); + + kref_put(&fsg->ref, fsg_release); +} +module_exit(fsg_cleanup); -- cgit v1.2.3 From be94679ae87f7ed25456e0177e1aea3e3c2a09a6 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:19 +0100 Subject: USB: g_mass_storage: testing code from f_mass_storage.c removed Removed code that was included when CONFIG_USB_FILE_STORAGE_TEST was defined. If this functionality is required one may still use the original File-backed Storage Gadget. It has been agreed that testing functionality is not required in the composite function. Also removed fsg_suspend() and fsg_resume() which were no operations. Moreover, storage_common.c has been modified in such a way that defining certain macros skips parts of the file. Those macros are: * FSG_NO_INTR_EP -- skips interrupt endpoint descriptors * FSG_NO_DEVICE_STRINGS -- skips certain strings * FSG_NO_OTG -- skips OTG descriptor Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 662 +++++------------------------------- drivers/usb/gadget/storage_common.c | 50 ++- 2 files changed, 140 insertions(+), 572 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 7998402b1840..ed16a9538753 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -50,14 +50,6 @@ * access is always read-only.) The gadget will indicate that it has * removable media if the optional "removable" module parameter is set. * - * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), - * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected - * by the optional "transport" module parameter. It also supports the - * 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. - * * 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 * "luns" module parameter (anywhere from 1 to 8), and the corresponding @@ -99,20 +91,6 @@ * bulk endpoints * cdrom Default false, boolean for whether to emulate * a CD-ROM drive - * transport=XXX Default BBB, transport name (CB, CBI, or BBB) - * protocol=YYY Default SCSI, protocol name (RBC, 8020 or - * ATAPI, QIC, UFI, 8070, or SCSI; - * also 1 - 6) - * vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID - * product=0xPPPP Default 0xa4a5 (FSG), USB Product ID - * release=0xRRRR Override the USB release number (bcdDevice) - * buflen=N Default N=16384, buffer size used (will be - * rounded down to a multiple of - * PAGE_CACHE_SIZE) - * - * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", - * "removable", "luns", "stall", and "cdrom" options are available; default - * values are used for everything else. * * The pathnames of the backing files and the ro settings are available in * the attribute files "file" and "ro" in the lun subdirectory of the @@ -279,6 +257,8 @@ static const char fsg_string_config[] = "Self-powered"; static const char fsg_string_interface[] = "Mass Storage"; +#define FSG_NO_INTR_EP 1 + #include "storage_common.c" @@ -309,28 +289,11 @@ static struct { int can_stall; int cdrom; - char *transport_parm; - char *protocol_parm; - unsigned short vendor; - unsigned short product; unsigned short release; - unsigned int buflen; - - int transport_type; - char *transport_name; - int protocol_type; - char *protocol_name; - } mod_data = { // Default values - .transport_parm = "BBB", - .protocol_parm = "SCSI", .removable = 0, .can_stall = 1, .cdrom = 0, - .vendor = FSG_VENDOR_ID, - .product = FSG_PRODUCT_ID, - .release = 0xffff, // Use controller chip type - .buflen = 16384, }; @@ -354,54 +317,6 @@ module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); -/* In the non-TEST version, only the module parameters listed above - * are available. */ -#ifdef CONFIG_USB_FILE_STORAGE_TEST - -module_param_named(transport, mod_data.transport_parm, charp, S_IRUGO); -MODULE_PARM_DESC(transport, "type of transport (BBB, CBI, or CB)"); - -module_param_named(protocol, mod_data.protocol_parm, charp, S_IRUGO); -MODULE_PARM_DESC(protocol, "type of protocol (RBC, 8020, QIC, UFI, " - "8070, or SCSI)"); - -module_param_named(vendor, mod_data.vendor, ushort, S_IRUGO); -MODULE_PARM_DESC(vendor, "USB Vendor ID"); - -module_param_named(product, mod_data.product, ushort, S_IRUGO); -MODULE_PARM_DESC(product, "USB Product ID"); - -module_param_named(release, mod_data.release, ushort, S_IRUGO); -MODULE_PARM_DESC(release, "USB release number"); - -module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); -MODULE_PARM_DESC(buflen, "I/O buffer size"); - -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -/* - * These definitions will permit the compiler to avoid generating code for - * parts of the driver that aren't used in the non-TEST version. Even gcc - * can recognize when a test of a constant expression yields a dead code - * path. - */ - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - -#define transport_is_bbb() (mod_data.transport_type == USB_PR_BULK) -#define transport_is_cbi() (mod_data.transport_type == USB_PR_CBI) -#define protocol_is_scsi() (mod_data.protocol_type == USB_SC_SCSI) - -#else - -#define transport_is_bbb() 1 -#define transport_is_cbi() 0 -#define protocol_is_scsi() 1 - -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - /*-------------------------------------------------------------------------*/ @@ -421,10 +336,6 @@ struct fsg_dev { unsigned int ep0_req_tag; const char *ep0req_name; - struct usb_request *intreq; // For interrupt responses - int intreq_busy; - struct fsg_buffhd *intr_buffhd; - unsigned int bulk_out_maxpacket; enum fsg_state state; // For exception handling unsigned int exception_req_tag; @@ -434,7 +345,6 @@ struct fsg_dev { unsigned int running : 1; unsigned int bulk_in_enabled : 1; unsigned int bulk_out_enabled : 1; - unsigned int intr_in_enabled : 1; unsigned int phase_error : 1; unsigned int short_packet_received : 1; unsigned int bad_lun_okay : 1; @@ -442,11 +352,9 @@ struct fsg_dev { unsigned long atomic_bitflags; #define REGISTERED 0 #define IGNORE_BULK_OUT 1 -#define SUSPENDED 2 struct usb_ep *bulk_in; struct usb_ep *bulk_out; - struct usb_ep *intr_in; struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; @@ -466,14 +374,6 @@ struct fsg_dev { u32 residue; u32 usb_amount_left; - /* The CB protocol offers no way for a host to know when a command - * has completed. As a result the next command may arrive early, - * and we will still have to handle it. For that reason we need - * a buffer to store new commands when using CB (or CBI, which - * does not oblige a host to wait for command completion either). */ - int cbbuf_cmnd_size; - u8 cbbuf_cmnd[MAX_COMMAND_SIZE]; - unsigned int nluns; struct fsg_lun *luns; struct fsg_lun *curlun; @@ -737,161 +637,53 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) } -#ifdef CONFIG_USB_FILE_STORAGE_TEST -static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - struct fsg_buffhd *bh = req->context; - - if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - /* Hold the lock while we update the request and buffer states */ - smp_wmb(); - spin_lock(&fsg->lock); - fsg->intreq_busy = 0; - bh->state = BUF_STATE_EMPTY; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - -#else -static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) -{} -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - /*-------------------------------------------------------------------------*/ /* Ep0 class-specific handlers. These always run in_irq. */ -#ifdef CONFIG_USB_FILE_STORAGE_TEST -static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct usb_request *req = fsg->ep0req; - static u8 cbi_reset_cmnd[6] = { - SC_SEND_DIAGNOSTIC, 4, 0xff, 0xff, 0xff, 0xff}; - - /* Error in command transfer? */ - if (req->status || req->length != req->actual || - req->actual < 6 || req->actual > MAX_COMMAND_SIZE) { - - /* Not all controllers allow a protocol stall after - * receiving control-out data, but we'll try anyway. */ - fsg_set_halt(fsg, fsg->ep0); - return; // Wait for reset - } - - /* Is it the special reset command? */ - if (req->actual >= sizeof cbi_reset_cmnd && - memcmp(req->buf, cbi_reset_cmnd, - sizeof cbi_reset_cmnd) == 0) { - - /* Raise an exception to stop the current operation - * and reinitialize our state. */ - DBG(fsg, "cbi reset request\n"); - raise_exception(fsg, FSG_STATE_RESET); - return; - } - - VDBG(fsg, "CB[I] accept device-specific command\n"); - spin_lock(&fsg->lock); - - /* Save the command for later */ - if (fsg->cbbuf_cmnd_size) - WARNING(fsg, "CB[I] overwriting previous command\n"); - fsg->cbbuf_cmnd_size = req->actual; - memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); - - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - -#else -static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{} -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - static int class_setup_req(struct fsg_dev *fsg, const struct usb_ctrlrequest *ctrl) { struct usb_request *req = fsg->ep0req; - int value = -EOPNOTSUPP; u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); if (!fsg->config) - return value; + return -EOPNOTSUPP; - /* Handle Bulk-only class-specific requests */ - if (transport_is_bbb()) { - switch (ctrl->bRequest) { - - case USB_BULK_RESET_REQUEST: - if (ctrl->bRequestType != (USB_DIR_OUT | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0) { - value = -EDOM; - break; - } - - /* Raise an exception to stop the current operation - * and reinitialize our state. */ - DBG(fsg, "bulk reset request\n"); - raise_exception(fsg, FSG_STATE_RESET); - value = DELAYED_STATUS; - break; + switch (ctrl->bRequest) { - case USB_BULK_GET_MAX_LUN_REQUEST: - if (ctrl->bRequestType != (USB_DIR_IN | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0) { - value = -EDOM; - break; - } - VDBG(fsg, "get max LUN\n"); - *(u8 *) req->buf = fsg->nluns - 1; - value = 1; + case USB_BULK_RESET_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - } - } + if (w_index != 0 || w_value != 0) + return -EDOM; - /* Handle CBI class-specific requests */ - else { - switch (ctrl->bRequest) { + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg, FSG_STATE_RESET); + return DELAYED_STATUS; - case USB_CBI_ADSC_REQUEST: - if (ctrl->bRequestType != (USB_DIR_OUT | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0) { - value = -EDOM; - break; - } - if (w_length > MAX_COMMAND_SIZE) { - value = -EOVERFLOW; - break; - } - value = w_length; - fsg->ep0req->context = received_cbi_adsc; + case USB_BULK_GET_MAX_LUN_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - } - } - - if (value == -EOPNOTSUPP) - VDBG(fsg, - "unknown class-specific control req " - "%02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - le16_to_cpu(ctrl->wValue), w_index, w_length); - return value; + if (w_index != 0 || w_value != 0) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *) req->buf = fsg->nluns - 1; + return 1; + } + + VDBG(fsg, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + le16_to_cpu(ctrl->wValue), w_index, w_length); + return -EOPNOTSUPP; } @@ -1063,8 +855,6 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, if (ep == fsg->bulk_in) dump_msg(fsg, "bulk-in", req->buf, req->length); - else if (ep == fsg->intr_in) - dump_msg(fsg, "intr-in", req->buf, req->length); spin_lock_irq(&fsg->lock); *pbusy = 1; @@ -1159,7 +949,7 @@ static int do_read(struct fsg_dev *fsg) * the next page. * If this means reading 0 then we were asked to read past * the end of file. */ - amount = min((unsigned int) amount_left, mod_data.buflen); + amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t) amount, curlun->file_length - file_offset); partial_page = file_offset & (PAGE_CACHE_SIZE - 1); @@ -1304,7 +1094,7 @@ static int do_write(struct fsg_dev *fsg) * If this means getting 0, then we were asked * to write past the end of file. * Finally, round down to a block boundary. */ - amount = min(amount_left_to_req, mod_data.buflen); + amount = min(amount_left_to_req, FSG_BUFLEN); amount = min((loff_t) amount, curlun->file_length - usb_offset); partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); @@ -1504,7 +1294,7 @@ static int do_verify(struct fsg_dev *fsg) * And don't try to read past the end of the file. * If this means reading 0 then we were asked to read * past the end of file. */ - amount = min((unsigned int) amount_left, mod_data.buflen); + amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t) amount, curlun->file_length - file_offset); if (amount == 0) { @@ -1743,7 +1533,7 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) } else { // SC_MODE_SENSE_10 buf[3] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA buf += 8; - limit = 65535; // Should really be mod_data.buflen + limit = 65535; // Should really be FSG_BUFLEN } /* No block descriptors */ @@ -1790,50 +1580,10 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_start_stop(struct fsg_dev *fsg) { - struct fsg_lun *curlun = fsg->curlun; - int loej, start; - if (!mod_data.removable) { - curlun->sense_data = SS_INVALID_COMMAND; - return -EINVAL; - } - - // int immed = fsg->cmnd[1] & 0x01; - loej = fsg->cmnd[4] & 0x02; - start = fsg->cmnd[4] & 0x01; - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - if ((fsg->cmnd[1] & ~0x01) != 0 || // Mask away Immed - (fsg->cmnd[4] & ~0x03) != 0) { // Mask LoEj, Start - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + fsg->curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; } - - if (!start) { - - /* Are we allowed to unload the media? */ - if (curlun->prevent_medium_removal) { - LDBG(curlun, "unload attempt prevented\n"); - curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; - return -EINVAL; - } - if (loej) { // Simulate an unload/eject - up_read(&fsg->filesem); - down_write(&fsg->filesem); - fsg_lun_close(curlun); - up_write(&fsg->filesem); - down_read(&fsg->filesem); - } - } else { - - /* Our emulation doesn't support mounting; the medium is - * available for use as soon as it is loaded. */ - if (!fsg_lun_is_open(curlun)) { - curlun->sense_data = SS_MEDIUM_NOT_PRESENT; - return -EINVAL; - } - } -#endif return 0; } @@ -1954,7 +1704,7 @@ static int pad_with_zeros(struct fsg_dev *fsg) return rc; } - nsend = min(fsg->usb_amount_left, (u32) mod_data.buflen); + nsend = min(fsg->usb_amount_left, FSG_BUFLEN); memset(bh->buf + nkeep, 0, nsend - nkeep); bh->inreq->length = nsend; bh->inreq->zero = 0; @@ -1994,8 +1744,7 @@ static int throw_away_data(struct fsg_dev *fsg) /* Try to submit another request if we need one */ bh = fsg->next_buffhd_to_fill; if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { - amount = min(fsg->usb_amount_left, - (u32) mod_data.buflen); + amount = min(fsg->usb_amount_left, FSG_BUFLEN); /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ @@ -2040,49 +1789,27 @@ static int finish_reply(struct fsg_dev *fsg) /* All but the last buffer of data must have already been sent */ case DATA_DIR_TO_HOST: - if (fsg->data_size == 0) - ; // Nothing to send + if (fsg->data_size == 0) { + /* Nothing to send */ /* If there's no residue, simply send the last buffer */ - else if (fsg->residue == 0) { + } else if (fsg->residue == 0) { bh->inreq->zero = 0; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; - } - - /* There is a residue. For CB and CBI, simply mark the end - * of the data with a short packet. However, if we are - * allowed to stall, there was no data at all (residue == - * data_size), and the command failed (invalid LUN or - * sense data is set), then halt the bulk-in endpoint - * instead. */ - else if (!transport_is_bbb()) { - if (mod_data.can_stall && - fsg->residue == fsg->data_size && - (!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) { - bh->state = BUF_STATE_EMPTY; - rc = halt_bulk_in_endpoint(fsg); - } else { - bh->inreq->zero = 1; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - } - } /* For Bulk-only, if we're allowed to stall then send the * short packet and halt the bulk-in endpoint. If we can't * stall, pad out the remaining data with 0's. */ - else { - if (mod_data.can_stall) { - bh->inreq->zero = 1; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - rc = halt_bulk_in_endpoint(fsg); - } else - rc = pad_with_zeros(fsg); + } else if (mod_data.can_stall) { + bh->inreq->zero = 1; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsg->next_buffhd_to_fill = bh->next; + rc = halt_bulk_in_endpoint(fsg); + } else { + rc = pad_with_zeros(fsg); } break; @@ -2126,6 +1853,7 @@ static int send_status(struct fsg_dev *fsg) { struct fsg_lun *curlun = fsg->curlun; struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; int rc; u8 status = USB_STATUS_PASS; u32 sd, sdinfo = 0; @@ -2158,46 +1886,19 @@ static int send_status(struct fsg_dev *fsg) SK(sd), ASC(sd), ASCQ(sd), sdinfo); } - if (transport_is_bbb()) { - struct bulk_cs_wrap *csw = bh->buf; - - /* Store and send the Bulk-only CSW */ - csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); - csw->Tag = fsg->tag; - csw->Residue = cpu_to_le32(fsg->residue); - csw->Status = status; - - bh->inreq->length = USB_BULK_CS_WRAP_LEN; - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - - } else if (mod_data.transport_type == USB_PR_CB) { - /* Control-Bulk transport has no status phase! */ - return 0; + /* Store and send the Bulk-only CSW */ + csw = bh->buf; - } else { // USB_PR_CBI - struct interrupt_data *buf = bh->buf; + csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); + csw->Tag = fsg->tag; + csw->Residue = cpu_to_le32(fsg->residue); + csw->Status = status; - /* Store and send the Interrupt data. UFI sends the ASC - * and ASCQ bytes. Everything else sends a Type (which - * is always 0) and the status Value. */ - if (mod_data.protocol_type == USB_SC_UFI) { - buf->bType = ASC(sd); - buf->bValue = ASCQ(sd); - } else { - buf->bType = 0; - buf->bValue = status; - } - fsg->intreq->length = CBI_INTERRUPT_DATA_LEN; - - fsg->intr_buffhd = bh; // Point to the right buffhd - fsg->intreq->buf = bh->inreq->buf; - fsg->intreq->context = bh; - start_transfer(fsg, fsg->intr_in, fsg->intreq, - &fsg->intreq_busy, &bh->state); - } + bh->inreq->length = USB_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; return 0; @@ -2218,21 +1919,6 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, char hdlen[20]; struct fsg_lun *curlun; - /* Adjust the expected cmnd_size for protocol encapsulation padding. - * Transparent SCSI doesn't pad. */ - if (protocol_is_scsi()) - ; - - /* There's some disagreement as to whether RBC pads commands or not. - * We'll play it safe and accept either form. */ - else if (mod_data.protocol_type == USB_SC_RBC) { - if (fsg->cmnd_size == 12) - cmnd_size = 12; - - /* All the other protocols pad to 12 bytes */ - } else - cmnd_size = 12; - hdlen[0] = 0; if (fsg->data_dir != DATA_DIR_UNKNOWN) sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], @@ -2294,13 +1980,9 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, } /* Check that the LUN values are consistent */ - if (transport_is_bbb()) { - if (fsg->lun != lun) - DBG(fsg, "using LUN %d from CBW, " - "not LUN %d from CDB\n", - fsg->lun, lun); - } else - fsg->lun = lun; // Use LUN from the command + if (fsg->lun != lun) + DBG(fsg, "using LUN %d from CBW, not LUN %d from CDB\n", + fsg->lun, lun); /* Check the LUN */ if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { @@ -2666,59 +2348,34 @@ static int get_next_command(struct fsg_dev *fsg) struct fsg_buffhd *bh; int rc = 0; - if (transport_is_bbb()) { - - /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - /* Queue a request to read a Bulk-only CBW */ - set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - - /* We will drain the buffer in software, which means we - * can reuse it for the next filling. No need to advance - * next_buffhd_to_fill. */ - - /* Wait for the CBW to arrive */ - while (bh->state != BUF_STATE_FULL) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - smp_rmb(); - rc = received_cbw(fsg, bh); - bh->state = BUF_STATE_EMPTY; - - } else { // USB_PR_CB or USB_PR_CBI + /* Wait for the next buffer to become available */ + bh = fsg->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg); + if (rc) + return rc; + } - /* Wait for the next command to arrive */ - while (fsg->cbbuf_cmnd_size == 0) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); - /* Is the previous status interrupt request still busy? - * The host is allowed to skip reading the status, - * so we must cancel it. */ - if (fsg->intreq_busy) - usb_ep_dequeue(fsg->intr_in, fsg->intreq); + /* We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. */ - /* Copy the command and mark the buffer empty */ - fsg->data_dir = DATA_DIR_UNKNOWN; - spin_lock_irq(&fsg->lock); - fsg->cmnd_size = fsg->cbbuf_cmnd_size; - memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size); - fsg->cbbuf_cmnd_size = 0; - spin_unlock_irq(&fsg->lock); + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { + rc = sleep_thread(fsg); + if (rc) + return rc; } + smp_rmb(); + rc = received_cbw(fsg, bh); + bh->state = BUF_STATE_EMPTY; + return rc; } @@ -2775,10 +2432,6 @@ reset: bh->outreq = NULL; } } - if (fsg->intreq) { - usb_ep_free_request(fsg->intr_in, fsg->intreq); - fsg->intreq = NULL; - } /* Disable the endpoints */ if (fsg->bulk_in_enabled) { @@ -2789,10 +2442,6 @@ reset: usb_ep_disable(fsg->bulk_out); fsg->bulk_out_enabled = 0; } - if (fsg->intr_in_enabled) { - usb_ep_disable(fsg->intr_in); - fsg->intr_in_enabled = 0; - } fsg->running = 0; if (altsetting < 0 || rc != 0) @@ -2815,14 +2464,6 @@ reset: fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); - if (transport_is_cbi()) { - d = fsg_ep_desc(fsg->gadget, - &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc); - if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) - goto reset; - fsg->intr_in_enabled = 1; - } - /* Allocate the requests */ for (i = 0; i < FSG_NUM_BUFFERS; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; @@ -2836,11 +2477,6 @@ reset: bh->inreq->complete = bulk_in_complete; bh->outreq->complete = bulk_out_complete; } - if (transport_is_cbi()) { - if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0) - goto reset; - fsg->intreq->complete = intr_in_complete; - } fsg->running = 1; for (i = 0; i < fsg->nluns; ++i) @@ -2918,8 +2554,6 @@ static void handle_exception(struct fsg_dev *fsg) } /* Cancel all the pending transfers */ - if (fsg->intreq_busy) - usb_ep_dequeue(fsg->intr_in, fsg->intreq); for (i = 0; i < FSG_NUM_BUFFERS; ++i) { bh = &fsg->buffhds[i]; if (bh->inreq_busy) @@ -2930,7 +2564,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Wait until everything is idle */ for (;;) { - num_active = fsg->intreq_busy; + num_active = 0; for (i = 0; i < FSG_NUM_BUFFERS; ++i) { bh = &fsg->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; @@ -2946,8 +2580,6 @@ static void handle_exception(struct fsg_dev *fsg) usb_ep_fifo_flush(fsg->bulk_in); if (fsg->bulk_out_enabled) usb_ep_fifo_flush(fsg->bulk_out); - if (fsg->intr_in_enabled) - usb_ep_fifo_flush(fsg->intr_in); /* Reset the I/O buffer states and pointers, the SCSI * state, and the exception. Then invoke the handler. */ @@ -2999,12 +2631,8 @@ static void handle_exception(struct fsg_dev *fsg) if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) usb_ep_clear_halt(fsg->bulk_in); - if (transport_is_bbb()) { - if (fsg->ep0_req_tag == exception_req_tag) - ep0_queue(fsg); // Complete the status stage - - } else if (transport_is_cbi()) - send_status(fsg); // Status by interrupt pipe + if (fsg->ep0_req_tag == exception_req_tag) + ep0_queue(fsg); // Complete the status stage /* Technically this should go here, but it would only be * a waste of time. Ditto for the INTERFACE_CHANGE and @@ -3196,15 +2824,8 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) static int __init check_parameters(struct fsg_dev *fsg) { - int prot; int gcnum; - /* Store the default values */ - mod_data.transport_type = USB_PR_BULK; - mod_data.transport_name = "Bulk-only"; - mod_data.protocol_type = USB_SC_SCSI; - mod_data.protocol_name = "Transparent SCSI"; - /* Some peripheral controllers are known not to be able to * halt bulk endpoints correctly. If one of them is present, * disable stalls. @@ -3227,58 +2848,6 @@ static int __init check_parameters(struct fsg_dev *fsg) } } - prot = simple_strtol(mod_data.protocol_parm, NULL, 0); - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - if (strnicmp(mod_data.transport_parm, "BBB", 10) == 0) { - ; // Use default setting - } else if (strnicmp(mod_data.transport_parm, "CB", 10) == 0) { - mod_data.transport_type = USB_PR_CB; - mod_data.transport_name = "Control-Bulk"; - } else if (strnicmp(mod_data.transport_parm, "CBI", 10) == 0) { - mod_data.transport_type = USB_PR_CBI; - mod_data.transport_name = "Control-Bulk-Interrupt"; - } else { - ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm); - return -EINVAL; - } - - if (strnicmp(mod_data.protocol_parm, "SCSI", 10) == 0 || - prot == USB_SC_SCSI) { - ; // Use default setting - } else if (strnicmp(mod_data.protocol_parm, "RBC", 10) == 0 || - prot == USB_SC_RBC) { - mod_data.protocol_type = USB_SC_RBC; - mod_data.protocol_name = "RBC"; - } else if (strnicmp(mod_data.protocol_parm, "8020", 4) == 0 || - strnicmp(mod_data.protocol_parm, "ATAPI", 10) == 0 || - prot == USB_SC_8020) { - mod_data.protocol_type = USB_SC_8020; - mod_data.protocol_name = "8020i (ATAPI)"; - } else if (strnicmp(mod_data.protocol_parm, "QIC", 3) == 0 || - prot == USB_SC_QIC) { - mod_data.protocol_type = USB_SC_QIC; - mod_data.protocol_name = "QIC-157"; - } else if (strnicmp(mod_data.protocol_parm, "UFI", 10) == 0 || - prot == USB_SC_UFI) { - mod_data.protocol_type = USB_SC_UFI; - mod_data.protocol_name = "UFI"; - } else if (strnicmp(mod_data.protocol_parm, "8070", 4) == 0 || - prot == USB_SC_8070) { - mod_data.protocol_type = USB_SC_8070; - mod_data.protocol_name = "8070i"; - } else { - ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm); - return -EINVAL; - } - - mod_data.buflen &= PAGE_CACHE_MASK; - if (mod_data.buflen <= 0) { - ERROR(fsg, "invalid buflen\n"); - return -ETOOSMALL; - } -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - return 0; } @@ -3381,29 +2950,11 @@ static int __init fsg_bind(struct usb_gadget *gadget) ep->driver_data = fsg; // claim the endpoint fsg->bulk_out = ep; - if (transport_is_cbi()) { - ep = usb_ep_autoconfig(gadget, &fsg_fs_intr_in_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint - fsg->intr_in = ep; - } - /* Fix up the descriptors */ device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; - device_desc.idVendor = cpu_to_le16(mod_data.vendor); - device_desc.idProduct = cpu_to_le16(mod_data.product); device_desc.bcdDevice = cpu_to_le16(mod_data.release); - i = (transport_is_cbi() ? 3 : 2); // Number of endpoints - fsg_intf_desc.bNumEndpoints = i; - fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type; - fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type; - fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL; - if (gadget_is_dualspeed(gadget)) { - fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL; - /* Assume ep0 uses the same maxpacket value for both speeds */ dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; @@ -3412,8 +2963,6 @@ static int __init fsg_bind(struct usb_gadget *gadget) fsg_fs_bulk_in_desc.bEndpointAddress; fsg_hs_bulk_out_desc.bEndpointAddress = fsg_fs_bulk_out_desc.bEndpointAddress; - fsg_hs_intr_in_desc.bEndpointAddress = - fsg_fs_intr_in_desc.bEndpointAddress; } if (gadget_is_otg(gadget)) @@ -3437,7 +2986,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Allocate for the bulk-in endpoint. We assume that * the buffer will also work with the bulk-out (and * interrupt-in) endpoint. */ - bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL); + bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); if (!bh->buf) goto out; bh->next = bh + 1; @@ -3489,15 +3038,9 @@ static int __init fsg_bind(struct usb_gadget *gadget) } kfree(pathbuf); - DBG(fsg, "transport=%s (x%02x)\n", - mod_data.transport_name, mod_data.transport_type); - DBG(fsg, "protocol=%s (x%02x)\n", - mod_data.protocol_name, mod_data.protocol_type); - DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", - mod_data.vendor, mod_data.product, mod_data.release); DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", mod_data.removable, mod_data.can_stall, - mod_data.cdrom, mod_data.buflen); + mod_data.cdrom, FSG_BUFLEN); DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); set_bit(REGISTERED, &fsg->atomic_bitflags); @@ -3518,25 +3061,6 @@ out: } -/*-------------------------------------------------------------------------*/ - -static void fsg_suspend(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "suspend\n"); - set_bit(SUSPENDED, &fsg->atomic_bitflags); -} - -static void fsg_resume(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "resume\n"); - clear_bit(SUSPENDED, &fsg->atomic_bitflags); -} - - /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver fsg_driver = { @@ -3550,8 +3074,6 @@ static struct usb_gadget_driver fsg_driver = { .unbind = fsg_unbind, .disconnect = fsg_disconnect, .setup = fsg_setup, - .suspend = fsg_suspend, - .resume = fsg_resume, .driver = { .name = DRIVER_NAME, diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 6523cb6f4c4d..e76c8ced41e2 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -33,6 +33,20 @@ * macro is defined prior to including this file. */ +/* + * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and + * fsg_hs_intr_in_desc objects as well as + * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES + * macros are not defined. + * + * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER, + * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not + * defined (as well as corresponding entries in string tables are + * missing) and FSG_STRING_INTERFACE has value of zero. + * + * When FSG_NO_OTG is defined fsg_otg_desc won't be defined. + */ + #include @@ -327,14 +341,17 @@ static inline u32 get_unaligned_be24(u8 *buf) enum { +#ifndef FSG_NO_DEVICE_STRINGS FSG_STRING_MANUFACTURER = 1, FSG_STRING_PRODUCT, FSG_STRING_SERIAL, FSG_STRING_CONFIG, +#endif FSG_STRING_INTERFACE }; +#ifndef FSG_NO_OTG static struct usb_otg_descriptor fsg_otg_desc = { .bLength = sizeof fsg_otg_desc, @@ -342,6 +359,7 @@ fsg_otg_desc = { .bmAttributes = USB_OTG_SRP, }; +#endif /* There is only one interface. */ @@ -380,6 +398,8 @@ fsg_fs_bulk_out_desc = { /* wMaxPacketSize set by autoconfiguration */ }; +#ifndef FSG_NO_INTR_EP + static struct usb_endpoint_descriptor fsg_fs_intr_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, @@ -391,15 +411,26 @@ fsg_fs_intr_in_desc = { .bInterval = 32, // frames -> 32 ms }; +#ifndef FSG_NO_OTG +# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + static const struct usb_descriptor_header *fsg_fs_function[] = { +#ifndef FSG_NO_OTG (struct usb_descriptor_header *) &fsg_otg_desc, +#endif (struct usb_descriptor_header *) &fsg_intf_desc, (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP (struct usb_descriptor_header *) &fsg_fs_intr_in_desc, +#endif NULL, }; -#define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2 /* @@ -431,6 +462,8 @@ fsg_hs_bulk_out_desc = { .bInterval = 1, // NAK every 1 uframe }; +#ifndef FSG_NO_INTR_EP + static struct usb_endpoint_descriptor fsg_hs_intr_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, @@ -442,15 +475,26 @@ fsg_hs_intr_in_desc = { .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms }; +#ifndef FSG_NO_OTG +# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + static const struct usb_descriptor_header *fsg_hs_function[] = { +#ifndef FSG_NO_OTG (struct usb_descriptor_header *) &fsg_otg_desc, +#endif (struct usb_descriptor_header *) &fsg_intf_desc, (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP (struct usb_descriptor_header *) &fsg_hs_intr_in_desc, +#endif NULL, }; -#define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2 /* Maxpacket and other transfer characteristics vary by speed. */ static struct usb_endpoint_descriptor * @@ -465,10 +509,12 @@ fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, /* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ static struct usb_string fsg_strings[] = { +#ifndef FSG_NO_DEVICE_STRINGS {FSG_STRING_MANUFACTURER, fsg_string_manufacturer}, {FSG_STRING_PRODUCT, fsg_string_product}, {FSG_STRING_SERIAL, fsg_string_serial}, {FSG_STRING_CONFIG, fsg_string_config}, +#endif {FSG_STRING_INTERFACE, fsg_string_interface}, {} }; -- cgit v1.2.3 From 2e04b83228df61a1a6f3241e87d83c1203b9a3d1 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:20 +0100 Subject: USB: g_mass_storage: parts of fsg_dev moved to fsg_common structure In the final version, many fsg_dev structures will (be able to) refer to a single fsg_common structure and so it is required to move common data to another object which can be shared. Situation where many fsg_dev structures refer single fsg_common structure is when a single instance of MSF is used in several USB configurations. Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 341 ++++++++++++++++++------------------ 1 file changed, 173 insertions(+), 168 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index ed16a9538753..7750346b696c 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -320,14 +320,32 @@ MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); /*-------------------------------------------------------------------------*/ +/* Data shared by all the FSG instances. */ +struct fsg_common { + /* filesem protects: backing files in use */ + struct rw_semaphore filesem; + + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + + unsigned int nluns; + unsigned int lun; + struct fsg_lun *luns; + struct fsg_lun *curlun; +}; + + struct fsg_dev { - /* lock protects: state, all the req_busy's, and cbbuf_cmnd */ + struct fsg_common *common; + + /* lock protects: state, all the req_busy's */ spinlock_t lock; struct usb_gadget *gadget; - /* filesem protects: backing files in use */ - struct rw_semaphore filesem; - /* reference counting: wait until all LUNs are released */ struct kref ref; @@ -356,27 +374,16 @@ struct fsg_dev { struct usb_ep *bulk_in; struct usb_ep *bulk_out; - struct fsg_buffhd *next_buffhd_to_fill; - struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; - int thread_wakeup_needed; struct completion thread_notifier; struct task_struct *thread_task; - int cmnd_size; - u8 cmnd[MAX_COMMAND_SIZE]; enum data_direction data_dir; u32 data_size; u32 data_size_from_cmnd; u32 tag; - unsigned int lun; u32 residue; u32 usb_amount_left; - - unsigned int nluns; - struct fsg_lun *luns; - struct fsg_lun *curlun; }; typedef void (*fsg_routine_t)(struct fsg_dev *); @@ -674,7 +681,7 @@ static int class_setup_req(struct fsg_dev *fsg, if (w_index != 0 || w_value != 0) return -EDOM; VDBG(fsg, "get max LUN\n"); - *(u8 *) req->buf = fsg->nluns - 1; + *(u8 *) req->buf = fsg->common->nluns - 1; return 1; } @@ -903,7 +910,7 @@ static int sleep_thread(struct fsg_dev *fsg) static int do_read(struct fsg_dev *fsg) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; u32 lba; struct fsg_buffhd *bh; int rc; @@ -915,15 +922,15 @@ static int do_read(struct fsg_dev *fsg) /* Get the starting Logical Block Address and check that it's * not too big */ - if (fsg->cmnd[0] == SC_READ_6) - lba = get_unaligned_be24(&fsg->cmnd[1]); + if (fsg->common->cmnd[0] == SC_READ_6) + lba = get_unaligned_be24(&fsg->common->cmnd[1]); else { - lba = get_unaligned_be32(&fsg->cmnd[2]); + lba = get_unaligned_be32(&fsg->common->cmnd[2]); /* We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = don't read from the * cache), but we don't implement them. */ - if ((fsg->cmnd[1] & ~0x18) != 0) { + if ((fsg->common->cmnd[1] & ~0x18) != 0) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -958,7 +965,7 @@ static int do_read(struct fsg_dev *fsg) partial_page); /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; + bh = fsg->common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { rc = sleep_thread(fsg); if (rc) @@ -1018,7 +1025,7 @@ static int do_read(struct fsg_dev *fsg) bh->inreq->zero = 0; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + fsg->common->next_buffhd_to_fill = bh->next; } return -EIO; // No default reply @@ -1029,7 +1036,7 @@ static int do_read(struct fsg_dev *fsg) static int do_write(struct fsg_dev *fsg) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; u32 lba; struct fsg_buffhd *bh; int get_some_more; @@ -1050,20 +1057,20 @@ static int do_write(struct fsg_dev *fsg) /* Get the starting Logical Block Address and check that it's * not too big */ - if (fsg->cmnd[0] == SC_WRITE_6) - lba = get_unaligned_be24(&fsg->cmnd[1]); + if (fsg->common->cmnd[0] == SC_WRITE_6) + lba = get_unaligned_be24(&fsg->common->cmnd[1]); else { - lba = get_unaligned_be32(&fsg->cmnd[2]); + lba = get_unaligned_be32(&fsg->common->cmnd[2]); /* We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = write directly to the * medium). We don't implement DPO; we implement FUA by * performing synchronous output. */ - if ((fsg->cmnd[1] & ~0x18) != 0) { + if ((fsg->common->cmnd[1] & ~0x18) != 0) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - if (fsg->cmnd[1] & 0x08) { // FUA + if (fsg->common->cmnd[1] & 0x08) { // FUA spin_lock(&curlun->filp->f_lock); curlun->filp->f_flags |= O_SYNC; spin_unlock(&curlun->filp->f_lock); @@ -1082,7 +1089,7 @@ static int do_write(struct fsg_dev *fsg) while (amount_left_to_write > 0) { /* Queue a request for more data from the host */ - bh = fsg->next_buffhd_to_fill; + bh = fsg->common->next_buffhd_to_fill; if (bh->state == BUF_STATE_EMPTY && get_some_more) { /* Figure out how much we want to get: @@ -1133,17 +1140,17 @@ static int do_write(struct fsg_dev *fsg) bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + fsg->common->next_buffhd_to_fill = bh->next; continue; } /* Write the received data to the backing file */ - bh = fsg->next_buffhd_to_drain; + bh = fsg->common->next_buffhd_to_drain; if (bh->state == BUF_STATE_EMPTY && !get_some_more) break; // We stopped early if (bh->state == BUF_STATE_FULL) { smp_rmb(); - fsg->next_buffhd_to_drain = bh->next; + fsg->common->next_buffhd_to_drain = bh->next; bh->state = BUF_STATE_EMPTY; /* Did something go wrong with the transfer? */ @@ -1218,7 +1225,7 @@ static int do_write(struct fsg_dev *fsg) static int do_synchronize_cache(struct fsg_dev *fsg) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; int rc; /* We ignore the requested LBA and write out all file's @@ -1244,10 +1251,10 @@ static void invalidate_sub(struct fsg_lun *curlun) static int do_verify(struct fsg_dev *fsg) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; u32 lba; u32 verification_length; - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; + struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; loff_t file_offset, file_offset_tmp; u32 amount_left; unsigned int amount; @@ -1255,7 +1262,7 @@ static int do_verify(struct fsg_dev *fsg) /* Get the starting Logical Block Address and check that it's * not too big */ - lba = get_unaligned_be32(&fsg->cmnd[2]); + lba = get_unaligned_be32(&fsg->common->cmnd[2]); if (lba >= curlun->num_sectors) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; @@ -1263,12 +1270,12 @@ static int do_verify(struct fsg_dev *fsg) /* We allow DPO (Disable Page Out = don't save data in the * cache) but we don't implement it. */ - if ((fsg->cmnd[1] & ~0x10) != 0) { + if ((fsg->common->cmnd[1] & ~0x10) != 0) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - verification_length = get_unaligned_be16(&fsg->cmnd[7]); + verification_length = get_unaligned_be16(&fsg->common->cmnd[7]); if (unlikely(verification_length == 0)) return -EIO; // No default reply @@ -1348,7 +1355,7 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) static char product_disk_id[] = "File-Stor Gadget"; static char product_cdrom_id[] = "File-CD Gadget "; - if (!fsg->curlun) { // Unsupported LUNs are okay + if (!fsg->common->curlun) { // Unsupported LUNs are okay fsg->bad_lun_okay = 1; memset(buf, 0, 36); buf[0] = 0x7f; // Unsupported, no device-type @@ -1374,7 +1381,7 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; u8 *buf = (u8 *) bh->buf; u32 sd, sdinfo; int valid; @@ -1428,9 +1435,9 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->curlun; - u32 lba = get_unaligned_be32(&fsg->cmnd[2]); - int pmi = fsg->cmnd[8]; + struct fsg_lun *curlun = fsg->common->curlun; + u32 lba = get_unaligned_be32(&fsg->common->cmnd[2]); + int pmi = fsg->common->cmnd[8]; u8 *buf = (u8 *) bh->buf; /* Check the PMI and LBA fields */ @@ -1448,12 +1455,12 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->curlun; - int msf = fsg->cmnd[1] & 0x02; - u32 lba = get_unaligned_be32(&fsg->cmnd[2]); + struct fsg_lun *curlun = fsg->common->curlun; + int msf = fsg->common->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&fsg->common->cmnd[2]); u8 *buf = (u8 *) bh->buf; - if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ + if ((fsg->common->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -1471,12 +1478,12 @@ static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->curlun; - int msf = fsg->cmnd[1] & 0x02; - int start_track = fsg->cmnd[6]; + struct fsg_lun *curlun = fsg->common->curlun; + int msf = fsg->common->cmnd[1] & 0x02; + int start_track = fsg->common->cmnd[6]; u8 *buf = (u8 *) bh->buf; - if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + if ((fsg->common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ start_track > 1) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; @@ -1499,8 +1506,8 @@ static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->curlun; - int mscmnd = fsg->cmnd[0]; + struct fsg_lun *curlun = fsg->common->curlun; + int mscmnd = fsg->common->cmnd[0]; u8 *buf = (u8 *) bh->buf; u8 *buf0 = buf; int pc, page_code; @@ -1508,12 +1515,12 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) int valid_page = 0; int len, limit; - if ((fsg->cmnd[1] & ~0x08) != 0) { // Mask away DBD + if ((fsg->common->cmnd[1] & ~0x08) != 0) { // Mask away DBD curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - pc = fsg->cmnd[2] >> 6; - page_code = fsg->cmnd[2] & 0x3f; + pc = fsg->common->cmnd[2] >> 6; + page_code = fsg->common->cmnd[2] & 0x3f; if (pc == 3) { curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; return -EINVAL; @@ -1581,7 +1588,7 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_start_stop(struct fsg_dev *fsg) { if (!mod_data.removable) { - fsg->curlun->sense_data = SS_INVALID_COMMAND; + fsg->common->curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; } return 0; @@ -1590,7 +1597,7 @@ static int do_start_stop(struct fsg_dev *fsg) static int do_prevent_allow(struct fsg_dev *fsg) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; int prevent; if (!mod_data.removable) { @@ -1598,8 +1605,8 @@ static int do_prevent_allow(struct fsg_dev *fsg) return -EINVAL; } - prevent = fsg->cmnd[4] & 0x01; - if ((fsg->cmnd[4] & ~0x01) != 0) { // Mask away Prevent + prevent = fsg->common->cmnd[4] & 0x01; + if ((fsg->common->cmnd[4] & ~0x01) != 0) { // Mask away Prevent curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -1614,7 +1621,7 @@ static int do_prevent_allow(struct fsg_dev *fsg) static int do_read_format_capacities(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; u8 *buf = (u8 *) bh->buf; buf[0] = buf[1] = buf[2] = 0; @@ -1631,7 +1638,7 @@ static int do_read_format_capacities(struct fsg_dev *fsg, static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; /* We don't support MODE SELECT */ curlun->sense_data = SS_INVALID_COMMAND; @@ -1688,7 +1695,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) static int pad_with_zeros(struct fsg_dev *fsg) { - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; + struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; u32 nkeep = bh->inreq->length; u32 nsend; int rc; @@ -1710,7 +1717,7 @@ static int pad_with_zeros(struct fsg_dev *fsg) bh->inreq->zero = 0; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); - bh = fsg->next_buffhd_to_fill = bh->next; + bh = fsg->common->next_buffhd_to_fill = bh->next; fsg->usb_amount_left -= nsend; nkeep = 0; } @@ -1723,14 +1730,15 @@ static int throw_away_data(struct fsg_dev *fsg) u32 amount; int rc; - while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY || - fsg->usb_amount_left > 0) { + for (bh = fsg->common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || fsg->usb_amount_left > 0; + bh = fsg->common->next_buffhd_to_drain) { /* Throw away the data in a filled buffer */ if (bh->state == BUF_STATE_FULL) { smp_rmb(); bh->state = BUF_STATE_EMPTY; - fsg->next_buffhd_to_drain = bh->next; + fsg->common->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ if (bh->outreq->actual != bh->outreq->length || @@ -1742,7 +1750,7 @@ static int throw_away_data(struct fsg_dev *fsg) } /* Try to submit another request if we need one */ - bh = fsg->next_buffhd_to_fill; + bh = fsg->common->next_buffhd_to_fill; if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { amount = min(fsg->usb_amount_left, FSG_BUFLEN); @@ -1753,7 +1761,7 @@ static int throw_away_data(struct fsg_dev *fsg) bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + fsg->common->next_buffhd_to_fill = bh->next; fsg->usb_amount_left -= amount; continue; } @@ -1769,7 +1777,7 @@ static int throw_away_data(struct fsg_dev *fsg) static int finish_reply(struct fsg_dev *fsg) { - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; + struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; int rc = 0; switch (fsg->data_dir) { @@ -1797,7 +1805,7 @@ static int finish_reply(struct fsg_dev *fsg) bh->inreq->zero = 0; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + fsg->common->next_buffhd_to_fill = bh->next; /* For Bulk-only, if we're allowed to stall then send the * short packet and halt the bulk-in endpoint. If we can't @@ -1806,7 +1814,7 @@ static int finish_reply(struct fsg_dev *fsg) bh->inreq->zero = 1; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + fsg->common->next_buffhd_to_fill = bh->next; rc = halt_bulk_in_endpoint(fsg); } else { rc = pad_with_zeros(fsg); @@ -1851,7 +1859,7 @@ static int finish_reply(struct fsg_dev *fsg) static int send_status(struct fsg_dev *fsg) { - struct fsg_lun *curlun = fsg->curlun; + struct fsg_lun *curlun = fsg->common->curlun; struct fsg_buffhd *bh; struct bulk_cs_wrap *csw; int rc; @@ -1859,7 +1867,7 @@ static int send_status(struct fsg_dev *fsg) u32 sd, sdinfo = 0; /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; + bh = fsg->common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { rc = sleep_thread(fsg); if (rc) @@ -1900,7 +1908,7 @@ static int send_status(struct fsg_dev *fsg) start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + fsg->common->next_buffhd_to_fill = bh->next; return 0; } @@ -1914,7 +1922,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, int needs_medium, const char *name) { int i; - int lun = fsg->cmnd[1] >> 5; + int lun = fsg->common->cmnd[1] >> 5; static const char dirletter[4] = {'u', 'o', 'i', 'n'}; char hdlen[20]; struct fsg_lun *curlun; @@ -1925,7 +1933,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, fsg->data_size); VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", name, cmnd_size, dirletter[(int) data_dir], - fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen); + fsg->data_size_from_cmnd, fsg->common->cmnd_size, hdlen); /* We can't reply at all until we know the correct data direction * and size. */ @@ -1954,7 +1962,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, } /* Verify the length of the command itself */ - if (cmnd_size != fsg->cmnd_size) { + if (cmnd_size != fsg->common->cmnd_size) { /* Special case workaround: There are plenty of buggy SCSI * implementations. Many have issues with cbw->Length @@ -1968,11 +1976,11 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, * REQUEST SENSE with cbw->Length == 10 where it should * be 6 as well. */ - if (cmnd_size <= fsg->cmnd_size) { + if (cmnd_size <= fsg->common->cmnd_size) { DBG(fsg, "%s is buggy! Expected length %d " - "but we got %d\n", name, - cmnd_size, fsg->cmnd_size); - cmnd_size = fsg->cmnd_size; + "but we got %d\n", name, + cmnd_size, fsg->common->cmnd_size); + cmnd_size = fsg->common->cmnd_size; } else { fsg->phase_error = 1; return -EINVAL; @@ -1980,27 +1988,27 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, } /* Check that the LUN values are consistent */ - if (fsg->lun != lun) + if (fsg->common->lun != lun) DBG(fsg, "using LUN %d from CBW, not LUN %d from CDB\n", - fsg->lun, lun); + fsg->common->lun, lun); /* Check the LUN */ - if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { - fsg->curlun = curlun = &fsg->luns[fsg->lun]; - if (fsg->cmnd[0] != SC_REQUEST_SENSE) { + if (fsg->common->lun >= 0 && fsg->common->lun < fsg->common->nluns) { + fsg->common->curlun = curlun = &fsg->common->luns[fsg->common->lun]; + if (fsg->common->cmnd[0] != SC_REQUEST_SENSE) { curlun->sense_data = SS_NO_SENSE; curlun->sense_data_info = 0; curlun->info_valid = 0; } } else { - fsg->curlun = curlun = NULL; + fsg->common->curlun = curlun = NULL; fsg->bad_lun_okay = 0; /* INQUIRY and REQUEST SENSE commands are explicitly allowed * to use unsupported LUNs; all others may not. */ - if (fsg->cmnd[0] != SC_INQUIRY && - fsg->cmnd[0] != SC_REQUEST_SENSE) { - DBG(fsg, "unsupported LUN %d\n", fsg->lun); + if (fsg->common->cmnd[0] != SC_INQUIRY && + fsg->common->cmnd[0] != SC_REQUEST_SENSE) { + DBG(fsg, "unsupported LUN %d\n", fsg->common->lun); return -EINVAL; } } @@ -2008,17 +2016,17 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, /* If a unit attention condition exists, only INQUIRY and * REQUEST SENSE commands are allowed; anything else must fail. */ if (curlun && curlun->unit_attention_data != SS_NO_SENSE && - fsg->cmnd[0] != SC_INQUIRY && - fsg->cmnd[0] != SC_REQUEST_SENSE) { + fsg->common->cmnd[0] != SC_INQUIRY && + fsg->common->cmnd[0] != SC_REQUEST_SENSE) { curlun->sense_data = curlun->unit_attention_data; curlun->unit_attention_data = SS_NO_SENSE; return -EINVAL; } /* Check that only command bytes listed in the mask are non-zero */ - fsg->cmnd[1] &= 0x1f; // Mask away the LUN + fsg->common->cmnd[1] &= 0x1f; // Mask away the LUN for (i = 1; i < cmnd_size; ++i) { - if (fsg->cmnd[i] && !(mask & (1 << i))) { + if (fsg->common->cmnd[i] && !(mask & (1 << i))) { if (curlun) curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; @@ -2044,10 +2052,10 @@ static int do_scsi_command(struct fsg_dev *fsg) int i; static char unknown[16]; - dump_cdb(fsg); + dump_cdb(fsg->common); /* Wait for the next buffer to become available for data or status */ - bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill; + bh = fsg->common->next_buffhd_to_drain = fsg->common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { rc = sleep_thread(fsg); if (rc) @@ -2056,11 +2064,11 @@ static int do_scsi_command(struct fsg_dev *fsg) fsg->phase_error = 0; fsg->short_packet_received = 0; - down_read(&fsg->filesem); // We're using the backing file - switch (fsg->cmnd[0]) { + down_read(&fsg->common->filesem); // We're using the backing file + switch (fsg->common->cmnd[0]) { case SC_INQUIRY: - fsg->data_size_from_cmnd = fsg->cmnd[4]; + fsg->data_size_from_cmnd = fsg->common->cmnd[4]; if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, (1<<4), 0, "INQUIRY")) == 0) @@ -2068,7 +2076,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_MODE_SELECT_6: - fsg->data_size_from_cmnd = fsg->cmnd[4]; + fsg->data_size_from_cmnd = fsg->common->cmnd[4]; if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, (1<<1) | (1<<4), 0, "MODE SELECT(6)")) == 0) @@ -2076,7 +2084,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_MODE_SELECT_10: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, (1<<1) | (3<<7), 0, "MODE SELECT(10)")) == 0) @@ -2084,7 +2092,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_MODE_SENSE_6: - fsg->data_size_from_cmnd = fsg->cmnd[4]; + fsg->data_size_from_cmnd = fsg->common->cmnd[4]; if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, (1<<1) | (1<<2) | (1<<4), 0, "MODE SENSE(6)")) == 0) @@ -2092,7 +2100,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_MODE_SENSE_10: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, (1<<1) | (1<<2) | (3<<7), 0, "MODE SENSE(10)")) == 0) @@ -2108,7 +2116,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_READ_6: - i = fsg->cmnd[4]; + i = fsg->common->cmnd[4]; fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1, @@ -2118,7 +2126,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case SC_READ_10: fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->cmnd[7]) << 9; + get_unaligned_be16(&fsg->common->cmnd[7]) << 9; if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "READ(10)")) == 0) @@ -2127,7 +2135,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case SC_READ_12: fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->cmnd[6]) << 9; + get_unaligned_be32(&fsg->common->cmnd[6]) << 9; if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "READ(12)")) == 0) @@ -2145,7 +2153,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case SC_READ_HEADER: if (!mod_data.cdrom) goto unknown_cmnd; - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, (3<<7) | (0x1f<<1), 1, "READ HEADER")) == 0) @@ -2155,7 +2163,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case SC_READ_TOC: if (!mod_data.cdrom) goto unknown_cmnd; - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, (7<<6) | (1<<1), 1, "READ TOC")) == 0) @@ -2163,7 +2171,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_READ_FORMAT_CAPACITIES: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]); + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, (3<<7), 1, "READ FORMAT CAPACITIES")) == 0) @@ -2171,7 +2179,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_REQUEST_SENSE: - fsg->data_size_from_cmnd = fsg->cmnd[4]; + fsg->data_size_from_cmnd = fsg->common->cmnd[4]; if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, (1<<4), 0, "REQUEST SENSE")) == 0) @@ -2212,7 +2220,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_WRITE_6: - i = fsg->cmnd[4]; + i = fsg->common->cmnd[4]; fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1, @@ -2222,7 +2230,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case SC_WRITE_10: fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->cmnd[7]) << 9; + get_unaligned_be16(&fsg->common->cmnd[7]) << 9; if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "WRITE(10)")) == 0) @@ -2231,7 +2239,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case SC_WRITE_12: fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->cmnd[6]) << 9; + get_unaligned_be32(&fsg->common->cmnd[6]) << 9; if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "WRITE(12)")) == 0) @@ -2251,15 +2259,15 @@ static int do_scsi_command(struct fsg_dev *fsg) default: unknown_cmnd: fsg->data_size_from_cmnd = 0; - sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); - if ((reply = check_command(fsg, fsg->cmnd_size, + sprintf(unknown, "Unknown x%02x", fsg->common->cmnd[0]); + if ((reply = check_command(fsg, fsg->common->cmnd_size, DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) { - fsg->curlun->sense_data = SS_INVALID_COMMAND; + fsg->common->curlun->sense_data = SS_INVALID_COMMAND; reply = -EINVAL; } break; } - up_read(&fsg->filesem); + up_read(&fsg->common->filesem); if (reply == -EINTR || signal_pending(current)) return -EINTR; @@ -2328,8 +2336,8 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) } /* Save the command for later */ - fsg->cmnd_size = cbw->Length; - memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size); + fsg->common->cmnd_size = cbw->Length; + memcpy(fsg->common->cmnd, cbw->CDB, fsg->common->cmnd_size); if (cbw->Flags & USB_BULK_IN_FLAG) fsg->data_dir = DATA_DIR_TO_HOST; else @@ -2337,7 +2345,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) fsg->data_size = le32_to_cpu(cbw->DataTransferLength); if (fsg->data_size == 0) fsg->data_dir = DATA_DIR_NONE; - fsg->lun = cbw->Lun; + fsg->common->lun = cbw->Lun; fsg->tag = cbw->Tag; return 0; } @@ -2349,7 +2357,7 @@ static int get_next_command(struct fsg_dev *fsg) int rc = 0; /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; + bh = fsg->common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { rc = sleep_thread(fsg); if (rc) @@ -2421,7 +2429,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting) reset: /* Deallocate the requests */ for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; + struct fsg_buffhd *bh = &fsg->common->buffhds[i]; if (bh->inreq) { usb_ep_free_request(fsg->bulk_in, bh->inreq); @@ -2466,7 +2474,7 @@ reset: /* Allocate the requests */ for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; + struct fsg_buffhd *bh = &fsg->common->buffhds[i]; if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) goto reset; @@ -2479,8 +2487,8 @@ reset: } fsg->running = 1; - for (i = 0; i < fsg->nluns; ++i) - fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; + for (i = 0; i < fsg->common->nluns; ++i) + fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; return rc; } @@ -2532,7 +2540,6 @@ static void handle_exception(struct fsg_dev *fsg) siginfo_t info; int sig; int i; - int num_active; struct fsg_buffhd *bh; enum fsg_state old_state; u8 new_config; @@ -2555,7 +2562,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Cancel all the pending transfers */ for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - bh = &fsg->buffhds[i]; + bh = &fsg->common->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(fsg->bulk_in, bh->inreq); if (bh->outreq_busy) @@ -2564,9 +2571,9 @@ static void handle_exception(struct fsg_dev *fsg) /* Wait until everything is idle */ for (;;) { - num_active = 0; + int num_active = 0; for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - bh = &fsg->buffhds[i]; + bh = &fsg->common->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } if (num_active == 0) @@ -2586,11 +2593,11 @@ static void handle_exception(struct fsg_dev *fsg) spin_lock_irq(&fsg->lock); for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - bh = &fsg->buffhds[i]; + bh = &fsg->common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } - fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain = - &fsg->buffhds[0]; + fsg->common->next_buffhd_to_fill = fsg->common->next_buffhd_to_drain = + &fsg->common->buffhds[0]; exception_req_tag = fsg->exception_req_tag; new_config = fsg->new_config; @@ -2599,8 +2606,8 @@ static void handle_exception(struct fsg_dev *fsg) if (old_state == FSG_STATE_ABORT_BULK_OUT) fsg->state = FSG_STATE_STATUS_PHASE; else { - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; + for (i = 0; i < fsg->common->nluns; ++i) { + curlun = &fsg->common->luns[i]; curlun->prevent_medium_removal = 0; curlun->sense_data = curlun->unit_attention_data = SS_NO_SENSE; @@ -2637,8 +2644,8 @@ static void handle_exception(struct fsg_dev *fsg) /* Technically this should go here, but it would only be * a waste of time. Ditto for the INTERFACE_CHANGE and * CONFIG_CHANGE cases. */ - // for (i = 0; i < fsg->nluns; ++i) - // fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; + // for (i = 0; i < fsg->common->nluns; ++i) + // fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; break; case FSG_STATE_INTERFACE_CHANGE: @@ -2662,8 +2669,8 @@ static void handle_exception(struct fsg_dev *fsg) break; case FSG_STATE_DISCONNECT: - for (i = 0; i < fsg->nluns; ++i) - fsg_lun_fsync_sub(fsg->luns + i); + for (i = 0; i < fsg->common->nluns; ++i) + fsg_lun_fsync_sub(&fsg->common->luns[i]); do_set_config(fsg, 0); // Unconfigured state break; @@ -2760,21 +2767,14 @@ static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); /*-------------------------------------------------------------------------*/ -static void fsg_release(struct kref *ref) +static void fsg_release(struct fsg_dev *fsg) { - struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref); - - kfree(fsg->luns); + kfree(fsg->common->luns); kfree(fsg); } static void lun_release(struct device *dev) { - struct rw_semaphore *filesem = dev_get_drvdata(dev); - struct fsg_dev *fsg = - container_of(filesem, struct fsg_dev, filesem); - - kref_put(&fsg->ref, fsg_release); } static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) @@ -2788,8 +2788,8 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) clear_bit(REGISTERED, &fsg->atomic_bitflags); /* Unregister the sysfs attribute files and the LUNs */ - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; + for (i = 0; i < fsg->common->nluns; ++i) { + curlun = &fsg->common->luns[i]; if (curlun->registered) { device_remove_file(&curlun->dev, &dev_attr_ro); device_remove_file(&curlun->dev, &dev_attr_file); @@ -2810,7 +2810,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) /* Free the data buffers */ for (i = 0; i < FSG_NUM_BUFFERS; ++i) - kfree(fsg->buffhds[i].buf); + kfree(fsg->common->buffhds[i].buf); /* Free the request and buffer for endpoint 0 */ if (req) { @@ -2891,15 +2891,15 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Create the LUNs, open their backing files, and register the * LUN devices in sysfs. */ - fsg->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL); - if (!fsg->luns) { + fsg->common->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL); + if (!fsg->common->luns) { rc = -ENOMEM; goto out; } - fsg->nluns = i; + fsg->common->nluns = i; - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; + for (i = 0; i < fsg->common->nluns; ++i) { + curlun = &fsg->common->luns[i]; curlun->cdrom = !!mod_data.cdrom; curlun->ro = mod_data.cdrom || mod_data.ro[i]; curlun->initially_ro = curlun->ro; @@ -2907,7 +2907,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) curlun->dev.release = lun_release; curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; - dev_set_drvdata(&curlun->dev, &fsg->filesem); + dev_set_drvdata(&curlun->dev, &fsg->common->filesem); dev_set_name(&curlun->dev,"%s-lun%d", dev_name(&gadget->dev), i); @@ -2923,7 +2923,6 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; } curlun->registered = 1; - kref_get(&fsg->ref); if (mod_data.file[i] && *mod_data.file[i]) { if ((rc = fsg_lun_open(curlun, @@ -2981,7 +2980,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Allocate the data buffers */ for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; + struct fsg_buffhd *bh = &fsg->common->buffhds[i]; /* Allocate for the bulk-in endpoint. We assume that * the buffer will also work with the bulk-out (and @@ -2991,7 +2990,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; bh->next = bh + 1; } - fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0]; + fsg->common->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->common->buffhds[0]; /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); @@ -3019,11 +3018,11 @@ static int __init fsg_bind(struct usb_gadget *gadget) } INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - INFO(fsg, "Number of LUNs=%d\n", fsg->nluns); + INFO(fsg, "Number of LUNs=%d\n", fsg->common->nluns); pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; + for (i = 0; i < fsg->common->nluns; ++i) { + curlun = &fsg->common->luns[i]; if (fsg_lun_is_open(curlun)) { p = NULL; if (pathbuf) { @@ -3092,9 +3091,15 @@ static int __init fsg_alloc(void) fsg = kzalloc(sizeof *fsg, GFP_KERNEL); if (!fsg) return -ENOMEM; + + fsg->common = kzalloc(sizeof *fsg->common, GFP_KERNEL); + if (!fsg->common) { + kfree(fsg); + return -ENOMEM; + } + spin_lock_init(&fsg->lock); - init_rwsem(&fsg->filesem); - kref_init(&fsg->ref); + init_rwsem(&fsg->common->filesem); init_completion(&fsg->thread_notifier); the_fsg = fsg; @@ -3111,7 +3116,7 @@ static int __init fsg_init(void) return rc; fsg = the_fsg; if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) - kref_put(&fsg->ref, fsg_release); + fsg_release(fsg); return rc; } module_init(fsg_init); @@ -3128,6 +3133,6 @@ static void __exit fsg_cleanup(void) /* Wait for the thread to finish up */ wait_for_completion(&fsg->thread_notifier); - kref_put(&fsg->ref, fsg_release); + fsg_release(fsg); } module_exit(fsg_cleanup); -- cgit v1.2.3 From 63a31294f624ffd59fc140c332d048f571045a29 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:21 +0100 Subject: USB: g_mass_storage: constant length buffers used Using version of fsg_buffhd structure with buf field being an array of characters with predefined size. Since mass storage function does not define changing buffer size on run-time it is not required for the field to be a pointer to void and allocating space dynamically. Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 31 +++++++++++-------------------- drivers/usb/gadget/storage_common.c | 10 ++++++++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 7750346b696c..b4ec76a3e37d 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -258,6 +258,7 @@ static const char fsg_string_interface[] = "Mass Storage"; #define FSG_NO_INTR_EP 1 +#define FSG_BUFFHD_STATIC_BUFFER 1 #include "storage_common.c" @@ -1894,9 +1895,8 @@ static int send_status(struct fsg_dev *fsg) SK(sd), ASC(sd), ASCQ(sd), sdinfo); } - /* Store and send the Bulk-only CSW */ - csw = bh->buf; + csw = (void*)bh->buf; csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); csw->Tag = fsg->tag; @@ -2808,10 +2808,6 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) complete(&fsg->thread_notifier); } - /* Free the data buffers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) - kfree(fsg->common->buffhds[i].buf); - /* Free the request and buffer for endpoint 0 */ if (req) { kfree(req->buf); @@ -2978,20 +2974,6 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; req->complete = ep0_complete; - /* Allocate the data buffers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->common->buffhds[i]; - - /* Allocate for the bulk-in endpoint. We assume that - * the buffer will also work with the bulk-out (and - * interrupt-in) endpoint. */ - bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); - if (!bh->buf) - goto out; - bh->next = bh + 1; - } - fsg->common->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->common->buffhds[0]; - /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); @@ -3087,6 +3069,8 @@ static struct usb_gadget_driver fsg_driver = { static int __init fsg_alloc(void) { struct fsg_dev *fsg; + struct fsg_buffhd *bh; + unsigned i; fsg = kzalloc(sizeof *fsg, GFP_KERNEL); if (!fsg) @@ -3098,6 +3082,13 @@ static int __init fsg_alloc(void) return -ENOMEM; } + bh = fsg->common->buffhds; + i = FSG_NUM_BUFFERS - 1; + do { + bh->next = bh + 1; + } while (++bh, --i); + bh->next = fsg->common->buffhds; + spin_lock_init(&fsg->lock); init_rwsem(&fsg->common->filesem); init_completion(&fsg->thread_notifier); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index e76c8ced41e2..60bc696778c9 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -47,6 +47,12 @@ * When FSG_NO_OTG is defined fsg_otg_desc won't be defined. */ +/* + * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included + * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN + * characters rather then a pointer to void. + */ + #include @@ -290,7 +296,11 @@ enum fsg_buffer_state { }; struct fsg_buffhd { +#ifdef FSG_BUFFHD_STATIC_BUFFER + char buf[FSG_BUFLEN]; +#else void *buf; +#endif enum fsg_buffer_state state; struct fsg_buffhd *next; -- cgit v1.2.3 From 4f7788d1fc4d7e7c3ebeea49966db987adac10b1 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:22 +0100 Subject: USB: g_mass_storage: fsg_common_init() created Moved code initialising fsg_common structure to fsg_common_init() function which is called from fsg_bind(). Moreover, changed reference counting mechanism: fsg_common has a reference counter which counts how many fsg_dev structures uses it. When this reaches zero fsg_common_release() is run which unregisters LUN devices and frees memory. fsg_common_init() takes pointer to fsg_common structure as an argument. If it is NULL function allocates storage otherwise uses pointed to memory (handy if fsg_common is a field of another structure or a static variable). fsg_common_release() will free storage only if free_storage_on_release is set -- it is initialised by fsg_common_init(): set if allocation was done, unset otherwise (one may overwrite it of course). Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 361 ++++++++++++++++++++---------------- 1 file changed, 199 insertions(+), 162 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index b4ec76a3e37d..5a0f5708c094 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -323,6 +323,8 @@ MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); /* Data shared by all the FSG instances. */ struct fsg_common { + struct usb_gadget *gadget; + /* filesem protects: backing files in use */ struct rw_semaphore filesem; @@ -337,6 +339,10 @@ struct fsg_common { unsigned int lun; struct fsg_lun *luns; struct fsg_lun *curlun; + + unsigned int free_storage_on_release:1; + + struct kref ref; }; @@ -347,9 +353,6 @@ struct fsg_dev { spinlock_t lock; struct usb_gadget *gadget; - /* reference counting: wait until all LUNs are released */ - struct kref ref; - struct usb_ep *ep0; // Handy copy of gadget->ep0 struct usb_request *ep0req; // For control responses unsigned int ep0_req_tag; @@ -2757,7 +2760,7 @@ static int fsg_main_thread(void *fsg_) } -/*-------------------------------------------------------------------------*/ +/*************************** DEVICE ATTRIBUTES ***************************/ /* The write permissions and store_xxx pointers are set in fsg_bind() */ @@ -2765,40 +2768,190 @@ static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); -/*-------------------------------------------------------------------------*/ +/****************************** FSG COMMON ******************************/ + +static void fsg_common_release(struct kref *ref); -static void fsg_release(struct fsg_dev *fsg) +static void fsg_lun_release(struct device *dev) { - kfree(fsg->common->luns); - kfree(fsg); + /* Nothing needs to be done */ } -static void lun_release(struct device *dev) +static inline void fsg_common_get(struct fsg_common *common) { + kref_get(&common->ref); } +static inline void fsg_common_put(struct fsg_common *common) +{ + kref_put(&common->ref, fsg_common_release); +} + + +static struct fsg_common *fsg_common_init(struct fsg_common *common, + struct usb_gadget *gadget) +{ + struct fsg_buffhd *bh; + struct fsg_lun *curlun; + int nluns, i, rc; + + /* Find out how many LUNs there should be */ + nluns = mod_data.nluns; + if (nluns == 0) + nluns = max(mod_data.num_filenames, 1u); + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns); + return ERR_PTR(-EINVAL); + } + + /* Allocate? */ + if (!common) { + common = kzalloc(sizeof *common, GFP_KERNEL); + if (!common) + return ERR_PTR(-ENOMEM); + common->free_storage_on_release = 1; + } else { + memset(common, 0, sizeof common); + common->free_storage_on_release = 0; + } + common->gadget = gadget; + + /* Create the LUNs, open their backing files, and register the + * LUN devices in sysfs. */ + curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL); + if (!curlun) { + kfree(common); + return ERR_PTR(-ENOMEM); + } + common->luns = curlun; + + init_rwsem(&common->filesem); + + for (i = 0; i < nluns; ++i, ++curlun) { + curlun->cdrom = !!mod_data.cdrom; + curlun->ro = mod_data.cdrom || mod_data.ro[i]; + curlun->removable = mod_data.removable; + curlun->dev.release = fsg_lun_release; + curlun->dev.parent = &gadget->dev; + curlun->dev.driver = &fsg_driver.driver; + dev_set_drvdata(&curlun->dev, &common->filesem); + dev_set_name(&curlun->dev,"%s-lun%d", + dev_name(&gadget->dev), i); + + rc = device_register(&curlun->dev); + if (rc) { + INFO(common, "failed to register LUN%d: %d\n", i, rc); + common->nluns = i; + goto error_release; + } + + rc = device_create_file(&curlun->dev, &dev_attr_ro); + if (rc) + goto error_luns; + rc = device_create_file(&curlun->dev, &dev_attr_file); + if (rc) + goto error_luns; + + if (mod_data.file[i] && *mod_data.file[i]) { + rc = fsg_lun_open(curlun, mod_data.file[i]); + if (rc) + goto error_luns; + } else if (!mod_data.removable) { + ERROR(common, "no file given for LUN%d\n", i); + rc = -EINVAL; + goto error_luns; + } + } + common->nluns = nluns; + + + /* Data buffers cyclic list */ + /* Buffers in buffhds are static -- no need for additional + * allocation. */ + bh = common->buffhds; + i = FSG_NUM_BUFFERS - 1; + do { + bh->next = bh + 1; + } while (++bh, --i); + bh->next = common->buffhds; + + + /* Release */ + if (mod_data.release == 0xffff) { // Parameter wasn't set + int gcnum; + + /* The sa1100 controller is not supported */ + if (gadget_is_sa1100(gadget)) + gcnum = -1; + else + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + mod_data.release = 0x0300 + gcnum; + else { + WARNING(common, "controller '%s' not recognized\n", + gadget->name); + WARNING(common, "controller '%s' not recognized\n", + gadget->name); + mod_data.release = 0x0399; + } + } + + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) + mod_data.can_stall = 0; + + + kref_init(&common->ref); + return common; + + +error_luns: + common->nluns = i + 1; +error_release: + /* Call fsg_common_release() directly, ref is not initialised */ + fsg_common_release(&common->ref); + return ERR_PTR(rc); +} + + +static void fsg_common_release(struct kref *ref) +{ + struct fsg_common *common = + container_of(ref, struct fsg_common, ref); + unsigned i = common->nluns; + struct fsg_lun *lun = common->luns; + + /* Beware tempting for -> do-while optimization: when in error + * recovery nluns may be zero. */ + + for (; i; --i, ++lun) { + device_remove_file(&lun->dev, &dev_attr_ro); + device_remove_file(&lun->dev, &dev_attr_file); + fsg_lun_close(lun); + device_unregister(&lun->dev); + } + + kfree(common->luns); + if (common->free_storage_on_release) + kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + + static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) { struct fsg_dev *fsg = get_gadget_data(gadget); - int i; - struct fsg_lun *curlun; struct usb_request *req = fsg->ep0req; DBG(fsg, "unbind\n"); clear_bit(REGISTERED, &fsg->atomic_bitflags); - /* Unregister the sysfs attribute files and the LUNs */ - for (i = 0; i < fsg->common->nluns; ++i) { - curlun = &fsg->common->luns[i]; - if (curlun->registered) { - device_remove_file(&curlun->dev, &dev_attr_ro); - device_remove_file(&curlun->dev, &dev_attr_file); - fsg_lun_close(curlun); - device_unregister(&curlun->dev); - curlun->registered = 0; - } - } - /* If the thread isn't already dead, tell it to exit now */ if (fsg->state != FSG_STATE_TERMINATED) { raise_exception(fsg, FSG_STATE_EXIT); @@ -2814,43 +2967,15 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) usb_ep_free_request(fsg->ep0, req); } + fsg_common_put(fsg->common); + kfree(fsg); set_gadget_data(gadget, NULL); } -static int __init check_parameters(struct fsg_dev *fsg) -{ - int gcnum; - - /* Some peripheral controllers are known not to be able to - * halt bulk endpoints correctly. If one of them is present, - * disable stalls. - */ - if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) - mod_data.can_stall = 0; - - if (mod_data.release == 0xffff) { // Parameter wasn't set - /* The sa1100 controller is not supported */ - if (gadget_is_sa1100(fsg->gadget)) - gcnum = -1; - else - gcnum = usb_gadget_controller_number(fsg->gadget); - if (gcnum >= 0) - mod_data.release = 0x0300 + gcnum; - else { - WARNING(fsg, "controller '%s' not recognized\n", - fsg->gadget->name); - mod_data.release = 0x0399; - } - } - - return 0; -} - - static int __init fsg_bind(struct usb_gadget *gadget) { - struct fsg_dev *fsg = the_fsg; + struct fsg_dev *fsg; int rc; int i; struct fsg_lun *curlun; @@ -2858,15 +2983,27 @@ static int __init fsg_bind(struct usb_gadget *gadget) struct usb_request *req; char *pathbuf, *p; + /* Allocate */ + fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + if (!fsg) + return -ENOMEM; + + /* Initialise common */ + fsg->common = fsg_common_init(0, gadget); + if (IS_ERR(fsg->common)) + return PTR_ERR(fsg->common); + + /* Basic parameters */ fsg->gadget = gadget; set_gadget_data(gadget, fsg); fsg->ep0 = gadget->ep0; fsg->ep0->driver_data = fsg; - if ((rc = check_parameters(fsg)) != 0) - goto out; + spin_lock_init(&fsg->lock); + init_completion(&fsg->thread_notifier); - if (mod_data.removable) { // Enable the store_xxx attributes + /* Enable the store_xxx attributes */ + if (mod_data.removable) { dev_attr_file.attr.mode = 0644; dev_attr_file.store = fsg_store_file; if (!mod_data.cdrom) { @@ -2875,62 +3012,6 @@ static int __init fsg_bind(struct usb_gadget *gadget) } } - /* Find out how many LUNs there should be */ - i = mod_data.nluns; - if (i == 0) - i = max(mod_data.num_filenames, 1u); - if (i > FSG_MAX_LUNS) { - ERROR(fsg, "invalid number of LUNs: %d\n", i); - rc = -EINVAL; - goto out; - } - - /* Create the LUNs, open their backing files, and register the - * LUN devices in sysfs. */ - fsg->common->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL); - if (!fsg->common->luns) { - rc = -ENOMEM; - goto out; - } - fsg->common->nluns = i; - - for (i = 0; i < fsg->common->nluns; ++i) { - curlun = &fsg->common->luns[i]; - curlun->cdrom = !!mod_data.cdrom; - curlun->ro = mod_data.cdrom || mod_data.ro[i]; - curlun->initially_ro = curlun->ro; - curlun->removable = mod_data.removable; - curlun->dev.release = lun_release; - curlun->dev.parent = &gadget->dev; - curlun->dev.driver = &fsg_driver.driver; - dev_set_drvdata(&curlun->dev, &fsg->common->filesem); - dev_set_name(&curlun->dev,"%s-lun%d", - dev_name(&gadget->dev), i); - - if ((rc = device_register(&curlun->dev)) != 0) { - INFO(fsg, "failed to register LUN%d: %d\n", i, rc); - goto out; - } - if ((rc = device_create_file(&curlun->dev, - &dev_attr_ro)) != 0 || - (rc = device_create_file(&curlun->dev, - &dev_attr_file)) != 0) { - device_unregister(&curlun->dev); - goto out; - } - curlun->registered = 1; - - if (mod_data.file[i] && *mod_data.file[i]) { - if ((rc = fsg_lun_open(curlun, - mod_data.file[i])) != 0) - goto out; - } else if (!mod_data.removable) { - ERROR(fsg, "no file given for LUN%d\n", i); - rc = -EINVAL; - goto out; - } - } - /* Find all the endpoints we will use */ usb_ep_autoconfig_reset(gadget); ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); @@ -3028,6 +3109,8 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Tell the thread to start working */ wake_up_process(fsg->thread_task); + + the_fsg = fsg; return 0; autoconf_fail: @@ -3066,64 +3149,18 @@ static struct usb_gadget_driver fsg_driver = { }; -static int __init fsg_alloc(void) -{ - struct fsg_dev *fsg; - struct fsg_buffhd *bh; - unsigned i; - - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); - if (!fsg) - return -ENOMEM; - - fsg->common = kzalloc(sizeof *fsg->common, GFP_KERNEL); - if (!fsg->common) { - kfree(fsg); - return -ENOMEM; - } - - bh = fsg->common->buffhds; - i = FSG_NUM_BUFFERS - 1; - do { - bh->next = bh + 1; - } while (++bh, --i); - bh->next = fsg->common->buffhds; - - spin_lock_init(&fsg->lock); - init_rwsem(&fsg->common->filesem); - init_completion(&fsg->thread_notifier); - - the_fsg = fsg; - return 0; -} - - static int __init fsg_init(void) { - int rc; - struct fsg_dev *fsg; - - if ((rc = fsg_alloc()) != 0) - return rc; - fsg = the_fsg; - if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) - fsg_release(fsg); - return rc; + return usb_gadget_register_driver(&fsg_driver); } module_init(fsg_init); static void __exit fsg_cleanup(void) { - struct fsg_dev *fsg = the_fsg; - /* Unregister the driver iff the thread hasn't already done so */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) + if (the_fsg && + test_and_clear_bit(REGISTERED, &the_fsg->atomic_bitflags)) usb_gadget_unregister_driver(&fsg_driver); - - /* Wait for the thread to finish up */ - wait_for_completion(&fsg->thread_notifier); - - fsg_release(fsg); } module_exit(fsg_cleanup); -- cgit v1.2.3 From 6d59e9af40420037a820d265d0bc11c72ca58965 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 28 Oct 2009 16:57:30 +0100 Subject: USB: Interface Association Descriptors added to CDC & RNDIS Without Interface Association Descriptor, the CDC serial and RNDIS functions did not work correctly when added to a composite gadget with other functions. This is because, it defined two interfaces and some hosts tried to treat each interface separatelly. Signed-off-by: Michal Nazarewicz Cc: David Brownell Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_acm.c | 28 ++++++++++++++++++++++++++++ drivers/usb/gadget/f_rndis.c | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 7953948bfe4a..30f4c8e86b06 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -4,6 +4,8 @@ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) * Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by Nokia Corporation + * Copyright (C) 2009 by Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) * * This software is distributed under the terms of the GNU General * Public License ("GPL") as published by the Free Software Foundation, @@ -99,6 +101,20 @@ static inline struct f_acm *port_to_acm(struct gserial *p) /* interface and class descriptors: */ +static struct usb_interface_assoc_descriptor +acm_iad_descriptor = { + .bLength = sizeof acm_iad_descriptor, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + /* .bFirstInterface = DYNAMIC, */ + .bInterfaceCount = 2, // control + data + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTO_NONE, + /* .iFunction = DYNAMIC */ +}; + + static struct usb_interface_descriptor acm_control_interface_desc __initdata = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, @@ -178,6 +194,7 @@ static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = { }; static struct usb_descriptor_header *acm_fs_function[] __initdata = { + (struct usb_descriptor_header *) &acm_iad_descriptor, (struct usb_descriptor_header *) &acm_control_interface_desc, (struct usb_descriptor_header *) &acm_header_desc, (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, @@ -216,6 +233,7 @@ static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = { }; static struct usb_descriptor_header *acm_hs_function[] __initdata = { + (struct usb_descriptor_header *) &acm_iad_descriptor, (struct usb_descriptor_header *) &acm_control_interface_desc, (struct usb_descriptor_header *) &acm_header_desc, (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, @@ -232,11 +250,13 @@ static struct usb_descriptor_header *acm_hs_function[] __initdata = { #define ACM_CTRL_IDX 0 #define ACM_DATA_IDX 1 +#define ACM_IAD_IDX 2 /* static strings, in UTF-8 */ static struct usb_string acm_string_defs[] = { [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", [ACM_DATA_IDX].s = "CDC ACM Data", + [ACM_IAD_IDX ].s = "CDC Serial", { /* ZEROES END LIST */ }, }; @@ -563,6 +583,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) if (status < 0) goto fail; acm->ctrl_id = status; + acm_iad_descriptor.bFirstInterface = status; acm_control_interface_desc.bInterfaceNumber = status; acm_union_desc .bMasterInterface0 = status; @@ -732,6 +753,13 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num) acm_string_defs[ACM_DATA_IDX].id = status; acm_data_interface_desc.iInterface = status; + + status = usb_string_id(c->cdev); + if (status < 0) + return status; + acm_string_defs[ACM_IAD_IDX].id = status; + + acm_iad_descriptor.iFunction = status; } /* allocate and initialize one new instance */ diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index c9966cc07d3a..95dae4c1ea40 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -4,6 +4,8 @@ * Copyright (C) 2003-2005,2008 David Brownell * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -149,8 +151,8 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { .bDataInterface = 0x01, }; -static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { - .bLength = sizeof acm_descriptor, +static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = { + .bLength = sizeof rndis_acm_descriptor, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ACM_TYPE, @@ -179,6 +181,20 @@ static struct usb_interface_descriptor rndis_data_intf __initdata = { /* .iInterface = DYNAMIC */ }; + +static struct usb_interface_assoc_descriptor +rndis_iad_descriptor = { + .bLength = sizeof rndis_iad_descriptor, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, /* XXX, hardcoded */ + .bInterfaceCount = 2, // control + data + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bFunctionProtocol = USB_CDC_PROTO_NONE, + /* .iFunction = DYNAMIC */ +}; + /* full speed support: */ static struct usb_endpoint_descriptor fs_notify_desc __initdata = { @@ -208,11 +224,12 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = { }; static struct usb_descriptor_header *eth_fs_function[] __initdata = { + (struct usb_descriptor_header *) &rndis_iad_descriptor, /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_acm_descriptor, (struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &fs_notify_desc, /* data interface has no altsetting */ @@ -252,11 +269,12 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = { }; static struct usb_descriptor_header *eth_hs_function[] __initdata = { + (struct usb_descriptor_header *) &rndis_iad_descriptor, /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_acm_descriptor, (struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &hs_notify_desc, /* data interface has no altsetting */ @@ -271,6 +289,7 @@ static struct usb_descriptor_header *eth_hs_function[] __initdata = { static struct usb_string rndis_string_defs[] = { [0].s = "RNDIS Communications Control", [1].s = "RNDIS Ethernet Data", + [2].s = "RNDIS", { } /* end of list */ }; @@ -587,6 +606,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (status < 0) goto fail; rndis->ctrl_id = status; + rndis_iad_descriptor.bFirstInterface = status; rndis_control_intf.bInterfaceNumber = status; rndis_union_desc.bMasterInterface0 = status; @@ -798,6 +818,13 @@ int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) return status; rndis_string_defs[1].id = status; rndis_data_intf.iInterface = status; + + /* IAD iFunction label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_string_defs[2].id = status; + rndis_iad_descriptor.iFunction = status; } /* allocate and initialize one new instance */ -- cgit v1.2.3 From 2b47e59177f3fad00860f97035e7abc785246ed0 Mon Sep 17 00:00:00 2001 From: Elina Pasheva Date: Wed, 4 Nov 2009 10:25:48 -0800 Subject: USB: serial: sierra driver memory reduction This patch deals with reducing the memory footprint for sierra driver. This optimization is aimed for embedded software customers. Some sierra modems can expose upwards of 7 USB interfaces, each possibly offering different services. In general, interfaces used for the exchange of wireless data require much higher throughput, hence require more memory (i.e. more URBs) than lower performance interfaces. URBs used for the IN direction are pre-allocated by the driver and this patch introduces a way to configure the number of IN URBs allocated on a per-interface basis. Interfaces with lower throughput requirements receive fewer URBs, thereby reducing the RAM memory consumed by the driver. NOTE1: This driver has always pre-allocated URBs for the IN direction. NOTE2: The number of URBs pre-allocated for the low-performance interfaces has already been extensively tested in previous versions of this driver. We also added the capability to log function calls by adding DEBUG flag. Please note that this flag is commented out because this is the default state for it. Signed-off-by: Elina Pasheva Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/sierra.c | 89 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 12 deletions(-) diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 5019325ba25d..c5c41aed106d 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -16,8 +16,9 @@ Portions based on the option driver by Matthias Urlichs Whom based his on the Keyspan driver by Hugh Blemings */ - -#define DRIVER_VERSION "v.1.3.8" +/* Uncomment to log function calls */ +/* #define DEBUG */ +#define DRIVER_VERSION "v.1.7.16" #define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer" #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems" @@ -33,8 +34,10 @@ #define SWIMS_USB_REQUEST_SetPower 0x00 #define SWIMS_USB_REQUEST_SetNmea 0x07 -#define N_IN_URB 8 -#define N_OUT_URB 64 +#define N_IN_URB_HM 8 +#define N_OUT_URB_HM 64 +#define N_IN_URB 4 +#define N_OUT_URB 4 #define IN_BUFLEN 4096 #define MAX_TRANSFER (PAGE_SIZE - 512) @@ -124,6 +127,23 @@ static int is_blacklisted(const u8 ifnum, return 0; } +static int is_himemory(const u8 ifnum, + const struct sierra_iface_info *himemorylist) +{ + const u8 *info; + int i; + + if (himemorylist) { + info = himemorylist->ifaceinfo; + + for (i=0; i < himemorylist->infolen; i++) { + if (info[i] == ifnum) + return 1; + } + } + return 0; +} + static int sierra_calc_interface(struct usb_serial *serial) { int interface; @@ -186,6 +206,20 @@ static int sierra_probe(struct usb_serial *serial, return result; } +/* interfaces with higher memory requirements */ +static const u8 hi_memory_typeA_ifaces[] = { 0, 2 }; +static const struct sierra_iface_info typeA_interface_list = { + .infolen = ARRAY_SIZE(hi_memory_typeA_ifaces), + .ifaceinfo = hi_memory_typeA_ifaces, +}; + +static const u8 hi_memory_typeB_ifaces[] = { 3, 4, 5, 6 }; +static const struct sierra_iface_info typeB_interface_list = { + .infolen = ARRAY_SIZE(hi_memory_typeB_ifaces), + .ifaceinfo = hi_memory_typeB_ifaces, +}; + +/* 'blacklist' of interfaces not served by this driver */ static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 }; static const struct sierra_iface_info direct_ip_interface_blacklist = { .infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces), @@ -286,8 +320,10 @@ struct sierra_port_private { struct usb_anchor active; struct usb_anchor delayed; + int num_out_urbs; + int num_in_urbs; /* Input endpoints and buffers for this port */ - struct urb *in_urbs[N_IN_URB]; + struct urb *in_urbs[N_IN_URB_HM]; /* Settings for the port */ int rts_state; /* Handshaking pins (outputs) */ @@ -460,7 +496,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, spin_lock_irqsave(&portdata->lock, flags); dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__, portdata->outstanding_urbs); - if (portdata->outstanding_urbs > N_OUT_URB) { + if (portdata->outstanding_urbs > portdata->num_out_urbs) { spin_unlock_irqrestore(&portdata->lock, flags); dev_dbg(&port->dev, "%s - write limit hit\n", __func__); return 0; @@ -665,7 +701,7 @@ static int sierra_write_room(struct tty_struct *tty) /* try to give a good number back based on if we have any free urbs at * this point in time */ spin_lock_irqsave(&portdata->lock, flags); - if (portdata->outstanding_urbs > N_OUT_URB * 2 / 3) { + if (portdata->outstanding_urbs > (portdata->num_out_urbs * 2) / 3) { spin_unlock_irqrestore(&portdata->lock, flags); dev_dbg(&port->dev, "%s - write limit hit\n", __func__); return 0; @@ -680,7 +716,7 @@ static void sierra_stop_rx_urbs(struct usb_serial_port *port) int i; struct sierra_port_private *portdata = usb_get_serial_port_data(port); - for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) + for (i = 0; i < portdata->num_in_urbs; i++) usb_kill_urb(portdata->in_urbs[i]); usb_kill_urb(port->interrupt_in_urb); @@ -695,7 +731,7 @@ static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags) struct sierra_port_private *portdata = usb_get_serial_port_data(port); ok_cnt = 0; - for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) { + for (i = 0; i < portdata->num_in_urbs; i++) { urb = portdata->in_urbs[i]; if (!urb) continue; @@ -791,7 +827,7 @@ static void sierra_close(struct usb_serial_port *port) /* Stop reading urbs */ sierra_stop_rx_urbs(port); /* .. and release them */ - for (i = 0; i < N_IN_URB; i++) { + for (i = 0; i < portdata->num_in_urbs; i++) { sierra_release_urb(portdata->in_urbs[i]); portdata->in_urbs[i] = NULL; } @@ -818,7 +854,7 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port) endpoint = port->bulk_in_endpointAddress; - for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) { + for (i = 0; i < portdata->num_in_urbs; i++) { urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port, IN_BUFLEN, GFP_KERNEL, sierra_indat_callback); @@ -869,7 +905,9 @@ static int sierra_startup(struct usb_serial *serial) { struct usb_serial_port *port; struct sierra_port_private *portdata; + struct sierra_iface_info *himemoryp = NULL; int i; + u8 ifnum; dev_dbg(&serial->dev->dev, "%s\n", __func__); @@ -886,13 +924,40 @@ static int sierra_startup(struct usb_serial *serial) portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); if (!portdata) { dev_dbg(&port->dev, "%s: kmalloc for " - "sierra_port_private (%d) failed!.\n", + "sierra_port_private (%d) failed!\n", __func__, i); return -ENOMEM; } spin_lock_init(&portdata->lock); init_usb_anchor(&portdata->active); init_usb_anchor(&portdata->delayed); + ifnum = i; + /* Assume low memory requirements */ + portdata->num_out_urbs = N_OUT_URB; + portdata->num_in_urbs = N_IN_URB; + + /* Determine actual memory requirements */ + if (serial->num_ports == 1) { + /* Get interface number for composite device */ + ifnum = sierra_calc_interface(serial); + himemoryp = + (struct sierra_iface_info *)&typeB_interface_list; + if (is_himemory(ifnum, himemoryp)) { + portdata->num_out_urbs = N_OUT_URB_HM; + portdata->num_in_urbs = N_IN_URB_HM; + } + } + else { + himemoryp = + (struct sierra_iface_info *)&typeA_interface_list; + if (is_himemory(i, himemoryp)) { + portdata->num_out_urbs = N_OUT_URB_HM; + portdata->num_in_urbs = N_IN_URB_HM; + } + } + dev_dbg(&serial->dev->dev, + "Memory usage (urbs) interface #%d, in=%d, out=%d\n", + ifnum,portdata->num_in_urbs, portdata->num_out_urbs ); /* Set the port private data pointer */ usb_set_serial_port_data(port, portdata); } -- cgit v1.2.3 From 002b9e69e84a150275f58b7b7c1f4851e08d568a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 6 Nov 2009 12:29:40 -0500 Subject: USB: EHCI: add native scatter-gather support This patch (as1300) adds native scatter-gather support to ehci-hcd. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 ++ drivers/usb/host/ehci-q.c | 32 +++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 0090f163b160..438321b04559 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -604,6 +604,8 @@ static int ehci_init(struct usb_hcd *hcd) } ehci->command = temp; + /* Accept arbitrarily long scatter-gather lists */ + hcd->self.sg_tablesize = ~0; return 0; } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 139a2cc3f641..a427d3b00634 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -616,9 +616,11 @@ qh_urb_transaction ( ) { struct ehci_qtd *qtd, *qtd_prev; dma_addr_t buf; - int len, maxpacket; + int len, this_sg_len, maxpacket; int is_input; u32 token; + int i; + struct scatterlist *sg; /* * URBs map to sequences of QTDs: one logical transaction @@ -659,7 +661,20 @@ qh_urb_transaction ( /* * data transfer stage: buffer setup */ - buf = urb->transfer_dma; + i = urb->num_sgs; + if (len > 0 && i > 0) { + sg = urb->sg->sg; + buf = sg_dma_address(sg); + + /* urb->transfer_buffer_length may be smaller than the + * size of the scatterlist (or vice versa) + */ + this_sg_len = min_t(int, sg_dma_len(sg), len); + } else { + sg = NULL; + buf = urb->transfer_dma; + this_sg_len = len; + } if (is_input) token |= (1 /* "in" */ << 8); @@ -675,7 +690,9 @@ qh_urb_transaction ( for (;;) { int this_qtd_len; - this_qtd_len = qtd_fill(ehci, qtd, buf, len, token, maxpacket); + this_qtd_len = qtd_fill(ehci, qtd, buf, this_sg_len, token, + maxpacket); + this_sg_len -= this_qtd_len; len -= this_qtd_len; buf += this_qtd_len; @@ -691,8 +708,13 @@ qh_urb_transaction ( if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) token ^= QTD_TOGGLE; - if (likely (len <= 0)) - break; + if (likely(this_sg_len <= 0)) { + if (--i <= 0 || len <= 0) + break; + sg = sg_next(sg); + buf = sg_dma_address(sg); + this_sg_len = min_t(int, sg_dma_len(sg), len); + } qtd_prev = qtd; qtd = ehci_qtd_alloc (ehci, flags); -- cgit v1.2.3 From ffc78691888879a299bfd876a20401b86f1ee860 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 6 Nov 2009 12:32:23 -0500 Subject: USB: add scatter-gather support to usbmon This patch (as1301) adds support to usbmon for scatter-gather URBs. The text interface looks at only the first scatterlist element, since it never copies more than 32 bytes of data anyway. The binary interface copies as much data as possible up to the first non-addressable buffer. Signed-off-by: Alan Stern CC: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 51 ++++++++++++++++++++++++++++++++++++++-------- drivers/usb/mon/mon_text.c | 23 ++++++++++++++++++--- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 10f3205798e8..385ec0520167 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -221,7 +222,7 @@ static void mon_free_buff(struct mon_pgmap *map, int npages); /* * This is a "chunked memcpy". It does not manipulate any counters. */ -static void mon_copy_to_buff(const struct mon_reader_bin *this, +static unsigned int mon_copy_to_buff(const struct mon_reader_bin *this, unsigned int off, const unsigned char *from, unsigned int length) { unsigned int step_len; @@ -246,6 +247,7 @@ static void mon_copy_to_buff(const struct mon_reader_bin *this, from += step_len; length -= step_len; } + return off; } /* @@ -394,14 +396,44 @@ static inline char mon_bin_get_setup(unsigned char *setupb, return 0; } -static char mon_bin_get_data(const struct mon_reader_bin *rp, - unsigned int offset, struct urb *urb, unsigned int length) +static unsigned int mon_bin_get_data(const struct mon_reader_bin *rp, + unsigned int offset, struct urb *urb, unsigned int length, + char *flag) { + int i; + struct scatterlist *sg; + unsigned int this_len; + + *flag = 0; + if (urb->num_sgs == 0) { + if (urb->transfer_buffer == NULL) { + *flag = 'Z'; + return length; + } + mon_copy_to_buff(rp, offset, urb->transfer_buffer, length); + length = 0; - if (urb->transfer_buffer == NULL) - return 'Z'; - mon_copy_to_buff(rp, offset, urb->transfer_buffer, length); - return 0; + } else { + /* If IOMMU coalescing occurred, we cannot trust sg_page */ + if (urb->sg->nents != urb->num_sgs) { + *flag = 'D'; + return length; + } + + /* Copy up to the first non-addressable segment */ + for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) { + if (length == 0 || PageHighMem(sg_page(sg))) + break; + this_len = min_t(unsigned int, sg->length, length); + offset = mon_copy_to_buff(rp, offset, sg_virt(sg), + this_len); + length -= this_len; + } + if (i == 0) + *flag = 'D'; + } + + return length; } static void mon_bin_get_isodesc(const struct mon_reader_bin *rp, @@ -536,8 +568,9 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, } if (length != 0) { - ep->flag_data = mon_bin_get_data(rp, offset, urb, length); - if (ep->flag_data != 0) { /* Yes, it's 0x00, not '0' */ + length = mon_bin_get_data(rp, offset, urb, length, + &ep->flag_data); + if (length > 0) { delta = (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); ep->len_cap -= length; delta -= (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 9f1a9227ebe6..047568ff223d 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "usb_mon.h" @@ -137,6 +138,8 @@ static inline char mon_text_get_setup(struct mon_event_text *ep, static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, int len, char ev_type, struct mon_bus *mbus) { + void *src; + if (len <= 0) return 'L'; if (len >= DATA_MAX) @@ -150,10 +153,24 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, return '>'; } - if (urb->transfer_buffer == NULL) - return 'Z'; /* '0' would be not as pretty. */ + if (urb->num_sgs == 0) { + src = urb->transfer_buffer; + if (src == NULL) + return 'Z'; /* '0' would be not as pretty. */ + } else { + struct scatterlist *sg = urb->sg->sg; + + /* If IOMMU coalescing occurred, we cannot trust sg_page */ + if (urb->sg->nents != urb->num_sgs || + PageHighMem(sg_page(sg))) + return 'D'; + + /* For the text interface we copy only the first sg buffer */ + len = min_t(int, sg->length, len); + src = sg_virt(sg); + } - memcpy(ep->data, urb->transfer_buffer, len); + memcpy(ep->data, src, len); return 0; } -- cgit v1.2.3 From b623984e6c59e60f2e6a6d94e18917861d5b2db2 Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Sat, 7 Nov 2009 01:16:32 +0530 Subject: USB: ehci: Allow EHCI to be built on OMAP3 usb: ehci: Allow EHCI to be built on OMAP3 OMAP3 chips have a built-in EHCI controller. The recently introduced omap ehci-hcd driver missed out on selecting USB_ARCH_HAS_EHCI in Kconfig. Without this, the driver cannot be built. Signed-off-by: Anand Gadiyar Cc: Vikram Pandita Cc: Ajay Kumar Gupta Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index a3b7caf265d4..81aac7f4ca59 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -61,6 +61,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_W90X900 default y if ARCH_AT91SAM9G45 default y if ARCH_MXC + default y if ARCH_OMAP34XX default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. -- cgit v1.2.3 From 2f1b3c7dca7d610755b5d04ece21b75d3cf91db9 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 5 Nov 2009 10:37:03 -0600 Subject: USB: Check results of dma_map_single In map_urb_for_dma(), the DMA address returned by dma_map_single() is not checked to determine if it is legal. This lack of checking contributed to a problem with the libertas wireless driver (http://marc.info/?l=linux-wireless&m=125695331205062&w=2). The difficulty was not detected until the buffer was unmapped. By this time memory corruption had occurred. The situation is fixed by testing the returned DMA address, and returning -EAGAIN if the address is invalid. Signed-off-by: Larry Finger Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 34de475f016e..026ab2fe5c2d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1275,13 +1275,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, if (usb_endpoint_xfer_control(&urb->ep->desc) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { - if (hcd->self.uses_dma) + if (hcd->self.uses_dma) { urb->setup_dma = dma_map_single( hcd->self.controller, urb->setup_packet, sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); - else if (hcd->driver->flags & HCD_LOCAL_MEM) + if (dma_mapping_error(hcd->self.controller, + urb->setup_dma)) + return -EAGAIN; + } else if (hcd->driver->flags & HCD_LOCAL_MEM) ret = hcd_alloc_coherent( urb->dev->bus, mem_flags, &urb->setup_dma, @@ -1293,13 +1296,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (ret == 0 && urb->transfer_buffer_length != 0 && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { - if (hcd->self.uses_dma) + if (hcd->self.uses_dma) { urb->transfer_dma = dma_map_single ( hcd->self.controller, urb->transfer_buffer, urb->transfer_buffer_length, dir); - else if (hcd->driver->flags & HCD_LOCAL_MEM) { + if (dma_mapping_error(hcd->self.controller, + urb->transfer_dma)) + return -EAGAIN; + } else if (hcd->driver->flags & HCD_LOCAL_MEM) { ret = hcd_alloc_coherent( urb->dev->bus, mem_flags, &urb->transfer_dma, -- cgit v1.2.3 From 714109eea81ff85c6f73d233a0873059d9318645 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Thu, 19 Nov 2009 10:35:33 +0000 Subject: USB: Exposing second ACM channel as tty for Nokia S60 phones. Nokia S60 phones expose two ACM channels. The first is a modem and is picked up by the standard AT-command interface information in the CDC-ACM driver. The second is marked as having a vendor-specific protocol. Normally, we don't expose those as ttys. (On some other devices, they may be claimed by the rndis_host driver and used as a network interface). But on S60 this second ACM channel is the way that third-party S60 application developers are expected to communicate over USB. It acts as a serial device at the S60 end, and so it should on Linux too. The list of devices is largely derived from: http://wiki.forum.nokia.com/index.php/S60_Platform_and_device_identification_codes http://wiki.forum.nokia.com/index.php/Nokia_USB_Product_IDs and includes only the S60 3rd Edition+ devices documented there. There are many devices for which the USB device ID is not documented, including: Nokia 6290 Nokia E63 Nokia 5630 XpressMusic Nokia 5730 XpressMusic Nokia 6710 Navigator Nokia 6720 classic Nokia 6730 Classic Nokia 6760 slide Nokia 6790 slide Nokia 6790 Surge Nokia E52 Nokia E55 Nokia E71x (AT&T) Nokia E72 Nokia E75 Nokia E75 US+LTA variant Nokia N79 Nokia N86 8MP Nokia 5230 (RM-588) Nokia 5230 (RM-594) Nokia 5530 XpressMusic Nokia 5530 XpressMusic (china) Nokia 5800 XM Nokia N97 (RM-506) Nokia N97 mini Nokia X6 It would be good to add those subsequently. Signed-off-by: Adrian Taylor Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 58 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index e4eca7810bcf..34d4eb98829e 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1461,6 +1461,12 @@ err_out: } #endif /* CONFIG_PM */ + +#define NOKIA_PCSUITE_ACM_INFO(x) \ + USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \ + USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \ + USB_CDC_ACM_PROTO_VENDOR) + /* * USB driver structure. */ @@ -1519,6 +1525,57 @@ static struct usb_device_id acm_ids[] = { .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + /* Nokia S60 phones expose two ACM channels. The first is + * a modem and is picked up by the standard AT-command + * information below. The second is 'vendor-specific' but + * is treated as a serial device at the S60 end, so we want + * to expose it on Linux too. */ + { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */ + { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */ + { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */ + { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */ + { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */ + { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */ + { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */ + { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */ + { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */ + { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */ + { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */ + { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */ + { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */ + { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */ + { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */ + { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */ + { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */ + { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */ + { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */ + { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */ + { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */ + { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */ + { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */ + { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */ + { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */ + { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */ + { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */ + { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */ + { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */ + { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */ + { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */ + { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */ + { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */ + { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */ + { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */ + { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */ + { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */ + { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */ + { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */ + { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */ + { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */ + { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */ + { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */ + + /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */ + /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_V25TER) }, @@ -1533,7 +1590,6 @@ static struct usb_device_id acm_ids[] = { { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_CDMA) }, - /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */ { } }; -- cgit v1.2.3 From 654057b1269910fa55060fd67e365260d8cfd4c9 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Wed, 18 Nov 2009 20:11:23 -0500 Subject: USB: add hex/bcd detection to usb modalias generation The current code to generate usb modaliases from usb_device_id assumes that the device's bcdDevice descriptor will actually be in BCD format. While this should be a sane assumption, some devices don't follow spec and just use plain old hex. This causes drivers for these devices to generate invalid modalias lines which will never actually match for the hardware. The following patch adds hex support for bcdDevice in file2alias.c by detecting when a driver uses a hex formatted bcdDevice_(lo|hi) and adjusts the output to hex format accordingly. Drivers for devices which have bcdDevice conforming to BCD will have no change in modalias output. Drivers for devices which don't conform (i.e. ibmcam) should now generate valid modaliases. EXAMPLE OUTPUT (ibmcam; space added to highlight change) Old: usb:v0545p800D d030[10-9] dc*dsc*dp*ic*isc*ip* New: usb:v0545p800D d030a dc*dsc*dp*ic*isc*ip* Signed-off-by: Nathaniel McCallum Signed-off-by: Greg Kroah-Hartman --- scripts/mod/file2alias.c | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 62a9025cdcc7..e31f03aaf294 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -104,7 +104,7 @@ static void device_id_check(const char *modname, const char *device_id, static void do_usb_entry(struct usb_device_id *id, unsigned int bcdDevice_initial, int bcdDevice_initial_digits, unsigned char range_lo, unsigned char range_hi, - struct module *mod) + unsigned char max, struct module *mod) { char alias[500]; strcpy(alias, "usb:"); @@ -118,9 +118,22 @@ static void do_usb_entry(struct usb_device_id *id, sprintf(alias + strlen(alias), "%0*X", bcdDevice_initial_digits, bcdDevice_initial); if (range_lo == range_hi) - sprintf(alias + strlen(alias), "%u", range_lo); - else if (range_lo > 0 || range_hi < 9) - sprintf(alias + strlen(alias), "[%u-%u]", range_lo, range_hi); + sprintf(alias + strlen(alias), "%X", range_lo); + else if (range_lo > 0 || range_hi < max) { + if (range_lo > 0x9 || range_hi < 0xA) + sprintf(alias + strlen(alias), + "[%X-%X]", + range_lo, + range_hi); + else { + sprintf(alias + strlen(alias), + range_lo < 0x9 ? "[%X-9" : "[%X", + range_lo); + sprintf(alias + strlen(alias), + range_hi > 0xA ? "a-%X]" : "%X]", + range_lo); + } + } if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1)) strcat(alias, "*"); @@ -150,7 +163,7 @@ static void do_usb_entry(struct usb_device_id *id, static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod) { unsigned int devlo, devhi; - unsigned char chi, clo; + unsigned char chi, clo, max; int ndigits; id->match_flags = TO_NATIVE(id->match_flags); @@ -162,6 +175,17 @@ static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod) devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ? TO_NATIVE(id->bcdDevice_hi) : ~0x0U; + /* Figure out if this entry is in bcd or hex format */ + max = 0x9; /* Default to decimal format */ + for (ndigits = 0 ; ndigits < sizeof(id->bcdDevice_lo) * 2 ; ndigits++) { + clo = (devlo >> (ndigits << 2)) & 0xf; + chi = ((devhi > 0x9999 ? 0x9999 : devhi) >> (ndigits << 2)) & 0xf; + if (clo > max || chi > max) { + max = 0xf; + break; + } + } + /* * Some modules (visor) have empty slots as placeholder for * run-time specification that results in catch-all alias @@ -173,21 +197,21 @@ static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod) for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi; ndigits--) { clo = devlo & 0xf; chi = devhi & 0xf; - if (chi > 9) /* it's bcd not hex */ - chi = 9; + if (chi > max) /* If we are in bcd mode, truncate if necessary */ + chi = max; devlo >>= 4; devhi >>= 4; if (devlo == devhi || !ndigits) { - do_usb_entry(id, devlo, ndigits, clo, chi, mod); + do_usb_entry(id, devlo, ndigits, clo, chi, max, mod); break; } - if (clo > 0) - do_usb_entry(id, devlo++, ndigits, clo, 9, mod); + if (clo > 0x0) + do_usb_entry(id, devlo++, ndigits, clo, max, max, mod); - if (chi < 9) - do_usb_entry(id, devhi--, ndigits, 0, chi, mod); + if (chi < max) + do_usb_entry(id, devhi--, ndigits, 0x0, chi, max, mod); } } -- cgit v1.2.3 From 8e22605ac636189fe586a53589c9097a76014e91 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Wed, 18 Nov 2009 20:15:28 -0500 Subject: USB: handle bcd incrementation in usb modalias generation This patch fixes a bug when incrementing/decrementing on a BCD formatted integer (i.e. 0x09++ should be 0x10 not 0x0A). It just adds a function for incrementing/decrementing BCD integers by converting to decimal, doing the increment/decrement and then converting back to BCD. Signed-off-by: Nathaniel McCallum Signed-off-by: Greg Kroah-Hartman --- scripts/mod/file2alias.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index e31f03aaf294..6f426afbc522 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -160,6 +160,45 @@ static void do_usb_entry(struct usb_device_id *id, "MODULE_ALIAS(\"%s\");\n", alias); } +/* Handles increment/decrement of BCD formatted integers */ +/* Returns the previous value, so it works like i++ or i-- */ +static unsigned int incbcd(unsigned int *bcd, + int inc, + unsigned char max, + size_t chars) +{ + unsigned int init = *bcd, i, j; + unsigned long long c, dec = 0; + + /* If bcd is not in BCD format, just increment */ + if (max > 0x9) { + *bcd += inc; + return init; + } + + /* Convert BCD to Decimal */ + for (i=0 ; i < chars ; i++) { + c = (*bcd >> (i << 2)) & 0xf; + c = c > 9 ? 9 : c; /* force to bcd just in case */ + for (j=0 ; j < i ; j++) + c = c * 10; + dec += c; + } + + /* Do our increment/decrement */ + dec += inc; + *bcd = 0; + + /* Convert back to BCD */ + for (i=0 ; i < chars ; i++) { + for (c=1,j=0 ; j < i ; j++) + c = c * 10; + c = (dec / c) % 10; + *bcd += c << (i << 2); + } + return init; +} + static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod) { unsigned int devlo, devhi; @@ -208,10 +247,16 @@ static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod) } if (clo > 0x0) - do_usb_entry(id, devlo++, ndigits, clo, max, max, mod); + do_usb_entry(id, + incbcd(&devlo, 1, max, + sizeof(id->bcdDevice_lo) * 2), + ndigits, clo, max, max, mod); if (chi < max) - do_usb_entry(id, devhi--, ndigits, 0x0, chi, max, mod); + do_usb_entry(id, + incbcd(&devhi, -1, max, + sizeof(id->bcdDevice_lo) * 2), + ndigits, 0x0, chi, max, mod); } } -- cgit v1.2.3 From 9bed95629f9bafc26ac10f2b2d175609bf61b9f5 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 18 Nov 2009 14:12:31 +0100 Subject: USB: FIX bitfield istl_flip:1, make it unsigned. istl_flip is a signed bitfield of one bit so it can be -1 or 0. However in drivers/usb/host/isp1362-hcd.c:1103: finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]); So if isp1362_hcd->istl_flip is set, the 2nd argument becomes &isp1362_hcd->istl_queue[-1], which is invalid. Signed-off-by: Roel Kluin Acked-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index 1a253ebf7e50..5151516ea1de 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -534,8 +534,8 @@ struct isp1362_hcd { /* periodic schedule: isochronous */ struct list_head isoc; - int istl_flip:1; - int irq_active:1; + unsigned int istl_flip:1; + unsigned int irq_active:1; /* Schedules for the current frame */ struct isp1362_ep_queue atl_queue; -- cgit v1.2.3 From 081688f78a81e76d1e6ece39059eb244bc12c397 Mon Sep 17 00:00:00 2001 From: Russ Dill Date: Wed, 18 Nov 2009 11:02:13 -0700 Subject: USB: Close usb_find_interface race USB drivers that create character devices call usb_register_dev in their probe function. This associates the usb_interface device with that minor number and creates the character device and announces it to the world. However, the driver's probe function is called before the new usb_interface is added to the driver's klist_devices. This is a problem because userspace will respond to the character device creation announcement by opening the character device. The driver's open function will the call usb_find_interface to find the usb_interface associated with that minor number. usb_find_interface will walk the driver's list of devices and find the usb_interface with the matching minor number. Because the announcement happens before the usb_interface is added to the driver's klist_devices, a race condition exists. A straightforward fix is to walk the list of devices on usb_bus_type instead since the device is added to that list before the announcement occurs. bus_find_device calls get_device to bump the reference count on the found device. It is arguable that the reference count should be dropped by the caller of usb_find_interface instead of usb_find_interface, however, the current users of usb_find_interface do not expect this. Signed-off-by: Russ Dill Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b1b85abb9a2d..d1e9440799de 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -130,24 +130,17 @@ struct usb_host_interface *usb_altnum_to_altsetting( } EXPORT_SYMBOL_GPL(usb_altnum_to_altsetting); -struct find_interface_arg { - int minor; - struct usb_interface *interface; -}; - static int __find_interface(struct device *dev, void *data) { - struct find_interface_arg *arg = data; + int *minor = data; struct usb_interface *intf; if (!is_usb_interface(dev)) return 0; intf = to_usb_interface(dev); - if (intf->minor != -1 && intf->minor == arg->minor) { - arg->interface = intf; + if (intf->minor != -1 && intf->minor == *minor) return 1; - } return 0; } @@ -156,21 +149,20 @@ static int __find_interface(struct device *dev, void *data) * @drv: the driver whose current configuration is considered * @minor: the minor number of the desired device * - * This walks the driver device list and returns a pointer to the interface + * This walks the bus device list and returns a pointer to the interface * with the matching minor. Note, this only works for devices that share the * USB major number. */ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) { - struct find_interface_arg argb; - int retval; + struct device *dev; + + dev = bus_find_device(&usb_bus_type, NULL, &minor, __find_interface); + + /* Drop reference count from bus_find_device */ + put_device(dev); - argb.minor = minor; - argb.interface = NULL; - /* eat the error, it will be in argb.interface */ - retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb, - __find_interface); - return argb.interface; + return dev ? to_usb_interface(dev) : NULL; } EXPORT_SYMBOL_GPL(usb_find_interface); -- cgit v1.2.3 From 2ece55355069564a55666c433ce62fc1c390c63f Mon Sep 17 00:00:00 2001 From: "bart.hartgers@gmail.com" Date: Wed, 28 Oct 2009 10:43:25 +0100 Subject: USB: ark3116: Setup some basic infrastructure for new ark3116 driver. Signed-off-by: Bart Hartgers Cc: Mike McCormack Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 188 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 131e61adaaf7..5c947410c857 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -1,4 +1,6 @@ /* + * Copyright (C) 2009 by Bart Hartgers (bart.hartgers+ark3116@gmail.com) + * Original version: * Copyright (C) 2006 * Simon Schulz (ark3116_driver auctionant.de) * @@ -6,10 +8,13 @@ * - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547, * productid=0x0232) (used in a datacable called KQ-U8A) * - * - based on code by krisfx -> thanks !! - * (see http://www.linuxquestions.org/questions/showthread.php?p=2184457#post2184457) + * Supports full modem status lines, break, hardware flow control. Does not + * support software flow control, since I do not know how to enable it in hw. * - * - based on logs created by usbsnoopy + * This driver is a essentially new implementation. I initially dug + * into the old ark3116.c driver and suddenly realized the ark3116 is + * a 16450 with a USB interface glued to it. See comments at the + * bottom of this file. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -19,15 +24,31 @@ #include #include +#include #include +#include #include #include #include #include +#include #include - +#include +#include static int debug; +/* + * Version information + */ + +#define DRIVER_VERSION "v0.5" +#define DRIVER_AUTHOR "Bart Hartgers " +#define DRIVER_DESC "USB ARK3116 serial/IrDA driver" +#define DRIVER_DEV_DESC "ARK3116 RS232/IrDA" +#define DRIVER_NAME "ark3116" + +/* usb timeout of 1 second */ +#define ARK_TIMEOUT (1*HZ) static struct usb_device_id id_table [] = { { USB_DEVICE(0x6547, 0x0232) }, @@ -45,6 +66,53 @@ static int is_irda(struct usb_serial *serial) return 0; } +struct ark3116_private { + wait_queue_head_t delta_msr_wait; + struct async_icount icount; + int irda; /* 1 for irda device */ + + /* protects hw register updates */ + struct mutex hw_lock; + + int quot; /* baudrate divisor */ + __u32 lcr; /* line control register value */ + __u32 hcr; /* handshake control register (0x8) + * value */ + __u32 mcr; /* modem contol register value */ + + /* protects the status values below */ + spinlock_t status_lock; + __u32 msr; /* modem status register value */ + __u32 lsr; /* line status register value */ +}; + +static int ark3116_write_reg(struct usb_serial *serial, + unsigned reg, __u8 val) +{ + int result; + /* 0xfe 0x40 are magic values taken from original driver */ + result = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + 0xfe, 0x40, val, reg, + NULL, 0, ARK_TIMEOUT); + return result; +} + +static int ark3116_read_reg(struct usb_serial *serial, + unsigned reg, unsigned char *buf) +{ + int result; + /* 0xfe 0xc0 are magic values taken from original driver */ + result = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 0xfe, 0xc0, 0, reg, + buf, 1, ARK_TIMEOUT); + if (result < 0) + return result; + else + return buf[0]; +} + static inline void ARK3116_SND(struct usb_serial *serial, int seq, __u8 request, __u8 requesttype, __u16 value, __u16 index) @@ -465,7 +533,12 @@ static int __init ark3116_init(void) if (retval) return retval; retval = usb_register(&ark3116_driver); - if (retval) + if (retval == 0) { + printk(KERN_INFO "%s:" + DRIVER_VERSION ":" + DRIVER_DESC "\n", + KBUILD_MODNAME); + } else usb_serial_deregister(&ark3116_device); return retval; } @@ -480,6 +553,109 @@ module_init(ark3116_init); module_exit(ark3116_exit); MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); + module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled or not"); +MODULE_PARM_DESC(debug, "Enable debug"); +/* + * The following describes what I learned from studying the old + * ark3116.c driver, disassembling the windows driver, and some lucky + * guesses. Since I do not have any datasheet or other + * documentation, inaccuracies are almost guaranteed. + * + * Some specs for the ARK3116 can be found here: + * http://web.archive.org/web/20060318000438/ + * www.arkmicro.com/en/products/view.php?id=10 + * On that page, 2 GPIO pins are mentioned: I assume these are the + * OUT1 and OUT2 pins of the UART, so I added support for those + * through the MCR. Since the pins are not available on my hardware, + * I could not verify this. + * Also, it states there is "on-chip hardware flow control". I have + * discovered how to enable that. Unfortunately, I do not know how to + * enable XON/XOFF (software) flow control, which would need support + * from the chip as well to work. Because of the wording on the web + * page there is a real possibility the chip simply does not support + * software flow control. + * + * I got my ark3116 as part of a mobile phone adapter cable. On the + * PCB, the following numbered contacts are present: + * + * 1:- +5V + * 2:o DTR + * 3:i RX + * 4:i DCD + * 5:o RTS + * 6:o TX + * 7:i RI + * 8:i DSR + * 10:- 0V + * 11:i CTS + * + * On my chip, all signals seem to be 3.3V, but 5V tolerant. But that + * may be different for the one you have ;-). + * + * The windows driver limits the registers to 0-F, so I assume there + * are actually 16 present on the device. + * + * On an UART interrupt, 4 bytes of data come in on the interrupt + * endpoint. The bytes are 0xe8 IIR LSR MSR. + * + * The baudrate seems to be generated from the 12MHz crystal, using + * 4-times subsampling. So quot=12e6/(4*baud). Also see description + * of register E. + * + * Registers 0-7: + * These seem to be the same as for a regular 16450. The FCR is set + * to UART_FCR_DMA_SELECT (0x8), I guess to enable transfers between + * the UART and the USB bridge/DMA engine. + * + * Register 8: + * By trial and error, I found out that bit 0 enables hardware CTS, + * stopping TX when CTS is +5V. Bit 1 does the same for RTS, making + * RTS +5V when the 3116 cannot transfer the data to the USB bus + * (verified by disabling the reading URB). Note that as far as I can + * tell, the windows driver does NOT use this, so there might be some + * hardware bug or something. + * + * According to a patch provided here + * (http://lkml.org/lkml/2009/7/26/56), the ARK3116 can also be used + * as an IrDA dongle. Since I do not have such a thing, I could not + * investigate that aspect. However, I can speculate ;-). + * + * - IrDA encodes data differently than RS232. Most likely, one of + * the bits in registers 9..E enables the IR ENDEC (encoder/decoder). + * - Depending on the IR transceiver, the input and output need to be + * inverted, so there are probably bits for that as well. + * - IrDA is half-duplex, so there should be a bit for selecting that. + * + * This still leaves at least two registers unaccounted for. Perhaps + * The chip can do XON/XOFF or CRC in HW? + * + * Register 9: + * Set to 0x00 for IrDA, when the baudrate is initialised. + * + * Register A: + * Set to 0x01 for IrDA, at init. + * + * Register B: + * Set to 0x01 for IrDA, 0x00 for RS232, at init. + * + * Register C: + * Set to 00 for IrDA, at init. + * + * Register D: + * Set to 0x41 for IrDA, at init. + * + * Register E: + * Somekind of baudrate override. The windows driver seems to set + * this to 0x00 for normal baudrates, 0x01 for 460800, 0x02 for 921600. + * Since 460800 and 921600 cannot be obtained by dividing 3MHz by an integer, + * it could be somekind of subdivisor thingy. + * However,it does not seem to do anything: selecting 921600 (divisor 3, + * reg E=2), still gets 1 MHz. I also checked if registers 9, C or F would + * work, but they don't. + * + * Register F: unknown + */ -- cgit v1.2.3 From a5ea6ae9eb45e5cd15a1c79a2560af54f3b3fe1f Mon Sep 17 00:00:00 2001 From: "bart.hartgers@gmail.com" Date: Wed, 28 Oct 2009 10:43:26 +0100 Subject: USB: ark3116: Make existing functions 16450-aware and add close and release functions. Signed-off-by: Bart Hartgers Cc: Mike McCormack Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 495 ++++++++++++++++++++++++------------------- 1 file changed, 276 insertions(+), 219 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 5c947410c857..d12df9484677 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -152,79 +152,105 @@ static inline void ARK3116_RCV_QUIET(struct usb_serial *serial, buf, 0x0000001, 1000); } +static inline int calc_divisor(int bps) +{ + /* Original ark3116 made some exceptions in rounding here + * because windows did the same. Assume that is not really + * necessary. + * Crystal is 12MHz, probably because of USB, but we divide by 4? + */ + return (12000000 + 2*bps) / (4*bps); +} + static int ark3116_attach(struct usb_serial *serial) { - char *buf; + struct usb_serial_port *port = serial->port[0]; + struct ark3116_private *priv; + + /* make sure we have our end-points */ + if ((serial->num_bulk_in == 0) || + (serial->num_bulk_out == 0) || + (serial->num_interrupt_in == 0)) { + dev_err(&serial->dev->dev, + "%s - missing endpoint - " + "bulk in: %d, bulk out: %d, int in %d\n", + KBUILD_MODNAME, + serial->num_bulk_in, + serial->num_bulk_out, + serial->num_interrupt_in); + return -EINVAL; + } - buf = kmalloc(1, GFP_KERNEL); - if (!buf) { - dbg("error kmalloc -> out of mem?"); + priv = kzalloc(sizeof(struct ark3116_private), + GFP_KERNEL); + if (!priv) return -ENOMEM; - } - if (is_irda(serial)) - dbg("IrDA mode"); + init_waitqueue_head(&priv->delta_msr_wait); + mutex_init(&priv->hw_lock); + spin_lock_init(&priv->status_lock); + + priv->irda = is_irda(serial); + + usb_set_serial_port_data(port, priv); - /* 3 */ - ARK3116_SND(serial, 3, 0xFE, 0x40, 0x0008, 0x0002); - ARK3116_SND(serial, 4, 0xFE, 0x40, 0x0008, 0x0001); - ARK3116_SND(serial, 5, 0xFE, 0x40, 0x0000, 0x0008); - ARK3116_SND(serial, 6, 0xFE, 0x40, is_irda(serial) ? 0x0001 : 0x0000, - 0x000B); + /* setup the hardware */ + ark3116_write_reg(serial, UART_IER, 0); + /* disable DMA */ + ark3116_write_reg(serial, UART_FCR, 0); + /* handshake control */ + priv->hcr = 0; + ark3116_write_reg(serial, 0x8 , 0); + /* modem control */ + priv->mcr = 0; + ark3116_write_reg(serial, UART_MCR, 0); - if (is_irda(serial)) { - ARK3116_SND(serial, 1001, 0xFE, 0x40, 0x0000, 0x000C); - ARK3116_SND(serial, 1002, 0xFE, 0x40, 0x0041, 0x000D); - ARK3116_SND(serial, 1003, 0xFE, 0x40, 0x0001, 0x000A); + if (!(priv->irda)) { + ark3116_write_reg(serial, 0xb , 0); + } else { + ark3116_write_reg(serial, 0xb , 1); + ark3116_write_reg(serial, 0xc , 0); + ark3116_write_reg(serial, 0xd , 0x41); + ark3116_write_reg(serial, 0xa , 1); } - /* <-- seq7 */ - ARK3116_RCV(serial, 7, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf); - ARK3116_SND(serial, 8, 0xFE, 0x40, 0x0080, 0x0003); - ARK3116_SND(serial, 9, 0xFE, 0x40, 0x001A, 0x0000); - ARK3116_SND(serial, 10, 0xFE, 0x40, 0x0000, 0x0001); - ARK3116_SND(serial, 11, 0xFE, 0x40, 0x0000, 0x0003); - - /* <-- seq12 */ - ARK3116_RCV(serial, 12, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf); - ARK3116_SND(serial, 13, 0xFE, 0x40, 0x0000, 0x0004); - - /* 14 */ - ARK3116_RCV(serial, 14, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf); - ARK3116_SND(serial, 15, 0xFE, 0x40, 0x0000, 0x0004); - - /* 16 */ - ARK3116_RCV(serial, 16, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf); - /* --> seq17 */ - ARK3116_SND(serial, 17, 0xFE, 0x40, 0x0001, 0x0004); - - /* <-- seq18 */ - ARK3116_RCV(serial, 18, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf); - - /* --> seq19 */ - ARK3116_SND(serial, 19, 0xFE, 0x40, 0x0003, 0x0004); - - /* <-- seq20 */ - /* seems like serial port status info (RTS, CTS, ...) */ - /* returns modem control line status?! */ - ARK3116_RCV(serial, 20, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf); - - /* set 9600 baud & do some init?! */ - ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003); - ARK3116_SND(serial, 148, 0xFE, 0x40, 0x0038, 0x0000); - ARK3116_SND(serial, 149, 0xFE, 0x40, 0x0001, 0x0001); - if (is_irda(serial)) - ARK3116_SND(serial, 1004, 0xFE, 0x40, 0x0000, 0x0009); - ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003); - ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf); - ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003); - ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf); - ARK3116_SND(serial, 154, 0xFE, 0x40, 0x0003, 0x0003); + /* setup baudrate */ + ark3116_write_reg(serial, UART_LCR, UART_LCR_DLAB); - kfree(buf); + /* setup for 9600 8N1 */ + priv->quot = calc_divisor(9600); + ark3116_write_reg(serial, UART_DLL, priv->quot & 0xff); + ark3116_write_reg(serial, UART_DLM, (priv->quot>>8) & 0xff); + + priv->lcr = UART_LCR_WLEN8; + ark3116_write_reg(serial, UART_LCR, UART_LCR_WLEN8); + + ark3116_write_reg(serial, 0xe, 0); + + if (priv->irda) + ark3116_write_reg(serial, 0x9, 0); + + dev_info(&serial->dev->dev, + "%s using %s mode\n", + KBUILD_MODNAME, + priv->irda ? "IrDA" : "RS232"); return 0; } +static void ark3116_release(struct usb_serial *serial) +{ + struct usb_serial_port *port = serial->port[0]; + struct ark3116_private *priv = usb_get_serial_port_data(port); + + /* device is closed, so URBs and DMA should be down */ + + usb_set_serial_port_data(port, NULL); + + mutex_destroy(&priv->hw_lock); + + kfree(priv); +} + static void ark3116_init_termios(struct tty_struct *tty) { struct ktermios *termios = tty->termios; @@ -240,200 +266,189 @@ static void ark3116_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; + struct ark3116_private *priv = usb_get_serial_port_data(port); struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; - int baud; - int ark3116_baud; - char *buf; - char config; - - config = 0; + int bps = tty_get_baud_rate(tty); + int quot; + __u8 lcr, hcr, eval; + + /* set data bit count */ + switch (cflag & CSIZE) { + case CS5: + lcr = UART_LCR_WLEN5; + break; + case CS6: + lcr = UART_LCR_WLEN6; + break; + case CS7: + lcr = UART_LCR_WLEN7; + break; + default: + case CS8: + lcr = UART_LCR_WLEN8; + break; + } + if (cflag & CSTOPB) + lcr |= UART_LCR_STOP; + if (cflag & PARENB) + lcr |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + lcr |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + lcr |= UART_LCR_SPAR; +#endif + /* handshake control */ + hcr = (cflag & CRTSCTS) ? 0x03 : 0x00; + + /* calc baudrate */ + dbg("%s - setting bps to %d", __func__, bps); + eval = 0; + switch (bps) { + case 0: + quot = calc_divisor(9600); + break; + default: + if ((bps < 75) || (bps > 3000000)) + bps = 9600; + quot = calc_divisor(bps); + break; + case 460800: + eval = 1; + quot = calc_divisor(bps); + break; + case 921600: + eval = 2; + quot = calc_divisor(bps); + break; + } - dbg("%s - port %d", __func__, port->number); + /* Update state: synchronize */ + mutex_lock(&priv->hw_lock); + /* keep old LCR_SBC bit */ + lcr |= (priv->lcr & UART_LCR_SBC); - cflag = termios->c_cflag; - termios->c_cflag &= ~(CMSPAR|CRTSCTS); + dbg("%s - setting hcr:0x%02x,lcr:0x%02x,quot:%d", + __func__, hcr, lcr, quot); - buf = kmalloc(1, GFP_KERNEL); - if (!buf) { - dbg("error kmalloc"); - *termios = *old_termios; - return; + /* handshake control */ + if (priv->hcr != hcr) { + priv->hcr = hcr; + ark3116_write_reg(serial, 0x8, hcr); } - /* set data bit count (8/7/6/5) */ - if (cflag & CSIZE) { - switch (cflag & CSIZE) { - case CS5: - config |= 0x00; - dbg("setting CS5"); - break; - case CS6: - config |= 0x01; - dbg("setting CS6"); - break; - case CS7: - config |= 0x02; - dbg("setting CS7"); - break; - default: - dbg("CSIZE was set but not CS5-CS8, using CS8!"); - /* fall through */ - case CS8: - config |= 0x03; - dbg("setting CS8"); - break; - } + /* baudrate */ + if (priv->quot != quot) { + priv->quot = quot; + priv->lcr = lcr; /* need to write lcr anyway */ + + /* disable DMA since transmit/receive is + * shadowed by UART_DLL + */ + ark3116_write_reg(serial, UART_FCR, 0); + + ark3116_write_reg(serial, UART_LCR, + lcr|UART_LCR_DLAB); + ark3116_write_reg(serial, UART_DLL, quot & 0xff); + ark3116_write_reg(serial, UART_DLM, (quot>>8) & 0xff); + + /* restore lcr */ + ark3116_write_reg(serial, UART_LCR, lcr); + /* magic baudrate thingy: not sure what it does, + * but windows does this as well. + */ + ark3116_write_reg(serial, 0xe, eval); + + /* enable DMA */ + ark3116_write_reg(serial, UART_FCR, UART_FCR_DMA_SELECT); + } else if (priv->lcr != lcr) { + priv->lcr = lcr; + ark3116_write_reg(serial, UART_LCR, lcr); } - /* set parity (NONE/EVEN/ODD) */ - if (cflag & PARENB) { - if (cflag & PARODD) { - config |= 0x08; - dbg("setting parity to ODD"); - } else { - config |= 0x18; - dbg("setting parity to EVEN"); - } - } else { - dbg("setting parity to NONE"); - } + mutex_unlock(&priv->hw_lock); - /* set stop bit (1/2) */ - if (cflag & CSTOPB) { - config |= 0x04; - dbg("setting 2 stop bits"); - } else { - dbg("setting 1 stop bit"); + /* check for software flow control */ + if (I_IXOFF(tty) || I_IXON(tty)) { + dev_warn(&serial->dev->dev, + "%s: don't know how to do software flow control\n", + KBUILD_MODNAME); } - /* set baudrate */ - baud = tty_get_baud_rate(tty); - - switch (baud) { - case 75: - case 150: - case 300: - case 600: - case 1200: - case 1800: - case 2400: - case 4800: - case 9600: - case 19200: - case 38400: - case 57600: - case 115200: - case 230400: - case 460800: - /* Report the resulting rate back to the caller */ - tty_encode_baud_rate(tty, baud, baud); - break; - /* set 9600 as default (if given baudrate is invalid for example) */ - default: - tty_encode_baud_rate(tty, 9600, 9600); - case 0: - baud = 9600; - } - - /* - * found by try'n'error, be careful, maybe there are other options - * for multiplicator etc! (3.5 for example) - */ - if (baud == 460800) - /* strange, for 460800 the formula is wrong - * if using round() then 9600baud is wrong) */ - ark3116_baud = 7; - else - ark3116_baud = 3000000 / baud; - - /* ? */ - ARK3116_RCV(serial, 0, 0xFE, 0xC0, 0x0000, 0x0003, 0x03, buf); - - /* offset = buf[0]; */ - /* offset = 0x03; */ - /* dbg("using 0x%04X as target for 0x0003:", 0x0080 + offset); */ - - /* set baudrate */ - dbg("setting baudrate to %d (->reg=%d)", baud, ark3116_baud); - ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003); - ARK3116_SND(serial, 148, 0xFE, 0x40, - (ark3116_baud & 0x00FF), 0x0000); - ARK3116_SND(serial, 149, 0xFE, 0x40, - (ark3116_baud & 0xFF00) >> 8, 0x0001); - ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003); - - /* ? */ - ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf); - ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003); - - /* set data bit count, stop bit count & parity: */ - dbg("updating bit count, stop bit or parity (cfg=0x%02X)", config); - ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf); - ARK3116_SND(serial, 154, 0xFE, 0x40, config, 0x0003); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, bps, bps); +} - if (cflag & CRTSCTS) - dbg("CRTSCTS not supported by chipset?!"); +static void ark3116_close(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; - /* TEST ARK3116_SND(154, 0xFE, 0x40, 0xFFFF, 0x0006); */ + if (serial->dev) { + /* disable DMA */ + ark3116_write_reg(serial, UART_FCR, 0); - kfree(buf); + /* deactivate interrupts */ + ark3116_write_reg(serial, UART_IER, 0); - return; + /* shutdown any bulk reads that might be going on */ + if (serial->num_bulk_out) + usb_kill_urb(port->write_urb); + if (serial->num_bulk_in) + usb_kill_urb(port->read_urb); + if (serial->num_interrupt_in) + usb_kill_urb(port->interrupt_in_urb); + } } static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct ktermios tmp_termios; + struct ark3116_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; - char *buf; - int result = 0; - - dbg("%s - port %d", __func__, port->number); + unsigned char *buf; + int result; buf = kmalloc(1, GFP_KERNEL); - if (!buf) { - dbg("error kmalloc -> out of mem?"); + if (buf == NULL) return -ENOMEM; - } result = usb_serial_generic_open(tty, port); - if (result) + if (result) { + dbg("%s - usb_serial_generic_open failed: %d", + __func__, result); goto err_out; + } - /* open */ - ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf); - - ARK3116_SND(serial, 112, 0xFE, 0x40, 0x0082, 0x0003); - ARK3116_SND(serial, 113, 0xFE, 0x40, 0x001A, 0x0000); - ARK3116_SND(serial, 114, 0xFE, 0x40, 0x0000, 0x0001); - ARK3116_SND(serial, 115, 0xFE, 0x40, 0x0002, 0x0003); - - ARK3116_RCV(serial, 116, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf); - ARK3116_SND(serial, 117, 0xFE, 0x40, 0x0002, 0x0004); - - ARK3116_RCV(serial, 118, 0xFE, 0xC0, 0x0000, 0x0004, 0x02, buf); - ARK3116_SND(serial, 119, 0xFE, 0x40, 0x0000, 0x0004); - - ARK3116_RCV(serial, 120, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf); + /* setup termios */ + if (tty) + ark3116_set_termios(tty, port, NULL); - ARK3116_SND(serial, 121, 0xFE, 0x40, 0x0001, 0x0004); + /* remove any data still left: also clears error state */ + ark3116_read_reg(serial, UART_RX, buf); - ARK3116_RCV(serial, 122, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf); + /* read modem status */ + priv->msr = ark3116_read_reg(serial, UART_MSR, buf); + /* read line status */ + priv->lsr = ark3116_read_reg(serial, UART_LSR, buf); - ARK3116_SND(serial, 123, 0xFE, 0x40, 0x0003, 0x0004); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (result) { + dev_err(&port->dev, "submit irq_in urb failed %d\n", + result); + ark3116_close(port); + goto err_out; + } - /* returns different values (control lines?!) */ - ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf); + /* activate interrupts */ + ark3116_write_reg(port->serial, UART_IER, UART_IER_MSI|UART_IER_RLSI); - /* initialise termios */ - if (tty) - ark3116_set_termios(tty, port, &tmp_termios); + /* enable DMA */ + ark3116_write_reg(port->serial, UART_FCR, UART_FCR_DMA_SELECT); err_out: kfree(buf); - return result; } @@ -441,6 +456,7 @@ static int ark3116_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; + struct ark3116_private *priv = usb_get_serial_port_data(port); struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; @@ -462,9 +478,48 @@ static int ark3116_ioctl(struct tty_struct *tty, struct file *file, if (copy_from_user(&serstruct, user_arg, sizeof(serstruct))) return -EFAULT; return 0; - default: - dbg("%s cmd 0x%04x not supported", __func__, cmd); + case TIOCMIWAIT: + for (;;) { + struct async_icount prev = priv->icount; + interruptible_sleep_on(&priv->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + if ((prev.rng == priv->icount.rng) && + (prev.dsr == priv->icount.dsr) && + (prev.dcd == priv->icount.dcd) && + (prev.cts == priv->icount.cts)) + return -EIO; + if ((arg & TIOCM_RNG && + (prev.rng != priv->icount.rng)) || + (arg & TIOCM_DSR && + (prev.dsr != priv->icount.dsr)) || + (arg & TIOCM_CD && + (prev.dcd != priv->icount.dcd)) || + (arg & TIOCM_CTS && + (prev.cts != priv->icount.cts))) + return 0; + } break; + case TIOCGICOUNT: { + struct serial_icounter_struct icount; + struct async_icount cnow = priv->icount; + memset(&icount, 0, sizeof(icount)); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + if (copy_to_user(user_arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + } } return -ENOIOCTLCMD; @@ -518,11 +573,13 @@ static struct usb_serial_driver ark3116_device = { .usb_driver = &ark3116_driver, .num_ports = 1, .attach = ark3116_attach, + .release = ark3116_release, .set_termios = ark3116_set_termios, .init_termios = ark3116_init_termios, .ioctl = ark3116_ioctl, .tiocmget = ark3116_tiocmget, .open = ark3116_open, + .close = ark3116_close, }; static int __init ark3116_init(void) -- cgit v1.2.3 From 1d4961ae56e1b942d2131335a50f5fc1d89b4646 Mon Sep 17 00:00:00 2001 From: "bart.hartgers@gmail.com" Date: Wed, 28 Oct 2009 10:43:27 +0100 Subject: USB: ark3116: Replace cmget Signed-off-by: Bart Hartgers Cc: Mike McCormack Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index d12df9484677..d8f0267a2d56 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -528,32 +528,27 @@ static int ark3116_ioctl(struct tty_struct *tty, struct file *file, static int ark3116_tiocmget(struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; - char *buf; - char temp; - - /* seems like serial port status info (RTS, CTS, ...) is stored - * in reg(?) 0x0006 - * pcb connection point 11 = GND -> sets bit4 of response - * pcb connection point 7 = GND -> sets bit6 of response - */ - - buf = kmalloc(1, GFP_KERNEL); - if (!buf) { - dbg("error kmalloc"); - return -ENOMEM; - } + struct ark3116_private *priv = usb_get_serial_port_data(port); + __u32 status; + __u32 ctrl; + unsigned long flags; - /* read register */ - ARK3116_RCV_QUIET(serial, 0xFE, 0xC0, 0x0000, 0x0006, buf); - temp = buf[0]; - kfree(buf); + mutex_lock(&priv->hw_lock); + ctrl = priv->mcr; + mutex_unlock(&priv->hw_lock); - /* i do not really know if bit4=CTS and bit6=DSR... just a - * quick guess! - */ - return (temp & (1<<4) ? TIOCM_CTS : 0) - | (temp & (1<<6) ? TIOCM_DSR : 0); + spin_lock_irqsave(&priv->status_lock, flags); + status = priv->msr; + spin_unlock_irqrestore(&priv->status_lock, flags); + + return (status & UART_MSR_DSR ? TIOCM_DSR : 0) | + (status & UART_MSR_CTS ? TIOCM_CTS : 0) | + (status & UART_MSR_RI ? TIOCM_RI : 0) | + (status & UART_MSR_DCD ? TIOCM_CD : 0) | + (ctrl & UART_MCR_DTR ? TIOCM_DTR : 0) | + (ctrl & UART_MCR_RTS ? TIOCM_RTS : 0) | + (ctrl & UART_MCR_OUT1 ? TIOCM_OUT1 : 0) | + (ctrl & UART_MCR_OUT2 ? TIOCM_OUT2 : 0); } static struct usb_driver ark3116_driver = { -- cgit v1.2.3 From fce84ab556888ab723044a409a0165df0699e1d5 Mon Sep 17 00:00:00 2001 From: "bart.hartgers@gmail.com" Date: Wed, 28 Oct 2009 10:43:28 +0100 Subject: USB: ark3116: Add cmset and break Signed-off-by: Bart Hartgers Cc: Mike McCormack Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index d8f0267a2d56..a7c26990ec67 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -551,6 +551,60 @@ static int ark3116_tiocmget(struct tty_struct *tty, struct file *file) (ctrl & UART_MCR_OUT2 ? TIOCM_OUT2 : 0); } +static int ark3116_tiocmset(struct tty_struct *tty, struct file *file, + unsigned set, unsigned clr) +{ + struct usb_serial_port *port = tty->driver_data; + struct ark3116_private *priv = usb_get_serial_port_data(port); + + /* we need to take the mutex here, to make sure that the value + * in priv->mcr is actually the one that is in the hardware + */ + + mutex_lock(&priv->hw_lock); + + if (set & TIOCM_RTS) + priv->mcr |= UART_MCR_RTS; + if (set & TIOCM_DTR) + priv->mcr |= UART_MCR_DTR; + if (set & TIOCM_OUT1) + priv->mcr |= UART_MCR_OUT1; + if (set & TIOCM_OUT2) + priv->mcr |= UART_MCR_OUT2; + if (clr & TIOCM_RTS) + priv->mcr &= ~UART_MCR_RTS; + if (clr & TIOCM_DTR) + priv->mcr &= ~UART_MCR_DTR; + if (clr & TIOCM_OUT1) + priv->mcr &= ~UART_MCR_OUT1; + if (clr & TIOCM_OUT2) + priv->mcr &= ~UART_MCR_OUT2; + + ark3116_write_reg(port->serial, UART_MCR, priv->mcr); + + mutex_unlock(&priv->hw_lock); + + return 0; +} + +static void ark3116_break_ctl(struct tty_struct *tty, int break_state) +{ + struct usb_serial_port *port = tty->driver_data; + struct ark3116_private *priv = usb_get_serial_port_data(port); + + /* LCR is also used for other things: protect access */ + mutex_lock(&priv->hw_lock); + + if (break_state) + priv->lcr |= UART_LCR_SBC; + else + priv->lcr &= ~UART_LCR_SBC; + + ark3116_write_reg(port->serial, UART_LCR, priv->lcr); + + mutex_unlock(&priv->hw_lock); +} + static struct usb_driver ark3116_driver = { .name = "ark3116", .probe = usb_serial_probe, @@ -573,8 +627,10 @@ static struct usb_serial_driver ark3116_device = { .init_termios = ark3116_init_termios, .ioctl = ark3116_ioctl, .tiocmget = ark3116_tiocmget, + .tiocmset = ark3116_tiocmset, .open = ark3116_open, .close = ark3116_close, + .break_ctl = ark3116_break_ctl, }; static int __init ark3116_init(void) -- cgit v1.2.3 From 8a45aef633ad631165d2616cf3f9c99a65432a98 Mon Sep 17 00:00:00 2001 From: "bart.hartgers@gmail.com" Date: Wed, 28 Oct 2009 10:43:29 +0100 Subject: USB: ark3116: Callbacks for interrupt and bulk read Signed-off-by: Bart Hartgers Cc: Mike McCormack Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 194 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index a7c26990ec67..067daf4db646 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -605,6 +605,198 @@ static void ark3116_break_ctl(struct tty_struct *tty, int break_state) mutex_unlock(&priv->hw_lock); } +static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr) +{ + struct ark3116_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + spin_lock_irqsave(&priv->status_lock, flags); + priv->msr = msr; + spin_unlock_irqrestore(&priv->status_lock, flags); + + if (msr & UART_MSR_ANY_DELTA) { + /* update input line counters */ + if (msr & UART_MSR_DCTS) + priv->icount.cts++; + if (msr & UART_MSR_DDSR) + priv->icount.dsr++; + if (msr & UART_MSR_DDCD) + priv->icount.dcd++; + if (msr & UART_MSR_TERI) + priv->icount.rng++; + wake_up_interruptible(&priv->delta_msr_wait); + } +} + +static void ark3116_update_lsr(struct usb_serial_port *port, __u8 lsr) +{ + struct ark3116_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + spin_lock_irqsave(&priv->status_lock, flags); + /* combine bits */ + priv->lsr |= lsr; + spin_unlock_irqrestore(&priv->status_lock, flags); + + if (lsr&UART_LSR_BRK_ERROR_BITS) { + if (lsr & UART_LSR_BI) + priv->icount.brk++; + if (lsr & UART_LSR_FE) + priv->icount.frame++; + if (lsr & UART_LSR_PE) + priv->icount.parity++; + if (lsr & UART_LSR_OE) + priv->icount.overrun++; + } +} + +static void ark3116_read_int_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + int status = urb->status; + const __u8 *data = urb->transfer_buffer; + int result; + + switch (status) { + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __func__, status); + break; + case 0: /* success */ + /* discovered this by trail and error... */ + if ((urb->actual_length == 4) && (data[0] == 0xe8)) { + const __u8 id = data[1]&UART_IIR_ID; + dbg("%s: iir=%02x", __func__, data[1]); + if (id == UART_IIR_MSI) { + dbg("%s: msr=%02x", __func__, data[3]); + ark3116_update_msr(port, data[3]); + break; + } else if (id == UART_IIR_RLSI) { + dbg("%s: lsr=%02x", __func__, data[2]); + ark3116_update_lsr(port, data[2]); + break; + } + } + /* + * Not sure what this data meant... + */ + usb_serial_debug_data(debug, &port->dev, + __func__, + urb->actual_length, + urb->transfer_buffer); + break; + } + + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&urb->dev->dev, + "%s - Error %d submitting interrupt urb\n", + __func__, result); +} + + +/* Data comes in via the bulk (data) URB, erors/interrupts via the int URB. + * This means that we cannot be sure which data byte has an associated error + * condition, so we report an error for all data in the next bulk read. + * + * Actually, there might even be a window between the bulk data leaving the + * ark and reading/resetting the lsr in the read_bulk_callback where an + * interrupt for the next data block could come in. + * Without somekind of ordering on the ark, we would have to report the + * error for the next block of data as well... + * For now, let's pretend this can't happen. + */ + +static void send_to_tty(struct tty_struct *tty, + const unsigned char *chars, + size_t size, char flag) +{ + if (size == 0) + return; + if (flag == TTY_NORMAL) { + tty_insert_flip_string(tty, chars, size); + } else { + int i; + for (i = 0; i < size; ++i) + tty_insert_flip_char(tty, chars[i], flag); + } +} + +static void ark3116_read_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct ark3116_private *priv = usb_get_serial_port_data(port); + const __u8 *data = urb->transfer_buffer; + int status = urb->status; + struct tty_struct *tty; + unsigned long flags; + int result; + char flag; + __u32 lsr; + + switch (status) { + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __func__, status); + break; + case 0: /* success */ + + spin_lock_irqsave(&priv->status_lock, flags); + lsr = priv->lsr; + /* clear error bits */ + priv->lsr &= ~UART_LSR_BRK_ERROR_BITS; + spin_unlock_irqrestore(&priv->status_lock, flags); + + if (unlikely(lsr & UART_LSR_BI)) + flag = TTY_BREAK; + else if (unlikely(lsr & UART_LSR_PE)) + flag = TTY_PARITY; + else if (unlikely(lsr & UART_LSR_FE)) + flag = TTY_FRAME; + else + flag = TTY_NORMAL; + + tty = tty_port_tty_get(&port->port); + if (tty) { + tty_buffer_request_room(tty, urb->actual_length + 1); + /* overrun is special, not associated with a char */ + if (unlikely(lsr & UART_LSR_OE)) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + send_to_tty(tty, data, urb->actual_length, flag); + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + + /* Throttle the device if requested by tty */ + spin_lock_irqsave(&port->lock, flags); + port->throttled = port->throttle_req; + if (port->throttled) { + spin_unlock_irqrestore(&port->lock, flags); + return; + } else + spin_unlock_irqrestore(&port->lock, flags); + } + /* Continue reading from device */ + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&urb->dev->dev, "%s - failed resubmitting" + " read urb, error %d\n", __func__, result); +} + static struct usb_driver ark3116_driver = { .name = "ark3116", .probe = usb_serial_probe, @@ -631,6 +823,8 @@ static struct usb_serial_driver ark3116_device = { .open = ark3116_open, .close = ark3116_close, .break_ctl = ark3116_break_ctl, + .read_int_callback = ark3116_read_int_callback, + .read_bulk_callback = ark3116_read_bulk_callback, }; static int __init ark3116_init(void) -- cgit v1.2.3 From b42a7c20eb205015bdbdce617e4cd16a56cab08b Mon Sep 17 00:00:00 2001 From: "bart.hartgers@gmail.com" Date: Wed, 28 Oct 2009 10:43:30 +0100 Subject: USB: ark3116: Cleanup of now unneeded functions Signed-off-by: Bart Hartgers Cc: Mike McCormack Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 067daf4db646..a9c2dec8e3fb 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -113,45 +113,6 @@ static int ark3116_read_reg(struct usb_serial *serial, return buf[0]; } -static inline void ARK3116_SND(struct usb_serial *serial, int seq, - __u8 request, __u8 requesttype, - __u16 value, __u16 index) -{ - int result; - result = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - request, requesttype, value, index, - NULL, 0x00, 1000); - dbg("%03d > ok", seq); -} - -static inline void ARK3116_RCV(struct usb_serial *serial, int seq, - __u8 request, __u8 requesttype, - __u16 value, __u16 index, __u8 expected, - char *buf) -{ - int result; - result = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - request, requesttype, value, index, - buf, 0x0000001, 1000); - if (result) - dbg("%03d < %d bytes [0x%02X]", seq, result, - ((unsigned char *)buf)[0]); - else - dbg("%03d < 0 bytes", seq); -} - -static inline void ARK3116_RCV_QUIET(struct usb_serial *serial, - __u8 request, __u8 requesttype, - __u16 value, __u16 index, char *buf) -{ - usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - request, requesttype, value, index, - buf, 0x0000001, 1000); -} - static inline int calc_divisor(int bps) { /* Original ark3116 made some exceptions in rounding here -- cgit v1.2.3 From 46647e78548f35af1c4240505a662eb85beaad4e Mon Sep 17 00:00:00 2001 From: Zhang Le Date: Tue, 17 Nov 2009 14:53:42 -0800 Subject: USB: option.c: add support for D-Link DWM-162-U5 Add D-Link DWM-162-U5 device id 1e0e:ce16 into option driver. The device has 4 interfaces, of which 1 is handled by storage and the other 3 by option driver. The device appears first as CD-only 05c6:2100 device and must be switched to 1e0e:ce16 mode either by using "eject CD" or usb_modeswitch. The MessageContent for usb_modeswitch.conf is: "55534243e0c26a85000000000000061b000000020000000000000000000000" Signed-off-by: Zhang Le Signed-off-by: Andrew Morton Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 319aaf9725b3..82c3ac786726 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -595,6 +595,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_G450) }, { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */ { USB_DEVICE(ALINK_VENDOR_ID, 0x9000) }, + { USB_DEVICE(ALINK_VENDOR_ID, 0xce16) }, { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S) }, { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) }, -- cgit v1.2.3 From d7cc3e31f1af0e05d3a2ed624583882274994e8d Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 17 Nov 2009 14:53:41 -0800 Subject: USB: hcd.c: quiet NULL pointer sparse noise Quiet the following sparse noise: warning: Using plain integer as NULL pointer Signed-off-by: H Hartley Sweeten Cc: Alan Stern Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 026ab2fe5c2d..daac0427bfd5 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1607,7 +1607,7 @@ int usb_hcd_check_bandwidth(struct usb_device *udev, { int num_intfs, i, j; struct usb_interface_cache *intf_cache; - struct usb_host_interface *alt = 0; + struct usb_host_interface *alt = NULL; int ret = 0; struct usb_hcd *hcd; struct usb_host_endpoint *ep; -- cgit v1.2.3 From a85c653a3945350875611d3cb071a0143cfeb2d2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 13 Nov 2009 11:53:59 -0500 Subject: USB: remove the auto_pm flag This patch (as1302) removes the auto_pm flag from struct usb_device. The flag's only purpose was to distinguish between autosuspends and external suspends, but that information is now available in the pm_message_t argument passed to suspend methods. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/power-management.txt | 9 +++++---- drivers/bluetooth/btusb.c | 2 +- drivers/hid/usbhid/hid-core.c | 8 ++++---- drivers/net/wimax/i2400m/usb.c | 4 ++-- drivers/usb/core/driver.c | 4 ---- drivers/usb/serial/option.c | 2 +- drivers/usb/serial/sierra.c | 2 +- include/linux/usb.h | 2 -- 8 files changed, 14 insertions(+), 19 deletions(-) diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index ad642615ad4c..8817368203d6 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -423,15 +423,16 @@ an URB had completed too recently. External suspend calls should never be allowed to fail in this way, only autosuspend calls. The driver can tell them apart by checking -udev->auto_pm; this flag will be set to 1 for internal PM events -(autosuspend or autoresume) and 0 for external PM events. +the PM_EVENT_AUTO bit in the message.event argument to the suspend +method; this bit will be set for internal PM events (autosuspend) and +clear for external PM events. Many of the ingredients in the autosuspend framework are oriented towards interfaces: The usb_interface structure contains the pm_usage_cnt field, and the usb_autopm_* routines take an interface pointer as their argument. But somewhat confusingly, a few of the -pieces (usb_mark_last_busy() and udev->auto_pm) use the usb_device -structure instead. Drivers need to keep this straight; they can call +pieces (i.e., usb_mark_last_busy()) use the usb_device structure +instead. Drivers need to keep this straight; they can call interface_to_usbdev() to find the device structure for a given interface. diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 44bc8bbabf54..4d2905996751 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1066,7 +1066,7 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message) return 0; spin_lock_irq(&data->txlock); - if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) { + if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) { set_bit(BTUSB_SUSPENDING, &data->flags); spin_unlock_irq(&data->txlock); } else { diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 03bd703255a3..eedf38e895ef 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1254,10 +1254,9 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) { struct hid_device *hid = usb_get_intfdata(intf); struct usbhid_device *usbhid = hid->driver_data; - struct usb_device *udev = interface_to_usbdev(intf); int status; - if (udev->auto_pm) { + if (message.event & PM_EVENT_AUTO) { spin_lock_irq(&usbhid->lock); /* Sync with error handler */ if (!test_bit(HID_RESET_PENDING, &usbhid->iofl) && !test_bit(HID_CLEAR_HALT, &usbhid->iofl) @@ -1282,7 +1281,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) return -EIO; } - if (!ignoreled && udev->auto_pm) { + if (!ignoreled && (message.event & PM_EVENT_AUTO)) { spin_lock_irq(&usbhid->lock); if (test_bit(HID_LED_ON, &usbhid->iofl)) { spin_unlock_irq(&usbhid->lock); @@ -1295,7 +1294,8 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) hid_cancel_delayed_stuff(usbhid); hid_cease_io(usbhid); - if (udev->auto_pm && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) { + if ((message.event & PM_EVENT_AUTO) && + test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) { /* lost race against keypresses */ status = hid_start_in(hid); if (status < 0) diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 7eadd11c815b..0ab88e3faa4d 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c @@ -498,7 +498,7 @@ void i2400mu_disconnect(struct usb_interface *iface) * * As well, the device might refuse going to sleep for whichever * reason. In this case we just fail. For system suspend/hibernate, - * we *can't* fail. We look at usb_dev->auto_pm to see if the + * we *can't* fail. We check PM_EVENT_AUTO to see if the * suspend call comes from the USB stack or from the system and act * in consequence. * @@ -517,7 +517,7 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) struct i2400m *i2400m = &i2400mu->i2400m; #ifdef CONFIG_PM - if (usb_dev->auto_pm > 0) + if (pm_msg.event & PM_EVENT_AUTO) is_autosuspend = 1; #endif diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 4f864472c5c4..8016a296010e 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1341,7 +1341,6 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) int status = 0; usb_pm_lock(udev); - udev->auto_pm = 1; udev->pm_usage_cnt += inc_usage_cnt; WARN_ON(udev->pm_usage_cnt < 0); if (inc_usage_cnt) @@ -1473,7 +1472,6 @@ static int usb_autopm_do_interface(struct usb_interface *intf, if (intf->condition == USB_INTERFACE_UNBOUND) status = -ENODEV; else { - udev->auto_pm = 1; atomic_add(inc_usage_cnt, &intf->pm_usage_cnt); udev->last_busy = jiffies; if (inc_usage_cnt >= 0 && @@ -1707,7 +1705,6 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) do_unbind_rebind(udev, DO_UNBIND); usb_pm_lock(udev); - udev->auto_pm = 0; status = usb_suspend_both(udev, msg); usb_pm_unlock(udev); return status; @@ -1730,7 +1727,6 @@ int usb_external_resume_device(struct usb_device *udev, pm_message_t msg) int status; usb_pm_lock(udev); - udev->auto_pm = 0; status = usb_resume_both(udev, msg); udev->last_busy = jiffies; usb_pm_unlock(udev); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 82c3ac786726..33c1326c4e15 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1308,7 +1308,7 @@ static int option_suspend(struct usb_serial *serial, pm_message_t message) dbg("%s entered", __func__); - if (serial->dev->auto_pm) { + if (message.event & PM_EVENT_AUTO) { spin_lock_irq(&intfdata->susp_lock); b = intfdata->in_flight; spin_unlock_irq(&intfdata->susp_lock); diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index c5c41aed106d..ac1b6449fb6a 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -1005,7 +1005,7 @@ static int sierra_suspend(struct usb_serial *serial, pm_message_t message) struct sierra_intf_private *intfdata; int b; - if (serial->dev->auto_pm) { + if (message.event & PM_EVENT_AUTO) { intfdata = serial->private; spin_lock_irq(&intfdata->susp_lock); b = intfdata->in_flight; diff --git a/include/linux/usb.h b/include/linux/usb.h index 6e91ee4f5b81..4b6f6db544ee 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -429,7 +429,6 @@ struct usb_tt; * @last_busy: time of last use * @autosuspend_delay: in jiffies * @connect_time: time device was first connected - * @auto_pm: autosuspend/resume in progress * @do_remote_wakeup: remote wakeup should be enabled * @reset_resume: needs reset instead of resume * @autosuspend_disabled: autosuspend disabled by the user @@ -514,7 +513,6 @@ struct usb_device { int autosuspend_delay; unsigned long connect_time; - unsigned auto_pm:1; unsigned do_remote_wakeup:1; unsigned reset_resume:1; unsigned autosuspend_disabled:1; -- cgit v1.2.3 From 872bbd67b6e700bac0b9f435edc44e43615bc872 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 10 Nov 2009 10:53:58 +0200 Subject: USB: r8a66597: clean up. remove unneeded null checks td and dev can not be null. Also they are dereferenced in list_for_each_entry_safe and list_for_each before the check happens so we would have an oops if it were possible for them to be null. Found using the smatch static checker. Signed-off-by: Dan Carpenter Acked-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index e33d36256350..41dbc70ae752 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -822,8 +822,6 @@ static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address) return; list_for_each_entry_safe(td, next, list, queue) { - if (!td) - continue; if (td->address != address) continue; @@ -2025,8 +2023,6 @@ static struct r8a66597_device *get_r8a66597_device(struct r8a66597 *r8a66597, struct list_head *list = &r8a66597->child_device; list_for_each_entry(dev, list, device_list) { - if (!dev) - continue; if (dev->usb_address != addr) continue; -- cgit v1.2.3 From 412e8bd7c9557ef3f36855f66dd5a69bc8bc7852 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 10 Nov 2009 11:02:08 +0200 Subject: USB: fix possible null deref in init_usb_class() Add a missing goto. We dereference usb_class on the next line. Found by smatch static checker. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 222ee07ea680..bfc6c2eea647 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -99,6 +99,7 @@ static int init_usb_class(void) printk(KERN_ERR "class_create failed for usb devices\n"); kfree(usb_class); usb_class = NULL; + goto exit; } usb_class->class->devnode = usb_devnode; -- cgit v1.2.3 From 09b5a1f2301b9d5532a8edfb8102933de7c37f4f Mon Sep 17 00:00:00 2001 From: Vikram Pandita Date: Mon, 9 Nov 2009 21:24:32 -0600 Subject: usbtest: make module param pattern writeable Allow module_param to be writeable. This allows us to change the parameter if usbtest is built-in in the kernel. Signed-off-by: Vikram Pandita Signed-off-by: Anand Gadiyar Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index a9f06d76960f..0aa04b747c73 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -213,8 +213,9 @@ static struct urb *simple_alloc_urb ( } static unsigned pattern = 0; -module_param (pattern, uint, S_IRUGO); -MODULE_PARM_DESC(pattern, "i/o pattern (0 == zeroes)"); +static unsigned mod_pattern = 0; +module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(mod_pattern, "i/o pattern (0 == zeroes)"); static inline void simple_fill_buf (struct urb *urb) { @@ -1567,6 +1568,8 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) // FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. + pattern = mod_pattern; + if (code != USBTEST_REQUEST) return -EOPNOTSUPP; -- cgit v1.2.3 From 75e32e5817a3f5679eb86fd8feba3ecbc7245a7c Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 9 Nov 2009 13:35:23 -0800 Subject: USB: xhci: Add tests for TRB address translation. It's not surprising that the transfer request buffer (TRB) physical to virtual address translation function has bugs in it, since I wrote most of it at 4am last October. Add a test suite to check the TRB math. This runs at memory initialization time, and causes the driver to fail to load if the TRB math fails. Please excuse the excessively long lines in the test vectors; they can't really be made shorter and still be readable. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 159 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci-ring.c | 3 +- drivers/usb/host/xhci.h | 3 + 3 files changed, 163 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index fd05247b7bb1..711dc554e716 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -859,6 +859,163 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->page_shift = 0; } +static int xhci_test_trb_in_td(struct xhci_hcd *xhci, + struct xhci_segment *input_seg, + union xhci_trb *start_trb, + union xhci_trb *end_trb, + dma_addr_t input_dma, + struct xhci_segment *result_seg, + char *test_name, int test_number) +{ + unsigned long long start_dma; + unsigned long long end_dma; + struct xhci_segment *seg; + + start_dma = xhci_trb_virt_to_dma(input_seg, start_trb); + end_dma = xhci_trb_virt_to_dma(input_seg, end_trb); + + seg = trb_in_td(input_seg, start_trb, end_trb, input_dma); + if (seg != result_seg) { + xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n", + test_name, test_number); + xhci_warn(xhci, "Tested TRB math w/ seg %p and " + "input DMA 0x%llx\n", + input_seg, + (unsigned long long) input_dma); + xhci_warn(xhci, "starting TRB %p (0x%llx DMA), " + "ending TRB %p (0x%llx DMA)\n", + start_trb, start_dma, + end_trb, end_dma); + xhci_warn(xhci, "Expected seg %p, got seg %p\n", + result_seg, seg); + return -1; + } + return 0; +} + +/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */ +static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags) +{ + struct { + dma_addr_t input_dma; + struct xhci_segment *result_seg; + } simple_test_vector [] = { + /* A zeroed DMA field should fail */ + { 0, NULL }, + /* One TRB before the ring start should fail */ + { xhci->event_ring->first_seg->dma - 16, NULL }, + /* One byte before the ring start should fail */ + { xhci->event_ring->first_seg->dma - 1, NULL }, + /* Starting TRB should succeed */ + { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg }, + /* Ending TRB should succeed */ + { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16, + xhci->event_ring->first_seg }, + /* One byte after the ring end should fail */ + { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL }, + /* One TRB after the ring end should fail */ + { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL }, + /* An address of all ones should fail */ + { (dma_addr_t) (~0), NULL }, + }; + struct { + struct xhci_segment *input_seg; + union xhci_trb *start_trb; + union xhci_trb *end_trb; + dma_addr_t input_dma; + struct xhci_segment *result_seg; + } complex_test_vector [] = { + /* Test feeding a valid DMA address from a different ring */ + { .input_seg = xhci->event_ring->first_seg, + .start_trb = xhci->event_ring->first_seg->trbs, + .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], + .input_dma = xhci->cmd_ring->first_seg->dma, + .result_seg = NULL, + }, + /* Test feeding a valid end TRB from a different ring */ + { .input_seg = xhci->event_ring->first_seg, + .start_trb = xhci->event_ring->first_seg->trbs, + .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], + .input_dma = xhci->cmd_ring->first_seg->dma, + .result_seg = NULL, + }, + /* Test feeding a valid start and end TRB from a different ring */ + { .input_seg = xhci->event_ring->first_seg, + .start_trb = xhci->cmd_ring->first_seg->trbs, + .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], + .input_dma = xhci->cmd_ring->first_seg->dma, + .result_seg = NULL, + }, + /* TRB in this ring, but after this TD */ + { .input_seg = xhci->event_ring->first_seg, + .start_trb = &xhci->event_ring->first_seg->trbs[0], + .end_trb = &xhci->event_ring->first_seg->trbs[3], + .input_dma = xhci->event_ring->first_seg->dma + 4*16, + .result_seg = NULL, + }, + /* TRB in this ring, but before this TD */ + { .input_seg = xhci->event_ring->first_seg, + .start_trb = &xhci->event_ring->first_seg->trbs[3], + .end_trb = &xhci->event_ring->first_seg->trbs[6], + .input_dma = xhci->event_ring->first_seg->dma + 2*16, + .result_seg = NULL, + }, + /* TRB in this ring, but after this wrapped TD */ + { .input_seg = xhci->event_ring->first_seg, + .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], + .end_trb = &xhci->event_ring->first_seg->trbs[1], + .input_dma = xhci->event_ring->first_seg->dma + 2*16, + .result_seg = NULL, + }, + /* TRB in this ring, but before this wrapped TD */ + { .input_seg = xhci->event_ring->first_seg, + .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], + .end_trb = &xhci->event_ring->first_seg->trbs[1], + .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16, + .result_seg = NULL, + }, + /* TRB not in this ring, and we have a wrapped TD */ + { .input_seg = xhci->event_ring->first_seg, + .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], + .end_trb = &xhci->event_ring->first_seg->trbs[1], + .input_dma = xhci->cmd_ring->first_seg->dma + 2*16, + .result_seg = NULL, + }, + }; + + unsigned int num_tests; + int i, ret; + + num_tests = sizeof(simple_test_vector) / sizeof(simple_test_vector[0]); + for (i = 0; i < num_tests; i++) { + ret = xhci_test_trb_in_td(xhci, + xhci->event_ring->first_seg, + xhci->event_ring->first_seg->trbs, + &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], + simple_test_vector[i].input_dma, + simple_test_vector[i].result_seg, + "Simple", i); + if (ret < 0) + return ret; + } + + num_tests = sizeof(complex_test_vector) / sizeof(complex_test_vector[0]); + for (i = 0; i < num_tests; i++) { + ret = xhci_test_trb_in_td(xhci, + complex_test_vector[i].input_seg, + complex_test_vector[i].start_trb, + complex_test_vector[i].end_trb, + complex_test_vector[i].input_dma, + complex_test_vector[i].result_seg, + "Complex", i); + if (ret < 0) + return ret; + } + xhci_dbg(xhci, "TRB math tests passed.\n"); + return 0; +} + + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; @@ -962,6 +1119,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags); if (!xhci->event_ring) goto fail; + if (xhci_check_trb_in_td_math(xhci, flags) < 0) + goto fail; xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev), sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index aaed076bae37..371ba4297d9c 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -987,8 +987,7 @@ static void handle_port_status(struct xhci_hcd *xhci, * TRB in this TD, this function returns that TRB's segment. Otherwise it * returns 0. */ -static struct xhci_segment *trb_in_td( - struct xhci_segment *start_seg, +struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, union xhci_trb *start_trb, union xhci_trb *end_trb, dma_addr_t suspect_dma) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index c92f84154fb5..3989aadcf670 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1268,6 +1268,9 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); /* xHCI ring, segment, TRB, and TD functions */ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); +struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, + union xhci_trb *start_trb, union xhci_trb *end_trb, + dma_addr_t suspect_dma); void xhci_ring_cmd_db(struct xhci_hcd *xhci); void *xhci_setup_one_noop(struct xhci_hcd *xhci); void xhci_handle_event(struct xhci_hcd *xhci); -- cgit v1.2.3 From 26b801f6b8ebd9890978b06f4b6ce4edfaa1406a Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Nov 2009 14:15:20 +0100 Subject: USB: g_mass_storage: Mass Storage Function created The f_mass_storage.c has been changed into a composite function. mass_storage.c file has been introduced which defines a g_mass_storage gadget based on composite framework. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 18 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_mass_storage.c | 644 +++++++++--------------------------- drivers/usb/gadget/file_storage.c | 7 +- drivers/usb/gadget/mass_storage.c | 233 +++++++++++++ drivers/usb/gadget/storage_common.c | 22 +- 6 files changed, 426 insertions(+), 500 deletions(-) create mode 100644 drivers/usb/gadget/mass_storage.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a18e3c5dd82e..3bb250fd5321 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -732,6 +732,24 @@ config USB_FILE_STORAGE_TEST behavior of USB Mass Storage hosts. Not needed for normal operation. +config USB_MASS_STORAGE + tristate "Mass Storage Gadget" + depends on BLOCK + help + The Mass Storage Gadget acts as a USB Mass Storage disk drive. + As its storage repository it can use a regular file or a block + device (in much the same way as the "loop" device driver), + specified as a module parameter or sysfs option. + + This is heavily based on File-backed Storage Gadget and in most + cases you will want to use FSG instead. This gadget is mostly + here to test the functionality of the Mass Storage Function + which may be used with composite framework. + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_file_storage". If unsure, + consider File-backed Storage Gadget. + config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" help diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 9d7b87c52e9f..025ba2ed7907 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -39,6 +39,7 @@ g_serial-objs := serial.o g_midi-objs := gmidi.o gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o +g_mass_storage-objs := mass_storage.o g_printer-objs := printer.o g_cdc-objs := cdc2.o @@ -47,6 +48,7 @@ obj-$(CONFIG_USB_AUDIO) += g_audio.o obj-$(CONFIG_USB_ETH) += g_ether.o obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o +obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 5a0f5708c094..5fc01c04af05 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -233,47 +233,23 @@ -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "usbstring.c" -#include "config.c" -#include "epautoconf.c" - /*-------------------------------------------------------------------------*/ -#define DRIVER_DESC "File-backed Storage Gadget" -#define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "20 November 2008" +#define FSG_DRIVER_DESC "Mass Storage Function" +#define FSG_DRIVER_VERSION "20 November 2008" -static char fsg_string_manufacturer[64]; -static const char fsg_string_product[] = DRIVER_DESC; -static char fsg_string_serial[13]; -static const char fsg_string_config[] = "Self-powered"; static const char fsg_string_interface[] = "Mass Storage"; #define FSG_NO_INTR_EP 1 #define FSG_BUFFHD_STATIC_BUFFER 1 +#define FSG_NO_DEVICE_STRINGS 1 +#define FSG_NO_OTG 1 +#define FSG_NO_INTR_EP 1 #include "storage_common.c" -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Alan Stern"); -MODULE_LICENSE("Dual BSD/GPL"); - -/* - * This driver assumes self-powered hardware and has no way for users to - * trigger remote wakeup. It uses autoconfiguration to select endpoints - * and endpoint addresses. - */ - - /*-------------------------------------------------------------------------*/ @@ -295,6 +271,7 @@ static struct { .removable = 0, .can_stall = 1, .cdrom = 0, + .release = 0xffff, }; @@ -347,14 +324,18 @@ struct fsg_common { struct fsg_dev { + struct usb_function function; + struct usb_composite_dev*cdev; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ struct fsg_common *common; + u16 interface_number; + /* lock protects: state, all the req_busy's */ spinlock_t lock; - struct usb_gadget *gadget; - struct usb_ep *ep0; // Handy copy of gadget->ep0 - struct usb_request *ep0req; // For control responses + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ unsigned int ep0_req_tag; const char *ep0req_name; @@ -390,6 +371,13 @@ struct fsg_dev { u32 usb_amount_left; }; + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ + return container_of(f, struct fsg_dev, function); +} + + typedef void (*fsg_routine_t)(struct fsg_dev *); static int exception_in_progress(struct fsg_dev *fsg) @@ -410,10 +398,6 @@ static void set_bulk_out_req_length(struct fsg_dev *fsg, bh->outreq->length = length; } -static struct fsg_dev *the_fsg; -static struct usb_gadget_driver fsg_driver; - - /*-------------------------------------------------------------------------*/ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) @@ -431,95 +415,6 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) } -/*-------------------------------------------------------------------------*/ - -/* - * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. Also the (static) config and interface - * descriptors are adjusted during fsg_bind(). - */ - -/* There is only one configuration. */ -#define CONFIG_VALUE 1 - -static struct usb_device_descriptor -device_desc = { - .bLength = sizeof device_desc, - .bDescriptorType = USB_DT_DEVICE, - - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - /* The next three values can be overridden by module parameters */ - .idVendor = cpu_to_le16(FSG_VENDOR_ID), - .idProduct = cpu_to_le16(FSG_PRODUCT_ID), - .bcdDevice = cpu_to_le16(0xffff), - - .iManufacturer = FSG_STRING_MANUFACTURER, - .iProduct = FSG_STRING_PRODUCT, - .iSerialNumber = FSG_STRING_SERIAL, - .bNumConfigurations = 1, -}; - -static struct usb_config_descriptor -config_desc = { - .bLength = sizeof config_desc, - .bDescriptorType = USB_DT_CONFIG, - - /* wTotalLength computed by usb_gadget_config_buf() */ - .bNumInterfaces = 1, - .bConfigurationValue = CONFIG_VALUE, - .iConfiguration = FSG_STRING_CONFIG, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - - -static struct usb_qualifier_descriptor -dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - .bNumConfigurations = 1, -}; - - - -/* - * Config descriptors must agree with the code that sets configurations - * and with code managing interfaces and their altsettings. They must - * also handle different speeds and other-speed requests. - */ -static int populate_config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - enum usb_device_speed speed = gadget->speed; - int len; - const struct usb_descriptor_header **function; - - if (index > 0) - return -EINVAL; - - if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) - speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; - if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) - function = fsg_hs_function; - else - function = fsg_fs_function; - - /* for now, don't advertise srp-only devices */ - if (!gadget_is_otg(gadget)) - function++; - - len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} - - /*-------------------------------------------------------------------------*/ /* These routines may be called in process context or in_irq */ @@ -555,25 +450,12 @@ static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) /*-------------------------------------------------------------------------*/ -/* The disconnect callback and ep0 routines. These always run in_irq, - * except that ep0_queue() is called in the main thread to acknowledge - * completion of various requests: set config, set interface, and - * Bulk-only device reset. */ - -static void fsg_disconnect(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "disconnect or port reset\n"); - raise_exception(fsg, FSG_STATE_DISCONNECT); -} - - static int ep0_queue(struct fsg_dev *fsg) { int rc; rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); + fsg->ep0->driver_data = fsg; if (rc != 0 && rc != -ESHUTDOWN) { /* We can't do much more than wait for a reset */ @@ -583,23 +465,6 @@ static int ep0_queue(struct fsg_dev *fsg) return rc; } -static void ep0_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - - if (req->actual > 0) - dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual); - if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - if (req->status == 0 && req->context) - ((fsg_routine_t) (req->context))(fsg); -} - - /*-------------------------------------------------------------------------*/ /* Bulk and interrupt endpoint completion handlers. @@ -652,9 +517,10 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) /* Ep0 class-specific handlers. These always run in_irq. */ -static int class_setup_req(struct fsg_dev *fsg, +static int fsg_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { + struct fsg_dev *fsg = fsg_from_func(f); struct usb_request *req = fsg->ep0req; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); @@ -669,7 +535,7 @@ static int class_setup_req(struct fsg_dev *fsg, if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - if (w_index != 0 || w_value != 0) + if (w_index != fsg->interface_number || w_value != 0) return -EDOM; /* Raise an exception to stop the current operation @@ -682,7 +548,7 @@ static int class_setup_req(struct fsg_dev *fsg, if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - if (w_index != 0 || w_value != 0) + if (w_index != fsg->interface_number || w_value != 0) return -EDOM; VDBG(fsg, "get max LUN\n"); *(u8 *) req->buf = fsg->common->nluns - 1; @@ -698,160 +564,6 @@ static int class_setup_req(struct fsg_dev *fsg, } -/*-------------------------------------------------------------------------*/ - -/* Ep0 standard request handlers. These always run in_irq. */ - -static int standard_setup_req(struct fsg_dev *fsg, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_request *req = fsg->ep0req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - - /* Usually this just stores reply data in the pre-allocated ep0 buffer, - * but config change events will also reconfigure hardware. */ - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - switch (w_value >> 8) { - - case USB_DT_DEVICE: - VDBG(fsg, "get device descriptor\n"); - value = sizeof device_desc; - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - VDBG(fsg, "get device qualifier\n"); - if (!gadget_is_dualspeed(fsg->gadget)) - break; - value = sizeof dev_qualifier; - memcpy(req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - VDBG(fsg, "get other-speed config descriptor\n"); - if (!gadget_is_dualspeed(fsg->gadget)) - break; - goto get_config; - case USB_DT_CONFIG: - VDBG(fsg, "get configuration descriptor\n"); -get_config: - value = populate_config_buf(fsg->gadget, - req->buf, - w_value >> 8, - w_value & 0xff); - break; - - case USB_DT_STRING: - VDBG(fsg, "get string descriptor\n"); - - /* wIndex == language code */ - value = usb_gadget_get_string(&fsg_stringtab, - w_value & 0xff, req->buf); - break; - } - break; - - /* One config, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - VDBG(fsg, "set configuration\n"); - if (w_value == CONFIG_VALUE || w_value == 0) { - fsg->new_config = w_value; - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and set the new config. */ - raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - VDBG(fsg, "get configuration\n"); - *(u8 *) req->buf = fsg->config; - value = 1; - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (fsg->config && w_index == 0) { - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and install the new - * interface altsetting. */ - raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (!fsg->config) - break; - if (w_index != 0) { - value = -EDOM; - break; - } - VDBG(fsg, "get interface\n"); - *(u8 *) req->buf = 0; - value = 1; - break; - - default: - VDBG(fsg, - "unknown control req %02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, le16_to_cpu(ctrl->wLength)); - } - - return value; -} - - -static int fsg_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - int rc; - int w_length = le16_to_cpu(ctrl->wLength); - - ++fsg->ep0_req_tag; // Record arrival of a new request - fsg->ep0req->context = NULL; - fsg->ep0req->length = 0; - dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); - - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) - rc = class_setup_req(fsg, ctrl); - else - rc = standard_setup_req(fsg, ctrl); - - /* Respond with data/status or defer until later? */ - if (rc >= 0 && rc != DELAYED_STATUS) { - rc = min(rc, w_length); - fsg->ep0req->length = rc; - fsg->ep0req->zero = rc < w_length; - fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? - "ep0-in" : "ep0-out"); - rc = ep0_queue(fsg); - } - - /* Device either stalls (rc < 0) or reports success */ - return rc; -} - - /*-------------------------------------------------------------------------*/ /* All the following routines run in process context */ @@ -2518,24 +2230,33 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config) /* Enable the interface */ if (new_config != 0) { fsg->config = new_config; - if ((rc = do_set_interface(fsg, 0)) != 0) - fsg->config = 0; // Reset on errors - else { - char *speed; - - switch (fsg->gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - INFO(fsg, "%s speed config #%d\n", speed, fsg->config); - } + rc = do_set_interface(fsg, 0); + if (rc != 0) + fsg->config = 0; /* Reset on errors */ } return rc; } +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->new_config = 1; + raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); + return 0; +} + +static void fsg_disable(struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->new_config = 0; + raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); +} + + /*-------------------------------------------------------------------------*/ static void handle_exception(struct fsg_dev *fsg) @@ -2623,9 +2344,6 @@ static void handle_exception(struct fsg_dev *fsg) /* Carry out any extra actions required for the exception */ switch (old_state) { - default: - break; - case FSG_STATE_ABORT_BULK_OUT: send_status(fsg); spin_lock_irq(&fsg->lock); @@ -2651,16 +2369,6 @@ static void handle_exception(struct fsg_dev *fsg) // fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; break; - case FSG_STATE_INTERFACE_CHANGE: - rc = do_set_interface(fsg, 0); - if (fsg->ep0_req_tag != exception_req_tag) - break; - if (rc != 0) // STALL on errors - fsg_set_halt(fsg, fsg->ep0); - else // Complete the status stage - ep0_queue(fsg); - break; - case FSG_STATE_CONFIG_CHANGE: rc = do_set_config(fsg, new_config); if (fsg->ep0_req_tag != exception_req_tag) @@ -2671,12 +2379,6 @@ static void handle_exception(struct fsg_dev *fsg) ep0_queue(fsg); break; - case FSG_STATE_DISCONNECT: - for (i = 0; i < fsg->common->nluns; ++i) - fsg_lun_fsync_sub(&fsg->common->luns[i]); - do_set_config(fsg, 0); // Unconfigured state - break; - case FSG_STATE_EXIT: case FSG_STATE_TERMINATED: do_set_config(fsg, 0); // Free resources @@ -2684,6 +2386,14 @@ static void handle_exception(struct fsg_dev *fsg) fsg->state = FSG_STATE_TERMINATED; // Stop the thread spin_unlock_irq(&fsg->lock); break; + + case FSG_STATE_INTERFACE_CHANGE: + case FSG_STATE_DISCONNECT: + case FSG_STATE_COMMAND_PHASE: + case FSG_STATE_DATA_PHASE: + case FSG_STATE_STATUS_PHASE: + case FSG_STATE_IDLE: + break; } } @@ -2744,16 +2454,17 @@ static int fsg_main_thread(void *fsg_) if (!exception_in_progress(fsg)) fsg->state = FSG_STATE_IDLE; spin_unlock_irq(&fsg->lock); - } + } spin_lock_irq(&fsg->lock); fsg->thread_task = NULL; spin_unlock_irq(&fsg->lock); + /* XXX */ /* If we are exiting because of a signal, unregister the * gadget driver. */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) - usb_gadget_unregister_driver(&fsg_driver); + /* if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) */ + /* usb_gadget_unregister_driver(&fsg_driver); */ /* Let the unbind and cleanup routines know the thread has exited */ complete_and_exit(&fsg->thread_notifier, 0); @@ -2762,10 +2473,9 @@ static int fsg_main_thread(void *fsg_) /*************************** DEVICE ATTRIBUTES ***************************/ - -/* The write permissions and store_xxx pointers are set in fsg_bind() */ -static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); -static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); +/* Write permission is checked per LUN in store_*() functions. */ +static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); +static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); /****************************** FSG COMMON ******************************/ @@ -2789,11 +2499,13 @@ static inline void fsg_common_put(struct fsg_common *common) static struct fsg_common *fsg_common_init(struct fsg_common *common, - struct usb_gadget *gadget) + struct usb_composite_dev *cdev) { + struct usb_gadget *gadget = cdev->gadget; struct fsg_buffhd *bh; struct fsg_lun *curlun; int nluns, i, rc; + char *pathbuf; /* Find out how many LUNs there should be */ nluns = mod_data.nluns; @@ -2833,7 +2545,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, curlun->removable = mod_data.removable; curlun->dev.release = fsg_lun_release; curlun->dev.parent = &gadget->dev; - curlun->dev.driver = &fsg_driver.driver; + /* curlun->dev.driver = &fsg_driver.driver; XXX */ dev_set_drvdata(&curlun->dev, &common->filesem); dev_set_name(&curlun->dev,"%s-lun%d", dev_name(&gadget->dev), i); @@ -2906,6 +2618,33 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, kref_init(&common->ref); + + /* Information */ + INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + INFO(common, "Number of LUNs=%d\n", common->nluns); + + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); + for (i = 0, nluns = common->nluns, curlun = common->luns; + i < nluns; + ++curlun, ++i) { + char *p = "(no medium)"; + if (fsg_lun_is_open(curlun)) { + p = "(error)"; + if (pathbuf) { + p = d_path(&curlun->filp->f_path, + pathbuf, PATH_MAX); + if (IS_ERR(p)) + p = "(error)"; + } + } + LINFO(curlun, "LUN: %s%s%sfile: %s\n", + curlun->removable ? "removable " : "", + curlun->ro ? "read only " : "", + curlun->cdrom ? "CD-ROM " : "", + p); + } + kfree(pathbuf); + return common; @@ -2944,10 +2683,9 @@ static void fsg_common_release(struct kref *ref) /*-------------------------------------------------------------------------*/ -static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) { - struct fsg_dev *fsg = get_gadget_data(gadget); - struct usb_request *req = fsg->ep0req; + struct fsg_dev *fsg = fsg_from_func(f); DBG(fsg, "unbind\n"); clear_bit(REGISTERED, &fsg->atomic_bitflags); @@ -2961,59 +2699,31 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) complete(&fsg->thread_notifier); } - /* Free the request and buffer for endpoint 0 */ - if (req) { - kfree(req->buf); - usb_ep_free_request(fsg->ep0, req); - } - fsg_common_put(fsg->common); kfree(fsg); - set_gadget_data(gadget, NULL); } -static int __init fsg_bind(struct usb_gadget *gadget) +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) { - struct fsg_dev *fsg; + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; int rc; int i; - struct fsg_lun *curlun; struct usb_ep *ep; - struct usb_request *req; - char *pathbuf, *p; - /* Allocate */ - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); - if (!fsg) - return -ENOMEM; - - /* Initialise common */ - fsg->common = fsg_common_init(0, gadget); - if (IS_ERR(fsg->common)) - return PTR_ERR(fsg->common); - - /* Basic parameters */ fsg->gadget = gadget; - set_gadget_data(gadget, fsg); fsg->ep0 = gadget->ep0; - fsg->ep0->driver_data = fsg; + fsg->ep0req = c->cdev->req; - spin_lock_init(&fsg->lock); - init_completion(&fsg->thread_notifier); - - /* Enable the store_xxx attributes */ - if (mod_data.removable) { - dev_attr_file.attr.mode = 0644; - dev_attr_file.store = fsg_store_file; - if (!mod_data.cdrom) { - dev_attr_ro.attr.mode = 0644; - dev_attr_ro.store = fsg_store_ro; - } - } + /* New interface */ + i = usb_interface_id(c, f); + if (i < 0) + return i; + fsg_intf_desc.bInterfaceNumber = i; + fsg->interface_number = i; /* Find all the endpoints we will use */ - usb_ep_autoconfig_reset(gadget); ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); if (!ep) goto autoconf_fail; @@ -3026,53 +2736,26 @@ static int __init fsg_bind(struct usb_gadget *gadget) ep->driver_data = fsg; // claim the endpoint fsg->bulk_out = ep; - /* Fix up the descriptors */ - device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; - device_desc.bcdDevice = cpu_to_le16(mod_data.release); - if (gadget_is_dualspeed(gadget)) { - /* Assume ep0 uses the same maxpacket value for both speeds */ - dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; - /* Assume endpoint addresses are the same for both speeds */ fsg_hs_bulk_in_desc.bEndpointAddress = fsg_fs_bulk_in_desc.bEndpointAddress; fsg_hs_bulk_out_desc.bEndpointAddress = fsg_fs_bulk_out_desc.bEndpointAddress; + f->hs_descriptors = fsg_hs_function; } - if (gadget_is_otg(gadget)) - fsg_otg_desc.bmAttributes |= USB_OTG_HNP; - - rc = -ENOMEM; - - /* Allocate the request and buffer for endpoint 0 */ - fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); - if (!req) - goto out; - req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); - if (!req->buf) - goto out; - req->complete = ep0_complete; - - /* This should reflect the actual gadget power source */ - usb_gadget_set_selfpowered(gadget); - - snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer, - "%s %s with %s", - 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); + /* maybe allocate device-global string IDs, and patch descriptors */ + if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { + i = usb_string_id(c->cdev); + if (i < 0) + return i; + fsg_strings[FSG_STRING_INTERFACE].id = i; + fsg_intf_desc.iInterface = i; } + fsg->thread_task = kthread_create(fsg_main_thread, fsg, "file-storage-gadget"); if (IS_ERR(fsg->thread_task)) { @@ -3080,37 +2763,12 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; } - INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - INFO(fsg, "Number of LUNs=%d\n", fsg->common->nluns); - - pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - for (i = 0; i < fsg->common->nluns; ++i) { - curlun = &fsg->common->luns[i]; - if (fsg_lun_is_open(curlun)) { - p = NULL; - if (pathbuf) { - p = d_path(&curlun->filp->f_path, - pathbuf, PATH_MAX); - if (IS_ERR(p)) - p = NULL; - } - LINFO(curlun, "ro=%d, file: %s\n", - curlun->ro, (p ? p : "(error)")); - } - } - kfree(pathbuf); - - DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", - mod_data.removable, mod_data.can_stall, - mod_data.cdrom, FSG_BUFLEN); DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); set_bit(REGISTERED, &fsg->atomic_bitflags); /* Tell the thread to start working */ wake_up_process(fsg->thread_task); - - the_fsg = fsg; return 0; autoconf_fail: @@ -3119,48 +2777,56 @@ autoconf_fail: out: fsg->state = FSG_STATE_TERMINATED; // The thread is dead - fsg_unbind(gadget); + fsg_unbind(c, f); complete(&fsg->thread_notifier); return rc; } -/*-------------------------------------------------------------------------*/ +/****************************** ADD FUNCTION ******************************/ -static struct usb_gadget_driver fsg_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif - .function = (char *) fsg_string_product, - .bind = fsg_bind, - .unbind = fsg_unbind, - .disconnect = fsg_disconnect, - .setup = fsg_setup, - - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - // .release = ... - // .suspend = ... - // .resume = ... - }, +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, }; - -static int __init fsg_init(void) +static int fsg_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsg_common *common) { - return usb_gadget_register_driver(&fsg_driver); -} -module_init(fsg_init); + struct fsg_dev *fsg; + int rc; + + fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + if (unlikely(!fsg)) + return -ENOMEM; + spin_lock_init(&fsg->lock); + init_completion(&fsg->thread_notifier); -static void __exit fsg_cleanup(void) -{ - /* Unregister the driver iff the thread hasn't already done so */ - if (the_fsg && - test_and_clear_bit(REGISTERED, &the_fsg->atomic_bitflags)) - usb_gadget_unregister_driver(&fsg_driver); + fsg->cdev = cdev; + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.strings = fsg_strings_array; + fsg->function.descriptors = fsg_fs_function; + fsg->function.bind = fsg_bind; + fsg->function.unbind = fsg_unbind; + fsg->function.setup = fsg_setup; + fsg->function.set_alt = fsg_set_alt; + fsg->function.disable = fsg_disable; + + fsg->common = common; + /* Our caller holds a reference to common structure so we + * don't have to be worry about it being freed until we return + * from this function. So instead of incrementing counter now + * and decrement in error recovery we increment it only when + * call to usb_add_function() was successful. */ + + rc = usb_add_function(c, &fsg->function); + + if (likely(rc == 0)) + fsg_common_get(fsg->common); + else + kfree(fsg); + + return rc; } -module_exit(fsg_cleanup); diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 7998402b1840..fca3407e48f2 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -594,10 +594,9 @@ static int populate_config_buf(struct usb_gadget *gadget, if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; - if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) - function = fsg_hs_function; - else - function = fsg_fs_function; + function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH + ? (const struct usb_descriptor_header **)fsg_hs_function + : (const struct usb_descriptor_header **)fsg_fs_function; /* for now, don't advertise srp-only devices */ if (!gadget_is_otg(gadget)) diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c new file mode 100644 index 000000000000..27339f04bc1b --- /dev/null +++ b/drivers/usb/gadget/mass_storage.c @@ -0,0 +1,233 @@ +/* + * mass_storage.c -- File-backed USB Storage Gadget, for USB development + * + * Copyright (C) 2003-2008 Alan Stern + * + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * The Mass Storage Gadget acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful gadget + * driver for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. Last but not least, it + * gives an easy way to probe the behavior of the Mass Storage drivers + * in a USB host. + * + * Since this file serves only administrative purposes and all the + * business logic is implemented in f_mass_storage.* file. Read + * comments in this file for more detailed description. + */ + + +#include +#include +#include + + +/*-------------------------------------------------------------------------*/ + +#define DRIVER_DESC "Mass Storage Gadget" +#define DRIVER_VERSION "2009/07/21" + +/*-------------------------------------------------------------------------*/ + +/* + * kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ + +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "f_mass_storage.c" + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor msg_device_desc = { + .bLength = sizeof msg_device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(FSG_VENDOR_ID), + .idProduct = cpu_to_le16(FSG_PRODUCT_ID), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_CONFIGURATION_IDX 2 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [STRING_CONFIGURATION_IDX].s = "Self Powered", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + + + +/****************************** Configurations ******************************/ + +static int __init msg_do_config(struct usb_configuration *c) +{ + struct fsg_common *common; + int ret; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + common = fsg_common_init(0, c->cdev); + if (IS_ERR(common)) + return PTR_ERR(common); + + ret = fsg_add(c->cdev, c, common); + fsg_common_put(common); + return ret; +} + +static struct usb_configuration msg_config_driver = { + .label = "Linux File-Backed Storage", + .bind = msg_do_config, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + + + +/****************************** Gadget Bind ******************************/ + + +static int __init msg_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + int status; + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + msg_device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_PRODUCT_IDX].id = status; + msg_device_desc.iProduct = status; + + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_CONFIGURATION_IDX].id = status; + msg_config_driver.iConfiguration = status; + + /* register our second configuration */ + status = usb_add_config(cdev, &msg_config_driver); + if (status < 0) + return status; + + dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); + return 0; +} + +static int __exit msg_unbind(struct usb_composite_dev *cdev) +{ + return 0; +} + + +/****************************** Some noise ******************************/ + + +static struct usb_composite_driver msg_driver = { + .name = "g_mass_storage", + .dev = &msg_device_desc, + .strings = dev_strings, + .bind = msg_bind, + .unbind = __exit_p(msg_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Michal Nazarewicz"); +MODULE_LICENSE("GPL"); + +static int __init msg_init(void) +{ + return usb_composite_register(&msg_driver); +} +module_init(msg_init); + +static void __exit msg_cleanup(void) +{ + usb_composite_unregister(&msg_driver); +} +module_exit(msg_cleanup); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 60bc696778c9..7e81a2d898f0 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -84,11 +84,19 @@ #define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) #define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) -#define DBG(d, fmt, args...) dev_dbg (&(d)->gadget->dev, fmt, ## args) -#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev, fmt, ## args) -#define ERROR(d, fmt, args...) dev_err (&(d)->gadget->dev, fmt, ## args) -#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev, fmt, ## args) -#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev, fmt, ## args) +/* Keep those macros in sync with thos in + * include/linux/ubs/composite.h or else GCC will complain. If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. No + * checking if those symbols is defined is performed because warning + * is desired when those macros were defined by someone else to mean + * something else. */ +#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev , fmt , ## args) @@ -429,7 +437,7 @@ fsg_fs_intr_in_desc = { #endif -static const struct usb_descriptor_header *fsg_fs_function[] = { +static struct usb_descriptor_header *fsg_fs_function[] = { #ifndef FSG_NO_OTG (struct usb_descriptor_header *) &fsg_otg_desc, #endif @@ -493,7 +501,7 @@ fsg_hs_intr_in_desc = { #endif -static const struct usb_descriptor_header *fsg_hs_function[] = { +static struct usb_descriptor_header *fsg_hs_function[] = { #ifndef FSG_NO_OTG (struct usb_descriptor_header *) &fsg_otg_desc, #endif -- cgit v1.2.3 From 88b9042822848afb037fe1c636f896a87026fc2d Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Nov 2009 14:15:21 +0100 Subject: USB: g_mass_storage: fsg_config added & module params handlig changed Removed all references to mod_data in f_mass_storage.c and instead created fsg_config structure fsg_common_init() takes as an argument -- it stores all configuration options that were previously taken from mod_data. Moreover, The fsg_config structure allows per-LUN configuration of removable and CD-ROM emulation. Module parameters are handled by defining an object of fsg_module_parameters structure and then declaring module parameters via FSG_MODULE_PARAMETERS() macro. It adds proper declarations to the code making specified object be populated from module parameters. To use values stored there one may use either fsg_config_from_params() which will will a fsg_config structure with values taken from fsg_module_parameters structure or fsg_common_from_params() which will initialise fsg_common structure directly. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 267 +++++++++++++++++++++++------------- drivers/usb/gadget/mass_storage.c | 7 +- 2 files changed, 178 insertions(+), 96 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 5fc01c04af05..e7c9f0ce86b6 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -253,51 +253,6 @@ static const char fsg_string_interface[] = "Mass Storage"; /*-------------------------------------------------------------------------*/ -/* Encapsulate the module parameter settings */ - -static struct { - char *file[FSG_MAX_LUNS]; - int ro[FSG_MAX_LUNS]; - unsigned int num_filenames; - unsigned int num_ros; - unsigned int nluns; - - int removable; - int can_stall; - int cdrom; - - unsigned short release; -} mod_data = { // Default values - .removable = 0, - .can_stall = 1, - .cdrom = 0, - .release = 0xffff, - }; - - -module_param_array_named(file, mod_data.file, charp, &mod_data.num_filenames, - S_IRUGO); -MODULE_PARM_DESC(file, "names of backing files or devices"); - -module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO); -MODULE_PARM_DESC(ro, "true to force read-only"); - -module_param_named(luns, mod_data.nluns, uint, S_IRUGO); -MODULE_PARM_DESC(luns, "number of LUNs"); - -module_param_named(removable, mod_data.removable, bool, S_IRUGO); -MODULE_PARM_DESC(removable, "true to simulate removable media"); - -module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); -MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); - -module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); -MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); - - -/*-------------------------------------------------------------------------*/ - - /* Data shared by all the FSG instances. */ struct fsg_common { struct usb_gadget *gadget; @@ -317,12 +272,34 @@ struct fsg_common { struct fsg_lun *luns; struct fsg_lun *curlun; + unsigned int can_stall:1; unsigned int free_storage_on_release:1; + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ + char inquiry_string[8 + 16 + 4 + 1]; + struct kref ref; }; +struct fsg_config { + unsigned nluns; + struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + } luns[FSG_MAX_LUNS]; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + u16 release; + + char can_stall; +}; + + struct fsg_dev { struct usb_function function; struct usb_composite_dev*cdev; @@ -351,6 +328,7 @@ struct fsg_dev { unsigned int phase_error : 1; unsigned int short_packet_received : 1; unsigned int bad_lun_okay : 1; + unsigned int can_stall : 1; unsigned long atomic_bitflags; #define REGISTERED 0 @@ -1065,13 +1043,10 @@ static int do_verify(struct fsg_dev *fsg) static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) { + struct fsg_lun *curlun = fsg->common->curlun; u8 *buf = (u8 *) bh->buf; - static char vendor_id[] = "Linux "; - static char product_disk_id[] = "File-Stor Gadget"; - static char product_cdrom_id[] = "File-CD Gadget "; - - if (!fsg->common->curlun) { // Unsupported LUNs are okay + if (!curlun) { /* Unsupported LUNs are okay */ fsg->bad_lun_okay = 1; memset(buf, 0, 36); buf[0] = 0x7f; // Unsupported, no device-type @@ -1079,18 +1054,16 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) return 36; } - memset(buf, 0, 8); - buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK); - if (mod_data.removable) - buf[1] = 0x80; + buf[0] = curlun->cdrom ? TYPE_CDROM : TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; buf[2] = 2; // ANSI SCSI level 2 buf[3] = 2; // SCSI-2 INQUIRY data format buf[4] = 31; // Additional length - // No special options - sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, - (mod_data.cdrom ? product_cdrom_id : - product_disk_id), - mod_data.release); + buf[5] = 0; // No special options + buf[6] = 0; + buf[7] = 0; + memcpy(buf + 8, fsg->common->inquiry_string, + sizeof fsg->common->inquiry_string); return 36; } @@ -1303,7 +1276,9 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) static int do_start_stop(struct fsg_dev *fsg) { - if (!mod_data.removable) { + if (!fsg->common->curlun) { + return -EINVAL; + } else if (!fsg->common->curlun->removable) { fsg->common->curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; } @@ -1316,8 +1291,10 @@ static int do_prevent_allow(struct fsg_dev *fsg) struct fsg_lun *curlun = fsg->common->curlun; int prevent; - if (!mod_data.removable) { - curlun->sense_data = SS_INVALID_COMMAND; + if (!fsg->common->curlun) { + return -EINVAL; + } else if (!fsg->common->curlun->removable) { + fsg->common->curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; } @@ -1505,7 +1482,7 @@ static int finish_reply(struct fsg_dev *fsg) * try to send or receive any data. So stall both bulk pipes * if we can and wait for a reset. */ case DATA_DIR_UNKNOWN: - if (mod_data.can_stall) { + if (fsg->can_stall) { fsg_set_halt(fsg, fsg->bulk_out); rc = halt_bulk_in_endpoint(fsg); } @@ -1526,7 +1503,7 @@ static int finish_reply(struct fsg_dev *fsg) /* For Bulk-only, if we're allowed to stall then send the * short packet and halt the bulk-in endpoint. If we can't * stall, pad out the remaining data with 0's. */ - } else if (mod_data.can_stall) { + } else if (fsg->can_stall) { bh->inreq->zero = 1; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); @@ -1556,7 +1533,7 @@ static int finish_reply(struct fsg_dev *fsg) * STALL. Not realizing the endpoint was halted, it wouldn't * clear the halt -- leading to problems later on. */ #if 0 - else if (mod_data.can_stall) { + else if (fsg->can_stall) { fsg_set_halt(fsg, fsg->bulk_out); raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); rc = -EINTR; @@ -1866,7 +1843,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_READ_HEADER: - if (!mod_data.cdrom) + if (!fsg->common->curlun || !fsg->common->curlun->cdrom) goto unknown_cmnd; fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, @@ -1876,7 +1853,7 @@ static int do_scsi_command(struct fsg_dev *fsg) break; case SC_READ_TOC: - if (!mod_data.cdrom) + if (!fsg->common->curlun || !fsg->common->curlun->cdrom) goto unknown_cmnd; fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, @@ -2043,7 +2020,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) /* We can do anything we want here, so let's stall the * bulk pipes if we are allowed to. */ - if (mod_data.can_stall) { + if (fsg->can_stall) { fsg_set_halt(fsg, fsg->bulk_out); halt_bulk_in_endpoint(fsg); } @@ -2499,18 +2476,18 @@ static inline void fsg_common_put(struct fsg_common *common) static struct fsg_common *fsg_common_init(struct fsg_common *common, - struct usb_composite_dev *cdev) + struct usb_composite_dev *cdev, + struct fsg_config *cfg) { struct usb_gadget *gadget = cdev->gadget; struct fsg_buffhd *bh; struct fsg_lun *curlun; + struct fsg_lun_config *lcfg; int nluns, i, rc; char *pathbuf; /* Find out how many LUNs there should be */ - nluns = mod_data.nluns; - if (nluns == 0) - nluns = max(mod_data.num_filenames, 1u); + nluns = cfg->nluns; if (nluns < 1 || nluns > FSG_MAX_LUNS) { dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns); return ERR_PTR(-EINVAL); @@ -2539,10 +2516,10 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, init_rwsem(&common->filesem); - for (i = 0; i < nluns; ++i, ++curlun) { - curlun->cdrom = !!mod_data.cdrom; - curlun->ro = mod_data.cdrom || mod_data.ro[i]; - curlun->removable = mod_data.removable; + for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) { + curlun->cdrom = !!lcfg->cdrom; + curlun->ro = lcfg->cdrom || lcfg->ro; + curlun->removable = lcfg->removable; curlun->dev.release = fsg_lun_release; curlun->dev.parent = &gadget->dev; /* curlun->dev.driver = &fsg_driver.driver; XXX */ @@ -2564,11 +2541,11 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, if (rc) goto error_luns; - if (mod_data.file[i] && *mod_data.file[i]) { - rc = fsg_lun_open(curlun, mod_data.file[i]); + if (lcfg->filename) { + rc = fsg_lun_open(curlun, lcfg->filename); if (rc) goto error_luns; - } else if (!mod_data.removable) { + } else if (!curlun->removable) { ERROR(common, "no file given for LUN%d\n", i); rc = -EINVAL; goto error_luns; @@ -2588,33 +2565,40 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, bh->next = common->buffhds; - /* Release */ - if (mod_data.release == 0xffff) { // Parameter wasn't set - int gcnum; - + /* Prepare inquiryString */ + if (cfg->release != 0xffff) { + i = cfg->release; + } else { /* The sa1100 controller is not supported */ - if (gadget_is_sa1100(gadget)) - gcnum = -1; - else - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - mod_data.release = 0x0300 + gcnum; - else { - WARNING(common, "controller '%s' not recognized\n", - gadget->name); + i = gadget_is_sa1100(gadget) + ? -1 + : usb_gadget_controller_number(gadget); + if (i >= 0) { + i = 0x0300 + i; + } else { WARNING(common, "controller '%s' not recognized\n", gadget->name); - mod_data.release = 0x0399; + i = 0x0399; } } +#define OR(x, y) ((x) ? (x) : (y)) + snprintf(common->inquiry_string, sizeof common->inquiry_string, + "%-8s%-16s%04x", + OR(cfg->vendor_name, "Linux "), + /* Assume product name dependent on the first LUN */ + OR(cfg->product_name, common->luns->cdrom + ? "File-Stor Gadget" + : "File-CD Gadget "), + i); +#undef OR /* Some peripheral controllers are known not to be able to * halt bulk endpoints correctly. If one of them is present, * disable stalls. */ - if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) - mod_data.can_stall = 0; + common->can_stall = cfg->can_stall && + !(gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)); kref_init(&common->ref); @@ -2820,6 +2804,7 @@ static int fsg_add(struct usb_composite_dev *cdev, * from this function. So instead of incrementing counter now * and decrement in error recovery we increment it only when * call to usb_add_function() was successful. */ + fsg->can_stall = common->can_stall; rc = usb_add_function(c, &fsg->function); @@ -2830,3 +2815,95 @@ static int fsg_add(struct usb_composite_dev *cdev, return rc; } + + + +/************************* Module parameters *************************/ + + +struct fsg_module_parameters { + char *file[FSG_MAX_LUNS]; + int ro[FSG_MAX_LUNS]; + int removable[FSG_MAX_LUNS]; + int cdrom[FSG_MAX_LUNS]; + + unsigned int file_count, ro_count, removable_count, cdrom_count; + unsigned int luns; /* nluns */ + int stall; /* can_stall */ +}; + + +#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ + module_param_array_named(prefix ## name, params.name, type, \ + &prefix ## params.name ## _count, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \ + module_param_named(prefix ## name, params.name, type, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define FSG_MODULE_PARAMETERS(prefix, params) \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \ + "names of backing files or devices"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \ + "true to force read-only"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \ + "true to simulate removable media"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \ + "true to simulate CD-ROM instead of disk"); \ + _FSG_MODULE_PARAM(prefix, params, luns, uint, \ + "number of LUNs"); \ + _FSG_MODULE_PARAM(prefix, params, stall, bool, \ + "false to prevent bulk stalls") + + +static void +fsg_config_from_params(struct fsg_config *cfg, + const struct fsg_module_parameters *params) +{ + struct fsg_lun_config *lun; + unsigned i, nluns; + + /* Configure LUNs */ + nluns = cfg->nluns = !params->luns + ? params->file_count ? params->file_count : 1 + : params->luns; + for (i = 0, lun = cfg->luns; + i < FSG_MAX_LUNS && i < nluns; + ++i, ++lun) { + lun->ro = !!params->ro[i]; + lun->cdrom = !!params->cdrom[i]; + lun->removable = + params->removable_count <= i || params->removable[i]; + lun->filename = + params->file_count > i && params->file[i][0] + ? params->file[i] + : 0; + } + + /* Let FSG use defaults */ + cfg->vendor_name = 0; + cfg->product_name = 0; + cfg->release = 0xffff; + + /* Finalise */ + cfg->can_stall = params->stall; +} + +static inline struct fsg_common * +fsg_common_from_params(struct fsg_common *common, + struct usb_composite_dev *cdev, + const struct fsg_module_parameters *params) + __attribute__((unused)); +static inline struct fsg_common * +fsg_common_from_params(struct fsg_common *common, + struct usb_composite_dev *cdev, + const struct fsg_module_parameters *params) +{ + struct fsg_config cfg; + fsg_config_from_params(&cfg, params); + return fsg_common_init(common, cdev, &cfg); +} + diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 27339f04bc1b..00396a36d011 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -127,6 +127,11 @@ static struct usb_gadget_strings *dev_strings[] = { /****************************** Configurations ******************************/ +static struct fsg_module_parameters mod_data = { + .stall = 1 +}; +FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); + static int __init msg_do_config(struct usb_configuration *c) { struct fsg_common *common; @@ -137,7 +142,7 @@ static int __init msg_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - common = fsg_common_init(0, c->cdev); + common = fsg_common_from_params(0, c->cdev, &mod_data); if (IS_ERR(common)) return PTR_ERR(common); -- cgit v1.2.3 From c76508f0bb5f58a2a833c967c5aecafcea3f578e Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Nov 2009 14:15:22 +0100 Subject: USB: g_mass_storage: lun_name_format and thread_name added A two fsg_config fields were added: * lun_name_format which lets one specify format of a name used when registering LUN devices. It is useful if there would be ever need for two MSFs to be used in a single composite gadget (as opposed to single MSF in two configuration); and * thread_name which lets one specify the name of a kernel thread used by MSF. This is not required since two or more threads can have the same name but nevertheless it's here for consistency. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index e7c9f0ce86b6..837aab624a46 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -233,7 +233,7 @@ -/*-------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------*/ #define FSG_DRIVER_DESC "Mass Storage Function" #define FSG_DRIVER_VERSION "20 November 2008" @@ -275,6 +275,8 @@ struct fsg_common { unsigned int can_stall:1; unsigned int free_storage_on_release:1; + const char *thread_name; + /* Vendor (8 chars), product (16 chars), release (4 * hexadecimal digits) and NUL byte */ char inquiry_string[8 + 16 + 4 + 1]; @@ -292,6 +294,9 @@ struct fsg_config { char cdrom; } luns[FSG_MAX_LUNS]; + const char *lun_name_format; + const char *thread_name; + const char *vendor_name; /* 8 characters or less */ const char *product_name; /* 16 characters or less */ u16 release; @@ -2524,8 +2529,11 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, curlun->dev.parent = &gadget->dev; /* curlun->dev.driver = &fsg_driver.driver; XXX */ dev_set_drvdata(&curlun->dev, &common->filesem); - dev_set_name(&curlun->dev,"%s-lun%d", - dev_name(&gadget->dev), i); + dev_set_name(&curlun->dev, + cfg->lun_name_format + ? cfg->lun_name_format + : "lun%d", + i); rc = device_register(&curlun->dev); if (rc) { @@ -2590,7 +2598,6 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, ? "File-Stor Gadget" : "File-CD Gadget "), i); -#undef OR /* Some peripheral controllers are known not to be able to @@ -2601,7 +2608,10 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, !(gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)); + common->thread_name = OR(cfg->thread_name, "file-storage"); kref_init(&common->ref); +#undef OR + /* Information */ INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); @@ -2741,7 +2751,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) fsg->thread_task = kthread_create(fsg_main_thread, fsg, - "file-storage-gadget"); + fsg->common->thread_name); if (IS_ERR(fsg->thread_task)) { rc = PTR_ERR(fsg->thread_task); goto out; @@ -2884,6 +2894,8 @@ fsg_config_from_params(struct fsg_config *cfg, } /* Let FSG use defaults */ + cfg->lun_name_format = 0; + cfg->thread_name = 0; cfg->vendor_name = 0; cfg->product_name = 0; cfg->release = 0xffff; -- cgit v1.2.3 From fb77d3a260c3dea66fb52dbe4bb4d29474612894 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Nov 2009 14:15:23 +0100 Subject: USB: g_mass_storage: code cleaned up and comments updated Fixed most of the errors and warnings in f_mass_storage.c and storage_common.c reported by checkpatch.pl as well as updated comments. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 567 +++++++++++++++++++++--------------- drivers/usb/gadget/mass_storage.c | 6 +- drivers/usb/gadget/storage_common.c | 108 +++---- 3 files changed, 387 insertions(+), 294 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 837aab624a46..5eaf22db7fcc 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -1,7 +1,9 @@ /* - * file_storage.c -- File-backed USB Storage Gadget, for USB development + * f_mass_storage.c -- Mass Storage USB Composite Function * * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,37 +39,112 @@ /* - * The File-backed Storage Gadget acts as a USB Mass Storage device, - * appearing to the host as a disk drive or as a CD-ROM drive. In addition - * to providing an example of a genuinely useful gadget driver for a USB - * device, it also illustrates a technique of double-buffering for increased - * throughput. Last but not least, it gives an easy way to probe the - * behavior of the Mass Storage drivers in a USB host. + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. * - * Backing storage is provided by a regular file or a block device, specified - * by the "file" module parameter. Access can be limited to read-only by - * setting the optional "ro" module parameter. (For CD-ROM emulation, - * access is always read-only.) The gadget will indicate that it has - * removable media if the optional "removable" module parameter is set. + * Function supports multiple logical units (LUNs). Backing storage + * for each LUN is provided by a regular file or a block device. + * Access for each LUN can be limited to read-only. Moreover, the + * function can indicate that LUN is removable and/or CD-ROM. (The + * later implies read-only access.) + * + * MSF is configured by specifying a fsg_config structure. It has the + * following fields: + * + * nluns Number of LUNs function have (anywhere from 1 + * to FSG_MAX_LUNS which is 8). + * luns An array of LUN configuration values. This + * should be filled for each LUN that + * function will include (ie. for "nluns" + * LUNs). Each element of the array has + * the following fields: + * ->filename The path to the backing file for the LUN. + * Required if LUN is not marked as + * removable. + * ->ro Flag specifying access to the LUN shall be + * read-only. This is implied if CD-ROM + * emulation is enabled as well as when + * it was impossible to open "filename" + * in R/W mode. + * ->removable Flag specifying that LUN shall be indicated as + * being removable. + * ->cdrom Flag specifying that LUN shall be reported as + * being a CD-ROM. + * + * lun_name_format A printf-like format for names of the LUN + * devices. This determines how the + * directory in sysfs will be named. + * Unless you are using several MSFs in + * a single gadget (as opposed to single + * MSF in many configurations) you may + * leave it as NULL (in which case + * "lun%d" will be used). In the format + * you can use "%d" to index LUNs for + * MSF's with more than one LUN. (Beware + * that there is only one integer given + * as an argument for the format and + * specifying invalid format may cause + * unspecified behaviour.) + * thread_name Name of the kernel thread process used by the + * MSF. You can safely set it to NULL + * (in which case default "file-storage" + * will be used). + * + * vendor_name + * product_name + * release Information used as a reply to INQUIRY + * request. To use default set to NULL, + * NULL, 0xffff respectively. The first + * field should be 8 and the second 16 + * characters or less. + * + * can_stall Set to permit function to halt bulk endpoints. + * Disabled on some USB devices known not + * to work correctly. You should set it + * to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified. If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error). The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN. Note also that the CD-ROM block length is + * set to 512 rather than the more common value 2048. + * + * + * MSF includes support for module parameters. If gadget using it + * decides to use it, the following module parameters will be + * available: + * + * file=filename[,filename...] + * Names of the files or block devices used for + * backing storage. + * ro=b[,b...] Default false, boolean for read-only access. + * removable=b[,b...] + * Default true, boolean for removable media. + * cdrom=b[,b...] Default false, boolean for whether to emulate + * a CD-ROM drive. + * luns=N Default N = number of filenames, number of + * LUNs to support. + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt + * bulk endpoints. + * + * The module parameters may be prefixed with some string. You need + * to consult gadget's documentation or source to verify whether it is + * using those module parameters and if it does what are the prefixes + * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is + * the prefix). * - * 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 - * "luns" module parameter (anywhere from 1 to 8), and the corresponding - * files are specified using comma-separated lists for "file" and "ro". - * The default number of LUNs is taken from the number of "file" elements; - * it is 1 if "file" is not given. If "removable" is not set then a backing - * file must be specified for each LUN. If it is set, then an unspecified - * or empty backing filename means the LUN's medium is not loaded. Ideally - * each LUN would be settable independently as a disk drive or a CD-ROM - * drive, but currently all LUNs have to be the same type. The CD-ROM - * emulation includes a single data track and no audio tracks; hence there - * need be only one backing file per LUN. Note also that the CD-ROM block - * length is set to 512 rather than the more common value 2048. * * Requirements are modest; only a bulk-in and a bulk-out endpoint are - * needed (an interrupt-out endpoint is also needed for CBI). The memory - * requirement amounts to two 16K buffers, size configurable by a parameter. - * Support is included for both full-speed and high-speed operation. + * needed. The memory requirement amounts to two 16K buffers, size + * configurable by a parameter. Support is included for both + * full-speed and high-speed operation. * * Note that the driver is slightly non-portable in that it assumes a * single memory/DMA buffer will be useable for bulk-in, bulk-out, and @@ -75,39 +152,28 @@ * issue, but there may be some with hardware restrictions that prevent * a buffer from being used by more than one endpoint. * - * Module options: * - * file=filename[,filename...] - * Required if "removable" is not set, names of - * the files or block devices used for - * backing storage - * ro=b[,b...] Default false, booleans for read-only access - * removable Default false, boolean for removable media - * luns=N Default N = number of filenames, number of - * LUNs to support - * stall Default determined according to the type of - * USB device controller (usually true), - * boolean to permit the driver to halt - * bulk endpoints - * cdrom Default false, boolean for whether to emulate - * a CD-ROM drive + * The pathnames of the backing files and the ro settings are + * available in the attribute files "file" and "ro" in the lun (or + * to be more precise in a directory which name comes from + * "lun_name_format" option!) subdirectory of the gadget's sysfs + * directory. If the "removable" option is set, writing to these + * files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab. Changes to the + * ro setting are not allowed when the medium is loaded or if CD-ROM + * emulation is being used. * - * The pathnames of the backing files and the ro settings are available in - * the attribute files "file" and "ro" in the lun subdirectory of the - * gadget's sysfs directory. If the "removable" option is set, writing to - * these files will simulate ejecting/loading the medium (writing an empty - * line means eject) and adjusting a write-enable tab. Changes to the ro - * setting are not allowed when the medium is loaded or if CD-ROM emulation - * is being used. * - * This gadget driver is heavily based on "Gadget Zero" by David Brownell. - * The driver's SCSI command interface was based on the "Information - * technology - Small Computer System Interface - 2" document from - * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at - * . The single exception - * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the - * "Universal Serial Bus Mass Storage Class UFI Command Specification" - * document, Revision 1.0, December 14, 1998, available at + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell. The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at . + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at * . */ @@ -115,7 +181,7 @@ /* * Driver Design * - * The FSG driver is fairly straightforward. There is a main kernel + * The MSF is fairly straightforward. There is a main kernel * thread that handles most of the work. Interrupt routines field * callbacks from the controller driver: bulk- and interrupt-request * completion notifications, endpoint-0 events, and disconnect events. @@ -138,17 +204,11 @@ * an EXIT exception. * * In normal operation the main thread is started during the gadget's - * fsg_bind() callback and stopped during fsg_unbind(). But it can also - * exit when it receives a signal, and there's no point leaving the - * gadget running when the thread is dead. So just before the thread - * exits, it deregisters the gadget driver. This makes things a little - * tricky: The driver is deregistered at two places, and the exiting - * thread can indirectly call fsg_unbind() which in turn can tell the - * thread to exit. The first problem is resolved through the use of the - * REGISTERED atomic bitflag; the driver will only be deregistered once. - * The second problem is resolved by having fsg_unbind() check - * fsg->state; it won't try to stop the thread if the state is already - * FSG_STATE_TERMINATED. + * fsg_bind() callback and stopped during fsg_unbind(). But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead. At of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. * * To provide maximum throughput, the driver uses a circular pipeline of * buffer heads (struct fsg_buffhd). In principle the pipeline can be @@ -236,7 +296,7 @@ /*------------------------------------------------------------------------*/ #define FSG_DRIVER_DESC "Mass Storage Function" -#define FSG_DRIVER_VERSION "20 November 2008" +#define FSG_DRIVER_VERSION "2009/09/11" static const char fsg_string_interface[] = "Mass Storage"; @@ -307,7 +367,7 @@ struct fsg_config { struct fsg_dev { struct usb_function function; - struct usb_composite_dev*cdev; + struct usb_composite_dev *cdev; struct usb_gadget *gadget; /* Copy of cdev->gadget */ struct fsg_common *common; @@ -322,18 +382,18 @@ struct fsg_dev { const char *ep0req_name; unsigned int bulk_out_maxpacket; - enum fsg_state state; // For exception handling + enum fsg_state state; /* For exception handling */ unsigned int exception_req_tag; u8 config, new_config; - unsigned int running : 1; - unsigned int bulk_in_enabled : 1; - unsigned int bulk_out_enabled : 1; - unsigned int phase_error : 1; - unsigned int short_packet_received : 1; - unsigned int bad_lun_okay : 1; - unsigned int can_stall : 1; + unsigned int running:1; + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + unsigned int can_stall:1; unsigned long atomic_bitflags; #define REGISTERED 0 @@ -461,7 +521,7 @@ static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) if (req->status || req->actual != req->length) DBG(fsg, "%s --> %d, %u/%u\n", __func__, req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled + if (req->status == -ECONNRESET) /* Request was cancelled */ usb_ep_fifo_flush(ep); /* Hold the lock while we update the request and buffer states */ @@ -483,7 +543,7 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) DBG(fsg, "%s --> %d, %u/%u\n", __func__, req->status, req->actual, bh->bulk_out_intended_length); - if (req->status == -ECONNRESET) // Request was cancelled + if (req->status == -ECONNRESET) /* Request was cancelled */ usb_ep_fifo_flush(ep); /* Hold the lock while we update the request and buffer states */ @@ -643,7 +703,7 @@ static int do_read(struct fsg_dev *fsg) /* Carry out the file reads */ amount_left = fsg->data_size_from_cmnd; if (unlikely(amount_left == 0)) - return -EIO; // No default reply + return -EIO; /* No default reply */ for (;;) { @@ -701,7 +761,7 @@ static int do_read(struct fsg_dev *fsg) } else if (nread < amount) { LDBG(curlun, "partial file read: %d/%u\n", (int) nread, amount); - nread -= (nread & 511); // Round down to a block + nread -= (nread & 511); /* Round down to a block */ } file_offset += nread; amount_left -= nread; @@ -718,7 +778,7 @@ static int do_read(struct fsg_dev *fsg) } if (amount_left == 0) - break; // No more left to read + break; /* No more left to read */ /* Send this buffer and go read some more */ bh->inreq->zero = 0; @@ -727,7 +787,7 @@ static int do_read(struct fsg_dev *fsg) fsg->common->next_buffhd_to_fill = bh->next; } - return -EIO; // No default reply + return -EIO; /* No default reply */ } @@ -751,7 +811,7 @@ static int do_write(struct fsg_dev *fsg) return -EINVAL; } spin_lock(&curlun->filp->f_lock); - curlun->filp->f_flags &= ~O_SYNC; // Default is not to wait + curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */ spin_unlock(&curlun->filp->f_lock); /* Get the starting Logical Block Address and check that it's @@ -769,7 +829,7 @@ static int do_write(struct fsg_dev *fsg) curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - if (fsg->common->cmnd[1] & 0x08) { // FUA + if (fsg->common->cmnd[1] & 0x08) { /* FUA */ spin_lock(&curlun->filp->f_lock); curlun->filp->f_flags |= O_SYNC; spin_unlock(&curlun->filp->f_lock); @@ -834,8 +894,8 @@ static int do_write(struct fsg_dev *fsg) /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); @@ -846,7 +906,7 @@ static int do_write(struct fsg_dev *fsg) /* Write the received data to the backing file */ bh = fsg->common->next_buffhd_to_drain; if (bh->state == BUF_STATE_EMPTY && !get_some_more) - break; // We stopped early + break; /* We stopped early */ if (bh->state == BUF_STATE_FULL) { smp_rmb(); fsg->common->next_buffhd_to_drain = bh->next; @@ -878,7 +938,7 @@ static int do_write(struct fsg_dev *fsg) (unsigned long long) file_offset, (int) nwritten); if (signal_pending(current)) - return -EINTR; // Interrupted! + return -EINTR; /* Interrupted! */ if (nwritten < 0) { LDBG(curlun, "error in file write: %d\n", @@ -888,7 +948,7 @@ static int do_write(struct fsg_dev *fsg) LDBG(curlun, "partial file write: %d/%u\n", (int) nwritten, amount); nwritten -= (nwritten & 511); - // Round down to a block + /* Round down to a block */ } file_offset += nwritten; amount_left_to_write -= nwritten; @@ -916,7 +976,7 @@ static int do_write(struct fsg_dev *fsg) return rc; } - return -EIO; // No default reply + return -EIO; /* No default reply */ } @@ -976,7 +1036,7 @@ static int do_verify(struct fsg_dev *fsg) verification_length = get_unaligned_be16(&fsg->common->cmnd[7]); if (unlikely(verification_length == 0)) - return -EIO; // No default reply + return -EIO; /* No default reply */ /* Prepare to carry out the file verify */ amount_left = verification_length << 9; @@ -1029,7 +1089,7 @@ static int do_verify(struct fsg_dev *fsg) } else if (nread < amount) { LDBG(curlun, "partial file verify: %d/%u\n", (int) nread, amount); - nread -= (nread & 511); // Round down to a sector + nread -= (nread & 511); /* Round down to a sector */ } if (nread == 0) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; @@ -1054,17 +1114,17 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) if (!curlun) { /* Unsupported LUNs are okay */ fsg->bad_lun_okay = 1; memset(buf, 0, 36); - buf[0] = 0x7f; // Unsupported, no device-type - buf[4] = 31; // Additional length + buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ return 36; } buf[0] = curlun->cdrom ? TYPE_CDROM : TYPE_DISK; buf[1] = curlun->removable ? 0x80 : 0; - buf[2] = 2; // ANSI SCSI level 2 - buf[3] = 2; // SCSI-2 INQUIRY data format - buf[4] = 31; // Additional length - buf[5] = 0; // No special options + buf[2] = 2; /* ANSI SCSI level 2 */ + buf[3] = 2; /* SCSI-2 INQUIRY data format */ + buf[4] = 31; /* Additional length */ + buf[5] = 0; /* No special options */ buf[6] = 0; buf[7] = 0; memcpy(buf + 8, fsg->common->inquiry_string, @@ -1102,7 +1162,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) } #endif - if (!curlun) { // Unsupported LUNs are okay + if (!curlun) { /* Unsupported LUNs are okay */ fsg->bad_lun_okay = 1; sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; sdinfo = 0; @@ -1117,10 +1177,10 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) } memset(buf, 0, 18); - buf[0] = valid | 0x70; // Valid, current error + buf[0] = valid | 0x70; /* Valid, current error */ buf[2] = SK(sd); put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ - buf[7] = 18 - 8; // Additional sense length + buf[7] = 18 - 8; /* Additional sense length */ buf[12] = ASC(sd); buf[13] = ASCQ(sd); return 18; @@ -1209,7 +1269,7 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) int valid_page = 0; int len, limit; - if ((fsg->common->cmnd[1] & ~0x08) != 0) { // Mask away DBD + if ((fsg->common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -1228,13 +1288,13 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) * the mode data length later. */ memset(buf, 0, 8); if (mscmnd == SC_MODE_SENSE_6) { - buf[2] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA + buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ buf += 4; limit = 255; - } else { // SC_MODE_SENSE_10 - buf[3] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA + } else { /* SC_MODE_SENSE_10 */ + buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ buf += 8; - limit = 65535; // Should really be FSG_BUFLEN + limit = 65535; /* Should really be FSG_BUFLEN */ } /* No block descriptors */ @@ -1243,14 +1303,14 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) * is the Caching page. */ if (page_code == 0x08 || all_pages) { valid_page = 1; - buf[0] = 0x08; // Page code - buf[1] = 10; // Page length - memset(buf+2, 0, 10); // None of the fields are changeable + buf[0] = 0x08; /* Page code */ + buf[1] = 10; /* Page length */ + memset(buf+2, 0, 10); /* None of the fields are changeable */ if (!changeable_values) { - buf[2] = 0x04; // Write cache enable, - // Read cache not disabled - // No cache retention priorities + buf[2] = 0x04; /* Write cache enable, */ + /* Read cache not disabled */ + /* No cache retention priorities */ put_unaligned_be16(0xffff, &buf[4]); /* Don't disable prefetch */ /* Minimum prefetch = 0 */ @@ -1304,7 +1364,7 @@ static int do_prevent_allow(struct fsg_dev *fsg) } prevent = fsg->common->cmnd[4] & 0x01; - if ((fsg->common->cmnd[4] & ~0x01) != 0) { // Mask away Prevent + if ((fsg->common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -1323,7 +1383,7 @@ static int do_read_format_capacities(struct fsg_dev *fsg, u8 *buf = (u8 *) bh->buf; buf[0] = buf[1] = buf[2] = 0; - buf[3] = 8; // Only the Current/Maximum Capacity Descriptor + buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ buf += 4; put_unaligned_be32(curlun->num_sectors, &buf[0]); @@ -1398,7 +1458,7 @@ static int pad_with_zeros(struct fsg_dev *fsg) u32 nsend; int rc; - bh->state = BUF_STATE_EMPTY; // For the first iteration + bh->state = BUF_STATE_EMPTY; /* For the first iteration */ fsg->usb_amount_left = nkeep + fsg->residue; while (fsg->usb_amount_left > 0) { @@ -1454,8 +1514,8 @@ static int throw_away_data(struct fsg_dev *fsg) /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); @@ -1480,7 +1540,7 @@ static int finish_reply(struct fsg_dev *fsg) switch (fsg->data_dir) { case DATA_DIR_NONE: - break; // Nothing to send + break; /* Nothing to send */ /* If we don't know whether the host wants to read or write, * this must be CB or CBI with an unknown command. We mustn't @@ -1522,14 +1582,13 @@ static int finish_reply(struct fsg_dev *fsg) /* We have processed all we want from the data the host has sent. * There may still be outstanding bulk-out requests. */ case DATA_DIR_FROM_HOST: - if (fsg->residue == 0) - ; // Nothing to receive + if (fsg->residue == 0) { + /* Nothing to receive */ /* Did the host stop sending unexpectedly early? */ - else if (fsg->short_packet_received) { + } else if (fsg->short_packet_received) { raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); rc = -EINTR; - } /* We haven't processed all the incoming data. Even though * we may be allowed to stall, doing so would cause a race. @@ -1538,17 +1597,17 @@ static int finish_reply(struct fsg_dev *fsg) * STALL. Not realizing the endpoint was halted, it wouldn't * clear the halt -- leading to problems later on. */ #if 0 - else if (fsg->can_stall) { + } else if (fsg->can_stall) { fsg_set_halt(fsg, fsg->bulk_out); raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); rc = -EINTR; - } #endif /* We can't stall. Read in the excess data and throw it * all away. */ - else + } else { rc = throw_away_data(fsg); + } break; } return rc; @@ -1593,7 +1652,7 @@ static int send_status(struct fsg_dev *fsg) } /* Store and send the Bulk-only CSW */ - csw = (void*)bh->buf; + csw = (void *)bh->buf; csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); csw->Tag = fsg->tag; @@ -1629,18 +1688,18 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], fsg->data_size); VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", - name, cmnd_size, dirletter[(int) data_dir], - fsg->data_size_from_cmnd, fsg->common->cmnd_size, hdlen); + name, cmnd_size, dirletter[(int) data_dir], + fsg->data_size_from_cmnd, fsg->common->cmnd_size, hdlen); /* We can't reply at all until we know the correct data direction * and size. */ if (fsg->data_size_from_cmnd == 0) data_dir = DATA_DIR_NONE; - if (fsg->data_dir == DATA_DIR_UNKNOWN) { // CB or CBI + if (fsg->data_dir == DATA_DIR_UNKNOWN) { /* CB or CBI */ fsg->data_dir = data_dir; fsg->data_size = fsg->data_size_from_cmnd; - } else { // Bulk-only + } else { /* Bulk-only */ if (fsg->data_size < fsg->data_size_from_cmnd) { /* Host data size < Device data size is a phase error. @@ -1691,7 +1750,8 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, /* Check the LUN */ if (fsg->common->lun >= 0 && fsg->common->lun < fsg->common->nluns) { - fsg->common->curlun = curlun = &fsg->common->luns[fsg->common->lun]; + curlun = &fsg->common->luns[fsg->common->lun]; + fsg->common->curlun = curlun; if (fsg->common->cmnd[0] != SC_REQUEST_SENSE) { curlun->sense_data = SS_NO_SENSE; curlun->sense_data_info = 0; @@ -1721,7 +1781,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, } /* Check that only command bytes listed in the mask are non-zero */ - fsg->common->cmnd[1] &= 0x1f; // Mask away the LUN + fsg->common->cmnd[1] &= 0x1f; /* Mask away the LUN */ for (i = 1; i < cmnd_size; ++i) { if (fsg->common->cmnd[i] && !(mask & (1 << i))) { if (curlun) @@ -1752,7 +1812,8 @@ static int do_scsi_command(struct fsg_dev *fsg) dump_cdb(fsg->common); /* Wait for the next buffer to become available for data or status */ - bh = fsg->common->next_buffhd_to_drain = fsg->common->next_buffhd_to_fill; + bh = fsg->common->next_buffhd_to_fill; + fsg->common->next_buffhd_to_drain = bh; while (bh->state != BUF_STATE_EMPTY) { rc = sleep_thread(fsg); if (rc) @@ -1761,141 +1822,163 @@ static int do_scsi_command(struct fsg_dev *fsg) fsg->phase_error = 0; fsg->short_packet_received = 0; - down_read(&fsg->common->filesem); // We're using the backing file + /* We're using the backing file */ + down_read(&fsg->common->filesem); switch (fsg->common->cmnd[0]) { case SC_INQUIRY: fsg->data_size_from_cmnd = fsg->common->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<4), 0, - "INQUIRY")) == 0) + reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) reply = do_inquiry(fsg, bh); break; case SC_MODE_SELECT_6: fsg->data_size_from_cmnd = fsg->common->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, - (1<<1) | (1<<4), 0, - "MODE SELECT(6)")) == 0) + reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) reply = do_mode_select(fsg, bh); break; case SC_MODE_SELECT_10: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, - (1<<1) | (3<<7), 0, - "MODE SELECT(10)")) == 0) + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->common->cmnd[7]); + reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) reply = do_mode_select(fsg, bh); break; case SC_MODE_SENSE_6: fsg->data_size_from_cmnd = fsg->common->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<1) | (1<<2) | (1<<4), 0, - "MODE SENSE(6)")) == 0) + reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) reply = do_mode_sense(fsg, bh); break; case SC_MODE_SENSE_10: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (1<<1) | (1<<2) | (3<<7), 0, - "MODE SENSE(10)")) == 0) + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->common->cmnd[7]); + reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) reply = do_mode_sense(fsg, bh); break; case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 6, DATA_DIR_NONE, - (1<<4), 0, - "PREVENT-ALLOW MEDIUM REMOVAL")) == 0) + reply = check_command(fsg, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) reply = do_prevent_allow(fsg); break; case SC_READ_6: i = fsg->common->cmnd[4]; fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (7<<1) | (1<<4), 1, - "READ(6)")) == 0) + reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) reply = do_read(fsg); break; case SC_READ_10: fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]) << 9; - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) | (3<<7), 1, - "READ(10)")) == 0) + reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) reply = do_read(fsg); break; case SC_READ_12: fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->common->cmnd[6]) << 9; - if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) | (0xf<<6), 1, - "READ(12)")) == 0) + reply = check_command(fsg, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) reply = do_read(fsg); break; case SC_READ_CAPACITY: fsg->data_size_from_cmnd = 8; - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (0xf<<2) | (1<<8), 1, - "READ CAPACITY")) == 0) + reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) reply = do_read_capacity(fsg, bh); break; case SC_READ_HEADER: if (!fsg->common->curlun || !fsg->common->curlun->cdrom) goto unknown_cmnd; - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (3<<7) | (0x1f<<1), 1, - "READ HEADER")) == 0) + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->common->cmnd[7]); + reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) reply = do_read_header(fsg, bh); break; case SC_READ_TOC: if (!fsg->common->curlun || !fsg->common->curlun->cdrom) goto unknown_cmnd; - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (7<<6) | (1<<1), 1, - "READ TOC")) == 0) + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->common->cmnd[7]); + reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) reply = do_read_toc(fsg, bh); break; case SC_READ_FORMAT_CAPACITIES: - fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (3<<7), 1, - "READ FORMAT CAPACITIES")) == 0) + fsg->data_size_from_cmnd = + get_unaligned_be16(&fsg->common->cmnd[7]); + reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) reply = do_read_format_capacities(fsg, bh); break; case SC_REQUEST_SENSE: fsg->data_size_from_cmnd = fsg->common->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<4), 0, - "REQUEST SENSE")) == 0) + reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) reply = do_request_sense(fsg, bh); break; case SC_START_STOP_UNIT: fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 6, DATA_DIR_NONE, - (1<<1) | (1<<4), 0, - "START-STOP UNIT")) == 0) + reply = check_command(fsg, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) reply = do_start_stop(fsg); break; case SC_SYNCHRONIZE_CACHE: fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 10, DATA_DIR_NONE, - (0xf<<2) | (3<<7), 1, - "SYNCHRONIZE CACHE")) == 0) + reply = check_command(fsg, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) reply = do_synchronize_cache(fsg); break; @@ -1910,36 +1993,40 @@ static int do_scsi_command(struct fsg_dev *fsg) * support a minimal version: BytChk must be 0. */ case SC_VERIFY: fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 10, DATA_DIR_NONE, - (1<<1) | (0xf<<2) | (3<<7), 1, - "VERIFY")) == 0) + reply = check_command(fsg, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) reply = do_verify(fsg); break; case SC_WRITE_6: i = fsg->common->cmnd[4]; fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; - if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, - (7<<1) | (1<<4), 1, - "WRITE(6)")) == 0) + reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) reply = do_write(fsg); break; case SC_WRITE_10: fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->common->cmnd[7]) << 9; - if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, - (1<<1) | (0xf<<2) | (3<<7), 1, - "WRITE(10)")) == 0) + reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) reply = do_write(fsg); break; case SC_WRITE_12: fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->common->cmnd[6]) << 9; - if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, - (1<<1) | (0xf<<2) | (0xf<<6), 1, - "WRITE(12)")) == 0) + reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) reply = do_write(fsg); break; @@ -1951,14 +2038,15 @@ static int do_scsi_command(struct fsg_dev *fsg) case SC_RELEASE: case SC_RESERVE: case SC_SEND_DIAGNOSTIC: - // Fall through + /* Fall through */ default: - unknown_cmnd: +unknown_cmnd: fsg->data_size_from_cmnd = 0; sprintf(unknown, "Unknown x%02x", fsg->common->cmnd[0]); - if ((reply = check_command(fsg, fsg->common->cmnd_size, - DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) { + reply = check_command(fsg, fsg->common->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown); + if (reply == 0) { fsg->common->curlun->sense_data = SS_INVALID_COMMAND; reply = -EINVAL; } @@ -1971,13 +2059,13 @@ static int do_scsi_command(struct fsg_dev *fsg) /* Set up the single reply buffer for finish_reply() */ if (reply == -EINVAL) - reply = 0; // Error reply length + reply = 0; /* Error reply length */ if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { reply = min((u32) reply, fsg->data_size_from_cmnd); bh->inreq->length = reply; bh->state = BUF_STATE_FULL; fsg->residue -= reply; - } // Otherwise it's already set + } /* Otherwise it's already set */ return 0; } @@ -2157,13 +2245,15 @@ reset: /* Enable the endpoints */ d = fsg_ep_desc(fsg->gadget, &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); - if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) + rc = enable_endpoint(fsg, fsg->bulk_in, d); + if (rc != 0) goto reset; fsg->bulk_in_enabled = 1; d = fsg_ep_desc(fsg->gadget, &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); - if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) + rc = enable_endpoint(fsg, fsg->bulk_out, d); + if (rc != 0) goto reset; fsg->bulk_out_enabled = 1; fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); @@ -2173,9 +2263,11 @@ reset: for (i = 0; i < FSG_NUM_BUFFERS; ++i) { struct fsg_buffhd *bh = &fsg->common->buffhds[i]; - if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) + rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq); + if (rc != 0) goto reset; - if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0) + rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq); + if (rc != 0) goto reset; bh->inreq->buf = bh->outreq->buf = bh->buf; bh->inreq->context = bh->outreq->context = bh; @@ -2302,9 +2394,8 @@ static void handle_exception(struct fsg_dev *fsg) bh = &fsg->common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } - fsg->common->next_buffhd_to_fill = fsg->common->next_buffhd_to_drain = - &fsg->common->buffhds[0]; - + fsg->common->next_buffhd_to_fill = &fsg->common->buffhds[0]; + fsg->common->next_buffhd_to_drain = &fsg->common->buffhds[0]; exception_req_tag = fsg->exception_req_tag; new_config = fsg->new_config; old_state = fsg->state; @@ -2315,8 +2406,8 @@ static void handle_exception(struct fsg_dev *fsg) for (i = 0; i < fsg->common->nluns; ++i) { curlun = &fsg->common->luns[i]; curlun->prevent_medium_removal = 0; - curlun->sense_data = curlun->unit_attention_data = - SS_NO_SENSE; + curlun->sense_data = SS_NO_SENSE; + curlun->unit_attention_data = SS_NO_SENSE; curlun->sense_data_info = 0; curlun->info_valid = 0; } @@ -2342,30 +2433,31 @@ static void handle_exception(struct fsg_dev *fsg) usb_ep_clear_halt(fsg->bulk_in); if (fsg->ep0_req_tag == exception_req_tag) - ep0_queue(fsg); // Complete the status stage + ep0_queue(fsg); /* Complete the status stage */ /* Technically this should go here, but it would only be * a waste of time. Ditto for the INTERFACE_CHANGE and * CONFIG_CHANGE cases. */ - // for (i = 0; i < fsg->common->nluns; ++i) - // fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; + /* for (i = 0; i < fsg->common->nluns; ++i) */ + /* fsg->common->luns[i].unit_attention_data = */ + /* SS_RESET_OCCURRED; */ break; case FSG_STATE_CONFIG_CHANGE: rc = do_set_config(fsg, new_config); if (fsg->ep0_req_tag != exception_req_tag) break; - if (rc != 0) // STALL on errors + if (rc != 0) /* STALL on errors */ fsg_set_halt(fsg, fsg->ep0); - else // Complete the status stage + else /* Complete the status stage */ ep0_queue(fsg); break; case FSG_STATE_EXIT: case FSG_STATE_TERMINATED: - do_set_config(fsg, 0); // Free resources + do_set_config(fsg, 0); /* Free resources */ spin_lock_irq(&fsg->lock); - fsg->state = FSG_STATE_TERMINATED; // Stop the thread + fsg->state = FSG_STATE_TERMINATED; /* Stop the thread */ spin_unlock_irq(&fsg->lock); break; @@ -2645,7 +2737,8 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, error_luns: common->nluns = i + 1; error_release: - /* Call fsg_common_release() directly, ref is not initialised */ + /* Call fsg_common_release() directly, ref might be not + * initialised */ fsg_common_release(&common->ref); return ERR_PTR(rc); } @@ -2721,13 +2814,13 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); if (!ep) goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint + ep->driver_data = fsg; /* claim the endpoint */ fsg->bulk_in = ep; ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); if (!ep) goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint + ep->driver_data = fsg; /* claim the endpoint */ fsg->bulk_out = ep; if (gadget_is_dualspeed(gadget)) { @@ -2770,7 +2863,7 @@ autoconf_fail: rc = -ENOTSUPP; out: - fsg->state = FSG_STATE_TERMINATED; // The thread is dead + fsg->state = FSG_STATE_TERMINATED; /* The thread is dead */ fsg_unbind(c, f); complete(&fsg->thread_notifier); return rc; @@ -2874,18 +2967,16 @@ fsg_config_from_params(struct fsg_config *cfg, const struct fsg_module_parameters *params) { struct fsg_lun_config *lun; - unsigned i, nluns; + unsigned i; /* Configure LUNs */ - nluns = cfg->nluns = !params->luns - ? params->file_count ? params->file_count : 1 - : params->luns; - for (i = 0, lun = cfg->luns; - i < FSG_MAX_LUNS && i < nluns; - ++i, ++lun) { + cfg->nluns = + min(params->luns ?: (params->file_count ?: 1u), + (unsigned)FSG_MAX_LUNS); + for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) { lun->ro = !!params->ro[i]; lun->cdrom = !!params->cdrom[i]; - lun->removable = + lun->removable = /* Removable by default */ params->removable_count <= i || params->removable[i]; lun->filename = params->file_count > i && params->file[i][0] @@ -2893,7 +2984,7 @@ fsg_config_from_params(struct fsg_config *cfg, : 0; } - /* Let FSG use defaults */ + /* Let MSF use defaults */ cfg->lun_name_format = 0; cfg->thread_name = 0; cfg->vendor_name = 0; diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 00396a36d011..e43ca8f4be14 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -1,10 +1,10 @@ /* - * mass_storage.c -- File-backed USB Storage Gadget, for USB development + * mass_storage.c -- Mass Storage USB Gadget * * Copyright (C) 2003-2008 Alan Stern - * * Copyright (C) 2009 Samsung Electronics * Author: Michal Nazarewicz + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,7 +45,7 @@ /*-------------------------------------------------------------------------*/ #define DRIVER_DESC "Mass Storage Gadget" -#define DRIVER_VERSION "2009/07/21" +#define DRIVER_VERSION "2009/09/11" /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 7e81a2d898f0..868d8ee86756 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -61,8 +61,8 @@ * * DO NOT REUSE THESE IDs with any other driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ -#define FSG_VENDOR_ID 0x0525 // NetChip -#define FSG_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget +#define FSG_VENDOR_ID 0x0525 /* NetChip */ +#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ /*-------------------------------------------------------------------------*/ @@ -103,7 +103,7 @@ #ifdef DUMP_MSGS # define dump_msg(fsg, /* const char * */ label, \ - /* const u8 * */ buf, /* unsigned */ length) do { \ + /* const u8 * */ buf, /* unsigned */ length) do { \ if (length < 512) { \ DBG(fsg, "%s, length %u:\n", label, length); \ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ @@ -116,11 +116,11 @@ #else # define dump_msg(fsg, /* const char * */ label, \ - /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) # ifdef VERBOSE_DEBUG -#define dump_cdb(fsg) \ +# define dump_cdb(fsg) \ print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ @@ -143,45 +143,45 @@ #define TYPE_CDROM 0x05 /* USB protocol value = the transport method */ -#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt -#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt -#define USB_PR_BULK 0x50 // Bulk-only +#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */ +#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */ +#define USB_PR_BULK 0x50 /* Bulk-only */ /* USB subclass value = the protocol encapsulation */ -#define USB_SC_RBC 0x01 // Reduced Block Commands (flash) -#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM) -#define USB_SC_QIC 0x03 // QIC-157 (tape) -#define USB_SC_UFI 0x04 // UFI (floppy) -#define USB_SC_8070 0x05 // SFF-8070i (removable) -#define USB_SC_SCSI 0x06 // Transparent SCSI +#define USB_SC_RBC 0x01 /* Reduced Block Commands (flash) */ +#define USB_SC_8020 0x02 /* SFF-8020i, MMC-2, ATAPI (CD-ROM) */ +#define USB_SC_QIC 0x03 /* QIC-157 (tape) */ +#define USB_SC_UFI 0x04 /* UFI (floppy) */ +#define USB_SC_8070 0x05 /* SFF-8070i (removable) */ +#define USB_SC_SCSI 0x06 /* Transparent SCSI */ /* Bulk-only data structures */ /* Command Block Wrapper */ struct fsg_bulk_cb_wrap { - __le32 Signature; // Contains 'USBC' - u32 Tag; // Unique per command id - __le32 DataTransferLength; // Size of the data - u8 Flags; // Direction in bit 7 - u8 Lun; // LUN (normally 0) - u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE - u8 CDB[16]; // Command Data Block + __le32 Signature; /* Contains 'USBC' */ + u32 Tag; /* Unique per command id */ + __le32 DataTransferLength; /* Size of the data */ + u8 Flags; /* Direction in bit 7 */ + u8 Lun; /* LUN (normally 0) */ + u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */ + u8 CDB[16]; /* Command Data Block */ }; #define USB_BULK_CB_WRAP_LEN 31 -#define USB_BULK_CB_SIG 0x43425355 // Spells out USBC +#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */ #define USB_BULK_IN_FLAG 0x80 /* Command Status Wrapper */ struct bulk_cs_wrap { - __le32 Signature; // Should = 'USBS' - u32 Tag; // Same as original command - __le32 Residue; // Amount not transferred - u8 Status; // See below + __le32 Signature; /* Should = 'USBS' */ + u32 Tag; /* Same as original command */ + __le32 Residue; /* Amount not transferred */ + u8 Status; /* See below */ }; #define USB_BULK_CS_WRAP_LEN 13 -#define USB_BULK_CS_SIG 0x53425355 // Spells out 'USBS' +#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */ #define USB_STATUS_PASS 0 #define USB_STATUS_FAIL 1 #define USB_STATUS_PHASE_ERROR 2 @@ -203,7 +203,8 @@ struct interrupt_data { #define USB_CBI_ADSC_REQUEST 0x00 -#define MAX_COMMAND_SIZE 16 // Length of a SCSI Command Data Block +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 /* SCSI commands that we recognize */ #define SC_FORMAT_UNIT 0x04 @@ -248,7 +249,7 @@ struct interrupt_data { #define SS_WRITE_ERROR 0x030c02 #define SS_WRITE_PROTECTED 0x072700 -#define SK(x) ((u8) ((x) >> 16)) // Sense Key byte, etc. +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ #define ASC(x) ((u8) ((x) >> 8)) #define ASCQ(x) ((u8) (x)) @@ -261,13 +262,13 @@ struct fsg_lun { loff_t file_length; loff_t num_sectors; - unsigned int initially_ro : 1; - unsigned int ro : 1; - unsigned int removable : 1; - unsigned int cdrom : 1; - unsigned int prevent_medium_removal : 1; - unsigned int registered : 1; - unsigned int info_valid : 1; + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; u32 sense_data; u32 sense_data_info; @@ -286,7 +287,7 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) /* Big enough to hold our biggest descriptor */ #define EP0_BUFSIZE 256 -#define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value +#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ /* Number of buffers we will use. 2 is enough for double-buffering */ #define FSG_NUM_BUFFERS 2 @@ -324,7 +325,8 @@ struct fsg_buffhd { }; enum fsg_state { - FSG_STATE_COMMAND_PHASE = -10, // This one isn't used anywhere + /* This one isn't used anywhere */ + FSG_STATE_COMMAND_PHASE = -10, FSG_STATE_DATA_PHASE, FSG_STATE_STATUS_PHASE, @@ -386,10 +388,10 @@ fsg_intf_desc = { .bLength = sizeof fsg_intf_desc, .bDescriptorType = USB_DT_INTERFACE, - .bNumEndpoints = 2, // Adjusted during fsg_bind() + .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind() - .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind() + .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ + .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ .iInterface = FSG_STRING_INTERFACE, }; @@ -426,7 +428,7 @@ fsg_fs_intr_in_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = cpu_to_le16(2), - .bInterval = 32, // frames -> 32 ms + .bInterval = 32, /* frames -> 32 ms */ }; #ifndef FSG_NO_OTG @@ -477,7 +479,7 @@ fsg_hs_bulk_out_desc = { /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), - .bInterval = 1, // NAK every 1 uframe + .bInterval = 1, /* NAK every 1 uframe */ }; #ifndef FSG_NO_INTR_EP @@ -490,7 +492,7 @@ fsg_hs_intr_in_desc = { /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = cpu_to_le16(2), - .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms + .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ }; #ifndef FSG_NO_OTG @@ -538,7 +540,7 @@ static struct usb_string fsg_strings[] = { }; static struct usb_gadget_strings fsg_stringtab = { - .language = 0x0409, // en-us + .language = 0x0409, /* en-us */ .strings = fsg_strings, }; @@ -600,11 +602,11 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) rc = (int) size; goto out; } - num_sectors = size >> 9; // File size in 512-byte blocks + num_sectors = size >> 9; /* File size in 512-byte blocks */ min_sectors = 1; if (curlun->cdrom) { - num_sectors &= ~3; // Reduce to a multiple of 2048 - min_sectors = 300*4; // Smallest track is 300 frames + num_sectors &= ~3; /* Reduce to a multiple of 2048 */ + min_sectors = 300*4; /* Smallest track is 300 frames */ if (num_sectors >= 256*60*75*4) { num_sectors = (256*60*75 - 1) * 4; LINFO(curlun, "file too big: %s\n", filename); @@ -696,17 +698,17 @@ static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, ssize_t rc; down_read(filesem); - if (fsg_lun_is_open(curlun)) { // Get the complete pathname + if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */ p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); if (IS_ERR(p)) rc = PTR_ERR(p); else { rc = strlen(p); memmove(buf, p, rc); - buf[rc] = '\n'; // Add a newline + buf[rc] = '\n'; /* Add a newline */ buf[++rc] = 0; } - } else { // No file, return 0 bytes + } else { /* No file, return 0 bytes */ *buf = 0; rc = 0; } @@ -750,12 +752,12 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { LDBG(curlun, "eject attempt prevented\n"); - return -EBUSY; // "Door is locked" + return -EBUSY; /* "Door is locked" */ } /* Remove a trailing newline */ if (count > 0 && buf[count-1] == '\n') - ((char *) buf)[count-1] = 0; // Ugh! + ((char *) buf)[count-1] = 0; /* Ugh! */ /* Eject current medium */ down_write(filesem); -- cgit v1.2.3 From a061cdd3532424ffcd274721fdfd9c6ff4376c90 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Nov 2009 14:15:24 +0100 Subject: USB: g_mass_storage: most data moved to fsg_common Most of the data from fsg_dev have been moved to fsg_common structure. The fsg_dev structure holds only endpoint dependent data. The fsg_common structure has a fsg pointer which points to active fsg_dev structure -- endpoints are referenced via this pointer. This fixes the problem of several threads created when a single instance of MSF is used in several USB configurations. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 1230 ++++++++++++++++++----------------- 1 file changed, 648 insertions(+), 582 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 5eaf22db7fcc..a6cec37768a9 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -312,14 +312,26 @@ static const char fsg_string_interface[] = "Mass Storage"; /*-------------------------------------------------------------------------*/ +struct fsg_dev; + /* Data shared by all the FSG instances. */ struct fsg_common { struct usb_gadget *gadget; + struct fsg_dev *fsg; + struct fsg_dev *prev_fsg; /* filesem protects: backing files in use */ struct rw_semaphore filesem; + /* lock protects: state, all the req_busy's */ + spinlock_t lock; + + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + const char *ep0req_name; + struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; @@ -332,10 +344,28 @@ struct fsg_common { struct fsg_lun *luns; struct fsg_lun *curlun; + unsigned int bulk_out_maxpacket; + enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + + u8 config, new_config; + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + unsigned int can_stall:1; unsigned int free_storage_on_release:1; + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + unsigned int running:1; - const char *thread_name; + int thread_wakeup_needed; + struct completion thread_notifier; + struct task_struct *thread_task; /* Vendor (8 chars), product (16 chars), release (4 * hexadecimal digits) and NUL byte */ @@ -367,52 +397,32 @@ struct fsg_config { struct fsg_dev { struct usb_function function; - struct usb_composite_dev *cdev; struct usb_gadget *gadget; /* Copy of cdev->gadget */ struct fsg_common *common; u16 interface_number; - /* lock protects: state, all the req_busy's */ - spinlock_t lock; - - struct usb_ep *ep0; /* Copy of gadget->ep0 */ - struct usb_request *ep0req; /* Copy of cdev->req */ - unsigned int ep0_req_tag; - const char *ep0req_name; - - unsigned int bulk_out_maxpacket; - enum fsg_state state; /* For exception handling */ - unsigned int exception_req_tag; - - u8 config, new_config; - - unsigned int running:1; unsigned int bulk_in_enabled:1; unsigned int bulk_out_enabled:1; - unsigned int phase_error:1; - unsigned int short_packet_received:1; - unsigned int bad_lun_okay:1; - unsigned int can_stall:1; unsigned long atomic_bitflags; -#define REGISTERED 0 -#define IGNORE_BULK_OUT 1 +#define IGNORE_BULK_OUT 0 struct usb_ep *bulk_in; struct usb_ep *bulk_out; +}; - int thread_wakeup_needed; - struct completion thread_notifier; - struct task_struct *thread_task; - enum data_direction data_dir; - u32 data_size; - u32 data_size_from_cmnd; - u32 tag; - u32 residue; - u32 usb_amount_left; -}; +static inline int __fsg_is_set(struct fsg_common *common, + const char *func, unsigned line) +{ + if (common->fsg) + return 1; + ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) static inline struct fsg_dev *fsg_from_func(struct usb_function *f) @@ -423,21 +433,21 @@ static inline struct fsg_dev *fsg_from_func(struct usb_function *f) typedef void (*fsg_routine_t)(struct fsg_dev *); -static int exception_in_progress(struct fsg_dev *fsg) +static int exception_in_progress(struct fsg_common *common) { - return (fsg->state > FSG_STATE_IDLE); + return common->state > FSG_STATE_IDLE; } /* Make bulk-out requests be divisible by the maxpacket size */ -static void set_bulk_out_req_length(struct fsg_dev *fsg, +static void set_bulk_out_req_length(struct fsg_common *common, struct fsg_buffhd *bh, unsigned int length) { unsigned int rem; bh->bulk_out_intended_length = length; - rem = length % fsg->bulk_out_maxpacket; + rem = length % common->bulk_out_maxpacket; if (rem > 0) - length += fsg->bulk_out_maxpacket - rem; + length += common->bulk_out_maxpacket - rem; bh->outreq->length = length; } @@ -463,47 +473,46 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) /* These routines may be called in process context or in_irq */ /* Caller must hold fsg->lock */ -static void wakeup_thread(struct fsg_dev *fsg) +static void wakeup_thread(struct fsg_common *common) { /* Tell the main thread that something has happened */ - fsg->thread_wakeup_needed = 1; - if (fsg->thread_task) - wake_up_process(fsg->thread_task); + common->thread_wakeup_needed = 1; + if (common->thread_task) + wake_up_process(common->thread_task); } -static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) { unsigned long flags; /* Do nothing if a higher-priority exception is already in progress. * If a lower-or-equal priority exception is in progress, preempt it * and notify the main thread by sending it a signal. */ - spin_lock_irqsave(&fsg->lock, flags); - if (fsg->state <= new_state) { - fsg->exception_req_tag = fsg->ep0_req_tag; - fsg->state = new_state; - if (fsg->thread_task) + spin_lock_irqsave(&common->lock, flags); + if (common->state <= new_state) { + common->exception_req_tag = common->ep0_req_tag; + common->state = new_state; + if (common->thread_task) send_sig_info(SIGUSR1, SEND_SIG_FORCED, - fsg->thread_task); + common->thread_task); } - spin_unlock_irqrestore(&fsg->lock, flags); + spin_unlock_irqrestore(&common->lock, flags); } /*-------------------------------------------------------------------------*/ -static int ep0_queue(struct fsg_dev *fsg) +static int ep0_queue(struct fsg_common *common) { int rc; - rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); - fsg->ep0->driver_data = fsg; + rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); + common->ep0->driver_data = common; if (rc != 0 && rc != -ESHUTDOWN) { - /* We can't do much more than wait for a reset */ - WARNING(fsg, "error in submission: %s --> %d\n", - fsg->ep0->name, rc); + WARNING(common, "error in submission: %s --> %d\n", + common->ep0->name, rc); } return rc; } @@ -515,32 +524,32 @@ static int ep0_queue(struct fsg_dev *fsg) static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) { - struct fsg_dev *fsg = ep->driver_data; + struct fsg_common *common = ep->driver_data; struct fsg_buffhd *bh = req->context; if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, + DBG(common, "%s --> %d, %u/%u\n", __func__, req->status, req->actual, req->length); if (req->status == -ECONNRESET) /* Request was cancelled */ usb_ep_fifo_flush(ep); /* Hold the lock while we update the request and buffer states */ smp_wmb(); - spin_lock(&fsg->lock); + spin_lock(&common->lock); bh->inreq_busy = 0; bh->state = BUF_STATE_EMPTY; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); + wakeup_thread(common); + spin_unlock(&common->lock); } static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) { - struct fsg_dev *fsg = ep->driver_data; + struct fsg_common *common = ep->driver_data; struct fsg_buffhd *bh = req->context; - dump_msg(fsg, "bulk-out", req->buf, req->actual); + dump_msg(common, "bulk-out", req->buf, req->actual); if (req->status || req->actual != bh->bulk_out_intended_length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, + DBG(common, "%s --> %d, %u/%u\n", __func__, req->status, req->actual, bh->bulk_out_intended_length); if (req->status == -ECONNRESET) /* Request was cancelled */ @@ -548,11 +557,11 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) /* Hold the lock while we update the request and buffer states */ smp_wmb(); - spin_lock(&fsg->lock); + spin_lock(&common->lock); bh->outreq_busy = 0; bh->state = BUF_STATE_FULL; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); + wakeup_thread(common); + spin_unlock(&common->lock); } @@ -564,12 +573,12 @@ static int fsg_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct fsg_dev *fsg = fsg_from_func(f); - struct usb_request *req = fsg->ep0req; + struct usb_request *req = fsg->common->ep0req; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); - if (!fsg->config) + if (!fsg->common->config) return -EOPNOTSUPP; switch (ctrl->bRequest) { @@ -584,7 +593,7 @@ static int fsg_setup(struct usb_function *f, /* Raise an exception to stop the current operation * and reinitialize our state. */ DBG(fsg, "bulk reset request\n"); - raise_exception(fsg, FSG_STATE_RESET); + raise_exception(fsg->common, FSG_STATE_RESET); return DELAYED_STATUS; case USB_BULK_GET_MAX_LUN_REQUEST: @@ -622,10 +631,10 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, if (ep == fsg->bulk_in) dump_msg(fsg, "bulk-in", req->buf, req->length); - spin_lock_irq(&fsg->lock); + spin_lock_irq(&fsg->common->lock); *pbusy = 1; *state = BUF_STATE_BUSY; - spin_unlock_irq(&fsg->lock); + spin_unlock_irq(&fsg->common->lock); rc = usb_ep_queue(ep, req, GFP_KERNEL); if (rc != 0) { *pbusy = 0; @@ -642,8 +651,18 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, } } +#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \ + if (fsg_is_set(common)) \ + start_transfer((common)->fsg, (common)->fsg->ep_name, \ + req, pbusy, state); \ + else + +#define START_TRANSFER(common, ep_name, req, pbusy, state) \ + START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 + + -static int sleep_thread(struct fsg_dev *fsg) +static int sleep_thread(struct fsg_common *common) { int rc = 0; @@ -655,21 +674,21 @@ static int sleep_thread(struct fsg_dev *fsg) rc = -EINTR; break; } - if (fsg->thread_wakeup_needed) + if (common->thread_wakeup_needed) break; schedule(); } __set_current_state(TASK_RUNNING); - fsg->thread_wakeup_needed = 0; + common->thread_wakeup_needed = 0; return rc; } /*-------------------------------------------------------------------------*/ -static int do_read(struct fsg_dev *fsg) +static int do_read(struct fsg_common *common) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; u32 lba; struct fsg_buffhd *bh; int rc; @@ -681,15 +700,15 @@ static int do_read(struct fsg_dev *fsg) /* Get the starting Logical Block Address and check that it's * not too big */ - if (fsg->common->cmnd[0] == SC_READ_6) - lba = get_unaligned_be24(&fsg->common->cmnd[1]); + if (common->cmnd[0] == SC_READ_6) + lba = get_unaligned_be24(&common->cmnd[1]); else { - lba = get_unaligned_be32(&fsg->common->cmnd[2]); + lba = get_unaligned_be32(&common->cmnd[2]); /* We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = don't read from the * cache), but we don't implement them. */ - if ((fsg->common->cmnd[1] & ~0x18) != 0) { + if ((common->cmnd[1] & ~0x18) != 0) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -701,7 +720,7 @@ static int do_read(struct fsg_dev *fsg) file_offset = ((loff_t) lba) << 9; /* Carry out the file reads */ - amount_left = fsg->data_size_from_cmnd; + amount_left = common->data_size_from_cmnd; if (unlikely(amount_left == 0)) return -EIO; /* No default reply */ @@ -724,9 +743,9 @@ static int do_read(struct fsg_dev *fsg) partial_page); /* Wait for the next buffer to become available */ - bh = fsg->common->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } @@ -765,7 +784,7 @@ static int do_read(struct fsg_dev *fsg) } file_offset += nread; amount_left -= nread; - fsg->residue -= nread; + common->residue -= nread; bh->inreq->length = nread; bh->state = BUF_STATE_FULL; @@ -782,9 +801,12 @@ static int do_read(struct fsg_dev *fsg) /* Send this buffer and go read some more */ bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->common->next_buffhd_to_fill = bh->next; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; } return -EIO; /* No default reply */ @@ -793,9 +815,9 @@ static int do_read(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int do_write(struct fsg_dev *fsg) +static int do_write(struct fsg_common *common) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; u32 lba; struct fsg_buffhd *bh; int get_some_more; @@ -816,20 +838,20 @@ static int do_write(struct fsg_dev *fsg) /* Get the starting Logical Block Address and check that it's * not too big */ - if (fsg->common->cmnd[0] == SC_WRITE_6) - lba = get_unaligned_be24(&fsg->common->cmnd[1]); + if (common->cmnd[0] == SC_WRITE_6) + lba = get_unaligned_be24(&common->cmnd[1]); else { - lba = get_unaligned_be32(&fsg->common->cmnd[2]); + lba = get_unaligned_be32(&common->cmnd[2]); /* We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = write directly to the * medium). We don't implement DPO; we implement FUA by * performing synchronous output. */ - if ((fsg->common->cmnd[1] & ~0x18) != 0) { + if (common->cmnd[1] & ~0x18) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - if (fsg->common->cmnd[1] & 0x08) { /* FUA */ + if (common->cmnd[1] & 0x08) { /* FUA */ spin_lock(&curlun->filp->f_lock); curlun->filp->f_flags |= O_SYNC; spin_unlock(&curlun->filp->f_lock); @@ -843,12 +865,13 @@ static int do_write(struct fsg_dev *fsg) /* Carry out the file writes */ get_some_more = 1; file_offset = usb_offset = ((loff_t) lba) << 9; - amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; + amount_left_to_req = common->data_size_from_cmnd; + amount_left_to_write = common->data_size_from_cmnd; while (amount_left_to_write > 0) { /* Queue a request for more data from the host */ - bh = fsg->common->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; if (bh->state == BUF_STATE_EMPTY && get_some_more) { /* Figure out how much we want to get: @@ -887,7 +910,7 @@ static int do_write(struct fsg_dev *fsg) /* Get the next buffer */ usb_offset += amount; - fsg->usb_amount_left -= amount; + common->usb_amount_left -= amount; amount_left_to_req -= amount; if (amount_left_to_req == 0) get_some_more = 0; @@ -897,19 +920,22 @@ static int do_write(struct fsg_dev *fsg) bh->outreq->length = amount; bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - fsg->common->next_buffhd_to_fill = bh->next; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; continue; } /* Write the received data to the backing file */ - bh = fsg->common->next_buffhd_to_drain; + bh = common->next_buffhd_to_drain; if (bh->state == BUF_STATE_EMPTY && !get_some_more) break; /* We stopped early */ if (bh->state == BUF_STATE_FULL) { smp_rmb(); - fsg->common->next_buffhd_to_drain = bh->next; + common->next_buffhd_to_drain = bh->next; bh->state = BUF_STATE_EMPTY; /* Did something go wrong with the transfer? */ @@ -952,7 +978,7 @@ static int do_write(struct fsg_dev *fsg) } file_offset += nwritten; amount_left_to_write -= nwritten; - fsg->residue -= nwritten; + common->residue -= nwritten; /* If an error occurred, report it and its position */ if (nwritten < amount) { @@ -964,14 +990,14 @@ static int do_write(struct fsg_dev *fsg) /* Did the host decide to stop early? */ if (bh->outreq->actual != bh->outreq->length) { - fsg->short_packet_received = 1; + common->short_packet_received = 1; break; } continue; } /* Wait for something to happen */ - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } @@ -982,9 +1008,9 @@ static int do_write(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int do_synchronize_cache(struct fsg_dev *fsg) +static int do_synchronize_cache(struct fsg_common *common) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; int rc; /* We ignore the requested LBA and write out all file's @@ -1008,12 +1034,12 @@ static void invalidate_sub(struct fsg_lun *curlun) VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc); } -static int do_verify(struct fsg_dev *fsg) +static int do_verify(struct fsg_common *common) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; u32 lba; u32 verification_length; - struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; loff_t file_offset, file_offset_tmp; u32 amount_left; unsigned int amount; @@ -1021,7 +1047,7 @@ static int do_verify(struct fsg_dev *fsg) /* Get the starting Logical Block Address and check that it's * not too big */ - lba = get_unaligned_be32(&fsg->common->cmnd[2]); + lba = get_unaligned_be32(&common->cmnd[2]); if (lba >= curlun->num_sectors) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; @@ -1029,12 +1055,12 @@ static int do_verify(struct fsg_dev *fsg) /* We allow DPO (Disable Page Out = don't save data in the * cache) but we don't implement it. */ - if ((fsg->common->cmnd[1] & ~0x10) != 0) { + if (common->cmnd[1] & ~0x10) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - verification_length = get_unaligned_be16(&fsg->common->cmnd[7]); + verification_length = get_unaligned_be16(&common->cmnd[7]); if (unlikely(verification_length == 0)) return -EIO; /* No default reply */ @@ -1106,13 +1132,13 @@ static int do_verify(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; u8 *buf = (u8 *) bh->buf; if (!curlun) { /* Unsupported LUNs are okay */ - fsg->bad_lun_okay = 1; + common->bad_lun_okay = 1; memset(buf, 0, 36); buf[0] = 0x7f; /* Unsupported, no device-type */ buf[4] = 31; /* Additional length */ @@ -1127,15 +1153,14 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) buf[5] = 0; /* No special options */ buf[6] = 0; buf[7] = 0; - memcpy(buf + 8, fsg->common->inquiry_string, - sizeof fsg->common->inquiry_string); + memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string); return 36; } -static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; u8 *buf = (u8 *) bh->buf; u32 sd, sdinfo; int valid; @@ -1163,7 +1188,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) #endif if (!curlun) { /* Unsupported LUNs are okay */ - fsg->bad_lun_okay = 1; + common->bad_lun_okay = 1; sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; sdinfo = 0; valid = 0; @@ -1187,11 +1212,11 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) } -static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->common->curlun; - u32 lba = get_unaligned_be32(&fsg->common->cmnd[2]); - int pmi = fsg->common->cmnd[8]; + struct fsg_lun *curlun = common->curlun; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + int pmi = common->cmnd[8]; u8 *buf = (u8 *) bh->buf; /* Check the PMI and LBA fields */ @@ -1207,14 +1232,14 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) } -static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->common->curlun; - int msf = fsg->common->cmnd[1] & 0x02; - u32 lba = get_unaligned_be32(&fsg->common->cmnd[2]); + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&common->cmnd[2]); u8 *buf = (u8 *) bh->buf; - if ((fsg->common->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ + if (common->cmnd[1] & ~0x02) { /* Mask away MSF */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -1230,14 +1255,14 @@ static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) } -static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->common->curlun; - int msf = fsg->common->cmnd[1] & 0x02; - int start_track = fsg->common->cmnd[6]; + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + int start_track = common->cmnd[6]; u8 *buf = (u8 *) bh->buf; - if ((fsg->common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ start_track > 1) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; @@ -1258,10 +1283,10 @@ static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) } -static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->common->curlun; - int mscmnd = fsg->common->cmnd[0]; + struct fsg_lun *curlun = common->curlun; + int mscmnd = common->cmnd[0]; u8 *buf = (u8 *) bh->buf; u8 *buf0 = buf; int pc, page_code; @@ -1269,12 +1294,12 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) int valid_page = 0; int len, limit; - if ((fsg->common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - pc = fsg->common->cmnd[2] >> 6; - page_code = fsg->common->cmnd[2] & 0x3f; + pc = common->cmnd[2] >> 6; + page_code = common->cmnd[2] & 0x3f; if (pc == 3) { curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; return -EINVAL; @@ -1339,32 +1364,32 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) } -static int do_start_stop(struct fsg_dev *fsg) +static int do_start_stop(struct fsg_common *common) { - if (!fsg->common->curlun) { + if (!common->curlun) { return -EINVAL; - } else if (!fsg->common->curlun->removable) { - fsg->common->curlun->sense_data = SS_INVALID_COMMAND; + } else if (!common->curlun->removable) { + common->curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; } return 0; } -static int do_prevent_allow(struct fsg_dev *fsg) +static int do_prevent_allow(struct fsg_common *common) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; int prevent; - if (!fsg->common->curlun) { + if (!common->curlun) { return -EINVAL; - } else if (!fsg->common->curlun->removable) { - fsg->common->curlun->sense_data = SS_INVALID_COMMAND; + } else if (!common->curlun->removable) { + common->curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; } - prevent = fsg->common->cmnd[4] & 0x01; - if ((fsg->common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + prevent = common->cmnd[4] & 0x01; + if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -1376,10 +1401,10 @@ static int do_prevent_allow(struct fsg_dev *fsg) } -static int do_read_format_capacities(struct fsg_dev *fsg, +static int do_read_format_capacities(struct fsg_common *common, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; u8 *buf = (u8 *) bh->buf; buf[0] = buf[1] = buf[2] = 0; @@ -1394,12 +1419,13 @@ static int do_read_format_capacities(struct fsg_dev *fsg, } -static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; /* We don't support MODE SELECT */ - curlun->sense_data = SS_INVALID_COMMAND; + if (curlun) + curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; } @@ -1459,73 +1485,78 @@ static int pad_with_zeros(struct fsg_dev *fsg) int rc; bh->state = BUF_STATE_EMPTY; /* For the first iteration */ - fsg->usb_amount_left = nkeep + fsg->residue; - while (fsg->usb_amount_left > 0) { + fsg->common->usb_amount_left = nkeep + fsg->common->residue; + while (fsg->common->usb_amount_left > 0) { /* Wait for the next buffer to be free */ while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(fsg->common); if (rc) return rc; } - nsend = min(fsg->usb_amount_left, FSG_BUFLEN); + nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); memset(bh->buf + nkeep, 0, nsend - nkeep); bh->inreq->length = nsend; bh->inreq->zero = 0; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); bh = fsg->common->next_buffhd_to_fill = bh->next; - fsg->usb_amount_left -= nsend; + fsg->common->usb_amount_left -= nsend; nkeep = 0; } return 0; } -static int throw_away_data(struct fsg_dev *fsg) +static int throw_away_data(struct fsg_common *common) { struct fsg_buffhd *bh; u32 amount; int rc; - for (bh = fsg->common->next_buffhd_to_drain; - bh->state != BUF_STATE_EMPTY || fsg->usb_amount_left > 0; - bh = fsg->common->next_buffhd_to_drain) { + for (bh = common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; + bh = common->next_buffhd_to_drain) { /* Throw away the data in a filled buffer */ if (bh->state == BUF_STATE_FULL) { smp_rmb(); bh->state = BUF_STATE_EMPTY; - fsg->common->next_buffhd_to_drain = bh->next; + common->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ if (bh->outreq->actual != bh->outreq->length || bh->outreq->status != 0) { - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + raise_exception(common, + FSG_STATE_ABORT_BULK_OUT); return -EINTR; } continue; } /* Try to submit another request if we need one */ - bh = fsg->common->next_buffhd_to_fill; - if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { - amount = min(fsg->usb_amount_left, FSG_BUFLEN); + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY + && common->usb_amount_left > 0) { + amount = min(common->usb_amount_left, FSG_BUFLEN); /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ bh->outreq->length = amount; bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - fsg->common->next_buffhd_to_fill = bh->next; - fsg->usb_amount_left -= amount; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + common->usb_amount_left -= amount; continue; } /* Otherwise wait for something to happen */ - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } @@ -1533,12 +1564,12 @@ static int throw_away_data(struct fsg_dev *fsg) } -static int finish_reply(struct fsg_dev *fsg) +static int finish_reply(struct fsg_common *common) { - struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; int rc = 0; - switch (fsg->data_dir) { + switch (common->data_dir) { case DATA_DIR_NONE: break; /* Nothing to send */ @@ -1547,47 +1578,60 @@ static int finish_reply(struct fsg_dev *fsg) * try to send or receive any data. So stall both bulk pipes * if we can and wait for a reset. */ case DATA_DIR_UNKNOWN: - if (fsg->can_stall) { - fsg_set_halt(fsg, fsg->bulk_out); - rc = halt_bulk_in_endpoint(fsg); + if (!common->can_stall) { + /* Nothing */ + } else if (fsg_is_set(common)) { + fsg_set_halt(common->fsg, common->fsg->bulk_out); + rc = halt_bulk_in_endpoint(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; } break; /* All but the last buffer of data must have already been sent */ case DATA_DIR_TO_HOST: - if (fsg->data_size == 0) { + if (common->data_size == 0) { /* Nothing to send */ /* If there's no residue, simply send the last buffer */ - } else if (fsg->residue == 0) { + } else if (common->residue == 0) { bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->common->next_buffhd_to_fill = bh->next; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + return -EIO; + common->next_buffhd_to_fill = bh->next; /* For Bulk-only, if we're allowed to stall then send the * short packet and halt the bulk-in endpoint. If we can't * stall, pad out the remaining data with 0's. */ - } else if (fsg->can_stall) { + } else if (common->can_stall) { bh->inreq->zero = 1; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->common->next_buffhd_to_fill = bh->next; - rc = halt_bulk_in_endpoint(fsg); + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + rc = -EIO; + common->next_buffhd_to_fill = bh->next; + if (common->fsg) + rc = halt_bulk_in_endpoint(common->fsg); + } else if (fsg_is_set(common)) { + rc = pad_with_zeros(common->fsg); } else { - rc = pad_with_zeros(fsg); + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; } break; /* We have processed all we want from the data the host has sent. * There may still be outstanding bulk-out requests. */ case DATA_DIR_FROM_HOST: - if (fsg->residue == 0) { + if (common->residue == 0) { /* Nothing to receive */ /* Did the host stop sending unexpectedly early? */ - } else if (fsg->short_packet_received) { - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + } else if (common->short_packet_received) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); rc = -EINTR; /* We haven't processed all the incoming data. Even though @@ -1597,16 +1641,18 @@ static int finish_reply(struct fsg_dev *fsg) * STALL. Not realizing the endpoint was halted, it wouldn't * clear the halt -- leading to problems later on. */ #if 0 - } else if (fsg->can_stall) { - fsg_set_halt(fsg, fsg->bulk_out); - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + } else if (common->can_stall) { + if (fsg_is_set(common)) + fsg_set_halt(common->fsg, + common->fsg->bulk_out); + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); rc = -EINTR; #endif /* We can't stall. Read in the excess data and throw it * all away. */ } else { - rc = throw_away_data(fsg); + rc = throw_away_data(common); } break; } @@ -1614,9 +1660,9 @@ static int finish_reply(struct fsg_dev *fsg) } -static int send_status(struct fsg_dev *fsg) +static int send_status(struct fsg_common *common) { - struct fsg_lun *curlun = fsg->common->curlun; + struct fsg_lun *curlun = common->curlun; struct fsg_buffhd *bh; struct bulk_cs_wrap *csw; int rc; @@ -1624,9 +1670,9 @@ static int send_status(struct fsg_dev *fsg) u32 sd, sdinfo = 0; /* Wait for the next buffer to become available */ - bh = fsg->common->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } @@ -1634,19 +1680,19 @@ static int send_status(struct fsg_dev *fsg) if (curlun) { sd = curlun->sense_data; sdinfo = curlun->sense_data_info; - } else if (fsg->bad_lun_okay) + } else if (common->bad_lun_okay) sd = SS_NO_SENSE; else sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; - if (fsg->phase_error) { - DBG(fsg, "sending phase-error status\n"); + if (common->phase_error) { + DBG(common, "sending phase-error status\n"); status = USB_STATUS_PHASE_ERROR; sd = SS_INVALID_COMMAND; } else if (sd != SS_NO_SENSE) { - DBG(fsg, "sending command-failure status\n"); + DBG(common, "sending command-failure status\n"); status = USB_STATUS_FAIL; - VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" " info x%x\n", SK(sd), ASC(sd), ASCQ(sd), sdinfo); } @@ -1655,16 +1701,18 @@ static int send_status(struct fsg_dev *fsg) csw = (void *)bh->buf; csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); - csw->Tag = fsg->tag; - csw->Residue = cpu_to_le32(fsg->residue); + csw->Tag = common->tag; + csw->Residue = cpu_to_le32(common->residue); csw->Status = status; bh->inreq->length = USB_BULK_CS_WRAP_LEN; bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; - fsg->common->next_buffhd_to_fill = bh->next; + common->next_buffhd_to_fill = bh->next; return 0; } @@ -1673,52 +1721,47 @@ static int send_status(struct fsg_dev *fsg) /* Check whether the command is properly formed and whether its data size * and direction agree with the values we already have. */ -static int check_command(struct fsg_dev *fsg, int cmnd_size, +static int check_command(struct fsg_common *common, int cmnd_size, enum data_direction data_dir, unsigned int mask, int needs_medium, const char *name) { int i; - int lun = fsg->common->cmnd[1] >> 5; + int lun = common->cmnd[1] >> 5; static const char dirletter[4] = {'u', 'o', 'i', 'n'}; char hdlen[20]; struct fsg_lun *curlun; hdlen[0] = 0; - if (fsg->data_dir != DATA_DIR_UNKNOWN) - sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], - fsg->data_size); - VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + if (common->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], + common->data_size); + VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", name, cmnd_size, dirletter[(int) data_dir], - fsg->data_size_from_cmnd, fsg->common->cmnd_size, hdlen); + common->data_size_from_cmnd, common->cmnd_size, hdlen); /* We can't reply at all until we know the correct data direction * and size. */ - if (fsg->data_size_from_cmnd == 0) + if (common->data_size_from_cmnd == 0) data_dir = DATA_DIR_NONE; - if (fsg->data_dir == DATA_DIR_UNKNOWN) { /* CB or CBI */ - fsg->data_dir = data_dir; - fsg->data_size = fsg->data_size_from_cmnd; - - } else { /* Bulk-only */ - if (fsg->data_size < fsg->data_size_from_cmnd) { - - /* Host data size < Device data size is a phase error. - * Carry out the command, but only transfer as much - * as we are allowed. */ - fsg->data_size_from_cmnd = fsg->data_size; - fsg->phase_error = 1; - } + if (common->data_size < common->data_size_from_cmnd) { + /* Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much as + * we are allowed. */ + common->data_size_from_cmnd = common->data_size; + common->phase_error = 1; } - fsg->residue = fsg->usb_amount_left = fsg->data_size; + common->residue = common->data_size; + common->usb_amount_left = common->data_size; /* Conflicting data directions is a phase error */ - if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) { - fsg->phase_error = 1; + if (common->data_dir != data_dir + && common->data_size_from_cmnd > 0) { + common->phase_error = 1; return -EINVAL; } /* Verify the length of the command itself */ - if (cmnd_size != fsg->common->cmnd_size) { + if (cmnd_size != common->cmnd_size) { /* Special case workaround: There are plenty of buggy SCSI * implementations. Many have issues with cbw->Length @@ -1732,40 +1775,41 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, * REQUEST SENSE with cbw->Length == 10 where it should * be 6 as well. */ - if (cmnd_size <= fsg->common->cmnd_size) { - DBG(fsg, "%s is buggy! Expected length %d " + if (cmnd_size <= common->cmnd_size) { + DBG(common, "%s is buggy! Expected length %d " "but we got %d\n", name, - cmnd_size, fsg->common->cmnd_size); - cmnd_size = fsg->common->cmnd_size; + cmnd_size, common->cmnd_size); + cmnd_size = common->cmnd_size; } else { - fsg->phase_error = 1; + common->phase_error = 1; return -EINVAL; } } /* Check that the LUN values are consistent */ - if (fsg->common->lun != lun) - DBG(fsg, "using LUN %d from CBW, not LUN %d from CDB\n", - fsg->common->lun, lun); + if (common->lun != lun) + DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", + common->lun, lun); /* Check the LUN */ - if (fsg->common->lun >= 0 && fsg->common->lun < fsg->common->nluns) { - curlun = &fsg->common->luns[fsg->common->lun]; - fsg->common->curlun = curlun; - if (fsg->common->cmnd[0] != SC_REQUEST_SENSE) { + if (common->lun >= 0 && common->lun < common->nluns) { + curlun = &common->luns[common->lun]; + common->curlun = curlun; + if (common->cmnd[0] != SC_REQUEST_SENSE) { curlun->sense_data = SS_NO_SENSE; curlun->sense_data_info = 0; curlun->info_valid = 0; } } else { - fsg->common->curlun = curlun = NULL; - fsg->bad_lun_okay = 0; + common->curlun = NULL; + curlun = NULL; + common->bad_lun_okay = 0; /* INQUIRY and REQUEST SENSE commands are explicitly allowed * to use unsupported LUNs; all others may not. */ - if (fsg->common->cmnd[0] != SC_INQUIRY && - fsg->common->cmnd[0] != SC_REQUEST_SENSE) { - DBG(fsg, "unsupported LUN %d\n", fsg->common->lun); + if (common->cmnd[0] != SC_INQUIRY && + common->cmnd[0] != SC_REQUEST_SENSE) { + DBG(common, "unsupported LUN %d\n", common->lun); return -EINVAL; } } @@ -1773,17 +1817,17 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, /* If a unit attention condition exists, only INQUIRY and * REQUEST SENSE commands are allowed; anything else must fail. */ if (curlun && curlun->unit_attention_data != SS_NO_SENSE && - fsg->common->cmnd[0] != SC_INQUIRY && - fsg->common->cmnd[0] != SC_REQUEST_SENSE) { + common->cmnd[0] != SC_INQUIRY && + common->cmnd[0] != SC_REQUEST_SENSE) { curlun->sense_data = curlun->unit_attention_data; curlun->unit_attention_data = SS_NO_SENSE; return -EINVAL; } /* Check that only command bytes listed in the mask are non-zero */ - fsg->common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + common->cmnd[1] &= 0x1f; /* Mask away the LUN */ for (i = 1; i < cmnd_size; ++i) { - if (fsg->common->cmnd[i] && !(mask & (1 << i))) { + if (common->cmnd[i] && !(mask & (1 << i))) { if (curlun) curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; @@ -1801,7 +1845,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, } -static int do_scsi_command(struct fsg_dev *fsg) +static int do_scsi_command(struct fsg_common *common) { struct fsg_buffhd *bh; int rc; @@ -1809,182 +1853,181 @@ static int do_scsi_command(struct fsg_dev *fsg) int i; static char unknown[16]; - dump_cdb(fsg->common); + dump_cdb(common); /* Wait for the next buffer to become available for data or status */ - bh = fsg->common->next_buffhd_to_fill; - fsg->common->next_buffhd_to_drain = bh; + bh = common->next_buffhd_to_fill; + common->next_buffhd_to_drain = bh; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } - fsg->phase_error = 0; - fsg->short_packet_received = 0; + common->phase_error = 0; + common->short_packet_received = 0; - /* We're using the backing file */ - down_read(&fsg->common->filesem); - switch (fsg->common->cmnd[0]) { + down_read(&common->filesem); /* We're using the backing file */ + switch (common->cmnd[0]) { case SC_INQUIRY: - fsg->data_size_from_cmnd = fsg->common->cmnd[4]; - reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, (1<<4), 0, "INQUIRY"); if (reply == 0) - reply = do_inquiry(fsg, bh); + reply = do_inquiry(common, bh); break; case SC_MODE_SELECT_6: - fsg->data_size_from_cmnd = fsg->common->cmnd[4]; - reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, (1<<1) | (1<<4), 0, "MODE SELECT(6)"); if (reply == 0) - reply = do_mode_select(fsg, bh); + reply = do_mode_select(common, bh); break; case SC_MODE_SELECT_10: - fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->common->cmnd[7]); - reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_FROM_HOST, (1<<1) | (3<<7), 0, "MODE SELECT(10)"); if (reply == 0) - reply = do_mode_select(fsg, bh); + reply = do_mode_select(common, bh); break; case SC_MODE_SENSE_6: - fsg->data_size_from_cmnd = fsg->common->cmnd[4]; - reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, (1<<1) | (1<<2) | (1<<4), 0, "MODE SENSE(6)"); if (reply == 0) - reply = do_mode_sense(fsg, bh); + reply = do_mode_sense(common, bh); break; case SC_MODE_SENSE_10: - fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->common->cmnd[7]); - reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, (1<<1) | (1<<2) | (3<<7), 0, "MODE SENSE(10)"); if (reply == 0) - reply = do_mode_sense(fsg, bh); + reply = do_mode_sense(common, bh); break; case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: - fsg->data_size_from_cmnd = 0; - reply = check_command(fsg, 6, DATA_DIR_NONE, + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, (1<<4), 0, "PREVENT-ALLOW MEDIUM REMOVAL"); if (reply == 0) - reply = do_prevent_allow(fsg); + reply = do_prevent_allow(common); break; case SC_READ_6: - i = fsg->common->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; - reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1, "READ(6)"); if (reply == 0) - reply = do_read(fsg); + reply = do_read(common); break; case SC_READ_10: - fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->common->cmnd[7]) << 9; - reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "READ(10)"); if (reply == 0) - reply = do_read(fsg); + reply = do_read(common); break; case SC_READ_12: - fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->common->cmnd[6]) << 9; - reply = check_command(fsg, 12, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "READ(12)"); if (reply == 0) - reply = do_read(fsg); + reply = do_read(common); break; case SC_READ_CAPACITY: - fsg->data_size_from_cmnd = 8; - reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = 8; + reply = check_command(common, 10, DATA_DIR_TO_HOST, (0xf<<2) | (1<<8), 1, "READ CAPACITY"); if (reply == 0) - reply = do_read_capacity(fsg, bh); + reply = do_read_capacity(common, bh); break; case SC_READ_HEADER: - if (!fsg->common->curlun || !fsg->common->curlun->cdrom) + if (!common->curlun || !common->curlun->cdrom) goto unknown_cmnd; - fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->common->cmnd[7]); - reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, (3<<7) | (0x1f<<1), 1, "READ HEADER"); if (reply == 0) - reply = do_read_header(fsg, bh); + reply = do_read_header(common, bh); break; case SC_READ_TOC: - if (!fsg->common->curlun || !fsg->common->curlun->cdrom) + if (!common->curlun || !common->curlun->cdrom) goto unknown_cmnd; - fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->common->cmnd[7]); - reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, (7<<6) | (1<<1), 1, "READ TOC"); if (reply == 0) - reply = do_read_toc(fsg, bh); + reply = do_read_toc(common, bh); break; case SC_READ_FORMAT_CAPACITIES: - fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->common->cmnd[7]); - reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, (3<<7), 1, "READ FORMAT CAPACITIES"); if (reply == 0) - reply = do_read_format_capacities(fsg, bh); + reply = do_read_format_capacities(common, bh); break; case SC_REQUEST_SENSE: - fsg->data_size_from_cmnd = fsg->common->cmnd[4]; - reply = check_command(fsg, 6, DATA_DIR_TO_HOST, + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, (1<<4), 0, "REQUEST SENSE"); if (reply == 0) - reply = do_request_sense(fsg, bh); + reply = do_request_sense(common, bh); break; case SC_START_STOP_UNIT: - fsg->data_size_from_cmnd = 0; - reply = check_command(fsg, 6, DATA_DIR_NONE, + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, (1<<1) | (1<<4), 0, "START-STOP UNIT"); if (reply == 0) - reply = do_start_stop(fsg); + reply = do_start_stop(common); break; case SC_SYNCHRONIZE_CACHE: - fsg->data_size_from_cmnd = 0; - reply = check_command(fsg, 10, DATA_DIR_NONE, + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, (0xf<<2) | (3<<7), 1, "SYNCHRONIZE CACHE"); if (reply == 0) - reply = do_synchronize_cache(fsg); + reply = do_synchronize_cache(common); break; case SC_TEST_UNIT_READY: - fsg->data_size_from_cmnd = 0; - reply = check_command(fsg, 6, DATA_DIR_NONE, + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, 0, 1, "TEST UNIT READY"); break; @@ -1992,42 +2035,42 @@ static int do_scsi_command(struct fsg_dev *fsg) /* Although optional, this command is used by MS-Windows. We * support a minimal version: BytChk must be 0. */ case SC_VERIFY: - fsg->data_size_from_cmnd = 0; - reply = check_command(fsg, 10, DATA_DIR_NONE, + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, (1<<1) | (0xf<<2) | (3<<7), 1, "VERIFY"); if (reply == 0) - reply = do_verify(fsg); + reply = do_verify(common); break; case SC_WRITE_6: - i = fsg->common->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; - reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1, "WRITE(6)"); if (reply == 0) - reply = do_write(fsg); + reply = do_write(common); break; case SC_WRITE_10: - fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->common->cmnd[7]) << 9; - reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "WRITE(10)"); if (reply == 0) - reply = do_write(fsg); + reply = do_write(common); break; case SC_WRITE_12: - fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->common->cmnd[6]) << 9; - reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "WRITE(12)"); if (reply == 0) - reply = do_write(fsg); + reply = do_write(common); break; /* Some mandatory commands that we recognize but don't implement. @@ -2042,17 +2085,17 @@ static int do_scsi_command(struct fsg_dev *fsg) default: unknown_cmnd: - fsg->data_size_from_cmnd = 0; - sprintf(unknown, "Unknown x%02x", fsg->common->cmnd[0]); - reply = check_command(fsg, fsg->common->cmnd_size, + common->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", common->cmnd[0]); + reply = check_command(common, common->cmnd_size, DATA_DIR_UNKNOWN, 0xff, 0, unknown); if (reply == 0) { - fsg->common->curlun->sense_data = SS_INVALID_COMMAND; + common->curlun->sense_data = SS_INVALID_COMMAND; reply = -EINVAL; } break; } - up_read(&fsg->common->filesem); + up_read(&common->filesem); if (reply == -EINTR || signal_pending(current)) return -EINTR; @@ -2060,11 +2103,11 @@ unknown_cmnd: /* Set up the single reply buffer for finish_reply() */ if (reply == -EINVAL) reply = 0; /* Error reply length */ - if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { - reply = min((u32) reply, fsg->data_size_from_cmnd); + if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, common->data_size_from_cmnd); bh->inreq->length = reply; bh->state = BUF_STATE_FULL; - fsg->residue -= reply; + common->residue -= reply; } /* Otherwise it's already set */ return 0; @@ -2075,8 +2118,9 @@ unknown_cmnd: static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) { - struct usb_request *req = bh->outreq; + struct usb_request *req = bh->outreq; struct fsg_bulk_cb_wrap *cbw = req->buf; + struct fsg_common *common = fsg->common; /* Was this a real packet? Should it be ignored? */ if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) @@ -2113,7 +2157,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) /* We can do anything we want here, so let's stall the * bulk pipes if we are allowed to. */ - if (fsg->can_stall) { + if (common->can_stall) { fsg_set_halt(fsg, fsg->bulk_out); halt_bulk_in_endpoint(fsg); } @@ -2121,39 +2165,41 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) } /* Save the command for later */ - fsg->common->cmnd_size = cbw->Length; - memcpy(fsg->common->cmnd, cbw->CDB, fsg->common->cmnd_size); + common->cmnd_size = cbw->Length; + memcpy(common->cmnd, cbw->CDB, common->cmnd_size); if (cbw->Flags & USB_BULK_IN_FLAG) - fsg->data_dir = DATA_DIR_TO_HOST; + common->data_dir = DATA_DIR_TO_HOST; else - fsg->data_dir = DATA_DIR_FROM_HOST; - fsg->data_size = le32_to_cpu(cbw->DataTransferLength); - if (fsg->data_size == 0) - fsg->data_dir = DATA_DIR_NONE; - fsg->common->lun = cbw->Lun; - fsg->tag = cbw->Tag; + common->data_dir = DATA_DIR_FROM_HOST; + common->data_size = le32_to_cpu(cbw->DataTransferLength); + if (common->data_size == 0) + common->data_dir = DATA_DIR_NONE; + common->lun = cbw->Lun; + common->tag = cbw->Tag; return 0; } -static int get_next_command(struct fsg_dev *fsg) +static int get_next_command(struct fsg_common *common) { struct fsg_buffhd *bh; int rc = 0; /* Wait for the next buffer to become available */ - bh = fsg->common->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } /* Queue a request to read a Bulk-only CBW */ - set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); + set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); bh->outreq->short_not_ok = 1; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; /* We will drain the buffer in software, which means we * can reuse it for the next filling. No need to advance @@ -2161,12 +2207,12 @@ static int get_next_command(struct fsg_dev *fsg) /* Wait for the CBW to arrive */ while (bh->state != BUF_STATE_FULL) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } smp_rmb(); - rc = received_cbw(fsg, bh); + rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; bh->state = BUF_STATE_EMPTY; return rc; @@ -2175,25 +2221,25 @@ static int get_next_command(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, +static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep, const struct usb_endpoint_descriptor *d) { int rc; - ep->driver_data = fsg; + ep->driver_data = common; rc = usb_ep_enable(ep, d); if (rc) - ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); + ERROR(common, "can't enable %s, result %d\n", ep->name, rc); return rc; } -static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, struct usb_request **preq) { *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); if (*preq) return 0; - ERROR(fsg, "can't allocate request for %s\n", ep->name); + ERROR(common, "can't allocate request for %s\n", ep->name); return -ENOMEM; } @@ -2202,83 +2248,96 @@ static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, * Call with altsetting < 0 to disable the interface. The only other * available altsetting is 0, which enables the interface. */ -static int do_set_interface(struct fsg_dev *fsg, int altsetting) +static int do_set_interface(struct fsg_common *common, int altsetting) { int rc = 0; int i; const struct usb_endpoint_descriptor *d; - if (fsg->running) - DBG(fsg, "reset interface\n"); + if (common->running) + DBG(common, "reset interface\n"); reset: /* Deallocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->common->buffhds[i]; + if (common->prev_fsg) { + struct fsg_dev *fsg = common->prev_fsg; + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; - if (bh->inreq) { - usb_ep_free_request(fsg->bulk_in, bh->inreq); - bh->inreq = NULL; + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } } - if (bh->outreq) { - usb_ep_free_request(fsg->bulk_out, bh->outreq); - bh->outreq = NULL; + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; } - } - /* Disable the endpoints */ - if (fsg->bulk_in_enabled) { - usb_ep_disable(fsg->bulk_in); - fsg->bulk_in_enabled = 0; - } - if (fsg->bulk_out_enabled) { - usb_ep_disable(fsg->bulk_out); - fsg->bulk_out_enabled = 0; + common->prev_fsg = 0; } - fsg->running = 0; + common->running = 0; if (altsetting < 0 || rc != 0) return rc; - DBG(fsg, "set interface %d\n", altsetting); - - /* Enable the endpoints */ - d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); - rc = enable_endpoint(fsg, fsg->bulk_in, d); - if (rc != 0) - goto reset; - fsg->bulk_in_enabled = 1; - - d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); - rc = enable_endpoint(fsg, fsg->bulk_out, d); - if (rc != 0) - goto reset; - fsg->bulk_out_enabled = 1; - fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); - clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); - - /* Allocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->common->buffhds[i]; + DBG(common, "set interface %d\n", altsetting); - rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq); - if (rc != 0) + if (fsg_is_set(common)) { + struct fsg_dev *fsg = common->fsg; + common->prev_fsg = common->fsg; + + /* Enable the endpoints */ + d = fsg_ep_desc(common->gadget, + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); + rc = enable_endpoint(common, fsg->bulk_in, d); + if (rc) goto reset; - rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq); - if (rc != 0) + fsg->bulk_in_enabled = 1; + + d = fsg_ep_desc(common->gadget, + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); + rc = enable_endpoint(common, fsg->bulk_out, d); + if (rc) goto reset; - bh->inreq->buf = bh->outreq->buf = bh->buf; - bh->inreq->context = bh->outreq->context = bh; - bh->inreq->complete = bulk_in_complete; - bh->outreq->complete = bulk_out_complete; - } + fsg->bulk_out_enabled = 1; + common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); - fsg->running = 1; - for (i = 0; i < fsg->common->nluns; ++i) - fsg->common->luns[i].unit_attention_data = SS_RESET_OCCURRED; - return rc; + /* Allocate the requests */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + rc = alloc_request(common, fsg->bulk_in, &bh->inreq); + if (rc) + goto reset; + rc = alloc_request(common, fsg->bulk_out, &bh->outreq); + if (rc) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + + common->running = 1; + for (i = 0; i < common->nluns; ++i) + common->luns[i].unit_attention_data = SS_RESET_OCCURRED; + return rc; + } else { + return -EIO; + } } @@ -2290,23 +2349,23 @@ reset: * configurations might not work with our current power sources. * For now we just assume the gadget is always self-powered. */ -static int do_set_config(struct fsg_dev *fsg, u8 new_config) +static int do_set_config(struct fsg_common *common, u8 new_config) { int rc = 0; /* Disable the single interface */ - if (fsg->config != 0) { - DBG(fsg, "reset config\n"); - fsg->config = 0; - rc = do_set_interface(fsg, -1); + if (common->config != 0) { + DBG(common, "reset config\n"); + common->config = 0; + rc = do_set_interface(common, -1); } /* Enable the interface */ if (new_config != 0) { - fsg->config = new_config; - rc = do_set_interface(fsg, 0); + common->config = new_config; + rc = do_set_interface(common, 0); if (rc != 0) - fsg->config = 0; /* Reset on errors */ + common->config = 0; /* Reset on errors */ } return rc; } @@ -2318,22 +2377,26 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config) static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct fsg_dev *fsg = fsg_from_func(f); - fsg->new_config = 1; - raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); + fsg->common->prev_fsg = fsg->common->fsg; + fsg->common->fsg = fsg; + fsg->common->new_config = 1; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); return 0; } static void fsg_disable(struct usb_function *f) { struct fsg_dev *fsg = fsg_from_func(f); - fsg->new_config = 0; - raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); + fsg->common->prev_fsg = fsg->common->fsg; + fsg->common->fsg = fsg; + fsg->common->new_config = 0; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); } /*-------------------------------------------------------------------------*/ -static void handle_exception(struct fsg_dev *fsg) +static void handle_exception(struct fsg_common *common) { siginfo_t info; int sig; @@ -2352,113 +2415,121 @@ static void handle_exception(struct fsg_dev *fsg) if (!sig) break; if (sig != SIGUSR1) { - if (fsg->state < FSG_STATE_EXIT) - DBG(fsg, "Main thread exiting on signal\n"); - raise_exception(fsg, FSG_STATE_EXIT); + if (common->state < FSG_STATE_EXIT) + DBG(common, "Main thread exiting on signal\n"); + raise_exception(common, FSG_STATE_EXIT); } } /* Cancel all the pending transfers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - bh = &fsg->common->buffhds[i]; - if (bh->inreq_busy) - usb_ep_dequeue(fsg->bulk_in, bh->inreq); - if (bh->outreq_busy) - usb_ep_dequeue(fsg->bulk_out, bh->outreq); - } - - /* Wait until everything is idle */ - for (;;) { - int num_active = 0; + if (fsg_is_set(common)) { for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - bh = &fsg->common->buffhds[i]; - num_active += bh->inreq_busy + bh->outreq_busy; + bh = &common->buffhds[i]; + if (bh->inreq_busy) + usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(common->fsg->bulk_out, + bh->outreq); } - if (num_active == 0) - break; - if (sleep_thread(fsg)) - return; - } - /* Clear out the controller's fifos */ - if (fsg->bulk_in_enabled) - usb_ep_fifo_flush(fsg->bulk_in); - if (fsg->bulk_out_enabled) - usb_ep_fifo_flush(fsg->bulk_out); + /* Wait until everything is idle */ + for (;;) { + int num_active = 0; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + num_active += bh->inreq_busy + bh->outreq_busy; + } + if (num_active == 0) + break; + if (sleep_thread(common)) + return; + } + + /* Clear out the controller's fifos */ + if (common->fsg->bulk_in_enabled) + usb_ep_fifo_flush(common->fsg->bulk_in); + if (common->fsg->bulk_out_enabled) + usb_ep_fifo_flush(common->fsg->bulk_out); + } /* Reset the I/O buffer states and pointers, the SCSI * state, and the exception. Then invoke the handler. */ - spin_lock_irq(&fsg->lock); + spin_lock_irq(&common->lock); for (i = 0; i < FSG_NUM_BUFFERS; ++i) { - bh = &fsg->common->buffhds[i]; + bh = &common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } - fsg->common->next_buffhd_to_fill = &fsg->common->buffhds[0]; - fsg->common->next_buffhd_to_drain = &fsg->common->buffhds[0]; - exception_req_tag = fsg->exception_req_tag; - new_config = fsg->new_config; - old_state = fsg->state; + common->next_buffhd_to_fill = &common->buffhds[0]; + common->next_buffhd_to_drain = &common->buffhds[0]; + exception_req_tag = common->exception_req_tag; + new_config = common->new_config; + old_state = common->state; if (old_state == FSG_STATE_ABORT_BULK_OUT) - fsg->state = FSG_STATE_STATUS_PHASE; + common->state = FSG_STATE_STATUS_PHASE; else { - for (i = 0; i < fsg->common->nluns; ++i) { - curlun = &fsg->common->luns[i]; + for (i = 0; i < common->nluns; ++i) { + curlun = &common->luns[i]; curlun->prevent_medium_removal = 0; curlun->sense_data = SS_NO_SENSE; curlun->unit_attention_data = SS_NO_SENSE; curlun->sense_data_info = 0; curlun->info_valid = 0; } - fsg->state = FSG_STATE_IDLE; + common->state = FSG_STATE_IDLE; } - spin_unlock_irq(&fsg->lock); + spin_unlock_irq(&common->lock); /* Carry out any extra actions required for the exception */ switch (old_state) { case FSG_STATE_ABORT_BULK_OUT: - send_status(fsg); - spin_lock_irq(&fsg->lock); - if (fsg->state == FSG_STATE_STATUS_PHASE) - fsg->state = FSG_STATE_IDLE; - spin_unlock_irq(&fsg->lock); + send_status(common); + spin_lock_irq(&common->lock); + if (common->state == FSG_STATE_STATUS_PHASE) + common->state = FSG_STATE_IDLE; + spin_unlock_irq(&common->lock); break; case FSG_STATE_RESET: /* In case we were forced against our will to halt a * bulk endpoint, clear the halt now. (The SuperH UDC * requires this.) */ - if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) - usb_ep_clear_halt(fsg->bulk_in); + if (!fsg_is_set(common)) + break; + if (test_and_clear_bit(IGNORE_BULK_OUT, + &common->fsg->atomic_bitflags)) + usb_ep_clear_halt(common->fsg->bulk_in); - if (fsg->ep0_req_tag == exception_req_tag) - ep0_queue(fsg); /* Complete the status stage */ + if (common->ep0_req_tag == exception_req_tag) + ep0_queue(common); /* Complete the status stage */ /* Technically this should go here, but it would only be * a waste of time. Ditto for the INTERFACE_CHANGE and * CONFIG_CHANGE cases. */ - /* for (i = 0; i < fsg->common->nluns; ++i) */ - /* fsg->common->luns[i].unit_attention_data = */ + /* for (i = 0; i < common->nluns; ++i) */ + /* common->luns[i].unit_attention_data = */ /* SS_RESET_OCCURRED; */ break; case FSG_STATE_CONFIG_CHANGE: - rc = do_set_config(fsg, new_config); - if (fsg->ep0_req_tag != exception_req_tag) + rc = do_set_config(common, new_config); + if (common->ep0_req_tag != exception_req_tag) break; - if (rc != 0) /* STALL on errors */ - fsg_set_halt(fsg, fsg->ep0); - else /* Complete the status stage */ - ep0_queue(fsg); + if (rc != 0) { /* STALL on errors */ + DBG(common, "ep0 set halt\n"); + usb_ep_set_halt(common->ep0); + } else { /* Complete the status stage */ + ep0_queue(common); + } break; case FSG_STATE_EXIT: case FSG_STATE_TERMINATED: - do_set_config(fsg, 0); /* Free resources */ - spin_lock_irq(&fsg->lock); - fsg->state = FSG_STATE_TERMINATED; /* Stop the thread */ - spin_unlock_irq(&fsg->lock); + do_set_config(common, 0); /* Free resources */ + spin_lock_irq(&common->lock); + common->state = FSG_STATE_TERMINATED; /* Stop the thread */ + spin_unlock_irq(&common->lock); break; case FSG_STATE_INTERFACE_CHANGE: @@ -2474,9 +2545,9 @@ static void handle_exception(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int fsg_main_thread(void *fsg_) +static int fsg_main_thread(void *common_) { - struct fsg_dev *fsg = fsg_; + struct fsg_common *common = common_; /* Allow the thread to be killed by a signal, but set the signal mask * to block everything but INT, TERM, KILL, and USR1. */ @@ -2494,45 +2565,45 @@ static int fsg_main_thread(void *fsg_) set_fs(get_ds()); /* The main loop */ - while (fsg->state != FSG_STATE_TERMINATED) { - if (exception_in_progress(fsg) || signal_pending(current)) { - handle_exception(fsg); + while (common->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(common) || signal_pending(current)) { + handle_exception(common); continue; } - if (!fsg->running) { - sleep_thread(fsg); + if (!common->running) { + sleep_thread(common); continue; } - if (get_next_command(fsg)) + if (get_next_command(common)) continue; - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_DATA_PHASE; - spin_unlock_irq(&fsg->lock); + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_DATA_PHASE; + spin_unlock_irq(&common->lock); - if (do_scsi_command(fsg) || finish_reply(fsg)) + if (do_scsi_command(common) || finish_reply(common)) continue; - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_STATUS_PHASE; - spin_unlock_irq(&fsg->lock); + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_STATUS_PHASE; + spin_unlock_irq(&common->lock); - if (send_status(fsg)) + if (send_status(common)) continue; - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_IDLE; - spin_unlock_irq(&fsg->lock); + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_IDLE; + spin_unlock_irq(&common->lock); } - spin_lock_irq(&fsg->lock); - fsg->thread_task = NULL; - spin_unlock_irq(&fsg->lock); + spin_lock_irq(&common->lock); + common->thread_task = NULL; + spin_unlock_irq(&common->lock); /* XXX */ /* If we are exiting because of a signal, unregister the @@ -2541,7 +2612,7 @@ static int fsg_main_thread(void *fsg_) /* usb_gadget_unregister_driver(&fsg_driver); */ /* Let the unbind and cleanup routines know the thread has exited */ - complete_and_exit(&fsg->thread_notifier, 0); + complete_and_exit(&common->thread_notifier, 0); } @@ -2600,7 +2671,21 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, memset(common, 0, sizeof common); common->free_storage_on_release = 0; } + common->gadget = gadget; + common->ep0 = gadget->ep0; + common->ep0req = cdev->req; + + /* Maybe allocate device-global string IDs, and patch descriptors */ + if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { + rc = usb_string_id(cdev); + if (rc < 0) { + kfree(common); + return ERR_PTR(rc); + } + fsg_strings[FSG_STRING_INTERFACE].id = rc; + fsg_intf_desc.iInterface = rc; + } /* Create the LUNs, open their backing files, and register the * LUN devices in sysfs. */ @@ -2697,11 +2782,23 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, * disable stalls. */ common->can_stall = cfg->can_stall && - !(gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)); + !(gadget_is_sh(common->gadget) || + gadget_is_at91(common->gadget)); - common->thread_name = OR(cfg->thread_name, "file-storage"); + spin_lock_init(&common->lock); kref_init(&common->ref); + + + /* Tell the thread to start working */ + common->thread_task = + kthread_create(fsg_main_thread, common, + OR(cfg->thread_name, "file-storage")); + if (IS_ERR(common->thread_task)) { + rc = PTR_ERR(common->thread_task); + goto error_release; + } + init_completion(&common->thread_notifier); #undef OR @@ -2731,15 +2828,21 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, } kfree(pathbuf); + DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); + + wake_up_process(common->thread_task); + return common; error_luns: common->nluns = i + 1; error_release: + common->state = FSG_STATE_TERMINATED; /* The thread is dead */ /* Call fsg_common_release() directly, ref might be not * initialised */ fsg_common_release(&common->ref); + complete(&common->thread_notifier); return ERR_PTR(rc); } @@ -2751,6 +2854,15 @@ static void fsg_common_release(struct kref *ref) unsigned i = common->nluns; struct fsg_lun *lun = common->luns; + /* If the thread isn't already dead, tell it to exit now */ + if (common->state != FSG_STATE_TERMINATED) { + raise_exception(common, FSG_STATE_EXIT); + wait_for_completion(&common->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&common->thread_notifier); + } + /* Beware tempting for -> do-while optimization: when in error * recovery nluns may be zero. */ @@ -2775,17 +2887,6 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) struct fsg_dev *fsg = fsg_from_func(f); DBG(fsg, "unbind\n"); - clear_bit(REGISTERED, &fsg->atomic_bitflags); - - /* If the thread isn't already dead, tell it to exit now */ - if (fsg->state != FSG_STATE_TERMINATED) { - raise_exception(fsg, FSG_STATE_EXIT); - wait_for_completion(&fsg->thread_notifier); - - /* The cleanup routine waits for this completion also */ - complete(&fsg->thread_notifier); - } - fsg_common_put(fsg->common); kfree(fsg); } @@ -2800,8 +2901,6 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; fsg->gadget = gadget; - fsg->ep0 = gadget->ep0; - fsg->ep0req = c->cdev->req; /* New interface */ i = usb_interface_id(c, f); @@ -2814,13 +2913,13 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); if (!ep) goto autoconf_fail; - ep->driver_data = fsg; /* claim the endpoint */ + ep->driver_data = fsg->common; /* claim the endpoint */ fsg->bulk_in = ep; ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); if (!ep) goto autoconf_fail; - ep->driver_data = fsg; /* claim the endpoint */ + ep->driver_data = fsg->common; /* claim the endpoint */ fsg->bulk_out = ep; if (gadget_is_dualspeed(gadget)) { @@ -2832,40 +2931,12 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors = fsg_hs_function; } - - /* maybe allocate device-global string IDs, and patch descriptors */ - if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { - i = usb_string_id(c->cdev); - if (i < 0) - return i; - fsg_strings[FSG_STRING_INTERFACE].id = i; - fsg_intf_desc.iInterface = i; - } - - - fsg->thread_task = kthread_create(fsg_main_thread, fsg, - fsg->common->thread_name); - if (IS_ERR(fsg->thread_task)) { - rc = PTR_ERR(fsg->thread_task); - goto out; - } - - DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); - - set_bit(REGISTERED, &fsg->atomic_bitflags); - - /* Tell the thread to start working */ - wake_up_process(fsg->thread_task); return 0; autoconf_fail: ERROR(fsg, "unable to autoconfigure all endpoints\n"); rc = -ENOTSUPP; - -out: - fsg->state = FSG_STATE_TERMINATED; /* The thread is dead */ fsg_unbind(c, f); - complete(&fsg->thread_notifier); return rc; } @@ -2888,10 +2959,6 @@ static int fsg_add(struct usb_composite_dev *cdev, if (unlikely(!fsg)) return -ENOMEM; - spin_lock_init(&fsg->lock); - init_completion(&fsg->thread_notifier); - - fsg->cdev = cdev; fsg->function.name = FSG_DRIVER_DESC; fsg->function.strings = fsg_strings_array; fsg->function.descriptors = fsg_fs_function; @@ -2907,7 +2974,6 @@ static int fsg_add(struct usb_composite_dev *cdev, * from this function. So instead of incrementing counter now * and decrement in error recovery we increment it only when * call to usb_add_function() was successful. */ - fsg->can_stall = common->can_stall; rc = usb_add_function(c, &fsg->function); -- cgit v1.2.3 From 8f46c136e63cda85b8564119b9075df24c0a5116 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Nov 2009 14:15:25 +0100 Subject: USB: composite: usb_composite_unregister() no longer __exit Changed definition of usb_composite_unregister() function removing __exit declaration. This way, the function is included even if the whole code was not compiled as module. This is required if a compiled-in code would like to unregister a composite gadget. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 8498f1a114d5..09289bb1e20f 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1092,7 +1092,8 @@ static struct usb_gadget_driver composite_driver = { .speed = USB_SPEED_HIGH, .bind = composite_bind, - .unbind = __exit_p(composite_unbind), + /* .unbind = __exit_p(composite_unbind), */ + .unbind = composite_unbind, .setup = composite_setup, .disconnect = composite_disconnect, @@ -1141,7 +1142,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver) * This function is used to unregister drivers using the composite * driver framework. */ -void __exit usb_composite_unregister(struct usb_composite_driver *driver) +void /* __exit */ usb_composite_unregister(struct usb_composite_driver *driver) { if (composite != driver) return; -- cgit v1.2.3 From 2a885651f83b365832868f6a14a8a2e03188d3d7 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Nov 2009 14:15:26 +0100 Subject: USB: g_mass_storage: thread_exits callback added thread_exits callback has been added to fsg_common structure. This callback is called when MSF's thread exits (is terminated by a signal or function is unregistered). It's then gadget's responsibility to unregister the gadget. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 23 ++++++++++++++++++----- drivers/usb/gadget/mass_storage.c | 20 +++++++++++--------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index a6cec37768a9..a37640eba434 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -367,6 +367,11 @@ struct fsg_common { struct completion thread_notifier; struct task_struct *thread_task; + /* Callback function to call when thread exits. */ + void (*thread_exits)(struct fsg_common *common); + /* Gadget's private data. */ + void *private_data; + /* Vendor (8 chars), product (16 chars), release (4 * hexadecimal digits) and NUL byte */ char inquiry_string[8 + 16 + 4 + 1]; @@ -387,6 +392,11 @@ struct fsg_config { const char *lun_name_format; const char *thread_name; + /* Callback function to call when thread exits. */ + void (*thread_exits)(struct fsg_common *common); + /* Gadget's private data. */ + void *private_data; + const char *vendor_name; /* 8 characters or less */ const char *product_name; /* 16 characters or less */ u16 release; @@ -2605,11 +2615,8 @@ static int fsg_main_thread(void *common_) common->thread_task = NULL; spin_unlock_irq(&common->lock); - /* XXX */ - /* If we are exiting because of a signal, unregister the - * gadget driver. */ - /* if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) */ - /* usb_gadget_unregister_driver(&fsg_driver); */ + if (common->thread_exits) + common->thread_exits(common); /* Let the unbind and cleanup routines know the thread has exited */ complete_and_exit(&common->thread_notifier, 0); @@ -2672,6 +2679,8 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, common->free_storage_on_release = 0; } + common->private_data = cfg->private_data; + common->gadget = gadget; common->ep0 = gadget->ep0; common->ep0req = cdev->req; @@ -2791,6 +2800,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, /* Tell the thread to start working */ + common->thread_exits = cfg->thread_exits; common->thread_task = kthread_create(fsg_main_thread, common, OR(cfg->thread_name, "file-storage")); @@ -3057,6 +3067,9 @@ fsg_config_from_params(struct fsg_config *cfg, cfg->product_name = 0; cfg->release = 0xffff; + cfg->thread_exits = 0; + cfg->private_data = 0; + /* Finalise */ cfg->can_stall = params->stall; } diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index e43ca8f4be14..19619fbf20ac 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -132,9 +132,13 @@ static struct fsg_module_parameters mod_data = { }; FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); +static unsigned long msg_registered = 0; +static void msg_cleanup(void); + static int __init msg_do_config(struct usb_configuration *c) { struct fsg_common *common; + struct fsg_config config; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -142,7 +146,9 @@ static int __init msg_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - common = fsg_common_from_params(0, c->cdev, &mod_data); + fsg_config_from_params(&config, &mod_data); + config.thread_exits = (void(*)(struct fsg_common*))&msg_cleanup; + common = fsg_common_init(0, c->cdev, &config); if (IS_ERR(common)) return PTR_ERR(common); @@ -201,11 +207,7 @@ static int __init msg_bind(struct usb_composite_dev *cdev) return status; dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - return 0; -} - -static int __exit msg_unbind(struct usb_composite_dev *cdev) -{ + set_bit(0, &msg_registered); return 0; } @@ -218,7 +220,6 @@ static struct usb_composite_driver msg_driver = { .dev = &msg_device_desc, .strings = dev_strings, .bind = msg_bind, - .unbind = __exit_p(msg_unbind), }; MODULE_DESCRIPTION(DRIVER_DESC); @@ -231,8 +232,9 @@ static int __init msg_init(void) } module_init(msg_init); -static void __exit msg_cleanup(void) +static void msg_cleanup(void) { - usb_composite_unregister(&msg_driver); + if (test_and_clear_bit(0, &msg_registered)) + usb_composite_unregister(&msg_driver); } module_exit(msg_cleanup); -- cgit v1.2.3 From a8e2db2fc35a9c4599181730ef5772376232e881 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Nov 2009 14:15:27 +0100 Subject: USB: g_multi: Multifunction Composite Gadget added The Multifunction Composite Gadget has two configurations consisting of Ethernet (RNDIS in first and CDC Ethernet in second configuration), CDC Serial and File-backed Storage functions. When connected to a Windows host, the first configuration is chosen thus gadget provides RNDIS Ethernet, serial and mass storage whereas when connected to Linux host, second configuration is chosen thus providing CDC Ethernet, serial and mass storage. Which configurations are built can be configured via KConfig options. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 42 ++++++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/multi.c | 355 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+) create mode 100644 drivers/usb/gadget/multi.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 3bb250fd5321..df8b11d6c0fe 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -812,6 +812,48 @@ config USB_CDC_COMPOSITE Say "y" to link the driver statically, or "m" to build a dynamically linked module. +config USB_G_MULTI + tristate "Multifunction Composite Gadget (EXPERIMENTAL)" + help + The Multifunction Composite Gadget provides Ethernet (RNDIS + and/or CDC Ethernet), mass storage and ACM serial link + interfaces. + + You will be asked too choose which of the two configurations are + to be available in the gadget. At least one configuration must + be choosen to make gadget usable. Selecting more then one + configuration will prevent Windows from automatically detecting + the gadget as a composite gadget an INF file will be needed to + use the gadget. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_multi". + +config USB_G_MULTI_RNDIS + bool "RNDIS + CDC Serial + Storage configuration" + depends on USB_G_MULTI + default y + help + This option enables a configuration with RNDIS, CDC Serial and + Mass Storage functions available in the Multifunction Composite + Gadget. This is configuration dedicated for Windows since RNDIS + is Microsfot's protocol. + + If unsure, say "y". + +config USB_G_MULTI_CDC + bool "CDC Ethernet + CDC Serial + Storage configuration" + depends on USB_G_MULTI + default n + help + This option enables a configuration with CDC Ethernet (ECM), CDC + Serial and Mass Storage functions available in the Multifunction + Composite Gadget. This is configuration dedicated for Windows + since RNDIS is Microsfot's protocol. + + If unsure, say "y". + + # put drivers that need isochronous transfer support (for audio # or video class gadget drivers), or specific hardware, here. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 025ba2ed7907..2e2c047262b7 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -42,6 +42,7 @@ g_file_storage-objs := file_storage.o g_mass_storage-objs := mass_storage.o g_printer-objs := printer.o g_cdc-objs := cdc2.o +g_multi-objs := multi.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_AUDIO) += g_audio.o @@ -53,4 +54,5 @@ obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o +obj-$(CONFIG_USB_G_MULTI) += g_multi.o diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c new file mode 100644 index 000000000000..64711feca845 --- /dev/null +++ b/drivers/usb/gadget/multi.c @@ -0,0 +1,355 @@ +/* + * multi.c -- Multifunction Composite driver + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + + +#if defined CONFIG_USB_G_MULTI_RNDIS +# define CONFIG_USB_ETH_RNDIS y +#endif + + +#define DRIVER_DESC "Multifunction Composite Gadget" +#define DRIVER_VERSION "2009/07/21" + +/*-------------------------------------------------------------------------*/ + +#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */ +#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */ + +/*-------------------------------------------------------------------------*/ + +/* + * kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ + +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" + +#include "u_serial.c" +#include "f_acm.c" + +#include "f_ecm.c" +#include "f_subset.c" +#ifdef CONFIG_USB_ETH_RNDIS +# include "f_rndis.c" +# include "rndis.c" +#endif +#include "u_ether.c" + +#undef DBG /* u_ether.c has broken idea about macros */ +#undef VDBG /* so clean up after it */ +#undef ERROR +#undef INFO +#include "f_mass_storage.c" + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + + /* .bDeviceClass = USB_CLASS_COMM, */ + /* .bDeviceSubClass = 0, */ + /* .bDeviceProtocol = 0, */ + .bDeviceClass = 0xEF, + .bDeviceSubClass = 2, + .bDeviceProtocol = 1, + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(MULTI_VENDOR_NUM), + .idProduct = cpu_to_le16(MULTI_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static u8 hostaddr[ETH_ALEN]; + + + +/****************************** Configurations ******************************/ + +static struct fsg_module_parameters mod_data = { + .stall = 1 +}; +FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); + +static struct fsg_common *fsg_common; + + +#ifdef CONFIG_USB_ETH_RNDIS + +static int __init rndis_do_config(struct usb_configuration *c) +{ + int ret; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + ret = rndis_bind_config(c, hostaddr); + if (ret < 0) + return ret; + + ret = acm_bind_config(c, 0); + if (ret < 0) + return ret; + + ret = fsg_add(c->cdev, c, fsg_common); + if (ret < 0) + return ret; + + return 0; +} + +static struct usb_configuration rndis_config_driver = { + .label = "Multifunction Composite (RNDIS + MS + ACM)", + .bind = rndis_do_config, + .bConfigurationValue = 2, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +#endif + +#ifdef CONFIG_USB_G_MULTI_CDC + +static int __init cdc_do_config(struct usb_configuration *c) +{ + int ret; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + ret = ecm_bind_config(c, hostaddr); + if (ret < 0) + return ret; + + ret = acm_bind_config(c, 0); + if (ret < 0) + return ret; + + ret = fsg_add(c->cdev, c, fsg_common); + if (ret < 0) + return ret; + if (ret < 0) + return ret; + + return 0; +} + +static struct usb_configuration cdc_config_driver = { + .label = "Multifunction Composite (CDC + MS + ACM)", + .bind = cdc_do_config, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +#endif + + + +/****************************** Gadget Bind ******************************/ + + +static int __init multi_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + int status, gcnum; + + if (!can_support_ecm(cdev->gadget)) { + dev_err(&gadget->dev, "controller '%s' not usable\n", + gadget->name); + return -EINVAL; + } + + /* set up network link layer */ + status = gether_setup(cdev->gadget, hostaddr); + if (status < 0) + return status; + + /* set up serial link layer */ + status = gserial_setup(cdev->gadget, 1); + if (status < 0) + goto fail0; + + /* set up mass storage function */ + fsg_common = fsg_common_from_params(0, cdev, &mod_data); + if (IS_ERR(fsg_common)) { + status = PTR_ERR(fsg_common); + goto fail1; + } + + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); + else { + /* We assume that can_support_ecm() tells the truth; + * but if the controller isn't recognized at all then + * that assumption is a bit more likely to be wrong. + */ + WARNING(cdev, "controller '%s' not recognized\n", + gadget->name); + device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099); + } + + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail2; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + goto fail2; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; + +#ifdef CONFIG_USB_ETH_RNDIS + /* register our first configuration */ + status = usb_add_config(cdev, &rndis_config_driver); + if (status < 0) + goto fail2; +#endif + +#ifdef CONFIG_USB_G_MULTI_CDC + /* register our second configuration */ + status = usb_add_config(cdev, &cdc_config_driver); + if (status < 0) + goto fail2; +#endif + + dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); + fsg_common_put(fsg_common); + return 0; + +fail2: + fsg_common_put(fsg_common); +fail1: + gserial_cleanup(); +fail0: + gether_cleanup(); + return status; +} + +static int __exit multi_unbind(struct usb_composite_dev *cdev) +{ + gserial_cleanup(); + gether_cleanup(); + return 0; +} + + +/****************************** Some noise ******************************/ + + +static struct usb_composite_driver multi_driver = { + .name = "g_multi", + .dev = &device_desc, + .strings = dev_strings, + .bind = multi_bind, + .unbind = __exit_p(multi_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Michal Nazarewicz"); +MODULE_LICENSE("GPL"); + +static int __init g_multi_init(void) +{ + return usb_composite_register(&multi_driver); +} +module_init(g_multi_init); + +static void __exit g_multi_cleanup(void) +{ + usb_composite_unregister(&multi_driver); +} +module_exit(g_multi_cleanup); -- cgit v1.2.3 From 62ab43c969ea1641b7b65ee333150648ccb6312d Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 11 Nov 2009 10:28:30 -0800 Subject: USB: xhci: Set transfer descriptor size field correctly. The transfer descriptor (TD) is a series of transfer request buffers (TRBs) that describe the buffer pointer, length, and other characteristics. The xHCI controllers want to know an estimate of how long the TD is, for caching reasons. In each TRB, there is a "TD size" field that provides a rough estimate of the remaining buffers to be transmitted, including the buffer pointed to by that TRB. The TD size is 5 bits long, and contains the remaining size in bytes, right shifted by 10 bits. So a remaining TD size less than 1024 would get a zero in the TD size field, and a remaining size greater than 32767 would get 31 in the field. This patches fixes a bug in the TD_REMAINDER macro that is triggered when the URB has a scatter gather list with a size bigger than 32767 bytes. Not all host controllers pay attention to the TD size field, so the bug will not appear on all USB 3.0 hosts. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 27 ++++++++++++++++++++++++--- drivers/usb/host/xhci.h | 3 --- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 371ba4297d9c..d7e10ea8f080 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1699,6 +1699,21 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); } +/* + * The TD size is the number of bytes remaining in the TD (including this TRB), + * right shifted by 10. + * It must fit in bits 21:17, so it can't be bigger than 31. + */ +static u32 xhci_td_remainder(unsigned int remainder) +{ + u32 max = (1 << (21 - 17 + 1)) - 1; + + if ((remainder >> 10) >= max) + return max << 17; + else + return (remainder >> 10) << 17; +} + static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) { @@ -1756,6 +1771,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, do { u32 field = 0; u32 length_field = 0; + u32 remainder = 0; /* Don't change the cycle bit of the first TRB until later */ if (first_trb) @@ -1785,8 +1801,10 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1), (unsigned int) addr + trb_buff_len); } + remainder = xhci_td_remainder(urb->transfer_buffer_length - + running_total) ; length_field = TRB_LEN(trb_buff_len) | - TD_REMAINDER(urb->transfer_buffer_length - running_total) | + remainder | TRB_INTR_TARGET(0); queue_trb(xhci, ep_ring, false, lower_32_bits(addr), @@ -1899,6 +1917,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Queue the first TRB, even if it's zero-length */ do { + u32 remainder = 0; field = 0; /* Don't change the cycle bit of the first TRB until later */ @@ -1917,8 +1936,10 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td->last_trb = ep_ring->enqueue; field |= TRB_IOC; } + remainder = xhci_td_remainder(urb->transfer_buffer_length - + running_total); length_field = TRB_LEN(trb_buff_len) | - TD_REMAINDER(urb->transfer_buffer_length - running_total) | + remainder | TRB_INTR_TARGET(0); queue_trb(xhci, ep_ring, false, lower_32_bits(addr), @@ -2006,7 +2027,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* If there's data, queue data TRBs */ field = 0; length_field = TRB_LEN(urb->transfer_buffer_length) | - TD_REMAINDER(urb->transfer_buffer_length) | + xhci_td_remainder(urb->transfer_buffer_length) | TRB_INTR_TARGET(0); if (urb->transfer_buffer_length > 0) { if (setup->bRequestType & USB_DIR_IN) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3989aadcf670..bb8e6656cca4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -828,9 +828,6 @@ struct xhci_event_cmd { /* Normal TRB fields */ /* transfer_len bitmasks - bits 0:16 */ #define TRB_LEN(p) ((p) & 0x1ffff) -/* TD size - number of bytes remaining in the TD (including this TRB): - * bits 17 - 21. Shift the number of bytes by 10. */ -#define TD_REMAINDER(p) ((((p) >> 10) & 0x1f) << 17) /* Interrupter Target - which MSI-X vector to target the completion event at */ #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) -- cgit v1.2.3 From 69916a1f34448fd215be405898130a1d7340d407 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 11 Nov 2009 10:28:36 -0800 Subject: USB: xhci: Return -EPROTO on a split transaction error. When the xHCI hardware says a transfer completed with a split transaction error, set the URB status to -EPROTO. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index d7e10ea8f080..98437ffb065e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1139,6 +1139,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_warn(xhci, "WARN: TRB error on endpoint\n"); status = -EILSEQ; break; + case COMP_SPLIT_ERR: case COMP_TX_ERR: xhci_warn(xhci, "WARN: transfer error on endpoint\n"); status = -EPROTO; -- cgit v1.2.3 From 06173ddb02c8fb606bf54539514df5225bf8b528 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 11 Nov 2009 10:28:40 -0800 Subject: USB: xhci: Return success for vendor-specific info codes. An xHCI host controller manufacturer can choose to implement several vendor-specific informational completion codes. These are all to be treated like a successful transfer completion. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 98437ffb065e..1549b9ceb91a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1153,6 +1153,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, status = -ENOSR; break; default: + if (trb_comp_code >= 224 && trb_comp_code <= 255) { + /* Vendor defined "informational" completion code, + * treat as not-an-error. + */ + xhci_dbg(xhci, "Vendor defined info completion code %u\n", + trb_comp_code); + xhci_dbg(xhci, "Treating code as success.\n"); + status = 0; + break; + } xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n"); urb = NULL; goto cleanup; -- cgit v1.2.3 From 5ac9de069091b84fb646124c846674ac8248fb0f Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 11 Nov 2009 10:28:44 -0800 Subject: USB: xhci: Handle errors that cause endpoint halts. The xHCI 0.95 and 0.96 specification defines several transfer buffer request completion codes that indicate a USB transaction error occurred. When a stall, babble, transaction, or split transaction error completion code is set, the xHCI has halted that endpoint ring. Software must issue a Reset Endpoint command and a Set Transfer Ring Dequeue Pointer command to clean up the halted ring. The USB device driver is supposed to call into usb_reset_endpoint() when an endpoint stalls. That calls into the xHCI driver to issue the proper commands. However, drivers don't call that function for the other errors that cause the xHC to halt the endpoint ring. If a babble, transaction, or split transaction error occurs, check if the endpoint context reports a halted condition, and clean up the endpoint ring if it does. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 79 +++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 1549b9ceb91a..2e346334a363 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1037,6 +1037,45 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, return 0; } +static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, + struct xhci_td *td, union xhci_trb *event_trb) +{ + struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; + ep->ep_state |= EP_HALTED; + ep->stopped_td = td; + ep->stopped_trb = event_trb; + xhci_queue_reset_ep(xhci, slot_id, ep_index); + xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index); + xhci_ring_cmd_db(xhci); +} + +/* Check if an error has halted the endpoint ring. The class driver will + * cleanup the halt for a non-default control endpoint if we indicate a stall. + * However, a babble and other errors also halt the endpoint ring, and the class + * driver won't clear the halt in that case, so we need to issue a Set Transfer + * Ring Dequeue Pointer command manually. + */ +static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci, + struct xhci_ep_ctx *ep_ctx, + unsigned int trb_comp_code) +{ + /* TRB completion codes that may require a manual halt cleanup */ + if (trb_comp_code == COMP_TX_ERR || + trb_comp_code == COMP_BABBLE || + trb_comp_code == COMP_SPLIT_ERR) + /* The 0.96 spec says a babbling control endpoint + * is not halted. The 0.96 spec says it is. Some HW + * claims to be 0.95 compliant, but it halts the control + * endpoint anyway. Check if a babble halted the + * endpoint. + */ + if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_HALTED) + return 1; + + return 0; +} + /* * If this function returns an error condition, it means it got a Transfer * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. @@ -1191,15 +1230,14 @@ static int handle_tx_event(struct xhci_hcd *xhci, else status = 0; break; - case COMP_BABBLE: - /* The 0.96 spec says a babbling control endpoint - * is not halted. The 0.96 spec says it is. Some HW - * claims to be 0.95 compliant, but it halts the control - * endpoint anyway. Check if a babble halted the - * endpoint. - */ - if (ep_ctx->ep_info != EP_STATE_HALTED) + + default: + if (!xhci_requires_manual_halt_cleanup(xhci, + ep_ctx, trb_comp_code)) break; + xhci_dbg(xhci, "TRB error code %u, " + "halted endpoint index = %u\n", + trb_comp_code, ep_index); /* else fall through */ case COMP_STALL: /* Did we transfer part of the data (middle) phase? */ @@ -1211,15 +1249,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, else td->urb->actual_length = 0; - ep->stopped_td = td; - ep->stopped_trb = event_trb; - xhci_queue_reset_ep(xhci, slot_id, ep_index); - xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index); - xhci_ring_cmd_db(xhci); + xhci_cleanup_halted_endpoint(xhci, + slot_id, ep_index, td, event_trb); goto td_cleanup; - default: - /* Others already handled above */ - break; } /* * Did we transfer any data, despite the errors that might have @@ -1357,16 +1389,25 @@ static int handle_tx_event(struct xhci_hcd *xhci, ep->stopped_td = td; ep->stopped_trb = event_trb; } else { - if (trb_comp_code == COMP_STALL || - trb_comp_code == COMP_BABBLE) { + if (trb_comp_code == COMP_STALL) { /* The transfer is completed from the driver's * perspective, but we need to issue a set dequeue * command for this stalled endpoint to move the dequeue * pointer past the TD. We can't do that here because - * the halt condition must be cleared first. + * the halt condition must be cleared first. Let the + * USB class driver clear the stall later. */ ep->stopped_td = td; ep->stopped_trb = event_trb; + } else if (xhci_requires_manual_halt_cleanup(xhci, + ep_ctx, trb_comp_code)) { + /* Other types of errors halt the endpoint, but the + * class driver doesn't call usb_reset_endpoint() unless + * the error is -EPIPE. Clear the halted status in the + * xHCI hardware manually. + */ + xhci_cleanup_halted_endpoint(xhci, + slot_id, ep_index, td, event_trb); } else { /* Update ring dequeue pointer */ while (ep_ring->dequeue != td->last_trb) -- cgit v1.2.3 From cb255a06a10b29201221b79d3f7c50424aa6099e Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 16 Nov 2009 16:19:19 +0530 Subject: USB: musb: tweak musb_read_fifo() to avoid unused warnings Otherwise gcc will whine about epnum/dma_reg being unused when building for BF54x parts. Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Cliff Cai Signed-off-by: Anand Gadiyar Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index fcec87ea709e..ef51eb4343ab 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -53,13 +53,11 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) { void __iomem *fifo = hw_ep->fifo; + +#ifdef CONFIG_BF52x u8 epnum = hw_ep->epnum; u16 dma_reg = 0; - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", - 'R', hw_ep->epnum, fifo, len, dst); - -#ifdef CONFIG_BF52x invalidate_dcache_range((unsigned int)dst, (unsigned int)(dst + len)); @@ -102,6 +100,9 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) len & 0x01 ? (len >> 1) + 1 : len >> 1); #endif + DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + 'R', hw_ep->epnum, fifo, len, dst); + dump_fifo_data(dst, len); } -- cgit v1.2.3 From fd9ef34c8e634f70203086d02144d165c72524e7 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Mon, 16 Nov 2009 16:19:20 +0530 Subject: USB: musb: kill compile warning for Blackfin systems The Blackfin version of musb_read_target_reg_base() returns a u16 when the common code expects a (void __iomem *), so update the Blackfin function to return the right value. This fixes the compile warning: drivers/usb/musb/musb_core.c: In function 'musb_core_init': drivers/usb/musb/musb_core.c:1448: warning: assignment makes pointer from integer without a cast Signed-off-by: Bryan Wu Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_regs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index cc1d71b57d3c..473a94ef905f 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -465,9 +465,9 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return 0; } -static inline u16 musb_read_target_reg_base(u8 i, void __iomem *mbase) +static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) { - return 0; + return NULL; } static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, -- cgit v1.2.3 From ad539722c0dbff57f2cf9a2fbb63b0641fcf4c95 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Mon, 16 Nov 2009 16:19:21 +0530 Subject: USB: musb: kill some useless comments in Blackfin driver Signed-off-by: Bryan Wu Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index ef51eb4343ab..4fe7b5411a2b 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -262,10 +262,6 @@ int __init musb_platform_init(struct musb *musb) SSYNC(); } - /* TODO - * Set SIC-IVG register - */ - /* Configure PLL oscillator register */ bfin_write_USB_PLLOSC_CTRL(0x30a8); SSYNC(); -- cgit v1.2.3 From d71e7415b26bed55d723dd9b7ec5e849895f13de Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 16 Nov 2009 20:04:21 -0500 Subject: USB: musb: update Blackfin processor dependency Do not allow MUSB driver to be selected on derivatives that don't have the MUSB controller on them. Signed-off-by: Sonic Zhang Signed-off-by: Bryan Wu Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index b84abd8ee8a5..20fbdcda0973 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -9,7 +9,7 @@ comment "Enable Host or Gadget support to see Inventra options" # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller config USB_MUSB_HDRC depends on (USB || USB_GADGET) - depends on (ARM || BLACKFIN) + depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523)) select NOP_USB_XCEIV if ARCH_DAVINCI select TWL4030_USB if MACH_OMAP_3430SDP select NOP_USB_XCEIV if MACH_OMAP3EVM -- cgit v1.2.3 From 62733d3b364a64988efb280dffc6d2eee1d50192 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 16 Nov 2009 19:13:08 +0530 Subject: USB: musb: add notes for Blackfin anomalies Add some helpful notes about how the driver works around different anomalies that exist in the on-chip host controller. Signed-off-by: Sonic Zhang Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/usb/musb/blackfin.h b/drivers/usb/musb/blackfin.h index a240c1e53d16..b49a362fb9fa 100644 --- a/drivers/usb/musb/blackfin.h +++ b/drivers/usb/musb/blackfin.h @@ -14,6 +14,33 @@ * Blackfin specific definitions */ +/* Anomalies notes: + * + * 05000450 - USB DMA Mode 1 Short Packet Data Corruption: + * MUSB driver is designed to transfer buffer of N * maxpacket size + * in DMA mode 1 and leave the rest of the data to the next + * transfer in DMA mode 0, so we never transmit a short packet in + * DMA mode 1. + * + * 05000463 - This anomaly doesn't affect this driver since it + * never uses L1 or L2 memory as data destination. + * + * 05000464 - This anomaly doesn't affect this driver since it + * never uses L1 or L2 memory as data source. + * + * 05000465 - The anomaly can be seen when SCLK is over 100 MHz, and there is + * no way to workaround for bulk endpoints. Since the wMaxPackSize + * of bulk is less than or equal to 512, while the fifo size of + * endpoint 5, 6, 7 is 1024, the double buffer mode is enabled + * automatically when these endpoints are used for bulk OUT. + * + * 05000466 - This anomaly doesn't affect this driver since it never mixes + * concurrent DMA and core accesses to the TX endpoint FIFOs. + * + * 05000467 - The workaround for this anomaly will introduce another + * anomaly - 05000465. + */ + #undef DUMP_FIFO_DATA #ifdef DUMP_FIFO_DATA static void dump_fifo_data(u8 *buf, u16 len) -- cgit v1.2.3 From c0be077d0d9615de41d7a5b35bf34055487cf4b9 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 16 Nov 2009 16:19:24 +0530 Subject: USB: musb: add work around for Blackfin anomaly 05000456 Only allow USE_MODE1 when the Blackfin part is not affected by anomaly 05000456 (USB Receive Interrupt Is Not Generated in DMA Mode 1) since we can't support the mode in that case. Signed-off-by: Sonic Zhang Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_dma.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 0a2c4e3602c1..916065ba9e70 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -80,6 +80,17 @@ struct musb_hw_ep; #define tusb_dma_omap() 0 #endif +/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1 + * Only allow DMA mode 1 to be used when the USB will actually generate the + * interrupts we expect. + */ +#ifdef CONFIG_BLACKFIN +# undef USE_MODE1 +# if !ANOMALY_05000456 +# define USE_MODE1 +# endif +#endif + /* * DMA channel status ... updated by the dma controller driver whenever that * status changes, and protected by the overall controller spinlock. -- cgit v1.2.3 From 9596c78d93018e80fbdff462ba3b02061924fedb Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Mon, 16 Nov 2009 16:19:25 +0530 Subject: USB: musb: fix musb_platform_set_mode() definition Update function definition to match latest MUSB framework. Signed-off-by: Bryan Wu Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 4fe7b5411a2b..fe4934d9602c 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -226,8 +226,9 @@ int musb_platform_get_vbus_status(struct musb *musb) return 0; } -void musb_platform_set_mode(struct musb *musb, u8 musb_mode) +int musb_platform_set_mode(struct musb *musb, u8 musb_mode) { + return -EIO; } int __init musb_platform_init(struct musb *musb) -- cgit v1.2.3 From 8d67796249b82e91668fd8409593c79dd823a63a Mon Sep 17 00:00:00 2001 From: Cliff Cai Date: Mon, 16 Nov 2009 16:19:26 +0530 Subject: USB: musb: clear the Blackfin interrupt pending bits early in the ISR If we clear the interrupt pending bits at the end, we sometimes return too fast and have the same interrupt assert itself. There is no way in a Blackfin system to force a sync of this state, so the hardware manual instructs people to clear interrupt flags early in their ISR. Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musbhsdma.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 5e83f96d6b77..c767387f507b 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -259,6 +259,11 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) if (!int_hsdma) goto done; +#ifdef CONFIG_BLACKFIN + /* Clear DMA interrupt flags */ + musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma); +#endif + for (bchannel = 0; bchannel < MUSB_HSDMA_CHANNELS; bchannel++) { if (int_hsdma & (1 << bchannel)) { musb_channel = (struct musb_dma_channel *) @@ -324,11 +329,6 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) } } -#ifdef CONFIG_BLACKFIN - /* Clear DMA interrup flags */ - musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma); -#endif - retval = IRQ_HANDLED; done: spin_unlock_irqrestore(&musb->lock, flags); -- cgit v1.2.3 From 0241a170c44630ac4d2d05fac8d5b77f482d5fd4 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 16 Nov 2009 16:19:27 +0530 Subject: USB: musb: error out when anomaly 05000380 is applicable Since we can't work around anomaly 05000380, throw a build error up and instruct the user to use a different mode. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/musb/blackfin.h b/drivers/usb/musb/blackfin.h index b49a362fb9fa..10b7d7584f4b 100644 --- a/drivers/usb/musb/blackfin.h +++ b/drivers/usb/musb/blackfin.h @@ -41,6 +41,16 @@ * anomaly - 05000465. */ +/* The Mentor USB DMA engine on BF52x (silicon v0.0 and v0.1) seems to be + * unstable in host mode. This may be caused by Anomaly 05000380. After + * digging out the root cause, we will change this number accordingly. + * So, need to either use silicon v0.2+ or disable DMA mode in MUSB. + */ +#if ANOMALY_05000380 && defined(CONFIG_BF52x) && \ + defined(CONFIG_USB_MUSB_HDRC) && !defined(CONFIG_MUSB_PIO_ONLY) +# error "Please use PIO mode in MUSB driver on bf52x chip v0.0 and v0.1" +#endif + #undef DUMP_FIFO_DATA #ifdef DUMP_FIFO_DATA static void dump_fifo_data(u8 *buf, u16 len) -- cgit v1.2.3 From 2eda1df3550d1c4824300c66eef6d00fd177f27f Mon Sep 17 00:00:00 2001 From: Cliff Cai Date: Mon, 16 Nov 2009 20:05:03 -0500 Subject: USB: musb: Blackfin code needs NOP_USB_XCEIV too Otherwise we get the link failure: drivers/built-in.o: In function 'musb_platform_init': drivers/usb/musb/blackfin.c:300: undefined reference to 'usb_nop_xceiv_register' make: *** [.tmp_vmlinux1] Error 1 Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 20fbdcda0973..d9db86498022 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -10,9 +10,8 @@ comment "Enable Host or Gadget support to see Inventra options" config USB_MUSB_HDRC depends on (USB || USB_GADGET) depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523)) - select NOP_USB_XCEIV if ARCH_DAVINCI + select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN) select TWL4030_USB if MACH_OMAP_3430SDP - select NOP_USB_XCEIV if MACH_OMAP3EVM select USB_OTG_UTILS tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help -- cgit v1.2.3 From db362ebb1f3149685a793c99fa159c53a675fd6c Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 16 Nov 2009 16:19:29 +0530 Subject: USB: musb: fix printf warning in debug code The debug code in the DMA ISR uses a %d for a size_t when it should be using %zu. Otherwise gcc whines with: drivers/usb/musb/musbhsdma.c: In function 'dma_controller_irq': drivers/usb/musb/musbhsdma.c:288: warning: format '%d' expects type 'int', but argument 7 has type 'size_t' Signed-off-by: Mike Frysinger Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musbhsdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index c767387f507b..a237550f91bf 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -285,7 +285,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) channel->actual_len = addr - musb_channel->start_addr; - DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n", + DBG(2, "ch %p, 0x%x -> 0x%x (%zu / %d) %s\n", channel, musb_channel->start_addr, addr, channel->actual_len, musb_channel->len, -- cgit v1.2.3 From 1e68d8ddf960ec24932349c1909dba6d38ef19de Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Mon, 16 Nov 2009 21:09:21 +0530 Subject: USB: MUSB: save hardware revision at init MUSB: save hardware revision at init This can be used later to flag workarounds for issues affecting particular revisions. Saving this at init avoids having to read the HWVERS register multiple times in code. While at it, use macros to extract the version information instead of using hardcoded values. Signed-off-by: Anand Gadiyar Cc: Ajay Kumar Gupta Acked-by: Felipe Balbi Cc: David Brownell Cc: Sergei Shtylyov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 10 ++++------ drivers/usb/musb/musb_core.h | 8 ++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 547e0e390726..49f2346afad3 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1319,7 +1319,6 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) #endif u8 reg; char *type; - u16 hwvers, rev_major, rev_minor; char aInfo[78], aRevision[32], aDate[12]; void __iomem *mbase = musb->mregs; int status = 0; @@ -1391,11 +1390,10 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) } /* log release info */ - hwvers = musb_read_hwvers(mbase); - rev_major = (hwvers >> 10) & 0x1f; - rev_minor = hwvers & 0x3ff; - snprintf(aRevision, 32, "%d.%d%s", rev_major, - rev_minor, (hwvers & 0x8000) ? "RC" : ""); + musb->hwvers = musb_read_hwvers(mbase); + snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers), + MUSB_HWVERS_MINOR(musb->hwvers), + (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : ""); printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n", musb_driver_name, type, aRevision, aDate); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 6aa5f22e5274..03d50909b078 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -322,6 +322,14 @@ struct musb { struct clk *clock; irqreturn_t (*isr)(int, void *); struct work_struct irq_work; +#define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f) +#define MUSB_HWVERS_MINOR(x) (x & 0x3ff) +#define MUSB_HWVERS_RC 0x8000 +#define MUSB_HWVERS_1300 0x52C +#define MUSB_HWVERS_1400 0x590 +#define MUSB_HWVERS_1800 0x720 +#define MUSB_HWVERS_2000 0x800 + u16 hwvers; /* this hub status bit is reserved by USB 2.0 and not seen by usbcore */ #define MUSB_PORT_STAT_RESUME (1 << 31) -- cgit v1.2.3 From 176939f0d75ee5feafca29f836073fdd5ad26c69 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 16 Nov 2009 16:24:05 +0530 Subject: USB: musb_gadget_ep0: fix unhandled endpoint 0 IRQs, again Commit a5073b52833e4df8e16c93dc4cbb7e0c558c74a2 (musb_gadget: fix unhandled endpoint 0 IRQs) somehow missed its key change: "The gadget EP0 code routinely ignores an interrupt at end of the data phase because of musb_g_ep0_giveback() resetting the state machine to "idle, waiting for SETUP" phase prematurely." So, the majority of the cases of unhandled IRQs is still unfixed... Signed-off-by: Sergei Shtylyov Signed-off-by: Anand Gadiyar Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget_ep0.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 522efb31b56b..1c44b975049c 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -199,7 +199,6 @@ service_in_request(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest) static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req) { musb_g_giveback(&musb->endpoints[0].ep_in, req, 0); - musb->ep0_state = MUSB_EP0_STAGE_SETUP; } /* -- cgit v1.2.3 From add94157a1132bdbdd8d61b5a6ec6c023336609a Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 18 Nov 2009 22:51:51 +0300 Subject: USB: musb_gadget: implement set_wedge() method Implement the driver's set_wedge() method by adding the 'wedged' flag to the 'struct musb_ep'. Signed-off-by: Sergei Shtylyov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 20 +++++++++++++++++++- drivers/usb/musb/musb_gadget.h | 2 ++ drivers/usb/musb/musb_gadget_ep0.c | 6 +++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 74073f9a43f0..173c963e7f02 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -966,6 +966,7 @@ static int musb_gadget_enable(struct usb_ep *ep, musb_ep->desc = desc; musb_ep->busy = 0; + musb_ep->wedged = 0; status = 0; pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n", @@ -1262,7 +1263,8 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value) goto done; } } - } + } else + musb_ep->wedged = 0; /* set/clear the stall and toggle bits */ DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); @@ -1301,6 +1303,21 @@ done: return status; } +/* + * Sets the halt feature with the clear requests ignored + */ +int musb_gadget_set_wedge(struct usb_ep *ep) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + + if (!ep) + return -EINVAL; + + musb_ep->wedged = 1; + + return usb_ep_set_halt(ep); +} + static int musb_gadget_fifo_status(struct usb_ep *ep) { struct musb_ep *musb_ep = to_musb_ep(ep); @@ -1371,6 +1388,7 @@ static const struct usb_ep_ops musb_ep_ops = { .queue = musb_gadget_queue, .dequeue = musb_gadget_dequeue, .set_halt = musb_gadget_set_halt, + .set_wedge = musb_gadget_set_wedge, .fifo_status = musb_gadget_fifo_status, .fifo_flush = musb_gadget_fifo_flush }; diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 59502da9f739..90966695ae53 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -75,6 +75,8 @@ struct musb_ep { /* later things are modified based on usage */ struct list_head req_list; + u8 wedged; + /* true if lock must be dropped but req_list may not be advanced */ u8 busy; }; diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 1c44b975049c..c63aff110c45 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -273,6 +273,11 @@ __acquires(musb->lock) if (!musb_ep->desc) break; + handled = 1; + /* Ignore request if endpoint is wedged */ + if (musb_ep->wedged) + break; + /* REVISIT do it directly, no locking games */ spin_unlock(&musb->lock); musb_gadget_set_halt(&musb_ep->end_point, 0); @@ -280,7 +285,6 @@ __acquires(musb->lock) /* select ep0 again */ musb_ep_select(mbase, 0); - handled = 1; } break; default: /* class, vendor, etc ... delegate */ -- cgit v1.2.3 From 579f97d66369627e3e3a4d3cb094e8c702d936a0 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 18 Nov 2009 22:54:32 +0300 Subject: USB: musb_gadget_ep0: stop abusing musb_gadget_set_halt() Stop playing with musb->lock and abusing musb_gadget_set_halt() in the code clearing the endpoint halt feature -- instead, manipulate the registers directly. While at it, get rid uf unneeded line breaks and over-indentation in the code setting the endpoint halt feature. Signed-off-by: Sergei Shtylyov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.h | 2 -- drivers/usb/musb/musb_gadget_ep0.c | 63 +++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 90966695ae53..c8b140325d82 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -105,6 +105,4 @@ extern void musb_gadget_cleanup(struct musb *); extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int); -extern int musb_gadget_set_halt(struct usb_ep *ep, int value); - #endif /* __MUSB_GADGET_H */ diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index c63aff110c45..8fba3f11e473 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -257,19 +257,25 @@ __acquires(musb->lock) case USB_RECIP_INTERFACE: break; case USB_RECIP_ENDPOINT:{ - const u8 num = ctrlrequest->wIndex & 0x0f; - struct musb_ep *musb_ep; + const u8 epnum = + ctrlrequest->wIndex & 0x0f; + struct musb_ep *musb_ep; + struct musb_hw_ep *ep; + void __iomem *regs; + int is_in; + u16 csr; - if (num == 0 - || num >= MUSB_C_NUM_EPS - || ctrlrequest->wValue - != USB_ENDPOINT_HALT) + if (epnum == 0 || epnum >= MUSB_C_NUM_EPS || + ctrlrequest->wValue != USB_ENDPOINT_HALT) break; - if (ctrlrequest->wIndex & USB_DIR_IN) - musb_ep = &musb->endpoints[num].ep_in; + ep = musb->endpoints + epnum; + regs = ep->regs; + is_in = ctrlrequest->wIndex & USB_DIR_IN; + if (is_in) + musb_ep = &ep->ep_in; else - musb_ep = &musb->endpoints[num].ep_out; + musb_ep = &ep->ep_out; if (!musb_ep->desc) break; @@ -278,10 +284,23 @@ __acquires(musb->lock) if (musb_ep->wedged) break; - /* REVISIT do it directly, no locking games */ - spin_unlock(&musb->lock); - musb_gadget_set_halt(&musb_ep->end_point, 0); - spin_lock(&musb->lock); + musb_ep_select(mbase, epnum); + if (is_in) { + csr = musb_readw(regs, MUSB_TXCSR); + csr |= MUSB_TXCSR_CLRDATATOG | + MUSB_TXCSR_P_WZC_BITS; + csr &= ~(MUSB_TXCSR_P_SENDSTALL | + MUSB_TXCSR_P_SENTSTALL | + MUSB_TXCSR_TXPKTRDY); + musb_writew(regs, MUSB_TXCSR, csr); + } else { + csr = musb_readw(regs, MUSB_RXCSR); + csr |= MUSB_RXCSR_CLRDATATOG | + MUSB_RXCSR_P_WZC_BITS; + csr &= ~(MUSB_RXCSR_P_SENDSTALL | + MUSB_RXCSR_P_SENTSTALL); + musb_writew(regs, MUSB_RXCSR, csr); + } /* select ep0 again */ musb_ep_select(mbase, 0); @@ -377,10 +396,8 @@ stall: int is_in; u16 csr; - if (epnum == 0 - || epnum >= MUSB_C_NUM_EPS - || ctrlrequest->wValue - != USB_ENDPOINT_HALT) + if (epnum == 0 || epnum >= MUSB_C_NUM_EPS || + ctrlrequest->wValue != USB_ENDPOINT_HALT) break; ep = musb->endpoints + epnum; @@ -395,24 +412,20 @@ stall: musb_ep_select(mbase, epnum); if (is_in) { - csr = musb_readw(regs, - MUSB_TXCSR); + csr = musb_readw(regs, MUSB_TXCSR); if (csr & MUSB_TXCSR_FIFONOTEMPTY) csr |= MUSB_TXCSR_FLUSHFIFO; csr |= MUSB_TXCSR_P_SENDSTALL | MUSB_TXCSR_CLRDATATOG | MUSB_TXCSR_P_WZC_BITS; - musb_writew(regs, MUSB_TXCSR, - csr); + musb_writew(regs, MUSB_TXCSR, csr); } else { - csr = musb_readw(regs, - MUSB_RXCSR); + csr = musb_readw(regs, MUSB_RXCSR); csr |= MUSB_RXCSR_P_SENDSTALL | MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_CLRDATATOG | MUSB_RXCSR_P_WZC_BITS; - musb_writew(regs, MUSB_RXCSR, - csr); + musb_writew(regs, MUSB_RXCSR, csr); } /* select ep0 again */ -- cgit v1.2.3 From cc1b8658d8266021f146fa9bac67cbd4765ae6b1 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 18 Nov 2009 22:55:28 +0300 Subject: USB: musb_gadget: remove pointless loop Remove the pointless 'do () while (0)' loop from musb_g_tx() -- it makes this function symmetric to musb_g_rx()... Signed-off-by: Sergei Shtylyov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 174 +++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 92 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 173c963e7f02..07d5dc97ff5e 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -429,112 +429,102 @@ void musb_g_tx(struct musb *musb, u8 epnum) DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); dma = is_dma_capable() ? musb_ep->dma : NULL; - do { - /* REVISIT for high bandwidth, MUSB_TXCSR_P_INCOMPTX - * probably rates reporting as a host error + + /* + * REVISIT: for high bandwidth, MUSB_TXCSR_P_INCOMPTX + * probably rates reporting as a host error. + */ + if (csr & MUSB_TXCSR_P_SENTSTALL) { + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~MUSB_TXCSR_P_SENTSTALL; + musb_writew(epio, MUSB_TXCSR, csr); + return; + } + + if (csr & MUSB_TXCSR_P_UNDERRUN) { + /* We NAKed, no big deal... little reason to care. */ + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, csr); + DBG(20, "underrun on ep%d, req %p\n", epnum, request); + } + + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + /* + * SHOULD NOT HAPPEN... has with CPPI though, after + * changing SENDSTALL (and other cases); harmless? */ - if (csr & MUSB_TXCSR_P_SENTSTALL) { - csr |= MUSB_TXCSR_P_WZC_BITS; - csr &= ~MUSB_TXCSR_P_SENTSTALL; - musb_writew(epio, MUSB_TXCSR, csr); - break; - } + DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); + return; + } - if (csr & MUSB_TXCSR_P_UNDERRUN) { - /* we NAKed, no big deal ... little reason to care */ + if (request) { + u8 is_dma = 0; + + if (dma && (csr & MUSB_TXCSR_DMAENAB)) { + is_dma = 1; csr |= MUSB_TXCSR_P_WZC_BITS; - csr &= ~(MUSB_TXCSR_P_UNDERRUN - | MUSB_TXCSR_TXPKTRDY); + csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN | + MUSB_TXCSR_TXPKTRDY); musb_writew(epio, MUSB_TXCSR, csr); - DBG(20, "underrun on ep%d, req %p\n", epnum, request); + /* Ensure writebuffer is empty. */ + csr = musb_readw(epio, MUSB_TXCSR); + request->actual += musb_ep->dma->actual_len; + DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n", + epnum, csr, musb_ep->dma->actual_len, request); } - if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { - /* SHOULD NOT HAPPEN ... has with cppi though, after - * changing SENDSTALL (and other cases); harmless? + if (is_dma || request->actual == request->length) { + /* + * First, maybe a terminating short packet. Some DMA + * engines might handle this by themselves. */ - DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); - break; - } - - if (request) { - u8 is_dma = 0; - - if (dma && (csr & MUSB_TXCSR_DMAENAB)) { - is_dma = 1; - csr |= MUSB_TXCSR_P_WZC_BITS; - csr &= ~(MUSB_TXCSR_DMAENAB - | MUSB_TXCSR_P_UNDERRUN - | MUSB_TXCSR_TXPKTRDY); - musb_writew(epio, MUSB_TXCSR, csr); - /* ensure writebuffer is empty */ - csr = musb_readw(epio, MUSB_TXCSR); - request->actual += musb_ep->dma->actual_len; - DBG(4, "TXCSR%d %04x, dma off, " - "len %zu, req %p\n", - epnum, csr, - musb_ep->dma->actual_len, - request); - } - - if (is_dma || request->actual == request->length) { - - /* First, maybe a terminating short packet. - * Some DMA engines might handle this by - * themselves. - */ - if ((request->zero - && request->length - && (request->length - % musb_ep->packet_sz) - == 0) + if ((request->zero && request->length + && request->length % musb_ep->packet_sz == 0) #ifdef CONFIG_USB_INVENTRA_DMA - || (is_dma && - ((!dma->desired_mode) || - (request->actual & - (musb_ep->packet_sz - 1)))) + || (is_dma && (!dma->desired_mode || + (request->actual & + (musb_ep->packet_sz - 1)))) #endif - ) { - /* on dma completion, fifo may not - * be available yet ... - */ - if (csr & MUSB_TXCSR_TXPKTRDY) - break; - - DBG(4, "sending zero pkt\n"); - musb_writew(epio, MUSB_TXCSR, - MUSB_TXCSR_MODE - | MUSB_TXCSR_TXPKTRDY); - request->zero = 0; - } - - /* ... or if not, then complete it */ - musb_g_giveback(musb_ep, request, 0); - - /* kickstart next transfer if appropriate; - * the packet that just completed might not - * be transmitted for hours or days. - * REVISIT for double buffering... - * FIXME revisit for stalls too... + ) { + /* + * On DMA completion, FIFO may not be + * available yet... */ - musb_ep_select(mbase, epnum); - csr = musb_readw(epio, MUSB_TXCSR); - if (csr & MUSB_TXCSR_FIFONOTEMPTY) - break; - request = musb_ep->desc - ? next_request(musb_ep) - : NULL; - if (!request) { - DBG(4, "%s idle now\n", - musb_ep->end_point.name); - break; - } + if (csr & MUSB_TXCSR_TXPKTRDY) + return; + + DBG(4, "sending zero pkt\n"); + musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE + | MUSB_TXCSR_TXPKTRDY); + request->zero = 0; } - txstate(musb, to_musb_request(request)); + /* ... or if not, then complete it. */ + musb_g_giveback(musb_ep, request, 0); + + /* + * Kickstart next transfer if appropriate; + * the packet that just completed might not + * be transmitted for hours or days. + * REVISIT for double buffering... + * FIXME revisit for stalls too... + */ + musb_ep_select(mbase, epnum); + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) + return; + + if (!musb_ep->desc) { + DBG(4, "%s idle now\n", + musb_ep->end_point.name); + return; + } else + request = next_request(musb_ep); } - } while (0); + txstate(musb, to_musb_request(request)); + } } /* ------------------------------------------------------------ */ -- cgit v1.2.3 From c816bb25441db27ff1b6578e137fd81f53ea7851 Mon Sep 17 00:00:00 2001 From: Andre Herms Date: Thu, 19 Nov 2009 18:14:49 +0100 Subject: USB: usbtmc: repeat usb_bulk_msg until whole message is transfered usb_bulk_msg() transfers only bytes up to the maximum packet size. It must be repeated by the usbtmc driver until all bytes of a TMC message are transfered. Without this patch, ETIMEDOUT is reported when writing TMC messages larger than the maximum USB bulk size and the transfer remains incomplete. The user will notice that the device hangs and must be reset by either closing the application or pulling the plug. Signed-off-by: Andre Herms Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 574ede15de85..0ce1bffa3936 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -562,10 +562,16 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, n_bytes = roundup(12 + this_part, 4); memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part)); - retval = usb_bulk_msg(data->usb_dev, - usb_sndbulkpipe(data->usb_dev, - data->bulk_out), - buffer, n_bytes, &actual, USBTMC_TIMEOUT); + do { + retval = usb_bulk_msg(data->usb_dev, + usb_sndbulkpipe(data->usb_dev, + data->bulk_out), + buffer, n_bytes, + &actual, USBTMC_TIMEOUT); + if (retval != 0) + break; + n_bytes -= actual; + } while (n_bytes); data->bTag_last_write = data->bTag; data->bTag++; -- cgit v1.2.3 From f167be8e2ec668bd98761479af9599d543de6558 Mon Sep 17 00:00:00 2001 From: "Aguilar Pena, Leed" Date: Fri, 20 Nov 2009 11:32:53 -0600 Subject: USB: twl4030: Enable USB regulators before enabling USB charging For TWL family of power management ICs, USB charging works only when USB regulators are in enabled state: 3v1, 1v5, 1v8 On a USB cable attach, twl4030_phy_resume(twl) function enables the regulators. Enable USB charging, only after all regulators are enabled. Its observed that enabling USB charging before regulators are enabled, causes USB charging to fail. Tested on: Zoom2: omap3430: ES3.1 + TWL5030 Needs T2-MADC and T2-BCI drivers which are still not upstreamed. Signed-off-by: Moiz Sonasath Signed-off-by: Leed Aguilar Acked-by: Vikram Pandita Acked-by: Anand Gadiyar Acked-by: Nishanth Menon Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/twl4030-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 9e3e7a5c258b..bd9883f41e63 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -598,12 +598,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - twl4030charger_usb_en(status == USB_LINK_VBUS); - if (status == USB_LINK_NONE) twl4030_phy_suspend(twl, 0); else twl4030_phy_resume(twl); + + twl4030charger_usb_en(status == USB_LINK_VBUS); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); -- cgit v1.2.3