From d01f87c0ffa96cb44faa78710711eb6e974b891c Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 4 Oct 2012 09:53:43 -0700 Subject: USB: Enable LPM after a failed probe. Before a driver is probed, we want to disable USB 3.0 Link Power Management (LPM), in case the driver needs hub-initiated LPM disabled. After the probe finishes, we want to attempt to re-enable LPM, order to balance the LPM ref count. When a probe fails (such as when libusual doesn't want to bind to a USB 3.0 mass storage device), make sure to balance the LPM ref counts by re-enabling LPM. This patch should be backported to kernels as old as 3.5, that contain the commit 8306095fd2c1100e8244c09bf560f97aca5a311d "USB: Disable USB 3.0 LPM in critical sections." Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/driver.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ddd820d25288..6056db7af410 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -367,6 +367,10 @@ static int usb_probe_interface(struct device *dev) intf->condition = USB_INTERFACE_UNBOUND; usb_cancel_queued_reset(intf); + /* If the LPM disable succeeded, balance the ref counts. */ + if (!lpm_disable_error) + usb_unlocked_enable_lpm(udev); + /* Unbound interfaces are always runtime-PM-disabled and -suspended */ if (driver->supports_autosuspend) pm_runtime_disable(dev); -- cgit v1.2.3 From ae8963adb4ad8c5f2a89ca1d99fb7bb721e7599f Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 3 Oct 2012 11:18:05 -0700 Subject: usb: Don't enable LPM if the exit latency is zero. Some USB 3.0 devices signal that they don't implement Link PM by having all zeroes in the U1/U2 exit latencies in their SuperSpeed BOS descriptor. Don found that a Western Digital device he has experiences transfer errors when LPM is enabled. The lsusb shows the U1/U2 exit latencies are set to zero: Binary Object Store Descriptor: bLength 5 bDescriptorType 15 wTotalLength 22 bNumDeviceCaps 2 SuperSpeed USB Device Capability: bLength 10 bDescriptorType 16 bDevCapabilityType 3 bmAttributes 0x00 Latency Tolerance Messages (LTM) Supported wSpeedsSupported 0x000e Device can operate at Full Speed (12Mbps) Device can operate at High Speed (480Mbps) Device can operate at SuperSpeed (5Gbps) bFunctionalitySupport 1 Lowest fully-functional device speed is Full Speed (12Mbps) bU1DevExitLat 0 micro seconds bU2DevExitLat 0 micro seconds The fix is to not enable LPM for a particular link state if we find its corresponding exit latency is zero. This patch should be backported to kernels as old as 3.5, that contain the commit 1ea7e0e8e3d0f50901d335ea4178ab2aa8c88201 "USB: Add support to enable/disable USB3 link states." Signed-off-by: Sarah Sharp Reported-by: Don Zickus Tested-by: Don Zickus Cc: stable@vger.kernel.org --- drivers/usb/core/hub.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 673ee4696262..8f0478709323 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3415,6 +3415,16 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, enum usb3_link_state state) { int timeout; + __u8 u1_mel = udev->bos->ss_cap->bU1devExitLat; + __le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat; + + /* If the device says it doesn't have *any* exit latency to come out of + * U1 or U2, it's probably lying. Assume it doesn't implement that link + * state. + */ + if ((state == USB3_LPM_U1 && u1_mel == 0) || + (state == USB3_LPM_U2 && u2_mel == 0)) + return; /* We allow the host controller to set the U1/U2 timeout internally * first, so that it can change its schedule to account for the -- cgit v1.2.3 From 65a95b75bc5afa7bbb844e222481044c1c4767eb Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 5 Oct 2012 10:32:07 -0700 Subject: usb: Send Set SEL before enabling parent U1/U2 timeout. The Set SEL control transfer tells a device the exit latencies associated with a device-initated U1 or U2 exit. Since a parent hub may initiate a transition to U1 soon after a downstream port's U1 timeout is set, we need to make sure the device receives the Set SEL transfer before the parent hub timeout is set. This patch should be backported to kernels as old as 3.5, that contain the commit 1ea7e0e8e3d0f50901d335ea4178ab2aa8c88201 "USB: Add support to enable/disable USB3 link states." Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/hub.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8f0478709323..b8e11fff17be 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3319,16 +3319,6 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev, } if (enable) { - /* - * First, let the device know about the exit latencies - * associated with the link state we're about to enable. - */ - ret = usb_req_set_sel(udev, state); - if (ret < 0) { - dev_warn(&udev->dev, "Set SEL for device-initiated " - "%s failed.\n", usb3_lpm_names[state]); - return -EBUSY; - } /* * Now send the control transfer to enable device-initiated LPM * for either U1 or U2. @@ -3414,7 +3404,7 @@ static int usb_set_lpm_timeout(struct usb_device *udev, static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, enum usb3_link_state state) { - int timeout; + int timeout, ret; __u8 u1_mel = udev->bos->ss_cap->bU1devExitLat; __le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat; @@ -3426,6 +3416,17 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, (state == USB3_LPM_U2 && u2_mel == 0)) return; + /* + * First, let the device know about the exit latencies + * associated with the link state we're about to enable. + */ + ret = usb_req_set_sel(udev, state); + if (ret < 0) { + dev_warn(&udev->dev, "Set SEL for device-initiated %s failed.\n", + usb3_lpm_names[state]); + return; + } + /* We allow the host controller to set the U1/U2 timeout internally * first, so that it can change its schedule to account for the * additional latency to send data to a device in a lower power -- cgit v1.2.3 From 1510a1a2d0946b34422fe8816187f274a5086904 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 5 Oct 2012 10:30:35 -0700 Subject: usb: trival: Fix debugging units mistake. SEL and PEL are in microseconds, not milliseconds. Also, fix a split string that will trigger checkpatch warnings. Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b8e11fff17be..64854d76f529 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3241,8 +3241,7 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) (state == USB3_LPM_U2 && (u2_sel > USB3_LPM_MAX_U2_SEL_PEL || u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) { - dev_dbg(&udev->dev, "Device-initiated %s disabled due " - "to long SEL %llu ms or PEL %llu ms\n", + dev_dbg(&udev->dev, "Device-initiated %s disabled due to long SEL %llu us or PEL %llu us\n", usb3_lpm_names[state], u1_sel, u1_pel); return -EINVAL; } -- cgit v1.2.3 From 8855e49b64668463d0db2f0deca7b517fdb63eee Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 4 Aug 2012 21:41:34 +0000 Subject: USB: EHCI: mark ehci_orion_conf_mbus_windows __devinit The __devinit section is going away soon, but while it's still there, we get a correct warning about ehci_orion_conf_mbus_windows being discarded before its caller, so it should be marked __devinit rather than __init. Without this patch, building dove_defconfig results in: WARNING: drivers/usb/host/built-in.o(.devinit.text+0x8a4): Section mismatch in reference from the function ehci_orion_drv_probe() to the function .init.text:ehci_orion_conf_mbus_windows() The function __devinit ehci_orion_drv_probe() references a function __init ehci_orion_conf_mbus_windows(). If ehci_orion_conf_mbus_windows is only used by ehci_orion_drv_probe then annotate ehci_orion_conf_mbus_windows with a matching annotation. Signed-off-by: Arnd Bergmann Acked-by: Alan Stern Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org --- drivers/usb/host/ehci-orion.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 8892d3642cef..1f5dd5ee5b79 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -160,7 +160,7 @@ static const struct hc_driver ehci_orion_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; -static void __init +static void __devinit ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, const struct mbus_dram_target_info *dram) { -- cgit v1.2.3 From 041d81f493d90c940ec41f0ec98bc7c4f2fba431 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 4 Oct 2012 11:58:00 +0300 Subject: usb: dwc3: gadget: fix 'endpoint always busy' bug If a USB transfer has already been started, meaning we have already issued StartTransfer command to that particular endpoint, DWC3_EP_BUSY flag has also already been set. When we try to cancel this transfer which is already in controller's cache, we will not receive XferComplete event and we must clear DWC3_EP_BUSY in order to allow subsequent requests to be properly started. The best place to clear that flag is right after issuing DWC3_DEPCMD_ENDTRANSFER. Cc: stable@vger.kernel.org # v3.4 v3.5 v3.6 Reported-by: Moiz Sonasath Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c9e729a4bf65..7b7deddf6a52 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1904,7 +1904,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); WARN_ON_ONCE(ret); dep->resource_index = 0; - + dep->flags &= ~DWC3_EP_BUSY; udelay(100); } -- cgit v1.2.3 From 6ff1f3d3bd7c69c62ca5773b1b684bce42eff06a Mon Sep 17 00:00:00 2001 From: Stefano Babic Date: Mon, 15 Oct 2012 11:20:22 +0200 Subject: usb: musb: am35xx: drop spurious unplugging a device On AM3517, tx and rx interrupt are detected together with the disconnect event. This generates a kernel panic in musb_interrupt, because rx / tx are handled after disconnect. This issue was seen on a Technexion's TAM3517 SOM. Unplugging a device, tx / rx interrupts together with disconnect are detected. This brings to kernel panic like this: [ 68.526153] Unable to handle kernel NULL pointer dereference at virtual address 00000011 [ 68.534698] pgd = c0004000 [ 68.537536] [00000011] *pgd=00000000 [ 68.541351] Internal error: Oops: 17 [#1] ARM [ 68.545928] Modules linked in: [ 68.549163] CPU: 0 Not tainted (3.6.0-rc5-00020-g9e05905 #178) [ 68.555694] PC is at rxstate+0x8/0xdc [ 68.559539] LR is at musb_interrupt+0x98/0x858 [ 68.564239] pc : [] lr : [] psr: 40000193 [ 68.564239] sp : ce83fb40 ip : d0906410 fp : 00000000 [ 68.576293] r10: 00000000 r9 : cf3b0e40 r8 : 00000002 [ 68.581817] r7 : 00000019 r6 : 00000001 r5 : 00000001 r4 : 000000d4 [ 68.588684] r3 : 00000000 r2 : 00000000 r1 : ffffffcc r0 : cf23c108 [ 68.595550] Flags: nZcv IRQs off FIQs on Mode SVC_32 ISA ARM Segment ke Note: this behavior is not seen with a USB hub, while it is easy to reproduce connecting a USB-pen directly to the USB-A of the board. Drop tx / rx interrupts if disconnect is detected. Signed-off-by: Stefano Babic CC: Felipe Balbi Cc: stable@vger.kernel.org # 3.5 3.6 Tested-by: Dmitry Lifshitz Tested-by: Igor Grinberg Signed-off-by: Felipe Balbi --- drivers/usb/musb/am35x.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 457f25e62c51..c964d6af178b 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -305,6 +305,12 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci) ret = IRQ_HANDLED; } + /* Drop spurious RX and TX if device is disconnected */ + if (musb->int_usb & MUSB_INTR_DISCONNECT) { + musb->int_tx = 0; + musb->int_rx = 0; + } + if (musb->int_tx || musb->int_rx || musb->int_usb) ret |= musb_interrupt(musb); -- cgit v1.2.3 From 01b8daf71b2fd2f6ece5f063a089abf4b13f1d6e Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Sat, 13 Oct 2012 19:20:18 +0530 Subject: usb: dwc3: shutdown usb_phy when removing the device We call usb_phy_init() from dwc3_core_init() during probe, so adding usb_phy_shutdown() to dwc3_core_exit() while removing the device so we don't keep PHYs turned on, consuming power, unnecessarily. Signed-off-by: Vivek Gautam Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index b415c0c859d3..c14ebc975ba4 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -409,6 +409,10 @@ static void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); dwc3_free_event_buffers(dwc); + + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + } #define DWC3_ALIGN_MASK (16 - 1) -- cgit v1.2.3 From 0b2ffb78c0ef83bb03f594fec2adc13ac6ac427b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 4 Oct 2012 02:32:41 +0200 Subject: usb: gadget: Make webcam gadget select USB_LIBCOMPOSITE Composite gadget support is now available as a library instead of being built with each gadget. Composite drivers need to select USB_LIBCOMPOSITE. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index dfb51a45496c..e0ff51b89529 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -952,6 +952,7 @@ endif config USB_G_WEBCAM tristate "USB Webcam Gadget" depends on VIDEO_DEV + select USB_LIBCOMPOSITE help The Webcam Gadget acts as a composite USB Audio and Video Class device. It provides a userspace API to process UVC control requests -- cgit v1.2.3 From c2fa3edc58a262dfcb7aea78e24661e90e00098c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Oct 2012 23:24:55 -0700 Subject: usb: renesas_usbhs: fixup __usbhs_for_each_pipe 1st pos __usbhs_for_each_pipe() is the macro which moves around each pipe, but it has a bug which didn't care about 1st pipe's position. Because of this bug, it moves around pipe0, pipe2, pipe3 ... even though it requested pipe1, pipe2, pipe3... This patch modifies it. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/pipe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 08786c06dcf1..3d80c7b1fd1b 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -54,7 +54,7 @@ struct usbhs_pipe_info { * pipe list */ #define __usbhs_for_each_pipe(start, pos, info, i) \ - for (i = start, pos = (info)->pipe; \ + for (i = start, pos = (info)->pipe + i; \ i < (info)->size; \ i++, pos = (info)->pipe + i) -- cgit v1.2.3 From d5c6a1e024dd5acd6ad2e1a6eb6a2fd5c15c50f1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 11 Oct 2012 21:49:38 -0700 Subject: usb: renesas_usbhs: fixup interrupt status clear method When interrupt happened, renesas_usbhs driver gets irq status by usbhs_status_get_each_irq(), and cleared all status by using 0. But, this method is incorrect, since extra interrupt might occur between them. This patch cleared corresponding bits only Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 35c5208f3249..61933a90e5bf 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -273,9 +273,9 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC); usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); - usbhs_write(priv, BRDYSTS, 0); - usbhs_write(priv, NRDYSTS, 0); - usbhs_write(priv, BEMPSTS, 0); + usbhs_write(priv, BRDYSTS, ~irq_state.brdysts); + usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts); + usbhs_write(priv, BEMPSTS, ~irq_state.bempsts); /* * call irq callback functions -- cgit v1.2.3 From 69da85edbab5cd42909d3ba65b3dad1c1fb40206 Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Mon, 15 Oct 2012 09:47:35 -0300 Subject: usb: gadget: lpc32xx_udc: Fix compatibility with STOTG04 The STOTG04 is an replacement for ISP1301. Most of the registers on STOTG04 are the same as on ISP1301, but the register ISP1301_I2C_OTG_CONTROL_2 (address 0x10) doesn't exist on the ST part. This is a work around for this by using the interrupt source register that should behave the same on both parts and has the needed information. Tested-by: Roland Stigge Signed-off-by: Alexandre Pereira da Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/lpc32xx_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index f696fb9b136d..21a9861dabf0 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -2930,10 +2930,10 @@ static void vbus_work(struct work_struct *work) /* Get the VBUS status from the transceiver */ value = i2c_smbus_read_byte_data(udc->isp1301_i2c_client, - ISP1301_I2C_OTG_CONTROL_2); + ISP1301_I2C_INTERRUPT_SOURCE); /* VBUS on or off? */ - if (value & OTG_B_SESS_VLD) + if (value & INT_SESS_VLD) udc->vbus = 1; else udc->vbus = 0; -- cgit v1.2.3 From 7bdce71822f471433dd3014692e9096996c7b5f0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 15 Oct 2012 18:20:52 +0200 Subject: USB: ark3116: fix NULL-pointer dereference Fix NULL-pointer dereference at release by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is NULL when release is called. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index cf2522c397d3..bd50a8a41a0f 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -125,9 +125,6 @@ static inline int calc_divisor(int bps) static int ark3116_attach(struct usb_serial *serial) { - 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) || @@ -142,8 +139,15 @@ static int ark3116_attach(struct usb_serial *serial) return -EINVAL; } - priv = kzalloc(sizeof(struct ark3116_private), - GFP_KERNEL); + return 0; +} + +static int ark3116_port_probe(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct ark3116_private *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -198,18 +202,15 @@ static int ark3116_attach(struct usb_serial *serial) return 0; } -static void ark3116_release(struct usb_serial *serial) +static int ark3116_port_remove(struct usb_serial_port *port) { - 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); + + return 0; } static void ark3116_init_termios(struct tty_struct *tty) @@ -723,7 +724,8 @@ static struct usb_serial_driver ark3116_device = { .id_table = id_table, .num_ports = 1, .attach = ark3116_attach, - .release = ark3116_release, + .port_probe = ark3116_port_probe, + .port_remove = ark3116_port_remove, .set_termios = ark3116_set_termios, .init_termios = ark3116_init_termios, .ioctl = ark3116_ioctl, -- cgit v1.2.3 From a9556040119a63d06fd5238d47f5b683fba4178b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 15 Oct 2012 18:20:54 +0200 Subject: USB: cyberjack: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the write waitqueue was initialised but never used. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cyberjack.c | 49 ++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 2a7aecc72237..4ee77dcbe690 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -55,9 +55,9 @@ #define CYBERJACK_PRODUCT_ID 0x0100 /* Function prototypes */ -static int cyberjack_startup(struct usb_serial *serial); static void cyberjack_disconnect(struct usb_serial *serial); -static void cyberjack_release(struct usb_serial *serial); +static int cyberjack_port_probe(struct usb_serial_port *port); +static int cyberjack_port_remove(struct usb_serial_port *port); static int cyberjack_open(struct tty_struct *tty, struct usb_serial_port *port); static void cyberjack_close(struct usb_serial_port *port); @@ -83,9 +83,9 @@ static struct usb_serial_driver cyberjack_device = { .description = "Reiner SCT Cyberjack USB card reader", .id_table = id_table, .num_ports = 1, - .attach = cyberjack_startup, .disconnect = cyberjack_disconnect, - .release = cyberjack_release, + .port_probe = cyberjack_port_probe, + .port_remove = cyberjack_port_remove, .open = cyberjack_open, .close = cyberjack_close, .write = cyberjack_write, @@ -107,56 +107,45 @@ struct cyberjack_private { short wrsent; /* Data already sent */ }; -/* do some startup allocations not currently performed by usb_serial_probe() */ -static int cyberjack_startup(struct usb_serial *serial) +static int cyberjack_port_probe(struct usb_serial_port *port) { struct cyberjack_private *priv; - int i; + int result; - /* allocate the private data structure */ priv = kmalloc(sizeof(struct cyberjack_private), GFP_KERNEL); if (!priv) return -ENOMEM; - /* set initial values */ spin_lock_init(&priv->lock); priv->rdtodo = 0; priv->wrfilled = 0; priv->wrsent = 0; - usb_set_serial_port_data(serial->port[0], priv); - init_waitqueue_head(&serial->port[0]->write_wait); + usb_set_serial_port_data(port, priv); - for (i = 0; i < serial->num_ports; ++i) { - int result; - result = usb_submit_urb(serial->port[i]->interrupt_in_urb, - GFP_KERNEL); - if (result) - dev_err(&serial->dev->dev, - "usb_submit_urb(read int) failed\n"); - dev_dbg(&serial->dev->dev, "%s - usb_submit_urb(int urb)\n", - __func__); - } + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, "usb_submit_urb(read int) failed\n"); return 0; } -static void cyberjack_disconnect(struct usb_serial *serial) +static int cyberjack_port_remove(struct usb_serial_port *port) { - int i; + struct cyberjack_private *priv; - for (i = 0; i < serial->num_ports; ++i) - usb_kill_urb(serial->port[i]->interrupt_in_urb); + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } -static void cyberjack_release(struct usb_serial *serial) +static void cyberjack_disconnect(struct usb_serial *serial) { int i; - for (i = 0; i < serial->num_ports; ++i) { - /* My special items, the standard routines free my urbs */ - kfree(usb_get_serial_port_data(serial->port[i])); - } + for (i = 0; i < serial->num_ports; ++i) + usb_kill_urb(serial->port[i]->interrupt_in_urb); } static int cyberjack_open(struct tty_struct *tty, -- cgit v1.2.3 From fa919751a2d26a88140fc5810124dd81644efe51 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 15 Oct 2012 18:20:53 +0200 Subject: USB: belkin_sa: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the write waitqueue was initialised but never used. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/belkin_sa.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 99449424193f..ea29556f0d72 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -45,8 +45,8 @@ #define DRIVER_DESC "USB Belkin Serial converter driver" /* function prototypes for a Belkin USB Serial Adapter F5U103 */ -static int belkin_sa_startup(struct usb_serial *serial); -static void belkin_sa_release(struct usb_serial *serial); +static int belkin_sa_port_probe(struct usb_serial_port *port); +static int belkin_sa_port_remove(struct usb_serial_port *port); static int belkin_sa_open(struct tty_struct *tty, struct usb_serial_port *port); static void belkin_sa_close(struct usb_serial_port *port); @@ -88,8 +88,8 @@ static struct usb_serial_driver belkin_device = { .break_ctl = belkin_sa_break_ctl, .tiocmget = belkin_sa_tiocmget, .tiocmset = belkin_sa_tiocmset, - .attach = belkin_sa_startup, - .release = belkin_sa_release, + .port_probe = belkin_sa_port_probe, + .port_remove = belkin_sa_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { @@ -118,17 +118,15 @@ struct belkin_sa_private { (c), BELKIN_SA_SET_REQUEST_TYPE, \ (v), 0, NULL, 0, WDR_TIMEOUT) -/* do some startup allocations not currently performed by usb_serial_probe() */ -static int belkin_sa_startup(struct usb_serial *serial) +static int belkin_sa_port_probe(struct usb_serial_port *port) { - struct usb_device *dev = serial->dev; + struct usb_device *dev = port->serial->dev; struct belkin_sa_private *priv; - /* allocate the private data structure */ priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL); if (!priv) - return -1; /* error */ - /* set initial values for control structures */ + return -ENOMEM; + spin_lock_init(&priv->lock); priv->control_state = 0; priv->last_lsr = 0; @@ -140,18 +138,19 @@ static int belkin_sa_startup(struct usb_serial *serial) le16_to_cpu(dev->descriptor.bcdDevice), priv->bad_flow_control); - init_waitqueue_head(&serial->port[0]->write_wait); - usb_set_serial_port_data(serial->port[0], priv); + usb_set_serial_port_data(port, priv); return 0; } -static void belkin_sa_release(struct usb_serial *serial) +static int belkin_sa_port_remove(struct usb_serial_port *port) { - int i; + struct belkin_sa_private *priv; - for (i = 0; i < serial->num_ports; ++i) - kfree(usb_get_serial_port_data(serial->port[i])); + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } static int belkin_sa_open(struct tty_struct *tty, -- cgit v1.2.3 From 4295fe7791a1b20c90cbaaa6f23f2fb94218b8a7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 15 Oct 2012 15:47:20 +0200 Subject: USB: cp210x: fix port-data memory leak Fix port data memory leak by replacing port private data with serial private data. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release. The private data is used to store the control interface number, but as this is the same for all ports on an interface it should be stored as usb-serial data anyway. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 28af5acc3360..eb033fc92a15 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -162,7 +162,7 @@ static const struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); -struct cp210x_port_private { +struct cp210x_serial_private { __u8 bInterfaceNumber; }; @@ -276,7 +276,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; - struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); + struct cp210x_serial_private *spriv = usb_get_serial_data(serial); __le32 *buf; int result, i, length; @@ -292,7 +292,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request, /* Issue the request, attempting to read 'size' bytes */ result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), request, REQTYPE_INTERFACE_TO_HOST, 0x0000, - port_priv->bInterfaceNumber, buf, size, + spriv->bInterfaceNumber, buf, size, USB_CTRL_GET_TIMEOUT); /* Convert data into an array of integers */ @@ -323,7 +323,7 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; - struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); + struct cp210x_serial_private *spriv = usb_get_serial_data(serial); __le32 *buf; int result, i, length; @@ -345,13 +345,13 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request, result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, REQTYPE_HOST_TO_INTERFACE, 0x0000, - port_priv->bInterfaceNumber, buf, size, + spriv->bInterfaceNumber, buf, size, USB_CTRL_SET_TIMEOUT); } else { result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, REQTYPE_HOST_TO_INTERFACE, data[0], - port_priv->bInterfaceNumber, NULL, 0, + spriv->bInterfaceNumber, NULL, 0, USB_CTRL_SET_TIMEOUT); } @@ -845,36 +845,30 @@ static void cp210x_break_ctl (struct tty_struct *tty, int break_state) static int cp210x_startup(struct usb_serial *serial) { - struct cp210x_port_private *port_priv; - int i; + struct usb_host_interface *cur_altsetting; + struct cp210x_serial_private *spriv; /* cp210x buffers behave strangely unless device is reset */ usb_reset_device(serial->dev); - for (i = 0; i < serial->num_ports; i++) { - port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL); - if (!port_priv) - return -ENOMEM; + spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); + if (!spriv) + return -ENOMEM; - port_priv->bInterfaceNumber = - serial->interface->cur_altsetting->desc.bInterfaceNumber; + cur_altsetting = serial->interface->cur_altsetting; + spriv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber; - usb_set_serial_port_data(serial->port[i], port_priv); - } + usb_set_serial_data(serial, spriv); return 0; } static void cp210x_release(struct usb_serial *serial) { - struct cp210x_port_private *port_priv; - int i; + struct cp210x_serial_private *spriv; - for (i = 0; i < serial->num_ports; i++) { - port_priv = usb_get_serial_port_data(serial->port[i]); - kfree(port_priv); - usb_set_serial_port_data(serial->port[i], NULL); - } + spriv = usb_get_serial_data(serial); + kfree(spriv); } module_usb_serial_driver(serial_drivers, id_table); -- cgit v1.2.3 From 8bf769eb5f6efc33f95088850f33fcc05d28b508 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 15 Oct 2012 15:47:21 +0200 Subject: USB: pl2303: fix port-data memory leak Fix port-data memory leak by allocating and freeing port data in port_probe/remove rather than in attach/release, and by introducing serial private data to store the device type which is interface rather than port specific. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 90 +++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 36 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 892ebdc7a364..600241901361 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -133,12 +133,15 @@ enum pl2303_type { HX, /* HX version of the pl2303 chip */ }; +struct pl2303_serial_private { + enum pl2303_type type; +}; + struct pl2303_private { spinlock_t lock; wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; - enum pl2303_type type; }; static int pl2303_vendor_read(__u16 value, __u16 index, @@ -167,14 +170,19 @@ static int pl2303_vendor_write(__u16 value, __u16 index, static int pl2303_startup(struct usb_serial *serial) { - struct pl2303_private *priv; + struct pl2303_serial_private *spriv; enum pl2303_type type = type_0; unsigned char *buf; - int i; + + spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); + if (!spriv) + return -ENOMEM; buf = kmalloc(10, GFP_KERNEL); - if (buf == NULL) + if (!buf) { + kfree(spriv); return -ENOMEM; + } if (serial->dev->descriptor.bDeviceClass == 0x02) type = type_0; @@ -186,15 +194,8 @@ static int pl2303_startup(struct usb_serial *serial) type = type_1; dev_dbg(&serial->interface->dev, "device type: %d\n", type); - for (i = 0; i < serial->num_ports; ++i) { - priv = kzalloc(sizeof(struct pl2303_private), GFP_KERNEL); - if (!priv) - goto cleanup; - spin_lock_init(&priv->lock); - init_waitqueue_head(&priv->delta_msr_wait); - priv->type = type; - usb_set_serial_port_data(serial->port[i], priv); - } + spriv->type = type; + usb_set_serial_data(serial, spriv); pl2303_vendor_read(0x8484, 0, serial, buf); pl2303_vendor_write(0x0404, 0, serial); @@ -213,15 +214,40 @@ static int pl2303_startup(struct usb_serial *serial) kfree(buf); return 0; +} -cleanup: - kfree(buf); - for (--i; i >= 0; --i) { - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - usb_set_serial_port_data(serial->port[i], NULL); - } - return -ENOMEM; +static void pl2303_release(struct usb_serial *serial) +{ + struct pl2303_serial_private *spriv; + + spriv = usb_get_serial_data(serial); + kfree(spriv); +} + +static int pl2303_port_probe(struct usb_serial_port *port) +{ + struct pl2303_private *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->delta_msr_wait); + + usb_set_serial_port_data(port, priv); + + return 0; +} + +static int pl2303_port_remove(struct usb_serial_port *port) +{ + struct pl2303_private *priv; + + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } static int set_control_lines(struct usb_device *dev, u8 value) @@ -240,6 +266,7 @@ static void pl2303_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int cflag; @@ -323,7 +350,7 @@ static void pl2303_set_termios(struct tty_struct *tty, } if (baud > 1228800) { /* type_0, type_1 only support up to 1228800 baud */ - if (priv->type != HX) + if (spriv->type != HX) baud = 1228800; else if (baud > 6000000) baud = 6000000; @@ -426,7 +453,7 @@ static void pl2303_set_termios(struct tty_struct *tty, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (cflag & CRTSCTS) { - if (priv->type == HX) + if (spriv->type == HX) pl2303_vendor_write(0x0, 0x61, serial); else pl2303_vendor_write(0x0, 0x41, serial); @@ -468,10 +495,10 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; - struct pl2303_private *priv = usb_get_serial_port_data(port); + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int result; - if (priv->type != HX) { + if (spriv->type != HX) { usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); } else { @@ -655,17 +682,6 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state) dev_err(&port->dev, "error sending break = %d\n", result); } -static void pl2303_release(struct usb_serial *serial) -{ - int i; - struct pl2303_private *priv; - - for (i = 0; i < serial->num_ports; ++i) { - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - } -} - static void pl2303_update_line_status(struct usb_serial_port *port, unsigned char *data, unsigned int actual_length) @@ -827,6 +843,8 @@ static struct usb_serial_driver pl2303_device = { .read_int_callback = pl2303_read_int_callback, .attach = pl2303_startup, .release = pl2303_release, + .port_probe = pl2303_port_probe, + .port_remove = pl2303_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit v1.2.3 From db5c8b524444d4fc6b1f32d368a50a3729e50002 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 10 Oct 2012 14:10:21 -0400 Subject: USB: fix port probing and removal in garmin_gps This patch (as1615) fixes a bug in the Garmin USB serial driver. It uses attach, disconnect, and release routines to carry out actions that should be handled by port_probe and port_remove routines, because they access port-specific data. The bug causes an oops when the device in unplugged, because the private data for each port structure now gets erased when the port is unbound from the driver, resulting in a null-pointer dereference. Signed-off-by: Alan Stern Reported--by: Markus Schauler Tested-by: Markus Schauler Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/garmin_gps.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 3ee92648c02d..203358d7e7bc 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1405,11 +1405,10 @@ static void timeout_handler(unsigned long data) -static int garmin_attach(struct usb_serial *serial) +static int garmin_port_probe(struct usb_serial_port *port) { - int status = 0; - struct usb_serial_port *port = serial->port[0]; - struct garmin_data *garmin_data_p = NULL; + int status; + struct garmin_data *garmin_data_p; garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL); if (garmin_data_p == NULL) { @@ -1434,22 +1433,14 @@ static int garmin_attach(struct usb_serial *serial) } -static void garmin_disconnect(struct usb_serial *serial) +static int garmin_port_remove(struct usb_serial_port *port) { - struct usb_serial_port *port = serial->port[0]; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); usb_kill_urb(port->interrupt_in_urb); del_timer_sync(&garmin_data_p->timer); -} - - -static void garmin_release(struct usb_serial *serial) -{ - struct usb_serial_port *port = serial->port[0]; - struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); - kfree(garmin_data_p); + return 0; } @@ -1466,9 +1457,8 @@ static struct usb_serial_driver garmin_device = { .close = garmin_close, .throttle = garmin_throttle, .unthrottle = garmin_unthrottle, - .attach = garmin_attach, - .disconnect = garmin_disconnect, - .release = garmin_release, + .port_probe = garmin_port_probe, + .port_remove = garmin_port_remove, .write = garmin_write, .write_room = garmin_write_room, .write_bulk_callback = garmin_write_bulk_callback, -- cgit v1.2.3 From 014639003c77a51fc319c9fdf225e7747cb6e18d Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 13 Oct 2012 12:20:36 +0200 Subject: usbdevfs: Fix broken scatter-gather transfer The handling of large output bulk transfers is broken; the same user page is read over and over again. Fixed with this patch. Cc: stable Acked-by: Peter Stuge Acked-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Henrik Rydberg Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e0356cb859b5..b78fbe222b72 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1348,6 +1348,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ret = -EFAULT; goto error; } + uurb->buffer += u; } totlen -= u; } -- cgit v1.2.3 From c5211187f7ff8e8dbff4ebf7c011ac4c0ffe319c Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Tue, 16 Oct 2012 21:21:21 +0800 Subject: USB: cdc-acm: fix pipe type of write endpoint If the write endpoint is interrupt type, usb_sndintpipe() should be passed to usb_fill_int_urb() instead of usb_sndbulkpipe(). Cc: Oliver Neukum Signed-off-by: Ming Lei Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 981f2132d128..572f4e7bd3ff 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1233,7 +1233,7 @@ made_compressed_probe: if (usb_endpoint_xfer_int(epwrite)) usb_fill_int_urb(snd->urb, usb_dev, - usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), + usb_sndintpipe(usb_dev, epwrite->bEndpointAddress), NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval); else usb_fill_bulk_urb(snd->urb, usb_dev, -- cgit v1.2.3 From 09eeffb70c086692064f4224490d1cd2ffe43bdd Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sun, 14 Oct 2012 16:22:34 +1300 Subject: usb: Missing dma_mask in uhci-platform.c when probed from device-tree Device-tree probed devices don't get a dev.dma_mask set. This patch sets a default 32bit mask on platforms using devicetree. Without this patch, arch-vt8500 cannot detect uhci attached devices. Signed-off-by: Tony Prisk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-platform.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index e4780491df4a..68ebf20e1519 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -60,6 +60,7 @@ static const struct hc_driver uhci_platform_hc_driver = { .hub_control = uhci_hub_control, }; +static u64 platform_uhci_dma_mask = DMA_BIT_MASK(32); static int __devinit uhci_hcd_platform_probe(struct platform_device *pdev) { @@ -71,6 +72,14 @@ static int __devinit uhci_hcd_platform_probe(struct platform_device *pdev) if (usb_disabled()) return -ENODEV; + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &platform_uhci_dma_mask; + hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev, pdev->name); if (!hcd) -- cgit v1.2.3 From 6e1babb3ff2651376f2e9843569fcb39cfe4686b Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sun, 14 Oct 2012 16:22:35 +1300 Subject: usb: Missing dma_mask in ehci-vt8500.c when probed from device-tree Device-tree probed devices don't get a dev.dma_mask set. This patch sets a default 32bit mask on arch-vt8500 when using devicetree. Without this patch, arch-vt8500 cannot detect ehci attached devices. Signed-off-by: Tony Prisk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-vt8500.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index 96722bfebc84..d3c9a3e397b9 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -85,6 +85,8 @@ static const struct hc_driver vt8500_ehci_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; +static u64 vt8500_ehci_dma_mask = DMA_BIT_MASK(32); + static int vt8500_ehci_drv_probe(struct platform_device *pdev) { struct usb_hcd *hcd; @@ -95,6 +97,14 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) if (usb_disabled()) return -ENODEV; + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &vt8500_ehci_dma_mask; + if (pdev->resource[1].flags != IORESOURCE_IRQ) { pr_debug("resource[1] is not IORESOURCE_IRQ"); return -ENOMEM; -- cgit v1.2.3 From 301a29da6e891e7eb95c843af0ecdbe86d01f723 Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 16 Oct 2012 00:06:23 +0200 Subject: usb: acm: fix the computation of the number of data bits The current code assumes that CSIZE is 0000060, which appears to be wrong on some arches (such as powerpc). Signed-off-by: Nicolas Boullis Acked-by: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 572f4e7bd3ff..6e49ec6f3adc 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -817,10 +817,6 @@ static const __u32 acm_tty_speed[] = { 2500000, 3000000, 3500000, 4000000 }; -static const __u8 acm_tty_size[] = { - 5, 6, 7, 8 -}; - static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) { @@ -834,7 +830,21 @@ static void acm_tty_set_termios(struct tty_struct *tty, newline.bParityType = termios->c_cflag & PARENB ? (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; - newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; + switch (termios->c_cflag & CSIZE) { + case CS5: + newline.bDataBits = 5; + break; + case CS6: + newline.bDataBits = 6; + break; + case CS7: + newline.bDataBits = 7; + break; + case CS8: + default: + newline.bDataBits = 8; + break; + } /* FIXME: Needs to clear unsupported bits in the termios */ acm->clocal = ((termios->c_cflag & CLOCAL) != 0); -- cgit v1.2.3 From 5c1a0f418d8d985f3a62849bcac43fc5404cc592 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 13:34:55 +0200 Subject: USB: cypress_m8: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cypress_m8.c | 75 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 38 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 1befce21e173..f0da1279c114 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -123,10 +123,10 @@ struct cypress_private { }; /* function prototypes for the Cypress USB to serial device */ -static int cypress_earthmate_startup(struct usb_serial *serial); -static int cypress_hidcom_startup(struct usb_serial *serial); -static int cypress_ca42v2_startup(struct usb_serial *serial); -static void cypress_release(struct usb_serial *serial); +static int cypress_earthmate_port_probe(struct usb_serial_port *port); +static int cypress_hidcom_port_probe(struct usb_serial_port *port); +static int cypress_ca42v2_port_probe(struct usb_serial_port *port); +static int cypress_port_remove(struct usb_serial_port *port); static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port); static void cypress_close(struct usb_serial_port *port); static void cypress_dtr_rts(struct usb_serial_port *port, int on); @@ -156,8 +156,8 @@ static struct usb_serial_driver cypress_earthmate_device = { .description = "DeLorme Earthmate USB", .id_table = id_table_earthmate, .num_ports = 1, - .attach = cypress_earthmate_startup, - .release = cypress_release, + .port_probe = cypress_earthmate_port_probe, + .port_remove = cypress_port_remove, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -182,8 +182,8 @@ static struct usb_serial_driver cypress_hidcom_device = { .description = "HID->COM RS232 Adapter", .id_table = id_table_cyphidcomrs232, .num_ports = 1, - .attach = cypress_hidcom_startup, - .release = cypress_release, + .port_probe = cypress_hidcom_port_probe, + .port_remove = cypress_port_remove, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -208,8 +208,8 @@ static struct usb_serial_driver cypress_ca42v2_device = { .description = "Nokia CA-42 V2 Adapter", .id_table = id_table_nokiaca42v2, .num_ports = 1, - .attach = cypress_ca42v2_startup, - .release = cypress_release, + .port_probe = cypress_ca42v2_port_probe, + .port_remove = cypress_port_remove, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -438,10 +438,10 @@ static void cypress_set_dead(struct usb_serial_port *port) *****************************************************************************/ -static int generic_startup(struct usb_serial *serial) +static int cypress_generic_port_probe(struct usb_serial_port *port) { + struct usb_serial *serial = port->serial; struct cypress_private *priv; - struct usb_serial_port *port = serial->port[0]; priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL); if (!priv) @@ -490,14 +490,16 @@ static int generic_startup(struct usb_serial *serial) } -static int cypress_earthmate_startup(struct usb_serial *serial) +static int cypress_earthmate_port_probe(struct usb_serial_port *port) { + struct usb_serial *serial = port->serial; struct cypress_private *priv; - struct usb_serial_port *port = serial->port[0]; + int ret; - if (generic_startup(serial)) { + ret = cypress_generic_port_probe(port); + if (ret) { dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); - return 1; + return ret; } priv = usb_get_serial_port_data(port); @@ -518,56 +520,53 @@ static int cypress_earthmate_startup(struct usb_serial *serial) } return 0; -} /* cypress_earthmate_startup */ - +} -static int cypress_hidcom_startup(struct usb_serial *serial) +static int cypress_hidcom_port_probe(struct usb_serial_port *port) { struct cypress_private *priv; - struct usb_serial_port *port = serial->port[0]; + int ret; - if (generic_startup(serial)) { + ret = cypress_generic_port_probe(port); + if (ret) { dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); - return 1; + return ret; } priv = usb_get_serial_port_data(port); priv->chiptype = CT_CYPHIDCOM; return 0; -} /* cypress_hidcom_startup */ - +} -static int cypress_ca42v2_startup(struct usb_serial *serial) +static int cypress_ca42v2_port_probe(struct usb_serial_port *port) { struct cypress_private *priv; - struct usb_serial_port *port = serial->port[0]; + int ret; - if (generic_startup(serial)) { + ret = cypress_generic_port_probe(port); + if (ret) { dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); - return 1; + return ret; } priv = usb_get_serial_port_data(port); priv->chiptype = CT_CA42V2; return 0; -} /* cypress_ca42v2_startup */ - +} -static void cypress_release(struct usb_serial *serial) +static int cypress_port_remove(struct usb_serial_port *port) { struct cypress_private *priv; - /* all open ports are closed at this point */ - priv = usb_get_serial_port_data(serial->port[0]); + priv = usb_get_serial_port_data(port); - if (priv) { - kfifo_free(&priv->write_fifo); - kfree(priv); - } -} + kfifo_free(&priv->write_fifo); + kfree(priv); + return 0; +} static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) { -- cgit v1.2.3 From 95940a04bfe8a4d246f4ca17c6a3b00148bdead0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 13:35:02 +0200 Subject: USB: kobil_sct: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/kobil_sct.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 5c4d2fbd4e11..c9ca7a5b12e0 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -54,8 +54,8 @@ /* Function prototypes */ -static int kobil_startup(struct usb_serial *serial); -static void kobil_release(struct usb_serial *serial); +static int kobil_port_probe(struct usb_serial_port *probe); +static int kobil_port_remove(struct usb_serial_port *probe); static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); static void kobil_close(struct usb_serial_port *port); static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -89,8 +89,8 @@ static struct usb_serial_driver kobil_device = { .description = "KOBIL USB smart card terminal", .id_table = id_table, .num_ports = 1, - .attach = kobil_startup, - .release = kobil_release, + .port_probe = kobil_port_probe, + .port_remove = kobil_port_remove, .ioctl = kobil_ioctl, .set_termios = kobil_set_termios, .init_termios = kobil_init_termios, @@ -117,9 +117,10 @@ struct kobil_private { }; -static int kobil_startup(struct usb_serial *serial) +static int kobil_port_probe(struct usb_serial_port *port) { int i; + struct usb_serial *serial = port->serial; struct kobil_private *priv; struct usb_device *pdev; struct usb_host_config *actconfig; @@ -149,7 +150,7 @@ static int kobil_startup(struct usb_serial *serial) dev_dbg(&serial->dev->dev, "KOBIL KAAN SIM detected\n"); break; } - usb_set_serial_port_data(serial->port[0], priv); + usb_set_serial_port_data(port, priv); /* search for the necessary endpoints */ pdev = serial->dev; @@ -179,12 +180,14 @@ static int kobil_startup(struct usb_serial *serial) } -static void kobil_release(struct usb_serial *serial) +static int kobil_port_remove(struct usb_serial_port *port) { - int i; + struct kobil_private *priv; - for (i = 0; i < serial->num_ports; ++i) - kfree(usb_get_serial_port_data(serial->port[i])); + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } static void kobil_init_termios(struct tty_struct *tty) -- cgit v1.2.3 From c27f3efc56080a246f6ab7f57f0a6f56d256d769 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 13:34:57 +0200 Subject: USB: io_edgeport: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_edgeport.c | 54 +++++++++++++++++++++------------------- drivers/usb/serial/io_tables.h | 8 ++++++ 2 files changed, 37 insertions(+), 25 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 8e6faaf3580c..5acc0d13864a 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -225,6 +225,8 @@ static int edge_get_icount(struct tty_struct *tty, static int edge_startup(struct usb_serial *serial); static void edge_disconnect(struct usb_serial *serial); static void edge_release(struct usb_serial *serial); +static int edge_port_probe(struct usb_serial_port *port); +static int edge_port_remove(struct usb_serial_port *port); #include "io_tables.h" /* all of the devices that this driver supports */ @@ -2875,10 +2877,9 @@ static void load_application_firmware(struct edgeport_serial *edge_serial) static int edge_startup(struct usb_serial *serial) { struct edgeport_serial *edge_serial; - struct edgeport_port *edge_port; struct usb_device *dev; struct device *ddev = &serial->dev->dev; - int i, j; + int i; int response; bool interrupt_in_found; bool bulk_in_found; @@ -2961,25 +2962,6 @@ static int edge_startup(struct usb_serial *serial) /* we set up the pointers to the endpoints in the edge_open function, * as the structures aren't created yet. */ - /* set up our port private structures */ - for (i = 0; i < serial->num_ports; ++i) { - edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL); - if (edge_port == NULL) { - dev_err(ddev, "%s - Out of memory\n", __func__); - for (j = 0; j < i; ++j) { - kfree(usb_get_serial_port_data(serial->port[j])); - usb_set_serial_port_data(serial->port[j], - NULL); - } - usb_set_serial_data(serial, NULL); - kfree(edge_serial); - return -ENOMEM; - } - spin_lock_init(&edge_port->ep_lock); - edge_port->port = serial->port[i]; - usb_set_serial_port_data(serial->port[i], edge_port); - } - response = 0; if (edge_serial->is_epic) { @@ -3120,14 +3102,36 @@ static void edge_disconnect(struct usb_serial *serial) static void edge_release(struct usb_serial *serial) { struct edgeport_serial *edge_serial = usb_get_serial_data(serial); - int i; - - for (i = 0; i < serial->num_ports; ++i) - kfree(usb_get_serial_port_data(serial->port[i])); kfree(edge_serial); } +static int edge_port_probe(struct usb_serial_port *port) +{ + struct edgeport_port *edge_port; + + edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL); + if (!edge_port) + return -ENOMEM; + + spin_lock_init(&edge_port->ep_lock); + edge_port->port = port; + + usb_set_serial_port_data(port, edge_port); + + return 0; +} + +static int edge_port_remove(struct usb_serial_port *port) +{ + struct edgeport_port *edge_port; + + edge_port = usb_get_serial_port_data(port); + kfree(edge_port); + + return 0; +} + module_usb_serial_driver(serial_drivers, id_table_combined); MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h index 350afddb55ba..1511dd0ad324 100644 --- a/drivers/usb/serial/io_tables.h +++ b/drivers/usb/serial/io_tables.h @@ -110,6 +110,8 @@ static struct usb_serial_driver edgeport_2port_device = { .attach = edge_startup, .disconnect = edge_disconnect, .release = edge_release, + .port_probe = edge_port_probe, + .port_remove = edge_port_remove, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -139,6 +141,8 @@ static struct usb_serial_driver edgeport_4port_device = { .attach = edge_startup, .disconnect = edge_disconnect, .release = edge_release, + .port_probe = edge_port_probe, + .port_remove = edge_port_remove, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -168,6 +172,8 @@ static struct usb_serial_driver edgeport_8port_device = { .attach = edge_startup, .disconnect = edge_disconnect, .release = edge_release, + .port_probe = edge_port_probe, + .port_remove = edge_port_remove, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -197,6 +203,8 @@ static struct usb_serial_driver epic_device = { .attach = edge_startup, .disconnect = edge_disconnect, .release = edge_release, + .port_probe = edge_port_probe, + .port_remove = edge_port_remove, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, -- cgit v1.2.3 From 3124d1d71d3df59d40b913b5481df58099e811d1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 13:34:56 +0200 Subject: USB: f81232: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/f81232.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 244477107e2f..6e4eb57d0177 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -318,39 +318,30 @@ static int f81232_ioctl(struct tty_struct *tty, return -ENOIOCTLCMD; } -static int f81232_startup(struct usb_serial *serial) +static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; - int i; - for (i = 0; i < serial->num_ports; ++i) { - priv = kzalloc(sizeof(struct f81232_private), GFP_KERNEL); - if (!priv) - goto cleanup; - spin_lock_init(&priv->lock); - init_waitqueue_head(&priv->delta_msr_wait); - usb_set_serial_port_data(serial->port[i], priv); - } - return 0; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; -cleanup: - for (--i; i >= 0; --i) { - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - usb_set_serial_port_data(serial->port[i], NULL); - } - return -ENOMEM; + spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->delta_msr_wait); + + usb_set_serial_port_data(port, priv); + + return 0; } -static void f81232_release(struct usb_serial *serial) +static int f81232_port_remove(struct usb_serial_port *port) { - int i; struct f81232_private *priv; - for (i = 0; i < serial->num_ports; ++i) { - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - } + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } static struct usb_serial_driver f81232_device = { @@ -373,8 +364,8 @@ static struct usb_serial_driver f81232_device = { .tiocmset = f81232_tiocmset, .process_read_urb = f81232_process_read_urb, .read_int_callback = f81232_read_int_callback, - .attach = f81232_startup, - .release = f81232_release, + .port_probe = f81232_port_probe, + .port_remove = f81232_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit v1.2.3 From 94ab71ce288921490ca857e25ad174e1921b1e29 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 13:35:00 +0200 Subject: USB: keyspan_pda: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the write waitqueue was initialised but never used. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan_pda.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index ca43ecb4a2bd..bb87e29c4ac2 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -713,29 +713,33 @@ MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw"); MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw"); #endif -static int keyspan_pda_startup(struct usb_serial *serial) +static int keyspan_pda_port_probe(struct usb_serial_port *port) { struct keyspan_pda_private *priv; - /* allocate the private data structures for all ports. Well, for all - one ports. */ - priv = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL); if (!priv) - return 1; /* error */ - usb_set_serial_port_data(serial->port[0], priv); - init_waitqueue_head(&serial->port[0]->write_wait); + return -ENOMEM; + INIT_WORK(&priv->wakeup_work, keyspan_pda_wakeup_write); INIT_WORK(&priv->unthrottle_work, keyspan_pda_request_unthrottle); - priv->serial = serial; - priv->port = serial->port[0]; + priv->serial = port->serial; + priv->port = port; + + usb_set_serial_port_data(port, priv); + return 0; } -static void keyspan_pda_release(struct usb_serial *serial) +static int keyspan_pda_port_remove(struct usb_serial_port *port) { - kfree(usb_get_serial_port_data(serial->port[0])); + struct keyspan_pda_private *priv; + + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } #ifdef KEYSPAN @@ -786,8 +790,8 @@ static struct usb_serial_driver keyspan_pda_device = { .break_ctl = keyspan_pda_break_ctl, .tiocmget = keyspan_pda_tiocmget, .tiocmset = keyspan_pda_tiocmset, - .attach = keyspan_pda_startup, - .release = keyspan_pda_release, + .port_probe = keyspan_pda_port_probe, + .port_remove = keyspan_pda_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit v1.2.3 From 003615302a16579531932576bcd9582ddeba9018 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 13:34:58 +0200 Subject: USB: io_ti: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_ti.c | 91 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 46 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index a2209cd45093..9ae65d679f02 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2532,12 +2532,7 @@ static void edge_break(struct tty_struct *tty, int break_state) static int edge_startup(struct usb_serial *serial) { struct edgeport_serial *edge_serial; - struct edgeport_port *edge_port; - struct usb_device *dev; int status; - int i; - - dev = serial->dev; /* create our private serial structure */ edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); @@ -2555,40 +2550,7 @@ static int edge_startup(struct usb_serial *serial) return status; } - /* set up our port private structures */ - for (i = 0; i < serial->num_ports; ++i) { - edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL); - if (edge_port == NULL) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", - __func__); - goto cleanup; - } - spin_lock_init(&edge_port->ep_lock); - if (kfifo_alloc(&edge_port->write_fifo, EDGE_OUT_BUF_SIZE, - GFP_KERNEL)) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", - __func__); - kfree(edge_port); - goto cleanup; - } - edge_port->port = serial->port[i]; - edge_port->edge_serial = edge_serial; - usb_set_serial_port_data(serial->port[i], edge_port); - edge_port->bUartMode = default_uart_mode; - } - return 0; - -cleanup: - for (--i; i >= 0; --i) { - edge_port = usb_get_serial_port_data(serial->port[i]); - kfifo_free(&edge_port->write_fifo); - kfree(edge_port); - usb_set_serial_port_data(serial->port[i], NULL); - } - kfree(edge_serial); - usb_set_serial_data(serial, NULL); - return -ENOMEM; } static void edge_disconnect(struct usb_serial *serial) @@ -2597,17 +2559,54 @@ static void edge_disconnect(struct usb_serial *serial) static void edge_release(struct usb_serial *serial) { - int i; + kfree(usb_get_serial_data(serial)); +} + +static int edge_port_probe(struct usb_serial_port *port) +{ struct edgeport_port *edge_port; + int ret; - for (i = 0; i < serial->num_ports; ++i) { - edge_port = usb_get_serial_port_data(serial->port[i]); + edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL); + if (!edge_port) + return -ENOMEM; + + ret = kfifo_alloc(&edge_port->write_fifo, EDGE_OUT_BUF_SIZE, + GFP_KERNEL); + if (ret) { + kfree(edge_port); + return -ENOMEM; + } + + ret = edge_create_sysfs_attrs(port); + if (ret) { kfifo_free(&edge_port->write_fifo); kfree(edge_port); + return ret; } - kfree(usb_get_serial_data(serial)); + + spin_lock_init(&edge_port->ep_lock); + edge_port->port = port; + edge_port->edge_serial = usb_get_serial_data(port->serial); + edge_port->bUartMode = default_uart_mode; + + usb_set_serial_port_data(port, edge_port); + + return 0; } +static int edge_port_remove(struct usb_serial_port *port) +{ + struct edgeport_port *edge_port; + + edge_port = usb_get_serial_port_data(port); + + edge_remove_sysfs_attrs(port); + kfifo_free(&edge_port->write_fifo); + kfree(edge_port); + + return 0; +} /* Sysfs Attributes */ @@ -2667,8 +2666,8 @@ static struct usb_serial_driver edgeport_1port_device = { .attach = edge_startup, .disconnect = edge_disconnect, .release = edge_release, - .port_probe = edge_create_sysfs_attrs, - .port_remove = edge_remove_sysfs_attrs, + .port_probe = edge_port_probe, + .port_remove = edge_port_remove, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -2698,8 +2697,8 @@ static struct usb_serial_driver edgeport_2port_device = { .attach = edge_startup, .disconnect = edge_disconnect, .release = edge_release, - .port_probe = edge_create_sysfs_attrs, - .port_remove = edge_remove_sysfs_attrs, + .port_probe = edge_port_probe, + .port_remove = edge_port_remove, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, -- cgit v1.2.3 From 99a6f73c495c420df826e5b267fb073fd6766fc3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 13:35:01 +0200 Subject: USB: kl5kusb105: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the write waitqueue was initialised but never used. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/kl5kusb105.c | 68 +++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 44 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 3f6d7376c02d..1f4517864cd2 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -60,8 +60,8 @@ /* * Function prototypes */ -static int klsi_105_startup(struct usb_serial *serial); -static void klsi_105_release(struct usb_serial *serial); +static int klsi_105_port_probe(struct usb_serial_port *port); +static int klsi_105_port_remove(struct usb_serial_port *port); static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port); static void klsi_105_close(struct usb_serial_port *port); static void klsi_105_set_termios(struct tty_struct *tty, @@ -99,8 +99,8 @@ static struct usb_serial_driver kl5kusb105d_device = { /*.break_ctl = klsi_105_break_ctl,*/ .tiocmget = klsi_105_tiocmget, .tiocmset = klsi_105_tiocmset, - .attach = klsi_105_startup, - .release = klsi_105_release, + .port_probe = klsi_105_port_probe, + .port_remove = klsi_105_port_remove, .throttle = usb_serial_generic_throttle, .unthrottle = usb_serial_generic_unthrottle, .process_read_urb = klsi_105_process_read_urb, @@ -223,60 +223,40 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, * Driver's tty interface functions */ -static int klsi_105_startup(struct usb_serial *serial) +static int klsi_105_port_probe(struct usb_serial_port *port) { struct klsi_105_private *priv; - int i; - /* check if we support the product id (see keyspan.c) - * FIXME - */ + priv = kmalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - /* allocate the private data structure */ - for (i = 0; i < serial->num_ports; i++) { - priv = kmalloc(sizeof(struct klsi_105_private), - GFP_KERNEL); - if (!priv) { - dev_dbg(&serial->interface->dev, - "%s - kmalloc for klsi_105_private failed.\n", - __func__); - i--; - goto err_cleanup; - } - /* set initial values for control structures */ - priv->cfg.pktlen = 5; - priv->cfg.baudrate = kl5kusb105a_sio_b9600; - priv->cfg.databits = kl5kusb105a_dtb_8; - priv->cfg.unknown1 = 0; - priv->cfg.unknown2 = 1; + /* set initial values for control structures */ + priv->cfg.pktlen = 5; + priv->cfg.baudrate = kl5kusb105a_sio_b9600; + priv->cfg.databits = kl5kusb105a_dtb_8; + priv->cfg.unknown1 = 0; + priv->cfg.unknown2 = 1; - priv->line_state = 0; + priv->line_state = 0; - usb_set_serial_port_data(serial->port[i], priv); + spin_lock_init(&priv->lock); - spin_lock_init(&priv->lock); + /* priv->termios is left uninitialized until port opening */ - /* priv->termios is left uninitialized until port opening */ - init_waitqueue_head(&serial->port[i]->write_wait); - } + usb_set_serial_port_data(port, priv); return 0; - -err_cleanup: - for (; i >= 0; i--) { - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - usb_set_serial_port_data(serial->port[i], NULL); - } - return -ENOMEM; } -static void klsi_105_release(struct usb_serial *serial) +static int klsi_105_port_remove(struct usb_serial_port *port) { - int i; + struct klsi_105_private *priv; + + priv = usb_get_serial_port_data(port); + kfree(priv); - for (i = 0; i < serial->num_ports; ++i) - kfree(usb_get_serial_port_data(serial->port[i])); + return 0; } static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) -- cgit v1.2.3 From 53636555b9190f88320d9d46cf142f8797895456 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 13:34:59 +0200 Subject: USB: iuu_phoenix: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/iuu_phoenix.c | 62 ++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 01da3ea36e89..959f71e31dca 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -72,63 +72,45 @@ struct iuu_private { u32 clk; }; - -static void iuu_free_buf(struct iuu_private *priv) -{ - kfree(priv->buf); - kfree(priv->writebuf); -} - -static int iuu_alloc_buf(struct usb_serial *serial, struct iuu_private *priv) -{ - priv->buf = kzalloc(256, GFP_KERNEL); - priv->writebuf = kzalloc(256, GFP_KERNEL); - if (!priv->buf || !priv->writebuf) { - iuu_free_buf(priv); - dev_dbg(&serial->dev->dev, "%s problem allocation buffer\n", __func__); - return -ENOMEM; - } - dev_dbg(&serial->dev->dev, "%s - Privates buffers allocation success\n", __func__); - return 0; -} - -static int iuu_startup(struct usb_serial *serial) +static int iuu_port_probe(struct usb_serial_port *port) { struct iuu_private *priv; priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL); - dev_dbg(&serial->dev->dev, "%s- priv allocation success\n", __func__); if (!priv) return -ENOMEM; - if (iuu_alloc_buf(serial, priv)) { + + priv->buf = kzalloc(256, GFP_KERNEL); + if (!priv->buf) { kfree(priv); return -ENOMEM; } + + priv->writebuf = kzalloc(256, GFP_KERNEL); + if (!priv->writebuf) { + kfree(priv->buf); + kfree(priv); + return -ENOMEM; + } + priv->vcc = vcc_default; spin_lock_init(&priv->lock); init_waitqueue_head(&priv->delta_msr_wait); - usb_set_serial_port_data(serial->port[0], priv); + + usb_set_serial_port_data(port, priv); + return 0; } -/* Release function */ -static void iuu_release(struct usb_serial *serial) +static int iuu_port_remove(struct usb_serial_port *port) { - struct usb_serial_port *port = serial->port[0]; struct iuu_private *priv = usb_get_serial_port_data(port); - if (!port) - return; - - if (priv) { - iuu_free_buf(priv); - dev_dbg(&port->dev, "%s - I will free all\n", __func__); - usb_set_serial_port_data(port, NULL); - dev_dbg(&port->dev, "%s - priv is not anymore in port structure\n", __func__); - kfree(priv); + kfree(priv->writebuf); + kfree(priv->buf); + kfree(priv); - dev_dbg(&port->dev, "%s priv is now kfree\n", __func__); - } + return 0; } static int iuu_tiocmset(struct tty_struct *tty, @@ -1225,8 +1207,8 @@ static struct usb_serial_driver iuu_device = { .tiocmset = iuu_tiocmset, .set_termios = iuu_set_termios, .init_termios = iuu_init_termios, - .attach = iuu_startup, - .release = iuu_release, + .port_probe = iuu_port_probe, + .port_remove = iuu_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit v1.2.3 From 289b076f89c2c3260e914dad18ae12f193ea86d5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 16:31:32 +0200 Subject: USB: oti6858: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/oti6858.c | 68 ++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 40 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 933241f03fd8..cee9a52ca891 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -137,8 +137,8 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty); static int oti6858_tiocmget(struct tty_struct *tty); static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static int oti6858_startup(struct usb_serial *serial); -static void oti6858_release(struct usb_serial *serial); +static int oti6858_port_probe(struct usb_serial_port *port); +static int oti6858_port_remove(struct usb_serial_port *port); /* device info */ static struct usb_serial_driver oti6858_device = { @@ -161,8 +161,8 @@ static struct usb_serial_driver oti6858_device = { .write_bulk_callback = oti6858_write_bulk_callback, .write_room = oti6858_write_room, .chars_in_buffer = oti6858_chars_in_buffer, - .attach = oti6858_startup, - .release = oti6858_release, + .port_probe = oti6858_port_probe, + .port_remove = oti6858_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { @@ -331,36 +331,33 @@ static void send_data(struct work_struct *work) usb_serial_port_softint(port); } -static int oti6858_startup(struct usb_serial *serial) +static int oti6858_port_probe(struct usb_serial_port *port) { - struct usb_serial_port *port = serial->port[0]; struct oti6858_private *priv; - int i; - - for (i = 0; i < serial->num_ports; ++i) { - priv = kzalloc(sizeof(struct oti6858_private), GFP_KERNEL); - if (!priv) - break; - - spin_lock_init(&priv->lock); - init_waitqueue_head(&priv->intr_wait); -/* INIT_WORK(&priv->setup_work, setup_line, serial->port[i]); */ -/* INIT_WORK(&priv->write_work, send_data, serial->port[i]); */ - priv->port = port; - INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line); - INIT_DELAYED_WORK(&priv->delayed_write_work, send_data); - - usb_set_serial_port_data(serial->port[i], priv); - } - if (i == serial->num_ports) - return 0; - for (--i; i >= 0; --i) { - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - usb_set_serial_port_data(serial->port[i], NULL); - } - return -ENOMEM; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->intr_wait); + priv->port = port; + INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line); + INIT_DELAYED_WORK(&priv->delayed_write_work, send_data); + + usb_set_serial_port_data(port, priv); + + return 0; +} + +static int oti6858_port_remove(struct usb_serial_port *port) +{ + struct oti6858_private *priv; + + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -709,15 +706,6 @@ static int oti6858_ioctl(struct tty_struct *tty, return -ENOIOCTLCMD; } - -static void oti6858_release(struct usb_serial *serial) -{ - int i; - - for (i = 0; i < serial->num_ports; ++i) - kfree(usb_get_serial_port_data(serial->port[i])); -} - static void oti6858_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; -- cgit v1.2.3 From 51ef847df74632e7cfdf952afc3887de105b8b35 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 16:31:35 +0200 Subject: USB: ti_usb_3410_5052: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ti_usb_3410_5052.c | 88 +++++++++++++++++------------------ 1 file changed, 43 insertions(+), 45 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 6f49392cda5b..f2530d2ef3c4 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -97,6 +97,8 @@ struct ti_device { static int ti_startup(struct usb_serial *serial); static void ti_release(struct usb_serial *serial); +static int ti_port_probe(struct usb_serial_port *port); +static int ti_port_remove(struct usb_serial_port *port); static int ti_open(struct tty_struct *tty, struct usb_serial_port *port); static void ti_close(struct usb_serial_port *port); static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -221,6 +223,8 @@ static struct usb_serial_driver ti_1port_device = { .num_ports = 1, .attach = ti_startup, .release = ti_release, + .port_probe = ti_port_probe, + .port_remove = ti_port_remove, .open = ti_open, .close = ti_close, .write = ti_write, @@ -249,6 +253,8 @@ static struct usb_serial_driver ti_2port_device = { .num_ports = 2, .attach = ti_startup, .release = ti_release, + .port_probe = ti_port_probe, + .port_remove = ti_port_remove, .open = ti_open, .close = ti_close, .write = ti_write, @@ -347,11 +353,8 @@ module_exit(ti_exit); static int ti_startup(struct usb_serial *serial) { struct ti_device *tdev; - struct ti_port *tport; struct usb_device *dev = serial->dev; int status; - int i; - dev_dbg(&dev->dev, "%s - product 0x%4X, num configurations %d, configuration value %d", @@ -399,42 +402,8 @@ static int ti_startup(struct usb_serial *serial) goto free_tdev; } - /* set up port structures */ - for (i = 0; i < serial->num_ports; ++i) { - tport = kzalloc(sizeof(struct ti_port), GFP_KERNEL); - if (tport == NULL) { - dev_err(&dev->dev, "%s - out of memory\n", __func__); - status = -ENOMEM; - goto free_tports; - } - spin_lock_init(&tport->tp_lock); - tport->tp_uart_base_addr = (i == 0 ? - TI_UART1_BASE_ADDR : TI_UART2_BASE_ADDR); - tport->tp_closing_wait = closing_wait; - init_waitqueue_head(&tport->tp_msr_wait); - init_waitqueue_head(&tport->tp_write_wait); - if (kfifo_alloc(&tport->write_fifo, TI_WRITE_BUF_SIZE, - GFP_KERNEL)) { - dev_err(&dev->dev, "%s - out of memory\n", __func__); - kfree(tport); - status = -ENOMEM; - goto free_tports; - } - tport->tp_port = serial->port[i]; - tport->tp_tdev = tdev; - usb_set_serial_port_data(serial->port[i], tport); - tport->tp_uart_mode = 0; /* default is RS232 */ - } - return 0; -free_tports: - for (--i; i >= 0; --i) { - tport = usb_get_serial_port_data(serial->port[i]); - kfifo_free(&tport->write_fifo); - kfree(tport); - usb_set_serial_port_data(serial->port[i], NULL); - } free_tdev: kfree(tdev); usb_set_serial_data(serial, NULL); @@ -444,21 +413,50 @@ free_tdev: static void ti_release(struct usb_serial *serial) { - int i; struct ti_device *tdev = usb_get_serial_data(serial); + + kfree(tdev); +} + +static int ti_port_probe(struct usb_serial_port *port) +{ struct ti_port *tport; - for (i = 0; i < serial->num_ports; ++i) { - tport = usb_get_serial_port_data(serial->port[i]); - if (tport) { - kfifo_free(&tport->write_fifo); - kfree(tport); - } + tport = kzalloc(sizeof(*tport), GFP_KERNEL); + if (!tport) + return -ENOMEM; + + spin_lock_init(&tport->tp_lock); + if (port == port->serial->port[0]) + tport->tp_uart_base_addr = TI_UART1_BASE_ADDR; + else + tport->tp_uart_base_addr = TI_UART2_BASE_ADDR; + tport->tp_closing_wait = closing_wait; + init_waitqueue_head(&tport->tp_msr_wait); + init_waitqueue_head(&tport->tp_write_wait); + if (kfifo_alloc(&tport->write_fifo, TI_WRITE_BUF_SIZE, GFP_KERNEL)) { + kfree(tport); + return -ENOMEM; } + tport->tp_port = port; + tport->tp_tdev = usb_get_serial_data(port->serial); + tport->tp_uart_mode = 0; /* default is RS232 */ - kfree(tdev); + usb_set_serial_port_data(port, tport); + + return 0; } +static int ti_port_remove(struct usb_serial_port *port) +{ + struct ti_port *tport; + + tport = usb_get_serial_port_data(port); + kfifo_free(&tport->write_fifo); + kfree(tport); + + return 0; +} static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) { -- cgit v1.2.3 From 638b9e15233c9570bce65301aa9877235316b9f0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 16:31:34 +0200 Subject: USB: ssu100: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ssu100.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 015810b3785b..868d1e6852e2 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -67,13 +67,6 @@ struct ssu100_port_private { struct async_icount icount; }; -static void ssu100_release(struct usb_serial *serial) -{ - struct ssu100_port_private *priv = usb_get_serial_port_data(*serial->port); - - kfree(priv); -} - static inline int ssu100_control_msg(struct usb_device *dev, u8 request, u16 data, u16 index) { @@ -441,22 +434,34 @@ static int ssu100_ioctl(struct tty_struct *tty, } static int ssu100_attach(struct usb_serial *serial) +{ + return ssu100_initdevice(serial->dev); +} + +static int ssu100_port_probe(struct usb_serial_port *port) { struct ssu100_port_private *priv; - struct usb_serial_port *port = *serial->port; priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__, - sizeof(*priv)); + if (!priv) return -ENOMEM; - } spin_lock_init(&priv->status_lock); init_waitqueue_head(&priv->delta_msr_wait); + usb_set_serial_port_data(port, priv); - return ssu100_initdevice(serial->dev); + return 0; +} + +static int ssu100_port_remove(struct usb_serial_port *port) +{ + struct ssu100_port_private *priv; + + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } static int ssu100_tiocmget(struct tty_struct *tty) @@ -647,7 +652,8 @@ static struct usb_serial_driver ssu100_device = { .open = ssu100_open, .close = ssu100_close, .attach = ssu100_attach, - .release = ssu100_release, + .port_probe = ssu100_port_probe, + .port_remove = ssu100_port_remove, .dtr_rts = ssu100_dtr_rts, .process_read_urb = ssu100_process_read_urb, .tiocmget = ssu100_tiocmget, -- cgit v1.2.3 From bf90ff5f3b8f67e5b42df4ea4fd543f8010a2676 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Oct 2012 16:31:33 +0200 Subject: USB: spcp8x5: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/spcp8x5.c | 46 +++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 9716efe92955..769c137f8975 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -157,13 +157,10 @@ struct spcp8x5_private { u8 line_status; }; -/* desc : when device plug in,this function would be called. - * thanks to usb_serial subsystem,then do almost every things for us. And what - * we should do just alloc the buffer */ -static int spcp8x5_startup(struct usb_serial *serial) +static int spcp8x5_port_probe(struct usb_serial_port *port) { + struct usb_serial *serial = port->serial; struct spcp8x5_private *priv; - int i; enum spcp8x5_type type = SPCP825_007_TYPE; u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); @@ -180,34 +177,27 @@ static int spcp8x5_startup(struct usb_serial *serial) type = SPCP825_PHILIP_TYPE; dev_dbg(&serial->dev->dev, "device type = %d\n", (int)type); - for (i = 0; i < serial->num_ports; ++i) { - priv = kzalloc(sizeof(struct spcp8x5_private), GFP_KERNEL); - if (!priv) - goto cleanup; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - spin_lock_init(&priv->lock); - init_waitqueue_head(&priv->delta_msr_wait); - priv->type = type; - usb_set_serial_port_data(serial->port[i] , priv); - } + spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->delta_msr_wait); + priv->type = type; + + usb_set_serial_port_data(port , priv); return 0; -cleanup: - for (--i; i >= 0; --i) { - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - usb_set_serial_port_data(serial->port[i] , NULL); - } - return -ENOMEM; } -/* call when the device plug out. free all the memory alloced by probe */ -static void spcp8x5_release(struct usb_serial *serial) +static int spcp8x5_port_remove(struct usb_serial_port *port) { - int i; + struct spcp8x5_private *priv; - for (i = 0; i < serial->num_ports; i++) - kfree(usb_get_serial_port_data(serial->port[i])); + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; } /* set the modem control line of the device. @@ -649,8 +639,8 @@ static struct usb_serial_driver spcp8x5_device = { .ioctl = spcp8x5_ioctl, .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, - .attach = spcp8x5_startup, - .release = spcp8x5_release, + .port_probe = spcp8x5_port_probe, + .port_remove = spcp8x5_port_remove, .process_read_urb = spcp8x5_process_read_urb, }; -- cgit v1.2.3 From 0978c9499944d0670338fd048a3bdb1624dc66dc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Oct 2012 10:52:17 +0200 Subject: USB: iuu_phoenix: fix sysfs-attribute creation Make sure sysfs attributes are created at port probe. A recent patch ("USB: iuu_phoenix: fix port-data memory leak") removed the sysfs-attribute creation by mistake. Reported-by: Yuanhan Liu Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/iuu_phoenix.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 959f71e31dca..cd5533e81de7 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -53,6 +53,8 @@ static int iuu_cardout; static bool xmas; static int vcc_default = 5; +static int iuu_create_sysfs_attrs(struct usb_serial_port *port); +static int iuu_remove_sysfs_attrs(struct usb_serial_port *port); static void read_rxcmd_callback(struct urb *urb); struct iuu_private { @@ -75,6 +77,7 @@ struct iuu_private { static int iuu_port_probe(struct usb_serial_port *port) { struct iuu_private *priv; + int ret; priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL); if (!priv) @@ -99,6 +102,14 @@ static int iuu_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, priv); + ret = iuu_create_sysfs_attrs(port); + if (ret) { + kfree(priv->writebuf); + kfree(priv->buf); + kfree(priv); + return ret; + } + return 0; } @@ -106,6 +117,7 @@ static int iuu_port_remove(struct usb_serial_port *port) { struct iuu_private *priv = usb_get_serial_port_data(port); + iuu_remove_sysfs_attrs(port); kfree(priv->writebuf); kfree(priv->buf); kfree(priv); @@ -1197,8 +1209,6 @@ static struct usb_serial_driver iuu_device = { .num_ports = 1, .bulk_in_size = 512, .bulk_out_size = 512, - .port_probe = iuu_create_sysfs_attrs, - .port_remove = iuu_remove_sysfs_attrs, .open = iuu_open, .close = iuu_close, .write = iuu_uart_write, -- cgit v1.2.3 From 5d8c61bc283826827e1f06816c146bfc507d3834 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Oct 2012 11:43:28 +0200 Subject: USB: io_ti: fix sysfs-attribute creation Make sure port data is initialised before creating sysfs attributes to avoid a race. A recent patch ("USB: io_ti: fix port-data memory leak") got the sysfs-attribute creation and port-data initialisation ordering wrong. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_ti.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 9ae65d679f02..60023c2d2a31 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2578,13 +2578,6 @@ static int edge_port_probe(struct usb_serial_port *port) return -ENOMEM; } - ret = edge_create_sysfs_attrs(port); - if (ret) { - kfifo_free(&edge_port->write_fifo); - kfree(edge_port); - return ret; - } - spin_lock_init(&edge_port->ep_lock); edge_port->port = port; edge_port->edge_serial = usb_get_serial_data(port->serial); @@ -2592,6 +2585,13 @@ static int edge_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, edge_port); + ret = edge_create_sysfs_attrs(port); + if (ret) { + kfifo_free(&edge_port->write_fifo); + kfree(edge_port); + return ret; + } + return 0; } -- cgit v1.2.3 From 470809741a28c3092279f4e1f3f432e534d46068 Mon Sep 17 00:00:00 2001 From: "Alexis R. Cortes" Date: Wed, 17 Oct 2012 14:09:12 -0500 Subject: usb: host: xhci: New system added for Compliance Mode Patch on SN65LVPE502CP This minor change adds a new system to which the "Fix Compliance Mode on SN65LVPE502CP Hardware" patch has to be applied also. System added: Vendor: Hewlett-Packard. System Model: Z1 Signed-off-by: Alexis R. Cortes Acked-by: Sarah Sharp Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8d7fcbbe6ade..7d462bf20092 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -479,7 +479,8 @@ static bool compliance_mode_recovery_timer_quirk_check(void) if (strstr(dmi_product_name, "Z420") || strstr(dmi_product_name, "Z620") || - strstr(dmi_product_name, "Z820")) + strstr(dmi_product_name, "Z820") || + strstr(dmi_product_name, "Z1")) return true; return false; -- cgit v1.2.3 From 1452df6f1b7e396d89c2a1fdbdc0e0e839f97671 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 18 Oct 2012 17:19:53 +0200 Subject: USB: option: blacklist net interface on ZTE devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on information from the ZTE Windows drivers. Cc: Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 68 ++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 30cff03e9f01..7e46894d784e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -503,11 +503,19 @@ static const struct option_blacklist_info net_intf5_blacklist = { .reserved = BIT(5), }; +static const struct option_blacklist_info net_intf6_blacklist = { + .reserved = BIT(6), +}; + static const struct option_blacklist_info zte_mf626_blacklist = { .sendsetup = BIT(0) | BIT(1), .reserved = BIT(4), }; +static const struct option_blacklist_info zte_1255_blacklist = { + .reserved = BIT(3) | BIT(4), +}; + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -853,13 +861,19 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0122, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf6_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0128, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0142, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0143, 0xff, 0xff, 0xff) }, @@ -872,7 +886,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) }, @@ -886,7 +901,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) }, @@ -1002,18 +1018,24 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1169, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1170, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1244, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1246, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1248, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1249, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1250, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1251, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1253, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_1255_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1257, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1258, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1259, 0xff, 0xff, 0xff) }, @@ -1071,15 +1093,21 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) }, -- cgit v1.2.3 From 4b35f1c52943851b310afb09047bfe991ac8f5ae Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 18 Oct 2012 17:14:17 +0200 Subject: USB: option: add more ZTE devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 7e46894d784e..54d4148d01d1 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -895,6 +895,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0191, 0xff, 0xff, 0xff), /* ZTE EuFi890 */ + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0199, 0xff, 0xff, 0xff), /* ZTE MF820S */ + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */ + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff), @@ -903,6 +909,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1021, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) }, @@ -1080,8 +1088,16 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff), /* ZTE MF91 */ + .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) }, -- cgit v1.2.3 From d479c911789e14a19b86ca09b0b634f1449c8c6b Mon Sep 17 00:00:00 2001 From: Ben Collins Date: Fri, 19 Oct 2012 10:24:12 -0400 Subject: USB: ehci-fsl: Return valid error in ehci_fsl_setup_phy ehci_fsl_setup_phy is supposed to return an int, but had a void return value in the case of controller_ver being invalid. Introduced by commit 3735ba8db8e6 ("powerpc/usb: fix bug of CPU hang when missing USB PHY clock"), which missed one return. Signed-off-by: Ben Collins Cc: Shengzhou Liu Cc: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/usb/host/ehci-fsl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 9bfde82078ec..0d2f35ca93f1 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -222,7 +222,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, if (pdata->controller_ver < 0) { dev_warn(hcd->self.controller, "Could not get controller version\n"); - return; + return -ENODEV; } portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]); -- cgit v1.2.3 From 3b6054da68f9b0d5ed6a7ed0f42a79e61904352c Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Mon, 1 Oct 2012 22:21:12 +0300 Subject: usb hub: send clear_tt_buffer_complete events when canceling TT clear work There is a race condition in the USB hub code with regard to handling TT clear requests that can get the HCD driver in a deadlock. Usually when an TT clear request is scheduled it will be executed immediately: <7>[ 6.077583] usb 2-1.3: unlink qh1-0e01/f4d4db00 start 0 [1/2 us] <3>[ 6.078041] usb 2-1: clear tt buffer port 3, a3 ep2 t04048d82 <7>[ 6.078299] hub_tt_work:731 <7>[ 9.309089] usb 2-1.5: link qh1-0e01/f4d506c0 start 0 [1/2 us] <7>[ 9.324526] ehci_hcd 0000:00:1d.0: reused qh f4d4db00 schedule <7>[ 9.324539] usb 2-1.3: link qh1-0e01/f4d4db00 start 0 [1/2 us] <7>[ 9.341530] usb 1-1.1: link qh4-0e01/f397aec0 start 2 [1/2 us] <7>[ 10.116159] usb 2-1.3: unlink qh1-0e01/f4d4db00 start 0 [1/2 us] <3>[ 10.116459] usb 2-1: clear tt buffer port 3, a3 ep2 t04048d82 <7>[ 10.116537] hub_tt_work:731 However, if a suspend operation is triggered before hub_tt_work is scheduled, hub_quiesce will cancel the work without notifying the HCD driver: <3>[ 35.033941] usb 2-1: clear tt buffer port 3, a3 ep2 t04048d80 <5>[ 35.034022] sd 0:0:0:0: [sda] Stopping disk <7>[ 35.034039] hub 2-1:1.0: hub_suspend <7>[ 35.034067] usb 2-1: unlink qh256-0001/f3b1ab00 start 1 [1/0 us] <7>[ 35.035085] hub 1-0:1.0: hub_suspend <7>[ 35.035102] usb usb1: bus suspend, wakeup 0 <7>[ 35.035106] ehci_hcd 0000:00:1a.0: suspend root hub <7>[ 35.035298] hub 2-0:1.0: hub_suspend <7>[ 35.035313] usb usb2: bus suspend, wakeup 0 <7>[ 35.035315] ehci_hcd 0000:00:1d.0: suspend root hub <6>[ 35.250017] PM: suspend of devices complete after 216.979 msecs <6>[ 35.250822] PM: late suspend of devices complete after 0.799 msecs <7>[ 35.252343] ehci_hcd 0000:00:1d.0: wakeup: 1 <7>[ 35.262923] ehci_hcd 0000:00:1d.0: --> PCI D3hot <7>[ 35.263302] ehci_hcd 0000:00:1a.0: wakeup: 1 <7>[ 35.273912] ehci_hcd 0000:00:1a.0: --> PCI D3hot <6>[ 35.274254] PM: noirq suspend of devices complete after 23.442 msecs <6>[ 35.274975] ACPI: Preparing to enter system sleep state S3 <6>[ 35.292666] PM: Saving platform NVS memory <7>[ 35.295030] Disabling non-boot CPUs ... <6>[ 35.297351] CPU 1 is now offline <6>[ 35.300345] CPU 2 is now offline <6>[ 35.303929] CPU 3 is now offline <7>[ 35.303931] lockdep: fixing up alternatives. <6>[ 35.304825] Extended CMOS year: 2000 When the device will resume the EHCI driver will get stuck in ehci_endpoint_disable waiting for the tt_clearing flag to reset: <0>[ 47.610967] usb 2-1.3: **** DPM device timeout **** <7>[ 47.610972] f2f11c60 00000092 f2f11c0c c10624a5 00000003 f4c6e880 c1c8a4c0 c1c8a4c0 <7>[ 47.610983] 15c55698 0000000b f56b34c0 f2a45b70 f4c6e880 00000082 f2a4602c f2f11c30 <7>[ 47.610993] c10787f8 f4cac000 f2a45b70 00000000 f4cac010 f2f11c58 00000046 00000001 <7>[ 47.611004] Call Trace: <7>[ 47.611006] [] ? sched_clock_cpu+0xf5/0x160 <7>[ 47.611019] [] ? lock_release_holdtime.part.22+0x88/0xf0 <7>[ 47.611026] [] ? lock_timer_base.isra.35+0x26/0x50 <7>[ 47.611034] [] ? schedule_timeout+0x133/0x290 <7>[ 47.611044] [] schedule+0x1e/0x50 <7>[ 47.611051] [] schedule_timeout+0x138/0x290 <7>[ 47.611057] [] ? sched_clock_cpu+0xf5/0x160 <7>[ 47.611063] [] ? usleep_range+0x40/0x40 <7>[ 47.611070] [] schedule_timeout_uninterruptible+0x15/0x20 <7>[ 47.611077] [] ehci_endpoint_disable+0x64/0x160 <7>[ 47.611084] [] ? usb_hcd_flush_endpoint+0x10e/0x1d0 <7>[ 47.611092] [] ? sysfs_add_file+0x13/0x20 <7>[ 47.611100] [] usb_hcd_disable_endpoint+0x29/0x40 <7>[ 47.611107] [] usb_disable_endpoint+0x5c/0x80 <7>[ 47.611111] [] usb_disable_interface+0x37/0x50 <7>[ 47.611116] [] usb_reset_and_verify_device+0x4b0/0x640 <7>[ 47.611122] [] ? hub_port_status+0xb5/0x100 <7>[ 47.611129] [] usb_port_resume+0xd5/0x220 <7>[ 47.611136] [] generic_resume+0xf/0x30 <7>[ 47.611142] [] usb_resume+0x133/0x180 <7>[ 47.611147] [] ? usb_dev_thaw+0x10/0x10 <7>[ 47.611152] [] usb_dev_resume+0xd/0x10 <7>[ 47.611157] [] dpm_run_callback+0x40/0xb0 <7>[ 47.611164] [] ? pm_runtime_enable+0x43/0x70 <7>[ 47.611171] [] device_resume+0x1a6/0x2c0 <7>[ 47.611177] [] ? dpm_show_time+0xe0/0xe0 <7>[ 47.611183] [] async_resume+0x19/0x40 <7>[ 47.611189] [] async_run_entry_fn+0x64/0x160 <7>[ 47.611196] [] ? process_one_work+0x104/0x480 <7>[ 47.611203] [] ? process_one_work+0x10c/0x480 <7>[ 47.611209] [] process_one_work+0x180/0x480 <7>[ 47.611215] [] ? process_one_work+0x104/0x480 <7>[ 47.611220] [] ? async_schedule+0x10/0x10 <7>[ 47.611226] [] worker_thread+0x11c/0x2f0 <7>[ 47.611233] [] ? manage_workers.isra.27+0x1f0/0x1f0 <7>[ 47.611239] [] kthread+0x78/0x80 <7>[ 47.611244] [] ? timer_cpu_notify+0xd6/0x20d <7>[ 47.611253] [] ? __init_kthread_worker+0x60/0x60 <7>[ 47.611258] [] kernel_thread_helper+0x6/0xd <7>[ 47.611283] ------------[ cut here ]------------ This patch changes hub_quiesce behavior to flush the TT clear work instead of canceling it, to make sure that no TT clear request remains uncompleted before suspend. Signed-off-by: Octavian Purdila Acked-by: Alan Stern Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 64854d76f529..1181e917fec7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -739,13 +739,16 @@ static void hub_tt_work(struct work_struct *work) int limit = 100; spin_lock_irqsave (&hub->tt.lock, flags); - while (--limit && !list_empty (&hub->tt.clear_list)) { + while (!list_empty(&hub->tt.clear_list)) { struct list_head *next; struct usb_tt_clear *clear; struct usb_device *hdev = hub->hdev; const struct hc_driver *drv; int status; + if (!hub->quiescing && --limit < 0) + break; + next = hub->tt.clear_list.next; clear = list_entry (next, struct usb_tt_clear, clear_list); list_del (&clear->clear_list); @@ -1210,7 +1213,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); if (hub->tt.hub) - cancel_work_sync(&hub->tt.clear_work); + flush_work_sync(&hub->tt.clear_work); } /* caller has locked the hub device */ -- cgit v1.2.3 From 170ed807340b4db0a5e010c2e4da43cf5a2c9a29 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Mon, 15 Oct 2012 15:38:34 +0800 Subject: usb/xhci: release xhci->lock during turning on/off usb port's acpi power resource and checking the existence of port's power resource When setting usb port's acpi power resource, there will be some xhci hub requests. This will cause dead lock since xhci->lock has been held before setting acpi power resource in the xhci_hub_control(). The usb_acpi_power_manageable() function might fall into sleep so release xhci->lock before invoking it. Signed-off-by: Lan Tianyu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index aa90ad4d4fd5..65d416c08cef 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -809,11 +809,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); + spin_unlock_irqrestore(&xhci->lock, flags); temp = usb_acpi_power_manageable(hcd->self.root_hub, wIndex); if (temp) usb_acpi_set_power_state(hcd->self.root_hub, wIndex, true); + spin_lock_irqsave(&xhci->lock, flags); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); @@ -917,11 +919,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_writel(xhci, temp & ~PORT_POWER, port_array[wIndex]); + spin_unlock_irqrestore(&xhci->lock, flags); temp = usb_acpi_power_manageable(hcd->self.root_hub, wIndex); if (temp) usb_acpi_set_power_state(hcd->self.root_hub, wIndex, false); + spin_lock_irqsave(&xhci->lock, flags); break; default: goto error; -- cgit v1.2.3 From 27c411c95a8e9ed92be658abf1d63e22873a3a66 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Mon, 15 Oct 2012 15:38:35 +0800 Subject: usb/xhci: Remove (__force__ __u16) before assigning DeviceRemovable and assign directly. Struct usb_hub_descriptor.ss.DeviceRemovable has been defined as __le16 and (__force__ __u16) doesn't need. Signed-off-by: Lan Tianyu Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 65d416c08cef..a686cf4905bb 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -151,9 +151,8 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, if (portsc & PORT_DEV_REMOVE) port_removable |= 1 << (i + 1); } - memset(&desc->u.ss.DeviceRemovable, - (__force __u16) cpu_to_le16(port_removable), - sizeof(__u16)); + + desc->u.ss.DeviceRemovable = cpu_to_le16(port_removable); } static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, -- cgit v1.2.3 From ca749b2a2e72b39b3a46d1e4d096e338bf714a3c Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 17 Oct 2012 22:17:35 +0200 Subject: usb: musb: dsps: fix res_name length The res_name is used for the name construction of a DT property as follows: sprintf(res_name, "port%d-mode", id); Hence, res_name must be at least 11 characters long in order to store the name including the terminating '\0'. While at it, use to snprintf() rather than sprintf() when accessing this buffer. Reported-by: David Binderman Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 444346e1e10d..ff5f112053d2 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -458,11 +458,11 @@ static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) struct platform_device *musb; struct resource *res; struct resource resources[2]; - char res_name[10]; + char res_name[11]; int ret, musbid; /* get memory resource */ - sprintf(res_name, "musb%d", id); + snprintf(res_name, sizeof(res_name), "musb%d", id); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); if (!res) { dev_err(dev, "%s get mem resource failed\n", res_name); @@ -473,7 +473,7 @@ static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) resources[0] = *res; /* get irq resource */ - sprintf(res_name, "musb%d-irq", id); + snprintf(res_name, sizeof(res_name), "musb%d-irq", id); res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); if (!res) { dev_err(dev, "%s get irq resource failed\n", res_name); @@ -530,7 +530,7 @@ static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps); of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits); - sprintf(res_name, "port%d-mode", id); + snprintf(res_name, sizeof(res_name), "port%d-mode", id); of_property_read_u32(np, res_name, (u32 *)&pdata->mode); of_property_read_u32(np, "power", (u32 *)&pdata->power); config->multipoint = of_property_read_bool(np, "multipoint"); -- cgit v1.2.3 From 000b7f5127900f695668191353a3291832c1914e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 22 Oct 2012 13:51:11 +0800 Subject: usb: gadget: net2272: fix missing unlock on error in net2272_irq() Add the missing unlock on the error handle path in function net2272_irq(). Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi --- drivers/usb/gadget/net2272.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 43ac7482fa91..c009263a47e3 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -2069,8 +2069,10 @@ static irqreturn_t net2272_irq(int irq, void *_dev) #if defined(PLX_PCI_RDK2) /* see if PCI int for us by checking irqstat */ intcsr = readl(dev->rdk2.fpga_base_addr + RDK2_IRQSTAT); - if (!intcsr & (1 << NET2272_PCI_IRQ)) + if (!intcsr & (1 << NET2272_PCI_IRQ)) { + spin_unlock(&dev->lock); return IRQ_NONE; + } /* check dma interrupts */ #endif /* Platform/devcice interrupt handler */ -- cgit v1.2.3 From 4f053a24eca933ec10ce68eef61d38d179f34e50 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Oct 2012 23:31:33 -0700 Subject: usb: renesas_usbhs: fixup: avoid NULL access on error case pipe detach If renesas_usbhs or DMAEngine interrupt didn't happen by a certain cause, urb->ep will be NULL by usb time out. Then, host mode will access to it and crash kernel. This patch fixes it. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod_host.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index 9b69a1323294..069cd765400c 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -334,6 +334,11 @@ static void usbhsh_pipe_detach(struct usbhsh_hpriv *hpriv, struct device *dev = usbhs_priv_to_dev(priv); unsigned long flags; + if (unlikely(!uep)) { + dev_err(dev, "no uep\n"); + return; + } + /******************** spin lock ********************/ usbhs_lock(priv, flags); -- cgit v1.2.3 From 1cb60156defa4f23d5318ea1ddd400f25b2d0ce5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Oct 2012 23:33:30 -0700 Subject: usb: renesas_usbhs: fixup dma transfer stall renesas_usbhs driver can switch DMA/PIO transfer by using handler, and each handler have push/pop direction. But unfortunately, current dma push handler didn't a path which calls usbhs_pipe_enable(). Thus, dma transfer never happened. this patch fixes it. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/fifo.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 143c4e9e1be4..c021b202c0f3 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -795,6 +795,7 @@ static void xfer_work(struct work_struct *work) dev_dbg(dev, " %s %d (%d/ %d)\n", fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); + usbhs_pipe_enable(pipe); usbhsf_dma_start(pipe, fifo); dma_async_issue_pending(chan); } -- cgit v1.2.3 From 966e7a854177097083683176ced871558b631a12 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 17 Oct 2012 12:17:50 +0200 Subject: xhci: endianness xhci_calculate_intel_u2_timeout An le16 is accessed without conversion. This patch should be backported to kernels as old as 3.5, that contain the commit e3567d2c15a7a8e2f992a5f7c7683453ca406d82 "xhci: Add Intel U1/U2 timeout policy." Signed-off-by: Oliver Neukum Signed-off-by: Sarah Sharp CC: stable@vger.kernel.org --- drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7d462bf20092..8d3c454dad76 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4142,7 +4142,7 @@ static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev, (xhci_service_interval_to_ns(desc) > timeout_ns)) timeout_ns = xhci_service_interval_to_ns(desc); - u2_del_ns = udev->bos->ss_cap->bU2DevExitLat * 1000; + u2_del_ns = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat) * 1000ULL; if (u2_del_ns > timeout_ns) timeout_ns = u2_del_ns; -- cgit v1.2.3 From 16b45fdf9c4e82f5d3bc53aa70737650e7c8d5ed Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 17 Oct 2012 10:16:16 +0200 Subject: xhci: fix integer overflow xhci_service_interval_to_ns() returns long long to avoid an overflow. However, the type cast happens too late. The fix is to force ULL from the beginning. This patch should be backported to kernels as old as 3.5, that contain the commit e3567d2c15a7a8e2f992a5f7c7683453ca406d82 "xhci: Add Intel U1/U2 timeout policy." Signed-off-by: Oliver Neukum Signed-off-by: Sarah Sharp CC: stable@vger.kernel.org --- drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8d3c454dad76..9ec939646b42 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4021,7 +4021,7 @@ int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) static unsigned long long xhci_service_interval_to_ns( struct usb_endpoint_descriptor *desc) { - return (1 << (desc->bInterval - 1)) * 125 * 1000; + return (1ULL << (desc->bInterval - 1)) * 125 * 1000; } static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev, -- cgit v1.2.3 From 197ef5ef37d91686545872703bb6d756a53898ea Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 23 Oct 2012 20:29:15 -0400 Subject: USB: Add missing license tag to ezusb driver. ezusb: module license 'unspecified' taints kernel Signed-off-by: Dave Jones Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ezusb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c index 4223d761223d..6589268a6515 100644 --- a/drivers/usb/misc/ezusb.c +++ b/drivers/usb/misc/ezusb.c @@ -158,3 +158,4 @@ int ezusb_fx2_ihex_firmware_download(struct usb_device *dev, } EXPORT_SYMBOL_GPL(ezusb_fx2_ihex_firmware_download); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 036546bf950b7916dcc051eb0d450e23028b487a Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 23 Oct 2012 11:33:12 +0300 Subject: usb hub: use flush_work instead of flush_work_sync flush_work_sync and flush_work are now the same and flush_work_sync has been deprecated. This fixes the following warning: drivers/usb/core/hub.c: In function hub_quiesce: drivers/usb/core/hub.c:1216:3: warning: flush_work_sync is deprecated (declared at include/linux/workqueue.h:448) [-Wdeprecated-declarations] Reported-by: Fengguang Wu Signed-off-by: Octavian Purdila Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1181e917fec7..1af04bdeaf0c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1213,7 +1213,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); if (hub->tt.hub) - flush_work_sync(&hub->tt.clear_work); + flush_work(&hub->tt.clear_work); } /* caller has locked the hub device */ -- cgit v1.2.3 From f7bc5051667b74c3861f79eed98c60d5c3b883f7 Mon Sep 17 00:00:00 2001 From: Lennart Sorensen Date: Wed, 24 Oct 2012 10:23:09 -0400 Subject: USB: serial: Fix memory leak in sierra_release() I found a memory leak in sierra_release() (well sierra_probe() I guess) that looses 8 bytes each time the driver releases a device. Signed-off-by: Len Sorensen Acked-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/sierra.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 01d882cf3775..76ef95bcbbf2 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -959,6 +959,7 @@ static void sierra_release(struct usb_serial *serial) continue; kfree(portdata); } + kfree(serial->private); } #ifdef CONFIG_PM -- cgit v1.2.3 From d7870af7e2e3a91b462075ec1ca669b482215187 Mon Sep 17 00:00:00 2001 From: Michael Shigorin Date: Mon, 22 Oct 2012 12:18:56 +0300 Subject: usb-storage: add unusual_devs entry for Casio EX-N1 digital camera This commit sets removable subclass for Casio EX-N1 digital camera. The patch has been tested within an ALT Linux kernel: http://git.altlinux.org/people/led/packages/?p=kernel-image-3.0.git;a=commitdiff;h=c0fd891836e89fe0c93a4d536a59216d90e4e3e7 See also https://bugzilla.kernel.org/show_bug.cgi?id=49221 Signed-off-by: Oleksandr Chumachenko Signed-off-by: Michael Shigorin Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 779cd954abcb..d305a5aa3a5d 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1004,6 +1004,12 @@ UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9999, USB_SC_8070, USB_PR_CB, NULL, US_FL_NEED_OVERRIDE | US_FL_FIX_INQUIRY ), +/* Submitted by Oleksandr Chumachenko */ +UNUSUAL_DEV( 0x07cf, 0x1167, 0x0100, 0x0100, + "Casio", + "EX-N1 DigitalCamera", + USB_SC_8070, USB_PR_DEVICE, NULL, 0), + /* Submitted by Hartmut Wahl */ UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001, "Samsung", -- cgit v1.2.3 From 50dde8686eec41bf3d7cbec7a6f76c073ab01903 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:28:59 +0200 Subject: USB: metro-usb: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the call to metrousb_clean (close) in shutdown was redundant. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/metro-usb.c | 50 +++++++++++------------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 0b257ddffbdb..25cb97c25ad4 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -271,51 +271,27 @@ static int metrousb_set_modem_ctrl(struct usb_serial *serial, unsigned int contr return retval; } -static void metrousb_shutdown(struct usb_serial *serial) +static int metrousb_port_probe(struct usb_serial_port *port) { - int i = 0; + struct metrousb_private *metro_priv; - dev_dbg(&serial->dev->dev, "%s\n", __func__); + metro_priv = kzalloc(sizeof(*metro_priv), GFP_KERNEL); + if (!metro_priv) + return -ENOMEM; - /* Stop reading and writing on all ports. */ - for (i = 0; i < serial->num_ports; ++i) { - /* Close any open urbs. */ - metrousb_cleanup(serial->port[i]); + spin_lock_init(&metro_priv->lock); - /* Free memory. */ - kfree(usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); + usb_set_serial_port_data(port, metro_priv); - dev_dbg(&serial->dev->dev, "%s - freed port number=%d\n", - __func__, serial->port[i]->number); - } + return 0; } -static int metrousb_startup(struct usb_serial *serial) +static int metrousb_port_remove(struct usb_serial_port *port) { struct metrousb_private *metro_priv; - struct usb_serial_port *port; - int i = 0; - - dev_dbg(&serial->dev->dev, "%s\n", __func__); - /* Loop through the serial ports setting up the private structures. - * Currently we only use one port. */ - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - - /* Declare memory. */ - metro_priv = kzalloc(sizeof(struct metrousb_private), GFP_KERNEL); - if (!metro_priv) - return -ENOMEM; - - /* Initialize memory. */ - spin_lock_init(&metro_priv->lock); - usb_set_serial_port_data(port, metro_priv); - - dev_dbg(&serial->dev->dev, "%s - port number=%d\n ", - __func__, port->number); - } + metro_priv = usb_get_serial_port_data(port); + kfree(metro_priv); return 0; } @@ -414,8 +390,8 @@ static struct usb_serial_driver metrousb_device = { .close = metrousb_cleanup, .read_int_callback = metrousb_read_int_callback, .write_int_callback = metrousb_write_int_callback, - .attach = metrousb_startup, - .release = metrousb_shutdown, + .port_probe = metrousb_port_probe, + .port_remove = metrousb_port_remove, .throttle = metrousb_throttle, .unthrottle = metrousb_unthrottle, .tiocmget = metrousb_tiocmget, -- cgit v1.2.3 From 2ee44fbeac92c36e53779a57ee84cfee1affe418 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:00 +0200 Subject: USB: metro-usb: fix io after disconnect Make sure no control urb is submitted during close after a disconnect by checking the disconnected flag. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/metro-usb.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 25cb97c25ad4..6f29c74eb769 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -179,16 +179,13 @@ static void metrousb_cleanup(struct usb_serial_port *port) { dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->dev) { - /* Shutdown any interrupt in urbs. */ - if (port->interrupt_in_urb) { - usb_unlink_urb(port->interrupt_in_urb); - usb_kill_urb(port->interrupt_in_urb); - } - - /* Send deactivate cmd to device */ + usb_unlink_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); + + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port); - } + mutex_unlock(&port->serial->disc_mutex); } static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) -- cgit v1.2.3 From c129197c99550d356cf5f69b046994dd53cd1b9d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:01 +0200 Subject: USB: whiteheat: fix memory leak in error path Make sure command buffer is deallocated in case of errors during attach. Cc: Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/whiteheat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 346c7efc20b0..cfd155e2b6a2 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -333,6 +333,7 @@ no_firmware: "%s: please contact support@connecttech.com\n", serial->type->description); kfree(result); + kfree(command); return -ENODEV; no_command_private: -- cgit v1.2.3 From c467206ed6bcce26c83d0435612cc4fee2527305 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:02 +0200 Subject: USB: whiteheat: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the fifth port (command port) is never registered as a port device and thus should be handled in attach and release. Compile-only tested. Cc: Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/whiteheat.c | 59 +++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index cfd155e2b6a2..b9fca3586d74 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -83,6 +83,8 @@ static int whiteheat_firmware_attach(struct usb_serial *serial); /* function prototypes for the Connect Tech WhiteHEAT serial converter */ static int whiteheat_attach(struct usb_serial *serial); static void whiteheat_release(struct usb_serial *serial); +static int whiteheat_port_probe(struct usb_serial_port *port); +static int whiteheat_port_remove(struct usb_serial_port *port); static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port); static void whiteheat_close(struct usb_serial_port *port); @@ -117,6 +119,8 @@ static struct usb_serial_driver whiteheat_device = { .num_ports = 4, .attach = whiteheat_attach, .release = whiteheat_release, + .port_probe = whiteheat_port_probe, + .port_remove = whiteheat_port_remove, .open = whiteheat_open, .close = whiteheat_close, .ioctl = whiteheat_ioctl, @@ -218,15 +222,12 @@ static int whiteheat_attach(struct usb_serial *serial) { struct usb_serial_port *command_port; struct whiteheat_command_private *command_info; - struct usb_serial_port *port; - struct whiteheat_private *info; struct whiteheat_hw_info *hw_info; int pipe; int ret; int alen; __u8 *command; __u8 *result; - int i; command_port = serial->port[COMMAND_PORT]; @@ -285,22 +286,6 @@ static int whiteheat_attach(struct usb_serial *serial) serial->type->description, hw_info->sw_major_rev, hw_info->sw_minor_rev); - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - - info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL); - if (info == NULL) { - dev_err(&port->dev, - "%s: Out of memory for port structures\n", - serial->type->description); - goto no_private; - } - - info->mcr = 0; - - usb_set_serial_port_data(port, info); - } - command_info = kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL); if (command_info == NULL) { @@ -337,13 +322,6 @@ no_firmware: return -ENODEV; no_command_private: - for (i = serial->num_ports - 1; i >= 0; i--) { - port = serial->port[i]; - info = usb_get_serial_port_data(port); - kfree(info); -no_private: - ; - } kfree(result); no_result_buffer: kfree(command); @@ -351,21 +329,36 @@ no_command_buffer: return -ENOMEM; } - static void whiteheat_release(struct usb_serial *serial) { struct usb_serial_port *command_port; - struct whiteheat_private *info; - int i; /* free up our private data for our command port */ command_port = serial->port[COMMAND_PORT]; kfree(usb_get_serial_port_data(command_port)); +} - for (i = 0; i < serial->num_ports; i++) { - info = usb_get_serial_port_data(serial->port[i]); - kfree(info); - } +static int whiteheat_port_probe(struct usb_serial_port *port) +{ + struct whiteheat_private *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + usb_set_serial_port_data(port, info); + + return 0; +} + +static int whiteheat_port_remove(struct usb_serial_port *port) +{ + struct whiteheat_private *info; + + info = usb_get_serial_port_data(port); + kfree(info); + + return 0; } static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port) -- cgit v1.2.3 From 456c5be56ed070a4d883c60b587bcc1c97a8cf3e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:03 +0200 Subject: USB: ch341: fix port-data memory leak Fix port-data memory leak by moving port data allocation to port_probe and actually implementing deallocation. Note that this driver has never even bothered to try to deallocate it's port data... Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ch341.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index e9c7046ae355..d255f66e708e 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -242,13 +242,11 @@ out: kfree(buffer); return r; } -/* allocate private data */ -static int ch341_attach(struct usb_serial *serial) +static int ch341_port_probe(struct usb_serial_port *port) { struct ch341_private *priv; int r; - /* private data */ priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -258,17 +256,27 @@ static int ch341_attach(struct usb_serial *serial) priv->baud_rate = DEFAULT_BAUD_RATE; priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; - r = ch341_configure(serial->dev, priv); + r = ch341_configure(port->serial->dev, priv); if (r < 0) goto error; - usb_set_serial_port_data(serial->port[0], priv); + usb_set_serial_port_data(port, priv); return 0; error: kfree(priv); return r; } +static int ch341_port_remove(struct usb_serial_port *port) +{ + struct ch341_private *priv; + + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; +} + static int ch341_carrier_raised(struct usb_serial_port *port) { struct ch341_private *priv = usb_get_serial_port_data(port); @@ -304,7 +312,7 @@ static void ch341_close(struct usb_serial_port *port) static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]); + struct ch341_private *priv = usb_get_serial_port_data(port); int r; priv->baud_rate = DEFAULT_BAUD_RATE; @@ -608,7 +616,8 @@ static struct usb_serial_driver ch341_device = { .tiocmget = ch341_tiocmget, .tiocmset = ch341_tiocmset, .read_int_callback = ch341_read_int_callback, - .attach = ch341_attach, + .port_probe = ch341_port_probe, + .port_remove = ch341_port_remove, .reset_resume = ch341_reset_resume, }; -- cgit v1.2.3 From fb44ff854e148bc5c5982dad32da98b7a0989d2d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:04 +0200 Subject: USB: digi_acceleport: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the oob port is never registered as a port device and should thus be handled in attach and release. Compile-only tested. Cc: Peter Berger Cc: Al Borchers Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 117 ++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 50 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index c86f68c6b078..b50fa1c6d885 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -244,6 +244,8 @@ static int digi_startup_device(struct usb_serial *serial); static int digi_startup(struct usb_serial *serial); static void digi_disconnect(struct usb_serial *serial); static void digi_release(struct usb_serial *serial); +static int digi_port_probe(struct usb_serial_port *port); +static int digi_port_remove(struct usb_serial_port *port); static void digi_read_bulk_callback(struct urb *urb); static int digi_read_inb_callback(struct urb *urb); static int digi_read_oob_callback(struct urb *urb); @@ -294,6 +296,8 @@ static struct usb_serial_driver digi_acceleport_2_device = { .attach = digi_startup, .disconnect = digi_disconnect, .release = digi_release, + .port_probe = digi_port_probe, + .port_remove = digi_port_remove, }; static struct usb_serial_driver digi_acceleport_4_device = { @@ -320,6 +324,8 @@ static struct usb_serial_driver digi_acceleport_4_device = { .attach = digi_startup, .disconnect = digi_disconnect, .release = digi_release, + .port_probe = digi_port_probe, + .port_remove = digi_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { @@ -1240,59 +1246,50 @@ static int digi_startup_device(struct usb_serial *serial) return ret; } - -static int digi_startup(struct usb_serial *serial) +static int digi_port_init(struct usb_serial_port *port, unsigned port_num) { - - int i; struct digi_port *priv; - struct digi_serial *serial_priv; - /* allocate the private data structures for all ports */ - /* number of regular ports + 1 for the out-of-band port */ - for (i = 0; i < serial->type->num_ports + 1; i++) { - /* allocate port private structure */ - priv = kmalloc(sizeof(struct digi_port), GFP_KERNEL); - if (priv == NULL) { - while (--i >= 0) - kfree(usb_get_serial_port_data(serial->port[i])); - return 1; /* error */ - } + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - /* initialize port private structure */ - spin_lock_init(&priv->dp_port_lock); - priv->dp_port_num = i; - priv->dp_out_buf_len = 0; - priv->dp_write_urb_in_use = 0; - priv->dp_modem_signals = 0; - init_waitqueue_head(&priv->dp_modem_change_wait); - priv->dp_transmit_idle = 0; - init_waitqueue_head(&priv->dp_transmit_idle_wait); - priv->dp_throttled = 0; - priv->dp_throttle_restart = 0; - init_waitqueue_head(&priv->dp_flush_wait); - init_waitqueue_head(&priv->dp_close_wait); - INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock); - priv->dp_port = serial->port[i]; - /* initialize write wait queue for this port */ - init_waitqueue_head(&serial->port[i]->write_wait); - - usb_set_serial_port_data(serial->port[i], priv); - } + spin_lock_init(&priv->dp_port_lock); + priv->dp_port_num = port_num; + init_waitqueue_head(&priv->dp_modem_change_wait); + init_waitqueue_head(&priv->dp_transmit_idle_wait); + init_waitqueue_head(&priv->dp_flush_wait); + init_waitqueue_head(&priv->dp_close_wait); + INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock); + priv->dp_port = port; - /* allocate serial private structure */ - serial_priv = kmalloc(sizeof(struct digi_serial), GFP_KERNEL); - if (serial_priv == NULL) { - for (i = 0; i < serial->type->num_ports + 1; i++) - kfree(usb_get_serial_port_data(serial->port[i])); - return 1; /* error */ - } + init_waitqueue_head(&port->write_wait); + + usb_set_serial_port_data(port, priv); + + return 0; +} + +static int digi_startup(struct usb_serial *serial) +{ + struct digi_serial *serial_priv; + int ret; + + serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL); + if (!serial_priv) + return -ENOMEM; - /* initialize serial private structure */ spin_lock_init(&serial_priv->ds_serial_lock); serial_priv->ds_oob_port_num = serial->type->num_ports; serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num]; - serial_priv->ds_device_started = 0; + + ret = digi_port_init(serial_priv->ds_oob_port, + serial_priv->ds_oob_port_num); + if (ret) { + kfree(serial_priv); + return ret; + } + usb_set_serial_data(serial, serial_priv); return 0; @@ -1313,15 +1310,35 @@ static void digi_disconnect(struct usb_serial *serial) static void digi_release(struct usb_serial *serial) { - int i; + struct digi_serial *serial_priv; + struct digi_port *priv; + + serial_priv = usb_get_serial_data(serial); + + priv = usb_get_serial_port_data(serial_priv->ds_oob_port); + kfree(priv); - /* free the private data structures for all ports */ - /* number of regular ports + 1 for the out-of-band port */ - for (i = 0; i < serial->type->num_ports + 1; i++) - kfree(usb_get_serial_port_data(serial->port[i])); - kfree(usb_get_serial_data(serial)); + kfree(serial_priv); } +static int digi_port_probe(struct usb_serial_port *port) +{ + unsigned port_num; + + port_num = port->number - port->serial->minor; + + return digi_port_init(port, port_num); +} + +static int digi_port_remove(struct usb_serial_port *port) +{ + struct digi_port *priv; + + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; +} static void digi_read_bulk_callback(struct urb *urb) { -- cgit v1.2.3 From 4230af572f95b3115bba1ee6fb95681f3851ab26 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:05 +0200 Subject: USB: mos7720: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that this patch also fixes a second port-data memory leak in the error path of attach, should parallel-port initialisation fail. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7720.c | 62 +++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 1bf1ad066666..75267421aad8 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1966,9 +1966,7 @@ static int mos7720_ioctl(struct tty_struct *tty, static int mos7720_startup(struct usb_serial *serial) { - struct moschip_port *mos7720_port; struct usb_device *dev; - int i; char data; u16 product; int ret_val; @@ -1999,29 +1997,6 @@ static int mos7720_startup(struct usb_serial *serial) serial->port[1]->interrupt_in_buffer = NULL; } - - /* set up serial port private structures */ - for (i = 0; i < serial->num_ports; ++i) { - mos7720_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); - if (mos7720_port == NULL) { - dev_err(&dev->dev, "%s - Out of memory\n", __func__); - return -ENOMEM; - } - - /* Initialize all port interrupt end point to port 0 int - * endpoint. Our device has only one interrupt endpoint - * common to all ports */ - serial->port[i]->interrupt_in_endpointAddress = - serial->port[0]->interrupt_in_endpointAddress; - - mos7720_port->port = serial->port[i]; - usb_set_serial_port_data(serial->port[i], mos7720_port); - - dev_dbg(&dev->dev, "port number is %d\n", serial->port[i]->number); - dev_dbg(&dev->dev, "serial number is %d\n", serial->minor); - } - - /* setting configuration feature to one */ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5*HZ); @@ -2049,8 +2024,6 @@ static int mos7720_startup(struct usb_serial *serial) static void mos7720_release(struct usb_serial *serial) { - int i; - #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT /* close the parallel port */ @@ -2089,9 +2062,36 @@ static void mos7720_release(struct usb_serial *serial) kref_put(&mos_parport->ref_count, destroy_mos_parport); } #endif - /* free private structure allocated for serial port */ - for (i = 0; i < serial->num_ports; ++i) - kfree(usb_get_serial_port_data(serial->port[i])); +} + +static int mos7720_port_probe(struct usb_serial_port *port) +{ + struct moschip_port *mos7720_port; + + mos7720_port = kzalloc(sizeof(*mos7720_port), GFP_KERNEL); + if (!mos7720_port) + return -ENOMEM; + + /* Initialize all port interrupt end point to port 0 int endpoint. + * Our device has only one interrupt endpoint common to all ports. + */ + port->interrupt_in_endpointAddress = + port->serial->port[0]->interrupt_in_endpointAddress; + mos7720_port->port = port; + + usb_set_serial_port_data(port, mos7720_port); + + return 0; +} + +static int mos7720_port_remove(struct usb_serial_port *port) +{ + struct moschip_port *mos7720_port; + + mos7720_port = usb_get_serial_port_data(port); + kfree(mos7720_port); + + return 0; } static struct usb_serial_driver moschip7720_2port_driver = { @@ -2109,6 +2109,8 @@ static struct usb_serial_driver moschip7720_2port_driver = { .probe = mos77xx_probe, .attach = mos7720_startup, .release = mos7720_release, + .port_probe = mos7720_port_probe, + .port_remove = mos7720_port_remove, .ioctl = mos7720_ioctl, .tiocmget = mos7720_tiocmget, .tiocmset = mos7720_tiocmset, -- cgit v1.2.3 From feffa7ca6008ab859dd7ab7448a5a899bf0aa98f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:06 +0200 Subject: USB: omninet: fix port-data memory leak Fix port-data memory leak by replacing attach and release with port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/omninet.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 6def58b79382..9ab73d295774 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -44,8 +44,8 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int omninet_write_room(struct tty_struct *tty); static void omninet_disconnect(struct usb_serial *serial); -static void omninet_release(struct usb_serial *serial); -static int omninet_attach(struct usb_serial *serial); +static int omninet_port_probe(struct usb_serial_port *port); +static int omninet_port_remove(struct usb_serial_port *port); static const struct usb_device_id id_table[] = { { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) }, @@ -62,7 +62,8 @@ static struct usb_serial_driver zyxel_omninet_device = { .description = "ZyXEL - omni.net lcd plus usb", .id_table = id_table, .num_ports = 1, - .attach = omninet_attach, + .port_probe = omninet_port_probe, + .port_remove = omninet_port_remove, .open = omninet_open, .close = omninet_close, .write = omninet_write, @@ -70,7 +71,6 @@ static struct usb_serial_driver zyxel_omninet_device = { .read_bulk_callback = omninet_read_bulk_callback, .write_bulk_callback = omninet_write_bulk_callback, .disconnect = omninet_disconnect, - .release = omninet_release, }; static struct usb_serial_driver * const serial_drivers[] = { @@ -112,18 +112,26 @@ struct omninet_data { __u8 od_outseq; /* Sequence number for bulk_out URBs */ }; -static int omninet_attach(struct usb_serial *serial) +static int omninet_port_probe(struct usb_serial_port *port) { struct omninet_data *od; - struct usb_serial_port *port = serial->port[0]; od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL); - if (!od) { - dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", - __func__, sizeof(struct omninet_data)); + if (!od) return -ENOMEM; - } + usb_set_serial_port_data(port, od); + + return 0; +} + +static int omninet_port_remove(struct usb_serial_port *port) +{ + struct omninet_data *od; + + od = usb_get_serial_port_data(port); + kfree(od); + return 0; } @@ -279,14 +287,6 @@ static void omninet_disconnect(struct usb_serial *serial) usb_kill_urb(wport->write_urb); } - -static void omninet_release(struct usb_serial *serial) -{ - struct usb_serial_port *port = serial->port[0]; - - kfree(usb_get_serial_port_data(port)); -} - module_usb_serial_driver(serial_drivers, id_table); MODULE_AUTHOR(DRIVER_AUTHOR); -- cgit v1.2.3 From b8a0055050b6294826171641b182c09f78f4cc63 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:07 +0200 Subject: USB: quatech2: fix memory leak in error path Fix memory leak in attach error path where the read urb was never freed. Cc: Bill Pemberton Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 2cdfdcc90b37..5adb7428fbb1 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -823,6 +823,7 @@ static int qt2_setup_urbs(struct usb_serial *serial) if (status != 0) { dev_err(&serial->dev->dev, "%s - submit read urb failed %i\n", __func__, status); + usb_free_urb(serial_priv->read_urb); return status; } -- cgit v1.2.3 From 40d04738491d7ac1aa708ba434ff3480ec9e1b96 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:08 +0200 Subject: USB: quatech2: fix port-data memory leaks Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that this also fixes memory leaks in the error path of attach where the write urbs were not freed on errors. Make sure all interface-data deallocation is done in release by moving the read urb deallocation from disconnect. Note that the write urb is killed during close so that the call in disconnect was superfluous. Compile-only tested. Cc: Bill Pemberton Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 121 ++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 70 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 5adb7428fbb1..8d0c4a0b6a7b 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -143,12 +143,12 @@ static void qt2_read_bulk_callback(struct urb *urb); static void qt2_release(struct usb_serial *serial) { - int i; + struct qt2_serial_private *serial_priv; - kfree(usb_get_serial_data(serial)); + serial_priv = usb_get_serial_data(serial); - for (i = 0; i < serial->num_ports; i++) - kfree(usb_get_serial_port_data(serial->port[i])); + usb_free_urb(serial_priv->read_urb); + kfree(serial_priv); } static inline int calc_baud_divisor(int baudrate) @@ -464,21 +464,9 @@ static void qt2_close(struct usb_serial_port *port) static void qt2_disconnect(struct usb_serial *serial) { struct qt2_serial_private *serial_priv = usb_get_serial_data(serial); - struct qt2_port_private *port_priv; - int i; if (serial_priv->read_urb->status == -EINPROGRESS) usb_kill_urb(serial_priv->read_urb); - - usb_free_urb(serial_priv->read_urb); - - for (i = 0; i < serial->num_ports; i++) { - port_priv = usb_get_serial_port_data(serial->port[i]); - - if (port_priv->write_urb->status == -EINPROGRESS) - usb_kill_urb(port_priv->write_urb); - usb_free_urb(port_priv->write_urb); - } } static int get_serial_info(struct usb_serial_port *port, @@ -773,11 +761,9 @@ static void qt2_read_bulk_callback(struct urb *urb) static int qt2_setup_urbs(struct usb_serial *serial) { - struct usb_serial_port *port; struct usb_serial_port *port0; struct qt2_serial_private *serial_priv; - struct qt2_port_private *port_priv; - int pcount, status; + int status; port0 = serial->port[0]; @@ -795,30 +781,6 @@ static int qt2_setup_urbs(struct usb_serial *serial) sizeof(serial_priv->read_buffer), qt2_read_bulk_callback, serial); - /* setup write_urb for each port */ - for (pcount = 0; pcount < serial->num_ports; pcount++) { - - port = serial->port[pcount]; - port_priv = usb_get_serial_port_data(port); - - port_priv->write_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!port_priv->write_urb) { - dev_err(&serial->dev->dev, - "failed to alloc write_urb for port %i\n", - pcount); - return -ENOMEM; - } - - usb_fill_bulk_urb(port_priv->write_urb, - serial->dev, - usb_sndbulkpipe(serial->dev, - port0-> - bulk_out_endpointAddress), - port_priv->write_buffer, - sizeof(port_priv->write_buffer), - qt2_write_bulk_callback, port); - } - status = usb_submit_urb(serial_priv->read_urb, GFP_KERNEL); if (status != 0) { dev_err(&serial->dev->dev, @@ -828,14 +790,12 @@ static int qt2_setup_urbs(struct usb_serial *serial) } return 0; - } static int qt2_attach(struct usb_serial *serial) { struct qt2_serial_private *serial_priv; - struct qt2_port_private *port_priv; - int status, pcount; + int status; /* power on unit */ status = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), @@ -855,26 +815,6 @@ static int qt2_attach(struct usb_serial *serial) usb_set_serial_data(serial, serial_priv); - for (pcount = 0; pcount < serial->num_ports; pcount++) { - port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL); - if (!port_priv) { - dev_err(&serial->dev->dev, - "%s- kmalloc(%Zd) failed.\n", __func__, - sizeof(*port_priv)); - pcount--; - status = -ENOMEM; - goto attach_failed; - } - - spin_lock_init(&port_priv->lock); - spin_lock_init(&port_priv->urb_lock); - init_waitqueue_head(&port_priv->delta_msr_wait); - - port_priv->port = serial->port[pcount]; - - usb_set_serial_port_data(serial->port[pcount], port_priv); - } - status = qt2_setup_urbs(serial); if (status != 0) goto attach_failed; @@ -882,14 +822,53 @@ static int qt2_attach(struct usb_serial *serial) return 0; attach_failed: - for (/* empty */; pcount >= 0; pcount--) { - port_priv = usb_get_serial_port_data(serial->port[pcount]); - kfree(port_priv); - } kfree(serial_priv); return status; } +static int qt2_port_probe(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct qt2_port_private *port_priv; + u8 bEndpointAddress; + + port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL); + if (!port_priv) + return -ENOMEM; + + spin_lock_init(&port_priv->lock); + spin_lock_init(&port_priv->urb_lock); + init_waitqueue_head(&port_priv->delta_msr_wait); + port_priv->port = port; + + port_priv->write_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!port_priv->write_urb) { + kfree(port_priv); + return -ENOMEM; + } + bEndpointAddress = serial->port[0]->bulk_out_endpointAddress; + usb_fill_bulk_urb(port_priv->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, bEndpointAddress), + port_priv->write_buffer, + sizeof(port_priv->write_buffer), + qt2_write_bulk_callback, port); + + usb_set_serial_port_data(port, port_priv); + + return 0; +} + +static int qt2_port_remove(struct usb_serial_port *port) +{ + struct qt2_port_private *port_priv; + + port_priv = usb_get_serial_port_data(port); + usb_free_urb(port_priv->write_urb); + kfree(port_priv); + + return 0; +} + static int qt2_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; @@ -1128,6 +1107,8 @@ static struct usb_serial_driver qt2_device = { .attach = qt2_attach, .release = qt2_release, .disconnect = qt2_disconnect, + .port_probe = qt2_port_probe, + .port_remove = qt2_port_remove, .dtr_rts = qt2_dtr_rts, .break_ctl = qt2_break_ctl, .tiocmget = qt2_tiocmget, -- cgit v1.2.3 From 8e512ab0b675da20e023439a5811e3f2554e6852 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:09 +0200 Subject: USB: quatech2: fix close and disconnect urb handling Kill urbs unconditionally at close and disconnect. Note that URB status is not valid outside of completion handler. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 8d0c4a0b6a7b..7e8d8f32b634 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -423,8 +423,7 @@ static void qt2_close(struct usb_serial_port *port) port_priv->is_open = false; spin_lock_irqsave(&port_priv->urb_lock, flags); - if (port_priv->write_urb->status == -EINPROGRESS) - usb_kill_urb(port_priv->write_urb); + usb_kill_urb(port_priv->write_urb); port_priv->urb_in_use = false; spin_unlock_irqrestore(&port_priv->urb_lock, flags); @@ -465,8 +464,7 @@ static void qt2_disconnect(struct usb_serial *serial) { struct qt2_serial_private *serial_priv = usb_get_serial_data(serial); - if (serial_priv->read_urb->status == -EINPROGRESS) - usb_kill_urb(serial_priv->read_urb); + usb_kill_urb(serial_priv->read_urb); } static int get_serial_info(struct usb_serial_port *port, -- cgit v1.2.3 From 2f0295adf6438188c4cd0868f2b1976a2b034e1d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:10 +0200 Subject: USB: quatech2: fix io after disconnect Make sure no control urb is submitted during close after a disconnect by checking the disconnected flag. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 7e8d8f32b634..ffcfc962ab10 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -427,6 +427,12 @@ static void qt2_close(struct usb_serial_port *port) port_priv->urb_in_use = false; spin_unlock_irqrestore(&port_priv->urb_lock, flags); + mutex_lock(&port->serial->disc_mutex); + if (port->serial->disconnected) { + mutex_unlock(&port->serial->disc_mutex); + return; + } + /* flush the port transmit buffer */ i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), @@ -458,6 +464,7 @@ static void qt2_close(struct usb_serial_port *port) dev_err(&port->dev, "%s - close port failed %i\n", __func__, i); + mutex_unlock(&port->serial->disc_mutex); } static void qt2_disconnect(struct usb_serial *serial) -- cgit v1.2.3 From ea0dbebffe118724cd4df7d9b071ea8ee48d48f0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:11 +0200 Subject: USB: opticon: fix DMA from stack Make sure to allocate the control-message buffer dynamically as some platforms cannot do DMA from stack. Note that only the first byte of the old buffer was used. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/opticon.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 41b1647306eb..459c28889fe4 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -155,7 +155,11 @@ static int send_control_msg(struct usb_serial_port *port, u8 requesttype, { struct usb_serial *serial = port->serial; int retval; - u8 buffer[2]; + u8 *buffer; + + buffer = kzalloc(1, GFP_KERNEL); + if (!buffer) + return -ENOMEM; buffer[0] = val; /* Send the message to the vendor control endpoint @@ -164,6 +168,7 @@ static int send_control_msg(struct usb_serial_port *port, u8 requesttype, requesttype, USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, 0, 0, buffer, 1, 0); + kfree(buffer); return retval; } -- cgit v1.2.3 From acbf0e5263de563e25f7c104868e4490b9e72b13 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:12 +0200 Subject: USB: opticon: fix memory leak in error path Fix memory leak in write error path. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/opticon.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 459c28889fe4..6aba731d4864 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -286,7 +286,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, if (!dr) { dev_err(&port->dev, "out of memory\n"); count = -ENOMEM; - goto error; + goto error_no_dr; } dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT; @@ -316,6 +316,8 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, return count; error: + kfree(dr); +error_no_dr: usb_free_urb(urb); error_no_urb: kfree(buffer); -- cgit v1.2.3 From a8f2ae7a3aa59079d7e7e1ddf5007f03532f458c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:13 +0200 Subject: USB: mct_u232: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the write waitqueue was initialised but never used. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mct_u232.c | 45 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index f3947712e137..a8bce134ffba 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -49,7 +49,8 @@ * Function prototypes */ static int mct_u232_startup(struct usb_serial *serial); -static void mct_u232_release(struct usb_serial *serial); +static int mct_u232_port_probe(struct usb_serial_port *port); +static int mct_u232_port_remove(struct usb_serial_port *remove); static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port); static void mct_u232_close(struct usb_serial_port *port); static void mct_u232_dtr_rts(struct usb_serial_port *port, int on); @@ -99,7 +100,8 @@ static struct usb_serial_driver mct_u232_device = { .tiocmget = mct_u232_tiocmget, .tiocmset = mct_u232_tiocmset, .attach = mct_u232_startup, - .release = mct_u232_release, + .port_probe = mct_u232_port_probe, + .port_remove = mct_u232_port_remove, .ioctl = mct_u232_ioctl, .get_icount = mct_u232_get_icount, }; @@ -388,18 +390,8 @@ static void mct_u232_msr_to_state(struct usb_serial_port *port, static int mct_u232_startup(struct usb_serial *serial) { - struct mct_u232_private *priv; struct usb_serial_port *port, *rport; - priv = kzalloc(sizeof(struct mct_u232_private), GFP_KERNEL); - if (!priv) - return -ENOMEM; - spin_lock_init(&priv->lock); - init_waitqueue_head(&priv->msr_wait); - usb_set_serial_port_data(serial->port[0], priv); - - init_waitqueue_head(&serial->port[0]->write_wait); - /* Puh, that's dirty */ port = serial->port[0]; rport = serial->port[1]; @@ -412,18 +404,31 @@ static int mct_u232_startup(struct usb_serial *serial) return 0; } /* mct_u232_startup */ +static int mct_u232_port_probe(struct usb_serial_port *port) +{ + struct mct_u232_private *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; -static void mct_u232_release(struct usb_serial *serial) + spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->msr_wait); + + usb_set_serial_port_data(port, priv); + + return 0; +} + +static int mct_u232_port_remove(struct usb_serial_port *port) { struct mct_u232_private *priv; - int i; - for (i = 0; i < serial->num_ports; ++i) { - /* My special items, the standard routines free my urbs */ - priv = usb_get_serial_port_data(serial->port[i]); - kfree(priv); - } -} /* mct_u232_release */ + priv = usb_get_serial_port_data(port); + kfree(priv); + + return 0; +} static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) { -- cgit v1.2.3 From 5260e458f5eff269a43e4f1e9c47186c57b88ddb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:14 +0200 Subject: USB: mct_u232: fix broken close Make sure generic close is called at close. The driver relies on the generic write implementation but did not call generic close. Note that the call to kill the read urb is not redundant, as mct_u232 uses an interrupt urb from the second port as the read urb and that generic close therefore fails to kill it. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mct_u232.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index a8bce134ffba..8a2081004107 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -520,12 +520,14 @@ static void mct_u232_dtr_rts(struct usb_serial_port *port, int on) static void mct_u232_close(struct usb_serial_port *port) { - if (port->serial->dev) { - /* shutdown our urbs */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); - usb_kill_urb(port->interrupt_in_urb); - } + /* + * Must kill the read urb as it is actually an interrupt urb, which + * generic close thus fails to kill. + */ + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); + + usb_serial_generic_close(port); } /* mct_u232_close */ -- cgit v1.2.3 From f79b2d0fe81eecb412dc48e87a119afc690da8e9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:15 +0200 Subject: USB: keyspan: fix NULL-pointer dereferences and memory leaks Fix NULL-pointer dereference at release by moving port data allocation and deallocation to port_probe and port_remove. Fix NULL-pointer dereference at disconnect by stopping port urbs at port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer accessible at disconnect or release. Note that this patch also fixes port and interface-data memory leaks in the error path of attach should port initialisation fail for any port. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 181 +++++++++++++++++++++---------------------- drivers/usb/serial/keyspan.h | 8 ++ 2 files changed, 96 insertions(+), 93 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 29c943d737d0..7179b0c5f814 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1374,13 +1374,9 @@ static struct callbacks { data in device_details */ static void keyspan_setup_urbs(struct usb_serial *serial) { - int i, j; struct keyspan_serial_private *s_priv; const struct keyspan_device_details *d_details; - struct usb_serial_port *port; - struct keyspan_port_private *p_priv; struct callbacks *cback; - int endp; s_priv = usb_get_serial_data(serial); d_details = s_priv->device_details; @@ -1404,45 +1400,6 @@ static void keyspan_setup_urbs(struct usb_serial *serial) (serial, d_details->glocont_endpoint, USB_DIR_OUT, serial, s_priv->glocont_buf, GLOCONT_BUFLEN, cback->glocont_callback); - - /* Setup endpoints for each port specific thing */ - for (i = 0; i < d_details->num_ports; i++) { - port = serial->port[i]; - p_priv = usb_get_serial_port_data(port); - - /* Do indat endpoints first, once for each flip */ - endp = d_details->indat_endpoints[i]; - for (j = 0; j <= d_details->indat_endp_flip; ++j, ++endp) { - p_priv->in_urbs[j] = keyspan_setup_urb - (serial, endp, USB_DIR_IN, port, - p_priv->in_buffer[j], 64, - cback->indat_callback); - } - for (; j < 2; ++j) - p_priv->in_urbs[j] = NULL; - - /* outdat endpoints also have flip */ - endp = d_details->outdat_endpoints[i]; - for (j = 0; j <= d_details->outdat_endp_flip; ++j, ++endp) { - p_priv->out_urbs[j] = keyspan_setup_urb - (serial, endp, USB_DIR_OUT, port, - p_priv->out_buffer[j], 64, - cback->outdat_callback); - } - for (; j < 2; ++j) - p_priv->out_urbs[j] = NULL; - - /* inack endpoint */ - p_priv->inack_urb = keyspan_setup_urb - (serial, d_details->inack_endpoints[i], USB_DIR_IN, - port, p_priv->inack_buffer, 1, cback->inack_callback); - - /* outcont endpoint */ - p_priv->outcont_urb = keyspan_setup_urb - (serial, d_details->outcont_endpoints[i], USB_DIR_OUT, - port, p_priv->outcont_buffer, 64, - cback->outcont_callback); - } } /* usa19 function doesn't require prescaler */ @@ -2407,9 +2364,7 @@ static void keyspan_send_setup(struct usb_serial_port *port, int reset_port) static int keyspan_startup(struct usb_serial *serial) { int i, err; - struct usb_serial_port *port; struct keyspan_serial_private *s_priv; - struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i) @@ -2432,19 +2387,6 @@ static int keyspan_startup(struct usb_serial *serial) s_priv->device_details = d_details; usb_set_serial_data(serial, s_priv); - /* Now setup per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - p_priv = kzalloc(sizeof(struct keyspan_port_private), - GFP_KERNEL); - if (!p_priv) { - dev_dbg(&port->dev, "%s - kmalloc for keyspan_port_private (%d) failed!.\n", __func__, i); - return 1; - } - p_priv->device_details = d_details; - usb_set_serial_port_data(port, p_priv); - } - keyspan_setup_urbs(serial); if (s_priv->instat_urb != NULL) { @@ -2463,59 +2405,112 @@ static int keyspan_startup(struct usb_serial *serial) static void keyspan_disconnect(struct usb_serial *serial) { - int i, j; - struct usb_serial_port *port; - struct keyspan_serial_private *s_priv; - struct keyspan_port_private *p_priv; + struct keyspan_serial_private *s_priv; s_priv = usb_get_serial_data(serial); - /* Stop reading/writing urbs */ stop_urb(s_priv->instat_urb); stop_urb(s_priv->glocont_urb); stop_urb(s_priv->indat_urb); - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - p_priv = usb_get_serial_port_data(port); - stop_urb(p_priv->inack_urb); - stop_urb(p_priv->outcont_urb); - for (j = 0; j < 2; j++) { - stop_urb(p_priv->in_urbs[j]); - stop_urb(p_priv->out_urbs[j]); - } - } +} + +static void keyspan_release(struct usb_serial *serial) +{ + struct keyspan_serial_private *s_priv; + + s_priv = usb_get_serial_data(serial); - /* Now free them */ usb_free_urb(s_priv->instat_urb); usb_free_urb(s_priv->indat_urb); usb_free_urb(s_priv->glocont_urb); - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - p_priv = usb_get_serial_port_data(port); - usb_free_urb(p_priv->inack_urb); - usb_free_urb(p_priv->outcont_urb); - for (j = 0; j < 2; j++) { - usb_free_urb(p_priv->in_urbs[j]); - usb_free_urb(p_priv->out_urbs[j]); - } - } + + kfree(s_priv); } -static void keyspan_release(struct usb_serial *serial) +static int keyspan_port_probe(struct usb_serial_port *port) { - int i; - struct usb_serial_port *port; - struct keyspan_serial_private *s_priv; + struct usb_serial *serial = port->serial; + struct keyspan_port_private *s_priv; + struct keyspan_port_private *p_priv; + const struct keyspan_device_details *d_details; + struct callbacks *cback; + int endp; + int port_num; + int i; s_priv = usb_get_serial_data(serial); + d_details = s_priv->device_details; - kfree(s_priv); + p_priv = kzalloc(sizeof(*p_priv), GFP_KERNEL); + if (!p_priv) + return -ENOMEM; - /* Now free per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - kfree(usb_get_serial_port_data(port)); + s_priv = usb_get_serial_data(port->serial); + p_priv->device_details = d_details; + + /* Setup values for the various callback routines */ + cback = &keyspan_callbacks[d_details->msg_format]; + + port_num = port->number - port->serial->minor; + + /* Do indat endpoints first, once for each flip */ + endp = d_details->indat_endpoints[port_num]; + for (i = 0; i <= d_details->indat_endp_flip; ++i, ++endp) { + p_priv->in_urbs[i] = keyspan_setup_urb(serial, endp, + USB_DIR_IN, port, + p_priv->in_buffer[i], 64, + cback->indat_callback); + } + /* outdat endpoints also have flip */ + endp = d_details->outdat_endpoints[port_num]; + for (i = 0; i <= d_details->outdat_endp_flip; ++i, ++endp) { + p_priv->out_urbs[i] = keyspan_setup_urb(serial, endp, + USB_DIR_OUT, port, + p_priv->out_buffer[i], 64, + cback->outdat_callback); + } + /* inack endpoint */ + p_priv->inack_urb = keyspan_setup_urb(serial, + d_details->inack_endpoints[port_num], + USB_DIR_IN, port, + p_priv->inack_buffer, 1, + cback->inack_callback); + /* outcont endpoint */ + p_priv->outcont_urb = keyspan_setup_urb(serial, + d_details->outcont_endpoints[port_num], + USB_DIR_OUT, port, + p_priv->outcont_buffer, 64, + cback->outcont_callback); + + usb_set_serial_port_data(port, p_priv); + + return 0; +} + +static int keyspan_port_remove(struct usb_serial_port *port) +{ + struct keyspan_port_private *p_priv; + int i; + + p_priv = usb_get_serial_port_data(port); + + stop_urb(p_priv->inack_urb); + stop_urb(p_priv->outcont_urb); + for (i = 0; i < 2; i++) { + stop_urb(p_priv->in_urbs[i]); + stop_urb(p_priv->out_urbs[i]); + } + + usb_free_urb(p_priv->inack_urb); + usb_free_urb(p_priv->outcont_urb); + for (i = 0; i < 2; i++) { + usb_free_urb(p_priv->in_urbs[i]); + usb_free_urb(p_priv->out_urbs[i]); } + + kfree(p_priv); + + return 0; } MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 0a8a40b5711e..0273dda303a4 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -42,6 +42,8 @@ static void keyspan_dtr_rts (struct usb_serial_port *port, int on); static int keyspan_startup (struct usb_serial *serial); static void keyspan_disconnect (struct usb_serial *serial); static void keyspan_release (struct usb_serial *serial); +static int keyspan_port_probe(struct usb_serial_port *port); +static int keyspan_port_remove(struct usb_serial_port *port); static int keyspan_write_room (struct tty_struct *tty); static int keyspan_write (struct tty_struct *tty, @@ -567,6 +569,8 @@ static struct usb_serial_driver keyspan_1port_device = { .attach = keyspan_startup, .disconnect = keyspan_disconnect, .release = keyspan_release, + .port_probe = keyspan_port_probe, + .port_remove = keyspan_port_remove, }; static struct usb_serial_driver keyspan_2port_device = { @@ -589,6 +593,8 @@ static struct usb_serial_driver keyspan_2port_device = { .attach = keyspan_startup, .disconnect = keyspan_disconnect, .release = keyspan_release, + .port_probe = keyspan_port_probe, + .port_remove = keyspan_port_remove, }; static struct usb_serial_driver keyspan_4port_device = { @@ -611,6 +617,8 @@ static struct usb_serial_driver keyspan_4port_device = { .attach = keyspan_startup, .disconnect = keyspan_disconnect, .release = keyspan_release, + .port_probe = keyspan_port_probe, + .port_remove = keyspan_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit v1.2.3 From b8f0e82044c9ba40e92340c8a6d47d6bd6d819bc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:16 +0200 Subject: USB: usb-wwan: fix multiple memory leaks in error paths Fix port-data memory leak in usb-serial probe error path by moving port data allocation to port_probe. Since commit a1028f0abf ("usb: usb_wwan: replace release and disconnect with a port_remove hook") port data is deallocated in port_remove. This leaves a possibility for memory leaks if usb-serial probe fails after attach but before the port in question has been successfully registered. Note that this patch also fixes two additional memory leaks in the error path of attach should port initialisation fail for any port as the urbs were never freed and neither was the data of any of the successfully initialised ports. Compile-only tested. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ipw.c | 2 +- drivers/usb/serial/option.c | 2 +- drivers/usb/serial/qcserial.c | 2 +- drivers/usb/serial/usb-wwan.h | 2 +- drivers/usb/serial/usb_wwan.c | 124 +++++++++++++++++------------------------- 5 files changed, 54 insertions(+), 78 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 20a132ec39e2..add45b7d8aa7 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -304,8 +304,8 @@ static struct usb_serial_driver ipw_device = { .open = ipw_open, .close = ipw_close, .probe = ipw_probe, - .attach = usb_wwan_startup, .release = ipw_release, + .port_probe = usb_wwan_port_probe, .port_remove = usb_wwan_port_remove, .dtr_rts = ipw_dtr_rts, .write = usb_wwan_write, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 54d4148d01d1..eb4bdd4a0106 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1288,8 +1288,8 @@ static struct usb_serial_driver option_1port_device = { .tiocmget = usb_wwan_tiocmget, .tiocmset = usb_wwan_tiocmset, .ioctl = usb_wwan_ioctl, - .attach = usb_wwan_startup, .release = option_release, + .port_probe = usb_wwan_port_probe, .port_remove = usb_wwan_port_remove, .read_int_callback = option_instat_callback, #ifdef CONFIG_PM diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index c3ddb65c05f2..8dd2280dff63 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -285,8 +285,8 @@ static struct usb_serial_driver qcdevice = { .write = usb_wwan_write, .write_room = usb_wwan_write_room, .chars_in_buffer = usb_wwan_chars_in_buffer, - .attach = usb_wwan_startup, .release = qc_release, + .port_probe = usb_wwan_port_probe, .port_remove = usb_wwan_port_remove, #ifdef CONFIG_PM .suspend = usb_wwan_suspend, diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index 1f034d2397c6..684739b8efd0 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h @@ -8,7 +8,7 @@ extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on); extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port); extern void usb_wwan_close(struct usb_serial_port *port); -extern int usb_wwan_startup(struct usb_serial *serial); +extern int usb_wwan_port_probe(struct usb_serial_port *port); extern int usb_wwan_port_remove(struct usb_serial_port *port); extern int usb_wwan_write_room(struct tty_struct *tty); extern void usb_wwan_set_termios(struct tty_struct *tty, diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index e42aa398ed37..61a73ad1a187 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -447,10 +447,12 @@ void usb_wwan_close(struct usb_serial_port *port) EXPORT_SYMBOL(usb_wwan_close); /* Helper functions used by usb_wwan_setup_urbs */ -static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint, +static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, + int endpoint, int dir, void *ctx, char *buf, int len, void (*callback) (struct urb *)) { + struct usb_serial *serial = port->serial; struct urb *urb; if (endpoint == -1) @@ -472,101 +474,75 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint, return urb; } -/* Setup urbs */ -static void usb_wwan_setup_urbs(struct usb_serial *serial) +int usb_wwan_port_probe(struct usb_serial_port *port) { - int i, j; - struct usb_serial_port *port; struct usb_wwan_port_private *portdata; + struct urb *urb; + u8 *buffer; + int err; + int i; - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - portdata = usb_get_serial_port_data(port); + portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); + if (!portdata) + return -ENOMEM; - /* Do indat endpoints first */ - for (j = 0; j < N_IN_URB; ++j) { - portdata->in_urbs[j] = usb_wwan_setup_urb(serial, - port-> - bulk_in_endpointAddress, - USB_DIR_IN, - port, - portdata-> - in_buffer[j], - IN_BUFLEN, - usb_wwan_indat_callback); - } + init_usb_anchor(&portdata->delayed); - /* outdat endpoints */ - for (j = 0; j < N_OUT_URB; ++j) { - portdata->out_urbs[j] = usb_wwan_setup_urb(serial, - port-> - bulk_out_endpointAddress, - USB_DIR_OUT, - port, - portdata-> - out_buffer - [j], - OUT_BUFLEN, - usb_wwan_outdat_callback); - } + for (i = 0; i < N_IN_URB; i++) { + buffer = (u8 *)__get_free_page(GFP_KERNEL); + if (!buffer) + goto bail_out_error; + portdata->in_buffer[i] = buffer; + + urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress, + USB_DIR_IN, port, + buffer, IN_BUFLEN, + usb_wwan_indat_callback); + portdata->in_urbs[i] = urb; } -} - -int usb_wwan_startup(struct usb_serial *serial) -{ - int i, j, err; - struct usb_serial_port *port; - struct usb_wwan_port_private *portdata; - u8 *buffer; - /* Now setup per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); - if (!portdata) { - dev_dbg(&port->dev, "%s: kmalloc for usb_wwan_port_private (%d) failed!.\n", - __func__, i); - return 1; - } - init_usb_anchor(&portdata->delayed); + for (i = 0; i < N_OUT_URB; i++) { + if (port->bulk_out_endpointAddress == -1) + continue; - for (j = 0; j < N_IN_URB; j++) { - buffer = (u8 *) __get_free_page(GFP_KERNEL); - if (!buffer) - goto bail_out_error; - portdata->in_buffer[j] = buffer; - } + buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); + if (!buffer) + goto bail_out_error2; + portdata->out_buffer[i] = buffer; - for (j = 0; j < N_OUT_URB; j++) { - buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); - if (!buffer) - goto bail_out_error2; - portdata->out_buffer[j] = buffer; - } + urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress, + USB_DIR_OUT, port, + buffer, OUT_BUFLEN, + usb_wwan_outdat_callback); + portdata->out_urbs[i] = urb; + } - usb_set_serial_port_data(port, portdata); + usb_set_serial_port_data(port, portdata); - if (!port->interrupt_in_urb) - continue; + if (port->interrupt_in_urb) { err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (err) dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n", __func__, err); } - usb_wwan_setup_urbs(serial); + return 0; bail_out_error2: - for (j = 0; j < N_OUT_URB; j++) - kfree(portdata->out_buffer[j]); + for (i = 0; i < N_OUT_URB; i++) { + usb_free_urb(portdata->out_urbs[i]); + kfree(portdata->out_buffer[i]); + } bail_out_error: - for (j = 0; j < N_IN_URB; j++) - if (portdata->in_buffer[j]) - free_page((unsigned long)portdata->in_buffer[j]); + for (i = 0; i < N_IN_URB; i++) { + usb_free_urb(portdata->in_urbs[i]); + free_page((unsigned long)portdata->in_buffer[i]); + } kfree(portdata); - return 1; + + return -ENOMEM; } -EXPORT_SYMBOL(usb_wwan_startup); +EXPORT_SYMBOL_GPL(usb_wwan_port_probe); int usb_wwan_port_remove(struct usb_serial_port *port) { -- cgit v1.2.3 From 7e41f9bcdd2e813ea2a3c40db291d87ea06b559f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:17 +0200 Subject: USB: sierra: fix memory leak in attach error path Make sure port private data is deallocated on errors in attach. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/sierra.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 76ef95bcbbf2..2cb27e49d4ac 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -905,7 +905,7 @@ static int sierra_startup(struct usb_serial *serial) dev_dbg(&port->dev, "%s: kmalloc for " "sierra_port_private (%d) failed!\n", __func__, i); - return -ENOMEM; + goto err; } spin_lock_init(&portdata->lock); init_usb_anchor(&portdata->active); @@ -942,6 +942,13 @@ static int sierra_startup(struct usb_serial *serial) } return 0; +err: + for (--i; i >= 0; --i) { + portdata = usb_get_serial_port_data(serial->port[i]); + kfree(portdata); + } + + return -ENOMEM; } static void sierra_release(struct usb_serial *serial) -- cgit v1.2.3 From 084817d79399ab5ccab2f90a148b0369912a8369 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:18 +0200 Subject: USB: sierra: fix memory leak in probe error path Move interface data allocation to attach so that it is deallocated on errors in usb-serial probe. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/sierra.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 2cb27e49d4ac..bb2ecaf3296b 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -161,7 +161,6 @@ static int sierra_probe(struct usb_serial *serial, { int result = 0; struct usb_device *udev; - struct sierra_intf_private *data; u8 ifnum; udev = serial->dev; @@ -188,11 +187,6 @@ static int sierra_probe(struct usb_serial *serial, return -ENODEV; } - data = serial->private = kzalloc(sizeof(struct sierra_intf_private), GFP_KERNEL); - if (!data) - return -ENOMEM; - spin_lock_init(&data->susp_lock); - return result; } @@ -885,11 +879,20 @@ static void sierra_dtr_rts(struct usb_serial_port *port, int on) static int sierra_startup(struct usb_serial *serial) { struct usb_serial_port *port; + struct sierra_intf_private *intfdata; struct sierra_port_private *portdata; struct sierra_iface_info *himemoryp = NULL; int i; u8 ifnum; + intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL); + if (!intfdata) + return -ENOMEM; + + spin_lock_init(&intfdata->susp_lock); + + usb_set_serial_data(serial, intfdata); + /* Set Device mode to D0 */ sierra_set_power_state(serial->dev, 0x0000); @@ -947,6 +950,7 @@ err: portdata = usb_get_serial_port_data(serial->port[i]); kfree(portdata); } + kfree(intfdata); return -ENOMEM; } -- cgit v1.2.3 From f525c05babc7938cc1d4236550fd8a659fb05960 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 10:29:19 +0200 Subject: USB: sierra: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note also that urb-count for multi-port interfaces has not been changed even though the usb-serial port number is now determined from the port and interface minor numbers. Compile-only tested. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/sierra.c | 125 ++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 67 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index bb2ecaf3296b..270860f6bb2a 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -878,12 +878,7 @@ static void sierra_dtr_rts(struct usb_serial_port *port, int on) static int sierra_startup(struct usb_serial *serial) { - struct usb_serial_port *port; struct sierra_intf_private *intfdata; - struct sierra_port_private *portdata; - struct sierra_iface_info *himemoryp = NULL; - int i; - u8 ifnum; intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL); if (!intfdata) @@ -900,77 +895,71 @@ static int sierra_startup(struct usb_serial *serial) if (nmea) sierra_vsc_set_nmea(serial->dev, 1); - /* Now setup per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); - if (!portdata) { - dev_dbg(&port->dev, "%s: kmalloc for " - "sierra_port_private (%d) failed!\n", - __func__, i); - goto err; - } - 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); + return 0; +} + +static void sierra_release(struct usb_serial *serial) +{ + struct sierra_intf_private *intfdata; + + intfdata = usb_get_serial_data(serial); + kfree(intfdata); +} + +static int sierra_port_probe(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct sierra_port_private *portdata; + const struct sierra_iface_info *himemoryp; + u8 ifnum; + + portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); + if (!portdata) + return -ENOMEM; + + spin_lock_init(&portdata->lock); + init_usb_anchor(&portdata->active); + init_usb_anchor(&portdata->delayed); + + /* 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 = &typeB_interface_list; + } else { + /* This is really the usb-serial port number of the interface + * rather than the interface number. + */ + ifnum = port->number - serial->minor; + himemoryp = &typeA_interface_list; } - return 0; -err: - for (--i; i >= 0; --i) { - portdata = usb_get_serial_port_data(serial->port[i]); - kfree(portdata); + if (is_himemory(ifnum, himemoryp)) { + portdata->num_out_urbs = N_OUT_URB_HM; + portdata->num_in_urbs = N_IN_URB_HM; } - kfree(intfdata); - return -ENOMEM; + dev_dbg(&port->dev, + "Memory usage (urbs) interface #%d, in=%d, out=%d\n", + ifnum, portdata->num_in_urbs, portdata->num_out_urbs); + + usb_set_serial_port_data(port, portdata); + + return 0; } -static void sierra_release(struct usb_serial *serial) +static int sierra_port_remove(struct usb_serial_port *port) { - int i; - struct usb_serial_port *port; struct sierra_port_private *portdata; - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - if (!port) - continue; - portdata = usb_get_serial_port_data(port); - if (!portdata) - continue; - kfree(portdata); - } - kfree(serial->private); + portdata = usb_get_serial_port_data(port); + kfree(portdata); + + return 0; } #ifdef CONFIG_PM @@ -1074,6 +1063,8 @@ static struct usb_serial_driver sierra_device = { .tiocmset = sierra_tiocmset, .attach = sierra_startup, .release = sierra_release, + .port_probe = sierra_port_probe, + .port_remove = sierra_port_remove, .suspend = sierra_suspend, .resume = sierra_resume, .read_int_callback = sierra_instat_callback, -- cgit v1.2.3 From 65a4cdbb170e4ec1a7fa0e94936d47e24a17b0e8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 13:35:09 +0200 Subject: USB: mos7840: fix urb leak at release Make sure control urb is freed at release. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7840.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index d6d4eeca8c68..35ad3f5c4c0e 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2645,6 +2645,7 @@ static void mos7840_release(struct usb_serial *serial) del_timer_sync(&mos7840_port->led_timer1); del_timer_sync(&mos7840_port->led_timer2); } + usb_free_urb(mos7840_port->control_urb); kfree(mos7840_port->ctrl_buf); kfree(mos7840_port->dr); kfree(mos7840_port); -- cgit v1.2.3 From 3eb55cc4ed88eee3b5230f66abcdbd2a91639eda Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 13:35:10 +0200 Subject: USB: mos7840: fix port-device leak in error path The driver set the usb-serial port pointers to NULL on errors in attach, effectively preventing usb-serial core from decrementing the port ref counters and releasing the port devices and associated data. Cc: Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7840.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 35ad3f5c4c0e..c5e5a6c8d69e 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2587,7 +2587,6 @@ error: kfree(mos7840_port->ctrl_buf); usb_free_urb(mos7840_port->control_urb); kfree(mos7840_port); - serial->port[i] = NULL; } return status; } -- cgit v1.2.3 From a997448c89905b80aa4022f734f03685e733d711 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 15:42:39 +0200 Subject: USB: ipw: fix interface-data memory leak in error path Move interface data allocation to attach so that it is deallocated should usb-serial probe fail. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ipw.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index add45b7d8aa7..4264821a3b34 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -203,8 +203,7 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) return 0; } -/* fake probe - only to allocate data structures */ -static int ipw_probe(struct usb_serial *serial, const struct usb_device_id *id) +static int ipw_attach(struct usb_serial *serial) { struct usb_wwan_intf_private *data; @@ -303,7 +302,7 @@ static struct usb_serial_driver ipw_device = { .num_ports = 1, .open = ipw_open, .close = ipw_close, - .probe = ipw_probe, + .attach = ipw_attach, .release = ipw_release, .port_probe = usb_wwan_port_probe, .port_remove = usb_wwan_port_remove, -- cgit v1.2.3 From c2dd4a8eac7821fed2c2d19e4607d0986b53b0fe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 15:42:40 +0200 Subject: USB: option: fix interface-data memory leak in error path Move interface data allocation to attach so that it is deallocated should usb-serial probe fail. Note that the usb device id is stored at probe so that it can be used in attach to determine send-setup blacklisting. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index eb4bdd4a0106..5dee7d61241e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -47,6 +47,7 @@ /* Function prototypes */ static int option_probe(struct usb_serial *serial, const struct usb_device_id *id); +static int option_attach(struct usb_serial *serial); static void option_release(struct usb_serial *serial); static int option_send_setup(struct usb_serial_port *port); static void option_instat_callback(struct urb *urb); @@ -1288,6 +1289,7 @@ static struct usb_serial_driver option_1port_device = { .tiocmget = usb_wwan_tiocmget, .tiocmset = usb_wwan_tiocmset, .ioctl = usb_wwan_ioctl, + .attach = option_attach, .release = option_release, .port_probe = usb_wwan_port_probe, .port_remove = usb_wwan_port_remove, @@ -1335,8 +1337,6 @@ static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason, static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { - struct usb_wwan_intf_private *data; - struct option_private *priv; struct usb_interface_descriptor *iface_desc = &serial->interface->cur_altsetting->desc; struct usb_device_descriptor *dev_desc = &serial->dev->descriptor; @@ -1374,6 +1374,19 @@ static int option_probe(struct usb_serial *serial, iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA) return -ENODEV; + /* Store device id so we can use it during attach. */ + usb_set_serial_data(serial, (void *)id); + + return 0; +} + +static int option_attach(struct usb_serial *serial) +{ + struct usb_interface_descriptor *iface_desc; + const struct usb_device_id *id; + struct usb_wwan_intf_private *data; + struct option_private *priv; + data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1384,6 +1397,10 @@ static int option_probe(struct usb_serial *serial, return -ENOMEM; } + /* Retrieve device id stored at probe. */ + id = usb_get_serial_data(serial); + iface_desc = &serial->interface->cur_altsetting->desc; + priv->bInterfaceNumber = iface_desc->bInterfaceNumber; data->private = priv; -- cgit v1.2.3 From 961be09e1ead58509ed4bed0d5819a15d8613d8d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 15:42:41 +0200 Subject: USB: qcserial: fix interface-data memory leak in error path Move interface data allocation to attach so that it is deallocated should usb-serial probe fail. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 8dd2280dff63..aa148c21ea40 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -138,7 +138,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) { - struct usb_wwan_intf_private *data; struct usb_host_interface *intf = serial->interface->cur_altsetting; struct device *dev = &serial->dev->dev; int retval = -ENODEV; @@ -154,13 +153,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) ifnum = intf->desc.bInterfaceNumber; dev_dbg(dev, "This Interface = %d\n", ifnum); - data = kzalloc(sizeof(struct usb_wwan_intf_private), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - spin_lock_init(&data->susp_lock); - if (nintf == 1) { /* QDL mode */ /* Gobi 2000 has a single altsetting, older ones have two */ @@ -253,20 +245,28 @@ done: } } - /* Set serial->private if not returning error */ - if (retval == 0) - usb_set_serial_data(serial, data); - else - kfree(data); - return retval; } +static int qc_attach(struct usb_serial *serial) +{ + struct usb_wwan_intf_private *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->susp_lock); + + usb_set_serial_data(serial, data); + + return 0; +} + static void qc_release(struct usb_serial *serial) { struct usb_wwan_intf_private *priv = usb_get_serial_data(serial); - /* Free the private data allocated in qcprobe */ usb_set_serial_data(serial, NULL); kfree(priv); } @@ -285,6 +285,7 @@ static struct usb_serial_driver qcdevice = { .write = usb_wwan_write, .write_room = usb_wwan_write_room, .chars_in_buffer = usb_wwan_chars_in_buffer, + .attach = qc_attach, .release = qc_release, .port_probe = usb_wwan_port_probe, .port_remove = usb_wwan_port_remove, -- cgit v1.2.3 From 28c3ae9a8cf45f439c9a0779ebd0256e2ae72813 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 18:56:32 +0200 Subject: USB: mos7840: remove NULL-urb submission The private int_urb is never allocated so the submission from the control completion handler will always fail. Remove this odd piece of broken code. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7840.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c5e5a6c8d69e..f2c4bb709747 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -218,7 +218,6 @@ struct moschip_port { int port_num; /*Actual port number in the device(1,2,etc) */ struct urb *write_urb; /* write URB for this port */ struct urb *read_urb; /* read URB for this port */ - struct urb *int_urb; __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ char open; @@ -478,7 +477,6 @@ static void mos7840_control_callback(struct urb *urb) struct moschip_port *mos7840_port; struct device *dev = &urb->dev->dev; __u8 regval = 0x0; - int result = 0; int status = urb->status; mos7840_port = urb->context; @@ -495,7 +493,7 @@ static void mos7840_control_callback(struct urb *urb) return; default: dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); - goto exit; + return; } dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length); @@ -508,16 +506,6 @@ static void mos7840_control_callback(struct urb *urb) mos7840_handle_new_msr(mos7840_port, regval); else if (mos7840_port->MsrLsr == 1) mos7840_handle_new_lsr(mos7840_port, regval); - -exit: - spin_lock(&mos7840_port->pool_lock); - if (!mos7840_port->zombie) - result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC); - spin_unlock(&mos7840_port->pool_lock); - if (result) { - dev_err(dev, "%s - Error %d submitting interrupt urb\n", - __func__, result); - } } static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, -- cgit v1.2.3 From e681b66f2e19fadbe8a7e2a17900978cb6bc921f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 18:56:33 +0200 Subject: USB: mos7840: remove invalid disconnect handling Remove private zombie flag used to signal disconnect and to prevent control urb from being submitted from interrupt urb completion handler. The control urb will not be re-submitted as both the control urb and the interrupt urb is killed on disconnect. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7840.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index f2c4bb709747..84f8c106e5e9 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -222,7 +222,6 @@ struct moschip_port { __u8 shadowMCR; /* last MCR value received */ char open; char open_ports; - char zombie; wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */ int delta_msr_cond; @@ -674,14 +673,7 @@ static void mos7840_interrupt_callback(struct urb *urb) wreg = MODEM_STATUS_REGISTER; break; } - spin_lock(&mos7840_port->pool_lock); - if (!mos7840_port->zombie) { - rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data); - } else { - spin_unlock(&mos7840_port->pool_lock); - return; - } - spin_unlock(&mos7840_port->pool_lock); + rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data); } } } @@ -2598,9 +2590,6 @@ static void mos7840_disconnect(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { mos7840_port = mos7840_get_port_private(serial->port[i]); if (mos7840_port) { - spin_lock_irqsave(&mos7840_port->pool_lock, flags); - mos7840_port->zombie = 1; - spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); usb_kill_urb(mos7840_port->control_urb); } } -- cgit v1.2.3 From 80c00750f0c9867a65b30a17880939b6bc660a77 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 18:56:34 +0200 Subject: USB: mos7840: fix port-data memory leak Fix port-data memory leak by moving port data allocation and deallocation to port_probe and port_remove. Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no driver is bound) the port private data is no longer freed at release as it is no longer accessible. Note that the indentation was kept intact using a do-while(0) in order to facilitate review. A follow-up patch will remove it. Compile-only tested. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7840.c | 219 ++++++++++++++++++------------------------- 1 file changed, 89 insertions(+), 130 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 84f8c106e5e9..bc3df86134fe 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2327,49 +2327,45 @@ static int mos7840_calc_num_ports(struct usb_serial *serial) return mos7840_num_ports; } -/**************************************************************************** - * mos7840_startup - ****************************************************************************/ - -static int mos7840_startup(struct usb_serial *serial) +static int mos7840_port_probe(struct usb_serial_port *port) { + struct usb_serial *serial = port->serial; struct moschip_port *mos7840_port; - struct usb_device *dev; - int i, status; + int status; + int pnum; __u16 Data; - dev = serial->dev; - /* we set up the pointers to the endpoints in the mos7840_open * * function, as the structures aren't created yet. */ - /* set up port private structures */ - for (i = 0; i < serial->num_ports; ++i) { - dev_dbg(&dev->dev, "mos7840_startup: configuring port %d............\n", i); + pnum = port->number - serial->minor; + + /* FIXME: remove do-while(0) loop used to keep stable patch minimal. + */ + do { + dev_dbg(&port->dev, "mos7840_startup: configuring port %d............\n", pnum); mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); if (mos7840_port == NULL) { - dev_err(&dev->dev, "%s - Out of memory\n", __func__); - status = -ENOMEM; - i--; /* don't follow NULL pointer cleaning up */ - goto error; + dev_err(&port->dev, "%s - Out of memory\n", __func__); + return -ENOMEM; } /* Initialize all port interrupt end point to port 0 int * endpoint. Our device has only one interrupt end point * common to all port */ - mos7840_port->port = serial->port[i]; - mos7840_set_port_private(serial->port[i], mos7840_port); + mos7840_port->port = port; + mos7840_set_port_private(port, mos7840_port); spin_lock_init(&mos7840_port->pool_lock); /* minor is not initialised until later by * usb-serial.c:get_free_serial() and cannot therefore be used * to index device instances */ - mos7840_port->port_num = i + 1; - dev_dbg(&dev->dev, "serial->port[i]->number = %d\n", serial->port[i]->number); - dev_dbg(&dev->dev, "serial->port[i]->serial->minor = %d\n", serial->port[i]->serial->minor); - dev_dbg(&dev->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num); - dev_dbg(&dev->dev, "serial->minor = %d\n", serial->minor); + mos7840_port->port_num = pnum + 1; + dev_dbg(&port->dev, "port->number = %d\n", port->number); + dev_dbg(&port->dev, "port->serial->minor = %d\n", port->serial->minor); + dev_dbg(&port->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num); + dev_dbg(&port->dev, "serial->minor = %d\n", serial->minor); if (mos7840_port->port_num == 1) { mos7840_port->SpRegOffset = 0x0; @@ -2396,115 +2392,115 @@ static int mos7840_startup(struct usb_serial *serial) mos7840_port->ControlRegOffset = 0xd; mos7840_port->DcrRegOffset = 0x1c; } - mos7840_dump_serial_port(serial->port[i], mos7840_port); - mos7840_set_port_private(serial->port[i], mos7840_port); + mos7840_dump_serial_port(port, mos7840_port); + mos7840_set_port_private(port, mos7840_port); /* enable rx_disable bit in control register */ - status = mos7840_get_reg_sync(serial->port[i], + status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data); if (status < 0) { - dev_dbg(&dev->dev, "Reading ControlReg failed status-0x%x\n", status); + dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status); break; } else - dev_dbg(&dev->dev, "ControlReg Reading success val is %x, status%d\n", Data, status); + dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status); Data |= 0x08; /* setting driver done bit */ Data |= 0x04; /* sp1_bit to have cts change reflect in modem status reg */ /* Data |= 0x20; //rx_disable bit */ - status = mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); if (status < 0) { - dev_dbg(&dev->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status); + dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status); break; } else - dev_dbg(&dev->dev, "ControlReg Writing success(rx_disable) status%d\n", status); + dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status); /* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 and 0x24 in DCR3 */ Data = 0x01; - status = mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(port, (__u16) (mos7840_port->DcrRegOffset + 0), Data); if (status < 0) { - dev_dbg(&dev->dev, "Writing DCR0 failed status-0x%x\n", status); + dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status); break; } else - dev_dbg(&dev->dev, "DCR0 Writing success status%d\n", status); + dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status); Data = 0x05; - status = mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(port, (__u16) (mos7840_port->DcrRegOffset + 1), Data); if (status < 0) { - dev_dbg(&dev->dev, "Writing DCR1 failed status-0x%x\n", status); + dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status); break; } else - dev_dbg(&dev->dev, "DCR1 Writing success status%d\n", status); + dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status); Data = 0x24; - status = mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(port, (__u16) (mos7840_port->DcrRegOffset + 2), Data); if (status < 0) { - dev_dbg(&dev->dev, "Writing DCR2 failed status-0x%x\n", status); + dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status); break; } else - dev_dbg(&dev->dev, "DCR2 Writing success status%d\n", status); + dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status); /* write values in clkstart0x0 and clkmulti 0x20 */ Data = 0x0; - status = mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(port, CLK_START_VALUE_REGISTER, Data); if (status < 0) { - dev_dbg(&dev->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); + dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); break; } else - dev_dbg(&dev->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status); + dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status); Data = 0x20; - status = mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(port, CLK_MULTI_REGISTER, Data); if (status < 0) { - dev_dbg(&dev->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status); + dev_dbg(&port->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status); goto error; } else - dev_dbg(&dev->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status); + dev_dbg(&port->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status); /* write value 0x0 to scratchpad register */ Data = 0x00; - status = mos7840_set_uart_reg(serial->port[i], + status = mos7840_set_uart_reg(port, SCRATCH_PAD_REGISTER, Data); if (status < 0) { - dev_dbg(&dev->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); + dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); break; } else - dev_dbg(&dev->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status); + dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status); /* Zero Length flag register */ if ((mos7840_port->port_num != 1) && (serial->num_ports == 2)) { Data = 0xff; - status = mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(port, (__u16) (ZLP_REG1 + ((__u16)mos7840_port->port_num)), Data); - dev_dbg(&dev->dev, "ZLIP offset %x\n", + dev_dbg(&port->dev, "ZLIP offset %x\n", (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num))); if (status < 0) { - dev_dbg(&dev->dev, "Writing ZLP_REG%d failed status-0x%x\n", i + 2, status); + dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status); break; } else - dev_dbg(&dev->dev, "ZLP_REG%d Writing success status%d\n", i + 2, status); + dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status); } else { Data = 0xff; - status = mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(port, (__u16) (ZLP_REG1 + ((__u16)mos7840_port->port_num) - 0x1), Data); - dev_dbg(&dev->dev, "ZLIP offset %x\n", + dev_dbg(&port->dev, "ZLIP offset %x\n", (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1)); if (status < 0) { - dev_dbg(&dev->dev, "Writing ZLP_REG%d failed status-0x%x\n", i + 1, status); + dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status); break; } else - dev_dbg(&dev->dev, "ZLP_REG%d Writing success status%d\n", i + 1, status); + dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status); } mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -2541,92 +2537,56 @@ static int mos7840_startup(struct usb_serial *serial) mos7840_port->led_flag = false; /* Turn off LED */ - mos7840_set_led_sync(serial->port[i], + mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); } - } + } while (0); - /* Zero Length flag enable */ - Data = 0x0f; - status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data); - if (status < 0) { - dev_dbg(&dev->dev, "Writing ZLP_REG5 failed status-0x%x\n", status); - goto error; - } else - dev_dbg(&dev->dev, "ZLP_REG5 Writing success status%d\n", status); - - /* setting configuration feature to one */ - usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - (__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, MOS_WDR_TIMEOUT); + if (pnum == serial->num_ports - 1) { + /* Zero Length flag enable */ + Data = 0x0f; + status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data); + if (status < 0) { + dev_dbg(&port->dev, "Writing ZLP_REG5 failed status-0x%x\n", status); + goto error; + } else + dev_dbg(&port->dev, "ZLP_REG5 Writing success status%d\n", status); + + /* setting configuration feature to one */ + usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + 0x03, 0x00, 0x01, 0x00, NULL, 0x00, + MOS_WDR_TIMEOUT); + } return 0; error: - for (/* nothing */; i >= 0; i--) { - mos7840_port = mos7840_get_port_private(serial->port[i]); + kfree(mos7840_port->dr); + kfree(mos7840_port->ctrl_buf); + usb_free_urb(mos7840_port->control_urb); + kfree(mos7840_port); - kfree(mos7840_port->dr); - kfree(mos7840_port->ctrl_buf); - usb_free_urb(mos7840_port->control_urb); - kfree(mos7840_port); - } return status; } -/**************************************************************************** - * mos7840_disconnect - * This function is called whenever the device is removed from the usb bus. - ****************************************************************************/ - -static void mos7840_disconnect(struct usb_serial *serial) +static int mos7840_port_remove(struct usb_serial_port *port) { - int i; - unsigned long flags; struct moschip_port *mos7840_port; - /* check for the ports to be closed,close the ports and disconnect */ + mos7840_port = mos7840_get_port_private(port); - /* free private structure allocated for serial port * - * stop reads and writes on all ports */ + if (mos7840_port->has_led) { + /* Turn off LED */ + mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); - for (i = 0; i < serial->num_ports; ++i) { - mos7840_port = mos7840_get_port_private(serial->port[i]); - if (mos7840_port) { - usb_kill_urb(mos7840_port->control_urb); - } + del_timer_sync(&mos7840_port->led_timer1); + del_timer_sync(&mos7840_port->led_timer2); } -} - -/**************************************************************************** - * mos7840_release - * This function is called when the usb_serial structure is freed. - ****************************************************************************/ - -static void mos7840_release(struct usb_serial *serial) -{ - int i; - struct moschip_port *mos7840_port; + usb_kill_urb(mos7840_port->control_urb); + usb_free_urb(mos7840_port->control_urb); + kfree(mos7840_port->ctrl_buf); + kfree(mos7840_port->dr); + kfree(mos7840_port); - /* check for the ports to be closed,close the ports and disconnect */ - - /* free private structure allocated for serial port * - * stop reads and writes on all ports */ - - for (i = 0; i < serial->num_ports; ++i) { - mos7840_port = mos7840_get_port_private(serial->port[i]); - if (mos7840_port) { - if (mos7840_port->has_led) { - /* Turn off LED */ - mos7840_set_led_sync(mos7840_port->port, - MODEM_CONTROL_REGISTER, 0x0300); - - del_timer_sync(&mos7840_port->led_timer1); - del_timer_sync(&mos7840_port->led_timer2); - } - usb_free_urb(mos7840_port->control_urb); - kfree(mos7840_port->ctrl_buf); - kfree(mos7840_port->dr); - kfree(mos7840_port); - } - } + return 0; } static struct usb_serial_driver moschip7840_4port_device = { @@ -2654,9 +2614,8 @@ static struct usb_serial_driver moschip7840_4port_device = { .tiocmget = mos7840_tiocmget, .tiocmset = mos7840_tiocmset, .get_icount = mos7840_get_icount, - .attach = mos7840_startup, - .disconnect = mos7840_disconnect, - .release = mos7840_release, + .port_probe = mos7840_port_probe, + .port_remove = mos7840_port_remove, .read_bulk_callback = mos7840_bulk_in_callback, .read_int_callback = mos7840_interrupt_callback, }; -- cgit v1.2.3 From ae685effe70cbe11fc269741629022f76005ea99 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Oct 2012 18:56:35 +0200 Subject: USB: mos7840: fix port_probe flow Remove temporary do-while(0) loop used to keep changes minimal. Fixup indentation, remove some line breaks, and replace break with goto to maintain flow. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7840.c | 349 ++++++++++++++++++++----------------------- 1 file changed, 166 insertions(+), 183 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index bc3df86134fe..1cf3375ec1af 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2340,208 +2340,191 @@ static int mos7840_port_probe(struct usb_serial_port *port) pnum = port->number - serial->minor; - /* FIXME: remove do-while(0) loop used to keep stable patch minimal. - */ - do { - dev_dbg(&port->dev, "mos7840_startup: configuring port %d............\n", pnum); - mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); - if (mos7840_port == NULL) { - dev_err(&port->dev, "%s - Out of memory\n", __func__); - return -ENOMEM; - } - - /* Initialize all port interrupt end point to port 0 int - * endpoint. Our device has only one interrupt end point - * common to all port */ - - mos7840_port->port = port; - mos7840_set_port_private(port, mos7840_port); - spin_lock_init(&mos7840_port->pool_lock); - - /* minor is not initialised until later by - * usb-serial.c:get_free_serial() and cannot therefore be used - * to index device instances */ - mos7840_port->port_num = pnum + 1; - dev_dbg(&port->dev, "port->number = %d\n", port->number); - dev_dbg(&port->dev, "port->serial->minor = %d\n", port->serial->minor); - dev_dbg(&port->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num); - dev_dbg(&port->dev, "serial->minor = %d\n", serial->minor); - - if (mos7840_port->port_num == 1) { - mos7840_port->SpRegOffset = 0x0; - mos7840_port->ControlRegOffset = 0x1; - mos7840_port->DcrRegOffset = 0x4; - } else if ((mos7840_port->port_num == 2) - && (serial->num_ports == 4)) { - mos7840_port->SpRegOffset = 0x8; - mos7840_port->ControlRegOffset = 0x9; - mos7840_port->DcrRegOffset = 0x16; - } else if ((mos7840_port->port_num == 2) - && (serial->num_ports == 2)) { - mos7840_port->SpRegOffset = 0xa; - mos7840_port->ControlRegOffset = 0xb; - mos7840_port->DcrRegOffset = 0x19; - } else if ((mos7840_port->port_num == 3) - && (serial->num_ports == 4)) { - mos7840_port->SpRegOffset = 0xa; - mos7840_port->ControlRegOffset = 0xb; - mos7840_port->DcrRegOffset = 0x19; - } else if ((mos7840_port->port_num == 4) - && (serial->num_ports == 4)) { - mos7840_port->SpRegOffset = 0xc; - mos7840_port->ControlRegOffset = 0xd; - mos7840_port->DcrRegOffset = 0x1c; - } - mos7840_dump_serial_port(port, mos7840_port); - mos7840_set_port_private(port, mos7840_port); + dev_dbg(&port->dev, "mos7840_startup: configuring port %d\n", pnum); + mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); + if (mos7840_port == NULL) { + dev_err(&port->dev, "%s - Out of memory\n", __func__); + return -ENOMEM; + } - /* enable rx_disable bit in control register */ - status = mos7840_get_reg_sync(port, - mos7840_port->ControlRegOffset, &Data); - if (status < 0) { - dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status); - break; - } else - dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status); - Data |= 0x08; /* setting driver done bit */ - Data |= 0x04; /* sp1_bit to have cts change reflect in - modem status reg */ + /* Initialize all port interrupt end point to port 0 int + * endpoint. Our device has only one interrupt end point + * common to all port */ + + mos7840_port->port = port; + mos7840_set_port_private(port, mos7840_port); + spin_lock_init(&mos7840_port->pool_lock); + + /* minor is not initialised until later by + * usb-serial.c:get_free_serial() and cannot therefore be used + * to index device instances */ + mos7840_port->port_num = pnum + 1; + dev_dbg(&port->dev, "port->number = %d\n", port->number); + dev_dbg(&port->dev, "port->serial->minor = %d\n", port->serial->minor); + dev_dbg(&port->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num); + dev_dbg(&port->dev, "serial->minor = %d\n", serial->minor); + + if (mos7840_port->port_num == 1) { + mos7840_port->SpRegOffset = 0x0; + mos7840_port->ControlRegOffset = 0x1; + mos7840_port->DcrRegOffset = 0x4; + } else if ((mos7840_port->port_num == 2) && (serial->num_ports == 4)) { + mos7840_port->SpRegOffset = 0x8; + mos7840_port->ControlRegOffset = 0x9; + mos7840_port->DcrRegOffset = 0x16; + } else if ((mos7840_port->port_num == 2) && (serial->num_ports == 2)) { + mos7840_port->SpRegOffset = 0xa; + mos7840_port->ControlRegOffset = 0xb; + mos7840_port->DcrRegOffset = 0x19; + } else if ((mos7840_port->port_num == 3) && (serial->num_ports == 4)) { + mos7840_port->SpRegOffset = 0xa; + mos7840_port->ControlRegOffset = 0xb; + mos7840_port->DcrRegOffset = 0x19; + } else if ((mos7840_port->port_num == 4) && (serial->num_ports == 4)) { + mos7840_port->SpRegOffset = 0xc; + mos7840_port->ControlRegOffset = 0xd; + mos7840_port->DcrRegOffset = 0x1c; + } + mos7840_dump_serial_port(port, mos7840_port); + mos7840_set_port_private(port, mos7840_port); + + /* enable rx_disable bit in control register */ + status = mos7840_get_reg_sync(port, + mos7840_port->ControlRegOffset, &Data); + if (status < 0) { + dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status); + goto out; + } else + dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status); + Data |= 0x08; /* setting driver done bit */ + Data |= 0x04; /* sp1_bit to have cts change reflect in + modem status reg */ + + /* Data |= 0x20; //rx_disable bit */ + status = mos7840_set_reg_sync(port, + mos7840_port->ControlRegOffset, Data); + if (status < 0) { + dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status); + goto out; + } else + dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status); + + /* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 + and 0x24 in DCR3 */ + Data = 0x01; + status = mos7840_set_reg_sync(port, + (__u16) (mos7840_port->DcrRegOffset + 0), Data); + if (status < 0) { + dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status); + goto out; + } else + dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status); - /* Data |= 0x20; //rx_disable bit */ - status = mos7840_set_reg_sync(port, - mos7840_port->ControlRegOffset, Data); - if (status < 0) { - dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status); - break; - } else - dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status); + Data = 0x05; + status = mos7840_set_reg_sync(port, + (__u16) (mos7840_port->DcrRegOffset + 1), Data); + if (status < 0) { + dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status); + goto out; + } else + dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status); - /* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 - and 0x24 in DCR3 */ - Data = 0x01; - status = mos7840_set_reg_sync(port, - (__u16) (mos7840_port->DcrRegOffset + 0), Data); - if (status < 0) { - dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status); - break; - } else - dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status); + Data = 0x24; + status = mos7840_set_reg_sync(port, + (__u16) (mos7840_port->DcrRegOffset + 2), Data); + if (status < 0) { + dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status); + goto out; + } else + dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status); - Data = 0x05; - status = mos7840_set_reg_sync(port, - (__u16) (mos7840_port->DcrRegOffset + 1), Data); - if (status < 0) { - dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status); - break; - } else - dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status); + /* write values in clkstart0x0 and clkmulti 0x20 */ + Data = 0x0; + status = mos7840_set_reg_sync(port, CLK_START_VALUE_REGISTER, Data); + if (status < 0) { + dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); + goto out; + } else + dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status); - Data = 0x24; - status = mos7840_set_reg_sync(port, - (__u16) (mos7840_port->DcrRegOffset + 2), Data); - if (status < 0) { - dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status); - break; - } else - dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status); + Data = 0x20; + status = mos7840_set_reg_sync(port, CLK_MULTI_REGISTER, Data); + if (status < 0) { + dev_dbg(&port->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status); + goto error; + } else + dev_dbg(&port->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status); - /* write values in clkstart0x0 and clkmulti 0x20 */ - Data = 0x0; + /* write value 0x0 to scratchpad register */ + Data = 0x00; + status = mos7840_set_uart_reg(port, SCRATCH_PAD_REGISTER, Data); + if (status < 0) { + dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); + goto out; + } else + dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status); + + /* Zero Length flag register */ + if ((mos7840_port->port_num != 1) && (serial->num_ports == 2)) { + Data = 0xff; status = mos7840_set_reg_sync(port, - CLK_START_VALUE_REGISTER, Data); + (__u16) (ZLP_REG1 + + ((__u16)mos7840_port->port_num)), Data); + dev_dbg(&port->dev, "ZLIP offset %x\n", + (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num))); if (status < 0) { - dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); - break; + dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status); + goto out; } else - dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status); - - Data = 0x20; + dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status); + } else { + Data = 0xff; status = mos7840_set_reg_sync(port, - CLK_MULTI_REGISTER, Data); - if (status < 0) { - dev_dbg(&port->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status); - goto error; - } else - dev_dbg(&port->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status); - - /* write value 0x0 to scratchpad register */ - Data = 0x00; - status = mos7840_set_uart_reg(port, - SCRATCH_PAD_REGISTER, Data); + (__u16) (ZLP_REG1 + + ((__u16)mos7840_port->port_num) - 0x1), Data); + dev_dbg(&port->dev, "ZLIP offset %x\n", + (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1)); if (status < 0) { - dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); - break; + dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status); + goto out; } else - dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status); + dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status); - /* Zero Length flag register */ - if ((mos7840_port->port_num != 1) - && (serial->num_ports == 2)) { - - Data = 0xff; - status = mos7840_set_reg_sync(port, - (__u16) (ZLP_REG1 + - ((__u16)mos7840_port->port_num)), Data); - dev_dbg(&port->dev, "ZLIP offset %x\n", - (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num))); - if (status < 0) { - dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status); - break; - } else - dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status); - } else { - Data = 0xff; - status = mos7840_set_reg_sync(port, - (__u16) (ZLP_REG1 + - ((__u16)mos7840_port->port_num) - 0x1), Data); - dev_dbg(&port->dev, "ZLIP offset %x\n", - (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1)); - if (status < 0) { - dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status); - break; - } else - dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status); - - } - mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL); - mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL); - mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest), - GFP_KERNEL); - if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf || - !mos7840_port->dr) { - status = -ENOMEM; - goto error; - } - - mos7840_port->has_led = false; + } + mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL); + mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL); + mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest), + GFP_KERNEL); + if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf || + !mos7840_port->dr) { + status = -ENOMEM; + goto error; + } - /* Initialize LED timers */ - if (device_type == MOSCHIP_DEVICE_ID_7810) { - mos7840_port->has_led = true; + mos7840_port->has_led = false; - init_timer(&mos7840_port->led_timer1); - mos7840_port->led_timer1.function = mos7840_led_off; - mos7840_port->led_timer1.expires = - jiffies + msecs_to_jiffies(LED_ON_MS); - mos7840_port->led_timer1.data = - (unsigned long)mos7840_port; + /* Initialize LED timers */ + if (device_type == MOSCHIP_DEVICE_ID_7810) { + mos7840_port->has_led = true; - init_timer(&mos7840_port->led_timer2); - mos7840_port->led_timer2.function = - mos7840_led_flag_off; - mos7840_port->led_timer2.expires = - jiffies + msecs_to_jiffies(LED_OFF_MS); - mos7840_port->led_timer2.data = - (unsigned long)mos7840_port; + init_timer(&mos7840_port->led_timer1); + mos7840_port->led_timer1.function = mos7840_led_off; + mos7840_port->led_timer1.expires = + jiffies + msecs_to_jiffies(LED_ON_MS); + mos7840_port->led_timer1.data = (unsigned long)mos7840_port; - mos7840_port->led_flag = false; + init_timer(&mos7840_port->led_timer2); + mos7840_port->led_timer2.function = mos7840_led_flag_off; + mos7840_port->led_timer2.expires = + jiffies + msecs_to_jiffies(LED_OFF_MS); + mos7840_port->led_timer2.data = (unsigned long)mos7840_port; - /* Turn off LED */ - mos7840_set_led_sync(port, - MODEM_CONTROL_REGISTER, 0x0300); - } - } while (0); + mos7840_port->led_flag = false; + /* Turn off LED */ + mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); + } +out: if (pnum == serial->num_ports - 1) { /* Zero Length flag enable */ Data = 0x0f; -- cgit v1.2.3 From c323dc023b9501e5d09582ec7efd1d40a9001d99 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Tue, 9 Oct 2012 12:22:36 +0200 Subject: ehci: fix Lucid nohandoff pci quirk to be more generic with BIOS versions BIOS vendors keep changing the BIOS versions. Only match the beginning of the string to match all Lucid tablets with board name M11JB. Signed-off-by: Anisse Astier Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 966d1484ee79..ead45259bf15 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -545,7 +545,7 @@ static const struct dmi_system_id __devinitconst ehci_dmi_nohandoff_table[] = { /* Pegatron Lucid (Ordissimo AIRIS) */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "M11JB"), - DMI_MATCH(DMI_BIOS_VERSION, "Lucid-GE-133"), + DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"), }, }, { } -- cgit v1.2.3 From 8daf8b6086f9d575200cd0aa3797e26137255609 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Tue, 9 Oct 2012 12:22:37 +0200 Subject: ehci: Add yet-another Lucid nohandoff pci quirk Board name changed on another shipping Lucid tablet. Signed-off-by: Anisse Astier Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index ead45259bf15..39f9e4a9a2d3 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -548,6 +548,13 @@ static const struct dmi_system_id __devinitconst ehci_dmi_nohandoff_table[] = { DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"), }, }, + { + /* Pegatron Lucid (Ordissimo) */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "Ordissimo"), + DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"), + }, + }, { } }; -- cgit v1.2.3 From 43a09f7fb01fa1e091416a2aa49b6c666458c1ee Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 16 Oct 2012 13:17:43 -0700 Subject: xhci: Fix potential NULL ptr deref in command cancellation. The command cancellation code doesn't check whether find_trb_seg() couldn't find the segment that contains the TRB to be canceled. This could cause a NULL pointer deference later in the function when next_trb is called. It's unlikely to happen unless something is wrong with the command ring pointers, so add some debugging in case it happens. This patch should be backported to stable kernels as old as 3.0, that contain the commit b63f4053cc8aa22a98e3f9a97845afe6c15d0a0d "xHCI: handle command after aborting the command ring". Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/host/xhci-ring.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c6ebb176dc4f..4e1a8946b8d1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1228,6 +1228,17 @@ static void xhci_cmd_to_noop(struct xhci_hcd *xhci, struct xhci_cd *cur_cd) cur_seg = find_trb_seg(xhci->cmd_ring->first_seg, xhci->cmd_ring->dequeue, &cycle_state); + if (!cur_seg) { + xhci_warn(xhci, "Command ring mismatch, dequeue = %p %llx (dma)\n", + xhci->cmd_ring->dequeue, + (unsigned long long) + xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, + xhci->cmd_ring->dequeue)); + xhci_debug_ring(xhci, xhci->cmd_ring); + xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring); + return; + } + /* find the command trb matched by cd from command ring */ for (cmd_trb = xhci->cmd_ring->dequeue; cmd_trb != xhci->cmd_ring->enqueue; -- cgit v1.2.3 From b80313422a1551cc1d4f3e2d532fe633154ae5dd Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 16 Oct 2012 13:26:22 -0700 Subject: xhci: Fix missing break in xhci_evaluate_context_result. Coverity complains that xhci_evaluate_context_result() is missing a break statement after the COMP_EBADSLT switch case. It's not a big deal, since we wanted to return the same error code as the case statement below it does. The end result would be one that a Slot Disabled error completion code would also print the warning message associated with a Context State error code. No other bad behavior would result. It's not worth backporting to stable kernels, since it only fixes an issue with too much debugging. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 9ec939646b42..ffe2e2e5c6ab 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1817,6 +1817,8 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, case COMP_EBADSLT: dev_warn(&udev->dev, "WARN: slot not enabled for" "evaluate context command.\n"); + ret = -EINVAL; + break; case COMP_CTX_STATE: dev_warn(&udev->dev, "WARN: invalid context state for " "evaluate context command.\n"); -- cgit v1.2.3 From 7e5f77f9bc30cb5338802df184b26d1365afca09 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 16 Oct 2012 13:33:45 -0700 Subject: xhci: trivial: Remove assigned but unused slot_ctx. Remove the variable slot_ctx from xhci_dbg_ctx(), since it is assigned but unused. Caught by Coverity. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-dbg.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 4b436f5a4171..5f3a7c74aa8d 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -544,7 +544,6 @@ void xhci_dbg_ctx(struct xhci_hcd *xhci, int i; /* Fields are 32 bits wide, DMA addresses are in bytes */ int field_size = 32 / 8; - struct xhci_slot_ctx *slot_ctx; dma_addr_t dma = ctx->dma; int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params); @@ -570,7 +569,6 @@ void xhci_dbg_ctx(struct xhci_hcd *xhci, dbg_rsvd64(xhci, (u64 *)ctrl_ctx, dma); } - slot_ctx = xhci_get_slot_ctx(xhci, ctx); xhci_dbg_slot_ctx(xhci, ctx); xhci_dbg_ep_ctx(xhci, ctx, last_ep); } -- cgit v1.2.3 From df0379065b3850cd42447b59578a2f9362047487 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 16 Oct 2012 13:33:45 -0700 Subject: xhci: trivial: Remove assigned but unused ep_ctx. Remove the variable ep_ctx from xhci_add_endpoint(), since it is assigned but unused. Caught by Coverity. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ffe2e2e5c6ab..c9e419f29b74 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1627,7 +1627,6 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx, *out_ctx; unsigned int ep_index; - struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; struct xhci_input_control_ctx *ctrl_ctx; u32 added_ctxs; @@ -1663,7 +1662,6 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, out_ctx = virt_dev->out_ctx; ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); ep_index = xhci_get_endpoint_index(&ep->desc); - ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); /* If this endpoint is already in use, and the upper layers are trying * to add it again without dropping it, reject the addition. -- cgit v1.2.3 From 00a89180e5419f6567135d86e66a989a1b610b45 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 26 Oct 2012 09:55:31 +0300 Subject: Revert "usb: musb: use DMA mode 1 whenever possible" This reverts commit 4f3e8d263d34e52e75b5adfa14811467d3033d8e. it turns out that current implementation of Mode 1 DMA added a few regressions to some users, so we need to revert this patch and let author work on a better version of Mode 1 DMA support. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index d0b87e7b4abf..b6b84dacc791 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -707,11 +707,12 @@ static void rxstate(struct musb *musb, struct musb_request *req) fifo_count = musb_readw(epio, MUSB_RXCOUNT); /* - * use mode 1 only if we expect data of at least ep packet_sz - * and have not yet received a short packet + * Enable Mode 1 on RX transfers only when short_not_ok flag + * is set. Currently short_not_ok flag is set only from + * file_storage and f_mass_storage drivers */ - if ((request->length - request->actual >= musb_ep->packet_sz) && - (fifo_count >= musb_ep->packet_sz)) + + if (request->short_not_ok && fifo_count == musb_ep->packet_sz) use_mode_1 = 1; else use_mode_1 = 0; @@ -727,6 +728,27 @@ static void rxstate(struct musb *musb, struct musb_request *req) c = musb->dma_controller; channel = musb_ep->dma; + /* We use DMA Req mode 0 in rx_csr, and DMA controller operates in + * mode 0 only. So we do not get endpoint interrupts due to DMA + * completion. We only get interrupts from DMA controller. + * + * We could operate in DMA mode 1 if we knew the size of the tranfer + * in advance. For mass storage class, request->length = what the host + * sends, so that'd work. But for pretty much everything else, + * request->length is routinely more than what the host sends. For + * most these gadgets, end of is signified either by a short packet, + * or filling the last byte of the buffer. (Sending extra data in + * that last pckate should trigger an overflow fault.) But in mode 1, + * we don't get DMA completion interrupt for short packets. + * + * Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1), + * to get endpoint interrupt on every DMA req, but that didn't seem + * to work reliably. + * + * REVISIT an updated g_file_storage can set req->short_not_ok, which + * then becomes usable as a runtime "use mode 1" hint... + */ + /* Experimental: Mode1 works with mass storage use cases */ if (use_mode_1) { csr |= MUSB_RXCSR_AUTOCLEAR; -- cgit v1.2.3 From 9a65d162e449145706aa7309a1be00e3c3d5c80e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 23 Oct 2012 14:12:37 +0800 Subject: usb: musb: ux500: fix 'musbid' undeclared error in ux500_remove() commit 65b3d52d02a558fbfe08e43688e15390c5ab3067 (usb: musb: add musb_ida for multi instance support) used musbid in ux500_remove() but nerver declared it. I found this in x86_64 platform, but not sure whether this is a error on the correct ARCH. $ make drivers/usb/musb/ux500.o make[1]: Nothing to be done for `all'. make[1]: Nothing to be done for `relocs'. CHK include/generated/uapi/linux/version.h CHK include/generated/utsrelease.h UPD include/generated/utsrelease.h CALL scripts/checksyscalls.sh CC drivers/usb/musb/ux500.o drivers/usb/musb/ux500.c: In function 'ux500_probe': drivers/usb/musb/ux500.c:78:2: error: 'musbid' undeclared (first use in this function) drivers/usb/musb/ux500.c:78:2: note: each undeclared identifier is reported only once for each function it appears in make[1]: *** [drivers/usb/musb/ux500.o] Error 1 make: *** [drivers/usb/musb/ux500.o] Error 2 Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi --- drivers/usb/musb/ux500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index d62a91fedc22..0e62f504410e 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -65,7 +65,7 @@ static int __devinit ux500_probe(struct platform_device *pdev) struct platform_device *musb; struct ux500_glue *glue; struct clk *clk; - + int musbid; int ret = -ENOMEM; glue = kzalloc(sizeof(*glue), GFP_KERNEL); -- cgit v1.2.3 From f6bc8c29383456b89ac1b6f1aa768d195670fb53 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 29 Oct 2012 16:44:46 +0200 Subject: usb: otg: Fix build errors if USB_MUSB_OMAP2PLUS is selected as module TWL4030_USB & TWL6030_USB must depend on USB_MUSB_OMAP2PLUS in Kconfig else we get build errors with CONFIG_USB_MUSB_HDRC=m CONFIG_USB_MUSB_OMAP2PLUS=m CONFIG_TWL4030_USB=y CONFIG_TWL6030_USB=y LD init/built-in.o drivers/built-in.o: In function `twl4030_usb_irq': drivers/usb/otg/twl4030-usb.c:518: undefined reference to `omap_musb_mailbox' drivers/built-in.o: In function `twl4030_usb_phy_init': drivers/usb/otg/twl4030-usb.c:540: undefined reference to `omap_musb_mailbox' drivers/built-in.o: In function `twl6030_usb_irq': drivers/usb/otg/twl6030-usb.c:230: undefined reference to `omap_musb_mailbox' drivers/usb/otg/twl6030-usb.c:225: undefined reference to `omap_musb_mailbox' drivers/built-in.o: In function `twl6030_usbotg_irq': drivers/usb/otg/twl6030-usb.c:259: undefined reference to `omap_musb_mailbox' CC: Peter Meerwald Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/otg/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index d8c8a42bff3e..6223062d5d1b 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -58,7 +58,7 @@ config USB_ULPI_VIEWPORT config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" - depends on TWL4030_CORE && REGULATOR_TWL4030 + depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS select USB_OTG_UTILS help Enable this to support the USB OTG transceiver on TWL4030 @@ -68,7 +68,7 @@ config TWL4030_USB config TWL6030_USB tristate "TWL6030 USB Transceiver Driver" - depends on TWL4030_CORE && OMAP_USB2 + depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS select USB_OTG_UTILS help Enable this to support the USB OTG transceiver on TWL6030 -- cgit v1.2.3 From 8e493ca1767d4951ed1322abaa74d6edbca29918 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 26 Oct 2012 18:44:20 +0200 Subject: USB: usb_wwan: fix bulk-urb allocation Make sure we do not allocate urbs if we do not have a bulk endpoint. Legacy code used incorrect assumption to test for bulk endpoints. Reported-by: Dan Carpenter Cc: stable Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb_wwan.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 61a73ad1a187..a3e9c095f0d8 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -455,9 +455,6 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, struct usb_serial *serial = port->serial; struct urb *urb; - if (endpoint == -1) - return NULL; /* endpoint not needed */ - urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ if (urb == NULL) { dev_dbg(&serial->interface->dev, @@ -489,6 +486,9 @@ int usb_wwan_port_probe(struct usb_serial_port *port) init_usb_anchor(&portdata->delayed); for (i = 0; i < N_IN_URB; i++) { + if (!port->bulk_in_size) + break; + buffer = (u8 *)__get_free_page(GFP_KERNEL); if (!buffer) goto bail_out_error; @@ -502,8 +502,8 @@ int usb_wwan_port_probe(struct usb_serial_port *port) } for (i = 0; i < N_OUT_URB; i++) { - if (port->bulk_out_endpointAddress == -1) - continue; + if (!port->bulk_out_size) + break; buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); if (!buffer) -- cgit v1.2.3 From d99e65bda806f49e192dd31e9b01959974dea3cd Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 29 Oct 2012 16:45:54 +0000 Subject: USB: fix build with XEN and EARLY_PRINTK_DBGP enabled but USB_SUPPORT disabled Since there's no possible caller of dbgp_external_startup() and dbgp_reset_prep() when !USB_EHCI_HCD, there's no point in building and exporting these functions in that case. This eliminates a build error under the conditions listed in the subject, introduced with the merge f1c6872e4980bc4078cfaead05f892b3d78dea64. Reported-by: Randy Dunlap Signed-off-by: Jan Beulich Cc: Stefano Stabellini Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/early/ehci-dbgp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index e426ad626d74..4bfa78af379c 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -614,12 +615,6 @@ err: return -ENODEV; } -int dbgp_external_startup(struct usb_hcd *hcd) -{ - return xen_dbgp_external_startup(hcd) ?: _dbgp_external_startup(); -} -EXPORT_SYMBOL_GPL(dbgp_external_startup); - static int ehci_reset_port(int port) { u32 portsc; @@ -979,6 +974,7 @@ struct console early_dbgp_console = { .index = -1, }; +#if IS_ENABLED(CONFIG_USB_EHCI_HCD) int dbgp_reset_prep(struct usb_hcd *hcd) { int ret = xen_dbgp_reset_prep(hcd); @@ -1007,6 +1003,13 @@ int dbgp_reset_prep(struct usb_hcd *hcd) } EXPORT_SYMBOL_GPL(dbgp_reset_prep); +int dbgp_external_startup(struct usb_hcd *hcd) +{ + return xen_dbgp_external_startup(hcd) ?: _dbgp_external_startup(); +} +EXPORT_SYMBOL_GPL(dbgp_external_startup); +#endif /* USB_EHCI_HCD */ + #ifdef CONFIG_KGDB static char kgdbdbgp_buf[DBGP_MAX_PACKET]; -- cgit v1.2.3 From 4fe5f079b499d7ff34fd49f4005f09018c4e80aa Mon Sep 17 00:00:00 2001 From: Ian Coolidge Date: Wed, 7 Nov 2012 14:39:18 +0000 Subject: usb: gadget: g_ether: fix frame size check for 802.1Q Checking skb->len against ETH_FRAME_LEN assumes a 1514 ethernet frame size. With an 802.1Q VLAN header, ethernet frame length can now be 1518. Validate frame length against that. Signed-off-by: Ian Coolidge Signed-off-by: David S. Miller --- drivers/usb/gadget/u_ether.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 6458764994ef..4ec3c0d7a18b 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "u_ether.h" @@ -295,7 +296,7 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) while (skb2) { if (status < 0 || ETH_HLEN > skb2->len - || skb2->len > ETH_FRAME_LEN) { + || skb2->len > VLAN_ETH_FRAME_LEN) { dev->net->stats.rx_errors++; dev->net->stats.rx_length_errors++; DBG(dev, "rx length %d\n", skb2->len); -- cgit v1.2.3 From f0e3e35c9049087172c65302b42da8fe7ebb63a8 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sat, 10 Nov 2012 10:13:42 +0100 Subject: USB: keyspan: fix typo causing GPF on open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f79b2d0f (USB: keyspan: fix NULL-pointer dereferences and memory leaks) had a small typo which made the driver use wrong offsets when mapping serial port private data. This results in in a GPF when the port is opened. Reported-by: Richard Cc: stable Signed-off-by: Bjørn Mork Acked-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 7179b0c5f814..cff8dd5b462d 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -2430,7 +2430,7 @@ static void keyspan_release(struct usb_serial *serial) static int keyspan_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - struct keyspan_port_private *s_priv; + struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; struct callbacks *cback; @@ -2445,7 +2445,6 @@ static int keyspan_port_probe(struct usb_serial_port *port) if (!p_priv) return -ENOMEM; - s_priv = usb_get_serial_data(port->serial); p_priv->device_details = d_details; /* Setup values for the various callback routines */ -- cgit v1.2.3 From fcb21645f1bd86d2be29baf48aa1b298de52ccc7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 8 Nov 2012 11:56:42 -0600 Subject: USB: option: add Novatel E362 and Dell Wireless 5800 USB IDs The Dell 5800 appears to be a simple rebrand of the Novatel E362. Signed-off-by: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5dee7d61241e..c25d3fdf4629 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -158,6 +158,7 @@ static void option_instat_callback(struct urb *urb); #define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED 0x8001 #define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED 0x9000 #define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED 0x9001 +#define NOVATELWIRELESS_PRODUCT_E362 0x9010 #define NOVATELWIRELESS_PRODUCT_G1 0xA001 #define NOVATELWIRELESS_PRODUCT_G1_M 0xA002 #define NOVATELWIRELESS_PRODUCT_G2 0xA010 @@ -193,6 +194,9 @@ static void option_instat_callback(struct urb *urb); #define DELL_PRODUCT_5730_MINICARD_TELUS 0x8181 #define DELL_PRODUCT_5730_MINICARD_VZW 0x8182 +#define DELL_PRODUCT_5800_MINICARD_VZW 0x8195 /* Novatel E362 */ +#define DELL_PRODUCT_5800_V2_MINICARD_VZW 0x8196 /* Novatel E362 */ + #define KYOCERA_VENDOR_ID 0x0c88 #define KYOCERA_PRODUCT_KPC650 0x17da #define KYOCERA_PRODUCT_KPC680 0x180a @@ -706,6 +710,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G2) }, /* Novatel Ovation MC551 a.k.a. Verizon USB551L */ { USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC551, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E362, 0xff, 0xff, 0xff) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) }, @@ -728,6 +733,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_SPRINT) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_TELUS) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_VZW) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ + { USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5800_MINICARD_VZW, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5800_V2_MINICARD_VZW, 0xff, 0xff, 0xff) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) }, -- cgit v1.2.3 From c0bc3098871dd9b964f6b45ec1e4d70d87811744 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 8 Nov 2012 11:56:53 -0600 Subject: USB: option: add Alcatel X220/X500D USB IDs Signed-off-by: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c25d3fdf4629..edc64bb6f457 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -287,6 +287,7 @@ static void option_instat_callback(struct urb *urb); /* ALCATEL PRODUCTS */ #define ALCATEL_VENDOR_ID 0x1bbb #define ALCATEL_PRODUCT_X060S_X200 0x0000 +#define ALCATEL_PRODUCT_X220_X500D 0x0017 #define PIRELLI_VENDOR_ID 0x1266 #define PIRELLI_PRODUCT_C100_1 0x1002 @@ -1164,6 +1165,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200), .driver_info = (kernel_ulong_t)&alcatel_x200_blacklist }, + { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D) }, { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) }, { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14), -- cgit v1.2.3 From e592c5d0b7db94b485b4a2342db041a1882b7f75 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 13 Nov 2012 10:52:52 -0800 Subject: Revert "USB/host: Cleanup unneccessary irq disable code" This reverts commit 73d4066055e0e2830533041f4b91df8e6e5976ff. Martin Steigerwald reported that this change caused a hard lockup when using USB if threadirqs are enabled. Thomas pointed out that this patch is incorrect, and can cause problems. So revert it to get the previously working functionality back. Reported-by: Martin Steigerwald Cc: Thomas Gleixner Cc: Chuansheng Liu Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 16 ++++++++++++++++ drivers/usb/host/ehci-ls1x.c | 2 +- drivers/usb/host/ohci-xls.c | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 1e741bca0265..f034716190ff 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2151,8 +2151,15 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum); irqreturn_t usb_hcd_irq (int irq, void *__hcd) { struct usb_hcd *hcd = __hcd; + unsigned long flags; irqreturn_t rc; + /* IRQF_DISABLED doesn't work correctly with shared IRQs + * when the first handler doesn't use it. So let's just + * assume it's never used. + */ + local_irq_save(flags); + if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) rc = IRQ_NONE; else if (hcd->driver->irq(hcd) == IRQ_NONE) @@ -2160,6 +2167,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) else rc = IRQ_HANDLED; + local_irq_restore(flags); return rc; } EXPORT_SYMBOL_GPL(usb_hcd_irq); @@ -2347,6 +2355,14 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd, int retval; if (hcd->driver->irq) { + + /* IRQF_DISABLED doesn't work as advertised when used together + * with IRQF_SHARED. As usb_hcd_irq() will always disable + * interrupts we can remove it here. + */ + if (irqflags & IRQF_SHARED) + irqflags &= ~IRQF_DISABLED; + snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); retval = request_irq(irqnum, &usb_hcd_irq, irqflags, diff --git a/drivers/usb/host/ehci-ls1x.c b/drivers/usb/host/ehci-ls1x.c index ca759652626b..aa0f328922df 100644 --- a/drivers/usb/host/ehci-ls1x.c +++ b/drivers/usb/host/ehci-ls1x.c @@ -113,7 +113,7 @@ static int ehci_hcd_ls1x_probe(struct platform_device *pdev) goto err_put_hcd; } - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (ret) goto err_put_hcd; diff --git a/drivers/usb/host/ohci-xls.c b/drivers/usb/host/ohci-xls.c index 84201cd1a472..41e378f17c66 100644 --- a/drivers/usb/host/ohci-xls.c +++ b/drivers/usb/host/ohci-xls.c @@ -56,7 +56,7 @@ static int ohci_xls_probe_internal(const struct hc_driver *driver, goto err3; } - retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) goto err4; return retval; -- cgit v1.2.3 From 3c6bdaeab4fda6c9fdd5f3f5c610dea97bddf7d6 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 18 Sep 2012 12:19:30 -0400 Subject: [SCSI] Add a report opcode helper The REPORT SUPPORTED OPERATION CODES command can be used to query whether a given opcode is supported by a device. Add a helper function that allows us to look up commands. We only issue RSOC if the device reports compliance with SPC-3 or later. But to err on the side of caution we disable the command for ATA, FireWire and USB. Signed-off-by: Martin K. Petersen Acked-by: Mike Snitzer Signed-off-by: James Bottomley --- drivers/ata/libata-scsi.c | 1 + drivers/firewire/sbp2.c | 1 + drivers/scsi/scsi.c | 45 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/storage/scsiglue.c | 3 +++ include/scsi/scsi_device.h | 3 +++ 5 files changed, 53 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index e3bda074fa12..7c2dead60518 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1052,6 +1052,7 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev) { sdev->use_10_for_rw = 1; sdev->use_10_for_ms = 1; + sdev->no_report_opcodes = 1; /* Schedule policy is determined by ->qc_defer() callback and * it needs to see every deferred qc. Set dev_blocked to 1 to diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 1162d6b3bf85..f82e9d4295d0 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -1546,6 +1546,7 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev) struct sbp2_logical_unit *lu = sdev->hostdata; sdev->use_10_for_rw = 1; + sdev->no_report_opcodes = 1; if (sbp2_param_exclusive_login) sdev->manage_start_stop = 1; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 2936b447cae9..2c0d0ec8150b 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -1061,6 +1062,50 @@ int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf, } EXPORT_SYMBOL_GPL(scsi_get_vpd_page); +/** + * scsi_report_opcode - Find out if a given command opcode is supported + * @sdev: scsi device to query + * @buffer: scratch buffer (must be at least 20 bytes long) + * @len: length of buffer + * @opcode: opcode for command to look up + * + * Uses the REPORT SUPPORTED OPERATION CODES to look up the given + * opcode. Returns 0 if RSOC fails or if the command opcode is + * unsupported. Returns 1 if the device claims to support the command. + */ +int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer, + unsigned int len, unsigned char opcode) +{ + unsigned char cmd[16]; + struct scsi_sense_hdr sshdr; + int result; + + if (sdev->no_report_opcodes || sdev->scsi_level < SCSI_SPC_3) + return 0; + + memset(cmd, 0, 16); + cmd[0] = MAINTENANCE_IN; + cmd[1] = MI_REPORT_SUPPORTED_OPERATION_CODES; + cmd[2] = 1; /* One command format */ + cmd[3] = opcode; + put_unaligned_be32(len, &cmd[6]); + memset(buffer, 0, len); + + result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, len, + &sshdr, 30 * HZ, 3, NULL); + + if (result && scsi_sense_valid(&sshdr) && + sshdr.sense_key == ILLEGAL_REQUEST && + (sshdr.asc == 0x20 || sshdr.asc == 0x24) && sshdr.ascq == 0x00) + return 0; + + if ((buffer[1] & 3) == 3) /* Command supported */ + return 1; + + return 0; +} +EXPORT_SYMBOL(scsi_report_opcode); + /** * scsi_device_get - get an additional reference to a scsi_device * @sdev: device to get a reference to diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index a3d54366afcc..6ab376a7c501 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -186,6 +186,9 @@ static int slave_configure(struct scsi_device *sdev) /* Some devices don't handle VPD pages correctly */ sdev->skip_vpd_pages = 1; + /* Do not attempt to use REPORT SUPPORTED OPERATION CODES */ + sdev->no_report_opcodes = 1; + /* Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. * If this device makes that mistake, tell the sd driver. */ diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 88fae8d20154..379d465e8070 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -135,6 +135,7 @@ struct scsi_device { * because we did a bus reset. */ unsigned use_10_for_rw:1; /* first try 10-byte read / write */ unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */ + unsigned no_report_opcodes:1; /* no REPORT SUPPORTED OPERATION CODES */ unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */ unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */ unsigned skip_vpd_pages:1; /* do not read VPD pages */ @@ -362,6 +363,8 @@ extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, struct scsi_sense_hdr *sshdr); extern int scsi_get_vpd_page(struct scsi_device *, u8 page, unsigned char *buf, int buf_len); +extern int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer, + unsigned int len, unsigned char opcode); extern int scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state); extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, -- cgit v1.2.3 From 5db44863b6ebbb400c5e61d56ebe8f21ef48b1bd Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 18 Sep 2012 12:19:32 -0400 Subject: [SCSI] sd: Implement support for WRITE SAME Implement support for WRITE SAME(10) and WRITE SAME(16) in the SCSI disk driver. - We set the default maximum to 0xFFFF because there are several devices out there that only support two-byte block counts even with WRITE SAME(16). We only enable transfers bigger than 0xFFFF if the device explicitly reports MAXIMUM WRITE SAME LENGTH in the BLOCK LIMITS VPD. - max_write_same_blocks can be overriden per-device basis in sysfs. - The UNMAP discovery heuristics remain unchanged but the discard limits are tweaked to match the "real" WRITE SAME commands. - In the error handling logic we now distinguish between WRITE SAME with and without UNMAP set. The discovery process heuristics are: - If the device reports a SCSI level of SPC-3 or greater we'll issue READ SUPPORTED OPERATION CODES to find out whether WRITE SAME(16) is supported. If that's the case we will use it. - If the device supports the block limits VPD and reports a MAXIMUM WRITE SAME LENGTH bigger than 0xFFFF we will use WRITE SAME(16). - Otherwise we will use WRITE SAME(10) unless the target LBA is beyond 0xFFFFFFFF or the block count exceeds 0xFFFF. - no_write_same is set for ATA, FireWire and USB. Signed-off-by: Martin K. Petersen Reviewed-by: Mike Snitzer Reviewed-by: Jeff Garzik Signed-off-by: James Bottomley --- drivers/ata/libata-scsi.c | 1 + drivers/firewire/sbp2.c | 1 + drivers/scsi/scsi_lib.c | 22 ++++-- drivers/scsi/sd.c | 172 ++++++++++++++++++++++++++++++++++++++--- drivers/scsi/sd.h | 7 ++ drivers/usb/storage/scsiglue.c | 3 + include/scsi/scsi_device.h | 1 + 7 files changed, 191 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 7c2dead60518..a6df6a351d6e 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1053,6 +1053,7 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev) sdev->use_10_for_rw = 1; sdev->use_10_for_ms = 1; sdev->no_report_opcodes = 1; + sdev->no_write_same = 1; /* Schedule policy is determined by ->qc_defer() callback and * it needs to see every deferred qc. Set dev_blocked to 1 to diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index f82e9d4295d0..bb1b392f5cda 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -1547,6 +1547,7 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev) sdev->use_10_for_rw = 1; sdev->no_report_opcodes = 1; + sdev->no_write_same = 1; if (sbp2_param_exclusive_login) sdev->manage_start_stop = 1; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index da36a3a81a9e..9032e910bca3 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -900,11 +900,23 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) action = ACTION_FAIL; error = -EILSEQ; /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */ - } else if ((sshdr.asc == 0x20 || sshdr.asc == 0x24) && - (cmd->cmnd[0] == UNMAP || - cmd->cmnd[0] == WRITE_SAME_16 || - cmd->cmnd[0] == WRITE_SAME)) { - description = "Discard failure"; + } else if (sshdr.asc == 0x20 || sshdr.asc == 0x24) { + switch (cmd->cmnd[0]) { + case UNMAP: + description = "Discard failure"; + break; + case WRITE_SAME: + case WRITE_SAME_16: + if (cmd->cmnd[1] & 0x8) + description = "Discard failure"; + else + description = + "Write same failure"; + break; + default: + description = "Invalid command failure"; + break; + } action = ACTION_FAIL; error = -EREMOTEIO; } else diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c6af4c1a9ca3..352bc77b7c88 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -99,6 +99,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); #endif static void sd_config_discard(struct scsi_disk *, unsigned int); +static void sd_config_write_same(struct scsi_disk *); static int sd_revalidate_disk(struct gendisk *); static void sd_unlock_native_capacity(struct gendisk *disk); static int sd_probe(struct device *); @@ -395,6 +396,45 @@ sd_store_max_medium_access_timeouts(struct device *dev, return err ? err : count; } +static ssize_t +sd_show_write_same_blocks(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + + return snprintf(buf, 20, "%u\n", sdkp->max_ws_blocks); +} + +static ssize_t +sd_store_write_same_blocks(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + struct scsi_device *sdp = sdkp->device; + unsigned long max; + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (sdp->type != TYPE_DISK) + return -EINVAL; + + err = kstrtoul(buf, 10, &max); + + if (err) + return err; + + if (max == 0) + sdp->no_write_same = 1; + else if (max <= SD_MAX_WS16_BLOCKS) + sdkp->max_ws_blocks = max; + + sd_config_write_same(sdkp); + + return count; +} + static struct device_attribute sd_disk_attrs[] = { __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, sd_store_cache_type), @@ -410,6 +450,8 @@ static struct device_attribute sd_disk_attrs[] = { __ATTR(thin_provisioning, S_IRUGO, sd_show_thin_provisioning, NULL), __ATTR(provisioning_mode, S_IRUGO|S_IWUSR, sd_show_provisioning_mode, sd_store_provisioning_mode), + __ATTR(max_write_same_blocks, S_IRUGO|S_IWUSR, + sd_show_write_same_blocks, sd_store_write_same_blocks), __ATTR(max_medium_access_timeouts, S_IRUGO|S_IWUSR, sd_show_max_medium_access_timeouts, sd_store_max_medium_access_timeouts), @@ -561,19 +603,23 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) return; case SD_LBP_UNMAP: - max_blocks = min_not_zero(sdkp->max_unmap_blocks, 0xffffffff); + max_blocks = min_not_zero(sdkp->max_unmap_blocks, + (u32)SD_MAX_WS16_BLOCKS); break; case SD_LBP_WS16: - max_blocks = min_not_zero(sdkp->max_ws_blocks, 0xffffffff); + max_blocks = min_not_zero(sdkp->max_ws_blocks, + (u32)SD_MAX_WS16_BLOCKS); break; case SD_LBP_WS10: - max_blocks = min_not_zero(sdkp->max_ws_blocks, (u32)0xffff); + max_blocks = min_not_zero(sdkp->max_ws_blocks, + (u32)SD_MAX_WS10_BLOCKS); break; case SD_LBP_ZERO: - max_blocks = min_not_zero(sdkp->max_ws_blocks, (u32)0xffff); + max_blocks = min_not_zero(sdkp->max_ws_blocks, + (u32)SD_MAX_WS10_BLOCKS); q->limits.discard_zeroes_data = 1; break; } @@ -667,6 +713,83 @@ out: return ret; } +static void sd_config_write_same(struct scsi_disk *sdkp) +{ + struct request_queue *q = sdkp->disk->queue; + unsigned int logical_block_size = sdkp->device->sector_size; + unsigned int blocks = 0; + + if (sdkp->device->no_write_same) { + sdkp->max_ws_blocks = 0; + goto out; + } + + /* Some devices can not handle block counts above 0xffff despite + * supporting WRITE SAME(16). Consequently we default to 64k + * blocks per I/O unless the device explicitly advertises a + * bigger limit. + */ + if (sdkp->max_ws_blocks == 0) + sdkp->max_ws_blocks = SD_MAX_WS10_BLOCKS; + + if (sdkp->ws16 || sdkp->max_ws_blocks > SD_MAX_WS10_BLOCKS) + blocks = min_not_zero(sdkp->max_ws_blocks, + (u32)SD_MAX_WS16_BLOCKS); + else + blocks = min_not_zero(sdkp->max_ws_blocks, + (u32)SD_MAX_WS10_BLOCKS); + +out: + blk_queue_max_write_same_sectors(q, blocks * (logical_block_size >> 9)); +} + +/** + * sd_setup_write_same_cmnd - write the same data to multiple blocks + * @sdp: scsi device to operate one + * @rq: Request to prepare + * + * Will issue either WRITE SAME(10) or WRITE SAME(16) depending on + * preference indicated by target device. + **/ +static int sd_setup_write_same_cmnd(struct scsi_device *sdp, struct request *rq) +{ + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); + struct bio *bio = rq->bio; + sector_t sector = blk_rq_pos(rq); + unsigned int nr_sectors = blk_rq_sectors(rq); + unsigned int nr_bytes = blk_rq_bytes(rq); + int ret; + + if (sdkp->device->no_write_same) + return BLKPREP_KILL; + + BUG_ON(bio_offset(bio) || bio_iovec(bio)->bv_len != sdp->sector_size); + + sector >>= ilog2(sdp->sector_size) - 9; + nr_sectors >>= ilog2(sdp->sector_size) - 9; + + rq->__data_len = sdp->sector_size; + rq->timeout = SD_WRITE_SAME_TIMEOUT; + memset(rq->cmd, 0, rq->cmd_len); + + if (sdkp->ws16 || sector > 0xffffffff || nr_sectors > 0xffff) { + rq->cmd_len = 16; + rq->cmd[0] = WRITE_SAME_16; + put_unaligned_be64(sector, &rq->cmd[2]); + put_unaligned_be32(nr_sectors, &rq->cmd[10]); + } else { + rq->cmd_len = 10; + rq->cmd[0] = WRITE_SAME; + put_unaligned_be32(sector, &rq->cmd[2]); + put_unaligned_be16(nr_sectors, &rq->cmd[7]); + } + + ret = scsi_setup_blk_pc_cmnd(sdp, rq); + rq->__data_len = nr_bytes; + + return ret; +} + static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq) { rq->timeout = SD_FLUSH_TIMEOUT; @@ -712,6 +835,9 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) if (rq->cmd_flags & REQ_DISCARD) { ret = sd_setup_discard_cmnd(sdp, rq); goto out; + } else if (rq->cmd_flags & REQ_WRITE_SAME) { + ret = sd_setup_write_same_cmnd(sdp, rq); + goto out; } else if (rq->cmd_flags & REQ_FLUSH) { ret = scsi_setup_flush_cmnd(sdp, rq); goto out; @@ -1484,8 +1610,9 @@ static int sd_done(struct scsi_cmnd *SCpnt) int sense_valid = 0; int sense_deferred = 0; unsigned char op = SCpnt->cmnd[0]; + unsigned char unmap = SCpnt->cmnd[1] & 8; - if (req->cmd_flags & REQ_DISCARD) { + if (req->cmd_flags & REQ_DISCARD || req->cmd_flags & REQ_WRITE_SAME) { if (!result) { good_bytes = blk_rq_bytes(req); scsi_set_resid(SCpnt, 0); @@ -1542,9 +1669,25 @@ static int sd_done(struct scsi_cmnd *SCpnt) if (sshdr.asc == 0x10) /* DIX: Host detected corruption */ good_bytes = sd_completed_bytes(SCpnt); /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */ - if ((sshdr.asc == 0x20 || sshdr.asc == 0x24) && - (op == UNMAP || op == WRITE_SAME_16 || op == WRITE_SAME)) - sd_config_discard(sdkp, SD_LBP_DISABLE); + if (sshdr.asc == 0x20 || sshdr.asc == 0x24) { + switch (op) { + case UNMAP: + sd_config_discard(sdkp, SD_LBP_DISABLE); + break; + case WRITE_SAME_16: + case WRITE_SAME: + if (unmap) + sd_config_discard(sdkp, SD_LBP_DISABLE); + else { + sdkp->device->no_write_same = 1; + sd_config_write_same(sdkp); + + good_bytes = 0; + req->__data_len = blk_rq_bytes(req); + req->cmd_flags |= REQ_QUIET; + } + } + } break; default: break; @@ -2380,9 +2523,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) if (buffer[3] == 0x3c) { unsigned int lba_count, desc_count; - sdkp->max_ws_blocks = - (u32) min_not_zero(get_unaligned_be64(&buffer[36]), - (u64)0xffffffff); + sdkp->max_ws_blocks = (u32)get_unaligned_be64(&buffer[36]); if (!sdkp->lbpme) goto out; @@ -2475,6 +2616,13 @@ static void sd_read_block_provisioning(struct scsi_disk *sdkp) kfree(buffer); } +static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer) +{ + if (scsi_report_opcode(sdkp->device, buffer, SD_BUF_SIZE, + WRITE_SAME_16)) + sdkp->ws16 = 1; +} + static int sd_try_extended_inquiry(struct scsi_device *sdp) { /* @@ -2534,6 +2682,7 @@ static int sd_revalidate_disk(struct gendisk *disk) sd_read_write_protect_flag(sdkp, buffer); sd_read_cache_type(sdkp, buffer); sd_read_app_tag_own(sdkp, buffer); + sd_read_write_same(sdkp, buffer); } sdkp->first_scan = 0; @@ -2551,6 +2700,7 @@ static int sd_revalidate_disk(struct gendisk *disk) blk_queue_flush(sdkp->disk->queue, flush); set_capacity(disk, sdkp->capacity); + sd_config_write_same(sdkp); kfree(buffer); out: diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 47c52a6d733c..74a1e4ca5401 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -14,6 +14,7 @@ #define SD_TIMEOUT (30 * HZ) #define SD_MOD_TIMEOUT (75 * HZ) #define SD_FLUSH_TIMEOUT (60 * HZ) +#define SD_WRITE_SAME_TIMEOUT (120 * HZ) /* * Number of allowed retries @@ -38,6 +39,11 @@ enum { SD_MEMPOOL_SIZE = 2, /* CDB pool size */ }; +enum { + SD_MAX_WS10_BLOCKS = 0xffff, + SD_MAX_WS16_BLOCKS = 0x7fffff, +}; + enum { SD_LBP_FULL = 0, /* Full logical block provisioning */ SD_LBP_UNMAP, /* Use UNMAP command */ @@ -77,6 +83,7 @@ struct scsi_disk { unsigned lbpws : 1; unsigned lbpws10 : 1; unsigned lbpvpd : 1; + unsigned ws16 : 1; }; #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev) diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 6ab376a7c501..92f35abee92d 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -189,6 +189,9 @@ static int slave_configure(struct scsi_device *sdev) /* Do not attempt to use REPORT SUPPORTED OPERATION CODES */ sdev->no_report_opcodes = 1; + /* Do not attempt to use WRITE SAME */ + sdev->no_write_same = 1; + /* Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. * If this device makes that mistake, tell the sd driver. */ diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 379d465e8070..55367b04dc94 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -136,6 +136,7 @@ struct scsi_device { unsigned use_10_for_rw:1; /* first try 10-byte read / write */ unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */ unsigned no_report_opcodes:1; /* no REPORT SUPPORTED OPERATION CODES */ + unsigned no_write_same:1; /* no WRITE SAME command */ unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */ unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */ unsigned skip_vpd_pages:1; /* do not read VPD pages */ -- cgit v1.2.3