diff options
Diffstat (limited to 'drivers/usb')
105 files changed, 4192 insertions, 2243 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 2e6b832e004b..e0cad4418085 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -2,10 +2,6 @@ # USB device configuration # -# These are unused now, remove them once they are no longer selected -config USB_ARCH_HAS_OHCI - bool - config USB_OHCI_BIG_ENDIAN_DESC bool @@ -17,18 +13,12 @@ config USB_OHCI_LITTLE_ENDIAN default n if STB03xxx || PPC_MPC52xx default y -config USB_ARCH_HAS_EHCI - bool - config USB_EHCI_BIG_ENDIAN_MMIO bool config USB_EHCI_BIG_ENDIAN_DESC bool -config USB_ARCH_HAS_XHCI - bool - menuconfig USB_SUPPORT bool "USB support" depends on HAS_IOMEM diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 7345d2115af2..480bd4d5710a 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -10,6 +10,7 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o # Glue/Bridge layers go here obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o +obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o # PCI doesn't provide stubs, need to check ifneq ($(CONFIG_PCI),) diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index a85713165688..83d06c1455b7 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h @@ -50,12 +50,14 @@ #define PORTSC_PTC (0x0FUL << 16) #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) /* PTS and PTW for non lpm version only */ +#define PORTSC_PFSC BIT(24) #define PORTSC_PTS(d) \ (u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0)) #define PORTSC_PTW BIT(28) #define PORTSC_STS BIT(29) /* DEVLC */ +#define DEVLC_PFSC BIT(23) #define DEVLC_PSPD (0x03UL << 25) #define DEVLC_PSPD_HS (0x02UL << 25) #define DEVLC_PTW BIT(27) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 88b80f7728e4..e206406ae1d9 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -196,8 +196,6 @@ struct ci_hdrc { struct ci_hdrc_platform_data *platdata; int vbus_active; - /* FIXME: some day, we'll not use global phy */ - bool global_phy; struct usb_phy *transceiver; struct usb_hcd *hcd; struct dentry *debugfs; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index c00f77257d36..2e58f8dfd311 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -96,7 +96,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) { struct ci_hdrc_imx_data *data; struct ci_hdrc_platform_data pdata = { - .name = "ci_hdrc_imx", + .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_REQUIRE_TRANSCEIVER | CI_HDRC_DISABLE_STREAMING, diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c new file mode 100644 index 000000000000..3bf6489ef5ec --- /dev/null +++ b/drivers/usb/chipidea/ci_hdrc_zevio.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * Based off drivers/usb/chipidea/ci_hdrc_msm.c + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/usb/gadget.h> +#include <linux/usb/chipidea.h> + +#include "ci.h" + +static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = { + .name = "ci_hdrc_zevio", + .flags = CI_HDRC_REGS_SHARED, + .capoffset = DEF_CAPOFFSET, +}; + +static int ci_hdrc_zevio_probe(struct platform_device *pdev) +{ + struct platform_device *ci_pdev; + + dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n"); + + ci_pdev = ci_hdrc_add_device(&pdev->dev, + pdev->resource, pdev->num_resources, + &ci_hdrc_zevio_platdata); + + if (IS_ERR(ci_pdev)) { + dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n"); + return PTR_ERR(ci_pdev); + } + + platform_set_drvdata(pdev, ci_pdev); + + return 0; +} + +static int ci_hdrc_zevio_remove(struct platform_device *pdev) +{ + struct platform_device *ci_pdev = platform_get_drvdata(pdev); + + ci_hdrc_remove_device(ci_pdev); + + return 0; +} + +static const struct of_device_id ci_hdrc_zevio_dt_ids[] = { + { .compatible = "lsi,zevio-usb", }, + { /* sentinel */ } +}; + +static struct platform_driver ci_hdrc_zevio_driver = { + .probe = ci_hdrc_zevio_probe, + .remove = ci_hdrc_zevio_remove, + .driver = { + .name = "zevio_usb", + .owner = THIS_MODULE, + .of_match_table = ci_hdrc_zevio_dt_ids, + }, +}; + +MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids); +module_platform_driver(ci_hdrc_zevio_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 33f22bc6ad7f..ca6831c5b763 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -64,6 +64,7 @@ #include <linux/usb/otg.h> #include <linux/usb/chipidea.h> #include <linux/usb/of.h> +#include <linux/of.h> #include <linux/phy.h> #include <linux/regulator/consumer.h> @@ -298,6 +299,13 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode) if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { + if (ci->hw_bank.lpm) + hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); + else + hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); + } + /* USBMODE should be configured step by step */ hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); hw_write(ci, OP_USBMODE, USBMODE_CM, mode); @@ -412,6 +420,9 @@ static int ci_get_platdata(struct device *dev, } } + if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) + platdata->flags |= CI_HDRC_FORCE_FULLSPEED; + return 0; } @@ -496,33 +507,6 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) } } -static int ci_usb_phy_init(struct ci_hdrc *ci) -{ - if (ci->platdata->phy) { - ci->transceiver = ci->platdata->phy; - return usb_phy_init(ci->transceiver); - } else { - ci->global_phy = true; - ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (IS_ERR(ci->transceiver)) - ci->transceiver = NULL; - - return 0; - } -} - -static void ci_usb_phy_destroy(struct ci_hdrc *ci) -{ - if (!ci->transceiver) - return; - - otg_set_peripheral(ci->transceiver->otg, NULL); - if (ci->global_phy) - usb_put_phy(ci->transceiver); - else - usb_phy_shutdown(ci->transceiver); -} - static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -532,7 +516,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) int ret; enum usb_dr_mode dr_mode; - if (!dev->platform_data) { + if (!dev_get_platdata(dev)) { dev_err(dev, "platform data missing\n"); return -ENODEV; } @@ -549,7 +533,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) } ci->dev = dev; - ci->platdata = dev->platform_data; + ci->platdata = dev_get_platdata(dev); ci->imx28_write_fix = !!(ci->platdata->flags & CI_HDRC_IMX28_WRITE_FIX); @@ -561,7 +545,26 @@ static int ci_hdrc_probe(struct platform_device *pdev) hw_phymode_configure(ci); - ret = ci_usb_phy_init(ci); + if (ci->platdata->phy) + ci->transceiver = ci->platdata->phy; + else + ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + + if (IS_ERR(ci->transceiver)) { + ret = PTR_ERR(ci->transceiver); + /* + * if -ENXIO is returned, it means PHY layer wasn't + * enabled, so it makes no sense to return -EPROBE_DEFER + * in that case, since no PHY driver will ever probe. + */ + if (ret == -ENXIO) + return ret; + + dev_err(dev, "no usb2 phy configured\n"); + return -EPROBE_DEFER; + } + + ret = usb_phy_init(ci->transceiver); if (ret) { dev_err(dev, "unable to init phy: %d\n", ret); return ret; @@ -572,8 +575,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci->irq = platform_get_irq(pdev, 0); if (ci->irq < 0) { dev_err(dev, "missing IRQ\n"); - ret = -ENODEV; - goto destroy_phy; + ret = ci->irq; + goto deinit_phy; } ci_get_otg_capable(ci); @@ -590,23 +593,12 @@ static int ci_hdrc_probe(struct platform_device *pdev) ret = ci_hdrc_gadget_init(ci); if (ret) dev_info(dev, "doesn't support gadget\n"); - if (!ret && ci->transceiver) { - ret = otg_set_peripheral(ci->transceiver->otg, - &ci->gadget); - /* - * If we implement all USB functions using chipidea drivers, - * it doesn't need to call above API, meanwhile, if we only - * use gadget function, calling above API is useless. - */ - if (ret && ret != -ENOTSUPP) - goto destroy_phy; - } } if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { dev_err(dev, "no supported roles\n"); ret = -ENODEV; - goto destroy_phy; + goto deinit_phy; } if (ci->is_otg) { @@ -663,8 +655,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) free_irq(ci->irq, ci); stop: ci_role_destroy(ci); -destroy_phy: - ci_usb_phy_destroy(ci); +deinit_phy: + usb_phy_shutdown(ci->transceiver); return ret; } @@ -677,7 +669,8 @@ static int ci_hdrc_remove(struct platform_device *pdev) free_irq(ci->irq, ci); ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); - ci_usb_phy_destroy(ci); + usb_phy_shutdown(ci->transceiver); + kfree(ci->hw_bank.regmap); return 0; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4ab2cb62dfce..7739c64ef259 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -178,19 +178,6 @@ static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir) } /** - * hw_test_and_clear_setup_status: test & clear setup status (execute without - * interruption) - * @n: endpoint number - * - * This function returns setup status - */ -static int hw_test_and_clear_setup_status(struct ci_hdrc *ci, int n) -{ - n = ep_to_bit(ci, n); - return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n)); -} - -/** * hw_ep_prime: primes endpoint (execute without interruption) * @num: endpoint number * @dir: endpoint direction @@ -962,6 +949,156 @@ __acquires(hwep->lock) } /** + * isr_setup_packet_handler: setup packet handler + * @ci: UDC descriptor + * + * This function handles setup packet + */ +static void isr_setup_packet_handler(struct ci_hdrc *ci) +__releases(ci->lock) +__acquires(ci->lock) +{ + struct ci_hw_ep *hwep = &ci->ci_hw_ep[0]; + struct usb_ctrlrequest req; + int type, num, dir, err = -EINVAL; + u8 tmode = 0; + + /* + * Flush data and handshake transactions of previous + * setup packet. + */ + _ep_nuke(ci->ep0out); + _ep_nuke(ci->ep0in); + + /* read_setup_packet */ + do { + hw_test_and_set_setup_guard(ci); + memcpy(&req, &hwep->qh.ptr->setup, sizeof(req)); + } while (!hw_test_and_clear_setup_guard(ci)); + + type = req.bRequestType; + + ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; + + switch (req.bRequest) { + case USB_REQ_CLEAR_FEATURE: + if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) == + USB_ENDPOINT_HALT) { + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; + num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += ci->hw_ep_max / 2; + if (!ci->ci_hw_ep[num].wedge) { + spin_unlock(&ci->lock); + err = usb_ep_clear_halt( + &ci->ci_hw_ep[num].ep); + spin_lock(&ci->lock); + if (err) + break; + } + err = isr_setup_status_phase(ci); + } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && + le16_to_cpu(req.wValue) == + USB_DEVICE_REMOTE_WAKEUP) { + if (req.wLength != 0) + break; + ci->remote_wakeup = 0; + err = isr_setup_status_phase(ci); + } else { + goto delegate; + } + break; + case USB_REQ_GET_STATUS: + if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && + type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && + type != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 2 || + le16_to_cpu(req.wValue) != 0) + break; + err = isr_get_status_response(ci, &req); + break; + case USB_REQ_SET_ADDRESS: + if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 0 || + le16_to_cpu(req.wIndex) != 0) + break; + ci->address = (u8)le16_to_cpu(req.wValue); + ci->setaddr = true; + err = isr_setup_status_phase(ci); + break; + case USB_REQ_SET_FEATURE: + if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) == + USB_ENDPOINT_HALT) { + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; + num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += ci->hw_ep_max / 2; + + spin_unlock(&ci->lock); + err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); + spin_lock(&ci->lock); + if (!err) + isr_setup_status_phase(ci); + } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { + if (req.wLength != 0) + break; + switch (le16_to_cpu(req.wValue)) { + case USB_DEVICE_REMOTE_WAKEUP: + ci->remote_wakeup = 1; + err = isr_setup_status_phase(ci); + break; + case USB_DEVICE_TEST_MODE: + tmode = le16_to_cpu(req.wIndex) >> 8; + switch (tmode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + ci->test_mode = tmode; + err = isr_setup_status_phase( + ci); + break; + default: + break; + } + default: + goto delegate; + } + } else { + goto delegate; + } + break; + default: +delegate: + if (req.wLength == 0) /* no data phase */ + ci->ep0_dir = TX; + + spin_unlock(&ci->lock); + err = ci->driver->setup(&ci->gadget, &req); + spin_lock(&ci->lock); + break; + } + + if (err < 0) { + spin_unlock(&ci->lock); + if (usb_ep_set_halt(&hwep->ep)) + dev_err(ci->dev, "error: ep_set_halt\n"); + spin_lock(&ci->lock); + } +} + +/** * isr_tr_complete_handler: transaction complete interrupt handler * @ci: UDC descriptor * @@ -972,12 +1109,10 @@ __releases(ci->lock) __acquires(ci->lock) { unsigned i; - u8 tmode = 0; + int err; for (i = 0; i < ci->hw_ep_max; i++) { struct ci_hw_ep *hwep = &ci->ci_hw_ep[i]; - int type, num, dir, err = -EINVAL; - struct usb_ctrlrequest req; if (hwep->ep.desc == NULL) continue; /* not configured */ @@ -997,148 +1132,10 @@ __acquires(ci->lock) } } - if (hwep->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(ci, i)) - continue; - - if (i != 0) { - dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i); - continue; - } - - /* - * Flush data and handshake transactions of previous - * setup packet. - */ - _ep_nuke(ci->ep0out); - _ep_nuke(ci->ep0in); - - /* read_setup_packet */ - do { - hw_test_and_set_setup_guard(ci); - memcpy(&req, &hwep->qh.ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard(ci)); - - type = req.bRequestType; - - ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; - - switch (req.bRequest) { - case USB_REQ_CLEAR_FEATURE: - if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) == - USB_ENDPOINT_HALT) { - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - dir = num & USB_ENDPOINT_DIR_MASK; - num &= USB_ENDPOINT_NUMBER_MASK; - if (dir) /* TX */ - num += ci->hw_ep_max/2; - if (!ci->ci_hw_ep[num].wedge) { - spin_unlock(&ci->lock); - err = usb_ep_clear_halt( - &ci->ci_hw_ep[num].ep); - spin_lock(&ci->lock); - if (err) - break; - } - err = isr_setup_status_phase(ci); - } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && - le16_to_cpu(req.wValue) == - USB_DEVICE_REMOTE_WAKEUP) { - if (req.wLength != 0) - break; - ci->remote_wakeup = 0; - err = isr_setup_status_phase(ci); - } else { - goto delegate; - } - break; - case USB_REQ_GET_STATUS: - if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && - type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && - type != (USB_DIR_IN|USB_RECIP_INTERFACE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 2 || - le16_to_cpu(req.wValue) != 0) - break; - err = isr_get_status_response(ci, &req); - break; - case USB_REQ_SET_ADDRESS: - if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 0 || - le16_to_cpu(req.wIndex) != 0) - break; - ci->address = (u8)le16_to_cpu(req.wValue); - ci->setaddr = true; - err = isr_setup_status_phase(ci); - break; - case USB_REQ_SET_FEATURE: - if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) == - USB_ENDPOINT_HALT) { - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - dir = num & USB_ENDPOINT_DIR_MASK; - num &= USB_ENDPOINT_NUMBER_MASK; - if (dir) /* TX */ - num += ci->hw_ep_max/2; - - spin_unlock(&ci->lock); - err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); - spin_lock(&ci->lock); - if (!err) - isr_setup_status_phase(ci); - } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { - if (req.wLength != 0) - break; - switch (le16_to_cpu(req.wValue)) { - case USB_DEVICE_REMOTE_WAKEUP: - ci->remote_wakeup = 1; - err = isr_setup_status_phase(ci); - break; - case USB_DEVICE_TEST_MODE: - tmode = le16_to_cpu(req.wIndex) >> 8; - switch (tmode) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: - ci->test_mode = tmode; - err = isr_setup_status_phase( - ci); - break; - default: - break; - } - default: - goto delegate; - } - } else { - goto delegate; - } - break; - default: -delegate: - if (req.wLength == 0) /* no data phase */ - ci->ep0_dir = TX; - - spin_unlock(&ci->lock); - err = ci->driver->setup(&ci->gadget, &req); - spin_lock(&ci->lock); - break; - } - - if (err < 0) { - spin_unlock(&ci->lock); - if (usb_ep_set_halt(&hwep->ep)) - dev_err(ci->dev, "error: ep_set_halt\n"); - spin_lock(&ci->lock); - } + /* Only handle setup packet below */ + if (i == 0 && + hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0))) + isr_setup_packet_handler(ci); } } @@ -1193,6 +1190,11 @@ static int ep_enable(struct usb_ep *ep, hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */ + if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) { + dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n"); + retval = -EINVAL; + } + /* * Enable endpoints in the HW other than ep0 as ep0 * is always enabled @@ -1837,12 +1839,6 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) dma_pool_destroy(ci->td_pool); dma_pool_destroy(ci->qh_pool); - - if (ci->transceiver) { - otg_set_peripheral(ci->transceiver->otg, NULL); - if (ci->global_phy) - usb_put_phy(ci->transceiver); - } } static int udc_id_switch_for_device(struct ci_hdrc *ci) diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index fed7f68d025d..cb8e99156f5a 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -10,7 +10,6 @@ config USB_DEBUG config USB_ANNOUNCE_NEW_DEVICES bool "USB announce new devices" - default N help Say Y here if you want the USB core to always announce the idVendor, idProduct, Manufacturer, Product, and SerialNumber diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 062967c90b2a..1ab4df1de2da 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -10,7 +10,6 @@ #define USB_MAXALTSETTING 128 /* Hard limit */ -#define USB_MAXENDPOINTS 30 /* Hard limit */ #define USB_MAXCONFIG 8 /* Arbitrary limit */ diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 90e18f6fa2bb..257876ea03a1 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -62,7 +62,7 @@ /* Mutual exclusion for removal, open, and release */ DEFINE_MUTEX(usbfs_mutex); -struct dev_state { +struct usb_dev_state { struct list_head list; /* state list */ struct usb_device *dev; struct file *file; @@ -81,7 +81,7 @@ struct dev_state { struct async { struct list_head asynclist; - struct dev_state *ps; + struct usb_dev_state *ps; struct pid *pid; const struct cred *cred; unsigned int signr; @@ -151,7 +151,7 @@ static void usbfs_decrease_memory_usage(unsigned amount) atomic_sub(amount, &usbfs_memory_usage); } -static int connected(struct dev_state *ps) +static int connected(struct usb_dev_state *ps) { return (!list_empty(&ps->list) && ps->dev->state != USB_STATE_NOTATTACHED); @@ -184,7 +184,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - struct dev_state *ps = file->private_data; + struct usb_dev_state *ps = file->private_data; struct usb_device *dev = ps->dev; ssize_t ret = 0; unsigned len; @@ -307,7 +307,7 @@ static void free_async(struct async *as) static void async_newpending(struct async *as) { - struct dev_state *ps = as->ps; + struct usb_dev_state *ps = as->ps; unsigned long flags; spin_lock_irqsave(&ps->lock, flags); @@ -317,7 +317,7 @@ static void async_newpending(struct async *as) static void async_removepending(struct async *as) { - struct dev_state *ps = as->ps; + struct usb_dev_state *ps = as->ps; unsigned long flags; spin_lock_irqsave(&ps->lock, flags); @@ -325,7 +325,7 @@ static void async_removepending(struct async *as) spin_unlock_irqrestore(&ps->lock, flags); } -static struct async *async_getcompleted(struct dev_state *ps) +static struct async *async_getcompleted(struct usb_dev_state *ps) { unsigned long flags; struct async *as = NULL; @@ -340,7 +340,7 @@ static struct async *async_getcompleted(struct dev_state *ps) return as; } -static struct async *async_getpending(struct dev_state *ps, +static struct async *async_getpending(struct usb_dev_state *ps, void __user *userurb) { struct async *as; @@ -448,7 +448,7 @@ static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb) #define AS_CONTINUATION 1 #define AS_UNLINK 2 -static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr) +static void cancel_bulk_urbs(struct usb_dev_state *ps, unsigned bulk_addr) __releases(ps->lock) __acquires(ps->lock) { @@ -489,7 +489,7 @@ __acquires(ps->lock) static void async_completed(struct urb *urb) { struct async *as = urb->context; - struct dev_state *ps = as->ps; + struct usb_dev_state *ps = as->ps; struct siginfo sinfo; struct pid *pid = NULL; u32 secid = 0; @@ -529,7 +529,7 @@ static void async_completed(struct urb *urb) wake_up(&ps->wait); } -static void destroy_async(struct dev_state *ps, struct list_head *list) +static void destroy_async(struct usb_dev_state *ps, struct list_head *list) { struct urb *urb; struct async *as; @@ -551,7 +551,7 @@ static void destroy_async(struct dev_state *ps, struct list_head *list) spin_unlock_irqrestore(&ps->lock, flags); } -static void destroy_async_on_interface(struct dev_state *ps, +static void destroy_async_on_interface(struct usb_dev_state *ps, unsigned int ifnum) { struct list_head *p, *q, hitlist; @@ -566,7 +566,7 @@ static void destroy_async_on_interface(struct dev_state *ps, destroy_async(ps, &hitlist); } -static void destroy_all_async(struct dev_state *ps) +static void destroy_all_async(struct usb_dev_state *ps) { destroy_async(ps, &ps->async_pending); } @@ -585,7 +585,7 @@ static int driver_probe(struct usb_interface *intf, static void driver_disconnect(struct usb_interface *intf) { - struct dev_state *ps = usb_get_intfdata(intf); + struct usb_dev_state *ps = usb_get_intfdata(intf); unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber; if (!ps) @@ -628,7 +628,7 @@ struct usb_driver usbfs_driver = { .resume = driver_resume, }; -static int claimintf(struct dev_state *ps, unsigned int ifnum) +static int claimintf(struct usb_dev_state *ps, unsigned int ifnum) { struct usb_device *dev = ps->dev; struct usb_interface *intf; @@ -650,7 +650,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum) return err; } -static int releaseintf(struct dev_state *ps, unsigned int ifnum) +static int releaseintf(struct usb_dev_state *ps, unsigned int ifnum) { struct usb_device *dev; struct usb_interface *intf; @@ -670,7 +670,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum) return err; } -static int checkintf(struct dev_state *ps, unsigned int ifnum) +static int checkintf(struct usb_dev_state *ps, unsigned int ifnum) { if (ps->dev->state != USB_STATE_CONFIGURED) return -EHOSTUNREACH; @@ -710,7 +710,7 @@ static int findintfep(struct usb_device *dev, unsigned int ep) return -ENOENT; } -static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, +static int check_ctrlrecip(struct usb_dev_state *ps, unsigned int requesttype, unsigned int request, unsigned int index) { int ret = 0; @@ -769,6 +769,88 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, return ret; } +static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev, + unsigned char ep) +{ + if (ep & USB_ENDPOINT_DIR_MASK) + return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK]; + else + return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK]; +} + +static int parse_usbdevfs_streams(struct usb_dev_state *ps, + struct usbdevfs_streams __user *streams, + unsigned int *num_streams_ret, + unsigned int *num_eps_ret, + struct usb_host_endpoint ***eps_ret, + struct usb_interface **intf_ret) +{ + unsigned int i, num_streams, num_eps; + struct usb_host_endpoint **eps; + struct usb_interface *intf = NULL; + unsigned char ep; + int ifnum, ret; + + if (get_user(num_streams, &streams->num_streams) || + get_user(num_eps, &streams->num_eps)) + return -EFAULT; + + if (num_eps < 1 || num_eps > USB_MAXENDPOINTS) + return -EINVAL; + + /* The XHCI controller allows max 2 ^ 16 streams */ + if (num_streams_ret && (num_streams < 2 || num_streams > 65536)) + return -EINVAL; + + eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL); + if (!eps) + return -ENOMEM; + + for (i = 0; i < num_eps; i++) { + if (get_user(ep, &streams->eps[i])) { + ret = -EFAULT; + goto error; + } + eps[i] = ep_to_host_endpoint(ps->dev, ep); + if (!eps[i]) { + ret = -EINVAL; + goto error; + } + + /* usb_alloc/free_streams operate on an usb_interface */ + ifnum = findintfep(ps->dev, ep); + if (ifnum < 0) { + ret = ifnum; + goto error; + } + + if (i == 0) { + ret = checkintf(ps, ifnum); + if (ret < 0) + goto error; + intf = usb_ifnum_to_if(ps->dev, ifnum); + } else { + /* Verify all eps belong to the same interface */ + if (ifnum != intf->altsetting->desc.bInterfaceNumber) { + ret = -EINVAL; + goto error; + } + } + } + + if (num_streams_ret) + *num_streams_ret = num_streams; + *num_eps_ret = num_eps; + *eps_ret = eps; + *intf_ret = intf; + + return 0; + +error: + kfree(eps); + return ret; +} + static int match_devt(struct device *dev, void *data) { return dev->devt == (dev_t) (unsigned long) data; @@ -791,11 +873,11 @@ static struct usb_device *usbdev_lookup_by_devt(dev_t devt) static int usbdev_open(struct inode *inode, struct file *file) { struct usb_device *dev = NULL; - struct dev_state *ps; + struct usb_dev_state *ps; int ret; ret = -ENOMEM; - ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL); + ps = kmalloc(sizeof(struct usb_dev_state), GFP_KERNEL); if (!ps) goto out_free_ps; @@ -852,7 +934,7 @@ static int usbdev_open(struct inode *inode, struct file *file) static int usbdev_release(struct inode *inode, struct file *file) { - struct dev_state *ps = file->private_data; + struct usb_dev_state *ps = file->private_data; struct usb_device *dev = ps->dev; unsigned int ifnum; struct async *as; @@ -883,7 +965,7 @@ static int usbdev_release(struct inode *inode, struct file *file) return 0; } -static int proc_control(struct dev_state *ps, void __user *arg) +static int proc_control(struct usb_dev_state *ps, void __user *arg) { struct usb_device *dev = ps->dev; struct usbdevfs_ctrltransfer ctrl; @@ -970,7 +1052,7 @@ static int proc_control(struct dev_state *ps, void __user *arg) return ret; } -static int proc_bulk(struct dev_state *ps, void __user *arg) +static int proc_bulk(struct usb_dev_state *ps, void __user *arg) { struct usb_device *dev = ps->dev; struct usbdevfs_bulktransfer bulk; @@ -1043,7 +1125,21 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return ret; } -static int proc_resetep(struct dev_state *ps, void __user *arg) +static void check_reset_of_active_ep(struct usb_device *udev, + unsigned int epnum, char *ioctl_name) +{ + struct usb_host_endpoint **eps; + struct usb_host_endpoint *ep; + + eps = (epnum & USB_DIR_IN) ? udev->ep_in : udev->ep_out; + ep = eps[epnum & 0x0f]; + if (ep && !list_empty(&ep->urb_list)) + dev_warn(&udev->dev, "Process %d (%s) called USBDEVFS_%s for active endpoint 0x%02x\n", + task_pid_nr(current), current->comm, + ioctl_name, epnum); +} + +static int proc_resetep(struct usb_dev_state *ps, void __user *arg) { unsigned int ep; int ret; @@ -1056,11 +1152,12 @@ static int proc_resetep(struct dev_state *ps, void __user *arg) ret = checkintf(ps, ret); if (ret) return ret; + check_reset_of_active_ep(ps->dev, ep, "RESETEP"); usb_reset_endpoint(ps->dev, ep); return 0; } -static int proc_clearhalt(struct dev_state *ps, void __user *arg) +static int proc_clearhalt(struct usb_dev_state *ps, void __user *arg) { unsigned int ep; int pipe; @@ -1074,6 +1171,7 @@ static int proc_clearhalt(struct dev_state *ps, void __user *arg) ret = checkintf(ps, ret); if (ret) return ret; + check_reset_of_active_ep(ps->dev, ep, "CLEAR_HALT"); if (ep & USB_DIR_IN) pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f); else @@ -1082,7 +1180,7 @@ static int proc_clearhalt(struct dev_state *ps, void __user *arg) return usb_clear_halt(ps->dev, pipe); } -static int proc_getdriver(struct dev_state *ps, void __user *arg) +static int proc_getdriver(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_getdriver gd; struct usb_interface *intf; @@ -1101,7 +1199,7 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg) return ret; } -static int proc_connectinfo(struct dev_state *ps, void __user *arg) +static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_connectinfo ci = { .devnum = ps->dev->devnum, @@ -1113,12 +1211,12 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) return 0; } -static int proc_resetdevice(struct dev_state *ps) +static int proc_resetdevice(struct usb_dev_state *ps) { return usb_reset_device(ps->dev); } -static int proc_setintf(struct dev_state *ps, void __user *arg) +static int proc_setintf(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_setinterface setintf; int ret; @@ -1127,11 +1225,14 @@ static int proc_setintf(struct dev_state *ps, void __user *arg) return -EFAULT; if ((ret = checkintf(ps, setintf.interface))) return ret; + + destroy_async_on_interface(ps, setintf.interface); + return usb_set_interface(ps->dev, setintf.interface, setintf.altsetting); } -static int proc_setconfig(struct dev_state *ps, void __user *arg) +static int proc_setconfig(struct usb_dev_state *ps, void __user *arg) { int u; int status = 0; @@ -1179,7 +1280,7 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg) return status; } -static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, +static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb, struct usbdevfs_iso_packet_desc __user *iso_frame_desc, void __user *arg) { @@ -1189,6 +1290,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; int i, ret, is_in, num_sgs = 0, ifnum = -1; + int number_of_packets = 0; + unsigned int stream_id = 0; void *buf; if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | @@ -1209,15 +1312,10 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, if (ret) return ret; } - if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) { - is_in = 1; - ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; - } else { - is_in = 0; - ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; - } + ep = ep_to_host_endpoint(ps->dev, uurb->endpoint); if (!ep) return -ENOENT; + is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0; u = 0; switch(uurb->type) { @@ -1242,7 +1340,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, le16_to_cpup(&dr->wIndex)); if (ret) goto error; - uurb->number_of_packets = 0; uurb->buffer_length = le16_to_cpup(&dr->wLength); uurb->buffer += 8; if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) { @@ -1272,17 +1369,17 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, uurb->type = USBDEVFS_URB_TYPE_INTERRUPT; goto interrupt_urb; } - uurb->number_of_packets = 0; num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) num_sgs = 0; + if (ep->streams) + stream_id = uurb->stream_id; break; case USBDEVFS_URB_TYPE_INTERRUPT: if (!usb_endpoint_xfer_int(&ep->desc)) return -EINVAL; interrupt_urb: - uurb->number_of_packets = 0; break; case USBDEVFS_URB_TYPE_ISO: @@ -1292,15 +1389,16 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (!usb_endpoint_xfer_isoc(&ep->desc)) return -EINVAL; + number_of_packets = uurb->number_of_packets; isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * - uurb->number_of_packets; + number_of_packets; if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) return -ENOMEM; if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) { ret = -EFAULT; goto error; } - for (totlen = u = 0; u < uurb->number_of_packets; u++) { + for (totlen = u = 0; u < number_of_packets; u++) { /* * arbitrary limit need for USB 3.0 * bMaxBurst (0~15 allowed, 1~16 packets) @@ -1331,7 +1429,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ret = -EFAULT; goto error; } - as = alloc_async(uurb->number_of_packets); + as = alloc_async(number_of_packets); if (!as) { ret = -ENOMEM; goto error; @@ -1425,7 +1523,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->urb->setup_packet = (unsigned char *)dr; dr = NULL; as->urb->start_frame = uurb->start_frame; - as->urb->number_of_packets = uurb->number_of_packets; + as->urb->number_of_packets = number_of_packets; + as->urb->stream_id = stream_id; if (uurb->type == USBDEVFS_URB_TYPE_ISO || ps->dev->speed == USB_SPEED_HIGH) as->urb->interval = 1 << min(15, ep->desc.bInterval - 1); @@ -1433,7 +1532,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->urb->interval = ep->desc.bInterval; as->urb->context = as; as->urb->complete = async_completed; - for (totlen = u = 0; u < uurb->number_of_packets; u++) { + for (totlen = u = 0; u < number_of_packets; u++) { as->urb->iso_frame_desc[u].offset = totlen; as->urb->iso_frame_desc[u].length = isopkt[u].length; totlen += isopkt[u].length; @@ -1508,7 +1607,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return ret; } -static int proc_submiturb(struct dev_state *ps, void __user *arg) +static int proc_submiturb(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_urb uurb; @@ -1520,7 +1619,7 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) arg); } -static int proc_unlinkurb(struct dev_state *ps, void __user *arg) +static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg) { struct urb *urb; struct async *as; @@ -1580,7 +1679,7 @@ err_out: return -EFAULT; } -static struct async *reap_as(struct dev_state *ps) +static struct async *reap_as(struct usb_dev_state *ps) { DECLARE_WAITQUEUE(wait, current); struct async *as = NULL; @@ -1603,7 +1702,7 @@ static struct async *reap_as(struct dev_state *ps) return as; } -static int proc_reapurb(struct dev_state *ps, void __user *arg) +static int proc_reapurb(struct usb_dev_state *ps, void __user *arg) { struct async *as = reap_as(ps); if (as) { @@ -1616,7 +1715,7 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) return -EIO; } -static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg) +static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg) { int retval; struct async *as; @@ -1631,7 +1730,7 @@ static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg) } #ifdef CONFIG_COMPAT -static int proc_control_compat(struct dev_state *ps, +static int proc_control_compat(struct usb_dev_state *ps, struct usbdevfs_ctrltransfer32 __user *p32) { struct usbdevfs_ctrltransfer __user *p; @@ -1644,7 +1743,7 @@ static int proc_control_compat(struct dev_state *ps, return proc_control(ps, p); } -static int proc_bulk_compat(struct dev_state *ps, +static int proc_bulk_compat(struct usb_dev_state *ps, struct usbdevfs_bulktransfer32 __user *p32) { struct usbdevfs_bulktransfer __user *p; @@ -1661,7 +1760,7 @@ static int proc_bulk_compat(struct dev_state *ps, return proc_bulk(ps, p); } -static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg) +static int proc_disconnectsignal_compat(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_disconnectsignal32 ds; @@ -1699,7 +1798,7 @@ static int get_urb32(struct usbdevfs_urb *kurb, return 0; } -static int proc_submiturb_compat(struct dev_state *ps, void __user *arg) +static int proc_submiturb_compat(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_urb uurb; @@ -1745,7 +1844,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) return 0; } -static int proc_reapurb_compat(struct dev_state *ps, void __user *arg) +static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg) { struct async *as = reap_as(ps); if (as) { @@ -1758,7 +1857,7 @@ static int proc_reapurb_compat(struct dev_state *ps, void __user *arg) return -EIO; } -static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg) +static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg) { int retval; struct async *as; @@ -1775,7 +1874,7 @@ static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg) #endif -static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) +static int proc_disconnectsignal(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_disconnectsignal ds; @@ -1786,7 +1885,7 @@ static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) return 0; } -static int proc_claiminterface(struct dev_state *ps, void __user *arg) +static int proc_claiminterface(struct usb_dev_state *ps, void __user *arg) { unsigned int ifnum; @@ -1795,7 +1894,7 @@ static int proc_claiminterface(struct dev_state *ps, void __user *arg) return claimintf(ps, ifnum); } -static int proc_releaseinterface(struct dev_state *ps, void __user *arg) +static int proc_releaseinterface(struct usb_dev_state *ps, void __user *arg) { unsigned int ifnum; int ret; @@ -1808,7 +1907,7 @@ static int proc_releaseinterface(struct dev_state *ps, void __user *arg) return 0; } -static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) +static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl) { int size; void *buf = NULL; @@ -1884,7 +1983,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) return retval; } -static int proc_ioctl_default(struct dev_state *ps, void __user *arg) +static int proc_ioctl_default(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_ioctl ctrl; @@ -1894,7 +1993,7 @@ static int proc_ioctl_default(struct dev_state *ps, void __user *arg) } #ifdef CONFIG_COMPAT -static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg) +static int proc_ioctl_compat(struct usb_dev_state *ps, compat_uptr_t arg) { struct usbdevfs_ioctl32 __user *uioc; struct usbdevfs_ioctl ctrl; @@ -1912,7 +2011,7 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg) } #endif -static int proc_claim_port(struct dev_state *ps, void __user *arg) +static int proc_claim_port(struct usb_dev_state *ps, void __user *arg) { unsigned portnum; int rc; @@ -1926,7 +2025,7 @@ static int proc_claim_port(struct dev_state *ps, void __user *arg) return rc; } -static int proc_release_port(struct dev_state *ps, void __user *arg) +static int proc_release_port(struct usb_dev_state *ps, void __user *arg) { unsigned portnum; @@ -1935,7 +2034,7 @@ static int proc_release_port(struct dev_state *ps, void __user *arg) return usb_hub_release_port(ps->dev, portnum, ps); } -static int proc_get_capabilities(struct dev_state *ps, void __user *arg) +static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg) { __u32 caps; @@ -1951,7 +2050,7 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg) return 0; } -static int proc_disconnect_claim(struct dev_state *ps, void __user *arg) +static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg) { struct usbdevfs_disconnect_claim dc; struct usb_interface *intf; @@ -1983,6 +2082,45 @@ static int proc_disconnect_claim(struct dev_state *ps, void __user *arg) return claimintf(ps, dc.interface); } +static int proc_alloc_streams(struct usb_dev_state *ps, void __user *arg) +{ + unsigned num_streams, num_eps; + struct usb_host_endpoint **eps; + struct usb_interface *intf; + int r; + + r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps, + &eps, &intf); + if (r) + return r; + + destroy_async_on_interface(ps, + intf->altsetting[0].desc.bInterfaceNumber); + + r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL); + kfree(eps); + return r; +} + +static int proc_free_streams(struct usb_dev_state *ps, void __user *arg) +{ + unsigned num_eps; + struct usb_host_endpoint **eps; + struct usb_interface *intf; + int r; + + r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf); + if (r) + return r; + + destroy_async_on_interface(ps, + intf->altsetting[0].desc.bInterfaceNumber); + + r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL); + kfree(eps); + return r; +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -1991,7 +2129,7 @@ static int proc_disconnect_claim(struct dev_state *ps, void __user *arg) static long usbdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p) { - struct dev_state *ps = file->private_data; + struct usb_dev_state *ps = file->private_data; struct inode *inode = file_inode(file); struct usb_device *dev = ps->dev; int ret = -ENOTTY; @@ -2159,6 +2297,12 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, case USBDEVFS_DISCONNECT_CLAIM: ret = proc_disconnect_claim(ps, p); break; + case USBDEVFS_ALLOC_STREAMS: + ret = proc_alloc_streams(ps, p); + break; + case USBDEVFS_FREE_STREAMS: + ret = proc_free_streams(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) @@ -2192,7 +2336,7 @@ static long usbdev_compat_ioctl(struct file *file, unsigned int cmd, static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait) { - struct dev_state *ps = file->private_data; + struct usb_dev_state *ps = file->private_data; unsigned int mask = 0; poll_wait(file, &ps->wait, wait); @@ -2218,11 +2362,11 @@ const struct file_operations usbdev_file_operations = { static void usbdev_remove(struct usb_device *udev) { - struct dev_state *ps; + struct usb_dev_state *ps; struct siginfo sinfo; while (!list_empty(&udev->filelist)) { - ps = list_entry(udev->filelist.next, struct dev_state, list); + ps = list_entry(udev->filelist.next, struct usb_dev_state, list); destroy_all_async(ps); wake_up_all(&ps->wait); list_del_init(&ps->list); diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ab90a0156828..888881e5f292 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -312,9 +312,9 @@ static int usb_probe_interface(struct device *dev) return error; } - id = usb_match_id(intf, driver->id_table); + id = usb_match_dynamic_id(intf, driver); if (!id) - id = usb_match_dynamic_id(intf, driver); + id = usb_match_id(intf, driver->id_table); if (!id) return error; @@ -400,8 +400,9 @@ static int usb_unbind_interface(struct device *dev) { struct usb_driver *driver = to_usb_driver(dev->driver); struct usb_interface *intf = to_usb_interface(dev); + struct usb_host_endpoint *ep, **eps = NULL; struct usb_device *udev; - int error, r, lpm_disable_error; + int i, j, error, r, lpm_disable_error; intf->condition = USB_INTERFACE_UNBINDING; @@ -425,6 +426,26 @@ static int usb_unbind_interface(struct device *dev) driver->disconnect(intf); usb_cancel_queued_reset(intf); + /* Free streams */ + for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + ep = &intf->cur_altsetting->endpoint[i]; + if (ep->streams == 0) + continue; + if (j == 0) { + eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *), + GFP_KERNEL); + if (!eps) { + dev_warn(dev, "oom, leaking streams\n"); + break; + } + } + eps[j++] = ep; + } + if (j) { + usb_free_streams(intf, eps, j, GFP_KERNEL); + kfree(eps); + } + /* Reset other interface state. * We cannot do a Set-Interface if the device is suspended or * if it is prepared for a system sleep (since installing a new @@ -990,8 +1011,7 @@ EXPORT_SYMBOL_GPL(usb_deregister); * it doesn't support pre_reset/post_reset/reset_resume or * because it doesn't support suspend/resume. * - * The caller must hold @intf's device's lock, but not its pm_mutex - * and not @intf->dev.sem. + * The caller must hold @intf's device's lock, but not @intf's lock. */ void usb_forced_unbind_intf(struct usb_interface *intf) { @@ -1004,16 +1024,37 @@ void usb_forced_unbind_intf(struct usb_interface *intf) intf->needs_binding = 1; } +/* + * Unbind drivers for @udev's marked interfaces. These interfaces have + * the needs_binding flag set, for example by usb_resume_interface(). + * + * The caller must hold @udev's device lock. + */ +static void unbind_marked_interfaces(struct usb_device *udev) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + if (intf->dev.driver && intf->needs_binding) + usb_forced_unbind_intf(intf); + } + } +} + /* Delayed forced unbinding of a USB interface driver and scan * for rebinding. * - * The caller must hold @intf's device's lock, but not its pm_mutex - * and not @intf->dev.sem. + * The caller must hold @intf's device's lock, but not @intf's lock. * * Note: Rebinds will be skipped if a system sleep transition is in * progress and the PM "complete" callback hasn't occurred yet. */ -void usb_rebind_intf(struct usb_interface *intf) +static void usb_rebind_intf(struct usb_interface *intf) { int rc; @@ -1030,68 +1071,66 @@ void usb_rebind_intf(struct usb_interface *intf) } } -#ifdef CONFIG_PM - -/* Unbind drivers for @udev's interfaces that don't support suspend/resume - * There is no check for reset_resume here because it can be determined - * only during resume whether reset_resume is needed. +/* + * Rebind drivers to @udev's marked interfaces. These interfaces have + * the needs_binding flag set. * * The caller must hold @udev's device lock. */ -static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) +static void rebind_marked_interfaces(struct usb_device *udev) { struct usb_host_config *config; int i; struct usb_interface *intf; - struct usb_driver *drv; config = udev->actconfig; if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { intf = config->interface[i]; - - if (intf->dev.driver) { - drv = to_usb_driver(intf->dev.driver); - if (!drv->suspend || !drv->resume) - usb_forced_unbind_intf(intf); - } + if (intf->needs_binding) + usb_rebind_intf(intf); } } } -/* Unbind drivers for @udev's interfaces that failed to support reset-resume. - * These interfaces have the needs_binding flag set by usb_resume_interface(). +/* + * Unbind all of @udev's marked interfaces and then rebind all of them. + * This ordering is necessary because some drivers claim several interfaces + * when they are first probed. * * The caller must hold @udev's device lock. */ -static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev) +void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev) { - struct usb_host_config *config; - int i; - struct usb_interface *intf; - - config = udev->actconfig; - if (config) { - for (i = 0; i < config->desc.bNumInterfaces; ++i) { - intf = config->interface[i]; - if (intf->dev.driver && intf->needs_binding) - usb_forced_unbind_intf(intf); - } - } + unbind_marked_interfaces(udev); + rebind_marked_interfaces(udev); } -static void do_rebind_interfaces(struct usb_device *udev) +#ifdef CONFIG_PM + +/* Unbind drivers for @udev's interfaces that don't support suspend/resume + * There is no check for reset_resume here because it can be determined + * only during resume whether reset_resume is needed. + * + * The caller must hold @udev's device lock. + */ +static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) { struct usb_host_config *config; int i; struct usb_interface *intf; + struct usb_driver *drv; config = udev->actconfig; if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { intf = config->interface[i]; - if (intf->needs_binding) - usb_rebind_intf(intf); + + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); + } } } } @@ -1420,7 +1459,7 @@ int usb_resume_complete(struct device *dev) * whose needs_binding flag is set */ if (udev->state != USB_STATE_NOTATTACHED) - do_rebind_interfaces(udev); + rebind_marked_interfaces(udev); return 0; } @@ -1442,7 +1481,7 @@ int usb_resume(struct device *dev, pm_message_t msg) pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - unbind_no_reset_resume_drivers_interfaces(udev); + unbind_marked_interfaces(udev); } /* Avoid PM error messages for devices disconnected while suspended diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index acbfeb0a0119..358ca8dd784f 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -155,6 +155,7 @@ int usb_choose_configuration(struct usb_device *udev) } return i; } +EXPORT_SYMBOL_GPL(usb_choose_configuration); static int generic_probe(struct usb_device *udev) { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 2518c3250750..9c4e2922b04d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2049,7 +2049,7 @@ int usb_alloc_streams(struct usb_interface *interface, { struct usb_hcd *hcd; struct usb_device *dev; - int i; + int i, ret; dev = interface_to_usbdev(interface); hcd = bus_to_hcd(dev->bus); @@ -2058,13 +2058,24 @@ int usb_alloc_streams(struct usb_interface *interface, if (dev->speed != USB_SPEED_SUPER) return -EINVAL; - /* Streams only apply to bulk endpoints. */ - for (i = 0; i < num_eps; i++) + for (i = 0; i < num_eps; i++) { + /* Streams only apply to bulk endpoints. */ if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) return -EINVAL; + /* Re-alloc is not allowed */ + if (eps[i]->streams) + return -EINVAL; + } - return hcd->driver->alloc_streams(hcd, dev, eps, num_eps, + ret = hcd->driver->alloc_streams(hcd, dev, eps, num_eps, num_streams, mem_flags); + if (ret < 0) + return ret; + + for (i = 0; i < num_eps; i++) + eps[i]->streams = ret; + + return ret; } EXPORT_SYMBOL_GPL(usb_alloc_streams); @@ -2078,8 +2089,7 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams); * Reverts a group of bulk endpoints back to not using stream IDs. * Can fail if we are given bad arguments, or HCD is broken. * - * Return: On success, the number of allocated streams. On failure, a negative - * error code. + * Return: 0 on success. On failure, a negative error code. */ int usb_free_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, @@ -2087,19 +2097,26 @@ int usb_free_streams(struct usb_interface *interface, { struct usb_hcd *hcd; struct usb_device *dev; - int i; + int i, ret; dev = interface_to_usbdev(interface); hcd = bus_to_hcd(dev->bus); if (dev->speed != USB_SPEED_SUPER) return -EINVAL; - /* Streams only apply to bulk endpoints. */ + /* Double-free is not allowed */ for (i = 0; i < num_eps; i++) - if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc)) + if (!eps[i] || !eps[i]->streams) return -EINVAL; - return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); + ret = hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); + if (ret < 0) + return ret; + + for (i = 0; i < num_eps; i++) + eps[i]->streams = 0; + + return ret; } EXPORT_SYMBOL_GPL(usb_free_streams); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 64ea21971be2..090469ebfcff 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -141,19 +141,27 @@ static int usb_device_supports_lpm(struct usb_device *udev) return 0; } - /* All USB 3.0 must support LPM, but we need their max exit latency - * information from the SuperSpeed Extended Capabilities BOS descriptor. + /* + * According to the USB 3.0 spec, all USB 3.0 devices must support LPM. + * However, there are some that don't, and they set the U1/U2 exit + * latencies to zero. */ if (!udev->bos->ss_cap) { - dev_warn(&udev->dev, "No LPM exit latency info found. " - "Power management will be impacted.\n"); + dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n"); + return 0; + } + + if (udev->bos->ss_cap->bU1devExitLat == 0 && + udev->bos->ss_cap->bU2DevExitLat == 0) { + if (udev->parent) + dev_info(&udev->dev, "LPM exit latency is zeroed, disabling LPM.\n"); + else + dev_info(&udev->dev, "We don't know the algorithms for LPM for this host, disabling LPM.\n"); return 0; } - if (udev->parent->lpm_capable) - return 1; - dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. " - "Power management will be impacted.\n"); + if (!udev->parent || udev->parent->lpm_capable) + return 1; return 0; } @@ -499,7 +507,8 @@ static void led_work (struct work_struct *work) changed++; } if (changed) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + queue_delayed_work(system_power_efficient_wq, + &hub->leds, LED_CYCLE_PERIOD); } /* use a short timeout for hub/port status fetches */ @@ -1040,8 +1049,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) */ if (type == HUB_INIT) { delay = hub_power_on(hub, false); - PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2); - schedule_delayed_work(&hub->init_work, + INIT_DELAYED_WORK(&hub->init_work, hub_init_func2); + queue_delayed_work(system_power_efficient_wq, + &hub->init_work, msecs_to_jiffies(delay)); /* Suppress autosuspend until init is done */ @@ -1194,8 +1204,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* Don't do a long sleep inside a workqueue routine */ if (type == HUB_INIT2) { - PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3); - schedule_delayed_work(&hub->init_work, + INIT_DELAYED_WORK(&hub->init_work, hub_init_func3); + queue_delayed_work(system_power_efficient_wq, + &hub->init_work, msecs_to_jiffies(delay)); return; /* Continues at init3: below */ } else { @@ -1209,7 +1220,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) if (status < 0) dev_err(hub->intfdev, "activate --> %d\n", status); if (hub->has_indicators && blinkenlights) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + queue_delayed_work(system_power_efficient_wq, + &hub->leds, LED_CYCLE_PERIOD); /* Scan all ports that need attention */ kick_khubd(hub); @@ -1788,7 +1800,7 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) * to one of these "claimed" ports, the program will "own" the device. */ static int find_port_owner(struct usb_device *hdev, unsigned port1, - struct dev_state ***ppowner) + struct usb_dev_state ***ppowner) { struct usb_hub *hub = usb_hub_to_struct_hub(hdev); @@ -1806,10 +1818,10 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1, /* In the following three functions, the caller must hold hdev's lock */ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, - struct dev_state *owner) + struct usb_dev_state *owner) { int rc; - struct dev_state **powner; + struct usb_dev_state **powner; rc = find_port_owner(hdev, port1, &powner); if (rc) @@ -1819,12 +1831,13 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, *powner = owner; return rc; } +EXPORT_SYMBOL_GPL(usb_hub_claim_port); int usb_hub_release_port(struct usb_device *hdev, unsigned port1, - struct dev_state *owner) + struct usb_dev_state *owner) { int rc; - struct dev_state **powner; + struct usb_dev_state **powner; rc = find_port_owner(hdev, port1, &powner); if (rc) @@ -1834,8 +1847,9 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, *powner = NULL; return rc; } +EXPORT_SYMBOL_GPL(usb_hub_release_port); -void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner) +void usb_hub_release_all_ports(struct usb_device *hdev, struct usb_dev_state *owner) { struct usb_hub *hub = usb_hub_to_struct_hub(hdev); int n; @@ -3093,9 +3107,19 @@ static int finish_port_resume(struct usb_device *udev) * operation is carried out here, after the port has been * resumed. */ - if (udev->reset_resume) + if (udev->reset_resume) { + /* + * If the device morphs or switches modes when it is reset, + * we don't want to perform a reset-resume. We'll fail the + * resume, which will cause a logical disconnect, and then + * the device will be rediscovered. + */ retry_reset_resume: - status = usb_reset_and_verify_device(udev); + if (udev->quirks & USB_QUIRK_RESET) + status = -ENODEV; + else + status = usb_reset_and_verify_device(udev); + } /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, @@ -3958,7 +3982,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) connect_type = usb_get_hub_port_connect_type(udev->parent, udev->portnum); - if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) || + if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) || connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) { udev->usb2_hw_lpm_allowed = 1; usb_set_usb2_hardware_lpm(udev, 1); @@ -4107,8 +4131,12 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, did_new_scheme = true; retval = hub_enable_device(udev); - if (retval < 0) + if (retval < 0) { + dev_err(&udev->dev, + "hub failed to enable device, error %d\n", + retval); goto fail; + } #define GET_DESCRIPTOR_BUFSIZE 64 buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); @@ -4311,7 +4339,8 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) /* hub LEDs are probably harder to miss than syslog */ if (hub->has_indicators) { hub->indicator[port1-1] = INDICATOR_GREEN_BLINK; - schedule_delayed_work (&hub->leds, 0); + queue_delayed_work(system_power_efficient_wq, + &hub->leds, 0); } } kfree(qual); @@ -4540,7 +4569,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if (hub->has_indicators) { hub->indicator[port1-1] = INDICATOR_AMBER_BLINK; - schedule_delayed_work (&hub->leds, 0); + queue_delayed_work( + system_power_efficient_wq, + &hub->leds, 0); } status = -ENOTCONN; /* Don't retry */ goto loop_disable; @@ -4739,6 +4770,8 @@ static void hub_events(void) /* deal with port status changes */ for (i = 1; i <= hdev->maxchild; i++) { + struct usb_device *udev = hub->ports[i - 1]->child; + if (test_bit(i, hub->busy_bits)) continue; connect_change = test_bit(i, hub->change_bits); @@ -4837,8 +4870,6 @@ static void hub_events(void) */ if (hub_port_warm_reset_required(hub, portstatus)) { int status; - struct usb_device *udev = - hub->ports[i - 1]->child; dev_dbg(hub_dev, "warm reset port %d\n", i); if (!udev || @@ -4855,6 +4886,24 @@ static void hub_events(void) usb_unlock_device(udev); connect_change = 0; } + /* + * On disconnect USB3 protocol ports transit from U0 to + * SS.Inactive to Rx.Detect. If this happens a warm- + * reset is not needed, but a (re)connect may happen + * before khubd runs and sees the disconnect, and the + * device may be an unknown state. + * + * If the port went through SS.Inactive without khubd + * seeing it the C_LINK_STATE change flag will be set, + * and we reset the dev to put it in a known state. + */ + } else if (udev && hub_is_superspeed(hub->hdev) && + (portchange & USB_PORT_STAT_C_LINK_STATE) && + (portstatus & USB_PORT_STAT_CONNECTION)) { + usb_lock_device(udev); + usb_reset_device(udev); + usb_unlock_device(udev); + connect_change = 0; } if (connect_change) @@ -5112,7 +5161,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) struct usb_hcd *hcd = bus_to_hcd(udev->bus); struct usb_device_descriptor descriptor = udev->descriptor; struct usb_host_bos *bos; - int i, ret = 0; + int i, j, ret = 0; int port1 = udev->portnum; if (udev->state == USB_STATE_NOTATTACHED || @@ -5238,6 +5287,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ret); goto re_enumerate; } + /* Resetting also frees any allocated streams */ + for (j = 0; j < intf->cur_altsetting->desc.bNumEndpoints; j++) + intf->cur_altsetting->endpoint[j].streams = 0; } done: @@ -5340,10 +5392,11 @@ int usb_reset_device(struct usb_device *udev) else if (cintf->condition == USB_INTERFACE_BOUND) rebind = 1; + if (rebind) + cintf->needs_binding = 1; } - if (ret == 0 && rebind) - usb_rebind_intf(cintf); } + usb_unbind_and_rebind_marked_interfaces(udev); } usb_autosuspend_device(udev); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index df629a310e44..33bcb2c6f90a 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -89,7 +89,7 @@ struct usb_hub { struct usb_port { struct usb_device *child; struct device dev; - struct dev_state *port_owner; + struct usb_dev_state *port_owner; enum usb_port_connect_type connect_type; u8 portnum; unsigned power_is_on:1; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f829a1aad1c3..0c8a7fc4dad8 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -178,7 +178,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg); * * Return: * If successful, 0. Otherwise a negative error number. The number of actual - * bytes transferred will be stored in the @actual_length paramater. + * bytes transferred will be stored in the @actual_length parameter. */ int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) @@ -1293,8 +1293,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) struct usb_interface *iface; struct usb_host_interface *alt; struct usb_hcd *hcd = bus_to_hcd(dev->bus); - int ret; - int manual = 0; + int i, ret, manual = 0; unsigned int epaddr; unsigned int pipe; @@ -1329,6 +1328,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } + /* Changing alt-setting also frees any allocated streams */ + for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++) + iface->cur_altsetting->endpoint[i].streams = 0; + ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); if (ret < 0) { dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", @@ -1920,6 +1923,7 @@ free_interfaces: usb_autosuspend_device(dev); return 0; } +EXPORT_SYMBOL_GPL(usb_set_configuration); static LIST_HEAD(set_config_list); static DEFINE_SPINLOCK(set_config_lock); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9ff665f1322f..991386ceb4ec 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -831,7 +831,7 @@ EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs); * * this allows all outstanding URBs to be unlinked starting * from the back of the queue. This function is asynchronous. - * The unlinking is just tiggered. It may happen after this + * The unlinking is just triggered. It may happen after this * function has returned. * * This routine should not be called by a driver after its disconnect diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 823857767a16..75bf649da82d 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -2,7 +2,7 @@ #include <linux/acpi.h> struct usb_hub_descriptor; -struct dev_state; +struct usb_dev_state; /* Functions local to drivers/usb/core/ */ @@ -55,14 +55,10 @@ extern int usb_match_one_id_intf(struct usb_device *dev, extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); extern void usb_forced_unbind_intf(struct usb_interface *intf); -extern void usb_rebind_intf(struct usb_interface *intf); +extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev); -extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port, - struct dev_state *owner); -extern int usb_hub_release_port(struct usb_device *hdev, unsigned port, - struct dev_state *owner); extern void usb_hub_release_all_ports(struct usb_device *hdev, - struct dev_state *owner); + struct usb_dev_state *owner); extern bool usb_device_is_owned(struct usb_device *udev); extern int usb_hub_init(void); diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 8205799e6db3..c93918b70d03 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -72,6 +72,26 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg) } /** + * dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts. + * When the PRTINT interrupt fires, there are certain status bits in the Host + * Port that needs to get cleared. + * + * @hsotg: Programming view of DWC_otg controller + */ +static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg) +{ + u32 hprt0 = readl(hsotg->regs + HPRT0); + + if (hprt0 & HPRT0_ENACHG) { + hprt0 &= ~HPRT0_ENA; + writel(hprt0, hsotg->regs + HPRT0); + } + + /* Clear interrupt */ + writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS); +} + +/** * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message * * @hsotg: Programming view of DWC_otg controller @@ -479,9 +499,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) if (dwc2_is_device_mode(hsotg)) { dev_dbg(hsotg->dev, " --Port interrupt received in Device mode--\n"); - gintsts = GINTSTS_PRTINT; - writel(gintsts, hsotg->regs + GINTSTS); - retval = 1; + dwc2_handle_usb_port_intr(hsotg); + retval = IRQ_HANDLED; } } diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 012f17ec1a37..47b9eb5389b4 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -975,8 +975,8 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd) { struct dwc2_hcd_urb *urb = qtd->urb; - int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); enum dwc2_halt_status halt_status = DWC2_HC_XFER_COMPLETE; + int pipe_type; int urb_xfer_done; if (dbg_hc(chan)) @@ -984,6 +984,11 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, "--Host Channel %d Interrupt: Transfer Complete--\n", chnum); + if (!urb) + goto handle_xfercomp_done; + + pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + if (hsotg->core_params->dma_desc_enable > 0) { dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status); if (pipe_type == USB_ENDPOINT_XFER_ISOC) @@ -1005,9 +1010,6 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, } } - if (!urb) - goto handle_xfercomp_done; - /* Update the QTD and URB states */ switch (pipe_type) { case USB_ENDPOINT_XFER_CONTROL: @@ -1105,7 +1107,7 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd) { struct dwc2_hcd_urb *urb = qtd->urb; - int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + int pipe_type; dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n", chnum); @@ -1119,6 +1121,8 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg, if (!urb) goto handle_stall_halt; + pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + if (pipe_type == USB_ENDPOINT_XFER_CONTROL) dwc2_host_complete(hsotg, qtd, -EPIPE); diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a49217ae3533..d001417e8e37 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -61,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure */ -static void dwc3_core_soft_reset(struct dwc3 *dwc) +static int dwc3_core_soft_reset(struct dwc3 *dwc) { u32 reg; + int ret; /* Before Resetting PHY, put Core in Reset */ reg = dwc3_readl(dwc->regs, DWC3_GCTL); @@ -82,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc) usb_phy_init(dwc->usb2_phy); usb_phy_init(dwc->usb3_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) { + phy_exit(dwc->usb2_generic_phy); + return ret; + } mdelay(100); /* Clear USB3 PHY reset */ @@ -100,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_CORESOFTRESET; dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + return 0; } /** @@ -242,6 +254,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) } } +static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + dwc->scratchbuf = kmalloc_array(dwc->nr_scratch, + DWC3_SCRATCHBUF_SIZE, GFP_KERNEL); + if (!dwc->scratchbuf) + return -ENOMEM; + + return 0; +} + +static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) +{ + dma_addr_t scratch_addr; + u32 param; + int ret; + + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) + return 0; + + scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf, + dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dwc->dev, scratch_addr)) { + dev_err(dwc->dev, "failed to map scratch buffer\n"); + ret = -EFAULT; + goto err0; + } + + dwc->scratch_addr = scratch_addr; + + param = lower_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param); + if (ret < 0) + goto err1; + + param = upper_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param); + if (ret < 0) + goto err1; + + return 0; + +err1: + dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + +err0: + return ret; +} + +static void dwc3_free_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return; + + if (!dwc->nr_scratch) + return; + + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) + return; + + dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + kfree(dwc->scratchbuf); +} + static void dwc3_core_num_eps(struct dwc3 *dwc) { struct dwc3_hwparams *parms = &dwc->hwparams; @@ -277,6 +373,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) static int dwc3_core_init(struct dwc3 *dwc) { unsigned long timeout; + u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; @@ -306,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc) cpu_relax(); } while (true); - dwc3_core_soft_reset(dwc); + ret = dwc3_core_soft_reset(dwc); + if (ret) + goto err0; reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; @@ -314,7 +413,29 @@ static int dwc3_core_init(struct dwc3 *dwc) switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { case DWC3_GHWPARAMS1_EN_PWROPT_CLK: - reg &= ~DWC3_GCTL_DSBLCLKGTNG; + /** + * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an + * issue which would cause xHCI compliance tests to fail. + * + * Because of that we cannot enable clock gating on such + * configurations. + * + * Refers to: + * + * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based + * SOF/ITP Mode Used + */ + if ((dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) && + (dwc->revision >= DWC3_REVISION_210A && + dwc->revision <= DWC3_REVISION_250A)) + reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; + else + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + case DWC3_GHWPARAMS1_EN_PWROPT_HIB: + /* enable hibernation here */ + dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4); break; default: dev_dbg(dwc->dev, "No power optimization available\n"); @@ -333,16 +454,36 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); + ret = dwc3_alloc_scratch_buffers(dwc); + if (ret) + goto err1; + + ret = dwc3_setup_scratch_buffers(dwc); + if (ret) + goto err2; + return 0; +err2: + dwc3_free_scratch_buffers(dwc); + +err1: + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + err0: return ret; } static void dwc3_core_exit(struct dwc3 *dwc) { + dwc3_free_scratch_buffers(dwc); usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); } #define DWC3_ALIGN_MASK (16 - 1) @@ -411,32 +552,52 @@ static int dwc3_probe(struct platform_device *pdev) if (IS_ERR(dwc->usb2_phy)) { ret = PTR_ERR(dwc->usb2_phy); - - /* - * if -ENXIO is returned, it means PHY layer wasn't - * enabled, so it makes no sense to return -EPROBE_DEFER - * in that case, since no PHY driver will ever probe. - */ - if (ret == -ENXIO) + if (ret == -ENXIO || ret == -ENODEV) { + dwc->usb2_phy = NULL; + } else if (ret == -EPROBE_DEFER) { return ret; - - dev_err(dev, "no usb2 phy configured\n"); - return -EPROBE_DEFER; + } else { + dev_err(dev, "no usb2 phy configured\n"); + return ret; + } } if (IS_ERR(dwc->usb3_phy)) { ret = PTR_ERR(dwc->usb3_phy); + if (ret == -ENXIO || ret == -ENODEV) { + dwc->usb3_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } + } - /* - * if -ENXIO is returned, it means PHY layer wasn't - * enabled, so it makes no sense to return -EPROBE_DEFER - * in that case, since no PHY driver will ever probe. - */ - if (ret == -ENXIO) + dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); + if (IS_ERR(dwc->usb2_generic_phy)) { + ret = PTR_ERR(dwc->usb2_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dwc->usb2_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb2 phy configured\n"); return ret; + } + } - dev_err(dev, "no usb3 phy configured\n"); - return -EPROBE_DEFER; + dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); + if (IS_ERR(dwc->usb3_generic_phy)) { + ret = PTR_ERR(dwc->usb3_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dwc->usb3_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } } dwc->xhci_resources[0].start = res->start; @@ -479,6 +640,14 @@ static int dwc3_probe(struct platform_device *pdev) goto err0; } + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + dwc->dr_mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + dwc->dr_mode = USB_DR_MODE_PERIPHERAL; + + if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) + dwc->dr_mode = USB_DR_MODE_OTG; + ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); @@ -487,21 +656,20 @@ static int dwc3_probe(struct platform_device *pdev) usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); + ret = phy_power_on(dwc->usb2_generic_phy); + if (ret < 0) + goto err1; + + ret = phy_power_on(dwc->usb3_generic_phy); + if (ret < 0) + goto err_usb2phy_power; ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err1; + goto err_usb3phy_power; } - if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) - dwc->dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) - dwc->dr_mode = USB_DR_MODE_PERIPHERAL; - - if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) - dwc->dr_mode = USB_DR_MODE_OTG; - switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); @@ -568,6 +736,12 @@ err3: err2: dwc3_event_buffers_cleanup(dwc); +err_usb3phy_power: + phy_power_off(dwc->usb3_generic_phy); + +err_usb2phy_power: + phy_power_off(dwc->usb2_generic_phy); + err1: usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); @@ -585,6 +759,8 @@ static int dwc3_remove(struct platform_device *pdev) usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); + phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -682,6 +858,8 @@ static int dwc3_suspend(struct device *dev) usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); return 0; } @@ -690,9 +868,17 @@ static int dwc3_resume(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); unsigned long flags; + int ret; usb_phy_init(dwc->usb3_phy); usb_phy_init(dwc->usb2_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) + goto err_usb2phy_init; spin_lock_irqsave(&dwc->lock, flags); @@ -716,6 +902,11 @@ static int dwc3_resume(struct device *dev) pm_runtime_enable(dev); return 0; + +err_usb2phy_init: + phy_exit(dwc->usb2_generic_phy); + + return ret; } static const struct dev_pm_ops dwc3_dev_pm_ops = { diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f8af8d44af85..57332e3768e4 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -31,11 +31,14 @@ #include <linux/usb/gadget.h> #include <linux/usb/otg.h> +#include <linux/phy/phy.h> + /* Global constants */ #define DWC3_EP0_BOUNCE_SIZE 512 #define DWC3_ENDPOINTS_NUM 32 #define DWC3_XHCI_RESOURCES_NUM 2 +#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ #define DWC3_EVENT_SIZE 4 /* bytes */ #define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */ #define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM) @@ -157,6 +160,7 @@ #define DWC3_GCTL_PRTCAP_OTG 3 #define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_SOFITPSYNC (1 << 10) #define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) #define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) #define DWC3_GCTL_DISSCRAMBLE (1 << 3) @@ -318,7 +322,7 @@ /* Device Endpoint Command Register */ #define DWC3_DEPCMD_PARAM_SHIFT 16 #define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT) -#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) +#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) #define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1) #define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) #define DWC3_DEPCMD_CMDACT (1 << 10) @@ -393,6 +397,7 @@ struct dwc3_event_buffer { * @busy_slot: first slot which is owned by HW * @desc: usb_endpoint_descriptor pointer * @dwc: pointer to DWC controller + * @saved_state: ep state saved during hibernation * @flags: endpoint flags (wedged, stalled, ...) * @current_trb: index of current used trb * @number: endpoint number (1 - 15) @@ -415,6 +420,7 @@ struct dwc3_ep { const struct usb_ss_ep_comp_descriptor *comp_desc; struct dwc3 *dwc; + u32 saved_state; unsigned flags; #define DWC3_EP_ENABLED (1 << 0) #define DWC3_EP_STALL (1 << 1) @@ -598,6 +604,7 @@ struct dwc3_scratchpad_array { * @ep0_trb: dma address of ep0_trb * @ep0_usb_req: dummy req used while handling STD USB requests * @ep0_bounce_addr: dma address of ep0_bounce + * @scratch_addr: dma address of scratchbuf * @lock: for synchronizing * @dev: pointer to our struct device * @xhci: pointer to our xHCI child @@ -606,6 +613,7 @@ struct dwc3_scratchpad_array { * @gadget_driver: pointer to the gadget driver * @regs: base address for our registers * @regs_size: address space size + * @nr_scratch: number of scratch buffers * @num_event_buffers: calculated number of event buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) @@ -613,16 +621,10 @@ struct dwc3_scratchpad_array { * @dr_mode: requested mode of operation * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY + * @usb2_generic_phy: pointer to USB2 PHY + * @usb3_generic_phy: pointer to USB3 PHY * @dcfg: saved contents of DCFG register * @gctl: saved contents of GCTL register - * @is_selfpowered: true when we are selfpowered - * @three_stage_setup: set if we perform a three phase setup - * @ep0_bounced: true when we used bounce buffer - * @ep0_expect_in: true when we expect a DATA IN transfer - * @start_config_issued: true when StartConfig command has been issued - * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround - * @needs_fifo_resize: not all users might want fifo resizing, flag it - * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes. * @isoch_delay: wValue from Set Isochronous Delay request; * @u2sel: parameter from Set SEL request. * @u2pel: parameter from Set SEL request. @@ -637,15 +639,31 @@ struct dwc3_scratchpad_array { * @mem: points to start of memory which is used for this struct. * @hwparams: copy of hwparams registers * @root: debugfs root folder pointer + * @regset: debugfs pointer to regdump file + * @test_mode: true when we're entering a USB test mode + * @test_mode_nr: test feature selector + * @delayed_status: true when gadget driver asks for delayed status + * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer + * @has_hibernation: true when dwc3 was configured with Hibernation + * @is_selfpowered: true when we are selfpowered + * @needs_fifo_resize: not all users might want fifo resizing, flag it + * @pullups_connected: true when Run/Stop bit is set + * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes. + * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround + * @start_config_issued: true when StartConfig command has been issued + * @three_stage_setup: set if we perform a three phase setup */ struct dwc3 { struct usb_ctrlrequest *ctrl_req; struct dwc3_trb *ep0_trb; void *ep0_bounce; + void *scratchbuf; u8 *setup_buf; dma_addr_t ctrl_req_addr; dma_addr_t ep0_trb_addr; dma_addr_t ep0_bounce_addr; + dma_addr_t scratch_addr; struct dwc3_request ep0_usb_req; /* device lock */ @@ -665,6 +683,9 @@ struct dwc3 { struct usb_phy *usb2_phy; struct usb_phy *usb3_phy; + struct phy *usb2_generic_phy; + struct phy *usb3_generic_phy; + void __iomem *regs; size_t regs_size; @@ -674,6 +695,7 @@ struct dwc3 { u32 dcfg; u32 gctl; + u32 nr_scratch; u32 num_event_buffers; u32 u1u2; u32 maximum_speed; @@ -695,17 +717,9 @@ struct dwc3 { #define DWC3_REVISION_230A 0x5533230a #define DWC3_REVISION_240A 0x5533240a #define DWC3_REVISION_250A 0x5533250a - - unsigned is_selfpowered:1; - unsigned three_stage_setup:1; - unsigned ep0_bounced:1; - unsigned ep0_expect_in:1; - unsigned start_config_issued:1; - unsigned setup_packet_pending:1; - unsigned delayed_status:1; - unsigned needs_fifo_resize:1; - unsigned resize_fifos:1; - unsigned pullups_connected:1; +#define DWC3_REVISION_260A 0x5533260a +#define DWC3_REVISION_270A 0x5533270a +#define DWC3_REVISION_280A 0x5533280a enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; @@ -730,6 +744,18 @@ struct dwc3 { u8 test_mode; u8 test_mode_nr; + + unsigned delayed_status:1; + unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; + unsigned has_hibernation:1; + unsigned is_selfpowered:1; + unsigned needs_fifo_resize:1; + unsigned pullups_connected:1; + unsigned resize_fifos:1; + unsigned setup_packet_pending:1; + unsigned start_config_issued:1; + unsigned three_stage_setup:1; }; /* -------------------------------------------------------------------------- */ @@ -815,15 +841,15 @@ struct dwc3_event_depevt { * 12 - VndrDevTstRcved * @reserved15_12: Reserved, not used * @event_info: Information about this event - * @reserved31_24: Reserved, not used + * @reserved31_25: Reserved, not used */ struct dwc3_event_devt { u32 one_bit:1; u32 device_event:7; u32 type:4; u32 reserved15_12:4; - u32 event_info:8; - u32 reserved31_24:8; + u32 event_info:9; + u32 reserved31_25:7; } __packed; /** @@ -856,6 +882,19 @@ union dwc3_event { struct dwc3_event_gevt gevt; }; +/** + * struct dwc3_gadget_ep_cmd_params - representation of endpoint command + * parameters + * @param2: third parameter + * @param1: second parameter + * @param0: first parameter + */ +struct dwc3_gadget_ep_cmd_params { + u32 param2; + u32 param1; + u32 param0; +}; + /* * DWC3 Features to be used as Driver Data */ @@ -881,11 +920,31 @@ static inline void dwc3_host_exit(struct dwc3 *dwc) #if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_gadget_init(struct dwc3 *dwc); void dwc3_gadget_exit(struct dwc3 *dwc); +int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); +int dwc3_gadget_get_link_state(struct dwc3 *dwc); +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } static inline void dwc3_gadget_exit(struct dwc3 *dwc) { } +static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) +{ return 0; } +static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ return 0; } +static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, + enum dwc3_link_state state) +{ return 0; } + +static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ return 0; } +static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, + int cmd, u32 param) +{ return 0; } #endif /* power management interface */ diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index b269dbd47fc4..1160ff41bed4 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -29,7 +29,6 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/extcon.h> -#include <linux/extcon/of_extcon.h> #include <linux/regulator/consumer.h> #include <linux/usb/otg.h> @@ -424,11 +423,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing memory base resource\n"); - return -EINVAL; - } - base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) return PTR_ERR(base); @@ -522,7 +516,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_enable_irqs(omap); if (of_property_read_bool(node, "extcon")) { - edev = of_extcon_get_extcon_dev(dev, 0); + edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(edev)) { dev_vdbg(dev, "couldn't get extcon device\n"); ret = -EPROBE_DEFER; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2da0a5a2803a..a740eac74d56 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -68,6 +68,22 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) } /** + * dwc3_gadget_get_link_state - Gets current state of USB Link + * @dwc: pointer to our context structure + * + * Caller should take care of locking. This function will + * return the link state on success (>= 0) or -ETIMEDOUT. + */ +int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + return DWC3_DSTS_USBLNKST(reg); +} + +/** * dwc3_gadget_set_link_state - Sets USB Link to a particular State * @dwc: pointer to our context structure * @state: the state to put link into @@ -417,7 +433,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, const struct usb_endpoint_descriptor *desc, const struct usb_ss_ep_comp_descriptor *comp_desc, - bool ignore) + bool ignore, bool restore) { struct dwc3_gadget_ep_cmd_params params; @@ -436,6 +452,11 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, if (ignore) params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM; + if (restore) { + params.param0 |= DWC3_DEPCFG_ACTION_RESTORE; + params.param2 |= dep->saved_state; + } + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_XFER_NOT_READY_EN; @@ -494,7 +515,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, const struct usb_endpoint_descriptor *desc, const struct usb_ss_ep_comp_descriptor *comp_desc, - bool ignore) + bool ignore, bool restore) { struct dwc3 *dwc = dep->dwc; u32 reg; @@ -508,7 +529,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return ret; } - ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore); + ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore, + restore); if (ret) return ret; @@ -548,13 +570,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return 0; } -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force); static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; if (!list_empty(&dep->req_queued)) { - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); /* - giveback all requests to gadget driver */ while (!list_empty(&dep->req_queued)) { @@ -659,7 +681,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, } spin_lock_irqsave(&dwc->lock, flags); - ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false); + ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -771,9 +793,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; else trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; - - if (!req->request.no_interrupt && !chain) - trb->ctrl |= DWC3_TRB_CTRL_IOC; break; case USB_ENDPOINT_XFER_BULK: @@ -788,6 +807,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, BUG(); } + if (!req->request.no_interrupt && !chain) + trb->ctrl |= DWC3_TRB_CTRL_IOC; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; trb->ctrl |= DWC3_TRB_CTRL_CSP; @@ -1077,7 +1099,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) */ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { if (list_empty(&dep->req_queued)) { - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); dep->flags = DWC3_EP_ENABLED; } return 0; @@ -1107,6 +1129,23 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) return ret; } + /* + * 4. Stream Capable Bulk Endpoints. We need to start the transfer + * right away, otherwise host will not know we have streams to be + * handled. + */ + if (dep->stream_capable) { + int ret; + + ret = __dwc3_gadget_kick_transfer(dep, 0, true); + if (ret && ret != -EBUSY) { + struct dwc3 *dwc = dep->dwc; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + } + return 0; } @@ -1163,7 +1202,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, } if (r == req) { /* wait until it is processed */ - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); goto out1; } dev_err(dwc->dev, "request %p was not queued to %s\n", @@ -1194,8 +1233,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_SETSTALL, ¶ms); if (ret) - dev_err(dwc->dev, "failed to %s STALL on %s\n", - value ? "set" : "clear", + dev_err(dwc->dev, "failed to set STALL on %s\n", dep->name); else dep->flags |= DWC3_EP_STALL; @@ -1203,8 +1241,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_CLEARSTALL, ¶ms); if (ret) - dev_err(dwc->dev, "failed to %s STALL on %s\n", - value ? "set" : "clear", + dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name); else dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); @@ -1387,7 +1424,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, return 0; } -static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) +static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { u32 reg; u32 timeout = 500; @@ -1402,9 +1439,17 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) if (dwc->revision >= DWC3_REVISION_194A) reg &= ~DWC3_DCTL_KEEP_CONNECT; reg |= DWC3_DCTL_RUN_STOP; + + if (dwc->has_hibernation) + reg |= DWC3_DCTL_KEEP_CONNECT; + dwc->pullups_connected = true; } else { reg &= ~DWC3_DCTL_RUN_STOP; + + if (dwc->has_hibernation && !suspend) + reg &= ~DWC3_DCTL_KEEP_CONNECT; + dwc->pullups_connected = false; } @@ -1442,7 +1487,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) is_on = !!is_on; spin_lock_irqsave(&dwc->lock, flags); - ret = dwc3_gadget_run_stop(dwc, is_on); + ret = dwc3_gadget_run_stop(dwc, is_on, false); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1549,14 +1594,16 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err2; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err3; @@ -1849,15 +1896,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, */ dep->flags = DWC3_EP_PENDING_REQUEST; } else { - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); dep->flags = DWC3_EP_ENABLED; } return 1; } - if ((event->status & DEPEVT_STATUS_IOC) && - (trb->ctrl & DWC3_TRB_CTRL_IOC)) - return 0; return 1; } @@ -1999,7 +2043,25 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc) } } -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) +static void dwc3_suspend_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->suspend) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->suspend(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_resume_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->resume) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->resume(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) { struct dwc3_ep *dep; struct dwc3_gadget_ep_cmd_params params; @@ -2031,7 +2093,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) */ cmd = DWC3_DEPCMD_ENDTRANSFER; - cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; + cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; + cmd |= DWC3_DEPCMD_CMDIOC; cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); @@ -2260,17 +2323,23 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) reg |= DWC3_DCTL_HIRD_THRES(12); dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } else { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_HIRD_THRES_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); } dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true, + false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true, + false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; @@ -2378,9 +2447,50 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, dwc->link_state = next; + switch (next) { + case DWC3_LINK_STATE_U1: + if (dwc->speed == USB_SPEED_SUPER) + dwc3_suspend_gadget(dwc); + break; + case DWC3_LINK_STATE_U2: + case DWC3_LINK_STATE_U3: + dwc3_suspend_gadget(dwc); + break; + case DWC3_LINK_STATE_RESUME: + dwc3_resume_gadget(dwc); + break; + default: + /* do nothing */ + break; + } + dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); } +static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + unsigned int is_ss = evtinfo & BIT(4); + + /** + * WORKAROUND: DWC3 revison 2.20a with hibernation support + * have a known issue which can cause USB CV TD.9.23 to fail + * randomly. + * + * Because of this issue, core could generate bogus hibernation + * events which SW needs to ignore. + * + * Refers to: + * + * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0 + * Device Fallback from SuperSpeed + */ + if (is_ss ^ (dwc->speed == USB_SPEED_SUPER)) + return; + + /* enter hibernation here */ +} + static void dwc3_gadget_interrupt(struct dwc3 *dwc, const struct dwc3_event_devt *event) { @@ -2397,6 +2507,13 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, case DWC3_DEVICE_EVENT_WAKEUP: dwc3_gadget_wakeup_interrupt(dwc); break; + case DWC3_DEVICE_EVENT_HIBER_REQ: + if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation, + "unexpected hibernation event\n")) + break; + + dwc3_gadget_hibernation_interrupt(dwc, event->event_info); + break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); break; @@ -2661,8 +2778,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc) int dwc3_gadget_prepare(struct dwc3 *dwc) { - if (dwc->pullups_connected) + if (dwc->pullups_connected) { dwc3_gadget_disable_irq(dwc); + dwc3_gadget_run_stop(dwc, true, true); + } return 0; } @@ -2671,7 +2790,7 @@ void dwc3_gadget_complete(struct dwc3 *dwc) { if (dwc->pullups_connected) { dwc3_gadget_enable_irq(dwc); - dwc3_gadget_run_stop(dwc, true); + dwc3_gadget_run_stop(dwc, true, false); } } @@ -2694,12 +2813,14 @@ int dwc3_gadget_resume(struct dwc3 *dwc) dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); if (ret) goto err0; dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); if (ret) goto err1; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index febe1aa7b714..a0ee75b68a80 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -56,12 +56,6 @@ struct dwc3; /* DEPXFERCFG parameter 0 */ #define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) -struct dwc3_gadget_ep_cmd_params { - u32 param2; - u32 param1; - u32 param0; -}; - /* -------------------------------------------------------------------------- */ #define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) @@ -85,9 +79,6 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); -int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); -int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); - void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); @@ -95,9 +86,6 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); -int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, - unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param); /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8154165aa601..3557c7e5040d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -226,7 +226,7 @@ config USB_GR_UDC config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 - select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG + select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 help Many Texas Instruments OMAP processors have flexible full speed USB device controllers, with support for up to 30 @@ -301,7 +301,6 @@ config USB_PXA27X gadget drivers to also be dynamically linked. config USB_S3C_HSOTG - depends on ARM tristate "Designware/S3C HS/OtG USB Device controller" help The Designware USB2.0 high-speed gadget controller diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index cea8c20a1425..f605ad8c1902 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1758,15 +1758,15 @@ static int at91udc_probe(struct platform_device *pdev) /* newer chips have more FIFO memory than rm9200 */ if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { - usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64); - usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); - usb_ep_set_maxpacket_limit(&udc->ep[4].ep, 512); - usb_ep_set_maxpacket_limit(&udc->ep[5].ep, 512); + udc->ep[0].maxpacket = 64; + udc->ep[3].maxpacket = 64; + udc->ep[4].maxpacket = 512; + udc->ep[5].maxpacket = 512; } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); + udc->ep[3].maxpacket = 64; } else if (cpu_is_at91sam9263()) { - usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64); - usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); + udc->ep[0].maxpacket = 64; + udc->ep[3].maxpacket = 64; } udc->udp_baseaddr = ioremap(res->start, resource_size(res)); diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 52771d4c44bc..9f65324f9ae0 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (dma_status) { int i; - for (i = 1; i < USBA_NR_ENDPOINTS; i++) + for (i = 1; i < USBA_NR_DMAS; i++) if (dma_status & (1 << i)) usba_dma_irq(udc, &udc->usba_ep[i]); } @@ -1670,7 +1670,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (ep_status) { int i; - for (i = 0; i < USBA_NR_ENDPOINTS; i++) + for (i = 0; i < udc->num_ep; i++) if (ep_status & (1 << i)) { if (ep_is_control(&udc->usba_ep[i])) usba_control_irq(udc, &udc->usba_ep[i]); @@ -1827,12 +1827,12 @@ static int atmel_usba_stop(struct usb_gadget *gadget, toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - udc->driver = NULL; - clk_disable_unprepare(udc->hclk); clk_disable_unprepare(udc->pclk); - DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name); + DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name); + + udc->driver = NULL; return 0; } @@ -1914,6 +1914,12 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, i++; } + if (i == 0) { + dev_err(&pdev->dev, "of_probe: no endpoint specified\n"); + ret = -EINVAL; + goto err; + } + return eps; err: return ERR_PTR(ret); diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h index 2922db50befe..a70706e8cb02 100644 --- a/drivers/usb/gadget/atmel_usba_udc.h +++ b/drivers/usb/gadget/atmel_usba_udc.h @@ -210,7 +210,7 @@ #define USBA_FIFO_BASE(x) ((x) << 16) /* Synth parameters */ -#define USBA_NR_ENDPOINTS 7 +#define USBA_NR_DMAS 7 #define EP0_FIFO_SIZE 64 #define EP0_EPT_SIZE USBA_EPT_SIZE_64 diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index d742bed7a5fa..fab906429b80 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1139,7 +1139,7 @@ struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, uc = copy_gadget_strings(sp, n_gstrings, n_strings); if (IS_ERR(uc)) - return ERR_PTR(PTR_ERR(uc)); + return ERR_CAST(uc); n_gs = get_containers_gs(uc); ret = usb_string_ids_tab(cdev, n_gs[0]->strings); diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 2b4334394076..2e164dca08e8 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -28,6 +28,10 @@ #include <linux/usb/composite.h> #include <linux/usb/functionfs.h> +#include <linux/aio.h> +#include <linux/mmu_context.h> +#include <linux/poll.h> + #include "u_fs.h" #include "configfs.h" @@ -99,6 +103,14 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f) } +static inline enum ffs_setup_state +ffs_setup_state_clear_cancelled(struct ffs_data *ffs) +{ + return (enum ffs_setup_state) + cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP); +} + + static void ffs_func_eps_disable(struct ffs_function *func); static int __must_check ffs_func_eps_enable(struct ffs_function *func); @@ -122,8 +134,8 @@ struct ffs_ep { struct usb_ep *ep; /* P: ffs->eps_lock */ struct usb_request *req; /* P: epfile->mutex */ - /* [0]: full speed, [1]: high speed */ - struct usb_endpoint_descriptor *descs[2]; + /* [0]: full speed, [1]: high speed, [2]: super speed */ + struct usb_endpoint_descriptor *descs[3]; u8 num; @@ -148,6 +160,25 @@ struct ffs_epfile { unsigned char _pad; }; +/* ffs_io_data structure ***************************************************/ + +struct ffs_io_data { + bool aio; + bool read; + + struct kiocb *kiocb; + const struct iovec *iovec; + unsigned long nr_segs; + char __user *buf; + size_t len; + + struct mm_struct *mm; + struct work_struct work; + + struct usb_ep *ep; + struct usb_request *req; +}; + static int __must_check ffs_epfiles_create(struct ffs_data *ffs); static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); @@ -161,8 +192,10 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data, DEFINE_MUTEX(ffs_lock); EXPORT_SYMBOL(ffs_lock); -static struct ffs_dev *ffs_find_dev(const char *name); +static struct ffs_dev *_ffs_find_dev(const char *name); +static struct ffs_dev *_ffs_alloc_dev(void); static int _ffs_name_dev(struct ffs_dev *dev, const char *name); +static void _ffs_free_dev(struct ffs_dev *dev); static void *ffs_acquire_dev(const char *dev_name); static void ffs_release_dev(struct ffs_data *ffs_data); static int ffs_ready(struct ffs_data *ffs); @@ -218,7 +251,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) } ffs->setup_state = FFS_NO_SETUP; - return ffs->ep0req_status; + return req->status ? req->status : req->actual; } static int __ffs_ep0_stall(struct ffs_data *ffs) @@ -244,7 +277,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, ENTER(); /* Fast check if setup was canceled */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) + if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; /* Acquire mutex */ @@ -310,8 +343,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, * rather then _irqsave */ spin_lock_irq(&ffs->ev.waitq.lock); - switch (FFS_SETUP_STATE(ffs)) { - case FFS_SETUP_CANCELED: + switch (ffs_setup_state_clear_cancelled(ffs)) { + case FFS_SETUP_CANCELLED: ret = -EIDRM; goto done_spin; @@ -346,7 +379,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, /* * We are guaranteed to be still in FFS_ACTIVE state * but the state of setup could have changed from - * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need + * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need * to check for that. If that happened we copied data * from user space in vain but it's unlikely. * @@ -355,7 +388,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, * transition can be performed and it's protected by * mutex. */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) { + if (ffs_setup_state_clear_cancelled(ffs) == + FFS_SETUP_CANCELLED) { ret = -EIDRM; done_spin: spin_unlock_irq(&ffs->ev.waitq.lock); @@ -421,7 +455,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, ENTER(); /* Fast check if setup was canceled */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) + if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; /* Acquire mutex */ @@ -441,8 +475,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, */ spin_lock_irq(&ffs->ev.waitq.lock); - switch (FFS_SETUP_STATE(ffs)) { - case FFS_SETUP_CANCELED: + switch (ffs_setup_state_clear_cancelled(ffs)) { + case FFS_SETUP_CANCELLED: ret = -EIDRM; break; @@ -489,7 +523,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, spin_lock_irq(&ffs->ev.waitq.lock); /* See ffs_ep0_write() */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) { + if (ffs_setup_state_clear_cancelled(ffs) == + FFS_SETUP_CANCELLED) { ret = -EIDRM; break; } @@ -558,6 +593,45 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value) return ret; } +static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait) +{ + struct ffs_data *ffs = file->private_data; + unsigned int mask = POLLWRNORM; + int ret; + + poll_wait(file, &ffs->ev.waitq, wait); + + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret < 0)) + return mask; + + switch (ffs->state) { + case FFS_READ_DESCRIPTORS: + case FFS_READ_STRINGS: + mask |= POLLOUT; + break; + + case FFS_ACTIVE: + switch (ffs->setup_state) { + case FFS_NO_SETUP: + if (ffs->ev.count) + mask |= POLLIN; + break; + + case FFS_SETUP_PENDING: + case FFS_SETUP_CANCELLED: + mask |= (POLLIN | POLLOUT); + break; + } + case FFS_CLOSING: + break; + } + + mutex_unlock(&ffs->mutex); + + return mask; +} + static const struct file_operations ffs_ep0_operations = { .llseek = no_llseek, @@ -566,6 +640,7 @@ static const struct file_operations ffs_ep0_operations = { .read = ffs_ep0_read, .release = ffs_ep0_release, .unlocked_ioctl = ffs_ep0_ioctl, + .poll = ffs_ep0_poll, }; @@ -581,8 +656,52 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) } } -static ssize_t ffs_epfile_io(struct file *file, - char __user *buf, size_t len, int read) +static void ffs_user_copy_worker(struct work_struct *work) +{ + struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, + work); + int ret = io_data->req->status ? io_data->req->status : + io_data->req->actual; + + if (io_data->read && ret > 0) { + int i; + size_t pos = 0; + use_mm(io_data->mm); + for (i = 0; i < io_data->nr_segs; i++) { + if (unlikely(copy_to_user(io_data->iovec[i].iov_base, + &io_data->buf[pos], + io_data->iovec[i].iov_len))) { + ret = -EFAULT; + break; + } + pos += io_data->iovec[i].iov_len; + } + unuse_mm(io_data->mm); + } + + aio_complete(io_data->kiocb, ret, ret); + + usb_ep_free_request(io_data->ep, io_data->req); + + io_data->kiocb->private = NULL; + if (io_data->read) + kfree(io_data->iovec); + kfree(io_data->buf); + kfree(io_data); +} + +static void ffs_epfile_async_io_complete(struct usb_ep *_ep, + struct usb_request *req) +{ + struct ffs_io_data *io_data = req->context; + + ENTER(); + + INIT_WORK(&io_data->work, ffs_user_copy_worker); + schedule_work(&io_data->work); +} + +static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) { struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; @@ -612,7 +731,7 @@ static ssize_t ffs_epfile_io(struct file *file, } /* Do we halt? */ - halt = !read == !epfile->in; + halt = (!io_data->read == !epfile->in); if (halt && epfile->isoc) { ret = -EINVAL; goto error; @@ -630,15 +749,32 @@ static ssize_t ffs_epfile_io(struct file *file, * Controller may require buffer size to be aligned to * maxpacketsize of an out endpoint. */ - data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len; + data_len = io_data->read ? + usb_ep_align_maybe(gadget, ep->ep, io_data->len) : + io_data->len; data = kmalloc(data_len, GFP_KERNEL); if (unlikely(!data)) return -ENOMEM; - - if (!read && unlikely(copy_from_user(data, buf, len))) { - ret = -EFAULT; - goto error; + if (io_data->aio && !io_data->read) { + int i; + size_t pos = 0; + for (i = 0; i < io_data->nr_segs; i++) { + if (unlikely(copy_from_user(&data[pos], + io_data->iovec[i].iov_base, + io_data->iovec[i].iov_len))) { + ret = -EFAULT; + goto error; + } + pos += io_data->iovec[i].iov_len; + } + } else { + if (!io_data->read && + unlikely(__copy_from_user(data, io_data->buf, + io_data->len))) { + ret = -EFAULT; + goto error; + } } } @@ -661,40 +797,78 @@ static ssize_t ffs_epfile_io(struct file *file, ret = -EBADMSG; } else { /* Fire the request */ - DECLARE_COMPLETION_ONSTACK(done); + struct usb_request *req; - struct usb_request *req = ep->req; - req->context = &done; - req->complete = ffs_epfile_io_complete; - req->buf = data; - req->length = data_len; + if (io_data->aio) { + req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); + if (unlikely(!req)) + goto error_lock; - ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + req->buf = data; + req->length = io_data->len; - spin_unlock_irq(&epfile->ffs->eps_lock); + io_data->buf = data; + io_data->ep = ep->ep; + io_data->req = req; - if (unlikely(ret < 0)) { - /* nop */ - } else if (unlikely(wait_for_completion_interruptible(&done))) { - ret = -EINTR; - usb_ep_dequeue(ep->ep, req); + req->context = io_data; + req->complete = ffs_epfile_async_io_complete; + + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + if (unlikely(ret)) { + usb_ep_free_request(ep->ep, req); + goto error_lock; + } + ret = -EIOCBQUEUED; + + spin_unlock_irq(&epfile->ffs->eps_lock); } else { - /* - * XXX We may end up silently droping data here. - * Since data_len (i.e. req->length) may be bigger - * than len (after being rounded up to maxpacketsize), - * we may end up with more data then user space has - * space for. - */ - ret = ep->status; - if (read && ret > 0 && - unlikely(copy_to_user(buf, data, - min_t(size_t, ret, len)))) - ret = -EFAULT; + DECLARE_COMPLETION_ONSTACK(done); + + req = ep->req; + req->buf = data; + req->length = io_data->len; + + req->context = &done; + req->complete = ffs_epfile_io_complete; + + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + + spin_unlock_irq(&epfile->ffs->eps_lock); + + if (unlikely(ret < 0)) { + /* nop */ + } else if (unlikely( + wait_for_completion_interruptible(&done))) { + ret = -EINTR; + usb_ep_dequeue(ep->ep, req); + } else { + /* + * XXX We may end up silently droping data + * here. Since data_len (i.e. req->length) may + * be bigger than len (after being rounded up + * to maxpacketsize), we may end up with more + * data then user space has space for. + */ + ret = ep->status; + if (io_data->read && ret > 0) { + ret = min_t(size_t, ret, io_data->len); + + if (unlikely(copy_to_user(io_data->buf, + data, ret))) + ret = -EFAULT; + } + } + kfree(data); } } mutex_unlock(&epfile->mutex); + return ret; + +error_lock: + spin_unlock_irq(&epfile->ffs->eps_lock); + mutex_unlock(&epfile->mutex); error: kfree(data); return ret; @@ -704,17 +878,31 @@ static ssize_t ffs_epfile_write(struct file *file, const char __user *buf, size_t len, loff_t *ptr) { + struct ffs_io_data io_data; + ENTER(); - return ffs_epfile_io(file, (char __user *)buf, len, 0); + io_data.aio = false; + io_data.read = false; + io_data.buf = (char * __user)buf; + io_data.len = len; + + return ffs_epfile_io(file, &io_data); } static ssize_t ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr) { + struct ffs_io_data io_data; + ENTER(); - return ffs_epfile_io(file, buf, len, 1); + io_data.aio = false; + io_data.read = true; + io_data.buf = buf; + io_data.len = len; + + return ffs_epfile_io(file, &io_data); } static int @@ -733,6 +921,89 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } +static int ffs_aio_cancel(struct kiocb *kiocb) +{ + struct ffs_io_data *io_data = kiocb->private; + struct ffs_epfile *epfile = kiocb->ki_filp->private_data; + int value; + + ENTER(); + + spin_lock_irq(&epfile->ffs->eps_lock); + + if (likely(io_data && io_data->ep && io_data->req)) + value = usb_ep_dequeue(io_data->ep, io_data->req); + else + value = -EINVAL; + + spin_unlock_irq(&epfile->ffs->eps_lock); + + return value; +} + +static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb, + const struct iovec *iovec, + unsigned long nr_segs, loff_t loff) +{ + struct ffs_io_data *io_data; + + ENTER(); + + io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); + if (unlikely(!io_data)) + return -ENOMEM; + + io_data->aio = true; + io_data->read = false; + io_data->kiocb = kiocb; + io_data->iovec = iovec; + io_data->nr_segs = nr_segs; + io_data->len = kiocb->ki_nbytes; + io_data->mm = current->mm; + + kiocb->private = io_data; + + kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + + return ffs_epfile_io(kiocb->ki_filp, io_data); +} + +static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb, + const struct iovec *iovec, + unsigned long nr_segs, loff_t loff) +{ + struct ffs_io_data *io_data; + struct iovec *iovec_copy; + + ENTER(); + + iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL); + if (unlikely(!iovec_copy)) + return -ENOMEM; + + memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs); + + io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); + if (unlikely(!io_data)) { + kfree(iovec_copy); + return -ENOMEM; + } + + io_data->aio = true; + io_data->read = true; + io_data->kiocb = kiocb; + io_data->iovec = iovec_copy; + io_data->nr_segs = nr_segs; + io_data->len = kiocb->ki_nbytes; + io_data->mm = current->mm; + + kiocb->private = io_data; + + kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + + return ffs_epfile_io(kiocb->ki_filp, io_data); +} + static int ffs_epfile_release(struct inode *inode, struct file *file) { @@ -789,6 +1060,8 @@ static const struct file_operations ffs_epfile_operations = { .open = ffs_epfile_open, .write = ffs_epfile_write, .read = ffs_epfile_read, + .aio_write = ffs_epfile_aio_write, + .aio_read = ffs_epfile_aio_read, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, }; @@ -1172,7 +1445,7 @@ static void ffs_data_clear(struct ffs_data *ffs) if (ffs->epfiles) ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); - kfree(ffs->raw_descs); + kfree(ffs->raw_descs_data); kfree(ffs->raw_strings); kfree(ffs->stringtabs); } @@ -1184,14 +1457,15 @@ static void ffs_data_reset(struct ffs_data *ffs) ffs_data_clear(ffs); ffs->epfiles = NULL; + ffs->raw_descs_data = NULL; ffs->raw_descs = NULL; ffs->raw_strings = NULL; ffs->stringtabs = NULL; ffs->raw_descs_length = 0; - ffs->raw_fs_descs_length = 0; ffs->fs_descs_count = 0; ffs->hs_descs_count = 0; + ffs->ss_descs_count = 0; ffs->strings_count = 0; ffs->interfaces_count = 0; @@ -1334,7 +1608,24 @@ static int ffs_func_eps_enable(struct ffs_function *func) spin_lock_irqsave(&func->ffs->eps_lock, flags); do { struct usb_endpoint_descriptor *ds; - ds = ep->descs[ep->descs[1] ? 1 : 0]; + int desc_idx; + + if (ffs->gadget->speed == USB_SPEED_SUPER) + desc_idx = 2; + else if (ffs->gadget->speed == USB_SPEED_HIGH) + desc_idx = 1; + else + desc_idx = 0; + + /* fall-back to lower speed if desc missing for current speed */ + do { + ds = ep->descs[desc_idx]; + } while (!ds && --desc_idx >= 0); + + if (!ds) { + ret = -EINVAL; + break; + } ep->ep->driver_data = ep; ep->ep->desc = ds; @@ -1469,6 +1760,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len, } break; + case USB_DT_SS_ENDPOINT_COMP: + pr_vdebug("EP SS companion descriptor\n"); + if (length != sizeof(struct usb_ss_ep_comp_descriptor)) + goto inv_length; + break; + case USB_DT_OTHER_SPEED_CONFIG: case USB_DT_INTERFACE_POWER: case USB_DT_DEBUG: @@ -1579,60 +1876,76 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { - unsigned fs_count, hs_count; - int fs_len, ret = -EINVAL; - char *data = _data; + char *data = _data, *raw_descs; + unsigned counts[3], flags; + int ret = -EINVAL, i; ENTER(); - if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC || - get_unaligned_le32(data + 4) != len)) + if (get_unaligned_le32(data + 4) != len) goto error; - fs_count = get_unaligned_le32(data + 8); - hs_count = get_unaligned_le32(data + 12); - if (!fs_count && !hs_count) - goto einval; - - data += 16; - len -= 16; - - if (likely(fs_count)) { - fs_len = ffs_do_descs(fs_count, data, len, - __ffs_data_do_entity, ffs); - if (unlikely(fs_len < 0)) { - ret = fs_len; + switch (get_unaligned_le32(data)) { + case FUNCTIONFS_DESCRIPTORS_MAGIC: + flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC; + data += 8; + len -= 8; + break; + case FUNCTIONFS_DESCRIPTORS_MAGIC_V2: + flags = get_unaligned_le32(data + 8); + if (flags & ~(FUNCTIONFS_HAS_FS_DESC | + FUNCTIONFS_HAS_HS_DESC | + FUNCTIONFS_HAS_SS_DESC)) { + ret = -ENOSYS; goto error; } + data += 12; + len -= 12; + break; + default: + goto error; + } - data += fs_len; - len -= fs_len; - } else { - fs_len = 0; + /* Read fs_count, hs_count and ss_count (if present) */ + for (i = 0; i < 3; ++i) { + if (!(flags & (1 << i))) { + counts[i] = 0; + } else if (len < 4) { + goto error; + } else { + counts[i] = get_unaligned_le32(data); + data += 4; + len -= 4; + } } - if (likely(hs_count)) { - ret = ffs_do_descs(hs_count, data, len, + /* Read descriptors */ + raw_descs = data; + for (i = 0; i < 3; ++i) { + if (!counts[i]) + continue; + ret = ffs_do_descs(counts[i], data, len, __ffs_data_do_entity, ffs); - if (unlikely(ret < 0)) + if (ret < 0) goto error; - } else { - ret = 0; + data += ret; + len -= ret; } - if (unlikely(len != ret)) - goto einval; + if (raw_descs == data || len) { + ret = -EINVAL; + goto error; + } - ffs->raw_fs_descs_length = fs_len; - ffs->raw_descs_length = fs_len + ret; - ffs->raw_descs = _data; - ffs->fs_descs_count = fs_count; - ffs->hs_descs_count = hs_count; + ffs->raw_descs_data = _data; + ffs->raw_descs = raw_descs; + ffs->raw_descs_length = data - raw_descs; + ffs->fs_descs_count = counts[0]; + ffs->hs_descs_count = counts[1]; + ffs->ss_descs_count = counts[2]; return 0; -einval: - ret = -EINVAL; error: kfree(_data); return ret; @@ -1789,7 +2102,7 @@ static void __ffs_event_add(struct ffs_data *ffs, * the source does nothing. */ if (ffs->setup_state == FFS_SETUP_PENDING) - ffs->setup_state = FFS_SETUP_CANCELED; + ffs->setup_state = FFS_SETUP_CANCELLED; switch (type) { case FUNCTIONFS_RESUME: @@ -1850,21 +2163,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, struct usb_endpoint_descriptor *ds = (void *)desc; struct ffs_function *func = priv; struct ffs_ep *ffs_ep; - - /* - * If hs_descriptors is not NULL then we are reading hs - * descriptors now - */ - const int isHS = func->function.hs_descriptors != NULL; - unsigned idx; + unsigned ep_desc_id, idx; + static const char *speed_names[] = { "full", "high", "super" }; if (type != FFS_DESCRIPTOR) return 0; - if (isHS) + /* + * If ss_descriptors is not NULL, we are reading super speed + * descriptors; if hs_descriptors is not NULL, we are reading high + * speed descriptors; otherwise, we are reading full speed + * descriptors. + */ + if (func->function.ss_descriptors) { + ep_desc_id = 2; + func->function.ss_descriptors[(long)valuep] = desc; + } else if (func->function.hs_descriptors) { + ep_desc_id = 1; func->function.hs_descriptors[(long)valuep] = desc; - else + } else { + ep_desc_id = 0; func->function.fs_descriptors[(long)valuep] = desc; + } if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) return 0; @@ -1872,13 +2192,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; ffs_ep = func->eps + idx; - if (unlikely(ffs_ep->descs[isHS])) { - pr_vdebug("two %sspeed descriptors for EP %d\n", - isHS ? "high" : "full", + if (unlikely(ffs_ep->descs[ep_desc_id])) { + pr_err("two %sspeed descriptors for EP %d\n", + speed_names[ep_desc_id], ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); return -EINVAL; } - ffs_ep->descs[isHS] = ds; + ffs_ep->descs[ep_desc_id] = ds; ffs_dump_mem(": Original ep desc", ds, ds->bLength); if (ffs_ep->ep) { @@ -2022,8 +2342,10 @@ static int _ffs_func_bind(struct usb_configuration *c, const int full = !!func->ffs->fs_descs_count; const int high = gadget_is_dualspeed(func->gadget) && func->ffs->hs_descs_count; + const int super = gadget_is_superspeed(func->gadget) && + func->ffs->ss_descs_count; - int ret; + int fs_len, hs_len, ret; /* Make it a single chunk, less management later on */ vla_group(d); @@ -2032,15 +2354,16 @@ static int _ffs_func_bind(struct usb_configuration *c, full ? ffs->fs_descs_count + 1 : 0); vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs, high ? ffs->hs_descs_count + 1 : 0); + vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, + super ? ffs->ss_descs_count + 1 : 0); vla_item_with_sz(d, short, inums, ffs->interfaces_count); - vla_item_with_sz(d, char, raw_descs, - high ? ffs->raw_descs_length : ffs->raw_fs_descs_length); + vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); char *vlabuf; ENTER(); - /* Only high speed but not supported by gadget? */ - if (unlikely(!(full | high))) + /* Has descriptors only for speeds gadget does not support */ + if (unlikely(!(full | high | super))) return -ENOTSUPP; /* Allocate a single chunk, less management later on */ @@ -2050,8 +2373,10 @@ static int _ffs_func_bind(struct usb_configuration *c, /* Zero */ memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); - memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16, - d_raw_descs__sz); + /* Copy descriptors */ + memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, + ffs->raw_descs_length); + memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz); for (ret = ffs->eps_count; ret; --ret) { struct ffs_ep *ptr; @@ -2073,22 +2398,38 @@ static int _ffs_func_bind(struct usb_configuration *c, */ if (likely(full)) { func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs); - ret = ffs_do_descs(ffs->fs_descs_count, - vla_ptr(vlabuf, d, raw_descs), - d_raw_descs__sz, - __ffs_func_bind_do_descs, func); - if (unlikely(ret < 0)) + fs_len = ffs_do_descs(ffs->fs_descs_count, + vla_ptr(vlabuf, d, raw_descs), + d_raw_descs__sz, + __ffs_func_bind_do_descs, func); + if (unlikely(fs_len < 0)) { + ret = fs_len; goto error; + } } else { - ret = 0; + fs_len = 0; } if (likely(high)) { func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs); - ret = ffs_do_descs(ffs->hs_descs_count, - vla_ptr(vlabuf, d, raw_descs) + ret, - d_raw_descs__sz - ret, - __ffs_func_bind_do_descs, func); + hs_len = ffs_do_descs(ffs->hs_descs_count, + vla_ptr(vlabuf, d, raw_descs) + fs_len, + d_raw_descs__sz - fs_len, + __ffs_func_bind_do_descs, func); + if (unlikely(hs_len < 0)) { + ret = hs_len; + goto error; + } + } else { + hs_len = 0; + } + + if (likely(super)) { + func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); + ret = ffs_do_descs(ffs->ss_descs_count, + vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, + d_raw_descs__sz - fs_len - hs_len, + __ffs_func_bind_do_descs, func); if (unlikely(ret < 0)) goto error; } @@ -2099,7 +2440,8 @@ static int _ffs_func_bind(struct usb_configuration *c, * now. */ ret = ffs_do_descs(ffs->fs_descs_count + - (high ? ffs->hs_descs_count : 0), + (high ? ffs->hs_descs_count : 0) + + (super ? ffs->ss_descs_count : 0), vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz, __ffs_func_bind_do_nums, func); if (unlikely(ret < 0)) @@ -2258,7 +2600,7 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf) static LIST_HEAD(ffs_devices); -static struct ffs_dev *_ffs_find_dev(const char *name) +static struct ffs_dev *_ffs_do_find_dev(const char *name) { struct ffs_dev *dev; @@ -2275,7 +2617,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name) /* * ffs_lock must be taken by the caller of this function */ -static struct ffs_dev *ffs_get_single_dev(void) +static struct ffs_dev *_ffs_get_single_dev(void) { struct ffs_dev *dev; @@ -2291,15 +2633,15 @@ static struct ffs_dev *ffs_get_single_dev(void) /* * ffs_lock must be taken by the caller of this function */ -static struct ffs_dev *ffs_find_dev(const char *name) +static struct ffs_dev *_ffs_find_dev(const char *name) { struct ffs_dev *dev; - dev = ffs_get_single_dev(); + dev = _ffs_get_single_dev(); if (dev) return dev; - return _ffs_find_dev(name); + return _ffs_do_find_dev(name); } /* Configfs support *********************************************************/ @@ -2335,7 +2677,7 @@ static void ffs_free_inst(struct usb_function_instance *f) opts = to_f_fs_opts(f); ffs_dev_lock(); - ffs_free_dev(opts->dev); + _ffs_free_dev(opts->dev); ffs_dev_unlock(); kfree(opts); } @@ -2390,7 +2732,7 @@ static struct usb_function_instance *ffs_alloc_inst(void) opts->func_inst.set_inst_name = ffs_set_inst_name; opts->func_inst.free_func_inst = ffs_free_inst; ffs_dev_lock(); - dev = ffs_alloc_dev(); + dev = _ffs_alloc_dev(); ffs_dev_unlock(); if (IS_ERR(dev)) { kfree(opts); @@ -2446,6 +2788,7 @@ static void ffs_func_unbind(struct usb_configuration *c, */ func->function.fs_descriptors = NULL; func->function.hs_descriptors = NULL; + func->function.ss_descriptors = NULL; func->interfaces_nums = NULL; ffs_event_add(ffs, FUNCTIONFS_UNBIND); @@ -2478,12 +2821,12 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi) /* * ffs_lock must be taken by the caller of this function */ -struct ffs_dev *ffs_alloc_dev(void) +static struct ffs_dev *_ffs_alloc_dev(void) { struct ffs_dev *dev; int ret; - if (ffs_get_single_dev()) + if (_ffs_get_single_dev()) return ERR_PTR(-EBUSY); dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -2511,10 +2854,10 @@ static int _ffs_name_dev(struct ffs_dev *dev, const char *name) { struct ffs_dev *existing; - existing = _ffs_find_dev(name); + existing = _ffs_do_find_dev(name); if (existing) return -EBUSY; - + dev->name = name; return 0; @@ -2555,7 +2898,7 @@ EXPORT_SYMBOL(ffs_single_dev); /* * ffs_lock must be taken by the caller of this function */ -void ffs_free_dev(struct ffs_dev *dev) +static void _ffs_free_dev(struct ffs_dev *dev) { list_del(&dev->entry); if (dev->name_allocated) @@ -2572,7 +2915,7 @@ static void *ffs_acquire_dev(const char *dev_name) ENTER(); ffs_dev_lock(); - ffs_dev = ffs_find_dev(dev_name); + ffs_dev = _ffs_find_dev(dev_name); if (!ffs_dev) ffs_dev = ERR_PTR(-ENODEV); else if (ffs_dev->mounted) @@ -2595,11 +2938,12 @@ static void ffs_release_dev(struct ffs_data *ffs_data) ffs_dev_lock(); ffs_dev = ffs_data->private_data; - if (ffs_dev) + if (ffs_dev) { ffs_dev->mounted = false; - - if (ffs_dev->ffs_release_dev_callback) - ffs_dev->ffs_release_dev_callback(ffs_dev); + + if (ffs_dev->ffs_release_dev_callback) + ffs_dev->ffs_release_dev_callback(ffs_dev); + } ffs_dev_unlock(); } diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index 36d4bb23087f..807b31c0edc3 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -664,9 +664,10 @@ static int f_midi_register_card(struct f_midi *midi) .dev_free = f_midi_snd_free, }; - err = snd_card_create(midi->index, midi->id, THIS_MODULE, 0, &card); + err = snd_card_new(&midi->gadget->dev, midi->index, midi->id, + THIS_MODULE, 0, &card); if (err < 0) { - ERROR(midi, "snd_card_create() failed\n"); + ERROR(midi, "snd_card_new() failed\n"); goto fail; } midi->card = card; @@ -703,8 +704,6 @@ static int f_midi_register_card(struct f_midi *midi) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); - snd_card_set_dev(card, &midi->gadget->dev); - /* register it - we're ready to go */ err = snd_card_register(card); if (err < 0) { diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index f1a59190ac9a..df4a0dcbc993 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -276,7 +276,7 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } net = gether_connect(&geth->port); - return IS_ERR(net) ? PTR_ERR(net) : 0; + return PTR_RET(net); } static void geth_disable(struct usb_function *f) diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c index 2f23566e53d8..bc23040c7790 100644 --- a/drivers/usb/gadget/f_uac2.c +++ b/drivers/usb/gadget/f_uac2.c @@ -394,7 +394,7 @@ static int snd_uac2_probe(struct platform_device *pdev) int err; /* Choose any slot, with no id */ - err = snd_card_create(-1, NULL, THIS_MODULE, 0, &card); + err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); if (err < 0) return err; @@ -421,8 +421,6 @@ static int snd_uac2_probe(struct platform_device *pdev) strcpy(card->shortname, "UAC2_Gadget"); sprintf(card->longname, "UAC2_Gadget %i", pdev->id); - snd_card_set_dev(card, &pdev->dev); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX); diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/gr_udc.c index 914cbd84ee40..f984ee75324d 100644 --- a/drivers/usb/gadget/gr_udc.c +++ b/drivers/usb/gadget/gr_udc.c @@ -225,14 +225,8 @@ static void gr_dfs_create(struct gr_udc *dev) const char *name = "gr_udc_state"; dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL); - if (IS_ERR(dev->dfs_root)) { - dev_err(dev->dev, "Failed to create debugfs directory\n"); - return; - } - dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, - dev, &gr_dfs_fops); - if (IS_ERR(dev->dfs_state)) - dev_err(dev->dev, "Failed to create debugfs file %s\n", name); + dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev, + &gr_dfs_fops); } static void gr_dfs_delete(struct gr_udc *dev) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index b94c049ab0d0..b5be6f0308c2 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -439,11 +439,9 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */ value = -ENOMEM; - kbuf = kmalloc (len, GFP_KERNEL); - if (!kbuf) - goto free1; - if (copy_from_user (kbuf, buf, len)) { - value = -EFAULT; + kbuf = memdup_user(buf, len); + if (!kbuf) { + value = PTR_ERR(kbuf); goto free1; } @@ -452,7 +450,6 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) data->name, len, (int) value); free1: mutex_unlock(&data->lock); - kfree (kbuf); return value; } diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 049ebab0d360..a139894c600f 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -3295,9 +3295,9 @@ usb_clk_enable_fail: pll_set_fail: clk_disable(udc->usb_pll_clk); pll_enable_fail: - clk_put(udc->usb_slv_clk); -usb_otg_clk_get_fail: clk_put(udc->usb_otg_clk); +usb_otg_clk_get_fail: + clk_put(udc->usb_slv_clk); usb_clk_get_fail: clk_put(udc->usb_pll_clk); pll_get_fail: diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 69b76efd11e9..6474081dcbaf 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -427,12 +427,17 @@ setup_rx_reqs(struct printer_dev *dev) req->length = USB_BUFSIZE; req->complete = rx_complete; + /* here, we unlock, and only unlock, to avoid deadlock. */ + spin_unlock(&dev->lock); error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); + spin_lock(&dev->lock); if (error) { DBG(dev, "rx submit --> %d\n", error); list_add(&req->list, &dev->rx_reqs); break; - } else { + } + /* if the req is empty, then add it into dev->rx_reqs_active. */ + else if (list_empty(&req->list)) { list_add(&req->list, &dev->rx_reqs_active); } } @@ -1133,6 +1138,7 @@ static int __init printer_bind_config(struct usb_configuration *c) NULL, "g_printer"); if (IS_ERR(dev->pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); + status = PTR_ERR(dev->pdev); goto fail; } diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 1172eaeddd85..2a9cb674926a 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -617,7 +617,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, to_write = DIV_ROUND_UP(to_write, 4); data = hs_req->req.buf + buf_pos; - writesl(hsotg->regs + EPFIFO(hs_ep->index), data, to_write); + iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write); return (to_write >= can_write) ? -ENOSPC : 0; } @@ -720,8 +720,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, ureq->length, ureq->actual); if (0) dev_dbg(hsotg->dev, - "REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n", - ureq->buf, length, ureq->dma, + "REQ buf %p len %d dma 0x%pad noi=%d zp=%d snok=%d\n", + ureq->buf, length, &ureq->dma, ureq->no_interrupt, ureq->zero, ureq->short_not_ok); maxreq = get_ep_limit(hs_ep); @@ -789,8 +789,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); writel(ureq->dma, hsotg->regs + dma_reg); - dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n", - __func__, ureq->dma, dma_reg); + dev_dbg(hsotg->dev, "%s: 0x%pad => 0x%08x\n", + __func__, &ureq->dma, dma_reg); } ctrl |= DxEPCTL_EPEna; /* ensure ep enabled */ @@ -1186,6 +1186,41 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); /** + * s3c_hsotg_stall_ep0 - stall ep0 + * @hsotg: The device state + * + * Set stall for ep0 as response for setup request. + */ +static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) { + struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; + u32 reg; + u32 ctrl; + + dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in); + reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; + + /* + * DxEPCTL_Stall will be cleared by EP once it has + * taken effect, so no need to clear later. + */ + + ctrl = readl(hsotg->regs + reg); + ctrl |= DxEPCTL_Stall; + ctrl |= DxEPCTL_CNAK; + writel(ctrl, hsotg->regs + reg); + + dev_dbg(hsotg->dev, + "written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n", + ctrl, reg, readl(hsotg->regs + reg)); + + /* + * complete won't be called, so we enqueue + * setup request here + */ + s3c_hsotg_enqueue_setup(hsotg); +} + +/** * s3c_hsotg_process_control - process a control request * @hsotg: The device state * @ctrl: The control request received @@ -1262,38 +1297,8 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, * so respond with a STALL for the status stage to indicate failure. */ - if (ret < 0) { - u32 reg; - u32 ctrl; - - dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in); - reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; - - /* - * DxEPCTL_Stall will be cleared by EP once it has - * taken effect, so no need to clear later. - */ - - ctrl = readl(hsotg->regs + reg); - ctrl |= DxEPCTL_Stall; - ctrl |= DxEPCTL_CNAK; - writel(ctrl, hsotg->regs + reg); - - dev_dbg(hsotg->dev, - "written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n", - ctrl, reg, readl(hsotg->regs + reg)); - - /* - * don't believe we need to anything more to get the EP - * to reply with a STALL packet - */ - - /* - * complete won't be called, so we enqueue - * setup request here - */ - s3c_hsotg_enqueue_setup(hsotg); - } + if (ret < 0) + s3c_hsotg_stall_ep0(hsotg); } /** @@ -1488,7 +1493,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) * note, we might over-write the buffer end by 3 bytes depending on * alignment of the data. */ - readsl(fifo, hs_req->req.buf + read_ptr, to_read); + ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read); } /** @@ -2832,6 +2837,15 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value); + if (index == 0) { + if (value) + s3c_hsotg_stall_ep0(hs); + else + dev_warn(hs->dev, + "%s: can't clear halt on ep0\n", __func__); + return 0; + } + /* write both IN and OUT control registers */ epreg = DIEPCTL(index); @@ -3760,10 +3774,55 @@ static int s3c_hsotg_remove(struct platform_device *pdev) return 0; } -#if 1 -#define s3c_hsotg_suspend NULL -#define s3c_hsotg_resume NULL -#endif +static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct s3c_hsotg *hsotg = platform_get_drvdata(pdev); + unsigned long flags; + int ret = 0; + + if (hsotg->driver) + dev_info(hsotg->dev, "suspending usb gadget %s\n", + hsotg->driver->driver.name); + + spin_lock_irqsave(&hsotg->lock, flags); + s3c_hsotg_disconnect(hsotg); + s3c_hsotg_phy_disable(hsotg); + hsotg->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock_irqrestore(&hsotg->lock, flags); + + if (hsotg->driver) { + int ep; + for (ep = 0; ep < hsotg->num_of_eps; ep++) + s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); + + ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + } + + return ret; +} + +static int s3c_hsotg_resume(struct platform_device *pdev) +{ + struct s3c_hsotg *hsotg = platform_get_drvdata(pdev); + unsigned long flags; + int ret = 0; + + if (hsotg->driver) { + dev_info(hsotg->dev, "resuming usb gadget %s\n", + hsotg->driver->driver.name); + ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + } + + spin_lock_irqsave(&hsotg->lock, flags); + hsotg->last_rst = jiffies; + s3c_hsotg_phy_enable(hsotg); + s3c_hsotg_core_init(hsotg); + spin_unlock_irqrestore(&hsotg->lock, flags); + + return ret; +} #ifdef CONFIG_OF static const struct of_device_id s3c_hsotg_of_ids[] = { diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index ea4bbfe72ec0..10c6a128250c 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1344,7 +1344,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) return 0; err_add_udc: -err_add_device: clk_disable(hsudc->uclk); err_res: if (!IS_ERR_OR_NULL(hsudc->transceiver)) diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index 0f8aad78b54f..460c266b8e24 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -1613,7 +1613,7 @@ static struct se_wwn *usbg_make_tport( return ERR_PTR(-ENOMEM); } tport->tport_wwpn = wwpn; - snprintf(tport->tport_name, sizeof(tport->tport_name), wnn_name); + snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name); return &tport->tport_wwn; } diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index b7d4f82872b7..50d09c289137 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -48,6 +48,8 @@ #define UETH__VERSION "29-May-2008" +#define GETHER_NAPI_WEIGHT 32 + struct eth_dev { /* lock is held while accessing port_usb */ @@ -72,6 +74,7 @@ struct eth_dev { struct sk_buff_head *list); struct work_struct work; + struct napi_struct rx_napi; unsigned long todo; #define WORK_RX_MEMORY 0 @@ -253,18 +256,16 @@ enomem: DBG(dev, "rx submit --> %d\n", retval); if (skb) dev_kfree_skb_any(skb); - spin_lock_irqsave(&dev->req_lock, flags); - list_add(&req->list, &dev->rx_reqs); - spin_unlock_irqrestore(&dev->req_lock, flags); } return retval; } static void rx_complete(struct usb_ep *ep, struct usb_request *req) { - struct sk_buff *skb = req->context, *skb2; + struct sk_buff *skb = req->context; struct eth_dev *dev = ep->driver_data; int status = req->status; + bool rx_queue = 0; switch (status) { @@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) } else { skb_queue_tail(&dev->rx_frames, skb); } - skb = NULL; - - skb2 = skb_dequeue(&dev->rx_frames); - while (skb2) { - if (status < 0 - || ETH_HLEN > skb2->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); - dev_kfree_skb_any(skb2); - goto next_frame; - } - skb2->protocol = eth_type_trans(skb2, dev->net); - dev->net->stats.rx_packets++; - dev->net->stats.rx_bytes += skb2->len; - - /* no buffer copies needed, unless hardware can't - * use skb buffers. - */ - status = netif_rx(skb2); -next_frame: - skb2 = skb_dequeue(&dev->rx_frames); - } + if (!status) + rx_queue = 1; break; /* software-driven interface shutdown */ @@ -334,22 +313,20 @@ quiesce: /* FALLTHROUGH */ default: + rx_queue = 1; + dev_kfree_skb_any(skb); dev->net->stats.rx_errors++; DBG(dev, "rx status %d\n", status); break; } - if (skb) - dev_kfree_skb_any(skb); - if (!netif_running(dev->net)) { clean: spin_lock(&dev->req_lock); list_add(&req->list, &dev->rx_reqs); spin_unlock(&dev->req_lock); - req = NULL; - } - if (req) - rx_submit(dev, req, GFP_ATOMIC); + + if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi))) + __napi_schedule(&dev->rx_napi); } static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) @@ -414,16 +391,24 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) { struct usb_request *req; unsigned long flags; + int rx_counts = 0; /* fill unused rxq slots with some skb */ spin_lock_irqsave(&dev->req_lock, flags); while (!list_empty(&dev->rx_reqs)) { + + if (++rx_counts > qlen(dev->gadget, dev->qmult)) + break; + req = container_of(dev->rx_reqs.next, struct usb_request, list); list_del_init(&req->list); spin_unlock_irqrestore(&dev->req_lock, flags); if (rx_submit(dev, req, gfp_flags) < 0) { + spin_lock_irqsave(&dev->req_lock, flags); + list_add(&req->list, &dev->rx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); defer_kevent(dev, WORK_RX_MEMORY); return; } @@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) spin_unlock_irqrestore(&dev->req_lock, flags); } +static int gether_poll(struct napi_struct *napi, int budget) +{ + struct eth_dev *dev = container_of(napi, struct eth_dev, rx_napi); + struct sk_buff *skb; + unsigned int work_done = 0; + int status = 0; + + while ((skb = skb_dequeue(&dev->rx_frames))) { + if (status < 0 + || ETH_HLEN > skb->len + || skb->len > VLAN_ETH_FRAME_LEN) { + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + DBG(dev, "rx length %d\n", skb->len); + dev_kfree_skb_any(skb); + continue; + } + skb->protocol = eth_type_trans(skb, dev->net); + dev->net->stats.rx_packets++; + dev->net->stats.rx_bytes += skb->len; + + status = netif_rx_ni(skb); + } + + if (netif_running(dev->net)) { + rx_fill(dev, GFP_KERNEL); + work_done++; + } + + if (work_done < budget) + napi_complete(&dev->rx_napi); + + return work_done; +} + static void eth_work(struct work_struct *work) { struct eth_dev *dev = container_of(work, struct eth_dev, work); @@ -625,6 +645,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) /* and open the tx floodgates */ atomic_set(&dev->tx_qlen, 0); netif_wake_queue(dev->net); + napi_enable(&dev->rx_napi); } static int eth_open(struct net_device *net) @@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net) unsigned long flags; VDBG(dev, "%s\n", __func__); + napi_disable(&dev->rx_napi); netif_stop_queue(net); DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", @@ -768,6 +790,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, return ERR_PTR(-ENOMEM); dev = netdev_priv(net); + netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT); spin_lock_init(&dev->lock); spin_lock_init(&dev->req_lock); INIT_WORK(&dev->work, eth_work); @@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname) return ERR_PTR(-ENOMEM); dev = netdev_priv(net); + netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT); spin_lock_init(&dev->lock); spin_lock_init(&dev->req_lock); INIT_WORK(&dev->work, eth_work); @@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link) { struct eth_dev *dev = link->ioport; struct usb_request *req; + struct sk_buff *skb; WARN_ON(!dev); if (!dev) @@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link) spin_lock(&dev->req_lock); } spin_unlock(&dev->req_lock); + + spin_lock(&dev->rx_frames.lock); + while ((skb = __skb_dequeue(&dev->rx_frames))) + dev_kfree_skb_any(skb); + spin_unlock(&dev->rx_frames.lock); + link->in_ep->driver_data = NULL; link->in_ep->desc = NULL; diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index bc2d3718219b..bf0ba375d459 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -65,10 +65,8 @@ static inline void ffs_dev_unlock(void) mutex_unlock(&ffs_lock); } -struct ffs_dev *ffs_alloc_dev(void); int ffs_name_dev(struct ffs_dev *dev, const char *name); int ffs_single_dev(struct ffs_dev *dev); -void ffs_free_dev(struct ffs_dev *dev); struct ffs_epfile; struct ffs_function; @@ -125,7 +123,7 @@ enum ffs_setup_state { * setup. If this state is set read/write on ep0 return * -EIDRM. This state is only set when adding event. */ - FFS_SETUP_CANCELED + FFS_SETUP_CANCELLED }; struct ffs_data { @@ -156,7 +154,6 @@ struct ffs_data { */ struct usb_request *ep0req; /* P: mutex */ struct completion ep0req_completion; /* P: mutex */ - int ep0req_status; /* P: mutex */ /* reference counter */ atomic_t ref; @@ -168,19 +165,18 @@ struct ffs_data { /* * Possible transitions: - * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock + * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock * happens only in ep0 read which is P: mutex - * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock + * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock * happens only in ep0 i/o which is P: mutex - * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock - * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg + * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock + * + FFS_SETUP_CANCELLED -> FFS_NO_SETUP -- cmpxchg + * + * This field should never be accessed directly and instead + * ffs_setup_state_clear_cancelled function should be used. */ enum ffs_setup_state setup_state; -#define FFS_SETUP_STATE(ffs) \ - ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ - FFS_SETUP_CANCELED, FFS_NO_SETUP)) - /* Events & such. */ struct { u8 types[4]; @@ -210,16 +206,16 @@ struct ffs_data { /* filled by __ffs_data_got_descs() */ /* - * Real descriptors are 16 bytes after raw_descs (so you need - * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the - * first full speed descriptor). raw_descs_length and - * raw_fs_descs_length do not have those 16 bytes added. + * raw_descs is what you kfree, real_descs points inside of raw_descs, + * where full speed, high speed and super speed descriptors start. + * real_descs_length is the length of all those descriptors. */ + const void *raw_descs_data; const void *raw_descs; unsigned raw_descs_length; - unsigned raw_fs_descs_length; unsigned fs_descs_count; unsigned hs_descs_count; + unsigned ss_descs_count; unsigned short strings_count; unsigned short interfaces_count; diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index b369292d4b90..ad0aca812002 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -549,8 +549,8 @@ static void gs_rx_push(unsigned long _port) port->read_started--; } - /* Push from tty to ldisc; without low_latency set this is handled by - * a workqueue, so we won't get callbacks and can hold port_lock + /* Push from tty to ldisc; this is handled by a workqueue, + * so we won't get callbacks and can hold port_lock */ if (do_push) tty_flip_buffer_push(&port->port); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index a9707da7da0b..3d9e54062d62 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -314,7 +314,6 @@ config USB_ISP1760_HCD config USB_ISP1362_HCD tristate "ISP1362 HCD support" - default N ---help--- Supports the Philips ISP1362 chip as a host controller @@ -326,7 +325,6 @@ config USB_ISP1362_HCD config USB_FUSBH200_HCD tristate "FUSBH200 HCD support" depends on USB - default N ---help--- Faraday FUSBH200 is designed to meet USB2.0 EHCI specification with minor modification. @@ -337,7 +335,6 @@ config USB_FUSBH200_HCD config USB_FOTG210_HCD tristate "FOTG210 HCD support" depends on USB - default N ---help--- Faraday FOTG210 is an OTG controller which can be configured as an USB2.0 host. It is designed to meet USB2.0 EHCI specification @@ -584,7 +581,6 @@ config FHCI_DEBUG config USB_U132_HCD tristate "Elan U132 Adapter Host Controller" depends on USB_FTDI_ELAN - default M help The U132 adapter is a USB to CardBus adapter specifically designed for PC cards that contain an OHCI host controller. Typical PC cards diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 01536cfd361d..b3a0e11073aa 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -3,6 +3,7 @@ * * Copyright 2007 Steven Brown <sbrown@cortland.com> * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> + * Copyright 2014 Hans de Goede <hdegoede@redhat.com> * * Derived from the ohci-ssb driver * Copyright 2007 Michael Buesch <m@bues.ch> @@ -18,6 +19,7 @@ * * Licensed under the GNU/GPL. See COPYING for details. */ +#include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/kernel.h> @@ -25,6 +27,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/usb.h> #include <linux/usb/hcd.h> @@ -33,6 +36,13 @@ #include "ehci.h" #define DRIVER_DESC "EHCI generic platform driver" +#define EHCI_MAX_CLKS 3 +#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv) + +struct ehci_platform_priv { + struct clk *clks[EHCI_MAX_CLKS]; + struct phy *phy; +}; static const char hcd_name[] = "ehci-platform"; @@ -45,8 +55,6 @@ static int ehci_platform_reset(struct usb_hcd *hcd) hcd->has_tt = pdata->has_tt; ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; - ehci->big_endian_desc = pdata->big_endian_desc; - ehci->big_endian_mmio = pdata->big_endian_mmio; if (pdata->pre_setup) { retval = pdata->pre_setup(hcd); @@ -64,38 +72,91 @@ static int ehci_platform_reset(struct usb_hcd *hcd) return 0; } +static int ehci_platform_power_on(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk, ret; + + for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); + if (ret) + goto err_disable_clks; + } + + if (priv->phy) { + ret = phy_init(priv->phy); + if (ret) + goto err_disable_clks; + + ret = phy_power_on(priv->phy); + if (ret) + goto err_exit_phy; + } + + return 0; + +err_exit_phy: + phy_exit(priv->phy); +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(priv->clks[clk]); + + return ret; +} + +static void ehci_platform_power_off(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk; + + if (priv->phy) { + phy_power_off(priv->phy); + phy_exit(priv->phy); + } + + for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) + if (priv->clks[clk]) + clk_disable_unprepare(priv->clks[clk]); +} + static struct hc_driver __read_mostly ehci_platform_hc_driver; static const struct ehci_driver_overrides platform_overrides __initconst = { - .reset = ehci_platform_reset, + .reset = ehci_platform_reset, + .extra_priv_size = sizeof(struct ehci_platform_priv), }; -static struct usb_ehci_pdata ehci_platform_defaults; +static struct usb_ehci_pdata ehci_platform_defaults = { + .power_on = ehci_platform_power_on, + .power_suspend = ehci_platform_power_off, + .power_off = ehci_platform_power_off, +}; static int ehci_platform_probe(struct platform_device *dev) { struct usb_hcd *hcd; struct resource *res_mem; - struct usb_ehci_pdata *pdata; - int irq; - int err; + struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ehci_platform_priv *priv; + struct ehci_hcd *ehci; + int err, irq, clk = 0; if (usb_disabled()) return -ENODEV; /* - * use reasonable defaults so platforms don't have to provide these. - * with DT probing on ARM, none of these are set. + * Use reasonable defaults so platforms don't have to provide these + * with DT probing on ARM. */ - if (!dev_get_platdata(&dev->dev)) - dev->dev.platform_data = &ehci_platform_defaults; + if (!pdata) + pdata = &ehci_platform_defaults; err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); if (err) return err; - pdata = dev_get_platdata(&dev->dev); - irq = platform_get_irq(dev, 0); if (irq < 0) { dev_err(&dev->dev, "no irq provided"); @@ -107,17 +168,72 @@ static int ehci_platform_probe(struct platform_device *dev) return -ENXIO; } + hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + platform_set_drvdata(dev, hcd); + dev->dev.platform_data = pdata; + priv = hcd_to_ehci_priv(hcd); + ehci = hcd_to_ehci(hcd); + + if (pdata == &ehci_platform_defaults && dev->dev.of_node) { + if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) + ehci->big_endian_mmio = 1; + + if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) + ehci->big_endian_desc = 1; + + if (of_property_read_bool(dev->dev.of_node, "big-endian")) + ehci->big_endian_mmio = ehci->big_endian_desc = 1; + + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); + if (err == -EPROBE_DEFER) + goto err_put_hcd; + priv->phy = NULL; + } + + for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { + priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); + if (IS_ERR(priv->clks[clk])) { + err = PTR_ERR(priv->clks[clk]); + if (err == -EPROBE_DEFER) + goto err_put_clks; + priv->clks[clk] = NULL; + break; + } + } + } + + if (pdata->big_endian_desc) + ehci->big_endian_desc = 1; + if (pdata->big_endian_mmio) + ehci->big_endian_mmio = 1; + +#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO + if (ehci->big_endian_mmio) { + dev_err(&dev->dev, + "Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n"); + err = -EINVAL; + goto err_put_clks; + } +#endif +#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC + if (ehci->big_endian_desc) { + dev_err(&dev->dev, + "Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n"); + err = -EINVAL; + goto err_put_clks; + } +#endif + if (pdata->power_on) { err = pdata->power_on(dev); if (err < 0) - return err; - } - - hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, - dev_name(&dev->dev)); - if (!hcd) { - err = -ENOMEM; - goto err_power; + goto err_put_clks; } hcd->rsrc_start = res_mem->start; @@ -126,22 +242,28 @@ static int ehci_platform_probe(struct platform_device *dev) hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); - goto err_put_hcd; + goto err_power; } err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) - goto err_put_hcd; + goto err_power; device_wakeup_enable(hcd->self.controller); platform_set_drvdata(dev, hcd); return err; -err_put_hcd: - usb_put_hcd(hcd); err_power: if (pdata->power_off) pdata->power_off(dev); +err_put_clks: + while (--clk >= 0) + clk_put(priv->clks[clk]); +err_put_hcd: + if (pdata == &ehci_platform_defaults) + dev->dev.platform_data = NULL; + + usb_put_hcd(hcd); return err; } @@ -150,13 +272,19 @@ static int ehci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk; usb_remove_hcd(hcd); - usb_put_hcd(hcd); if (pdata->power_off) pdata->power_off(dev); + for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) + clk_put(priv->clks[clk]); + + usb_put_hcd(hcd); + if (pdata == &ehci_platform_defaults) dev->dev.platform_data = NULL; @@ -207,8 +335,10 @@ static int ehci_platform_resume(struct device *dev) static const struct of_device_id vt8500_ehci_ids[] = { { .compatible = "via,vt8500-ehci", }, { .compatible = "wm,prizm-ehci", }, + { .compatible = "generic-ehci", }, {} }; +MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); static const struct platform_device_id ehci_platform_table[] = { { "ehci-platform", 0 }, diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index af28b748e87a..27ac6ad53c3d 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -38,10 +38,6 @@ #include "ehci.h" -#define TEGRA_USB_BASE 0xC5000000 -#define TEGRA_USB2_BASE 0xC5004000 -#define TEGRA_USB3_BASE 0xC5008000 - #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) #define TEGRA_USB_DMA_ALIGN 32 diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index e07669993f58..d0d8fadf7066 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -261,8 +261,44 @@ static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc) dev_err(dev, "cannot listen to notifications: %d\n", result); goto error_stop; } + /* + * If WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS is set, + * disable transfer notifications. + */ + if (hwahc->wa.quirks & + WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS) { + struct usb_host_interface *cur_altsetting = + hwahc->wa.usb_iface->cur_altsetting; + + result = usb_control_msg(hwahc->wa.usb_dev, + usb_sndctrlpipe(hwahc->wa.usb_dev, 0), + WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + WA_REQ_ALEREON_FEATURE_SET, + cur_altsetting->desc.bInterfaceNumber, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + /* + * If we successfully sent the control message, start DTI here + * because no transfer notifications will be received which is + * where DTI is normally started. + */ + if (result == 0) + result = wa_dti_start(&hwahc->wa); + else + result = 0; /* OK. Continue normally. */ + + if (result < 0) { + dev_err(dev, "cannot start DTI: %d\n", result); + goto error_dti_start; + } + } + return result; +error_dti_start: + wa_nep_disarm(&hwahc->wa); error_stop: __wa_clear_feature(&hwahc->wa, WA_ENABLE); return result; @@ -827,10 +863,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface) static struct usb_device_id hwahc_id_table[] = { /* Alereon 5310 */ { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01), - .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, + .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC | + WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS }, /* Alereon 5611 */ { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01), - .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, + .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC | + WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS }, /* FIXME: use class labels for this */ { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), }, {}, diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 68f674cd095f..b6002c951c5c 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -3,6 +3,7 @@ * * Copyright 2007 Michael Buesch <m@bues.ch> * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de> + * Copyright 2014 Hans de Goede <hdegoede@redhat.com> * * Derived from the OCHI-SSB driver * Derived from the OHCI-PCI driver @@ -14,11 +15,14 @@ * Licensed under the GNU/GPL. See COPYING for details. */ +#include <linux/clk.h> +#include <linux/dma-mapping.h> #include <linux/hrtimer.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/err.h> +#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/usb/ohci_pdriver.h> #include <linux/usb.h> @@ -27,6 +31,13 @@ #include "ohci.h" #define DRIVER_DESC "OHCI generic platform driver" +#define OHCI_MAX_CLKS 3 +#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv) + +struct ohci_platform_priv { + struct clk *clks[OHCI_MAX_CLKS]; + struct phy *phy; +}; static const char hcd_name[] = "ohci-platform"; @@ -36,10 +47,6 @@ static int ohci_platform_reset(struct usb_hcd *hcd) struct usb_ohci_pdata *pdata = dev_get_platdata(&pdev->dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); - if (pdata->big_endian_desc) - ohci->flags |= OHCI_QUIRK_BE_DESC; - if (pdata->big_endian_mmio) - ohci->flags |= OHCI_QUIRK_BE_MMIO; if (pdata->no_big_frame_no) ohci->flags |= OHCI_QUIRK_FRAME_NO; if (pdata->num_ports) @@ -48,11 +55,67 @@ static int ohci_platform_reset(struct usb_hcd *hcd) return ohci_setup(hcd); } +static int ohci_platform_power_on(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk, ret; + + for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); + if (ret) + goto err_disable_clks; + } + + if (priv->phy) { + ret = phy_init(priv->phy); + if (ret) + goto err_disable_clks; + + ret = phy_power_on(priv->phy); + if (ret) + goto err_exit_phy; + } + + return 0; + +err_exit_phy: + phy_exit(priv->phy); +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(priv->clks[clk]); + + return ret; +} + +static void ohci_platform_power_off(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk; + + if (priv->phy) { + phy_power_off(priv->phy); + phy_exit(priv->phy); + } + + for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) + if (priv->clks[clk]) + clk_disable_unprepare(priv->clks[clk]); +} + static struct hc_driver __read_mostly ohci_platform_hc_driver; static const struct ohci_driver_overrides platform_overrides __initconst = { - .product_desc = "Generic Platform OHCI controller", - .reset = ohci_platform_reset, + .product_desc = "Generic Platform OHCI controller", + .reset = ohci_platform_reset, + .extra_priv_size = sizeof(struct ohci_platform_priv), +}; + +static struct usb_ohci_pdata ohci_platform_defaults = { + .power_on = ohci_platform_power_on, + .power_suspend = ohci_platform_power_off, + .power_off = ohci_platform_power_off, }; static int ohci_platform_probe(struct platform_device *dev) @@ -60,17 +123,24 @@ static int ohci_platform_probe(struct platform_device *dev) struct usb_hcd *hcd; struct resource *res_mem; struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); - int irq; - int err = -ENOMEM; - - if (!pdata) { - WARN_ON(1); - return -ENODEV; - } + struct ohci_platform_priv *priv; + struct ohci_hcd *ohci; + int err, irq, clk = 0; if (usb_disabled()) return -ENODEV; + /* + * Use reasonable defaults so platforms don't have to provide these + * with DT probing on ARM. + */ + if (!pdata) + pdata = &ohci_platform_defaults; + + err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); + if (err) + return err; + irq = platform_get_irq(dev, 0); if (irq < 0) { dev_err(&dev->dev, "no irq provided"); @@ -83,17 +153,72 @@ static int ohci_platform_probe(struct platform_device *dev) return -ENXIO; } + hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + platform_set_drvdata(dev, hcd); + dev->dev.platform_data = pdata; + priv = hcd_to_ohci_priv(hcd); + ohci = hcd_to_ohci(hcd); + + if (pdata == &ohci_platform_defaults && dev->dev.of_node) { + if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) + ohci->flags |= OHCI_QUIRK_BE_MMIO; + + if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) + ohci->flags |= OHCI_QUIRK_BE_DESC; + + if (of_property_read_bool(dev->dev.of_node, "big-endian")) + ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; + + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); + if (err == -EPROBE_DEFER) + goto err_put_hcd; + priv->phy = NULL; + } + + for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { + priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); + if (IS_ERR(priv->clks[clk])) { + err = PTR_ERR(priv->clks[clk]); + if (err == -EPROBE_DEFER) + goto err_put_clks; + priv->clks[clk] = NULL; + break; + } + } + } + + if (pdata->big_endian_desc) + ohci->flags |= OHCI_QUIRK_BE_DESC; + if (pdata->big_endian_mmio) + ohci->flags |= OHCI_QUIRK_BE_MMIO; + +#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO + if (ohci->flags & OHCI_QUIRK_BE_MMIO) { + dev_err(&dev->dev, + "Error: CONFIG_USB_OHCI_BIG_ENDIAN_MMIO not set\n"); + err = -EINVAL; + goto err_put_clks; + } +#endif +#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC + if (ohci->flags & OHCI_QUIRK_BE_DESC) { + dev_err(&dev->dev, + "Error: CONFIG_USB_OHCI_BIG_ENDIAN_DESC not set\n"); + err = -EINVAL; + goto err_put_clks; + } +#endif + if (pdata->power_on) { err = pdata->power_on(dev); if (err < 0) - return err; - } - - hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, - dev_name(&dev->dev)); - if (!hcd) { - err = -ENOMEM; - goto err_power; + goto err_put_clks; } hcd->rsrc_start = res_mem->start; @@ -102,11 +227,11 @@ static int ohci_platform_probe(struct platform_device *dev) hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); - goto err_put_hcd; + goto err_power; } err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) - goto err_put_hcd; + goto err_power; device_wakeup_enable(hcd->self.controller); @@ -114,11 +239,17 @@ static int ohci_platform_probe(struct platform_device *dev) return err; -err_put_hcd: - usb_put_hcd(hcd); err_power: if (pdata->power_off) pdata->power_off(dev); +err_put_clks: + while (--clk >= 0) + clk_put(priv->clks[clk]); +err_put_hcd: + if (pdata == &ohci_platform_defaults) + dev->dev.platform_data = NULL; + + usb_put_hcd(hcd); return err; } @@ -127,13 +258,22 @@ static int ohci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk; usb_remove_hcd(hcd); - usb_put_hcd(hcd); if (pdata->power_off) pdata->power_off(dev); + for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) + clk_put(priv->clks[clk]); + + usb_put_hcd(hcd); + + if (pdata == &ohci_platform_defaults) + dev->dev.platform_data = NULL; + return 0; } @@ -180,6 +320,12 @@ static int ohci_platform_resume(struct device *dev) #define ohci_platform_resume NULL #endif /* CONFIG_PM */ +static const struct of_device_id ohci_platform_ids[] = { + { .compatible = "generic-ohci", }, + { } +}; +MODULE_DEVICE_TABLE(of, ohci_platform_ids); + static const struct platform_device_id ohci_platform_table[] = { { "ohci-platform", 0 }, { } @@ -200,6 +346,7 @@ static struct platform_driver ohci_platform_driver = { .owner = THIS_MODULE, .name = "ohci-platform", .pm = &ohci_platform_pm_ops, + .of_match_table = ohci_platform_ids, } }; diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 44e6c9da8892..01833ab2b5c3 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -148,6 +148,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op) } static const struct of_device_id platform_uhci_ids[] = { + { .compatible = "generic-uhci", }, { .compatible = "platform-uhci", }, {} }; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 9992fbfec85f..1ad6bc1951c7 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -732,9 +732,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* Set the U1 and U2 exit latencies. */ memcpy(buf, &usb_bos_descriptor, USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); - temp = readl(&xhci->cap_regs->hcs_params3); - buf[12] = HCS_U1_LATENCY(temp); - put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + if ((xhci->quirks & XHCI_LPM_SUPPORT)) { + temp = readl(&xhci->cap_regs->hcs_params3); + buf[12] = HCS_U1_LATENCY(temp); + put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + } /* Indicate whether the host has LTM support. */ temp = readl(&xhci->cap_regs->hcc_params); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bce4391a0e7d..c089668308ad 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -149,14 +149,140 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, } } +/* + * We need a radix tree for mapping physical addresses of TRBs to which stream + * ID they belong to. We need to do this because the host controller won't tell + * us which stream ring the TRB came from. We could store the stream ID in an + * event data TRB, but that doesn't help us for the cancellation case, since the + * endpoint may stop before it reaches that event data TRB. + * + * The radix tree maps the upper portion of the TRB DMA address to a ring + * segment that has the same upper portion of DMA addresses. For example, say I + * have segments of size 1KB, that are always 1KB aligned. A segment may + * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the + * key to the stream ID is 0x43244. I can use the DMA address of the TRB to + * pass the radix tree a key to get the right stream ID: + * + * 0x10c90fff >> 10 = 0x43243 + * 0x10c912c0 >> 10 = 0x43244 + * 0x10c91400 >> 10 = 0x43245 + * + * Obviously, only those TRBs with DMA addresses that are within the segment + * will make the radix tree return the stream ID for that ring. + * + * Caveats for the radix tree: + * + * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an + * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be + * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the + * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit + * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit + * extended systems (where the DMA address can be bigger than 32-bits), + * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that. + */ +static int xhci_insert_segment_mapping(struct radix_tree_root *trb_address_map, + struct xhci_ring *ring, + struct xhci_segment *seg, + gfp_t mem_flags) +{ + unsigned long key; + int ret; + + key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT); + /* Skip any segments that were already added. */ + if (radix_tree_lookup(trb_address_map, key)) + return 0; + + ret = radix_tree_maybe_preload(mem_flags); + if (ret) + return ret; + ret = radix_tree_insert(trb_address_map, + key, ring); + radix_tree_preload_end(); + return ret; +} + +static void xhci_remove_segment_mapping(struct radix_tree_root *trb_address_map, + struct xhci_segment *seg) +{ + unsigned long key; + + key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT); + if (radix_tree_lookup(trb_address_map, key)) + radix_tree_delete(trb_address_map, key); +} + +static int xhci_update_stream_segment_mapping( + struct radix_tree_root *trb_address_map, + struct xhci_ring *ring, + struct xhci_segment *first_seg, + struct xhci_segment *last_seg, + gfp_t mem_flags) +{ + struct xhci_segment *seg; + struct xhci_segment *failed_seg; + int ret; + + if (WARN_ON_ONCE(trb_address_map == NULL)) + return 0; + + seg = first_seg; + do { + ret = xhci_insert_segment_mapping(trb_address_map, + ring, seg, mem_flags); + if (ret) + goto remove_streams; + if (seg == last_seg) + return 0; + seg = seg->next; + } while (seg != first_seg); + + return 0; + +remove_streams: + failed_seg = seg; + seg = first_seg; + do { + xhci_remove_segment_mapping(trb_address_map, seg); + if (seg == failed_seg) + return ret; + seg = seg->next; + } while (seg != first_seg); + + return ret; +} + +static void xhci_remove_stream_mapping(struct xhci_ring *ring) +{ + struct xhci_segment *seg; + + if (WARN_ON_ONCE(ring->trb_address_map == NULL)) + return; + + seg = ring->first_seg; + do { + xhci_remove_segment_mapping(ring->trb_address_map, seg); + seg = seg->next; + } while (seg != ring->first_seg); +} + +static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags) +{ + return xhci_update_stream_segment_mapping(ring->trb_address_map, ring, + ring->first_seg, ring->last_seg, mem_flags); +} + /* XXX: Do we need the hcd structure in all these functions? */ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) { if (!ring) return; - if (ring->first_seg) + if (ring->first_seg) { + if (ring->type == TYPE_STREAM) + xhci_remove_stream_mapping(ring); xhci_free_segments_for_ring(xhci, ring->first_seg); + } kfree(ring); } @@ -349,6 +475,21 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, if (ret) return -ENOMEM; + if (ring->type == TYPE_STREAM) + ret = xhci_update_stream_segment_mapping(ring->trb_address_map, + ring, first, last, flags); + if (ret) { + struct xhci_segment *next; + do { + next = first->next; + xhci_segment_free(xhci, first); + if (first == last) + break; + first = next; + } while (true); + return ret; + } + xhci_link_rings(xhci, ring, first, last, num_segs); xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, "ring expansion succeed, now has %d segments", @@ -434,12 +575,12 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci, struct xhci_stream_ctx *stream_ctx, dma_addr_t dma) { struct device *dev = xhci_to_hcd(xhci)->self.controller; + size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs; - if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - dma_free_coherent(dev, - sizeof(struct xhci_stream_ctx)*num_stream_ctxs, + if (size > MEDIUM_STREAM_ARRAY_SIZE) + dma_free_coherent(dev, size, stream_ctx, dma); - else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) + else if (size <= SMALL_STREAM_ARRAY_SIZE) return dma_pool_free(xhci->small_streams_pool, stream_ctx, dma); else @@ -462,12 +603,12 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, gfp_t mem_flags) { struct device *dev = xhci_to_hcd(xhci)->self.controller; + size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs; - if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - return dma_alloc_coherent(dev, - sizeof(struct xhci_stream_ctx)*num_stream_ctxs, + if (size > MEDIUM_STREAM_ARRAY_SIZE) + return dma_alloc_coherent(dev, size, dma, mem_flags); - else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) + else if (size <= SMALL_STREAM_ARRAY_SIZE) return dma_pool_alloc(xhci->small_streams_pool, mem_flags, dma); else @@ -510,36 +651,6 @@ struct xhci_ring *xhci_stream_id_to_ring( * The number of stream contexts in the stream context array may be bigger than * the number of streams the driver wants to use. This is because the number of * stream context array entries must be a power of two. - * - * We need a radix tree for mapping physical addresses of TRBs to which stream - * ID they belong to. We need to do this because the host controller won't tell - * us which stream ring the TRB came from. We could store the stream ID in an - * event data TRB, but that doesn't help us for the cancellation case, since the - * endpoint may stop before it reaches that event data TRB. - * - * The radix tree maps the upper portion of the TRB DMA address to a ring - * segment that has the same upper portion of DMA addresses. For example, say I - * have segments of size 1KB, that are always 64-byte aligned. A segment may - * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the - * key to the stream ID is 0x43244. I can use the DMA address of the TRB to - * pass the radix tree a key to get the right stream ID: - * - * 0x10c90fff >> 10 = 0x43243 - * 0x10c912c0 >> 10 = 0x43244 - * 0x10c91400 >> 10 = 0x43245 - * - * Obviously, only those TRBs with DMA addresses that are within the segment - * will make the radix tree return the stream ID for that ring. - * - * Caveats for the radix tree: - * - * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an - * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be - * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the - * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit - * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit - * extended systems (where the DMA address can be bigger than 32-bits), - * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that. */ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, @@ -548,7 +659,6 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, struct xhci_stream_info *stream_info; u32 cur_stream; struct xhci_ring *cur_ring; - unsigned long key; u64 addr; int ret; @@ -603,6 +713,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, if (!cur_ring) goto cleanup_rings; cur_ring->stream_id = cur_stream; + cur_ring->trb_address_map = &stream_info->trb_address_map; /* Set deq ptr, cycle bit, and stream context type */ addr = cur_ring->first_seg->dma | SCT_FOR_CTX(SCT_PRI_TR) | @@ -612,10 +723,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", cur_stream, (unsigned long long) addr); - key = (unsigned long) - (cur_ring->first_seg->dma >> TRB_SEGMENT_SHIFT); - ret = radix_tree_insert(&stream_info->trb_address_map, - key, cur_ring); + ret = xhci_update_stream_mapping(cur_ring, mem_flags); if (ret) { xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; @@ -635,9 +743,6 @@ cleanup_rings: for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { cur_ring = stream_info->stream_rings[cur_stream]; if (cur_ring) { - addr = cur_ring->first_seg->dma; - radix_tree_delete(&stream_info->trb_address_map, - addr >> TRB_SEGMENT_SHIFT); xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; } @@ -698,7 +803,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci, { int cur_stream; struct xhci_ring *cur_ring; - dma_addr_t addr; if (!stream_info) return; @@ -707,9 +811,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci, cur_stream++) { cur_ring = stream_info->stream_rings[cur_stream]; if (cur_ring) { - addr = cur_ring->first_seg->dma; - radix_tree_delete(&stream_info->trb_address_map, - addr >> TRB_SEGMENT_SHIFT); xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; } @@ -1711,7 +1812,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); - xhci->cmd_ring_reserved_trbs = 0; if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); xhci->cmd_ring = NULL; @@ -1776,6 +1876,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } no_bw: + xhci->cmd_ring_reserved_trbs = 0; xhci->num_usb2_ports = 0; xhci->num_usb3_ports = 0; xhci->num_active_eps = 0; @@ -2274,11 +2375,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* * Initialize the ring segment pool. The ring must be a contiguous * structure comprised of TRBs. The TRBs must be 16 byte aligned, - * however, the command ring segment needs 64-byte aligned segments, - * so we pick the greater alignment need. + * however, the command ring segment needs 64-byte aligned segments + * and our use of dma addresses in the trb_address_map radix tree needs + * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need. */ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, - TRB_SEGMENT_SIZE, 64, xhci->page_size); + TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size); /* See Table 46 and Note on Figure 55 */ xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 04f986d9234f..47390e369cd4 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -190,6 +190,10 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) struct usb_hcd *hcd; driver = (struct hc_driver *)id->driver_data; + + /* Prevent runtime suspending between USB-2 and USB-3 initialization */ + pm_runtime_get_noresume(&dev->dev); + /* Register the USB 2.0 roothub. * FIXME: USB core must know to register the USB 2.0 roothub first. * This is sort of silly, because we could just set the HCD driver flags @@ -199,7 +203,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) retval = usb_hcd_pci_probe(dev, id); if (retval) - return retval; + goto put_runtime_pm; /* USB 2.0 roothub is stored in the PCI device now. */ hcd = dev_get_drvdata(&dev->dev); @@ -222,11 +226,11 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto put_usb3_hcd; /* Roothub already marked as USB 3.0 speed */ - /* We know the LPM timeout algorithms for this host, let the USB core - * enable and disable LPM for devices under the USB 3.0 roothub. - */ - if (xhci->quirks & XHCI_LPM_SUPPORT) - hcd_to_bus(xhci->shared_hcd)->root_hub->lpm_capable = 1; + if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + + /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ + pm_runtime_put_noidle(&dev->dev); return 0; @@ -234,6 +238,8 @@ put_usb3_hcd: usb_put_hcd(xhci->shared_hcd); dealloc_usb2_hcd: usb_hcd_pci_remove(dev); +put_runtime_pm: + pm_runtime_put_noidle(&dev->dev); return retval; } diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 8abda5c73ca1..151901ce1ba9 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -158,6 +158,9 @@ static int xhci_plat_probe(struct platform_device *pdev) */ *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; + if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); if (ret) goto put_usb3_hcd; @@ -226,6 +229,7 @@ static const struct dev_pm_ops xhci_plat_pm_ops = { #ifdef CONFIG_OF static const struct of_device_id usb_xhci_of_match[] = { + { .compatible = "generic-xhci" }, { .compatible = "xhci-platform" }, { }, }; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0ed64eb68e48..5f926bea5ab1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -546,9 +546,9 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, struct xhci_dequeue_state *state) { struct xhci_virt_device *dev = xhci->devs[slot_id]; + struct xhci_virt_ep *ep = &dev->eps[ep_index]; struct xhci_ring *ep_ring; struct xhci_generic_trb *trb; - struct xhci_ep_ctx *ep_ctx; dma_addr_t addr; ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, @@ -573,8 +573,16 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, /* Dig out the cycle state saved by the xHC during the stop ep cmd */ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Finding endpoint context"); - ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); - state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq); + /* 4.6.9 the css flag is written to the stream context for streams */ + if (ep->ep_state & EP_HAS_STREAMS) { + struct xhci_stream_ctx *ctx = + &ep->stream_info->stream_ctx_array[stream_id]; + state->new_cycle_state = 0x1 & le64_to_cpu(ctx->stream_ring); + } else { + struct xhci_ep_ctx *ep_ctx + = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); + state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq); + } state->new_deq_ptr = cur_td->last_trb; xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, @@ -892,6 +900,57 @@ remove_finished_td: /* Return to the event handler with xhci->lock re-acquired */ } +static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring) +{ + struct xhci_td *cur_td; + + while (!list_empty(&ring->td_list)) { + cur_td = list_first_entry(&ring->td_list, + struct xhci_td, td_list); + list_del_init(&cur_td->td_list); + if (!list_empty(&cur_td->cancelled_td_list)) + list_del_init(&cur_td->cancelled_td_list); + xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN); + } +} + +static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci, + int slot_id, int ep_index) +{ + struct xhci_td *cur_td; + struct xhci_virt_ep *ep; + struct xhci_ring *ring; + + ep = &xhci->devs[slot_id]->eps[ep_index]; + if ((ep->ep_state & EP_HAS_STREAMS) || + (ep->ep_state & EP_GETTING_NO_STREAMS)) { + int stream_id; + + for (stream_id = 0; stream_id < ep->stream_info->num_streams; + stream_id++) { + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Killing URBs for slot ID %u, ep index %u, stream %u", + slot_id, ep_index, stream_id + 1); + xhci_kill_ring_urbs(xhci, + ep->stream_info->stream_rings[stream_id]); + } + } else { + ring = ep->ring; + if (!ring) + return; + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Killing URBs for slot ID %u, ep index %u", + slot_id, ep_index); + xhci_kill_ring_urbs(xhci, ring); + } + while (!list_empty(&ep->cancelled_td_list)) { + cur_td = list_first_entry(&ep->cancelled_td_list, + struct xhci_td, cancelled_td_list); + list_del_init(&cur_td->cancelled_td_list); + xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN); + } +} + /* Watchdog timer function for when a stop endpoint command fails to complete. * In this case, we assume the host controller is broken or dying or dead. The * host may still be completing some other events, so we have to be careful to @@ -915,9 +974,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) { struct xhci_hcd *xhci; struct xhci_virt_ep *ep; - struct xhci_virt_ep *temp_ep; - struct xhci_ring *ring; - struct xhci_td *cur_td; int ret, i, j; unsigned long flags; @@ -974,34 +1030,8 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) for (i = 0; i < MAX_HC_SLOTS; i++) { if (!xhci->devs[i]) continue; - for (j = 0; j < 31; j++) { - temp_ep = &xhci->devs[i]->eps[j]; - ring = temp_ep->ring; - if (!ring) - continue; - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Killing URBs for slot ID %u, " - "ep index %u", i, j); - while (!list_empty(&ring->td_list)) { - cur_td = list_first_entry(&ring->td_list, - struct xhci_td, - td_list); - list_del_init(&cur_td->td_list); - if (!list_empty(&cur_td->cancelled_td_list)) - list_del_init(&cur_td->cancelled_td_list); - xhci_giveback_urb_in_irq(xhci, cur_td, - -ESHUTDOWN); - } - while (!list_empty(&temp_ep->cancelled_td_list)) { - cur_td = list_first_entry( - &temp_ep->cancelled_td_list, - struct xhci_td, - cancelled_td_list); - list_del_init(&cur_td->cancelled_td_list); - xhci_giveback_urb_in_irq(xhci, cur_td, - -ESHUTDOWN); - } - } + for (j = 0; j < 31; j++) + xhci_kill_endpoint_urbs(xhci, i, j); } spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, @@ -1073,17 +1103,18 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, unsigned int stream_id; struct xhci_ring *ep_ring; struct xhci_virt_device *dev; + struct xhci_virt_ep *ep; struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); dev = xhci->devs[slot_id]; + ep = &dev->eps[ep_index]; ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id); if (!ep_ring) { - xhci_warn(xhci, "WARN Set TR deq ptr command for " - "freed stream ID %u\n", + xhci_warn(xhci, "WARN Set TR deq ptr command for freed stream ID %u\n", stream_id); /* XXX: Harmless??? */ dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; @@ -1099,12 +1130,10 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, switch (cmd_comp_code) { case COMP_TRB_ERR: - xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because " - "of stream ID configuration\n"); + xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because of stream ID configuration\n"); break; case COMP_CTX_STATE: - xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due " - "to incorrect slot or ep state.\n"); + xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.\n"); ep_state = le32_to_cpu(ep_ctx->ep_info); ep_state &= EP_STATE_MASK; slot_state = le32_to_cpu(slot_ctx->dev_state); @@ -1114,13 +1143,12 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, slot_state, ep_state); break; case COMP_EBADSLT: - xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because " - "slot %u was not enabled.\n", slot_id); + xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because slot %u was not enabled.\n", + slot_id); break; default: - xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown " - "completion code of %u.\n", - cmd_comp_code); + xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown completion code of %u.\n", + cmd_comp_code); break; } /* OK what do we do now? The endpoint state is hosed, and we @@ -1130,23 +1158,28 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, * cancelling URBs, which might not be an error... */ } else { + u64 deq; + /* 4.6.10 deq ptr is written to the stream ctx for streams */ + if (ep->ep_state & EP_HAS_STREAMS) { + struct xhci_stream_ctx *ctx = + &ep->stream_info->stream_ctx_array[stream_id]; + deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK; + } else { + deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; + } xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Successful Set TR Deq Ptr cmd, deq = @%08llx", - le64_to_cpu(ep_ctx->deq)); - if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg, - dev->eps[ep_index].queued_deq_ptr) == - (le64_to_cpu(ep_ctx->deq) & ~(EP_CTX_CYCLE_MASK))) { + "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq); + if (xhci_trb_virt_to_dma(ep->queued_deq_seg, + ep->queued_deq_ptr) == deq) { /* Update the ring's dequeue segment and dequeue pointer * to reflect the new position. */ update_ring_for_set_deq_completion(xhci, dev, ep_ring, ep_index); } else { - xhci_warn(xhci, "Mismatch between completed Set TR Deq " - "Ptr command & xHCI internal state.\n"); + xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n"); xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n", - dev->eps[ep_index].queued_deq_seg, - dev->eps[ep_index].queued_deq_ptr); + ep->queued_deq_seg, ep->queued_deq_ptr); } } @@ -4070,6 +4103,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id); + u32 trb_sct = 0; u32 type = TRB_TYPE(TRB_SET_DEQ); struct xhci_virt_ep *ep; @@ -4088,7 +4122,9 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, } ep->queued_deq_seg = deq_seg; ep->queued_deq_ptr = deq_ptr; - return queue_command(xhci, lower_32_bits(addr) | cycle_state, + if (stream_id) + trb_sct = SCT_FOR_TRB(SCT_PRI_TR); + return queue_command(xhci, lower_32_bits(addr) | trb_sct | cycle_state, upper_32_bits(addr), trb_stream_id, trb_slot_id | trb_ep_index | type, false); } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 924a6ccdb622..8fe4e124ddd4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -390,6 +390,10 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) } legacy_irq: + if (!strlen(hcd->irq_descr)) + snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", + hcd->driver->description, hcd->self.busnum); + /* fall back to legacy interrupt*/ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); @@ -2678,6 +2682,20 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, return ret; } +static void xhci_check_bw_drop_ep_streams(struct xhci_hcd *xhci, + struct xhci_virt_device *vdev, int i) +{ + struct xhci_virt_ep *ep = &vdev->eps[i]; + + if (ep->ep_state & EP_HAS_STREAMS) { + xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on set_interface, freeing streams.\n", + xhci_get_endpoint_address(i)); + xhci_free_stream_info(xhci, ep->stream_info); + ep->stream_info = NULL; + ep->ep_state &= ~EP_HAS_STREAMS; + } +} + /* Called after one or more calls to xhci_add_endpoint() or * xhci_drop_endpoint(). If this call fails, the USB core is expected * to call xhci_reset_bandwidth(). @@ -2742,8 +2760,10 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) /* Free any rings that were dropped, but not changed. */ for (i = 1; i < 31; ++i) { if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) && - !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) + !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) { xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); + xhci_check_bw_drop_ep_streams(xhci, virt_dev, i); + } } xhci_zero_in_ctx(xhci, virt_dev); /* @@ -2759,6 +2779,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) if (virt_dev->eps[i].ring) { xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); } + xhci_check_bw_drop_ep_streams(xhci, virt_dev, i); virt_dev->eps[i].ring = virt_dev->eps[i].new_ring; virt_dev->eps[i].new_ring = NULL; } @@ -2954,7 +2975,7 @@ static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__); if (ret <= 0) return -EINVAL; - if (ep->ss_ep_comp.bmAttributes == 0) { + if (usb_ss_max_streams(&ep->ss_ep_comp) == 0) { xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion" " descriptor for ep 0x%x does not support streams\n", ep->desc.bEndpointAddress); @@ -3121,6 +3142,12 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg(xhci, "Driver wants %u stream IDs (including stream 0).\n", num_streams); + /* MaxPSASize value 0 (2 streams) means streams are not supported */ + if (HCC_MAX_PSA(xhci->hcc_params) < 4) { + xhci_dbg(xhci, "xHCI controller does not support streams.\n"); + return -ENOSYS; + } + config_cmd = xhci_alloc_command(xhci, true, true, mem_flags); if (!config_cmd) { xhci_dbg(xhci, "Could not allocate xHCI command structure.\n"); @@ -3519,6 +3546,8 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_virt_ep *ep = &virt_dev->eps[i]; if (ep->ep_state & EP_HAS_STREAMS) { + xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on device reset, freeing streams.\n", + xhci_get_endpoint_address(i)); xhci_free_stream_info(xhci, ep->stream_info); ep->stream_info = NULL; ep->ep_state &= ~EP_HAS_STREAMS; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 58ed9d088e63..d280e9213d08 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -703,6 +703,7 @@ struct xhci_ep_ctx { /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0) +#define SCTX_DEQ_MASK (~0xfL) /** @@ -1118,9 +1119,10 @@ enum xhci_setup_dev { #define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23) #define LAST_EP_INDEX 30 -/* Set TR Dequeue Pointer command TRB fields */ +/* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */ #define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16)) #define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16) +#define SCT_FOR_TRB(p) (((p) << 1) & 0x7) /* Port Status Change Event TRB fields */ @@ -1341,6 +1343,7 @@ struct xhci_ring { unsigned int num_trbs_free_temp; enum xhci_ring_type type; bool last_td_was_short; + struct radix_tree_root *trb_address_map; }; struct xhci_erst_entry { diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index ba5f70f92888..1bca274dc3b5 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -128,7 +128,6 @@ config USB_IDMOUSE config USB_FTDI_ELAN tristate "Elan PCMCIA CardBus Adapter USB Client" - default M help ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters. Currently only the U132 adapter is available. diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index de98906f786d..06b5d77cd9ad 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -2123,8 +2123,8 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) u8 tmp8, tmp82, ramtype; int bw = 0; char *ramtypetext1 = NULL; - const char *ramtypetext2[] = { "SDR SDRAM", "SDR SGRAM", - "DDR SDRAM", "DDR SGRAM" }; + static const char ram_datarate[4] = {'S', 'S', 'D', 'D'}; + static const char ram_dynamictype[4] = {'D', 'G', 'D', 'G'}; static const int busSDR[4] = {64, 64, 128, 128}; static const int busDDR[4] = {32, 32, 64, 64}; static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2}; @@ -2156,8 +2156,10 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) break; } - dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %s, bus width %d\n", (sisusb->vramsize >> 20), ramtypetext1, - ramtypetext2[ramtype], bw); + + dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %cDR S%cRAM, bus width %d\n", + sisusb->vramsize >> 20, ramtypetext1, + ram_datarate[ramtype], ram_dynamictype[ramtype], bw); } static int diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 78eb4ff33269..bdef0d6eb91d 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -22,8 +22,27 @@ enum led_type { DELCOM_VISUAL_SIGNAL_INDICATOR, DREAM_CHEEKY_WEBMAIL_NOTIFIER, + RISO_KAGAKU_LED }; +/* the Webmail LED made by RISO KAGAKU CORP. decodes a color index + internally, we want to keep the red+green+blue sysfs api, so we decode + from 1-bit RGB to the riso kagaku color index according to this table... */ + +static unsigned const char riso_kagaku_tbl[] = { +/* R+2G+4B -> riso kagaku color index */ + [0] = 0, /* black */ + [1] = 2, /* red */ + [2] = 1, /* green */ + [3] = 5, /* yellow */ + [4] = 3, /* blue */ + [5] = 6, /* magenta */ + [6] = 4, /* cyan */ + [7] = 7 /* white */ +}; + +#define RISO_KAGAKU_IX(r,g,b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)] + /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x0fc5, 0x1223), @@ -32,6 +51,8 @@ static const struct usb_device_id id_table[] = { .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, { USB_DEVICE(0x1d34, 0x000a), .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, + { USB_DEVICE(0x1294, 0x1320), + .driver_info = RISO_KAGAKU_LED }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); @@ -48,6 +69,7 @@ static void change_color(struct usb_led *led) { int retval = 0; unsigned char *buffer; + int actlength; buffer = kmalloc(8, GFP_KERNEL); if (!buffer) { @@ -104,6 +126,18 @@ static void change_color(struct usb_led *led) 2000); break; + case RISO_KAGAKU_LED: + buffer[0] = RISO_KAGAKU_IX(led->red, led->green, led->blue); + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + buffer[4] = 0; + + retval = usb_interrupt_msg(led->udev, + usb_sndctrlpipe(led->udev, 2), + buffer, 5, &actlength, 1000 /*ms timeout*/); + break; + default: dev_err(&led->udev->dev, "unknown device type %d\n", led->type); } diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 688dc8bb192d..8b789792f6fa 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -43,6 +43,7 @@ config USB_MUSB_HOST config USB_MUSB_GADGET bool "Gadget only mode" depends on USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC + depends on HAS_DMA help Select this when you want to use MUSB in gadget mode only, thereby the host feature will be regressed. @@ -50,6 +51,7 @@ config USB_MUSB_GADGET config USB_MUSB_DUAL_ROLE bool "Dual Role mode" depends on ((USB=y || USB=USB_MUSB_HDRC) && (USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC)) + depends on HAS_DMA help This is the default mode of working of MUSB controller where both host and gadget features are enabled. diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 239ad0b1ceb6..07576907e2c6 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -438,7 +438,6 @@ void musb_hnp_stop(struct musb *musb) static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, u8 devctl) { - struct usb_otg *otg = musb->xceiv->otg; irqreturn_t handled = IRQ_NONE; dev_dbg(musb->controller, "<== DevCtl=%02x, int_usb=0x%x\n", devctl, @@ -656,7 +655,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; case OTG_STATE_B_PERIPHERAL: musb_g_suspend(musb); - musb->is_active = otg->gadget->b_hnp_enable; + musb->is_active = musb->g.b_hnp_enable; if (musb->is_active) { musb->xceiv->state = OTG_STATE_B_WAIT_ACON; dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n"); @@ -672,7 +671,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; case OTG_STATE_A_HOST: musb->xceiv->state = OTG_STATE_A_SUSPEND; - musb->is_active = otg->host->b_hnp_enable; + musb->is_active = musb->hcd->self.b_hnp_enable; break; case OTG_STATE_B_HOST: /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index f88929609bac..7b8bbf53127e 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -39,6 +39,7 @@ struct cppi41_dma_channel { u32 transferred; u32 packet_sz; struct list_head tx_check; + struct work_struct dma_completion; }; #define MUSB_DMA_NUM_CHANNELS 15 @@ -112,6 +113,18 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep) return true; } +static bool is_isoc(struct musb_hw_ep *hw_ep, bool in) +{ + if (in && hw_ep->in_qh) { + if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC) + return true; + } else if (hw_ep->out_qh) { + if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC) + return true; + } + return false; +} + static void cppi41_dma_callback(void *private_data); static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) @@ -119,7 +132,8 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; struct musb *musb = hw_ep->musb; - if (!cppi41_channel->prog_len) { + if (!cppi41_channel->prog_len || + (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)) { /* done, complete */ cppi41_channel->channel.actual_len = @@ -165,6 +179,32 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) } } +static void cppi_trans_done_work(struct work_struct *work) +{ + unsigned long flags; + struct cppi41_dma_channel *cppi41_channel = + container_of(work, struct cppi41_dma_channel, dma_completion); + struct cppi41_dma_controller *controller = cppi41_channel->controller; + struct musb *musb = controller->musb; + struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + bool empty; + + if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) { + spin_lock_irqsave(&musb->lock, flags); + cppi41_trans_done(cppi41_channel); + spin_unlock_irqrestore(&musb->lock, flags); + } else { + empty = musb_is_tx_fifo_empty(hw_ep); + if (empty) { + spin_lock_irqsave(&musb->lock, flags); + cppi41_trans_done(cppi41_channel); + spin_unlock_irqrestore(&musb->lock, flags); + } else { + schedule_work(&cppi41_channel->dma_completion); + } + } +} + static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) { struct cppi41_dma_controller *controller; @@ -228,6 +268,14 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; + if (!cppi41_channel->is_tx) { + if (is_isoc(hw_ep, 1)) + schedule_work(&cppi41_channel->dma_completion); + else + cppi41_trans_done(cppi41_channel); + goto out; + } + empty = musb_is_tx_fifo_empty(hw_ep); if (empty) { cppi41_trans_done(cppi41_channel); @@ -264,6 +312,10 @@ static void cppi41_dma_callback(void *private_data) goto out; } } + if (is_isoc(hw_ep, 0)) { + schedule_work(&cppi41_channel->dma_completion); + goto out; + } list_add_tail(&cppi41_channel->tx_check, &controller->early_tx_list); if (!hrtimer_active(&controller->early_tx)) { @@ -448,12 +500,25 @@ static int cppi41_dma_channel_program(struct dma_channel *channel, dma_addr_t dma_addr, u32 len) { int ret; + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + int hb_mult = 0; BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || channel->status == MUSB_DMA_STATUS_BUSY); + if (is_host_active(cppi41_channel->controller->musb)) { + if (cppi41_channel->is_tx) + hb_mult = cppi41_channel->hw_ep->out_qh->hb_mult; + else + hb_mult = cppi41_channel->hw_ep->in_qh->hb_mult; + } + channel->status = MUSB_DMA_STATUS_BUSY; channel->actual_len = 0; + + if (hb_mult) + packet_sz = hb_mult * (packet_sz & 0x7FF); + ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len); if (!ret) channel->status = MUSB_DMA_STATUS_FREE; @@ -607,6 +672,8 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) cppi41_channel->port_num = port; cppi41_channel->is_tx = is_tx; INIT_LIST_HEAD(&cppi41_channel->tx_check); + INIT_WORK(&cppi41_channel->dma_completion, + cppi_trans_done_work); musb_dma = &cppi41_channel->channel; musb_dma->private_data = cppi41_channel; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 7a109eae9b9a..3372ded5def7 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -45,6 +45,8 @@ #include <linux/of_irq.h> #include <linux/usb/of.h> +#include <linux/debugfs.h> + #include "musb_core.h" static const struct of_device_id musb_dsps_of_match[]; @@ -136,6 +138,26 @@ struct dsps_glue { unsigned long last_timer; /* last timer data for each instance */ struct dsps_context context; + struct debugfs_regset32 regset; + struct dentry *dbgfs_root; +}; + +static const struct debugfs_reg32 dsps_musb_regs[] = { + { "revision", 0x00 }, + { "control", 0x14 }, + { "status", 0x18 }, + { "eoi", 0x24 }, + { "intr0_stat", 0x30 }, + { "intr1_stat", 0x34 }, + { "intr0_set", 0x38 }, + { "intr1_set", 0x3c }, + { "txmode", 0x70 }, + { "rxmode", 0x74 }, + { "autoreq", 0xd0 }, + { "srpfixtime", 0xd4 }, + { "tdown", 0xd8 }, + { "phy_utmi", 0xe0 }, + { "mode", 0xe8 }, }; static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) @@ -368,6 +390,30 @@ out: return ret; } +static int dsps_musb_dbg_init(struct musb *musb, struct dsps_glue *glue) +{ + struct dentry *root; + struct dentry *file; + char buf[128]; + + sprintf(buf, "%s.dsps", dev_name(musb->controller)); + root = debugfs_create_dir(buf, NULL); + if (!root) + return -ENOMEM; + glue->dbgfs_root = root; + + glue->regset.regs = dsps_musb_regs; + glue->regset.nregs = ARRAY_SIZE(dsps_musb_regs); + glue->regset.base = musb->ctrl_base; + + file = debugfs_create_regset32("regdump", S_IRUGO, root, &glue->regset); + if (!file) { + debugfs_remove_recursive(root); + return -ENOMEM; + } + return 0; +} + static int dsps_musb_init(struct musb *musb) { struct device *dev = musb->controller; @@ -377,6 +423,7 @@ static int dsps_musb_init(struct musb *musb) void __iomem *reg_base; struct resource *r; u32 rev, val; + int ret; r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control"); if (!r) @@ -410,6 +457,10 @@ static int dsps_musb_init(struct musb *musb) val &= ~(1 << wrp->otg_disable); dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); + ret = dsps_musb_dbg_init(musb, glue); + if (ret) + return ret; + return 0; } @@ -616,7 +667,7 @@ static int dsps_probe(struct platform_device *pdev) wrp = match->data; /* allocate glue */ - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "unable to allocate glue memory\n"); return -ENOMEM; @@ -644,7 +695,6 @@ err3: pm_runtime_put(&pdev->dev); err2: pm_runtime_disable(&pdev->dev); - kfree(glue); return ret; } @@ -657,7 +707,9 @@ static int dsps_remove(struct platform_device *pdev) /* disable usbss clocks */ pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - kfree(glue); + + debugfs_remove_recursive(glue->dbgfs_root); + return 0; } diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index abb38c3833ef..eb06291a40c8 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1694,7 +1694,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) | MUSB_RXCSR_RXPKTRDY); musb_writew(hw_ep->regs, MUSB_RXCSR, val); -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ + defined(CONFIG_USB_TI_CPPI41_DMA) if (usb_pipeisoc(pipe)) { struct usb_iso_packet_descriptor *d; @@ -1707,10 +1708,30 @@ void musb_host_rx(struct musb *musb, u8 epnum) if (d->status != -EILSEQ && d->status != -EOVERFLOW) d->status = 0; - if (++qh->iso_idx >= urb->number_of_packets) + if (++qh->iso_idx >= urb->number_of_packets) { done = true; - else + } else { +#if defined(CONFIG_USB_TI_CPPI41_DMA) + struct dma_controller *c; + dma_addr_t *buf; + u32 length, ret; + + c = musb->dma_controller; + buf = (void *) + urb->iso_frame_desc[qh->iso_idx].offset + + (u32)urb->transfer_dma; + + length = + urb->iso_frame_desc[qh->iso_idx].length; + + val |= MUSB_RXCSR_DMAENAB; + musb_writew(hw_ep->regs, MUSB_RXCSR, val); + + ret = c->channel_program(dma, qh->maxpacket, + 0, (u32) buf, length); +#endif done = false; + } } else { /* done if urb buffer is full or short packet is recd */ @@ -1750,7 +1771,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) } /* we are expecting IN packets */ -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ + defined(CONFIG_USB_TI_CPPI41_DMA) if (dma) { struct dma_controller *c; u16 rx_count; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 8aa59a2c5eb2..d341c149a2f9 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -37,7 +37,7 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/usb/musb-omap.h> -#include <linux/usb/omap_control_usb.h> +#include <linux/phy/omap_control_phy.h> #include <linux/of_platform.h> #include "musb_core.h" diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 7d1451d5bbea..416e0c8cf6ff 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -75,27 +75,6 @@ config NOP_USB_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. -config OMAP_CONTROL_USB - tristate "OMAP CONTROL USB Driver" - depends on ARCH_OMAP2PLUS || COMPILE_TEST - help - Enable this to add support for the USB part present in the control - module. This driver has API to power on the USB2 PHY and to write to - the mailbox. The mailbox is present only in omap4 and the register to - power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an - additional register to power on USB3 PHY. - -config OMAP_USB3 - tristate "OMAP USB3 PHY Driver" - depends on ARCH_OMAP2PLUS || COMPILE_TEST - select OMAP_CONTROL_USB - select USB_PHY - help - Enable this to support the USB3 PHY that is part of SOC. This - driver takes care of all the PHY functionality apart from comparator. - This driver interacts with the "OMAP Control USB Driver" to power - on/off the PHY. - config AM335X_CONTROL_USB tristate diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index be58adae3496..f8fa719a31b9 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -13,11 +13,9 @@ obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o obj-$(CONFIG_TAHVO_USB) += phy-tahvo.o -obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o -obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 7aa314ef4a8a..c47e5a6edde2 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -317,10 +317,12 @@ int otg_statemachine(struct otg_fsm *fsm) otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); break; case OTG_STATE_A_HOST: - if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) && + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) && fsm->otg->host->b_hnp_enable) otg_set_state(fsm, OTG_STATE_A_SUSPEND); - else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) + else if (!fsm->b_conn) otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); else if (!fsm->a_vbus_vld) otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); @@ -346,8 +348,7 @@ int otg_statemachine(struct otg_fsm *fsm) otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); break; case OTG_STATE_A_WAIT_VFALL: - if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req || - (!fsm->a_sess_vld && !fsm->b_conn)) + if (fsm->a_wait_vfall_tmout) otg_set_state(fsm, OTG_STATE_A_IDLE); break; case OTG_STATE_A_VBUS_ERR: diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index d204f745ed05..5b37b81f2ef6 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1428,7 +1428,8 @@ static int __init msm_otg_probe(struct platform_device *pdev) motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); if (!motg->phy.otg) { dev_err(&pdev->dev, "unable to allocate msm_otg\n"); - return -ENOMEM; + ret = -ENOMEM; + goto free_motg; } motg->pdata = dev_get_platdata(&pdev->dev); diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index b42897b6474c..c42bdf0c4a1f 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -1,5 +1,5 @@ /* - * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012-2013 Freescale Semiconductor, Inc. * Copyright (C) 2012 Marek Vasut <marex@denx.de> * on behalf of DENX Software Engineering GmbH * @@ -20,6 +20,9 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> #define DRIVER_NAME "mxs_phy" @@ -28,18 +31,134 @@ #define HW_USBPHY_CTRL_SET 0x34 #define HW_USBPHY_CTRL_CLR 0x38 +#define HW_USBPHY_DEBUG_SET 0x54 +#define HW_USBPHY_DEBUG_CLR 0x58 + +#define HW_USBPHY_IP 0x90 +#define HW_USBPHY_IP_SET 0x94 +#define HW_USBPHY_IP_CLR 0x98 + #define BM_USBPHY_CTRL_SFTRST BIT(31) #define BM_USBPHY_CTRL_CLKGATE BIT(30) +#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) +#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) +#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) +#define BM_USBPHY_CTRL_ENIDCHG_WKUP BIT(22) +#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP BIT(21) +#define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD BIT(20) +#define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE BIT(19) +#define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL BIT(18) #define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) #define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) +#define BM_USBPHY_IP_FIX (BIT(17) | BIT(18)) + +#define BM_USBPHY_DEBUG_CLKGATE BIT(30) + +/* Anatop Registers */ +#define ANADIG_ANA_MISC0 0x150 +#define ANADIG_ANA_MISC0_SET 0x154 +#define ANADIG_ANA_MISC0_CLR 0x158 + +#define ANADIG_USB1_VBUS_DET_STAT 0x1c0 +#define ANADIG_USB2_VBUS_DET_STAT 0x220 + +#define ANADIG_USB1_LOOPBACK_SET 0x1e4 +#define ANADIG_USB1_LOOPBACK_CLR 0x1e8 +#define ANADIG_USB2_LOOPBACK_SET 0x244 +#define ANADIG_USB2_LOOPBACK_CLR 0x248 + +#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12) +#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11) + +#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) +#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID BIT(3) + +#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 BIT(2) +#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN BIT(5) +#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2) +#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5) + +#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) + +/* Do disconnection between PHY and controller without vbus */ +#define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS BIT(0) + +/* + * The PHY will be in messy if there is a wakeup after putting + * bus to suspend (set portsc.suspendM) but before setting PHY to low + * power mode (set portsc.phcd). + */ +#define MXS_PHY_ABNORMAL_IN_SUSPEND BIT(1) + +/* + * The SOF sends too fast after resuming, it will cause disconnection + * between host and high speed device. + */ +#define MXS_PHY_SENDING_SOF_TOO_FAST BIT(2) + +/* + * IC has bug fixes logic, they include + * MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST + * which are described at above flags, the RTL will handle it + * according to different versions. + */ +#define MXS_PHY_NEED_IP_FIX BIT(3) + +struct mxs_phy_data { + unsigned int flags; +}; + +static const struct mxs_phy_data imx23_phy_data = { + .flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST, +}; + +static const struct mxs_phy_data imx6q_phy_data = { + .flags = MXS_PHY_SENDING_SOF_TOO_FAST | + MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_NEED_IP_FIX, +}; + +static const struct mxs_phy_data imx6sl_phy_data = { + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_NEED_IP_FIX, +}; + +static const struct of_device_id mxs_phy_dt_ids[] = { + { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, }, + { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, }, + { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); + struct mxs_phy { struct usb_phy phy; struct clk *clk; + const struct mxs_phy_data *data; + struct regmap *regmap_anatop; + int port_id; }; -#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) +static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy) +{ + return mxs_phy->data == &imx6q_phy_data; +} + +static inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy) +{ + return mxs_phy->data == &imx6sl_phy_data; +} + +/* + * PHY needs some 32K cycles to switch from 32K clock to + * bus (such as AHB/AXI, etc) clock. + */ +static void mxs_phy_clock_switch_delay(void) +{ + usleep_range(300, 400); +} static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) { @@ -53,19 +172,105 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) /* Power up the PHY */ writel(0, base + HW_USBPHY_PWD); - /* enable FS/LS device */ - writel(BM_USBPHY_CTRL_ENUTMILEVEL2 | - BM_USBPHY_CTRL_ENUTMILEVEL3, + /* + * USB PHY Ctrl Setting + * - Auto clock/power on + * - Enable full/low speed support + */ + writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | + BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | + BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | + BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | + BM_USBPHY_CTRL_ENAUTO_PWRON_PLL | + BM_USBPHY_CTRL_ENUTMILEVEL2 | + BM_USBPHY_CTRL_ENUTMILEVEL3, base + HW_USBPHY_CTRL_SET); + if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX) + writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET); + return 0; } +/* Return true if the vbus is there */ +static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy) +{ + unsigned int vbus_value; + + if (mxs_phy->port_id == 0) + regmap_read(mxs_phy->regmap_anatop, + ANADIG_USB1_VBUS_DET_STAT, + &vbus_value); + else if (mxs_phy->port_id == 1) + regmap_read(mxs_phy->regmap_anatop, + ANADIG_USB2_VBUS_DET_STAT, + &vbus_value); + + if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID) + return true; + else + return false; +} + +static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) +{ + void __iomem *base = mxs_phy->phy.io_priv; + u32 reg; + + if (disconnect) + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_CLR); + + if (mxs_phy->port_id == 0) { + reg = disconnect ? ANADIG_USB1_LOOPBACK_SET + : ANADIG_USB1_LOOPBACK_CLR; + regmap_write(mxs_phy->regmap_anatop, reg, + BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 | + BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN); + } else if (mxs_phy->port_id == 1) { + reg = disconnect ? ANADIG_USB2_LOOPBACK_SET + : ANADIG_USB2_LOOPBACK_CLR; + regmap_write(mxs_phy->regmap_anatop, reg, + BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 | + BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN); + } + + if (!disconnect) + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_SET); + + /* Delay some time, and let Linestate be SE0 for controller */ + if (disconnect) + usleep_range(500, 1000); +} + +static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) +{ + bool vbus_is_on = false; + + /* If the SoCs don't need to disconnect line without vbus, quit */ + if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS)) + return; + + /* If the SoCs don't have anatop, quit */ + if (!mxs_phy->regmap_anatop) + return; + + vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); + + if (on && !vbus_is_on) + __mxs_phy_disconnect_line(mxs_phy, true); + else + __mxs_phy_disconnect_line(mxs_phy, false); + +} + static int mxs_phy_init(struct usb_phy *phy) { int ret; struct mxs_phy *mxs_phy = to_mxs_phy(phy); + mxs_phy_clock_switch_delay(); ret = clk_prepare_enable(mxs_phy->clk); if (ret) return ret; @@ -94,6 +299,7 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) x->io_priv + HW_USBPHY_CTRL_SET); clk_disable_unprepare(mxs_phy->clk); } else { + mxs_phy_clock_switch_delay(); ret = clk_prepare_enable(mxs_phy->clk); if (ret) return ret; @@ -105,11 +311,28 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) return 0; } +static int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(x); + u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | + BM_USBPHY_CTRL_ENDPDMCHG_WKUP | + BM_USBPHY_CTRL_ENIDCHG_WKUP; + if (enabled) { + mxs_phy_disconnect_line(mxs_phy, true); + writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET); + } else { + writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR); + mxs_phy_disconnect_line(mxs_phy, false); + } + + return 0; +} + static int mxs_phy_on_connect(struct usb_phy *phy, enum usb_device_speed speed) { - dev_dbg(phy->dev, "%s speed device has connected\n", - (speed == USB_SPEED_HIGH) ? "high" : "non-high"); + dev_dbg(phy->dev, "%s device has connected\n", + (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); if (speed == USB_SPEED_HIGH) writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, @@ -121,8 +344,8 @@ static int mxs_phy_on_connect(struct usb_phy *phy, static int mxs_phy_on_disconnect(struct usb_phy *phy, enum usb_device_speed speed) { - dev_dbg(phy->dev, "%s speed device has disconnected\n", - (speed == USB_SPEED_HIGH) ? "high" : "non-high"); + dev_dbg(phy->dev, "%s device has disconnected\n", + (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); if (speed == USB_SPEED_HIGH) writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, @@ -138,6 +361,9 @@ static int mxs_phy_probe(struct platform_device *pdev) struct clk *clk; struct mxs_phy *mxs_phy; int ret; + const struct of_device_id *of_id = + of_match_device(mxs_phy_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); @@ -157,6 +383,22 @@ static int mxs_phy_probe(struct platform_device *pdev) return -ENOMEM; } + /* Some SoCs don't have anatop registers */ + if (of_get_property(np, "fsl,anatop", NULL)) { + mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle + (np, "fsl,anatop"); + if (IS_ERR(mxs_phy->regmap_anatop)) { + dev_dbg(&pdev->dev, + "failed to find regmap for anatop\n"); + return PTR_ERR(mxs_phy->regmap_anatop); + } + } + + ret = of_alias_get_id(np, "usbphy"); + if (ret < 0) + dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret); + mxs_phy->port_id = ret; + mxs_phy->phy.io_priv = base; mxs_phy->phy.dev = &pdev->dev; mxs_phy->phy.label = DRIVER_NAME; @@ -166,11 +408,15 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->phy.notify_connect = mxs_phy_on_connect; mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; mxs_phy->phy.type = USB_PHY_TYPE_USB2; + mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup; mxs_phy->clk = clk; + mxs_phy->data = of_id->data; platform_set_drvdata(pdev, mxs_phy); + device_set_wakeup_capable(&pdev->dev, true); + ret = usb_add_phy_dev(&mxs_phy->phy); if (ret) return ret; @@ -187,11 +433,46 @@ static int mxs_phy_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id mxs_phy_dt_ids[] = { - { .compatible = "fsl,imx23-usbphy", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); +#ifdef CONFIG_PM_SLEEP +static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on) +{ + unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR; + + /* If the SoCs don't have anatop, quit */ + if (!mxs_phy->regmap_anatop) + return; + + if (is_imx6q_phy(mxs_phy)) + regmap_write(mxs_phy->regmap_anatop, reg, + BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG); + else if (is_imx6sl_phy(mxs_phy)) + regmap_write(mxs_phy->regmap_anatop, + reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL); +} + +static int mxs_phy_system_suspend(struct device *dev) +{ + struct mxs_phy *mxs_phy = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + mxs_phy_enable_ldo_in_suspend(mxs_phy, true); + + return 0; +} + +static int mxs_phy_system_resume(struct device *dev) +{ + struct mxs_phy *mxs_phy = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + mxs_phy_enable_ldo_in_suspend(mxs_phy, false); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend, + mxs_phy_system_resume); static struct platform_driver mxs_phy_driver = { .probe = mxs_phy_probe, @@ -200,6 +481,7 @@ static struct platform_driver mxs_phy_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = mxs_phy_dt_ids, + .pm = &mxs_phy_pm, }, }; diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c deleted file mode 100644 index e7253182e47d..000000000000 --- a/drivers/usb/phy/phy-omap-control.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * omap-control-usb.c - The USB part of control module. - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I <kishon@ti.com> - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/clk.h> -#include <linux/usb/omap_control_usb.h> - -/** - * omap_control_usb_phy_power - power on/off the phy using control module reg - * @dev: the control module device - * @on: 0 or 1, based on powering on or off the PHY - */ -void omap_control_usb_phy_power(struct device *dev, int on) -{ - u32 val; - unsigned long rate; - struct omap_control_usb *control_usb; - - if (IS_ERR(dev) || !dev) { - pr_err("%s: invalid device\n", __func__); - return; - } - - control_usb = dev_get_drvdata(dev); - if (!control_usb) { - dev_err(dev, "%s: invalid control usb device\n", __func__); - return; - } - - if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) - return; - - val = readl(control_usb->power); - - switch (control_usb->type) { - case OMAP_CTRL_TYPE_USB2: - if (on) - val &= ~OMAP_CTRL_DEV_PHY_PD; - else - val |= OMAP_CTRL_DEV_PHY_PD; - break; - - case OMAP_CTRL_TYPE_PIPE3: - rate = clk_get_rate(control_usb->sys_clk); - rate = rate/1000000; - - if (on) { - val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK | - OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK); - val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON << - OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; - val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT; - } else { - val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK; - val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF << - OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; - } - break; - - case OMAP_CTRL_TYPE_DRA7USB2: - if (on) - val &= ~OMAP_CTRL_USB2_PHY_PD; - else - val |= OMAP_CTRL_USB2_PHY_PD; - break; - - case OMAP_CTRL_TYPE_AM437USB2: - if (on) { - val &= ~(AM437X_CTRL_USB2_PHY_PD | - AM437X_CTRL_USB2_OTG_PD); - val |= (AM437X_CTRL_USB2_OTGVDET_EN | - AM437X_CTRL_USB2_OTGSESSEND_EN); - } else { - val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | - AM437X_CTRL_USB2_OTGSESSEND_EN); - val |= (AM437X_CTRL_USB2_PHY_PD | - AM437X_CTRL_USB2_OTG_PD); - } - break; - default: - dev_err(dev, "%s: type %d not recognized\n", - __func__, control_usb->type); - break; - } - - writel(val, control_usb->power); -} -EXPORT_SYMBOL_GPL(omap_control_usb_phy_power); - -/** - * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core that a usb - * device has been connected. - */ -static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); - val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high - * impedance - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core that it has been - * connected to a usb host. - */ -static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~OMAP_CTRL_DEV_SESSEND; - val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | - OMAP_CTRL_DEV_VBUSVALID; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high - * impedance - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core it's now in - * disconnected state. - */ -static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); - val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode - * or device mode or to denote disconnected state - * @dev: the control module device - * @mode: The mode to which usb should be configured - * - * This is an API to write to the mailbox register to notify the usb core that - * a usb device has been connected. - */ -void omap_control_usb_set_mode(struct device *dev, - enum omap_control_usb_mode mode) -{ - struct omap_control_usb *ctrl_usb; - - if (IS_ERR(dev) || !dev) - return; - - ctrl_usb = dev_get_drvdata(dev); - - if (!ctrl_usb) { - dev_err(dev, "Invalid control usb device\n"); - return; - } - - if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS) - return; - - switch (mode) { - case USB_MODE_HOST: - omap_control_usb_host_mode(ctrl_usb); - break; - case USB_MODE_DEVICE: - omap_control_usb_device_mode(ctrl_usb); - break; - case USB_MODE_DISCONNECT: - omap_control_usb_set_sessionend(ctrl_usb); - break; - default: - dev_vdbg(dev, "invalid omap control usb mode\n"); - } -} -EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); - -#ifdef CONFIG_OF - -static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS; -static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2; -static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; -static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; -static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; - -static const struct of_device_id omap_control_usb_id_table[] = { - { - .compatible = "ti,control-phy-otghs", - .data = &otghs_data, - }, - { - .compatible = "ti,control-phy-usb2", - .data = &usb2_data, - }, - { - .compatible = "ti,control-phy-pipe3", - .data = &pipe3_data, - }, - { - .compatible = "ti,control-phy-dra7usb2", - .data = &dra7usb2_data, - }, - { - .compatible = "ti,control-phy-am437usb2", - .data = &am437usb2_data, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); -#endif - - -static int omap_control_usb_probe(struct platform_device *pdev) -{ - struct resource *res; - const struct of_device_id *of_id; - struct omap_control_usb *control_usb; - - of_id = of_match_device(of_match_ptr(omap_control_usb_id_table), - &pdev->dev); - if (!of_id) - return -EINVAL; - - control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb), - GFP_KERNEL); - if (!control_usb) { - dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); - return -ENOMEM; - } - - control_usb->dev = &pdev->dev; - control_usb->type = *(enum omap_control_usb_type *)of_id->data; - - if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "otghs_control"); - control_usb->otghs_control = devm_ioremap_resource( - &pdev->dev, res); - if (IS_ERR(control_usb->otghs_control)) - return PTR_ERR(control_usb->otghs_control); - } else { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "power"); - control_usb->power = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(control_usb->power)) { - dev_err(&pdev->dev, "Couldn't get power register\n"); - return PTR_ERR(control_usb->power); - } - } - - if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) { - control_usb->sys_clk = devm_clk_get(control_usb->dev, - "sys_clkin"); - if (IS_ERR(control_usb->sys_clk)) { - pr_err("%s: unable to get sys_clkin\n", __func__); - return -EINVAL; - } - } - - dev_set_drvdata(control_usb->dev, control_usb); - - return 0; -} - -static struct platform_driver omap_control_usb_driver = { - .probe = omap_control_usb_probe, - .driver = { - .name = "omap-control-usb", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(omap_control_usb_id_table), - }, -}; - -static int __init omap_control_usb_init(void) -{ - return platform_driver_register(&omap_control_usb_driver); -} -subsys_initcall(omap_control_usb_init); - -static void __exit omap_control_usb_exit(void) -{ - platform_driver_unregister(&omap_control_usb_driver); -} -module_exit(omap_control_usb_exit); - -MODULE_ALIAS("platform: omap_control_usb"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP Control Module USB Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/phy-omap-usb3.c b/drivers/usb/phy/phy-omap-usb3.c deleted file mode 100644 index 0c6ba29bdddd..000000000000 --- a/drivers/usb/phy/phy-omap-usb3.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP. - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I <kishon@ti.com> - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/usb/omap_usb.h> -#include <linux/of.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/pm_runtime.h> -#include <linux/delay.h> -#include <linux/usb/omap_control_usb.h> -#include <linux/of_platform.h> - -#define PLL_STATUS 0x00000004 -#define PLL_GO 0x00000008 -#define PLL_CONFIGURATION1 0x0000000C -#define PLL_CONFIGURATION2 0x00000010 -#define PLL_CONFIGURATION3 0x00000014 -#define PLL_CONFIGURATION4 0x00000020 - -#define PLL_REGM_MASK 0x001FFE00 -#define PLL_REGM_SHIFT 0x9 -#define PLL_REGM_F_MASK 0x0003FFFF -#define PLL_REGM_F_SHIFT 0x0 -#define PLL_REGN_MASK 0x000001FE -#define PLL_REGN_SHIFT 0x1 -#define PLL_SELFREQDCO_MASK 0x0000000E -#define PLL_SELFREQDCO_SHIFT 0x1 -#define PLL_SD_MASK 0x0003FC00 -#define PLL_SD_SHIFT 0x9 -#define SET_PLL_GO 0x1 -#define PLL_TICOPWDN 0x10000 -#define PLL_LOCK 0x2 -#define PLL_IDLE 0x1 - -/* - * This is an Empirical value that works, need to confirm the actual - * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status - * to be correctly reflected in the USB3PHY_PLL_STATUS register. - */ -# define PLL_IDLE_TIME 100; - -struct usb_dpll_map { - unsigned long rate; - struct usb_dpll_params params; -}; - -static struct usb_dpll_map dpll_map[] = { - {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ - {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ - {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ - {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ - {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ - {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ -}; - -static struct usb_dpll_params *omap_usb3_get_dpll_params(unsigned long rate) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dpll_map); i++) { - if (rate == dpll_map[i].rate) - return &dpll_map[i].params; - } - - return NULL; -} - -static int omap_usb3_suspend(struct usb_phy *x, int suspend) -{ - struct omap_usb *phy = phy_to_omapusb(x); - int val; - int timeout = PLL_IDLE_TIME; - - if (suspend && !phy->is_suspended) { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val |= PLL_IDLE; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (val & PLL_TICOPWDN) - break; - udelay(1); - } while (--timeout); - - omap_control_usb_phy_power(phy->control_dev, 0); - - phy->is_suspended = 1; - } else if (!suspend && phy->is_suspended) { - phy->is_suspended = 0; - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val &= ~PLL_IDLE; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (!(val & PLL_TICOPWDN)) - break; - udelay(1); - } while (--timeout); - } - - return 0; -} - -static void omap_usb_dpll_relock(struct omap_usb *phy) -{ - u32 val; - unsigned long timeout; - - omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); - - timeout = jiffies + msecs_to_jiffies(20); - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (val & PLL_LOCK) - break; - } while (!WARN_ON(time_after(jiffies, timeout))); -} - -static int omap_usb_dpll_lock(struct omap_usb *phy) -{ - u32 val; - unsigned long rate; - struct usb_dpll_params *dpll_params; - - rate = clk_get_rate(phy->sys_clk); - dpll_params = omap_usb3_get_dpll_params(rate); - if (!dpll_params) { - dev_err(phy->dev, - "No DPLL configuration for %lu Hz SYS CLK\n", rate); - return -EINVAL; - } - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); - val &= ~PLL_REGN_MASK; - val |= dpll_params->n << PLL_REGN_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val &= ~PLL_SELFREQDCO_MASK; - val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); - val &= ~PLL_REGM_MASK; - val |= dpll_params->m << PLL_REGM_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); - val &= ~PLL_REGM_F_MASK; - val |= dpll_params->mf << PLL_REGM_F_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); - val &= ~PLL_SD_MASK; - val |= dpll_params->sd << PLL_SD_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); - - omap_usb_dpll_relock(phy); - - return 0; -} - -static int omap_usb3_init(struct usb_phy *x) -{ - struct omap_usb *phy = phy_to_omapusb(x); - int ret; - - ret = omap_usb_dpll_lock(phy); - if (ret) - return ret; - - omap_control_usb_phy_power(phy->control_dev, 1); - - return 0; -} - -static int omap_usb3_probe(struct platform_device *pdev) -{ - struct omap_usb *phy; - struct resource *res; - struct device_node *node = pdev->dev.of_node; - struct device_node *control_node; - struct platform_device *control_pdev; - - if (!node) - return -EINVAL; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n"); - return -ENOMEM; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); - phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->pll_ctrl_base)) - return PTR_ERR(phy->pll_ctrl_base); - - phy->dev = &pdev->dev; - - phy->phy.dev = phy->dev; - phy->phy.label = "omap-usb3"; - phy->phy.init = omap_usb3_init; - phy->phy.set_suspend = omap_usb3_suspend; - phy->phy.type = USB_PHY_TYPE_USB3; - - phy->is_suspended = 1; - phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); - if (IS_ERR(phy->wkupclk)) { - dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); - return PTR_ERR(phy->wkupclk); - } - clk_prepare(phy->wkupclk); - - phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); - if (IS_ERR(phy->optclk)) { - dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n"); - return PTR_ERR(phy->optclk); - } - clk_prepare(phy->optclk); - - phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin"); - if (IS_ERR(phy->sys_clk)) { - pr_err("%s: unable to get sys_clkin\n", __func__); - return -EINVAL; - } - - control_node = of_parse_phandle(node, "ctrl-module", 0); - if (!control_node) { - dev_err(&pdev->dev, "Failed to get control device phandle\n"); - return -EINVAL; - } - control_pdev = of_find_device_by_node(control_node); - if (!control_pdev) { - dev_err(&pdev->dev, "Failed to get control device\n"); - return -EINVAL; - } - - phy->control_dev = &control_pdev->dev; - - omap_control_usb_phy_power(phy->control_dev, 0); - usb_add_phy_dev(&phy->phy); - - platform_set_drvdata(pdev, phy); - - pm_runtime_enable(phy->dev); - pm_runtime_get(&pdev->dev); - - return 0; -} - -static int omap_usb3_remove(struct platform_device *pdev) -{ - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_unprepare(phy->wkupclk); - clk_unprepare(phy->optclk); - usb_remove_phy(&phy->phy); - if (!pm_runtime_suspended(&pdev->dev)) - pm_runtime_put(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -#ifdef CONFIG_PM_RUNTIME - -static int omap_usb3_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_disable(phy->wkupclk); - clk_disable(phy->optclk); - - return 0; -} - -static int omap_usb3_runtime_resume(struct device *dev) -{ - u32 ret = 0; - struct platform_device *pdev = to_platform_device(dev); - struct omap_usb *phy = platform_get_drvdata(pdev); - - ret = clk_enable(phy->optclk); - if (ret) { - dev_err(phy->dev, "Failed to enable optclk %d\n", ret); - goto err1; - } - - ret = clk_enable(phy->wkupclk); - if (ret) { - dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); - goto err2; - } - - return 0; - -err2: - clk_disable(phy->optclk); - -err1: - return ret; -} - -static const struct dev_pm_ops omap_usb3_pm_ops = { - SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume, - NULL) -}; - -#define DEV_PM_OPS (&omap_usb3_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - -#ifdef CONFIG_OF -static const struct of_device_id omap_usb3_id_table[] = { - { .compatible = "ti,omap-usb3" }, - {} -}; -MODULE_DEVICE_TABLE(of, omap_usb3_id_table); -#endif - -static struct platform_driver omap_usb3_driver = { - .probe = omap_usb3_probe, - .remove = omap_usb3_remove, - .driver = { - .name = "omap-usb3", - .owner = THIS_MODULE, - .pm = DEV_PM_OPS, - .of_match_table = of_match_ptr(omap_usb3_id_table), - }, -}; - -module_platform_driver(omap_usb3_driver); - -MODULE_ALIAS("platform: omap_usb3"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP USB3 phy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c index 551e0a6c0e22..388d89f6b141 100644 --- a/drivers/usb/phy/phy-rcar-gen2-usb.c +++ b/drivers/usb/phy/phy-rcar-gen2-usb.c @@ -177,15 +177,15 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) struct clk *clk; int retval; - pdata = dev_get_platdata(&pdev->dev); + pdata = dev_get_platdata(dev); if (!pdata) { dev_err(dev, "No platform data\n"); return -EINVAL; } - clk = devm_clk_get(&pdev->dev, "usbhs"); + clk = devm_clk_get(dev, "usbhs"); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "Can't get the clock\n"); + dev_err(dev, "Can't get the clock\n"); return PTR_ERR(clk); } diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 214172b68d5d..04778cf80d60 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -27,7 +27,7 @@ #include <linux/io.h> #include <linux/usb/musb-omap.h> #include <linux/usb/phy_companion.h> -#include <linux/usb/omap_usb.h> +#include <linux/phy/omap_usb.h> #include <linux/i2c/twl.h> #include <linux/regulator/consumer.h> #include <linux/err.h> diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c index 217339dd7a90..17ea3f271bd8 100644 --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -47,6 +47,8 @@ struct ulpi_info { static struct ulpi_info ulpi_ids[] = { ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"), ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), + ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"), + ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"), }; static int ulpi_set_otg_flags(struct usb_phy *phy) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 82371f61f23d..2d72aa3564a3 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -323,11 +323,11 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) if (r) goto out; - dev_dbg(&port->dev, "%s - submitting interrupt urb", __func__); + dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__); r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (r) { - dev_err(&port->dev, "%s - failed submitting interrupt urb," - " error %d\n", __func__, r); + dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n", + __func__, r); ch341_close(port); goto out; } diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 0ac3b3b3236c..2916dea3ede8 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -220,7 +220,7 @@ static int cyberjack_write(struct tty_struct *tty, result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { dev_err(&port->dev, - "%s - failed submitting write urb, error %d", + "%s - failed submitting write urb, error %d\n", __func__, result); /* Throw away data. No better idea what to do with it. */ priv->wrfilled = 0; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index bccb1223143a..01bf53392819 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -279,7 +279,7 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate) * the generic firmware, but are not used with * NMEA and SiRF protocols */ dev_dbg(&port->dev, - "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS", + "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n", __func__, new_rate); return -1; } @@ -1224,7 +1224,6 @@ static void cypress_write_int_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct cypress_private *priv = usb_get_serial_port_data(port); struct device *dev = &urb->dev->dev; - int result; int status = urb->status; switch (status) { @@ -1239,21 +1238,9 @@ static void cypress_write_int_callback(struct urb *urb) __func__, status); priv->write_urb_in_use = 0; return; - case -EPIPE: /* no break needed; clear halt and resubmit */ - if (!priv->comm_is_ok) - break; - usb_clear_halt(port->serial->dev, 0x02); - /* error in the urb, so we have to resubmit it */ - dev_dbg(dev, "%s - nonzero write bulk status received: %d\n", - __func__, status); - port->interrupt_out_urb->transfer_buffer_length = 1; - result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); - if (!result) - return; - dev_err(dev, "%s - failed resubmitting write urb, error %d\n", - __func__, result); - cypress_set_dead(port); - break; + case -EPIPE: + /* Cannot call usb_clear_halt while in_interrupt */ + /* FALLTHROUGH */ default: dev_err(dev, "%s - unexpected nonzero write status received: %d\n", __func__, status); diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index b63ce023f96f..1bd192290b08 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -332,9 +332,9 @@ void usb_serial_generic_process_read_urb(struct urb *urb) * stuff like 3G modems, so shortcircuit it in the 99.9999999% of * cases where the USB serial is not a console anyway. */ - if (!port->port.console || !port->sysrq) + if (!port->port.console || !port->sysrq) { tty_insert_flip_string(&port->port, ch, urb->actual_length); - else { + } else { for (i = 0; i < urb->actual_length; i++, ch++) { if (!usb_serial_handle_sysrq_char(port, *ch)) tty_insert_flip_char(&port->port, *ch, TTY_NORMAL); @@ -359,24 +359,38 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, urb->actual_length); - - if (urb->status) { - dev_dbg(&port->dev, "%s - non-zero urb status: %d\n", - __func__, urb->status); + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&port->dev, "%s - urb stopped: %d\n", + __func__, urb->status); return; + case -EPIPE: + dev_err(&port->dev, "%s - urb stopped: %d\n", + __func__, urb->status); + return; + default: + dev_err(&port->dev, "%s - nonzero urb status: %d\n", + __func__, urb->status); + goto resubmit; } usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); port->serial->type->process_read_urb(urb); +resubmit: /* Throttle the device if requested by tty */ spin_lock_irqsave(&port->lock, flags); port->throttled = port->throttle_req; if (!port->throttled) { spin_unlock_irqrestore(&port->lock, flags); usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); - } else + } else { spin_unlock_irqrestore(&port->lock, flags); + } } EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); @@ -384,29 +398,38 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) { unsigned long flags; struct usb_serial_port *port = urb->context; - int status = urb->status; int i; - for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { if (port->write_urbs[i] == urb) break; - + } spin_lock_irqsave(&port->lock, flags); port->tx_bytes -= urb->transfer_buffer_length; set_bit(i, &port->write_urbs_free); spin_unlock_irqrestore(&port->lock, flags); - if (status) { - dev_dbg(&port->dev, "%s - non-zero urb status: %d\n", - __func__, status); - - spin_lock_irqsave(&port->lock, flags); - kfifo_reset_out(&port->write_fifo); - spin_unlock_irqrestore(&port->lock, flags); - } else { - usb_serial_generic_write_start(port, GFP_ATOMIC); + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&port->dev, "%s - urb stopped: %d\n", + __func__, urb->status); + return; + case -EPIPE: + dev_err_console(port, "%s - urb stopped: %d\n", + __func__, urb->status); + return; + default: + dev_err_console(port, "%s - nonzero urb status: %d\n", + __func__, urb->status); + goto resubmit; } +resubmit: + usb_serial_generic_write_start(port, GFP_ATOMIC); usb_serial_port_softint(port); } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index d00dae17d520..5ad4a0fb4b26 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -1151,7 +1151,7 @@ static ssize_t vcc_mode_store(struct device *dev, goto fail_store_vcc_mode; } - dev_dbg(dev, "%s: setting vcc_mode = %ld", __func__, v); + dev_dbg(dev, "%s: setting vcc_mode = %ld\n", __func__, v); if ((v != 3) && (v != 5)) { dev_err(dev, "%s - vcc_mode %ld is invalid\n", __func__, v); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 265c6776b081..d3acaead5a81 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -397,17 +397,6 @@ static void usa26_instat_callback(struct urb *urb) msg = (struct keyspan_usa26_portStatusMessage *)data; -#if 0 - dev_dbg(&urb->dev->dev, - "%s - port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d", - __func__, msg->port, msg->hskia_cts, msg->gpia_dcd, msg->dsr, - msg->ri, msg->_txOff, msg->_txXoff, msg->rxEnabled, - msg->controlResponse); -#endif - - /* Now do something useful with the data */ - - /* Check port number from message and retrieve private data */ if (msg->port >= serial->num_ports) { dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port); @@ -523,9 +512,6 @@ static void usa28_instat_callback(struct urb *urb) goto exit; } - /*dev_dbg(&urb->dev->dev, "%s %12ph", __func__, data);*/ - - /* Now do something useful with the data */ msg = (struct keyspan_usa28_portStatusMessage *)data; /* Check port number from message and retrieve private data */ @@ -605,9 +591,6 @@ static void usa49_instat_callback(struct urb *urb) goto exit; } - /*dev_dbg(&urb->dev->dev, "%s: %11ph", __func__, data);*/ - - /* Now do something useful with the data */ msg = (struct keyspan_usa49_portStatusMessage *)data; /* Check port number from message and retrieve private data */ @@ -1793,12 +1776,6 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err != 0) dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed\n", __func__); -#if 0 - else { - dev_dbg(&port->dev, "%s - usb_submit_urb(setup) OK %d bytes\n", __func__, - this_urb->transfer_buffer_length); - } -#endif return 0; } @@ -1976,13 +1953,6 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err != 0) dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err); -#if 0 - else { - dev_dbg(&port->dev, "%s - usb_submit_urb(%d) OK %d bytes (end %d)\n", __func__, - outcont_urb, this_urb->transfer_buffer_length, - usb_pipeendpoint(this_urb->pipe)); - } -#endif return 0; } diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index e972412b614b..742d827f876c 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -189,7 +189,7 @@ exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(&port->dev, - "%s - usb_submit_urb failed with result %d", + "%s - usb_submit_urb failed with result %d\n", __func__, retval); } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index c88cc4966b23..d7440b7557af 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -201,7 +201,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, else { status = get_unaligned_le16(status_buf); - dev_info(&port->serial->dev->dev, "read status %x %x", + dev_info(&port->serial->dev->dev, "read status %x %x\n", status_buf[0], status_buf[1]); *line_state_p = klsi_105_status2linestate(status); @@ -464,7 +464,7 @@ static void klsi_105_set_termios(struct tty_struct *tty, priv->cfg.baudrate = kl5kusb105a_sio_b115200; break; default: - dev_dbg(dev, "KLSI USB->Serial converter: unsupported baudrate request, using default of 9600"); + dev_dbg(dev, "unsupported baudrate, using 9600\n"); priv->cfg.baudrate = kl5kusb105a_sio_b9600; baud = 9600; break; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 618c1c1f227e..fee242387f55 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -557,7 +557,8 @@ static int kobil_ioctl(struct tty_struct *tty, ); dev_dbg(&port->dev, - "%s - Send reset_all_queues (FLUSH) URB returns: %i", __func__, result); + "%s - Send reset_all_queues (FLUSH) URB returns: %i\n", + __func__, result); kfree(transfer_buffer); return (result < 0) ? -EIO: 0; default: diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 4eb277225a77..dfd728a263d2 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -209,7 +209,7 @@ static int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, index, NULL, 0, MOS_WDR_TIMEOUT); if (status < 0) dev_err(&usbdev->dev, - "mos7720: usb_control_msg() failed: %d", status); + "mos7720: usb_control_msg() failed: %d\n", status); return status; } @@ -240,7 +240,7 @@ static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, *data = *buf; else if (status < 0) dev_err(&usbdev->dev, - "mos7720: usb_control_msg() failed: %d", status); + "mos7720: usb_control_msg() failed: %d\n", status); kfree(buf); return status; @@ -399,7 +399,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, &mos_parport->deferred_urbs); spin_unlock_irqrestore(&mos_parport->listlock, flags); tasklet_schedule(&mos_parport->urb_tasklet); - dev_dbg(&usbdev->dev, "tasklet scheduled"); + dev_dbg(&usbdev->dev, "tasklet scheduled\n"); return 0; } @@ -418,7 +418,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, mutex_unlock(&serial->disc_mutex); if (ret_val) { dev_err(&usbdev->dev, - "%s: submit_urb() failed: %d", __func__, ret_val); + "%s: submit_urb() failed: %d\n", __func__, ret_val); spin_lock_irqsave(&mos_parport->listlock, flags); list_del(&urbtrack->urblist_entry); spin_unlock_irqrestore(&mos_parport->listlock, flags); @@ -656,7 +656,7 @@ static size_t parport_mos7715_write_compat(struct parport *pp, parport_epilogue(pp); if (retval) { dev_err(&mos_parport->serial->dev->dev, - "mos7720: usb_bulk_msg() failed: %d", retval); + "mos7720: usb_bulk_msg() failed: %d\n", retval); return 0; } return actual_len; @@ -875,7 +875,7 @@ static void mos7715_interrupt_callback(struct urb *urb) if (!(iir & 0x01)) { /* serial port interrupt pending */ switch (iir & 0x0f) { case SERIAL_IIR_RLS: - dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n\n"); + dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n"); break; case SERIAL_IIR_CTI: dev_dbg(dev, "Serial Port: Receiver time out\n"); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index e9d967ff521b..393be562d875 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -522,11 +522,11 @@ static void mos7840_set_led_callback(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* This urb is terminated, clean up */ - dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d", + dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n", __func__, urb->status); break; default: - dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d", + dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, urb->status); } } diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 7725ed261ed6..504f5bff79c0 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -372,7 +372,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) device_port, data, 2, QT2_USB_TIMEOUT); if (status < 0) { - dev_err(&port->dev, "%s - open port failed %i", __func__, + dev_err(&port->dev, "%s - open port failed %i\n", __func__, status); kfree(data); return status; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 4ec04f73c800..ef0dbf0703c5 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -220,9 +220,9 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) GET_UART_STATUS, GET_UART_STATUS_TYPE, 0, GET_UART_STATUS_MSR, buf, 1, 100); if (ret < 0) - dev_err(&port->dev, "failed to get modem status: %d", ret); + dev_err(&port->dev, "failed to get modem status: %d\n", ret); - dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x", ret, *buf); + dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf); *status = *buf; kfree(buf); @@ -342,8 +342,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, case 1000000: buf[0] = 0x0b; break; default: - dev_err(&port->dev, "spcp825 driver does not support the " - "baudrate requested, using default of 9600.\n"); + dev_err(&port->dev, "unsupported baudrate, using 9600\n"); } /* Set Data Length : 00:5bit, 01:6bit, 10:7bit, 11:8bit */ diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 9fa7dd413e83..8fceec7298e0 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -74,9 +74,7 @@ static void symbol_int_callback(struct urb *urb) tty_insert_flip_string(&port->port, &data[1], data_length); tty_flip_buffer_push(&port->port); } else { - dev_dbg(&port->dev, - "Improper amount of data received from the device, " - "%d bytes", urb->actual_length); + dev_dbg(&port->dev, "%s - short packet\n", __func__); } exit: diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index ec7cea585663..3dd3ff8c50d3 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -293,7 +293,7 @@ static int ti_startup(struct usb_serial *serial) int status; dev_dbg(&dev->dev, - "%s - product 0x%4X, num configurations %d, configuration value %d", + "%s - product 0x%4X, num configurations %d, configuration value %d\n", __func__, le16_to_cpu(dev->descriptor.idProduct), dev->descriptor.bNumConfigurations, dev->actconfig->desc.bConfigurationValue); @@ -803,7 +803,7 @@ static void ti_set_termios(struct tty_struct *tty, tty_encode_baud_rate(tty, baud, baud); dev_dbg(&port->dev, - "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d", + "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d\n", __func__, baud, config->wBaudRate, config->wFlags, config->bDataBits, config->bParity, config->bStopBits, config->cXon, config->cXoff, config->bUartMode); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 7c9dc28640bb..81fc0dfcfdcf 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -868,7 +868,7 @@ static int usb_serial_probe(struct usb_interface *interface, max_endpoints = max(max_endpoints, (int)serial->num_ports); serial->num_port_pointers = max_endpoints; - dev_dbg(ddev, "setting up %d port structures for this device", max_endpoints); + dev_dbg(ddev, "setting up %d port structure(s)\n", max_endpoints); for (i = 0; i < max_endpoints; ++i) { port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) @@ -923,9 +923,8 @@ static int usb_serial_probe(struct usb_interface *interface, port = serial->port[i]; if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL)) goto probe_error; - buffer_size = serial->type->bulk_out_size; - if (!buffer_size) - buffer_size = usb_endpoint_maxp(endpoint); + buffer_size = max_t(int, serial->type->bulk_out_size, + usb_endpoint_maxp(endpoint)); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; @@ -1034,7 +1033,7 @@ static int usb_serial_probe(struct usb_interface *interface, for (i = 0; i < num_ports; ++i) { port = serial->port[i]; dev_set_name(&port->dev, "ttyUSB%d", port->minor); - dev_dbg(ddev, "registering %s", dev_name(&port->dev)); + dev_dbg(ddev, "registering %s\n", dev_name(&port->dev)); device_enable_async_suspend(&port->dev); retval = device_add(&port->dev); @@ -1161,9 +1160,9 @@ static int usb_serial_reset_resume(struct usb_interface *intf) usb_serial_unpoison_port_urbs(serial); serial->suspending = 0; - if (serial->type->reset_resume) + if (serial->type->reset_resume) { rv = serial->type->reset_resume(serial); - else { + } else { rv = -EOPNOTSUPP; intf->needs_binding = 1; } @@ -1338,9 +1337,9 @@ static int usb_serial_register(struct usb_serial_driver *driver) if (retval) { pr_err("problem %d when registering driver %s\n", retval, driver->description); list_del(&driver->driver_list); - } else + } else { pr_info("USB Serial support registered for %s\n", driver->description); - + } mutex_unlock(&table_lock); return retval; } diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 1dd0604d1911..13b5bfbaf951 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -204,7 +204,7 @@ config USB_STORAGE_ENE_UB6250 config USB_UAS tristate "USB Attached SCSI" - depends on SCSI && BROKEN + depends on SCSI && USB_STORAGE help The USB Attached SCSI protocol is supported by some USB storage devices. It permits higher performance by supporting diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h new file mode 100644 index 000000000000..bb05b984d5f6 --- /dev/null +++ b/drivers/usb/storage/uas-detect.h @@ -0,0 +1,96 @@ +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include "usb.h" + +static int uas_is_interface(struct usb_host_interface *intf) +{ + return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE && + intf->desc.bInterfaceSubClass == USB_SC_SCSI && + intf->desc.bInterfaceProtocol == USB_PR_UAS); +} + +static int uas_isnt_supported(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + dev_warn(&udev->dev, "The driver for the USB controller %s does not " + "support scatter-gather which is\n", + hcd->driver->description); + dev_warn(&udev->dev, "required by the UAS driver. Please try an" + "alternative USB controller if you wish to use UAS.\n"); + return -ENODEV; +} + +static int uas_find_uas_alt_setting(struct usb_interface *intf) +{ + int i; + struct usb_device *udev = interface_to_usbdev(intf); + int sg_supported = udev->bus->sg_tablesize != 0; + + for (i = 0; i < intf->num_altsetting; i++) { + struct usb_host_interface *alt = &intf->altsetting[i]; + + if (uas_is_interface(alt)) { + if (!sg_supported) + return uas_isnt_supported(udev); + return alt->desc.bAlternateSetting; + } + } + + return -ENODEV; +} + +static int uas_find_endpoints(struct usb_host_interface *alt, + struct usb_host_endpoint *eps[]) +{ + struct usb_host_endpoint *endpoint = alt->endpoint; + unsigned i, n_endpoints = alt->desc.bNumEndpoints; + + for (i = 0; i < n_endpoints; i++) { + unsigned char *extra = endpoint[i].extra; + int len = endpoint[i].extralen; + while (len >= 3) { + if (extra[1] == USB_DT_PIPE_USAGE) { + unsigned pipe_id = extra[2]; + if (pipe_id > 0 && pipe_id < 5) + eps[pipe_id - 1] = &endpoint[i]; + break; + } + len -= extra[0]; + extra += extra[0]; + } + } + + if (!eps[0] || !eps[1] || !eps[2] || !eps[3]) + return -ENODEV; + + return 0; +} + +static int uas_use_uas_driver(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_endpoint *eps[4] = { }; + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + unsigned long flags = id->driver_info; + int r, alt; + + usb_stor_adjust_quirks(udev, &flags); + + if (flags & US_FL_IGNORE_UAS) + return 0; + + if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) + return 0; + + alt = uas_find_uas_alt_setting(intf); + if (alt < 0) + return 0; + + r = uas_find_endpoints(&intf->altsetting[alt], eps); + if (r < 0) + return 0; + + return 1; +} diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index d966b59f7d7b..a7ac97cc5949 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -2,6 +2,7 @@ * USB Attached SCSI * Note that this is not the same as the USB Mass Storage driver * + * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 * Copyright Matthew Wilcox for Intel Corp, 2010 * Copyright Sarah Sharp for Intel Corp, 2010 * @@ -13,17 +14,21 @@ #include <linux/types.h> #include <linux/module.h> #include <linux/usb.h> +#include <linux/usb_usual.h> #include <linux/usb/hcd.h> #include <linux/usb/storage.h> #include <linux/usb/uas.h> #include <scsi/scsi.h> +#include <scsi/scsi_eh.h> #include <scsi/scsi_dbg.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> +#include "uas-detect.h" + /* * The r00-r01c specs define this version of the SENSE IU data structure. * It's still in use by several different firmware releases. @@ -45,12 +50,17 @@ struct uas_dev_info { struct usb_anchor sense_urbs; struct usb_anchor data_urbs; int qdepth, resetting; - struct response_ui response; + struct response_iu response; unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; unsigned use_streams:1; unsigned uas_sense_old:1; + unsigned running_task:1; + unsigned shutdown:1; struct scsi_cmnd *cmnd; spinlock_t lock; + struct work_struct work; + struct list_head inflight_list; + struct list_head dead_list; }; enum { @@ -85,103 +95,117 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo, gfp_t gfp); static void uas_do_work(struct work_struct *work); static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); +static void uas_free_streams(struct uas_dev_info *devinfo); +static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller); -static DECLARE_WORK(uas_work, uas_do_work); -static DEFINE_SPINLOCK(uas_work_lock); -static LIST_HEAD(uas_work_list); - +/* Must be called with devinfo->lock held, will temporary unlock the lock */ static void uas_unlink_data_urbs(struct uas_dev_info *devinfo, - struct uas_cmd_info *cmdinfo) + struct uas_cmd_info *cmdinfo, + unsigned long *lock_flags) { - unsigned long flags; - /* * The UNLINK_DATA_URBS flag makes sure uas_try_complete * (called by urb completion) doesn't release cmdinfo * underneath us. */ - spin_lock_irqsave(&devinfo->lock, flags); cmdinfo->state |= UNLINK_DATA_URBS; - spin_unlock_irqrestore(&devinfo->lock, flags); + spin_unlock_irqrestore(&devinfo->lock, *lock_flags); if (cmdinfo->data_in_urb) usb_unlink_urb(cmdinfo->data_in_urb); if (cmdinfo->data_out_urb) usb_unlink_urb(cmdinfo->data_out_urb); - spin_lock_irqsave(&devinfo->lock, flags); + spin_lock_irqsave(&devinfo->lock, *lock_flags); cmdinfo->state &= ~UNLINK_DATA_URBS; - spin_unlock_irqrestore(&devinfo->lock, flags); } static void uas_do_work(struct work_struct *work) { + struct uas_dev_info *devinfo = + container_of(work, struct uas_dev_info, work); struct uas_cmd_info *cmdinfo; - struct uas_cmd_info *temp; - struct list_head list; unsigned long flags; int err; - spin_lock_irq(&uas_work_lock); - list_replace_init(&uas_work_list, &list); - spin_unlock_irq(&uas_work_lock); - - list_for_each_entry_safe(cmdinfo, temp, &list, list) { + spin_lock_irqsave(&devinfo->lock, flags); + list_for_each_entry(cmdinfo, &devinfo->inflight_list, list) { struct scsi_pointer *scp = (void *)cmdinfo; - struct scsi_cmnd *cmnd = container_of(scp, - struct scsi_cmnd, SCp); - struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; - spin_lock_irqsave(&devinfo->lock, flags); - err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, + SCp); + + if (!(cmdinfo->state & IS_IN_WORK_LIST)) + continue; + + err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); if (!err) cmdinfo->state &= ~IS_IN_WORK_LIST; - spin_unlock_irqrestore(&devinfo->lock, flags); - if (err) { - list_del(&cmdinfo->list); - spin_lock_irq(&uas_work_lock); - list_add_tail(&cmdinfo->list, &uas_work_list); - spin_unlock_irq(&uas_work_lock); - schedule_work(&uas_work); - } + else + schedule_work(&devinfo->work); } + spin_unlock_irqrestore(&devinfo->lock, flags); } -static void uas_abort_work(struct uas_dev_info *devinfo) +static void uas_mark_cmd_dead(struct uas_dev_info *devinfo, + struct uas_cmd_info *cmdinfo, + int result, const char *caller) +{ + struct scsi_pointer *scp = (void *)cmdinfo; + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); + + uas_log_cmd_state(cmnd, caller); + WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); + WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); + cmdinfo->state |= COMMAND_ABORTED; + cmdinfo->state &= ~IS_IN_WORK_LIST; + cmnd->result = result << 16; + list_move_tail(&cmdinfo->list, &devinfo->dead_list); +} + +static void uas_abort_inflight(struct uas_dev_info *devinfo, int result, + const char *caller) { struct uas_cmd_info *cmdinfo; struct uas_cmd_info *temp; - struct list_head list; unsigned long flags; - spin_lock_irq(&uas_work_lock); - list_replace_init(&uas_work_list, &list); - spin_unlock_irq(&uas_work_lock); + spin_lock_irqsave(&devinfo->lock, flags); + list_for_each_entry_safe(cmdinfo, temp, &devinfo->inflight_list, list) + uas_mark_cmd_dead(devinfo, cmdinfo, result, caller); + spin_unlock_irqrestore(&devinfo->lock, flags); +} + +static void uas_add_work(struct uas_cmd_info *cmdinfo) +{ + struct scsi_pointer *scp = (void *)cmdinfo; + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); + struct uas_dev_info *devinfo = cmnd->device->hostdata; + + WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); + cmdinfo->state |= IS_IN_WORK_LIST; + schedule_work(&devinfo->work); +} + +static void uas_zap_dead(struct uas_dev_info *devinfo) +{ + struct uas_cmd_info *cmdinfo; + struct uas_cmd_info *temp; + unsigned long flags; spin_lock_irqsave(&devinfo->lock, flags); - list_for_each_entry_safe(cmdinfo, temp, &list, list) { + list_for_each_entry_safe(cmdinfo, temp, &devinfo->dead_list, list) { struct scsi_pointer *scp = (void *)cmdinfo; - struct scsi_cmnd *cmnd = container_of(scp, - struct scsi_cmnd, SCp); - struct uas_dev_info *di = (void *)cmnd->device->hostdata; - - if (di == devinfo) { - cmdinfo->state |= COMMAND_ABORTED; - cmdinfo->state &= ~IS_IN_WORK_LIST; - if (devinfo->resetting) { - /* uas_stat_cmplt() will not do that - * when a device reset is in - * progress */ - cmdinfo->state &= ~COMMAND_INFLIGHT; - } - uas_try_complete(cmnd, __func__); - } else { - /* not our uas device, relink into list */ - list_del(&cmdinfo->list); - spin_lock_irq(&uas_work_lock); - list_add_tail(&cmdinfo->list, &uas_work_list); - spin_unlock_irq(&uas_work_lock); - } + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, + SCp); + uas_log_cmd_state(cmnd, __func__); + WARN_ON_ONCE(!(cmdinfo->state & COMMAND_ABORTED)); + /* all urbs are killed, clear inflight bits */ + cmdinfo->state &= ~(COMMAND_INFLIGHT | + DATA_IN_URB_INFLIGHT | + DATA_OUT_URB_INFLIGHT); + uas_try_complete(cmnd, __func__); } + devinfo->running_task = 0; spin_unlock_irqrestore(&devinfo->lock, flags); } @@ -259,20 +283,19 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; - WARN_ON(!spin_is_locked(&devinfo->lock)); + WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); if (cmdinfo->state & (COMMAND_INFLIGHT | DATA_IN_URB_INFLIGHT | DATA_OUT_URB_INFLIGHT | UNLINK_DATA_URBS)) return -EBUSY; - BUG_ON(cmdinfo->state & COMMAND_COMPLETED); + WARN_ON_ONCE(cmdinfo->state & COMMAND_COMPLETED); cmdinfo->state |= COMMAND_COMPLETED; usb_free_urb(cmdinfo->data_in_urb); usb_free_urb(cmdinfo->data_out_urb); - if (cmdinfo->state & COMMAND_ABORTED) { + if (cmdinfo->state & COMMAND_ABORTED) scmd_printk(KERN_INFO, cmnd, "abort completed\n"); - cmnd->result = DID_ABORT << 16; - } + list_del(&cmdinfo->list); cmnd->scsi_done(cmnd); return 0; } @@ -286,11 +309,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, cmdinfo->state |= direction | SUBMIT_STATUS_URB; err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); if (err) { - spin_lock(&uas_work_lock); - list_add_tail(&cmdinfo->list, &uas_work_list); - cmdinfo->state |= IS_IN_WORK_LIST; - spin_unlock(&uas_work_lock); - schedule_work(&uas_work); + uas_add_work(cmdinfo); } } @@ -298,14 +317,20 @@ static void uas_stat_cmplt(struct urb *urb) { struct iu *iu = urb->transfer_buffer; struct Scsi_Host *shost = urb->context; - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; struct scsi_cmnd *cmnd; struct uas_cmd_info *cmdinfo; unsigned long flags; u16 tag; if (urb->status) { - dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status); + if (urb->status == -ENOENT) { + dev_err(&urb->dev->dev, "stat urb: killed, stream %d\n", + urb->stream_id); + } else { + dev_err(&urb->dev->dev, "stat urb: status %d\n", + urb->status); + } usb_free_urb(urb); return; } @@ -324,6 +349,9 @@ static void uas_stat_cmplt(struct urb *urb) if (!cmnd) { if (iu->iu_id == IU_ID_RESPONSE) { + if (!devinfo->running_task) + dev_warn(&urb->dev->dev, + "stat urb: recv unexpected response iu\n"); /* store results for uas_eh_task_mgmt() */ memcpy(&devinfo->response, iu, sizeof(devinfo->response)); } @@ -346,17 +374,25 @@ static void uas_stat_cmplt(struct urb *urb) uas_sense(urb, cmnd); if (cmnd->result != 0) { /* cancel data transfers on error */ - spin_unlock_irqrestore(&devinfo->lock, flags); - uas_unlink_data_urbs(devinfo, cmdinfo); - spin_lock_irqsave(&devinfo->lock, flags); + uas_unlink_data_urbs(devinfo, cmdinfo, &flags); } cmdinfo->state &= ~COMMAND_INFLIGHT; uas_try_complete(cmnd, __func__); break; case IU_ID_READ_READY: + if (!cmdinfo->data_in_urb || + (cmdinfo->state & DATA_IN_URB_INFLIGHT)) { + scmd_printk(KERN_ERR, cmnd, "unexpected read rdy\n"); + break; + } uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB); break; case IU_ID_WRITE_READY: + if (!cmdinfo->data_out_urb || + (cmdinfo->state & DATA_OUT_URB_INFLIGHT)) { + scmd_printk(KERN_ERR, cmnd, "unexpected write rdy\n"); + break; + } uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); break; default: @@ -383,8 +419,15 @@ static void uas_data_cmplt(struct urb *urb) sdb = scsi_out(cmnd); cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT; } - BUG_ON(sdb == NULL); - if (urb->status) { + if (sdb == NULL) { + WARN_ON_ONCE(1); + } else if (urb->status) { + if (urb->status != -ECONNRESET) { + uas_log_cmd_state(cmnd, __func__); + scmd_printk(KERN_ERR, cmnd, + "data cmplt err %d stream %d\n", + urb->status, urb->stream_id); + } /* error: no data transfered */ sdb->resid = sdb->length; } else { @@ -394,6 +437,17 @@ static void uas_data_cmplt(struct urb *urb) spin_unlock_irqrestore(&devinfo->lock, flags); } +static void uas_cmd_cmplt(struct urb *urb) +{ + struct scsi_cmnd *cmnd = urb->context; + + if (urb->status) { + uas_log_cmd_state(cmnd, __func__); + scmd_printk(KERN_ERR, cmnd, "cmd cmplt err %d\n", urb->status); + } + usb_free_urb(urb); +} + static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, unsigned int pipe, u16 stream_id, struct scsi_cmnd *cmnd, @@ -408,8 +462,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto out; usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt, cmnd); - if (devinfo->use_streams) - urb->stream_id = stream_id; + urb->stream_id = stream_id; urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; urb->sg = sdb->table.sgl; out: @@ -442,7 +495,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, } static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, - struct scsi_cmnd *cmnd, u16 stream_id) + struct scsi_cmnd *cmnd) { struct usb_device *udev = devinfo->udev; struct scsi_device *sdev = cmnd->device; @@ -472,7 +525,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len); usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu) + len, - usb_free_urb, NULL); + uas_cmd_cmplt, cmnd); urb->transfer_flags |= URB_FREE_BUFFER; out: return urb; @@ -512,13 +565,17 @@ static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp, } usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu), - usb_free_urb, NULL); + uas_cmd_cmplt, cmnd); urb->transfer_flags |= URB_FREE_BUFFER; + usb_anchor_urb(urb, &devinfo->cmd_urbs); err = usb_submit_urb(urb, gfp); - if (err) + if (err) { + usb_unanchor_urb(urb); + uas_log_cmd_state(cmnd, __func__); + scmd_printk(KERN_ERR, cmnd, "task submission err %d\n", err); goto err; - usb_anchor_urb(urb, &devinfo->cmd_urbs); + } return 0; @@ -533,38 +590,43 @@ err: * daft to me. */ -static int uas_submit_sense_urb(struct Scsi_Host *shost, - gfp_t gfp, unsigned int stream) +static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, + gfp_t gfp, unsigned int stream) { - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct Scsi_Host *shost = cmnd->device->host; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; struct urb *urb; + int err; urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream); if (!urb) - return SCSI_MLQUEUE_DEVICE_BUSY; - if (usb_submit_urb(urb, gfp)) { + return NULL; + usb_anchor_urb(urb, &devinfo->sense_urbs); + err = usb_submit_urb(urb, gfp); + if (err) { + usb_unanchor_urb(urb); + uas_log_cmd_state(cmnd, __func__); shost_printk(KERN_INFO, shost, - "sense urb submission failure\n"); + "sense urb submission error %d stream %d\n", + err, stream); usb_free_urb(urb); - return SCSI_MLQUEUE_DEVICE_BUSY; + return NULL; } - usb_anchor_urb(urb, &devinfo->sense_urbs); - return 0; + return urb; } static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo, gfp_t gfp) { struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + struct urb *urb; int err; - WARN_ON(!spin_is_locked(&devinfo->lock)); + WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); if (cmdinfo->state & SUBMIT_STATUS_URB) { - err = uas_submit_sense_urb(cmnd->device->host, gfp, - cmdinfo->stream); - if (err) { - return err; - } + urb = uas_submit_sense_urb(cmnd, gfp, cmdinfo->stream); + if (!urb) + return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~SUBMIT_STATUS_URB; } @@ -578,14 +640,18 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, } if (cmdinfo->state & SUBMIT_DATA_IN_URB) { - if (usb_submit_urb(cmdinfo->data_in_urb, gfp)) { + usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); + err = usb_submit_urb(cmdinfo->data_in_urb, gfp); + if (err) { + usb_unanchor_urb(cmdinfo->data_in_urb); + uas_log_cmd_state(cmnd, __func__); scmd_printk(KERN_INFO, cmnd, - "data in urb submission failure\n"); + "data in urb submission error %d stream %d\n", + err, cmdinfo->data_in_urb->stream_id); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_IN_URB; cmdinfo->state |= DATA_IN_URB_INFLIGHT; - usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); } if (cmdinfo->state & ALLOC_DATA_OUT_URB) { @@ -598,33 +664,37 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, } if (cmdinfo->state & SUBMIT_DATA_OUT_URB) { - if (usb_submit_urb(cmdinfo->data_out_urb, gfp)) { + usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); + err = usb_submit_urb(cmdinfo->data_out_urb, gfp); + if (err) { + usb_unanchor_urb(cmdinfo->data_out_urb); + uas_log_cmd_state(cmnd, __func__); scmd_printk(KERN_INFO, cmnd, - "data out urb submission failure\n"); + "data out urb submission error %d stream %d\n", + err, cmdinfo->data_out_urb->stream_id); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_OUT_URB; cmdinfo->state |= DATA_OUT_URB_INFLIGHT; - usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); } if (cmdinfo->state & ALLOC_CMD_URB) { - cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd, - cmdinfo->stream); + cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd); if (!cmdinfo->cmd_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_CMD_URB; } if (cmdinfo->state & SUBMIT_CMD_URB) { - usb_get_urb(cmdinfo->cmd_urb); - if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) { + usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); + err = usb_submit_urb(cmdinfo->cmd_urb, gfp); + if (err) { + usb_unanchor_urb(cmdinfo->cmd_urb); + uas_log_cmd_state(cmnd, __func__); scmd_printk(KERN_INFO, cmnd, - "cmd urb submission failure\n"); + "cmd urb submission error %d\n", err); return SCSI_MLQUEUE_DEVICE_BUSY; } - usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); - usb_put_urb(cmdinfo->cmd_urb); cmdinfo->cmd_urb = NULL; cmdinfo->state &= ~SUBMIT_CMD_URB; cmdinfo->state |= COMMAND_INFLIGHT; @@ -644,18 +714,22 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); + spin_lock_irqsave(&devinfo->lock, flags); + if (devinfo->resetting) { cmnd->result = DID_ERROR << 16; cmnd->scsi_done(cmnd); + spin_unlock_irqrestore(&devinfo->lock, flags); return 0; } - spin_lock_irqsave(&devinfo->lock, flags); if (devinfo->cmnd) { spin_unlock_irqrestore(&devinfo->lock, flags); return SCSI_MLQUEUE_DEVICE_BUSY; } + memset(cmdinfo, 0, sizeof(*cmdinfo)); + if (blk_rq_tagged(cmnd->request)) { cmdinfo->stream = cmnd->request->tag + 2; } else { @@ -692,13 +766,10 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, spin_unlock_irqrestore(&devinfo->lock, flags); return SCSI_MLQUEUE_DEVICE_BUSY; } - spin_lock(&uas_work_lock); - list_add_tail(&cmdinfo->list, &uas_work_list); - cmdinfo->state |= IS_IN_WORK_LIST; - spin_unlock(&uas_work_lock); - schedule_work(&uas_work); + uas_add_work(cmdinfo); } + list_add_tail(&cmdinfo->list, &devinfo->inflight_list); spin_unlock_irqrestore(&devinfo->lock, flags); return 0; } @@ -709,46 +780,78 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, const char *fname, u8 function) { struct Scsi_Host *shost = cmnd->device->host; - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; - u16 tag = devinfo->qdepth - 1; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + u16 tag = devinfo->qdepth; unsigned long flags; + struct urb *sense_urb; + int result = SUCCESS; spin_lock_irqsave(&devinfo->lock, flags); + + if (devinfo->resetting) { + spin_unlock_irqrestore(&devinfo->lock, flags); + return FAILED; + } + + if (devinfo->running_task) { + shost_printk(KERN_INFO, shost, + "%s: %s: error already running a task\n", + __func__, fname); + spin_unlock_irqrestore(&devinfo->lock, flags); + return FAILED; + } + + devinfo->running_task = 1; memset(&devinfo->response, 0, sizeof(devinfo->response)); - if (uas_submit_sense_urb(shost, GFP_ATOMIC, tag)) { + sense_urb = uas_submit_sense_urb(cmnd, GFP_NOIO, + devinfo->use_streams ? tag : 0); + if (!sense_urb) { shost_printk(KERN_INFO, shost, "%s: %s: submit sense urb failed\n", __func__, fname); + devinfo->running_task = 0; spin_unlock_irqrestore(&devinfo->lock, flags); return FAILED; } - if (uas_submit_task_urb(cmnd, GFP_ATOMIC, function, tag)) { + if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) { shost_printk(KERN_INFO, shost, "%s: %s: submit task mgmt urb failed\n", __func__, fname); + devinfo->running_task = 0; spin_unlock_irqrestore(&devinfo->lock, flags); + usb_kill_urb(sense_urb); return FAILED; } spin_unlock_irqrestore(&devinfo->lock, flags); if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) { + /* + * Note we deliberately do not clear running_task here. If we + * allow new tasks to be submitted, there is no way to figure + * out if a received response_iu is for the failed task or for + * the new one. A bus-reset will eventually clear running_task. + */ shost_printk(KERN_INFO, shost, "%s: %s timed out\n", __func__, fname); return FAILED; } + + spin_lock_irqsave(&devinfo->lock, flags); + devinfo->running_task = 0; if (be16_to_cpu(devinfo->response.tag) != tag) { shost_printk(KERN_INFO, shost, "%s: %s failed (wrong tag %d/%d)\n", __func__, fname, be16_to_cpu(devinfo->response.tag), tag); - return FAILED; - } - if (devinfo->response.response_code != RC_TMF_COMPLETE) { + result = FAILED; + } else if (devinfo->response.response_code != RC_TMF_COMPLETE) { shost_printk(KERN_INFO, shost, "%s: %s failed (rc 0x%x)\n", __func__, fname, devinfo->response.response_code); - return FAILED; + result = FAILED; } - return SUCCESS; + spin_unlock_irqrestore(&devinfo->lock, flags); + + return result; } static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) @@ -758,22 +861,19 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) unsigned long flags; int ret; - uas_log_cmd_state(cmnd, __func__); spin_lock_irqsave(&devinfo->lock, flags); - cmdinfo->state |= COMMAND_ABORTED; - if (cmdinfo->state & IS_IN_WORK_LIST) { - spin_lock(&uas_work_lock); - list_del(&cmdinfo->list); - cmdinfo->state &= ~IS_IN_WORK_LIST; - spin_unlock(&uas_work_lock); + + if (devinfo->resetting) { + spin_unlock_irqrestore(&devinfo->lock, flags); + return FAILED; } + + uas_mark_cmd_dead(devinfo, cmdinfo, DID_ABORT, __func__); if (cmdinfo->state & COMMAND_INFLIGHT) { spin_unlock_irqrestore(&devinfo->lock, flags); ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); } else { - spin_unlock_irqrestore(&devinfo->lock, flags); - uas_unlink_data_urbs(devinfo, cmdinfo); - spin_lock_irqsave(&devinfo->lock, flags); + uas_unlink_data_urbs(devinfo, cmdinfo, &flags); uas_try_complete(cmnd, __func__); spin_unlock_irqrestore(&devinfo->lock, flags); ret = SUCCESS; @@ -795,14 +895,25 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) struct usb_device *udev = devinfo->udev; int err; + err = usb_lock_device_for_reset(udev, devinfo->intf); + if (err) { + shost_printk(KERN_ERR, sdev->host, + "%s FAILED to get lock err %d\n", __func__, err); + return FAILED; + } + + shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__); devinfo->resetting = 1; - uas_abort_work(devinfo); + uas_abort_inflight(devinfo, DID_RESET, __func__); usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); + uas_zap_dead(devinfo); err = usb_reset_device(udev); devinfo->resetting = 0; + usb_unlock_device(udev); + if (err) { shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__); return FAILED; @@ -814,7 +925,25 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) static int uas_slave_alloc(struct scsi_device *sdev) { - sdev->hostdata = (void *)sdev->host->hostdata[0]; + sdev->hostdata = (void *)sdev->host->hostdata; + + /* USB has unusual DMA-alignment requirements: Although the + * starting address of each scatter-gather element doesn't matter, + * the length of each element except the last must be divisible + * by the Bulk maxpacket value. There's currently no way to + * express this by block-layer constraints, so we'll cop out + * and simply require addresses to be aligned at 512-byte + * boundaries. This is okay since most block I/O involves + * hardware sectors that are multiples of 512 bytes in length, + * and since host controllers up through USB 2.0 have maxpacket + * values no larger than 512. + * + * But it doesn't suffice for Wireless USB, where Bulk maxpacket + * values can be as large as 2048. To make that work properly + * will require changes to the block layer. + */ + blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); + return 0; } @@ -822,7 +951,7 @@ static int uas_slave_configure(struct scsi_device *sdev) { struct uas_dev_info *devinfo = sdev->hostdata; scsi_set_tag_type(sdev, MSG_ORDERED_TAG); - scsi_activate_tcq(sdev, devinfo->qdepth - 3); + scsi_activate_tcq(sdev, devinfo->qdepth - 2); return 0; } @@ -843,7 +972,14 @@ static struct scsi_host_template uas_host_template = { .ordered_tag = 1, }; +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + static struct usb_device_id uas_usb_ids[] = { +# include "unusual_uas.h" { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_UAS) }, /* 0xaa is a prototype device I happen to have access to */ @@ -852,105 +988,55 @@ static struct usb_device_id uas_usb_ids[] = { }; MODULE_DEVICE_TABLE(usb, uas_usb_ids); -static int uas_is_interface(struct usb_host_interface *intf) -{ - return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE && - intf->desc.bInterfaceSubClass == USB_SC_SCSI && - intf->desc.bInterfaceProtocol == USB_PR_UAS); -} - -static int uas_isnt_supported(struct usb_device *udev) -{ - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - - dev_warn(&udev->dev, "The driver for the USB controller %s does not " - "support scatter-gather which is\n", - hcd->driver->description); - dev_warn(&udev->dev, "required by the UAS driver. Please try an" - "alternative USB controller if you wish to use UAS.\n"); - return -ENODEV; -} +#undef UNUSUAL_DEV static int uas_switch_interface(struct usb_device *udev, - struct usb_interface *intf) + struct usb_interface *intf) { - int i; - int sg_supported = udev->bus->sg_tablesize != 0; - - for (i = 0; i < intf->num_altsetting; i++) { - struct usb_host_interface *alt = &intf->altsetting[i]; - - if (uas_is_interface(alt)) { - if (!sg_supported) - return uas_isnt_supported(udev); - return usb_set_interface(udev, - alt->desc.bInterfaceNumber, - alt->desc.bAlternateSetting); - } - } + int alt; + + alt = uas_find_uas_alt_setting(intf); + if (alt < 0) + return alt; - return -ENODEV; + return usb_set_interface(udev, + intf->altsetting[0].desc.bInterfaceNumber, alt); } -static void uas_configure_endpoints(struct uas_dev_info *devinfo) +static int uas_configure_endpoints(struct uas_dev_info *devinfo) { struct usb_host_endpoint *eps[4] = { }; - struct usb_interface *intf = devinfo->intf; struct usb_device *udev = devinfo->udev; - struct usb_host_endpoint *endpoint = intf->cur_altsetting->endpoint; - unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints; + int r; devinfo->uas_sense_old = 0; devinfo->cmnd = NULL; - for (i = 0; i < n_endpoints; i++) { - unsigned char *extra = endpoint[i].extra; - int len = endpoint[i].extralen; - while (len > 1) { - if (extra[1] == USB_DT_PIPE_USAGE) { - unsigned pipe_id = extra[2]; - if (pipe_id > 0 && pipe_id < 5) - eps[pipe_id - 1] = &endpoint[i]; - break; - } - len -= extra[0]; - extra += extra[0]; - } - } + r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps); + if (r) + return r; - /* - * Assume that if we didn't find a control pipe descriptor, we're - * using a device with old firmware that happens to be set up like - * this. - */ - if (!eps[0]) { - devinfo->cmd_pipe = usb_sndbulkpipe(udev, 1); - devinfo->status_pipe = usb_rcvbulkpipe(udev, 1); - devinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2); - devinfo->data_out_pipe = usb_sndbulkpipe(udev, 2); - - eps[1] = usb_pipe_endpoint(udev, devinfo->status_pipe); - eps[2] = usb_pipe_endpoint(udev, devinfo->data_in_pipe); - eps[3] = usb_pipe_endpoint(udev, devinfo->data_out_pipe); - } else { - devinfo->cmd_pipe = usb_sndbulkpipe(udev, - eps[0]->desc.bEndpointAddress); - devinfo->status_pipe = usb_rcvbulkpipe(udev, - eps[1]->desc.bEndpointAddress); - devinfo->data_in_pipe = usb_rcvbulkpipe(udev, - eps[2]->desc.bEndpointAddress); - devinfo->data_out_pipe = usb_sndbulkpipe(udev, - eps[3]->desc.bEndpointAddress); - } + devinfo->cmd_pipe = usb_sndbulkpipe(udev, + usb_endpoint_num(&eps[0]->desc)); + devinfo->status_pipe = usb_rcvbulkpipe(udev, + usb_endpoint_num(&eps[1]->desc)); + devinfo->data_in_pipe = usb_rcvbulkpipe(udev, + usb_endpoint_num(&eps[2]->desc)); + devinfo->data_out_pipe = usb_sndbulkpipe(udev, + usb_endpoint_num(&eps[3]->desc)); - devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256, - GFP_KERNEL); - if (devinfo->qdepth < 0) { + if (udev->speed != USB_SPEED_SUPER) { devinfo->qdepth = 256; devinfo->use_streams = 0; } else { + devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, + 3, 256, GFP_KERNEL); + if (devinfo->qdepth < 0) + return devinfo->qdepth; devinfo->use_streams = 1; } + + return 0; } static void uas_free_streams(struct uas_dev_info *devinfo) @@ -964,30 +1050,23 @@ static void uas_free_streams(struct uas_dev_info *devinfo) usb_free_streams(devinfo->intf, eps, 3, GFP_KERNEL); } -/* - * XXX: What I'd like to do here is register a SCSI host for each USB host in - * the system. Follow usb-storage's design of registering a SCSI host for - * each USB device for the moment. Can implement this by walking up the - * USB hierarchy until we find a USB host. - */ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) { - int result; - struct Scsi_Host *shost; + int result = -ENOMEM; + struct Scsi_Host *shost = NULL; struct uas_dev_info *devinfo; struct usb_device *udev = interface_to_usbdev(intf); - if (uas_switch_interface(udev, intf)) + if (!uas_use_uas_driver(intf, id)) return -ENODEV; - devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL); - if (!devinfo) - return -ENOMEM; + if (uas_switch_interface(udev, intf)) + return -ENODEV; - result = -ENOMEM; - shost = scsi_host_alloc(&uas_host_template, sizeof(void *)); + shost = scsi_host_alloc(&uas_host_template, + sizeof(struct uas_dev_info)); if (!shost) - goto free; + goto set_alt0; shost->max_cmd_len = 16 + 252; shost->max_id = 1; @@ -995,33 +1074,40 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) shost->max_channel = 0; shost->sg_tablesize = udev->bus->sg_tablesize; + devinfo = (struct uas_dev_info *)shost->hostdata; devinfo->intf = intf; devinfo->udev = udev; devinfo->resetting = 0; + devinfo->running_task = 0; + devinfo->shutdown = 0; init_usb_anchor(&devinfo->cmd_urbs); init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->data_urbs); spin_lock_init(&devinfo->lock); - uas_configure_endpoints(devinfo); + INIT_WORK(&devinfo->work, uas_do_work); + INIT_LIST_HEAD(&devinfo->inflight_list); + INIT_LIST_HEAD(&devinfo->dead_list); - result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); + result = uas_configure_endpoints(devinfo); if (result) - goto free; + goto set_alt0; - result = scsi_add_host(shost, &intf->dev); + result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2); if (result) - goto deconfig_eps; + goto free_streams; - shost->hostdata[0] = (unsigned long)devinfo; + result = scsi_add_host(shost, &intf->dev); + if (result) + goto free_streams; scsi_scan_host(shost); usb_set_intfdata(intf, shost); return result; -deconfig_eps: +free_streams: uas_free_streams(devinfo); - free: - kfree(devinfo); +set_alt0: + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); if (shost) scsi_host_put(shost); return result; @@ -1029,45 +1115,146 @@ deconfig_eps: static int uas_pre_reset(struct usb_interface *intf) { -/* XXX: Need to return 1 if it's not our device in error handling */ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + unsigned long flags; + + if (devinfo->shutdown) + return 0; + + /* Block new requests */ + spin_lock_irqsave(shost->host_lock, flags); + scsi_block_requests(shost); + spin_unlock_irqrestore(shost->host_lock, flags); + + /* Wait for any pending requests to complete */ + flush_work(&devinfo->work); + if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) { + shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); + return 1; + } + + uas_free_streams(devinfo); + return 0; } static int uas_post_reset(struct usb_interface *intf) { -/* XXX: Need to return 1 if it's not our device in error handling */ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + unsigned long flags; + + if (devinfo->shutdown) + return 0; + + if (uas_configure_endpoints(devinfo) != 0) { + shost_printk(KERN_ERR, shost, + "%s: alloc streams error after reset", __func__); + return 1; + } + + spin_lock_irqsave(shost->host_lock, flags); + scsi_report_bus_reset(shost, 0); + spin_unlock_irqrestore(shost->host_lock, flags); + + scsi_unblock_requests(shost); + + return 0; +} + +static int uas_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + + /* Wait for any pending requests to complete */ + flush_work(&devinfo->work); + if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) { + shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); + return -ETIME; + } + + return 0; +} + +static int uas_resume(struct usb_interface *intf) +{ + return 0; +} + +static int uas_reset_resume(struct usb_interface *intf) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + unsigned long flags; + + if (uas_configure_endpoints(devinfo) != 0) { + shost_printk(KERN_ERR, shost, + "%s: alloc streams error after reset", __func__); + return -EIO; + } + + spin_lock_irqsave(shost->host_lock, flags); + scsi_report_bus_reset(shost, 0); + spin_unlock_irqrestore(shost->host_lock, flags); + return 0; } static void uas_disconnect(struct usb_interface *intf) { struct Scsi_Host *shost = usb_get_intfdata(intf); - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; devinfo->resetting = 1; - uas_abort_work(devinfo); + cancel_work_sync(&devinfo->work); + uas_abort_inflight(devinfo, DID_NO_CONNECT, __func__); usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); + uas_zap_dead(devinfo); scsi_remove_host(shost); uas_free_streams(devinfo); - kfree(devinfo); + scsi_host_put(shost); } /* - * XXX: Should this plug into libusual so we can auto-upgrade devices from - * Bulk-Only to UAS? + * Put the device back in usb-storage mode on shutdown, as some BIOS-es + * hang on reboot when the device is still in uas mode. Note the reset is + * necessary as some devices won't revert to usb-storage mode without it. */ +static void uas_shutdown(struct device *dev) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev = interface_to_usbdev(intf); + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + + if (system_state != SYSTEM_RESTART) + return; + + devinfo->shutdown = 1; + uas_free_streams(devinfo); + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); + usb_reset_device(udev); +} + static struct usb_driver uas_driver = { .name = "uas", .probe = uas_probe, .disconnect = uas_disconnect, .pre_reset = uas_pre_reset, .post_reset = uas_post_reset, + .suspend = uas_suspend, + .resume = uas_resume, + .reset_resume = uas_reset_resume, + .drvwrap.driver.shutdown = uas_shutdown, .id_table = uas_usb_ids, }; module_usb_driver(uas_driver); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Matthew Wilcox and Sarah Sharp"); +MODULE_AUTHOR( + "Hans de Goede <hdegoede@redhat.com>, Matthew Wilcox and Sarah Sharp"); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index adbeb255616a..f4a82291894a 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2086,6 +2086,11 @@ UNUSUAL_DEV( 0xed10, 0x7636, 0x0001, 0x0001, "Digital MP3 Audio Player", USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ), +/* Unusual uas devices */ +#if IS_ENABLED(CONFIG_USB_UAS) +#include "unusual_uas.h" +#endif + /* Control/Bulk transport for all SubClass values */ USUAL_DEV(USB_SC_RBC, USB_PR_CB), USUAL_DEV(USB_SC_8020, USB_PR_CB), diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h new file mode 100644 index 000000000000..7244444df8ee --- /dev/null +++ b/drivers/usb/storage/unusual_uas.h @@ -0,0 +1,52 @@ +/* Driver for USB Attached SCSI devices - Unusual Devices File + * + * (c) 2013 Hans de Goede <hdegoede@redhat.com> + * + * Based on the same file for the usb-storage driver, which is: + * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * (c) 2000 Adam J. Richter (adam@yggdrasil.com), Yggdrasil Computing, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * IMPORTANT NOTE: This file must be included in another file which defines + * a UNUSUAL_DEV macro before this file is included. + */ + +/* + * If you edit this file, please try to keep it sorted first by VendorID, + * then by ProductID. + * + * If you want to add an entry for this file, be sure to include the + * following information: + * - a patch that adds the entry for your device, including your + * email address right above the entry (plus maybe a brief + * explanation of the reason for the entry), + * - lsusb -v output for the device + * Send your submission to Hans de Goede <hdegoede@redhat.com> + * and don't forget to CC: the USB development list <linux-usb@vger.kernel.org> + */ + +/* + * This is an example entry for the US_FL_IGNORE_UAS flag. Once we have an + * actual entry using US_FL_IGNORE_UAS this entry should be removed. + * + * UNUSUAL_DEV( 0xabcd, 0x1234, 0x0100, 0x0100, + * "Example", + * "Storage with broken UAS", + * USB_SC_DEVICE, USB_PR_DEVICE, NULL, + * US_FL_IGNORE_UAS), + */ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 1c0b89f2a138..f1c96261a501 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -72,6 +72,10 @@ #include "sierra_ms.h" #include "option_ms.h" +#if IS_ENABLED(CONFIG_USB_UAS) +#include "uas-detect.h" +#endif + /* Some informational data */ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); @@ -459,14 +463,14 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf) #define TOLOWER(x) ((x) | 0x20) /* Adjust device flags based on the "quirks=" module parameter */ -static void adjust_quirks(struct us_data *us) +void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) { char *p; - u16 vid = le16_to_cpu(us->pusb_dev->descriptor.idVendor); - u16 pid = le16_to_cpu(us->pusb_dev->descriptor.idProduct); + u16 vid = le16_to_cpu(udev->descriptor.idVendor); + u16 pid = le16_to_cpu(udev->descriptor.idProduct); unsigned f = 0; unsigned int mask = (US_FL_SANE_SENSE | US_FL_BAD_SENSE | - US_FL_FIX_CAPACITY | + US_FL_FIX_CAPACITY | US_FL_IGNORE_UAS | US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE | US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | @@ -537,14 +541,18 @@ static void adjust_quirks(struct us_data *us) case 's': f |= US_FL_SINGLE_LUN; break; + case 'u': + f |= US_FL_IGNORE_UAS; + break; case 'w': f |= US_FL_NO_WP_DETECT; break; /* Ignore unrecognized flag characters */ } } - us->fflags = (us->fflags & ~mask) | f; + *fflags = (*fflags & ~mask) | f; } +EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks); /* Get the unusual_devs entries and the string descriptors */ static int get_device_info(struct us_data *us, const struct usb_device_id *id, @@ -564,7 +572,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id, idesc->bInterfaceProtocol : unusual_dev->useTransport; us->fflags = id->driver_info; - adjust_quirks(us); + usb_stor_adjust_quirks(us->pusb_dev, &us->fflags); if (us->fflags & US_FL_IGNORE_DEVICE) { dev_info(pdev, "device ignored\n"); @@ -1035,6 +1043,12 @@ static int storage_probe(struct usb_interface *intf, int result; int size; + /* If uas is enabled and this device can do uas then ignore it. */ +#if IS_ENABLED(CONFIG_USB_UAS) + if (uas_use_uas_driver(intf, id)) + return -ENXIO; +#endif + /* * If the device isn't standard (is handled by a subdriver * module) then don't accept it. diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 75f70f04f37b..307e339a9478 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -201,4 +201,7 @@ extern int usb_stor_probe1(struct us_data **pus, extern int usb_stor_probe2(struct us_data *us); extern void usb_stor_disconnect(struct usb_interface *intf); +extern void usb_stor_adjust_quirks(struct usb_device *dev, + unsigned long *fflags); + #endif diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index 3b959e83b28e..0677139c6065 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -284,7 +284,7 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc, struct device *dev = wusbhc->dev; struct wusb_dev *wusb_dev; struct wusb_port *port; - unsigned idx, devnum; + unsigned idx; mutex_lock(&wusbhc->mutex); @@ -312,8 +312,6 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc, goto error_unlock; } - devnum = idx + 2; - /* Make sure we are using no crypto on that "virtual port" */ wusbhc->set_ptk(wusbhc, idx, 0, NULL, 0); diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index 368360f9a93a..252c7bd9218a 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c @@ -75,8 +75,6 @@ void __wa_destroy(struct wahc *wa) if (wa->dti_urb) { usb_kill_urb(wa->dti_urb); usb_put_urb(wa->dti_urb); - usb_kill_urb(wa->buf_in_urb); - usb_put_urb(wa->buf_in_urb); } kfree(wa->dti_buf); wa_nep_destroy(wa); diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index a2ef84b8397e..f2a8d29e17b9 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -125,7 +125,8 @@ struct wa_rpipe { enum wa_dti_state { WA_DTI_TRANSFER_RESULT_PENDING, - WA_DTI_ISOC_PACKET_STATUS_PENDING + WA_DTI_ISOC_PACKET_STATUS_PENDING, + WA_DTI_BUF_IN_DATA_PENDING }; enum wa_quirks { @@ -134,8 +135,20 @@ enum wa_quirks { * requests to be concatenated and not sent as separate packets. */ WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC = 0x01, + /* + * The Alereon HWA can be instructed to not send transfer notifications + * as an optimization. + */ + WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS = 0x02, }; +enum wa_vendor_specific_requests { + WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS = 0x4C, + WA_REQ_ALEREON_FEATURE_SET = 0x01, + WA_REQ_ALEREON_FEATURE_CLEAR = 0x00, +}; + +#define WA_MAX_BUF_IN_URBS 4 /** * Instance of a HWA Host Controller * @@ -206,7 +219,9 @@ struct wahc { u32 dti_isoc_xfer_in_progress; u8 dti_isoc_xfer_seg; struct urb *dti_urb; /* URB for reading xfer results */ - struct urb *buf_in_urb; /* URB for reading data in */ + /* URBs for reading data in */ + struct urb buf_in_urbs[WA_MAX_BUF_IN_URBS]; + int active_buf_in_urbs; /* number of buf_in_urbs active. */ struct edc dti_edc; /* DTI error density counter */ void *dti_buf; size_t dti_buf_size; @@ -234,6 +249,7 @@ struct wahc { extern int wa_create(struct wahc *wa, struct usb_interface *iface, kernel_ulong_t); extern void __wa_destroy(struct wahc *wa); +extern int wa_dti_start(struct wahc *wa); void wa_reset_all(struct wahc *wa); @@ -275,6 +291,8 @@ static inline void wa_rpipe_init(struct wahc *wa) static inline void wa_init(struct wahc *wa) { + int index; + edc_init(&wa->nep_edc); atomic_set(&wa->notifs_queued, 0); wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; @@ -288,6 +306,10 @@ static inline void wa_init(struct wahc *wa) INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run); wa->dto_in_use = 0; atomic_set(&wa->xfer_id_count, 1); + /* init the buf in URBs */ + for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index) + usb_init_urb(&(wa->buf_in_urbs[index])); + wa->active_buf_in_urbs = 0; } /** diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index 6ca80a4efc1b..6d6da127f6de 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -298,7 +298,7 @@ static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find( break; } itr += hdr->bLength; - itr_size -= hdr->bDescriptorType; + itr_size -= hdr->bLength; } out: return epcd; diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 3cd96e936d77..c8e2a47d62a7 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -167,6 +167,8 @@ struct wa_xfer { static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer, struct wa_seg *seg, int curr_iso_frame); +static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, + int starting_index, enum wa_seg_status status); static inline void wa_xfer_init(struct wa_xfer *xfer) { @@ -367,13 +369,13 @@ static unsigned __wa_xfer_is_done(struct wa_xfer *xfer) break; case WA_SEG_ERROR: xfer->result = seg->result; - dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zu(0x%08zX)\n", + dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zi(0x%08zX)\n", xfer, wa_xfer_id(xfer), seg->index, seg->result, seg->result); goto out; case WA_SEG_ABORTED: xfer->result = seg->result; - dev_dbg(dev, "xfer %p ID %08X#%u: ABORTED result %zu(0x%08zX)\n", + dev_dbg(dev, "xfer %p ID %08X#%u: ABORTED result %zi(0x%08zX)\n", xfer, wa_xfer_id(xfer), seg->index, seg->result, seg->result); goto out; @@ -390,6 +392,24 @@ out: } /* + * Mark the given segment as done. Return true if this completes the xfer. + * This should only be called for segs that have been submitted to an RPIPE. + * Delayed segs are not marked as submitted so they do not need to be marked + * as done when cleaning up. + * + * xfer->lock has to be locked + */ +static unsigned __wa_xfer_mark_seg_as_done(struct wa_xfer *xfer, + struct wa_seg *seg, enum wa_seg_status status) +{ + seg->status = status; + xfer->segs_done++; + + /* check for done. */ + return __wa_xfer_is_done(xfer); +} + +/* * Search for a transfer list ID on the HCD's URB list * * For 32 bit architectures, we use the pointer itself; for 64 bits, a @@ -416,12 +436,51 @@ out: struct wa_xfer_abort_buffer { struct urb urb; + struct wahc *wa; struct wa_xfer_abort cmd; }; static void __wa_xfer_abort_cb(struct urb *urb) { struct wa_xfer_abort_buffer *b = urb->context; + struct wahc *wa = b->wa; + + /* + * If the abort request URB failed, then the HWA did not get the abort + * command. Forcibly clean up the xfer without waiting for a Transfer + * Result from the HWA. + */ + if (urb->status < 0) { + struct wa_xfer *xfer; + struct device *dev = &wa->usb_iface->dev; + + xfer = wa_xfer_get_by_id(wa, le32_to_cpu(b->cmd.dwTransferID)); + dev_err(dev, "%s: Transfer Abort request failed. result: %d\n", + __func__, urb->status); + if (xfer) { + unsigned long flags; + int done; + struct wa_rpipe *rpipe = xfer->ep->hcpriv; + + dev_err(dev, "%s: cleaning up xfer %p ID 0x%08X.\n", + __func__, xfer, wa_xfer_id(xfer)); + spin_lock_irqsave(&xfer->lock, flags); + /* mark all segs as aborted. */ + wa_complete_remaining_xfer_segs(xfer, 0, + WA_SEG_ABORTED); + done = __wa_xfer_is_done(xfer); + spin_unlock_irqrestore(&xfer->lock, flags); + if (done) + wa_xfer_completion(xfer); + wa_xfer_delayed_run(rpipe); + wa_xfer_put(xfer); + } else { + dev_err(dev, "%s: xfer ID 0x%08X already gone.\n", + __func__, le32_to_cpu(b->cmd.dwTransferID)); + } + } + + wa_put(wa); /* taken in __wa_xfer_abort */ usb_put_urb(&b->urb); } @@ -449,6 +508,7 @@ static int __wa_xfer_abort(struct wa_xfer *xfer) b->cmd.bRequestType = WA_XFER_ABORT; b->cmd.wRPipe = rpipe->descr.wRPipeIndex; b->cmd.dwTransferID = wa_xfer_id_le32(xfer); + b->wa = wa_get(xfer->wa); usb_init_urb(&b->urb); usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev, @@ -462,6 +522,7 @@ static int __wa_xfer_abort(struct wa_xfer *xfer) error_submit: + wa_put(xfer->wa); if (printk_ratelimit()) dev_err(dev, "xfer %p: Can't submit abort request: %d\n", xfer, result); @@ -733,6 +794,8 @@ static void wa_seg_dto_cb(struct urb *urb) seg->isoc_frame_offset + seg->isoc_frame_index); /* resubmit the URB with the next isoc frame. */ + /* take a ref on resubmit. */ + wa_xfer_get(xfer); result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); if (result < 0) { dev_err(dev, "xfer 0x%08X#%u: DTO submit failed: %d\n", @@ -760,9 +823,13 @@ static void wa_seg_dto_cb(struct urb *urb) goto error_default; } + /* taken when this URB was submitted. */ + wa_xfer_put(xfer); return; error_dto_submit: + /* taken on resubmit attempt. */ + wa_xfer_put(xfer); error_default: spin_lock_irqsave(&xfer->lock, flags); rpipe = xfer->ep->hcpriv; @@ -772,12 +839,10 @@ error_default: wa_reset_all(wa); } if (seg->status != WA_SEG_ERROR) { - seg->status = WA_SEG_ERROR; seg->result = urb->status; - xfer->segs_done++; __wa_xfer_abort(xfer); rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR); } spin_unlock_irqrestore(&xfer->lock, flags); if (holding_dto) { @@ -788,7 +853,8 @@ error_default: wa_xfer_completion(xfer); if (rpipe_ready) wa_xfer_delayed_run(rpipe); - + /* taken when this URB was submitted. */ + wa_xfer_put(xfer); } /* @@ -842,12 +908,11 @@ static void wa_seg_iso_pack_desc_cb(struct urb *urb) } if (seg->status != WA_SEG_ERROR) { usb_unlink_urb(seg->dto_urb); - seg->status = WA_SEG_ERROR; seg->result = urb->status; - xfer->segs_done++; __wa_xfer_abort(xfer); rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, + WA_SEG_ERROR); } spin_unlock_irqrestore(&xfer->lock, flags); if (done) @@ -855,6 +920,8 @@ static void wa_seg_iso_pack_desc_cb(struct urb *urb) if (rpipe_ready) wa_xfer_delayed_run(rpipe); } + /* taken when this URB was submitted. */ + wa_xfer_put(xfer); } /* @@ -919,18 +986,18 @@ static void wa_seg_tr_cb(struct urb *urb) } usb_unlink_urb(seg->isoc_pack_desc_urb); usb_unlink_urb(seg->dto_urb); - seg->status = WA_SEG_ERROR; seg->result = urb->status; - xfer->segs_done++; __wa_xfer_abort(xfer); rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR); spin_unlock_irqrestore(&xfer->lock, flags); if (done) wa_xfer_completion(xfer); if (rpipe_ready) wa_xfer_delayed_run(rpipe); } + /* taken when this URB was submitted. */ + wa_xfer_put(xfer); } /* @@ -940,7 +1007,7 @@ static void wa_seg_tr_cb(struct urb *urb) */ static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg, const unsigned int bytes_transferred, - const unsigned int bytes_to_transfer, unsigned int *out_num_sgs) + const unsigned int bytes_to_transfer, int *out_num_sgs) { struct scatterlist *out_sg; unsigned int bytes_processed = 0, offset_into_current_page_data = 0, @@ -1094,14 +1161,13 @@ static int __wa_populate_dto_urb(struct wa_xfer *xfer, */ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) { - int result, cnt, iso_frame_offset; + int result, cnt, isoc_frame_offset = 0; size_t alloc_size = sizeof(*xfer->seg[0]) - sizeof(xfer->seg[0]->xfer_hdr) + xfer_hdr_size; struct usb_device *usb_dev = xfer->wa->usb_dev; const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd; struct wa_seg *seg; size_t buf_itr, buf_size, buf_itr_size; - int isoc_frame_offset = 0; result = -ENOMEM; xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC); @@ -1109,7 +1175,6 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) goto error_segs_kzalloc; buf_itr = 0; buf_size = xfer->urb->transfer_buffer_length; - iso_frame_offset = 0; for (cnt = 0; cnt < xfer->segs; cnt++) { size_t iso_pkt_descr_size = 0; int seg_isoc_frame_count = 0, seg_isoc_size = 0; @@ -1318,30 +1383,41 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, /* default to done unless we encounter a multi-frame isoc segment. */ *dto_done = 1; + /* + * Take a ref for each segment urb so the xfer cannot disappear until + * all of the callbacks run. + */ + wa_xfer_get(xfer); /* submit the transfer request. */ + seg->status = WA_SEG_SUBMITTED; result = usb_submit_urb(&seg->tr_urb, GFP_ATOMIC); if (result < 0) { pr_err("%s: xfer %p#%u: REQ submit failed: %d\n", __func__, xfer, seg->index, result); - goto error_seg_submit; + wa_xfer_put(xfer); + goto error_tr_submit; } /* submit the isoc packet descriptor if present. */ if (seg->isoc_pack_desc_urb) { + wa_xfer_get(xfer); result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC); seg->isoc_frame_index = 0; if (result < 0) { pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n", __func__, xfer, seg->index, result); + wa_xfer_put(xfer); goto error_iso_pack_desc_submit; } } /* submit the out data if this is an out request. */ if (seg->dto_urb) { struct wahc *wa = xfer->wa; + wa_xfer_get(xfer); result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); if (result < 0) { pr_err("%s: xfer %p#%u: DTO submit failed: %d\n", __func__, xfer, seg->index, result); + wa_xfer_put(xfer); goto error_dto_submit; } /* @@ -1353,7 +1429,6 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, && (seg->isoc_frame_count > 1)) *dto_done = 0; } - seg->status = WA_SEG_SUBMITTED; rpipe_avail_dec(rpipe); return 0; @@ -1361,7 +1436,7 @@ error_dto_submit: usb_unlink_urb(seg->isoc_pack_desc_urb); error_iso_pack_desc_submit: usb_unlink_urb(&seg->tr_urb); -error_seg_submit: +error_tr_submit: seg->status = WA_SEG_ERROR; seg->result = result; *dto_done = 1; @@ -1393,6 +1468,12 @@ static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting) list_node); list_del(&seg->list_node); xfer = seg->xfer; + /* + * Get a reference to the xfer in case the callbacks for the + * URBs submitted by __wa_seg_submit attempt to complete + * the xfer before this function completes. + */ + wa_xfer_get(xfer); result = __wa_seg_submit(rpipe, xfer, seg, &dto_done); /* release the dto resource if this RPIPE is done with it. */ if (dto_done) @@ -1401,13 +1482,23 @@ static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting) xfer, wa_xfer_id(xfer), seg->index, atomic_read(&rpipe->segs_available), result); if (unlikely(result < 0)) { + int done; + spin_unlock_irqrestore(&rpipe->seg_lock, flags); spin_lock_irqsave(&xfer->lock, flags); __wa_xfer_abort(xfer); + /* + * This seg was marked as submitted when it was put on + * the RPIPE seg_list. Mark it done. + */ xfer->segs_done++; + done = __wa_xfer_is_done(xfer); spin_unlock_irqrestore(&xfer->lock, flags); + if (done) + wa_xfer_completion(xfer); spin_lock_irqsave(&rpipe->seg_lock, flags); } + wa_xfer_put(xfer); } /* * Mark this RPIPE as waiting if dto was not acquired, there are @@ -1592,12 +1683,19 @@ static int wa_urb_enqueue_b(struct wa_xfer *xfer) dev_err(&(urb->dev->dev), "%s: error_xfer_setup\n", __func__); goto error_xfer_setup; } + /* + * Get a xfer reference since __wa_xfer_submit starts asynchronous + * operations that may try to complete the xfer before this function + * exits. + */ + wa_xfer_get(xfer); result = __wa_xfer_submit(xfer); if (result < 0) { dev_err(&(urb->dev->dev), "%s: error_xfer_submit\n", __func__); goto error_xfer_submit; } spin_unlock_irqrestore(&xfer->lock, flags); + wa_xfer_put(xfer); return 0; /* @@ -1623,6 +1721,7 @@ error_xfer_submit: spin_unlock_irqrestore(&xfer->lock, flags); if (done) wa_xfer_completion(xfer); + wa_xfer_put(xfer); /* return success since the completion routine will run. */ return 0; } @@ -1832,20 +1931,20 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) /* check if it is safe to unlink. */ spin_lock_irqsave(&wa->xfer_list_lock, flags); result = usb_hcd_check_unlink_urb(&(wa->wusb->usb_hcd), urb, status); + if ((result == 0) && urb->hcpriv) { + /* + * Get a xfer ref to prevent a race with wa_xfer_giveback + * cleaning up the xfer while we are working with it. + */ + wa_xfer_get(urb->hcpriv); + } spin_unlock_irqrestore(&wa->xfer_list_lock, flags); if (result) return result; xfer = urb->hcpriv; - if (xfer == NULL) { - /* - * Nothing setup yet enqueue will see urb->status != - * -EINPROGRESS (by hcd layer) and bail out with - * error, no need to do completion - */ - BUG_ON(urb->status == -EINPROGRESS); - goto out; - } + if (xfer == NULL) + return -ENOENT; spin_lock_irqsave(&xfer->lock, flags); pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer)); rpipe = xfer->ep->hcpriv; @@ -1856,6 +1955,16 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) result = -ENOENT; goto out_unlock; } + /* + * Check for done to avoid racing with wa_xfer_giveback and completing + * twice. + */ + if (__wa_xfer_is_done(xfer)) { + pr_debug("%s: xfer %p id 0x%08X already done.\n", __func__, + xfer, wa_xfer_id(xfer)); + result = -ENOENT; + goto out_unlock; + } /* Check the delayed list -> if there, release and complete */ spin_lock_irqsave(&wa->xfer_list_lock, flags2); if (!list_empty(&xfer->list_node) && xfer->seg == NULL) @@ -1865,6 +1974,11 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) goto out_unlock; /* setup(), enqueue_b() completes */ /* Ok, the xfer is in flight already, it's been setup and submitted.*/ xfer_abort_pending = __wa_xfer_abort(xfer) >= 0; + /* + * grab the rpipe->seg_lock here to prevent racing with + * __wa_xfer_delayed_run. + */ + spin_lock(&rpipe->seg_lock); for (cnt = 0; cnt < xfer->segs; cnt++) { seg = xfer->seg[cnt]; pr_debug("%s: xfer id 0x%08X#%d status = %d\n", @@ -1885,16 +1999,24 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) */ seg->status = WA_SEG_ABORTED; seg->result = -ENOENT; - spin_lock_irqsave(&rpipe->seg_lock, flags2); list_del(&seg->list_node); xfer->segs_done++; - spin_unlock_irqrestore(&rpipe->seg_lock, flags2); break; case WA_SEG_DONE: case WA_SEG_ERROR: case WA_SEG_ABORTED: break; /* + * The buf_in data for a segment in the + * WA_SEG_DTI_PENDING state is actively being read. + * Let wa_buf_in_cb handle it since it will be called + * and will increment xfer->segs_done. Cleaning up + * here could cause wa_buf_in_cb to access the xfer + * after it has been completed/freed. + */ + case WA_SEG_DTI_PENDING: + break; + /* * In the states below, the HWA device already knows * about the transfer. If an abort request was sent, * allow the HWA to process it and wait for the @@ -1903,7 +2025,6 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) */ case WA_SEG_SUBMITTED: case WA_SEG_PENDING: - case WA_SEG_DTI_PENDING: /* * Check if the abort was successfully sent. This could * be false if the HWA has been removed but we haven't @@ -1917,6 +2038,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) break; } } + spin_unlock(&rpipe->seg_lock); xfer->result = urb->status; /* -ENOENT or -ECONNRESET */ done = __wa_xfer_is_done(xfer); spin_unlock_irqrestore(&xfer->lock, flags); @@ -1924,11 +2046,12 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) wa_xfer_completion(xfer); if (rpipe_ready) wa_xfer_delayed_run(rpipe); + wa_xfer_put(xfer); return result; out_unlock: spin_unlock_irqrestore(&xfer->lock, flags); -out: + wa_xfer_put(xfer); return result; dequeue_delayed: @@ -1937,6 +2060,7 @@ dequeue_delayed: xfer->result = urb->status; spin_unlock_irqrestore(&xfer->lock, flags); wa_xfer_giveback(xfer); + wa_xfer_put(xfer); usb_put_urb(urb); /* we got a ref in enqueue() */ return 0; } @@ -1996,15 +2120,17 @@ static int wa_xfer_status_to_errno(u8 status) * no other segment transfer results will be returned from the device. * Mark the remaining submitted or pending xfers as completed so that * the xfer will complete cleanly. + * + * xfer->lock must be held + * */ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, - struct wa_seg *incoming_seg, enum wa_seg_status status) + int starting_index, enum wa_seg_status status) { int index; struct wa_rpipe *rpipe = xfer->ep->hcpriv; - for (index = incoming_seg->index + 1; index < xfer->segs_submitted; - index++) { + for (index = starting_index; index < xfer->segs_submitted; index++) { struct wa_seg *current_seg = xfer->seg[index]; BUG_ON(current_seg == NULL); @@ -2033,73 +2159,110 @@ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, } } -/* Populate the wa->buf_in_urb based on the current isoc transfer state. */ -static void __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer, - struct wa_seg *seg, int curr_iso_frame) +/* Populate the given urb based on the current isoc transfer state. */ +static int __wa_populate_buf_in_urb_isoc(struct wahc *wa, + struct urb *buf_in_urb, struct wa_xfer *xfer, struct wa_seg *seg) { - BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); + int urb_start_frame = seg->isoc_frame_index + seg->isoc_frame_offset; + int seg_index, total_len = 0, urb_frame_index = urb_start_frame; + struct usb_iso_packet_descriptor *iso_frame_desc = + xfer->urb->iso_frame_desc; + const int dti_packet_size = usb_endpoint_maxp(wa->dti_epd); + int next_frame_contiguous; + struct usb_iso_packet_descriptor *iso_frame; + + BUG_ON(buf_in_urb->status == -EINPROGRESS); + + /* + * If the current frame actual_length is contiguous with the next frame + * and actual_length is a multiple of the DTI endpoint max packet size, + * combine the current frame with the next frame in a single URB. This + * reduces the number of URBs that must be submitted in that case. + */ + seg_index = seg->isoc_frame_index; + do { + next_frame_contiguous = 0; + + iso_frame = &iso_frame_desc[urb_frame_index]; + total_len += iso_frame->actual_length; + ++urb_frame_index; + ++seg_index; + + if (seg_index < seg->isoc_frame_count) { + struct usb_iso_packet_descriptor *next_iso_frame; + + next_iso_frame = &iso_frame_desc[urb_frame_index]; + + if ((iso_frame->offset + iso_frame->actual_length) == + next_iso_frame->offset) + next_frame_contiguous = 1; + } + } while (next_frame_contiguous + && ((iso_frame->actual_length % dti_packet_size) == 0)); /* this should always be 0 before a resubmit. */ - wa->buf_in_urb->num_mapped_sgs = 0; - wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma + - xfer->urb->iso_frame_desc[curr_iso_frame].offset; - wa->buf_in_urb->transfer_buffer_length = - xfer->urb->iso_frame_desc[curr_iso_frame].length; - wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - wa->buf_in_urb->transfer_buffer = NULL; - wa->buf_in_urb->sg = NULL; - wa->buf_in_urb->num_sgs = 0; - wa->buf_in_urb->context = seg; + buf_in_urb->num_mapped_sgs = 0; + buf_in_urb->transfer_dma = xfer->urb->transfer_dma + + iso_frame_desc[urb_start_frame].offset; + buf_in_urb->transfer_buffer_length = total_len; + buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + buf_in_urb->transfer_buffer = NULL; + buf_in_urb->sg = NULL; + buf_in_urb->num_sgs = 0; + buf_in_urb->context = seg; + + /* return the number of frames included in this URB. */ + return seg_index - seg->isoc_frame_index; } -/* Populate the wa->buf_in_urb based on the current transfer state. */ -static int wa_populate_buf_in_urb(struct wahc *wa, struct wa_xfer *xfer, +/* Populate the given urb based on the current transfer state. */ +static int wa_populate_buf_in_urb(struct urb *buf_in_urb, struct wa_xfer *xfer, unsigned int seg_idx, unsigned int bytes_transferred) { int result = 0; struct wa_seg *seg = xfer->seg[seg_idx]; - BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); + BUG_ON(buf_in_urb->status == -EINPROGRESS); /* this should always be 0 before a resubmit. */ - wa->buf_in_urb->num_mapped_sgs = 0; + buf_in_urb->num_mapped_sgs = 0; if (xfer->is_dma) { - wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma + buf_in_urb->transfer_dma = xfer->urb->transfer_dma + (seg_idx * xfer->seg_size); - wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - wa->buf_in_urb->transfer_buffer = NULL; - wa->buf_in_urb->sg = NULL; - wa->buf_in_urb->num_sgs = 0; + buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + buf_in_urb->transfer_buffer = NULL; + buf_in_urb->sg = NULL; + buf_in_urb->num_sgs = 0; } else { /* do buffer or SG processing. */ - wa->buf_in_urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP; + buf_in_urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP; if (xfer->urb->transfer_buffer) { - wa->buf_in_urb->transfer_buffer = + buf_in_urb->transfer_buffer = xfer->urb->transfer_buffer + (seg_idx * xfer->seg_size); - wa->buf_in_urb->sg = NULL; - wa->buf_in_urb->num_sgs = 0; + buf_in_urb->sg = NULL; + buf_in_urb->num_sgs = 0; } else { /* allocate an SG list to store seg_size bytes and copy the subset of the xfer->urb->sg that matches the buffer subset we are about to read. */ - wa->buf_in_urb->sg = wa_xfer_create_subset_sg( + buf_in_urb->sg = wa_xfer_create_subset_sg( xfer->urb->sg, seg_idx * xfer->seg_size, bytes_transferred, - &(wa->buf_in_urb->num_sgs)); + &(buf_in_urb->num_sgs)); - if (!(wa->buf_in_urb->sg)) { - wa->buf_in_urb->num_sgs = 0; + if (!(buf_in_urb->sg)) { + buf_in_urb->num_sgs = 0; result = -ENOMEM; } - wa->buf_in_urb->transfer_buffer = NULL; + buf_in_urb->transfer_buffer = NULL; } } - wa->buf_in_urb->transfer_buffer_length = bytes_transferred; - wa->buf_in_urb->context = seg; + buf_in_urb->transfer_buffer_length = bytes_transferred; + buf_in_urb->context = seg; return result; } @@ -2124,6 +2287,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, u8 usb_status; unsigned rpipe_ready = 0; unsigned bytes_transferred = le32_to_cpu(xfer_result->dwTransferLength); + struct urb *buf_in_urb = &(wa->buf_in_urbs[0]); spin_lock_irqsave(&xfer->lock, flags); seg_idx = xfer_result->bTransferSegment & 0x7f; @@ -2147,7 +2311,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, } if (usb_status & 0x80) { seg->result = wa_xfer_status_to_errno(usb_status); - dev_err(dev, "DTI: xfer %p#:%08X:%u failed (0x%02x)\n", + dev_err(dev, "DTI: xfer %p 0x%08X:#%u failed (0x%02x)\n", xfer, xfer->id, seg->index, usb_status); seg->status = ((usb_status & 0x7F) == WA_XFER_STATUS_ABORTED) ? WA_SEG_ABORTED : WA_SEG_ERROR; @@ -2162,7 +2326,8 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, * transfers with data or below for no data, the xfer will complete. */ if (xfer_result->bTransferSegment & 0x80) - wa_complete_remaining_xfer_segs(xfer, seg, WA_SEG_DONE); + wa_complete_remaining_xfer_segs(xfer, seg->index + 1, + WA_SEG_DONE); if (usb_pipeisoc(xfer->urb->pipe) && (le32_to_cpu(xfer_result->dwNumOfPackets) > 0)) { /* set up WA state to read the isoc packet status next. */ @@ -2173,20 +2338,21 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, && (bytes_transferred > 0)) { /* IN data phase: read to buffer */ seg->status = WA_SEG_DTI_PENDING; - result = wa_populate_buf_in_urb(wa, xfer, seg_idx, + result = wa_populate_buf_in_urb(buf_in_urb, xfer, seg_idx, bytes_transferred); if (result < 0) goto error_buf_in_populate; - result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); - if (result < 0) + ++(wa->active_buf_in_urbs); + result = usb_submit_urb(buf_in_urb, GFP_ATOMIC); + if (result < 0) { + --(wa->active_buf_in_urbs); goto error_submit_buf_in; + } } else { /* OUT data phase or no data, complete it -- */ - seg->status = WA_SEG_DONE; seg->result = bytes_transferred; - xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE); } spin_unlock_irqrestore(&xfer->lock, flags); if (done) @@ -2205,15 +2371,15 @@ error_submit_buf_in: dev_err(dev, "xfer %p#%u: can't submit DTI data phase: %d\n", xfer, seg_idx, result); seg->result = result; - kfree(wa->buf_in_urb->sg); - wa->buf_in_urb->sg = NULL; + kfree(buf_in_urb->sg); + buf_in_urb->sg = NULL; error_buf_in_populate: __wa_xfer_abort(xfer); seg->status = WA_SEG_ERROR; error_complete: xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - wa_complete_remaining_xfer_segs(xfer, seg, seg->status); + wa_complete_remaining_xfer_segs(xfer, seg->index + 1, seg->status); done = __wa_xfer_is_done(xfer); /* * queue work item to clear STALL for control endpoints. @@ -2315,16 +2481,16 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) for (seg_index = 0; seg_index < seg->isoc_frame_count; ++seg_index) { struct usb_iso_packet_descriptor *iso_frame_desc = xfer->urb->iso_frame_desc; - const int urb_frame_index = + const int xfer_frame_index = seg->isoc_frame_offset + seg_index; - iso_frame_desc[urb_frame_index].status = + iso_frame_desc[xfer_frame_index].status = wa_xfer_status_to_errno( le16_to_cpu(status_array[seg_index].PacketStatus)); - iso_frame_desc[urb_frame_index].actual_length = + iso_frame_desc[xfer_frame_index].actual_length = le16_to_cpu(status_array[seg_index].PacketLength); /* track the number of frames successfully transferred. */ - if (iso_frame_desc[urb_frame_index].actual_length > 0) { + if (iso_frame_desc[xfer_frame_index].actual_length > 0) { /* save the starting frame index for buf_in_urb. */ if (!data_frame_count) first_frame_index = seg_index; @@ -2333,30 +2499,64 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) } if (xfer->is_inbound && data_frame_count) { - int result; + int result, total_frames_read = 0, urb_index = 0; + struct urb *buf_in_urb; + /* IN data phase: read to buffer */ + seg->status = WA_SEG_DTI_PENDING; + + /* start with the first frame with data. */ seg->isoc_frame_index = first_frame_index; - /* submit a read URB for the first frame with data. */ - __wa_populate_buf_in_urb_isoc(wa, xfer, seg, - seg->isoc_frame_index + seg->isoc_frame_offset); + /* submit up to WA_MAX_BUF_IN_URBS read URBs. */ + do { + int urb_frame_index, urb_frame_count; + struct usb_iso_packet_descriptor *iso_frame_desc; + + buf_in_urb = &(wa->buf_in_urbs[urb_index]); + urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, + buf_in_urb, xfer, seg); + /* advance frame index to start of next read URB. */ + seg->isoc_frame_index += urb_frame_count; + total_frames_read += urb_frame_count; + + ++(wa->active_buf_in_urbs); + result = usb_submit_urb(buf_in_urb, GFP_ATOMIC); + + /* skip 0-byte frames. */ + urb_frame_index = + seg->isoc_frame_offset + seg->isoc_frame_index; + iso_frame_desc = + &(xfer->urb->iso_frame_desc[urb_frame_index]); + while ((seg->isoc_frame_index < + seg->isoc_frame_count) && + (iso_frame_desc->actual_length == 0)) { + ++(seg->isoc_frame_index); + ++iso_frame_desc; + } + ++urb_index; + + } while ((result == 0) && (urb_index < WA_MAX_BUF_IN_URBS) + && (seg->isoc_frame_index < + seg->isoc_frame_count)); - result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); if (result < 0) { + --(wa->active_buf_in_urbs); dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", result); wa_reset_all(wa); - } else if (data_frame_count > 1) - /* If we need to read multiple frames, set DTI busy. */ + } else if (data_frame_count > total_frames_read) + /* If we need to read more frames, set DTI busy. */ dti_busy = 1; } else { /* OUT transfer or no more IN data, complete it -- */ - seg->status = WA_SEG_DONE; - xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE); } spin_unlock_irqrestore(&xfer->lock, flags); - wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; + if (dti_busy) + wa->dti_state = WA_DTI_BUF_IN_DATA_PENDING; + else + wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; if (done) wa_xfer_completion(xfer); if (rpipe_ready) @@ -2388,8 +2588,9 @@ static void wa_buf_in_cb(struct urb *urb) struct wahc *wa; struct device *dev; struct wa_rpipe *rpipe; - unsigned rpipe_ready = 0, seg_index, isoc_data_frame_count = 0; + unsigned rpipe_ready = 0, isoc_data_frame_count = 0; unsigned long flags; + int resubmit_dti = 0, active_buf_in_urbs; u8 done = 0; /* free the sg if it was used. */ @@ -2399,19 +2600,20 @@ static void wa_buf_in_cb(struct urb *urb) spin_lock_irqsave(&xfer->lock, flags); wa = xfer->wa; dev = &wa->usb_iface->dev; + --(wa->active_buf_in_urbs); + active_buf_in_urbs = wa->active_buf_in_urbs; if (usb_pipeisoc(xfer->urb->pipe)) { + struct usb_iso_packet_descriptor *iso_frame_desc = + xfer->urb->iso_frame_desc; + int seg_index; + /* - * Find the next isoc frame with data. Bail out after - * isoc_data_frame_count > 1 since there is no need to walk - * the entire frame array. We just need to know if - * isoc_data_frame_count is 0, 1, or >1. + * Find the next isoc frame with data and count how many + * frames with data remain. */ - seg_index = seg->isoc_frame_index + 1; - while ((seg_index < seg->isoc_frame_count) - && (isoc_data_frame_count <= 1)) { - struct usb_iso_packet_descriptor *iso_frame_desc = - xfer->urb->iso_frame_desc; + seg_index = seg->isoc_frame_index; + while (seg_index < seg->isoc_frame_count) { const int urb_frame_index = seg->isoc_frame_offset + seg_index; @@ -2432,24 +2634,39 @@ static void wa_buf_in_cb(struct urb *urb) seg->result += urb->actual_length; if (isoc_data_frame_count > 0) { - int result; - /* submit a read URB for the first frame with data. */ - __wa_populate_buf_in_urb_isoc(wa, xfer, seg, - seg->isoc_frame_index + seg->isoc_frame_offset); - result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); + int result, urb_frame_count; + + /* submit a read URB for the next frame with data. */ + urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, urb, + xfer, seg); + /* advance index to start of next read URB. */ + seg->isoc_frame_index += urb_frame_count; + ++(wa->active_buf_in_urbs); + result = usb_submit_urb(urb, GFP_ATOMIC); if (result < 0) { + --(wa->active_buf_in_urbs); dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", result); wa_reset_all(wa); } - } else { + /* + * If we are in this callback and + * isoc_data_frame_count > 0, it means that the dti_urb + * submission was delayed in wa_dti_cb. Once + * we submit the last buf_in_urb, we can submit the + * delayed dti_urb. + */ + resubmit_dti = (isoc_data_frame_count == + urb_frame_count); + } else if (active_buf_in_urbs == 0) { rpipe = xfer->ep->hcpriv; - seg->status = WA_SEG_DONE; - dev_dbg(dev, "xfer %p#%u: data in done (%zu bytes)\n", - xfer, seg->index, seg->result); - xfer->segs_done++; + dev_dbg(dev, + "xfer %p 0x%08X#%u: data in done (%zu bytes)\n", + xfer, wa_xfer_id(xfer), seg->index, + seg->result); rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, + WA_SEG_DONE); } spin_unlock_irqrestore(&xfer->lock, flags); if (done) @@ -2461,37 +2678,44 @@ static void wa_buf_in_cb(struct urb *urb) case -ENOENT: /* as it was done by the who unlinked us */ break; default: /* Other errors ... */ + /* + * Error on data buf read. Only resubmit DTI if it hasn't + * already been done by previously hitting this error or by a + * successful completion of the previous buf_in_urb. + */ + resubmit_dti = wa->dti_state != WA_DTI_TRANSFER_RESULT_PENDING; spin_lock_irqsave(&xfer->lock, flags); rpipe = xfer->ep->hcpriv; if (printk_ratelimit()) - dev_err(dev, "xfer %p#%u: data in error %d\n", - xfer, seg->index, urb->status); + dev_err(dev, "xfer %p 0x%08X#%u: data in error %d\n", + xfer, wa_xfer_id(xfer), seg->index, + urb->status); if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)){ dev_err(dev, "DTO: URB max acceptable errors " "exceeded, resetting device\n"); wa_reset_all(wa); } - seg->status = WA_SEG_ERROR; seg->result = urb->status; - xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - __wa_xfer_abort(xfer); - done = __wa_xfer_is_done(xfer); + if (active_buf_in_urbs == 0) + done = __wa_xfer_mark_seg_as_done(xfer, seg, + WA_SEG_ERROR); + else + __wa_xfer_abort(xfer); spin_unlock_irqrestore(&xfer->lock, flags); if (done) wa_xfer_completion(xfer); if (rpipe_ready) wa_xfer_delayed_run(rpipe); } - /* - * If we are in this callback and isoc_data_frame_count > 0, it means - * that the dti_urb submission was delayed in wa_dti_cb. Once - * isoc_data_frame_count gets to 1, we can submit the deferred URB - * since the last buf_in_urb was just submitted. - */ - if (isoc_data_frame_count == 1) { - int result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); + + if (resubmit_dti) { + int result; + + wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; + + result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); if (result < 0) { dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n", result); @@ -2561,11 +2785,15 @@ static void wa_dti_cb(struct urb *urb) xfer_result->hdr.bNotifyType); break; } + xfer_id = le32_to_cpu(xfer_result->dwTransferID); usb_status = xfer_result->bTransferStatus & 0x3f; - if (usb_status == WA_XFER_STATUS_NOT_FOUND) + if (usb_status == WA_XFER_STATUS_NOT_FOUND) { /* taken care of already */ + dev_dbg(dev, "%s: xfer 0x%08X#%u not found.\n", + __func__, xfer_id, + xfer_result->bTransferSegment & 0x7f); break; - xfer_id = le32_to_cpu(xfer_result->dwTransferID); + } xfer = wa_xfer_get_by_id(wa, xfer_id); if (xfer == NULL) { /* FIXME: transaction not found. */ @@ -2614,6 +2842,54 @@ out: } /* + * Initialize the DTI URB for reading transfer result notifications and also + * the buffer-in URB, for reading buffers. Then we just submit the DTI URB. + */ +int wa_dti_start(struct wahc *wa) +{ + const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; + struct device *dev = &wa->usb_iface->dev; + int result = -ENOMEM, index; + + if (wa->dti_urb != NULL) /* DTI URB already started */ + goto out; + + wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL); + if (wa->dti_urb == NULL) { + dev_err(dev, "Can't allocate DTI URB\n"); + goto error_dti_urb_alloc; + } + usb_fill_bulk_urb( + wa->dti_urb, wa->usb_dev, + usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress), + wa->dti_buf, wa->dti_buf_size, + wa_dti_cb, wa); + + /* init the buf in URBs */ + for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index) { + usb_fill_bulk_urb( + &(wa->buf_in_urbs[index]), wa->usb_dev, + usb_rcvbulkpipe(wa->usb_dev, + 0x80 | dti_epd->bEndpointAddress), + NULL, 0, wa_buf_in_cb, wa); + } + result = usb_submit_urb(wa->dti_urb, GFP_KERNEL); + if (result < 0) { + dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n", + result); + goto error_dti_urb_submit; + } +out: + return 0; + +error_dti_urb_submit: + usb_put_urb(wa->dti_urb); + wa->dti_urb = NULL; +error_dti_urb_alloc: + return result; +} +EXPORT_SYMBOL_GPL(wa_dti_start); +/* * Transfer complete notification * * Called from the notif.c code. We get a notification on EP2 saying @@ -2627,15 +2903,10 @@ out: * Follow up in wa_dti_cb(), as that's where the whole state * machine starts. * - * So here we just initialize the DTI URB for reading transfer result - * notifications and also the buffer-in URB, for reading buffers. Then - * we just submit the DTI URB. - * * @wa shall be referenced */ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) { - int result; struct device *dev = &wa->usb_iface->dev; struct wa_notif_xfer *notif_xfer; const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; @@ -2649,45 +2920,13 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) notif_xfer->bEndpoint, dti_epd->bEndpointAddress); goto error; } - if (wa->dti_urb != NULL) /* DTI URB already started */ - goto out; - wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL); - if (wa->dti_urb == NULL) { - dev_err(dev, "Can't allocate DTI URB\n"); - goto error_dti_urb_alloc; - } - usb_fill_bulk_urb( - wa->dti_urb, wa->usb_dev, - usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint), - wa->dti_buf, wa->dti_buf_size, - wa_dti_cb, wa); + /* attempt to start the DTI ep processing. */ + if (wa_dti_start(wa) < 0) + goto error; - wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL); - if (wa->buf_in_urb == NULL) { - dev_err(dev, "Can't allocate BUF-IN URB\n"); - goto error_buf_in_urb_alloc; - } - usb_fill_bulk_urb( - wa->buf_in_urb, wa->usb_dev, - usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint), - NULL, 0, wa_buf_in_cb, wa); - result = usb_submit_urb(wa->dti_urb, GFP_KERNEL); - if (result < 0) { - dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n", - result); - goto error_dti_urb_submit; - } -out: return; -error_dti_urb_submit: - usb_put_urb(wa->buf_in_urb); - wa->buf_in_urb = NULL; -error_buf_in_urb_alloc: - usb_put_urb(wa->dti_urb); - wa->dti_urb = NULL; -error_dti_urb_alloc: error: wa_reset_all(wa); } |