diff options
Diffstat (limited to 'drivers/usb')
335 files changed, 22182 insertions, 10126 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index e0cad4418085..ae481c37a208 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -92,6 +92,8 @@ source "drivers/usb/storage/Kconfig" source "drivers/usb/image/Kconfig" +source "drivers/usb/usbip/Kconfig" + endif source "drivers/usb/musb/Kconfig" @@ -147,4 +149,14 @@ source "drivers/usb/phy/Kconfig" source "drivers/usb/gadget/Kconfig" +config USB_LED_TRIG + bool "USB LED Triggers" + depends on LEDS_CLASS && USB_COMMON && LEDS_TRIGGERS + help + This option adds LED triggers for USB host and/or gadget activity. + + Say Y here if you are working on a system with led-class supported + LEDs and you want to use them as activity indicators for USB host or + gadget. + endif # USB_SUPPORT diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 3cba892b83a2..d7be71778059 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -60,3 +60,5 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_GADGET) += gadget/ obj-$(CONFIG_USB_COMMON) += common/ + +obj-$(CONFIG_USBIP_CORE) += usbip/ diff --git a/drivers/usb/README b/drivers/usb/README index 284f46b3e1cc..2144e7dbfa41 100644 --- a/drivers/usb/README +++ b/drivers/usb/README @@ -24,7 +24,7 @@ Here is a list of what each subdirectory here is, and what is contained in them. core/ - This is for the core USB host code, including the - usbfs files and the hub class driver ("khubd"). + usbfs files and the hub class driver ("hub_wq"). host/ - This is for USB host controller drivers. This includes UHCI, OHCI, EHCI, and others that might diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 9563cb56d564..ea40626e0246 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -99,10 +99,10 @@ enum ci_role { /** * struct ci_role_driver - host/gadget role driver - * start: start this role - * stop: stop this role - * irq: irq handler for this role - * name: role name string (host/gadget) + * @start: start this role + * @stop: stop this role + * @irq: irq handler for this role + * @name: role name string (host/gadget) */ struct ci_role_driver { int (*start)(struct ci_hdrc *); @@ -245,6 +245,7 @@ static inline void ci_role_stop(struct ci_hdrc *ci) /** * hw_read: reads from a hw register + * @ci: the controller * @reg: register index * @mask: bitfield mask * @@ -277,6 +278,7 @@ static inline void __hw_write(struct ci_hdrc *ci, u32 val, /** * hw_write: writes to a hw register + * @ci: the controller * @reg: register index * @mask: bitfield mask * @data: new value @@ -293,6 +295,7 @@ static inline void hw_write(struct ci_hdrc *ci, enum ci_hw_regs reg, /** * hw_test_and_clear: tests & clears a hw register + * @ci: the controller * @reg: register index * @mask: bitfield mask * @@ -309,6 +312,7 @@ static inline u32 hw_test_and_clear(struct ci_hdrc *ci, enum ci_hw_regs reg, /** * hw_test_and_write: tests & writes a hw register + * @ci: the controller * @reg: register index * @mask: bitfield mask * @data: new value @@ -327,6 +331,8 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg, /** * ci_otg_is_fsm_mode: runtime check if otg controller * is in otg fsm mode. + * + * @ci: chipidea device */ static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci) { diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 2e58f8dfd311..a7ab0f15926e 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -54,6 +54,7 @@ struct ci_hdrc_imx_data { static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) { + struct platform_device *misc_pdev; struct device_node *np = dev->of_node; struct of_phandle_args args; struct imx_usbmisc_data *data; @@ -79,8 +80,15 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) } data->index = args.args[0]; + + misc_pdev = of_find_device_by_node(args.np); of_node_put(args.np); + if (!misc_pdev) + return ERR_PTR(-EPROBE_DEFER); + + data->dev = &misc_pdev->dev; + if (of_find_property(np, "disable-over-current", NULL)) data->disable_oc = 1; @@ -133,6 +141,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); if (IS_ERR(data->phy)) { ret = PTR_ERR(data->phy); + /* Return -EINVAL if no usbphy is available */ + if (ret == -ENODEV) + ret = -EINVAL; goto err_clk; } diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 996ec93467b2..4ed828f75a1e 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -13,6 +13,7 @@ #define __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H struct imx_usbmisc_data { + struct device *dev; int index; unsigned int disable_oc:1; /* over current detect disabled */ diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index d72b9d2de2c5..4935ac38fd00 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -20,13 +20,13 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event) { struct device *dev = ci->gadget.dev.parent; - int val; switch (event) { case CI_HDRC_CONTROLLER_RESET_EVENT: dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n"); writel(0, USB_AHBBURST); writel(0, USB_AHBMODE); + usb_phy_init(ci->transceiver); break; case CI_HDRC_CONTROLLER_STOPPED_EVENT: dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n"); @@ -34,10 +34,7 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event) * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. */ - val = usb_phy_io_read(ci->transceiver, ULPI_FUNC_CTRL); - val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - usb_phy_io_write(ci->transceiver, val, ULPI_FUNC_CTRL); + usb_phy_notify_disconnect(ci->transceiver, USB_SPEED_UNKNOWN); break; default: dev_dbg(dev, "unknown ci_hdrc event\n"); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 619d13e29995..3df5005c554d 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -139,6 +139,8 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) /** * hw_read_intr_enable: returns interrupt enable register * + * @ci: the controller + * * This function returns register data */ u32 hw_read_intr_enable(struct ci_hdrc *ci) @@ -149,6 +151,8 @@ u32 hw_read_intr_enable(struct ci_hdrc *ci) /** * hw_read_intr_status: returns interrupt status register * + * @ci: the controller + * * This function returns register data */ u32 hw_read_intr_status(struct ci_hdrc *ci) @@ -176,6 +180,8 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode) /** * hw_port_test_get: reads port test mode value * + * @ci: the controller + * * This function returns port test mode value */ u8 hw_port_test_get(struct ci_hdrc *ci) @@ -295,7 +301,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci) /** * ci_usb_phy_init: initialize phy according to different phy type * @ci: the controller - * + * * This function returns an error code if usb_phy_init has failed */ static int ci_usb_phy_init(struct ci_hdrc *ci) @@ -473,6 +479,10 @@ static int ci_get_platdata(struct device *dev, PTR_ERR(platdata->reg_vbus)); return PTR_ERR(platdata->reg_vbus); } + /* Get TPL support */ + if (!platdata->tpl_support) + platdata->tpl_support = + of_usb_host_tpl_support(dev->of_node); } if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) @@ -658,7 +668,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) goto deinit_phy; } - if (ci->is_otg) { + if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { /* Disable and clear all OTG irq */ hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, OTGSC_INT_STATUS_BITS); diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 7cccab6ff308..795d6538d630 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -208,7 +208,7 @@ static const struct file_operations ci_requests_fops = { .release = single_release, }; -int ci_otg_show(struct seq_file *s, void *unused) +static int ci_otg_show(struct seq_file *s, void *unused) { struct ci_hdrc *ci = s->private; struct otg_fsm *fsm; @@ -331,7 +331,7 @@ static const struct file_operations ci_role_fops = { .release = single_release, }; -int ci_registers_show(struct seq_file *s, void *unused) +static int ci_registers_show(struct seq_file *s, void *unused) { struct ci_hdrc *ci = s->private; u32 tmp_reg; diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index a93d950e9468..ebde7b6ce687 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -59,7 +59,8 @@ static int host_start(struct ci_hdrc *ci) hcd->has_tt = 1; hcd->power_budget = ci->platdata->power_budget; - hcd->phy = ci->transceiver; + hcd->usb_phy = ci->transceiver; + hcd->tpl_support = ci->platdata->tpl_support; ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index b8125aa64ad8..0444d3f8971a 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -627,7 +627,7 @@ __acquires(hwep->lock) if (hwreq->req.complete != NULL) { spin_unlock(hwep->lock); - hwreq->req.complete(&hwep->ep, &hwreq->req); + usb_gadget_giveback_request(&hwep->ep, &hwreq->req); spin_lock(hwep->lock); } } @@ -922,7 +922,7 @@ __acquires(hwep->lock) if ((hwep->type == USB_ENDPOINT_XFER_CONTROL) && hwreq->req.length) hweptemp = hwep->ci->ep0in; - hwreq->req.complete(&hweptemp->ep, &hwreq->req); + usb_gadget_giveback_request(&hweptemp->ep, &hwreq->req); spin_lock(hwep->lock); } } @@ -1347,7 +1347,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) if (hwreq->req.complete != NULL) { spin_unlock(hwep->lock); - hwreq->req.complete(&hwep->ep, &hwreq->req); + usb_gadget_giveback_request(&hwep->ep, &hwreq->req); spin_lock(hwep->lock); } diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 85293b8b1df9..926c997ef310 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -57,6 +57,8 @@ #define MX6_BM_OVER_CUR_DIS BIT(7) +#define VF610_OVER_CUR_DIS BIT(7) + struct usbmisc_ops { /* It's called once when probe a usb device */ int (*init)(struct imx_usbmisc_data *data); @@ -71,10 +73,9 @@ struct imx_usbmisc { const struct usbmisc_ops *ops; }; -static struct imx_usbmisc *usbmisc; - static int usbmisc_imx25_init(struct imx_usbmisc_data *data) { + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 val = 0; @@ -108,6 +109,7 @@ static int usbmisc_imx25_init(struct imx_usbmisc_data *data) static int usbmisc_imx25_post(struct imx_usbmisc_data *data) { + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); void __iomem *reg; unsigned long flags; u32 val; @@ -130,6 +132,7 @@ static int usbmisc_imx25_post(struct imx_usbmisc_data *data) static int usbmisc_imx27_init(struct imx_usbmisc_data *data) { + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 val; @@ -160,6 +163,7 @@ static int usbmisc_imx27_init(struct imx_usbmisc_data *data) static int usbmisc_imx53_init(struct imx_usbmisc_data *data) { + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); void __iomem *reg = NULL; unsigned long flags; u32 val = 0; @@ -204,6 +208,7 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data) static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) { + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 reg; @@ -221,6 +226,26 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_vf610_init(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + u32 reg; + + /* + * Vybrid only has one misc register set, but in two different + * areas. These is reflected in two instances of this driver. + */ + if (data->index >= 1) + return -EINVAL; + + if (data->disable_oc) { + reg = readl(usbmisc->base); + writel(reg | VF610_OVER_CUR_DIS, usbmisc->base); + } + + return 0; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -238,10 +263,14 @@ static const struct usbmisc_ops imx6q_usbmisc_ops = { .init = usbmisc_imx6q_init, }; +static const struct usbmisc_ops vf610_usbmisc_ops = { + .init = usbmisc_vf610_init, +}; + int imx_usbmisc_init(struct imx_usbmisc_data *data) { - if (!usbmisc) - return -EPROBE_DEFER; + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + if (!usbmisc->ops->init) return 0; return usbmisc->ops->init(data); @@ -250,8 +279,8 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init); int imx_usbmisc_init_post(struct imx_usbmisc_data *data) { - if (!usbmisc) - return -EPROBE_DEFER; + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + if (!usbmisc->ops->post) return 0; return usbmisc->ops->post(data); @@ -283,6 +312,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx6q-usbmisc", .data = &imx6q_usbmisc_ops, }, + { + .compatible = "fsl,vf610-usbmisc", + .data = &vf610_usbmisc_ops, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); @@ -294,9 +327,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev) int ret; struct of_device_id *tmp_dev; - if (usbmisc) - return -EBUSY; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -325,15 +355,15 @@ static int usbmisc_imx_probe(struct platform_device *pdev) tmp_dev = (struct of_device_id *) of_match_device(usbmisc_imx_dt_ids, &pdev->dev); data->ops = (const struct usbmisc_ops *)tmp_dev->data; - usbmisc = data; + platform_set_drvdata(pdev, data); return 0; } static int usbmisc_imx_remove(struct platform_device *pdev) { + struct imx_usbmisc *usbmisc = dev_get_drvdata(&pdev->dev); clk_disable_unprepare(usbmisc->clk); - usbmisc = NULL; return 0; } diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 103a6e9ee49d..ec978408a2ee 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -715,7 +715,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) u8 *buffer; int rv; int n; - int actual; + int actual = 0; int max_size; dev = &data->intf->dev; diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index 752646167e1e..ca2f8bd0e431 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -2,5 +2,8 @@ # Makefile for the usb common parts. # -obj-$(CONFIG_USB_COMMON) += usb-common.o +obj-$(CONFIG_USB_COMMON) += usb-common.o +usb-common-y += common.o +usb-common-$(CONFIG_USB_LED_TRIG) += led.o + obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o diff --git a/drivers/usb/common/usb-common.c b/drivers/usb/common/common.c index 6dfd30a863c7..b530fd403ffb 100644 --- a/drivers/usb/common/usb-common.c +++ b/drivers/usb/common/common.c @@ -139,6 +139,21 @@ enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np) } EXPORT_SYMBOL_GPL(of_usb_get_maximum_speed); +/** + * of_usb_host_tpl_support - to get if Targeted Peripheral List is supported + * for given targeted hosts (non-PC hosts) + * @np: Pointer to the given device_node + * + * The function gets if the targeted hosts support TPL or not + */ +bool of_usb_host_tpl_support(struct device_node *np) +{ + if (of_find_property(np, "tpl-support", NULL)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(of_usb_host_tpl_support); #endif MODULE_LICENSE("GPL"); diff --git a/drivers/usb/common/led.c b/drivers/usb/common/led.c new file mode 100644 index 000000000000..df23da00a901 --- /dev/null +++ b/drivers/usb/common/led.c @@ -0,0 +1,57 @@ +/* + * LED Triggers for USB Activity + * + * Copyright 2014 Michal Sojka <sojka@merica.cz> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/leds.h> +#include <linux/usb.h> + +#define BLINK_DELAY 30 + +static unsigned long usb_blink_delay = BLINK_DELAY; + +DEFINE_LED_TRIGGER(ledtrig_usb_gadget); +DEFINE_LED_TRIGGER(ledtrig_usb_host); + +void usb_led_activity(enum usb_led_event ev) +{ + struct led_trigger *trig = NULL; + + switch (ev) { + case USB_LED_EVENT_GADGET: + trig = ledtrig_usb_gadget; + break; + case USB_LED_EVENT_HOST: + trig = ledtrig_usb_host; + break; + } + /* led_trigger_blink_oneshot() handles trig == NULL gracefully */ + led_trigger_blink_oneshot(trig, &usb_blink_delay, &usb_blink_delay, 0); +} +EXPORT_SYMBOL_GPL(usb_led_activity); + + +static int __init ledtrig_usb_init(void) +{ + led_trigger_register_simple("usb-gadget", &ledtrig_usb_gadget); + led_trigger_register_simple("usb-host", &ledtrig_usb_host); + return 0; +} + +static void __exit ledtrig_usb_exit(void) +{ + led_trigger_unregister_simple(ledtrig_usb_gadget); + led_trigger_unregister_simple(ledtrig_usb_host); +} + +module_init(ledtrig_usb_init); +module_exit(ledtrig_usb_exit); diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 1060657ca1b0..9cfda6a72194 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -56,22 +56,16 @@ config USB_OTG connector. config USB_OTG_WHITELIST - bool "Rely on OTG Targeted Peripherals List" - depends on USB_OTG || EXPERT - default y if USB_OTG + bool "Rely on OTG and EH Targeted Peripherals List" + depends on USB help If you say Y here, the "otg_whitelist.h" file will be used as a product whitelist, so USB peripherals not listed there will be rejected during enumeration. This behavior is required by the - USB OTG specification for all devices not on your product's + USB OTG and EH specification for all devices not on your product's "Targeted Peripherals List". "Embedded Hosts" are likewise allowed to support only a limited number of peripherals. - Otherwise, peripherals not listed there will only generate a - warning and enumeration will continue. That's more like what - normal Linux-USB hosts do (other than the warning), and is - convenient for many stages of product development. - config USB_OTG_BLACKLIST_HUB bool "Disable external hubs" depends on USB_OTG || EXPERT diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 1ab4df1de2da..b2a540b43f97 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -199,6 +199,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (n == 0) n = 9; /* 32 ms = 2^(9-1) uframes */ j = 16; + + /* + * Adjust bInterval for quirked devices. + * This quirk fixes bIntervals reported in + * linear microframes. + */ + if (to_usb_device(ddev)->quirks & + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) { + n = clamp(fls(d->bInterval), i, j); + i = j = n; + } break; default: /* USB_SPEED_FULL or _LOW */ /* For low-speed, 10 ms is the official minimum. diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 257876ea03a1..0b59731c3021 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1509,7 +1509,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb u = (is_in ? URB_DIR_IN : URB_DIR_OUT); if (uurb->flags & USBDEVFS_URB_ISO_ASAP) u |= URB_ISO_ASAP; - if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK) + if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in) u |= URB_SHORT_NOT_OK; if (uurb->flags & USBDEVFS_URB_NO_FSBR) u |= URB_NO_FSBR; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 4aeb10034de7..9bffd26cea05 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -417,10 +417,11 @@ static int usb_unbind_interface(struct device *dev) */ lpm_disable_error = usb_unlocked_disable_lpm(udev); - /* Terminate all URBs for this interface unless the driver - * supports "soft" unbinding. + /* + * Terminate all URBs for this interface unless the driver + * supports "soft" unbinding and the device is still present. */ - if (!driver->soft_unbind) + if (!driver->soft_unbind || udev->state == USB_STATE_NOTATTACHED) usb_disable_interface(udev, intf, false); driver->disconnect(intf); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 82044b5d6113..efc953119ce2 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -380,6 +380,8 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && hcd->driver->shutdown) { hcd->driver->shutdown(hcd); + if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0) + free_irq(hcd->irq, hcd); pci_disable_device(dev); } } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index bec31e2efb88..b84fb141e122 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -42,6 +42,7 @@ #include <linux/pm_runtime.h> #include <linux/types.h> +#include <linux/phy/phy.h> #include <linux/usb.h> #include <linux/usb/hcd.h> #include <linux/usb/phy.h> @@ -855,8 +856,6 @@ static ssize_t authorized_default_show(struct device *dev, struct usb_bus *usb_bus = rh_usb_dev->bus; struct usb_hcd *usb_hcd; - if (usb_bus == NULL) /* FIXME: not sure if this case is possible */ - return -ENODEV; usb_hcd = bus_to_hcd(usb_bus); return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default); } @@ -871,8 +870,6 @@ static ssize_t authorized_default_store(struct device *dev, struct usb_bus *usb_bus = rh_usb_dev->bus; struct usb_hcd *usb_hcd; - if (usb_bus == NULL) /* FIXME: not sure if this case is possible */ - return -ENODEV; usb_hcd = bus_to_hcd(usb_bus); result = sscanf(buf, "%u\n", &val); if (result == 1) { @@ -1276,7 +1273,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep); * The usb core itself is however optimized for host controllers that can dma * using regular system memory - like pci devices doing bus mastering. * - * To support host controllers with limited dma capabilites we provide dma + * To support host controllers with limited dma capabilities we provide dma * bounce buffers. This feature can be enabled using the HCD_LOCAL_MEM flag. * For this to work properly the host controller code must first use the * function dma_declare_coherent_memory() to point out which memory area @@ -1668,6 +1665,8 @@ static void __usb_hcd_giveback_urb(struct urb *urb) usbmon_urb_complete(&hcd->self, urb, status); usb_anchor_suspend_wakeups(anchor); usb_unanchor_urb(urb); + if (likely(status == 0)) + usb_led_activity(USB_LED_EVENT_HOST); /* pass ownership to the completion handler */ urb->status = status; @@ -2305,7 +2304,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); * Context: in_interrupt() * * Starts enumeration, with an immediate reset followed later by - * khubd identifying and possibly configuring the device. + * hub_wq identifying and possibly configuring the device. * This is needed by OTG controller drivers, where it helps meet * HNP protocol timing requirements for starting a port reset. * @@ -2324,7 +2323,7 @@ int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) if (port_num && hcd->driver->start_port_reset) status = hcd->driver->start_port_reset(hcd, port_num); - /* run khubd shortly after (first) root port reset finishes; + /* allocate hub_wq shortly after (first) root port reset finishes; * it may issue others, until at least 50 msecs have passed. */ if (status == 0) @@ -2387,20 +2386,20 @@ void usb_hc_died (struct usb_hcd *hcd) if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - /* make khubd clean up old urbs and devices */ + /* make hub_wq clean up old urbs and devices */ usb_set_device_state (hcd->self.root_hub, USB_STATE_NOTATTACHED); - usb_kick_khubd (hcd->self.root_hub); + usb_kick_hub_wq(hcd->self.root_hub); } if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) { hcd = hcd->shared_hcd; if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - /* make khubd clean up old urbs and devices */ + /* make hub_wq clean up old urbs and devices */ usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED); - usb_kick_khubd(hcd->self.root_hub); + usb_kick_hub_wq(hcd->self.root_hub); } } spin_unlock_irqrestore (&hcd_root_hub_lock, flags); @@ -2631,7 +2630,7 @@ int usb_add_hcd(struct usb_hcd *hcd, int retval; struct usb_device *rhdev; - if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->phy) { + if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->usb_phy) { struct usb_phy *phy = usb_get_phy_dev(hcd->self.controller, 0); if (IS_ERR(phy)) { @@ -2644,11 +2643,34 @@ int usb_add_hcd(struct usb_hcd *hcd, usb_put_phy(phy); return retval; } - hcd->phy = phy; + hcd->usb_phy = phy; hcd->remove_phy = 1; } } + if (IS_ENABLED(CONFIG_GENERIC_PHY)) { + struct phy *phy = phy_get(hcd->self.controller, "usb"); + + if (IS_ERR(phy)) { + retval = PTR_ERR(phy); + if (retval == -EPROBE_DEFER) + goto err_phy; + } else { + retval = phy_init(phy); + if (retval) { + phy_put(phy); + goto err_phy; + } + retval = phy_power_on(phy); + if (retval) { + phy_exit(phy); + phy_put(phy); + goto err_phy; + } + hcd->phy = phy; + } + } + dev_info(hcd->self.controller, "%s\n", hcd->product_desc); /* Keep old behaviour if authorized_default is not in [0, 1]. */ @@ -2659,12 +2681,12 @@ int usb_add_hcd(struct usb_hcd *hcd, set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); /* HC is in reset state, but accessible. Now do the one-time init, - * bottom up so that hcds can customize the root hubs before khubd + * bottom up so that hcds can customize the root hubs before hub_wq * starts talking to them. (Note, bus id is assigned early too.) */ if ((retval = hcd_buffer_create(hcd)) != 0) { dev_dbg(hcd->self.controller, "pool alloc failed\n"); - goto err_remove_phy; + goto err_create_buf; } if ((retval = usb_register_bus(&hcd->self)) < 0) @@ -2791,12 +2813,19 @@ err_allocate_root_hub: usb_deregister_bus(&hcd->self); err_register_bus: hcd_buffer_destroy(hcd); -err_remove_phy: - if (hcd->remove_phy && hcd->phy) { - usb_phy_shutdown(hcd->phy); - usb_put_phy(hcd->phy); +err_create_buf: + if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) { + phy_power_off(hcd->phy); + phy_exit(hcd->phy); + phy_put(hcd->phy); hcd->phy = NULL; } +err_phy: + if (hcd->remove_phy && hcd->usb_phy) { + usb_phy_shutdown(hcd->usb_phy); + usb_put_phy(hcd->usb_phy); + hcd->usb_phy = NULL; + } return retval; } EXPORT_SYMBOL_GPL(usb_add_hcd); @@ -2868,11 +2897,18 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_deregister_bus(&hcd->self); hcd_buffer_destroy(hcd); - if (hcd->remove_phy && hcd->phy) { - usb_phy_shutdown(hcd->phy); - usb_put_phy(hcd->phy); + + if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) { + phy_power_off(hcd->phy); + phy_exit(hcd->phy); + phy_put(hcd->phy); hcd->phy = NULL; } + if (hcd->remove_phy && hcd->usb_phy) { + usb_phy_shutdown(hcd->usb_phy); + usb_put_phy(hcd->usb_phy); + hcd->usb_phy = NULL; + } usb_put_invalidate_rhdev(hcd); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0e950ad8cb25..11e80ac31324 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -22,9 +22,8 @@ #include <linux/usb/hcd.h> #include <linux/usb/otg.h> #include <linux/usb/quirks.h> -#include <linux/kthread.h> +#include <linux/workqueue.h> #include <linux/mutex.h> -#include <linux/freezer.h> #include <linux/random.h> #include <linux/pm_qos.h> @@ -32,6 +31,7 @@ #include <asm/byteorder.h> #include "hub.h" +#include "otg_whitelist.h" #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 @@ -41,14 +41,9 @@ * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ static DEFINE_SPINLOCK(device_state_lock); -/* khubd's worklist and its lock */ -static DEFINE_SPINLOCK(hub_event_lock); -static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ - -/* Wakes up khubd */ -static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); - -static struct task_struct *khubd_task; +/* workqueue to process hub events */ +static struct workqueue_struct *hub_wq; +static void hub_event(struct work_struct *work); /* synchronize hub-port add/remove and peering operations */ DEFINE_MUTEX(usb_port_peer_mutex); @@ -104,6 +99,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); #define HUB_DEBOUNCE_STEP 25 #define HUB_DEBOUNCE_STABLE 100 +static void hub_release(struct kref *kref); static int usb_reset_and_verify_device(struct usb_device *udev); static inline char *portspeed(struct usb_hub *hub, int portstatus) @@ -575,28 +571,39 @@ static int hub_port_status(struct usb_hub *hub, int port1, return ret; } -static void kick_khubd(struct usb_hub *hub) +static void kick_hub_wq(struct usb_hub *hub) { - unsigned long flags; + struct usb_interface *intf; - spin_lock_irqsave(&hub_event_lock, flags); - if (!hub->disconnected && list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); + if (hub->disconnected || work_pending(&hub->events)) + return; - /* Suppress autosuspend until khubd runs */ - usb_autopm_get_interface_no_resume( - to_usb_interface(hub->intfdev)); - wake_up(&khubd_wait); - } - spin_unlock_irqrestore(&hub_event_lock, flags); + /* + * Suppress autosuspend until the event is proceed. + * + * Be careful and make sure that the symmetric operation is + * always called. We are here only when there is no pending + * work for this hub. Therefore put the interface either when + * the new work is called or when it is canceled. + */ + intf = to_usb_interface(hub->intfdev); + usb_autopm_get_interface_no_resume(intf); + kref_get(&hub->kref); + + if (queue_work(hub_wq, &hub->events)) + return; + + /* the work has already been scheduled */ + usb_autopm_put_interface_async(intf); + kref_put(&hub->kref, hub_release); } -void usb_kick_khubd(struct usb_device *hdev) +void usb_kick_hub_wq(struct usb_device *hdev) { struct usb_hub *hub = usb_hub_to_struct_hub(hdev); if (hub) - kick_khubd(hub); + kick_hub_wq(hub); } /* @@ -618,7 +625,7 @@ void usb_wakeup_notification(struct usb_device *hdev, hub = usb_hub_to_struct_hub(hdev); if (hub) { set_bit(portnum, hub->wakeup_bits); - kick_khubd(hub); + kick_hub_wq(hub); } } EXPORT_SYMBOL_GPL(usb_wakeup_notification); @@ -645,7 +652,7 @@ static void hub_irq(struct urb *urb) hub->error = status; /* FALL THROUGH */ - /* let khubd handle things */ + /* let hub_wq handle things */ case 0: /* we got data: port status changed */ bits = 0; for (i = 0; i < urb->actual_length; ++i) @@ -657,8 +664,8 @@ static void hub_irq(struct urb *urb) hub->nerrors = 0; - /* Something happened, let khubd figure it out */ - kick_khubd(hub); + /* Something happened, let hub_wq figure it out */ + kick_hub_wq(hub); resubmit: if (hub->quiescing) @@ -688,7 +695,7 @@ hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt) } /* - * enumeration blocks khubd for a long time. we use keventd instead, since + * enumeration blocks hub_wq for a long time. we use keventd instead, since * long blocking there is the exception, not the rule. accordingly, HCDs * talking to TTs must queue control transfers (not just bulk and iso), so * both can talk to the same hub concurrently. @@ -954,7 +961,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) /* * Disable a port and mark a logical connect-change event, so that some - * time later khubd will disconnect() any existing usb_device on the port + * time later hub_wq will disconnect() any existing usb_device on the port * and will re-enumerate if there actually is a device attached. */ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) @@ -967,12 +974,12 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) * - SRP saves power that way * - ... new call, TBD ... * That's easy if this hub can switch power per-port, and - * khubd reactivates the port later (timer, SRP, etc). + * hub_wq reactivates the port later (timer, SRP, etc). * Powerdown must be optional, because of reset/DFU. */ set_bit(port1, hub->change_bits); - kick_khubd(hub); + kick_hub_wq(hub); } /** @@ -980,7 +987,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) * @udev: device to be disabled and removed * Context: @udev locked, must be able to sleep. * - * After @udev's port has been disabled, khubd is notified and it will + * After @udev's port has been disabled, hub_wq is notified and it will * see that the device has been disconnected. When the device is * physically unplugged and something is plugged in, the events will * be received and processed normally. @@ -1100,7 +1107,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) init2: /* - * Check each port and set hub->change_bits to let khubd know + * Check each port and set hub->change_bits to let hub_wq know * which ports need attention. */ for (port1 = 1; port1 <= hdev->maxchild; ++port1) { @@ -1167,7 +1174,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) clear_bit(port1, hub->removed_bits); if (!udev || udev->state == USB_STATE_NOTATTACHED) { - /* Tell khubd to disconnect the device or + /* Tell hub_wq to disconnect the device or * check for a new connection */ if (udev || (portstatus & USB_PORT_STAT_CONNECTION) || @@ -1180,7 +1187,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) USB_SS_PORT_LS_U0; /* The power session apparently survived the resume. * If there was an overcurrent or suspend change - * (i.e., remote wakeup request), have khubd + * (i.e., remote wakeup request), have hub_wq * take care of it. Look at the port link state * for USB 3.0 hubs, since they don't have a suspend * change bit, and they don't set the port link change @@ -1201,7 +1208,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) set_bit(port1, hub->change_bits); } else { - /* The power session is gone; tell khubd */ + /* The power session is gone; tell hub_wq */ usb_set_device_state(udev, USB_STATE_NOTATTACHED); set_bit(port1, hub->change_bits); } @@ -1209,10 +1216,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* If no port-status-change flags were set, we don't need any * debouncing. If flags were set we can try to debounce the - * ports all at once right now, instead of letting khubd do them + * ports all at once right now, instead of letting hub_wq do them * one at a time later on. * - * If any port-status changes do occur during this delay, khubd + * If any port-status changes do occur during this delay, hub_wq * will see them later and handle them normally. */ if (need_debounce_delay) { @@ -1240,7 +1247,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) &hub->leds, LED_CYCLE_PERIOD); /* Scan all ports that need attention */ - kick_khubd(hub); + kick_hub_wq(hub); /* Allow autosuspend if it was suppressed */ if (type <= HUB_INIT3) @@ -1273,7 +1280,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) cancel_delayed_work_sync(&hub->init_work); - /* khubd and related activity won't re-trigger */ + /* hub_wq and related activity won't re-trigger */ hub->quiescing = 1; if (type != HUB_SUSPEND) { @@ -1284,7 +1291,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) } } - /* Stop khubd and related activity */ + /* Stop hub_wq and related activity */ usb_kill_urb(hub->urb); if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); @@ -1606,7 +1613,7 @@ static int hub_configure(struct usb_hub *hub, if (ret < 0) goto fail; - /* Update the HCD's internal representation of this hub before khubd + /* Update the HCD's internal representation of this hub before hub_wq * starts getting port status changes for devices under the hub. */ if (hcd->driver->update_hub_device) { @@ -1634,6 +1641,7 @@ static void hub_release(struct kref *kref) { struct usb_hub *hub = container_of(kref, struct usb_hub, kref); + usb_put_dev(hub->hdev); usb_put_intf(to_usb_interface(hub->intfdev)); kfree(hub); } @@ -1646,14 +1654,11 @@ static void hub_disconnect(struct usb_interface *intf) struct usb_device *hdev = interface_to_usbdev(intf); int port1; - /* Take the hub off the event list and don't let it be added again */ - spin_lock_irq(&hub_event_lock); - if (!list_empty(&hub->event_list)) { - list_del_init(&hub->event_list); - usb_autopm_put_interface_no_suspend(intf); - } + /* + * Stop adding new hub events. We do not want to block here and thus + * will not try to remove any pending work item. + */ hub->disconnected = 1; - spin_unlock_irq(&hub_event_lock); /* Disconnect all children and quiesce the hub */ hub->error = 0; @@ -1728,8 +1733,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) * - Change autosuspend delay of hub can avoid unnecessary auto * suspend timer for hub, also may decrease power consumption * of USB bus. + * + * - If user has indicated to prevent autosuspend by passing + * usbcore.autosuspend = -1 then keep autosuspend disabled. */ - pm_runtime_set_autosuspend_delay(&hdev->dev, 0); +#ifdef CONFIG_PM_RUNTIME + if (hdev->dev.power.autosuspend_delay >= 0) + pm_runtime_set_autosuspend_delay(&hdev->dev, 0); +#endif /* * Hubs have proper suspend/resume support, except for root hubs @@ -1787,12 +1798,13 @@ descriptor_error: } kref_init(&hub->kref); - INIT_LIST_HEAD(&hub->event_list); hub->intfdev = &intf->dev; hub->hdev = hdev; INIT_DELAYED_WORK(&hub->leds, led_work); INIT_DELAYED_WORK(&hub->init_work, NULL); + INIT_WORK(&hub->events, hub_event); usb_get_intf(intf); + usb_get_dev(hdev); usb_set_intfdata (intf, hub); intf->needs_remote_wakeup = 1; @@ -1977,8 +1989,10 @@ void usb_set_device_state(struct usb_device *udev, || new_state == USB_STATE_SUSPENDED) ; /* No change to wakeup settings */ else if (new_state == USB_STATE_CONFIGURED) - wakeup = udev->actconfig->desc.bmAttributes - & USB_CONFIG_ATT_WAKEUP; + wakeup = (udev->quirks & + USB_QUIRK_IGNORE_REMOTE_WAKEUP) ? 0 : + udev->actconfig->desc.bmAttributes & + USB_CONFIG_ATT_WAKEUP; else wakeup = 0; } @@ -2031,7 +2045,8 @@ static void choose_devnum(struct usb_device *udev) int devnum; struct usb_bus *bus = udev->bus; - /* If khubd ever becomes multithreaded, this will need a lock */ + /* be safe when more hub events are proceed in parallel */ + mutex_lock(&bus->usb_address0_mutex); if (udev->wusb) { devnum = udev->portnum + 1; BUG_ON(test_bit(devnum, bus->devmap.devicemap)); @@ -2049,6 +2064,7 @@ static void choose_devnum(struct usb_device *udev) set_bit(devnum, bus->devmap.devicemap); udev->devnum = devnum; } + mutex_unlock(&bus->usb_address0_mutex); } static void release_devnum(struct usb_device *udev) @@ -2107,8 +2123,8 @@ void usb_disconnect(struct usb_device **pdev) { struct usb_port *port_dev = NULL; struct usb_device *udev = *pdev; - struct usb_hub *hub; - int port1; + struct usb_hub *hub = NULL; + int port1 = 1; /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. @@ -2199,9 +2215,6 @@ static void announce_device(struct usb_device *udev) static inline void announce_device(struct usb_device *udev) { } #endif -#ifdef CONFIG_USB_OTG -#include "otg_whitelist.h" -#endif /** * usb_enumerate_device_otg - FIXME (usbcore-internal) @@ -2261,21 +2274,6 @@ static int usb_enumerate_device_otg(struct usb_device *udev) } } } - - if (!is_targeted(udev)) { - - /* Maybe it can talk to us, though we can't talk to it. - * (Includes HNP test device.) - */ - if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { - err = usb_port_suspend(udev, PMSG_SUSPEND); - if (err < 0) - dev_dbg(&udev->dev, "HNP fail, %d\n", err); - } - err = -ENOTSUPP; - goto fail; - } -fail: #endif return err; } @@ -2298,6 +2296,7 @@ fail: static int usb_enumerate_device(struct usb_device *udev) { int err; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); if (udev->config == NULL) { err = usb_get_configuration(udev); @@ -2319,6 +2318,20 @@ static int usb_enumerate_device(struct usb_device *udev) if (err < 0) return err; + if (IS_ENABLED(CONFIG_USB_OTG_WHITELIST) && hcd->tpl_support && + !is_targeted(udev)) { + /* Maybe it can talk to us, though we can't talk to it. + * (Includes HNP test device.) + */ + if (IS_ENABLED(CONFIG_USB_OTG) && (udev->bus->b_hnp_enable + || udev->bus->is_b_host)) { + err = usb_port_suspend(udev, PMSG_AUTO_SUSPEND); + if (err < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", err); + } + return -ENOTSUPP; + } + usb_detect_interface_quirks(udev); return 0; @@ -2606,13 +2619,20 @@ static int hub_port_reset(struct usb_hub *hub, int port1, /* Is a USB 3.0 port in the Inactive or Compliance Mode state? * Port worm reset is required to recover */ -static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus) +static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1, + u16 portstatus) { - return hub_is_superspeed(hub->hdev) && - (((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_SS_INACTIVE) || - ((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_COMP_MOD)) ; + u16 link_state; + + if (!hub_is_superspeed(hub->hdev)) + return false; + + if (test_bit(port1, hub->warm_reset_bits)) + return true; + + link_state = portstatus & USB_PORT_STAT_LINK_STATE; + return link_state == USB_SS_PORT_LS_SS_INACTIVE + || link_state == USB_SS_PORT_LS_COMP_MOD; } static int hub_port_wait_reset(struct usb_hub *hub, int port1, @@ -2649,7 +2669,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if ((portstatus & USB_PORT_STAT_RESET)) return -EBUSY; - if (hub_port_warm_reset_required(hub, portstatus)) + if (hub_port_warm_reset_required(hub, port1, portstatus)) return -ENOTCONN; /* Device went away? */ @@ -2749,9 +2769,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1, if (status < 0) goto done; - if (hub_port_warm_reset_required(hub, portstatus)) + if (hub_port_warm_reset_required(hub, port1, portstatus)) warm = true; } + clear_bit(port1, hub->warm_reset_bits); /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { @@ -2788,7 +2809,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1, &portstatus, &portchange) < 0) goto done; - if (!hub_port_warm_reset_required(hub, portstatus)) + if (!hub_port_warm_reset_required(hub, port1, + portstatus)) goto done; /* @@ -2875,8 +2897,13 @@ static int check_port_resume_type(struct usb_device *udev, { struct usb_port *port_dev = hub->ports[port1 - 1]; + /* Is a warm reset needed to recover the connection? */ + if (status == 0 && udev->reset_resume + && hub_port_warm_reset_required(hub, port1, portstatus)) { + /* pass */; + } /* Is the device still present? */ - if (status || port_is_suspended(hub, portstatus) || + else if (status || port_is_suspended(hub, portstatus) || !port_is_power_on(hub, portstatus) || !(portstatus & USB_PORT_STAT_CONNECTION)) { if (status >= 0) @@ -3050,7 +3077,7 @@ static unsigned wakeup_enabled_descendants(struct usb_device *udev) * Once VBUS drop breaks the circuit, the port it's using has to go through * normal re-enumeration procedures, starting with enabling VBUS power. * Other than re-initializing the hub (plug/unplug, except for root hubs), - * Linux (2.6) currently has NO mechanisms to initiate that: no khubd + * Linux (2.6) currently has NO mechanisms to initiate that: no hub_wq * timer, no SRP, no requests through sysfs. * * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get @@ -3192,7 +3219,7 @@ static int finish_port_resume(struct usb_device *udev) /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the * first two on the host side; they'd be inside hub_port_init() - * during many timeouts, but khubd can't suspend until later. + * during many timeouts, but hub_wq can't suspend until later. */ usb_set_device_state(udev, udev->actconfig ? USB_STATE_CONFIGURED @@ -3264,6 +3291,43 @@ static int finish_port_resume(struct usb_device *udev) } /* + * There are some SS USB devices which take longer time for link training. + * XHCI specs 4.19.4 says that when Link training is successful, port + * sets CSC bit to 1. So if SW reads port status before successful link + * training, then it will not find device to be present. + * USB Analyzer log with such buggy devices show that in some cases + * device switch on the RX termination after long delay of host enabling + * the VBUS. In few other cases it has been seen that device fails to + * negotiate link training in first attempt. It has been + * reported till now that few devices take as long as 2000 ms to train + * the link after host enabling its VBUS and termination. Following + * routine implements a 2000 ms timeout for link training. If in a case + * link trains before timeout, loop will exit earlier. + * + * FIXME: If a device was connected before suspend, but was removed + * while system was asleep, then the loop in the following routine will + * only exit at timeout. + * + * This routine should only be called when persist is enabled for a SS + * device. + */ +static int wait_for_ss_port_enable(struct usb_device *udev, + struct usb_hub *hub, int *port1, + u16 *portchange, u16 *portstatus) +{ + int status = 0, delay_ms = 0; + + while (delay_ms < 2000) { + if (status || *portstatus & USB_PORT_STAT_CONNECTION) + break; + msleep(20); + delay_ms += 20; + status = hub_port_status(hub, *port1, portstatus, portchange); + } + return status; +} + +/* * usb_port_resume - re-activate a suspended usb device's upstream port * @udev: device to re-activate, not a root hub * Context: must be able to sleep; device not locked; pm locks held @@ -3359,6 +3423,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) } } + if (udev->persist_enabled && hub_is_superspeed(hub->hdev)) + status = wait_for_ss_port_enable(udev, hub, &port1, &portchange, + &portstatus); + status = check_port_resume_type(udev, hub, port1, status, portchange, portstatus); if (status == 0) @@ -3516,7 +3584,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) dev_dbg(&intf->dev, "%s\n", __func__); - /* stop khubd and related activity */ + /* stop hub_wq and related activity */ hub_quiesce(hub, HUB_SUSPEND); return 0; } @@ -3879,7 +3947,8 @@ int usb_disable_lpm(struct usb_device *udev) if (!udev || !udev->parent || udev->speed != USB_SPEED_SUPER || - !udev->lpm_capable) + !udev->lpm_capable || + udev->state < USB_STATE_DEFAULT) return 0; hcd = bus_to_hcd(udev->bus); @@ -3935,7 +4004,8 @@ void usb_enable_lpm(struct usb_device *udev) if (!udev || !udev->parent || udev->speed != USB_SPEED_SUPER || - !udev->lpm_capable) + !udev->lpm_capable || + udev->state < USB_STATE_DEFAULT) return; udev->lpm_disable_count--; @@ -4398,8 +4468,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (retval) goto fail; - if (hcd->phy && !hdev->parent) - usb_phy_notify_connect(hcd->phy, udev->speed); + if (hcd->usb_phy && !hdev->parent) + usb_phy_notify_connect(hcd->usb_phy, udev->speed); /* * Some superspeed devices have finished the link training process @@ -4475,6 +4545,9 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) struct usb_qualifier_descriptor *qual; int status; + if (udev->quirks & USB_QUIRK_DEVICE_QUALIFIER) + return; + qual = kmalloc (sizeof *qual, GFP_KERNEL); if (qual == NULL) return; @@ -4550,12 +4623,13 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, struct usb_hcd *hcd = bus_to_hcd(hdev->bus); struct usb_port *port_dev = hub->ports[port1 - 1]; struct usb_device *udev = port_dev->child; + static int unreliable_port = -1; /* Disconnect any existing devices under this port */ if (udev) { - if (hcd->phy && !hdev->parent && + if (hcd->usb_phy && !hdev->parent && !(portstatus & USB_PORT_STAT_CONNECTION)) - usb_phy_notify_disconnect(hcd->phy, udev->speed); + usb_phy_notify_disconnect(hcd->usb_phy, udev->speed); usb_disconnect(&port_dev->child); } @@ -4570,10 +4644,12 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, USB_PORT_STAT_C_ENABLE)) { status = hub_port_debounce_be_stable(hub, port1); if (status < 0) { - if (status != -ENODEV && printk_ratelimit()) - dev_err(&port_dev->dev, - "connect-debounce failed\n"); + if (status != -ENODEV && + port1 != unreliable_port && + printk_ratelimit()) + dev_err(&port_dev->dev, "connect-debounce failed\n"); portstatus &= ~USB_PORT_STAT_CONNECTION; + unreliable_port = port1; } else { portstatus = status; } @@ -4889,7 +4965,7 @@ static void port_event(struct usb_hub *hub, int port1) * Warm reset a USB3 protocol port if it's in * SS.Inactive state. */ - if (hub_port_warm_reset_required(hub, portstatus)) { + if (hub_port_warm_reset_required(hub, port1, portstatus)) { dev_dbg(&port_dev->dev, "do warm reset\n"); if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) || udev->state == USB_STATE_NOTATTACHED) { @@ -4904,10 +4980,10 @@ static void port_event(struct usb_hub *hub, int port1) * 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 + * before hub_wq runs and sees the disconnect, and the * device may be an unknown state. * - * If the port went through SS.Inactive without khubd + * If the port went through SS.Inactive without hub_wq * seeing it the C_LINK_STATE change flag will be set, * and we reset the dev to put it in a known state. */ @@ -4926,10 +5002,8 @@ static void port_event(struct usb_hub *hub, int port1) hub_port_connect_change(hub, port1, portstatus, portchange); } - -static void hub_events(void) +static void hub_event(struct work_struct *work) { - struct list_head *tmp; struct usb_device *hdev; struct usb_interface *intf; struct usb_hub *hub; @@ -4938,164 +5012,117 @@ static void hub_events(void) u16 hubchange; int i, ret; - /* - * We restart the list every time to avoid a deadlock with - * deleting hubs downstream from this one. This should be - * safe since we delete the hub from the event list. - * Not the most efficient, but avoids deadlocks. - */ - while (1) { + hub = container_of(work, struct usb_hub, events); + hdev = hub->hdev; + hub_dev = hub->intfdev; + intf = to_usb_interface(hub_dev); + + dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", + hdev->state, hdev->maxchild, + /* NOTE: expects max 15 ports... */ + (u16) hub->change_bits[0], + (u16) hub->event_bits[0]); + + /* Lock the device, then check to see if we were + * disconnected while waiting for the lock to succeed. */ + usb_lock_device(hdev); + if (unlikely(hub->disconnected)) + goto out_hdev_lock; + + /* If the hub has died, clean up after it */ + if (hdev->state == USB_STATE_NOTATTACHED) { + hub->error = -ENODEV; + hub_quiesce(hub, HUB_DISCONNECT); + goto out_hdev_lock; + } + + /* Autoresume */ + ret = usb_autopm_get_interface(intf); + if (ret) { + dev_dbg(hub_dev, "Can't autoresume: %d\n", ret); + goto out_hdev_lock; + } - /* Grab the first entry at the beginning of the list */ - spin_lock_irq(&hub_event_lock); - if (list_empty(&hub_event_list)) { - spin_unlock_irq(&hub_event_lock); - break; - } + /* If this is an inactive hub, do nothing */ + if (hub->quiescing) + goto out_autopm; - tmp = hub_event_list.next; - list_del_init(tmp); - - hub = list_entry(tmp, struct usb_hub, event_list); - kref_get(&hub->kref); - spin_unlock_irq(&hub_event_lock); - - hdev = hub->hdev; - hub_dev = hub->intfdev; - intf = to_usb_interface(hub_dev); - dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", - hdev->state, hdev->maxchild, - /* NOTE: expects max 15 ports... */ - (u16) hub->change_bits[0], - (u16) hub->event_bits[0]); - - /* Lock the device, then check to see if we were - * disconnected while waiting for the lock to succeed. */ - usb_lock_device(hdev); - if (unlikely(hub->disconnected)) - goto loop_disconnected; - - /* If the hub has died, clean up after it */ - if (hdev->state == USB_STATE_NOTATTACHED) { - hub->error = -ENODEV; - hub_quiesce(hub, HUB_DISCONNECT); - goto loop; - } + if (hub->error) { + dev_dbg(hub_dev, "resetting for error %d\n", hub->error); - /* Autoresume */ - ret = usb_autopm_get_interface(intf); + ret = usb_reset_device(hdev); if (ret) { - dev_dbg(hub_dev, "Can't autoresume: %d\n", ret); - goto loop; + dev_dbg(hub_dev, "error resetting hub: %d\n", ret); + goto out_autopm; } - /* If this is an inactive hub, do nothing */ - if (hub->quiescing) - goto loop_autopm; - - if (hub->error) { - dev_dbg (hub_dev, "resetting for error %d\n", - hub->error); + hub->nerrors = 0; + hub->error = 0; + } - ret = usb_reset_device(hdev); - if (ret) { - dev_dbg (hub_dev, - "error resetting hub: %d\n", ret); - goto loop_autopm; - } + /* deal with port status changes */ + for (i = 1; i <= hdev->maxchild; i++) { + struct usb_port *port_dev = hub->ports[i - 1]; - hub->nerrors = 0; - hub->error = 0; + if (test_bit(i, hub->event_bits) + || test_bit(i, hub->change_bits) + || test_bit(i, hub->wakeup_bits)) { + /* + * The get_noresume and barrier ensure that if + * the port was in the process of resuming, we + * flush that work and keep the port active for + * the duration of the port_event(). However, + * if the port is runtime pm suspended + * (powered-off), we leave it in that state, run + * an abbreviated port_event(), and move on. + */ + pm_runtime_get_noresume(&port_dev->dev); + pm_runtime_barrier(&port_dev->dev); + usb_lock_port(port_dev); + port_event(hub, i); + usb_unlock_port(port_dev); + pm_runtime_put_sync(&port_dev->dev); } + } - /* deal with port status changes */ - for (i = 1; i <= hdev->maxchild; i++) { - struct usb_port *port_dev = hub->ports[i - 1]; - - if (test_bit(i, hub->event_bits) - || test_bit(i, hub->change_bits) - || test_bit(i, hub->wakeup_bits)) { - /* - * The get_noresume and barrier ensure that if - * the port was in the process of resuming, we - * flush that work and keep the port active for - * the duration of the port_event(). However, - * if the port is runtime pm suspended - * (powered-off), we leave it in that state, run - * an abbreviated port_event(), and move on. - */ - pm_runtime_get_noresume(&port_dev->dev); - pm_runtime_barrier(&port_dev->dev); - usb_lock_port(port_dev); - port_event(hub, i); - usb_unlock_port(port_dev); - pm_runtime_put_sync(&port_dev->dev); - } + /* deal with hub status changes */ + if (test_and_clear_bit(0, hub->event_bits) == 0) + ; /* do nothing */ + else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) + dev_err(hub_dev, "get_hub_status failed\n"); + else { + if (hubchange & HUB_CHANGE_LOCAL_POWER) { + dev_dbg(hub_dev, "power change\n"); + clear_hub_feature(hdev, C_HUB_LOCAL_POWER); + if (hubstatus & HUB_STATUS_LOCAL_POWER) + /* FIXME: Is this always true? */ + hub->limited_power = 1; + else + hub->limited_power = 0; } + if (hubchange & HUB_CHANGE_OVERCURRENT) { + u16 status = 0; + u16 unused; - /* deal with hub status changes */ - if (test_and_clear_bit(0, hub->event_bits) == 0) - ; /* do nothing */ - else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) - dev_err (hub_dev, "get_hub_status failed\n"); - else { - if (hubchange & HUB_CHANGE_LOCAL_POWER) { - dev_dbg (hub_dev, "power change\n"); - clear_hub_feature(hdev, C_HUB_LOCAL_POWER); - if (hubstatus & HUB_STATUS_LOCAL_POWER) - /* FIXME: Is this always true? */ - hub->limited_power = 1; - else - hub->limited_power = 0; - } - if (hubchange & HUB_CHANGE_OVERCURRENT) { - u16 status = 0; - u16 unused; - - dev_dbg(hub_dev, "over-current change\n"); - clear_hub_feature(hdev, C_HUB_OVER_CURRENT); - msleep(500); /* Cool down */ - hub_power_on(hub, true); - hub_hub_status(hub, &status, &unused); - if (status & HUB_STATUS_OVERCURRENT) - dev_err(hub_dev, "over-current " - "condition\n"); - } + dev_dbg(hub_dev, "over-current change\n"); + clear_hub_feature(hdev, C_HUB_OVER_CURRENT); + msleep(500); /* Cool down */ + hub_power_on(hub, true); + hub_hub_status(hub, &status, &unused); + if (status & HUB_STATUS_OVERCURRENT) + dev_err(hub_dev, "over-current condition\n"); } + } - loop_autopm: - /* Balance the usb_autopm_get_interface() above */ - usb_autopm_put_interface_no_suspend(intf); - loop: - /* Balance the usb_autopm_get_interface_no_resume() in - * kick_khubd() and allow autosuspend. - */ - usb_autopm_put_interface(intf); - loop_disconnected: - usb_unlock_device(hdev); - kref_put(&hub->kref, hub_release); - - } /* end while (1) */ -} - -static int hub_thread(void *__unused) -{ - /* khubd needs to be freezable to avoid interfering with USB-PERSIST - * port handover. Otherwise it might see that a full-speed device - * was gone before the EHCI controller had handed its port over to - * the companion full-speed controller. - */ - set_freezable(); - - do { - hub_events(); - wait_event_freezable(khubd_wait, - !list_empty(&hub_event_list) || - kthread_should_stop()); - } while (!kthread_should_stop() || !list_empty(&hub_event_list)); +out_autopm: + /* Balance the usb_autopm_get_interface() above */ + usb_autopm_put_interface_no_suspend(intf); +out_hdev_lock: + usb_unlock_device(hdev); - pr_debug("%s: khubd exiting\n", usbcore_name); - return 0; + /* Balance the stuff in kick_hub_wq() and allow autosuspend */ + usb_autopm_put_interface(intf); + kref_put(&hub->kref, hub_release); } static const struct usb_device_id hub_id_table[] = { @@ -5135,20 +5162,26 @@ int usb_hub_init(void) return -1; } - khubd_task = kthread_run(hub_thread, NULL, "khubd"); - if (!IS_ERR(khubd_task)) + /* + * The workqueue needs to be freezable to avoid interfering with + * USB-PERSIST port handover. Otherwise it might see that a full-speed + * device was gone before the EHCI controller had handed its port + * over to the companion full-speed controller. + */ + hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); + if (hub_wq) return 0; /* Fall through if kernel_thread failed */ usb_deregister(&hub_driver); - printk(KERN_ERR "%s: can't start khubd\n", usbcore_name); + pr_err("%s: can't allocate workqueue for usb hub\n", usbcore_name); return -1; } void usb_hub_cleanup(void) { - kthread_stop(khubd_task); + destroy_workqueue(hub_wq); /* * Hub resources are freed for us by usb_deregister. It calls @@ -5257,7 +5290,7 @@ static int descriptors_changed(struct usb_device *udev, * former operating configuration. If the reset fails, or the device's * descriptors change from their values before the reset, or the original * configuration and altsettings cannot be restored, a flag will be set - * telling khubd to pretend the device has been disconnected and then + * telling hub_wq to pretend the device has been disconnected and then * re-connected. All drivers will be unbound, and the device will be * re-enumerated and probed all over again. * diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 326308e53961..688817fb3246 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -41,7 +41,6 @@ struct usb_hub { int error; /* last reported error */ int nerrors; /* track consecutive errors */ - struct list_head event_list; /* hubs w/data or errs ready */ unsigned long event_bits[1]; /* status change bitmask */ unsigned long change_bits[1]; /* ports with logical connect status change */ @@ -52,6 +51,8 @@ struct usb_hub { unsigned long power_bits[1]; /* ports that are powered */ unsigned long child_usage_bits[1]; /* ports powered on for children */ + unsigned long warm_reset_bits[1]; /* ports requesting warm + reset recovery */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif @@ -75,6 +76,7 @@ struct usb_hub { u8 indicator[USB_MAXCHILDREN]; struct delayed_work leds; struct delayed_work init_work; + struct work_struct events; struct usb_port **ports; }; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 0c8a7fc4dad8..f7b7713cfb2a 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -770,9 +770,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) dev->string_langid = 0x0409; dev->have_langid = 1; dev_err(&dev->dev, - "string descriptor 0 malformed (err = %d), " - "defaulting to 0x%04x\n", - err, dev->string_langid); + "language id specifier not provided by device, defaulting to English\n"); return 0; } diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h index e8cdce571bb1..de0c9c9d7091 100644 --- a/drivers/usb/core/otg_whitelist.h +++ b/drivers/usb/core/otg_whitelist.h @@ -10,8 +10,8 @@ */ /* - * This OTG Whitelist is the OTG "Targeted Peripheral List". It should - * mostly use of USB_DEVICE() or USB_DEVICE_VER() entries.. + * This OTG and Embedded Host Whitelist is "Targeted Peripheral List". + * It should mostly use of USB_DEVICE() or USB_DEVICE_VER() entries.. * * YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING! */ @@ -50,10 +50,6 @@ static int is_targeted(struct usb_device *dev) { struct usb_device_id *id = whitelist_table; - /* possible in developer configs only! */ - if (!dev->bus->otg_port) - return 1; - /* HNP test device is _never_ targeted (see OTG spec 6.6.6) */ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a && le16_to_cpu(dev->descriptor.idProduct) == 0xbadd)) @@ -103,10 +99,7 @@ static int is_targeted(struct usb_device *dev) dev_err(&dev->dev, "device v%04x p%04x is not supported\n", le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); -#ifdef CONFIG_USB_OTG_WHITELIST + return 0; -#else - return 1; -#endif } diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index fe1b6d0967e3..cd3f9dc24a06 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -103,16 +103,19 @@ static int usb_port_runtime_resume(struct device *dev) msleep(hub_power_on_good_delay(hub)); if (udev && !retval) { /* - * Attempt to wait for usb hub port to be reconnected in order - * to make the resume procedure successful. The device may have - * disconnected while the port was powered off, so ignore the - * return status. + * Our preference is to simply wait for the port to reconnect, + * as that is the lowest latency method to restart the port. + * However, there are cases where toggling port power results in + * the host port and the device port getting out of sync causing + * a link training live lock. Upon timeout, flag the port as + * needing warm reset recovery (to be performed later by + * usb_port_resume() as requested via usb_wakeup_notification()) */ - retval = hub_port_debounce_be_connected(hub, port1); - if (retval < 0) - dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", - retval); - retval = 0; + if (hub_port_debounce_be_connected(hub, port1) < 0) { + dev_dbg(&port_dev->dev, "reconnect timeout\n"); + if (hub_is_superspeed(hdev)) + set_bit(port1, hub->warm_reset_bits); + } /* Force the child awake to revalidate after the power loss. */ if (!test_and_set_bit(port1, hub->child_usage_bits)) { diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 739ee8e8bdfd..5ae883dc21f5 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -93,6 +93,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x04e8, 0x6601), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Elan Touchscreen */ + { USB_DEVICE(0x04f3, 0x0089), .driver_info = + USB_QUIRK_DEVICE_QUALIFIER }, + /* Roland SC-8820 */ { USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -145,6 +149,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* SKYMEDI USB_DRIVE */ { USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Razer - Razer Blade Keyboard */ + { USB_DEVICE(0x1532, 0x0116), .driver_info = + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, @@ -152,6 +160,13 @@ static const struct usb_device_id usb_quirk_list[] = { /* INTEL VALUE SSD */ { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME }, + /* USB3503 */ + { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* ASUS Base Station(T100) */ + { USB_DEVICE(0x0b05, 0x17e0), .driver_info = + USB_QUIRK_IGNORE_REMOTE_WAKEUP }, + { } /* terminating entry must be last */ }; diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 991386ceb4ec..c9e8ee81b6b7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -454,6 +454,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) URB_FREE_BUFFER); switch (xfertype) { case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: if (is_out) allowed |= URB_ZERO_PACKET; /* FALLTHROUGH */ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 4d1144990d4c..2dd2362198d2 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -501,6 +501,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, } return dev; } +EXPORT_SYMBOL_GPL(usb_alloc_dev); /** * usb_get_dev - increments the reference count of the usb device structure diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index d9d08720c386..b1b34d0557c9 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -48,7 +48,7 @@ static inline unsigned usb_get_max_power(struct usb_device *udev, return c->desc.bMaxPower * mul; } -extern void usb_kick_khubd(struct usb_device *dev); +extern void usb_kick_hub_wq(struct usb_device *dev); extern int usb_match_one_id_intf(struct usb_device *dev, struct usb_host_interface *intf, const struct usb_device_id *id); diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 27d2c9b8a034..d9269459d481 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -118,6 +118,7 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) { u32 greset; int count = 0; + u32 gusbcfg; dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -148,6 +149,23 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) } } while (greset & GRSTCTL_CSFTRST); + if (hsotg->dr_mode == USB_DR_MODE_HOST) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + gusbcfg |= GUSBCFG_FORCEHOSTMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg |= GUSBCFG_FORCEDEVMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } else if (hsotg->dr_mode == USB_DR_MODE_OTG) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } + /* * NOTE: This long sleep is _very_ important, otherwise the core will * not stay in host mode after a connector ID change! @@ -2674,23 +2692,23 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hwcfg2 = readl(hsotg->regs + GHWCFG2); hwcfg3 = readl(hsotg->regs + GHWCFG3); hwcfg4 = readl(hsotg->regs + GHWCFG4); - gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); grxfsiz = readl(hsotg->regs + GRXFSIZ); dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1); dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2); dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3); dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4); - dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz); dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz); - /* Force host mode to get HPTXFSIZ exact power on value */ + /* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */ gusbcfg = readl(hsotg->regs + GUSBCFG); gusbcfg |= GUSBCFG_FORCEHOSTMODE; writel(gusbcfg, hsotg->regs + GUSBCFG); usleep_range(100000, 150000); + gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); hptxfsiz = readl(hsotg->regs + HPTXFSIZ); + dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz); dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz); gusbcfg = readl(hsotg->regs + GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; @@ -2725,6 +2743,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >> GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT; hw->max_transfer_size = (1 << (width + 11)) - 1; + /* + * Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates + * coherent buffers with this size, and if it's too large we can + * exhaust the coherent DMA pool. + */ + if (hw->max_transfer_size > 65535) + hw->max_transfer_size = 65535; width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >> GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT; hw->max_packet_count = (1 << (width + 4)) - 1; diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 1efd10cc9629..bf015ab3b44c 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -139,6 +139,7 @@ struct s3c_hsotg_ep { unsigned int last_load; unsigned int fifo_load; unsigned short fifo_size; + unsigned short fifo_index; unsigned char dir_in; unsigned char index; @@ -194,8 +195,10 @@ struct s3c_hsotg { struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)]; u32 phyif; + int fifo_mem; unsigned int dedicated_fifos:1; unsigned char num_of_eps; + u32 fifo_map; struct dentry *debug_root; struct dentry *debug_file; @@ -501,6 +504,10 @@ struct dwc2_hw_params { * a_peripheral and b_device=>b_host) this may not match * the core, but allows the software to determine * transitions + * @dr_mode: Requested mode of operation, one of following: + * - USB_DR_MODE_PERIPHERAL + * - USB_DR_MODE_HOST + * - USB_DR_MODE_OTG * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth * transfer are in process of being queued * @srp_success: Stores status of SRP request in the case of a FS PHY @@ -592,6 +599,7 @@ struct dwc2_hsotg { /** Params to actually use */ struct dwc2_core_params *core_params; enum usb_otg_state op_state; + enum usb_dr_mode dr_mode; unsigned int queuing_high_bandwidth:1; unsigned int srp_success:1; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index f3c56a2fed5b..7b5856fadd93 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1,6 +1,4 @@ /** - * linux/drivers/usb/gadget/s3c-hsotg.c - * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * @@ -184,16 +182,33 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg) /* start at the end of the GNPTXFSIZ, rounded up */ addr = 2048 + 1024; - size = 768; /* - * currently we allocate TX FIFOs for all possible endpoints, - * and assume that they are all the same size. + * Because we have not enough memory to have each TX FIFO of size at + * least 3072 bytes (the maximum single packet size), we create four + * FIFOs of lenght 1024, and four of length 3072 bytes, and assing + * them to endpoints dynamically according to maxpacket size value of + * given endpoint. */ - for (ep = 1; ep <= 15; ep++) { + /* 256*4=1024 bytes FIFO length */ + size = 256; + for (ep = 1; ep <= 4; ep++) { val = addr; val |= size << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + size > hsotg->fifo_mem, + "insufficient fifo memory"); + addr += size; + + writel(val, hsotg->regs + DPTXFSIZN(ep)); + } + /* 768*4=3072 bytes FIFO length */ + size = 768; + for (ep = 5; ep <= 8; ep++) { + val = addr; + val |= size << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + size > hsotg->fifo_mem, + "insufficient fifo memory"); addr += size; writel(val, hsotg->regs + DPTXFSIZN(ep)); @@ -989,8 +1004,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, hs_req = ep->req; ep->req = NULL; list_del_init(&hs_req->queue); - hs_req->req.complete(&ep->ep, - &hs_req->req); + usb_gadget_giveback_request(&ep->ep, + &hs_req->req); } /* If we have pending request, then start it */ @@ -1022,7 +1037,8 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); * * Set stall for ep0 as response for setup request. */ -static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) { +static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) +{ struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; u32 reg; u32 ctrl; @@ -1246,7 +1262,7 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg, if (hs_req->req.complete) { spin_unlock(&hsotg->lock); - hs_req->req.complete(&hs_ep->ep, &hs_req->req); + usb_gadget_giveback_request(&hs_ep->ep, &hs_req->req); spin_lock(&hsotg->lock); } @@ -1650,6 +1666,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx) dev_err(hsotg->dev, "%s: timeout flushing fifo (GRSTCTL=%08x)\n", __func__, val); + break; } udelay(1); @@ -1832,7 +1849,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, if (dir_in) { int epctl = readl(hsotg->regs + epctl_reg); - s3c_hsotg_txfifo_flush(hsotg, idx); + s3c_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { @@ -1902,7 +1919,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) { u32 dsts = readl(hsotg->regs + DSTS); - int ep0_mps = 0, ep_mps; + int ep0_mps = 0, ep_mps = 8; /* * This should signal the finish of the enumeration phase @@ -1981,6 +1998,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, int result, bool force) { struct s3c_hsotg_req *req, *treq; + unsigned size; list_for_each_entry_safe(req, treq, &ep->queue, queue) { /* @@ -1994,9 +2012,11 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, s3c_hsotg_complete_request(hsotg, ep, req, result); } - if(hsotg->dedicated_fifos) - if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072) - s3c_hsotg_txfifo_flush(hsotg, ep->index); + if (!hsotg->dedicated_fifos) + return; + size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; + if (size < ep->fifo_size) + s3c_hsotg_txfifo_flush(hsotg, ep->fifo_index); } /** @@ -2437,6 +2457,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, u32 epctrl; u32 mps; int dir_in; + int i, val, size; int ret = 0; dev_dbg(hsotg->dev, @@ -2509,17 +2530,8 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, break; case USB_ENDPOINT_XFER_INT: - if (dir_in) { - /* - * Allocate our TxFNum by simply using the index - * of the endpoint for the moment. We could do - * something better if the host indicates how - * many FIFOs we are expecting to use. - */ - + if (dir_in) hs_ep->periodic = 1; - epctrl |= DXEPCTL_TXFNUM(index); - } epctrl |= DXEPCTL_EPTYPE_INTERRUPT; break; @@ -2533,8 +2545,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, * if the hardware has dedicated fifos, we must give each IN EP * a unique tx-fifo even if it is non-periodic. */ - if (dir_in && hsotg->dedicated_fifos) - epctrl |= DXEPCTL_TXFNUM(index); + if (dir_in && hsotg->dedicated_fifos) { + size = hs_ep->ep.maxpacket*hs_ep->mc; + for (i = 1; i <= 8; ++i) { + if (hsotg->fifo_map & (1<<i)) + continue; + val = readl(hsotg->regs + DPTXFSIZN(i)); + val = (val >> FIFOSIZE_DEPTH_SHIFT)*4; + if (val < size) + continue; + hsotg->fifo_map |= 1<<i; + + epctrl |= DXEPCTL_TXFNUM(i); + hs_ep->fifo_index = i; + hs_ep->fifo_size = val; + break; + } + if (i == 8) + return -ENOMEM; + } /* for non control endpoints, set PID to D0 */ if (index) @@ -2568,7 +2597,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) u32 epctrl_reg; u32 ctrl; - dev_info(hsotg->dev, "%s(ep %p)\n", __func__, ep); + dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep); if (ep == &hsotg->eps[0].ep) { dev_err(hsotg->dev, "%s: called for ep0\n", __func__); @@ -2581,6 +2610,9 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) /* terminate all requests with shutdown */ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false); + hsotg->fifo_map &= ~(1<<hs_ep->fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; ctrl = readl(hsotg->regs + epctrl_reg); ctrl &= ~DXEPCTL_EPENA; @@ -2626,7 +2658,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) struct s3c_hsotg *hs = hs_ep->parent; unsigned long flags; - dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); + dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); spin_lock_irqsave(&hs->lock, flags); @@ -2748,13 +2780,14 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg) dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev); - if (hsotg->phy) { - phy_init(hsotg->phy); - phy_power_on(hsotg->phy); - } else if (hsotg->uphy) + if (hsotg->uphy) usb_phy_init(hsotg->uphy); - else if (hsotg->plat->phy_init) + else if (hsotg->plat && hsotg->plat->phy_init) hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); + else { + phy_init(hsotg->phy); + phy_power_on(hsotg->phy); + } } /** @@ -2768,13 +2801,14 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); - if (hsotg->phy) { - phy_power_off(hsotg->phy); - phy_exit(hsotg->phy); - } else if (hsotg->uphy) + if (hsotg->uphy) usb_phy_shutdown(hsotg->uphy); - else if (hsotg->plat->phy_exit) + else if (hsotg->plat && hsotg->plat->phy_exit) hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); + else { + phy_power_off(hsotg->phy); + phy_exit(hsotg->phy); + } } /** @@ -2859,6 +2893,8 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, hsotg->gadget.dev.of_node = hsotg->dev->of_node; hsotg->gadget.speed = USB_SPEED_UNKNOWN; + clk_enable(hsotg->clk); + ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); if (ret) { @@ -2893,13 +2929,11 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, return -ENODEV; /* all endpoints should be shutdown */ - for (ep = 0; ep < hsotg->num_of_eps; ep++) + for (ep = 1; ep < hsotg->num_of_eps; ep++) s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_phy_disable(hsotg); - if (!driver) hsotg->driver = NULL; @@ -2909,6 +2943,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); + clk_disable(hsotg->clk); + return 0; } @@ -2935,14 +2971,15 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) struct s3c_hsotg *hsotg = to_hsotg(gadget); unsigned long flags = 0; - dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on); + dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on); spin_lock_irqsave(&hsotg->lock, flags); if (is_on) { s3c_hsotg_phy_enable(hsotg); + clk_enable(hsotg->clk); s3c_hsotg_core_init(hsotg); } else { - s3c_hsotg_disconnect(hsotg); + clk_disable(hsotg->clk); s3c_hsotg_phy_disable(hsotg); } @@ -2973,7 +3010,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, int epnum) { - u32 ptxfifo; char *dir; if (epnum == 0) @@ -3002,15 +3038,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, hs_ep->ep.ops = &s3c_hsotg_ep_ops; /* - * Read the FIFO size for the Periodic TX FIFO, even if we're - * an OUT endpoint, we may as well do this if in future the - * code is changed to make each endpoint's direction changeable. - */ - - ptxfifo = readl(hsotg->regs + DPTXFSIZN(epnum)); - hs_ep->fifo_size = FIFOSIZE_DEPTH_GET(ptxfifo) * 4; - - /* * if we're using dma, we need to set the next-endpoint pointer * to be something valid. */ @@ -3030,19 +3057,22 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, */ static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg) { - u32 cfg2, cfg4; + u32 cfg2, cfg3, cfg4; /* check hardware configuration */ cfg2 = readl(hsotg->regs + 0x48); hsotg->num_of_eps = (cfg2 >> 10) & 0xF; - dev_info(hsotg->dev, "EPs:%d\n", hsotg->num_of_eps); + cfg3 = readl(hsotg->regs + 0x4C); + hsotg->fifo_mem = (cfg3 >> 16); cfg4 = readl(hsotg->regs + 0x50); hsotg->dedicated_fifos = (cfg4 >> 25) & 1; - dev_info(hsotg->dev, "%s fifos\n", - hsotg->dedicated_fifos ? "dedicated" : "shared"); + dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n", + hsotg->num_of_eps, + hsotg->dedicated_fifos ? "dedicated" : "shared", + hsotg->fifo_mem); } /** @@ -3390,10 +3420,11 @@ static int s3c_hsotg_probe(struct platform_device *pdev) int i; hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); - if (!hsotg) { - dev_err(dev, "cannot get memory\n"); + if (!hsotg) return -ENOMEM; - } + + /* Set default UTMI width */ + hsotg->phyif = GUSBCFG_PHYIF16; /* * Attempt to find a generic PHY, then look for an old style @@ -3413,8 +3444,15 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->plat = plat; } else hsotg->uphy = uphy; - } else + } else { hsotg->phy = phy; + /* + * If using the generic PHY framework, check if the PHY bus + * width is 8-bit and set the phyif appropriately. + */ + if (phy_get_bus_width(phy) == 8) + hsotg->phyif = GUSBCFG_PHYIF8; + } hsotg->dev = dev; @@ -3444,13 +3482,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->irq = ret; - ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, - dev_name(dev), hsotg); - if (ret < 0) { - dev_err(dev, "cannot claim IRQ\n"); - goto err_clk; - } - dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq); hsotg->gadget.max_speed = USB_SPEED_HIGH; @@ -3481,25 +3512,23 @@ static int s3c_hsotg_probe(struct platform_device *pdev) goto err_supplies; } - /* Set default UTMI width */ - hsotg->phyif = GUSBCFG_PHYIF16; - - /* - * If using the generic PHY framework, check if the PHY bus - * width is 8-bit and set the phyif appropriately. - */ - if (hsotg->phy && (phy_get_bus_width(phy) == 8)) - hsotg->phyif = GUSBCFG_PHYIF8; - - if (hsotg->phy) - phy_init(hsotg->phy); - /* usb phy enable */ s3c_hsotg_phy_enable(hsotg); s3c_hsotg_corereset(hsotg); - s3c_hsotg_init(hsotg); s3c_hsotg_hw_cfg(hsotg); + s3c_hsotg_init(hsotg); + + ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, + dev_name(dev), hsotg); + if (ret < 0) { + s3c_hsotg_phy_disable(hsotg); + clk_disable_unprepare(hsotg->clk); + regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + dev_err(dev, "cannot claim IRQ\n"); + goto err_clk; + } /* hsotg->num_of_eps holds number of EPs other than ep0 */ @@ -3512,7 +3541,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep), GFP_KERNEL); if (!eps) { - dev_err(dev, "cannot get memory\n"); ret = -ENOMEM; goto err_supplies; } @@ -3586,9 +3614,6 @@ static int s3c_hsotg_remove(struct platform_device *pdev) usb_gadget_unregister_driver(hsotg->driver); } - s3c_hsotg_phy_disable(hsotg); - if (hsotg->phy) - phy_exit(hsotg->phy); clk_disable_unprepare(hsotg->clk); return 0; @@ -3617,6 +3642,7 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state) ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); + clk_disable(hsotg->clk); } return ret; @@ -3631,6 +3657,8 @@ static int s3c_hsotg_resume(struct platform_device *pdev) if (hsotg->driver) { dev_info(hsotg->dev, "resuming usb gadget %s\n", hsotg->driver->driver.name); + + clk_enable(hsotg->clk); ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); } diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 4d918ed8d343..0a0e6f0ad15f 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -697,29 +697,45 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, } static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, - struct dwc2_host_chan *chan, void *bufptr) + struct dwc2_host_chan *chan, + struct dwc2_hcd_urb *urb, void *bufptr) { u32 buf_size; - - if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) - buf_size = hsotg->core_params->max_transfer_size; - else - buf_size = 4096; + struct urb *usb_urb; + struct usb_hcd *hcd; if (!qh->dw_align_buf) { + if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) + buf_size = hsotg->core_params->max_transfer_size; + else + /* 3072 = 3 max-size Isoc packets */ + buf_size = 3072; + qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, &qh->dw_align_buf_dma, GFP_ATOMIC); if (!qh->dw_align_buf) return -ENOMEM; + qh->dw_align_buf_size = buf_size; } - if (!chan->ep_is_in && chan->xfer_len) { - dma_sync_single_for_cpu(hsotg->dev, chan->xfer_dma, buf_size, - DMA_TO_DEVICE); - memcpy(qh->dw_align_buf, bufptr, chan->xfer_len); - dma_sync_single_for_device(hsotg->dev, chan->xfer_dma, buf_size, - DMA_TO_DEVICE); + if (chan->xfer_len) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + usb_urb = urb->priv; + + if (usb_urb) { + if (usb_urb->transfer_flags & + (URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG | + URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) { + hcd = dwc2_hsotg_to_hcd(hsotg); + usb_hcd_unmap_urb_for_dma(hcd, usb_urb); + } + if (!chan->ep_is_in) + memcpy(qh->dw_align_buf, bufptr, + chan->xfer_len); + } else { + dev_warn(hsotg->dev, "no URB in dwc2_urb\n"); + } } chan->align_buf = qh->dw_align_buf_dma; @@ -828,7 +844,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) /* Non DWORD-aligned buffer case */ if (bufptr) { dev_vdbg(hsotg->dev, "Non-aligned buffer\n"); - if (dwc2_hc_setup_align_buf(hsotg, qh, chan, bufptr)) { + if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) { dev_err(hsotg->dev, "%s: Failed to allocate memory to handle non-dword aligned buffer\n", __func__); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index fdc6d489084a..a12bb1538666 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -243,7 +243,8 @@ enum dwc2_transaction_type { * @ntd: Actual number of transfer descriptors in a list * @dw_align_buf: Used instead of original buffer if its physical address * is not dword-aligned - * @dw_align_buf_dma: DMA address for align_buf + * @dw_align_buf_size: Size of dw_align_buf + * @dw_align_buf_dma: DMA address for dw_align_buf * @qtd_list: List of QTDs for this QH * @channel: Host channel currently processing transfers for this QH * @qh_list_entry: Entry for QH in either the periodic or non-periodic @@ -276,6 +277,7 @@ struct dwc2_qh { u16 start_split_frame; u16 ntd; u8 *dw_align_buf; + int dw_align_buf_size; dma_addr_t dw_align_buf_dma; struct list_head qtd_list; struct dwc2_host_chan *channel; diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 47b9eb5389b4..551ba878b003 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -465,12 +465,8 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, xfer_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); } dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", @@ -560,14 +556,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, - urb->length, DMA_FROM_DEVICE); memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, frame_desc->actual_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, - urb->length, - DMA_FROM_DEVICE); } break; case DWC2_HC_XFER_FRAME_OVERRUN: @@ -594,14 +585,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, - urb->length, DMA_FROM_DEVICE); memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, frame_desc->actual_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, - urb->length, - DMA_FROM_DEVICE); } /* Skip whole frame */ @@ -937,12 +923,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, if (chan->align_buf) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, qtd->urb->dma, - qtd->urb->length, DMA_FROM_DEVICE); memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); - dma_sync_single_for_device(hsotg->dev, qtd->urb->dma, - qtd->urb->length, DMA_FROM_DEVICE); } qtd->isoc_split_offset += len; @@ -1170,12 +1152,8 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, xfer_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); } urb->actual_length += xfer_length; @@ -1890,12 +1868,20 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, "hcint 0x%08x, intsts 0x%08x\n", chan->hcint, readl(hsotg->regs + GINTSTS)); + goto error; } } } else { dev_info(hsotg->dev, "NYET/NAK/ACK/other in non-error case, 0x%08x\n", chan->hcint); +error: + /* Failthrough: use 3-strikes rule */ + qtd->error_count++; + dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb, + qtd, DWC2_HC_XFER_XACT_ERR); + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR); } } diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 9540f7e1e20e..bb97838bc6c0 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -229,19 +229,11 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, */ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - u32 buf_size; - - if (hsotg->core_params->dma_desc_enable > 0) { + if (hsotg->core_params->dma_desc_enable > 0) dwc2_hcd_qh_free_ddma(hsotg, qh); - } else if (qh->dw_align_buf) { - if (qh->ep_type == USB_ENDPOINT_XFER_ISOC) - buf_size = 4096; - else - buf_size = hsotg->core_params->max_transfer_size; - dma_free_coherent(hsotg->dev, buf_size, qh->dw_align_buf, - qh->dw_align_buf_dma); - } - + else if (qh->dw_align_buf) + dma_free_coherent(hsotg->dev, qh->dw_align_buf_size, + qh->dw_align_buf, qh->dw_align_buf_dma); kfree(qh); } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index a10e7a353576..121dbdafc06b 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -42,6 +42,8 @@ #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/usb/of.h> + #include "core.h" #include "hcd.h" @@ -75,6 +77,34 @@ static const struct dwc2_core_params params_bcm2835 = { .uframe_sched = 0, }; +static const struct dwc2_core_params params_rk3066 = { + .otg_cap = 2, /* non-HNP/non-SRP */ + .otg_ver = -1, + .dma_enable = -1, + .dma_desc_enable = 0, + .speed = -1, + .enable_dynamic_fifo = 1, + .en_multiple_tx_fifo = -1, + .host_rx_fifo_size = 520, /* 520 DWORDs */ + .host_nperio_tx_fifo_size = 128, /* 128 DWORDs */ + .host_perio_tx_fifo_size = 256, /* 256 DWORDs */ + .max_transfer_size = 65535, + .max_packet_count = -1, + .host_channels = -1, + .phy_type = -1, + .phy_utmi_width = -1, + .phy_ulpi_ddr = -1, + .phy_ulpi_ext_vbus = -1, + .i2c_enable = -1, + .ulpi_fs_ls = -1, + .host_support_fs_ls_low_power = -1, + .host_ls_low_power_phy_clk = -1, + .ts_dline = -1, + .reload_ctl = -1, + .ahbcfg = 0x7, /* INCR16 */ + .uframe_sched = -1, +}; + /** * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the * DWC_otg driver @@ -97,6 +127,7 @@ static int dwc2_driver_remove(struct platform_device *dev) static const struct of_device_id dwc2_of_match_table[] = { { .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 }, + { .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 }, { .compatible = "snps,dwc2", .data = NULL }, {}, }; @@ -171,6 +202,8 @@ static int dwc2_driver_probe(struct platform_device *dev) dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", (unsigned long)res->start, hsotg->regs); + hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node); + retval = dwc2_hcd_init(hsotg, irq, params); if (retval) return retval; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 261c3b428220..f4e5cc60db0b 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -80,6 +80,23 @@ config USB_DWC3_KEYSTONE Support of USB2/3 functionality in TI Keystone2 platforms. Say 'Y' or 'M' here if you have one such device +config USB_DWC3_ST + tristate "STMicroelectronics Platforms" + depends on ARCH_STI && OF + default USB_DWC3 + help + STMicroelectronics SoCs with one DesignWare Core USB3 IP + inside (i.e. STiH407). + Say 'Y' or 'M' if you have one such device. + +config USB_DWC3_QCOM + tristate "Qualcomm Platforms" + depends on ARCH_QCOM || COMPILE_TEST + default USB_DWC3 + help + Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside, + say 'Y' or 'M' if you have one such device. + comment "Debugging features" config USB_DWC3_DEBUG @@ -93,4 +110,11 @@ config USB_DWC3_VERBOSE help Say Y here to enable verbose debugging messages on DWC3 Driver. +config DWC3_HOST_USB3_LPM_ENABLE + bool "Enable USB3 LPM Capability" + depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y + default n + help + Select this when you want to enable USB3 LPM with dwc3 xhci host. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 10ac3e72482e..bb34fbcfeab3 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -1,9 +1,12 @@ +# define_trace.h needs to know how to find our header +CFLAGS_trace.o := -I$(src) + ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC3) += dwc3.o -dwc3-y := core.o +dwc3-y := core.o debug.o trace.o ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),) dwc3-y += host.o @@ -33,3 +36,5 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o +obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index eb69eb9f06c8..b0f4d52b7f04 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -186,10 +186,8 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num, GFP_KERNEL); - if (!dwc->ev_buffs) { - dev_err(dwc->dev, "can't allocate event buffers array\n"); + if (!dwc->ev_buffs) return -ENOMEM; - } for (i = 0; i < num; i++) { struct dwc3_event_buffer *evt; @@ -386,6 +384,13 @@ static int dwc3_core_init(struct dwc3 *dwc) } dwc->revision = reg; + /* Handle USB2.0-only core configuration */ + if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { + if (dwc->maximum_speed == USB_SPEED_SUPER) + dwc->maximum_speed = USB_SPEED_HIGH; + } + /* issue device SoftReset too */ timeout = jiffies + msecs_to_jiffies(500); dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); @@ -632,10 +637,9 @@ static int dwc3_probe(struct platform_device *pdev) void *mem; mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); - if (!mem) { - dev_err(dev, "not enough memory\n"); + if (!mem) return -ENOMEM; - } + dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); dwc->mem = mem; dwc->dev = dev; @@ -656,6 +660,31 @@ static int dwc3_probe(struct platform_device *pdev) return -ENODEV; } + dwc->xhci_resources[0].start = res->start; + dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + + DWC3_XHCI_REGS_END; + dwc->xhci_resources[0].flags = res->flags; + dwc->xhci_resources[0].name = res->name; + + res->start += DWC3_GLOBALS_REGS_START; + + /* + * Request memory region but exclude xHCI regs, + * since it will be requested by the xhci-plat driver. + */ + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + dwc->regs = regs; + dwc->regs_size = resource_size(res); + /* + * restore res->start back to its original value so that, + * in case the probe is deferred, we don't end up getting error in + * request the memory region the next time probe is called. + */ + res->start -= DWC3_GLOBALS_REGS_START; + if (node) { dwc->maximum_speed = of_usb_get_maximum_speed(node); @@ -676,28 +705,9 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) return ret; - dwc->xhci_resources[0].start = res->start; - dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + - DWC3_XHCI_REGS_END; - dwc->xhci_resources[0].flags = res->flags; - dwc->xhci_resources[0].name = res->name; - - res->start += DWC3_GLOBALS_REGS_START; - - /* - * Request memory region but exclude xHCI regs, - * since it will be requested by the xhci-plat driver. - */ - regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) - return PTR_ERR(regs); - spin_lock_init(&dwc->lock); platform_set_drvdata(pdev, dwc); - dwc->regs = regs; - dwc->regs_size = resource_size(res); - dev->dma_mask = dev->parent->dma_mask; dev->dma_parms = dev->parent->dma_parms; dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); @@ -786,20 +796,21 @@ static int dwc3_remove(struct platform_device *pdev) { struct dwc3 *dwc = platform_get_drvdata(pdev); + dwc3_debugfs_exit(dwc); + dwc3_core_exit_mode(dwc); + dwc3_event_buffers_cleanup(dwc); + dwc3_free_event_buffers(dwc); + 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); + dwc3_core_exit(dwc); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - dwc3_debugfs_exit(dwc); - dwc3_core_exit_mode(dwc); - dwc3_event_buffers_cleanup(dwc); - dwc3_free_event_buffers(dwc); - dwc3_core_exit(dwc); - return 0; } diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 57332e3768e4..66f62563bcf9 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -33,6 +33,8 @@ #include <linux/phy/phy.h> +#define DWC3_MSG_MAX 500 + /* Global constants */ #define DWC3_EP0_BOUNCE_SIZE 512 #define DWC3_ENDPOINTS_NUM 32 @@ -191,6 +193,19 @@ #define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) #define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) +/* Global HWPARAMS3 Register */ +#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) +#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2) +#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3 +#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4) +#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1 + /* Global HWPARAMS4 Register */ #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) #define DWC3_MAX_HIBER_SCRATCHBUFS 15 @@ -925,7 +940,7 @@ 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); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } diff --git a/drivers/usb/dwc3/debug.c b/drivers/usb/dwc3/debug.c new file mode 100644 index 000000000000..0be6885bc370 --- /dev/null +++ b/drivers/usb/dwc3/debug.c @@ -0,0 +1,32 @@ +/** + * debug.c - DesignWare USB3 DRD Controller Debug/Trace Support + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi <balbi@ti.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "debug.h" + +void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + trace(&vaf); + + va_end(args); +} diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index fceb39dc4bba..07fbc2d94fd4 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -16,8 +16,206 @@ * GNU General Public License for more details. */ +#ifndef __DWC3_DEBUG_H +#define __DWC3_DEBUG_H + #include "core.h" +/** + * dwc3_gadget_ep_cmd_string - returns endpoint command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_ep_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DEPCMD_DEPSTARTCFG: + return "Start New Configuration"; + case DWC3_DEPCMD_ENDTRANSFER: + return "End Transfer"; + case DWC3_DEPCMD_UPDATETRANSFER: + return "Update Transfer"; + case DWC3_DEPCMD_STARTTRANSFER: + return "Start Transfer"; + case DWC3_DEPCMD_CLEARSTALL: + return "Clear Stall"; + case DWC3_DEPCMD_SETSTALL: + return "Set Stall"; + case DWC3_DEPCMD_GETEPSTATE: + return "Get Endpoint State"; + case DWC3_DEPCMD_SETTRANSFRESOURCE: + return "Set Endpoint Transfer Resource"; + case DWC3_DEPCMD_SETEPCONFIG: + return "Set Endpoint Configuration"; + default: + return "UNKNOWN command"; + } +} + +/** + * dwc3_gadget_generic_cmd_string - returns generic command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_generic_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DGCMD_SET_LMP: + return "Set LMP"; + case DWC3_DGCMD_SET_PERIODIC_PAR: + return "Set Periodic Parameters"; + case DWC3_DGCMD_XMIT_FUNCTION: + return "Transmit Function Wake Device Notification"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO: + return "Set Scratchpad Buffer Array Address Lo"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI: + return "Set Scratchpad Buffer Array Address Hi"; + case DWC3_DGCMD_SELECTED_FIFO_FLUSH: + return "Selected FIFO Flush"; + case DWC3_DGCMD_ALL_FIFO_FLUSH: + return "All FIFO Flush"; + case DWC3_DGCMD_SET_ENDPOINT_NRDY: + return "Set Endpoint NRDY"; + case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: + return "Run SoC Bus Loopback Test"; + default: + return "UNKNOWN"; + } +} + +/** + * dwc3_gadget_link_string - returns link name + * @link_state: link state code + */ +static inline const char * +dwc3_gadget_link_string(enum dwc3_link_state link_state) +{ + switch (link_state) { + case DWC3_LINK_STATE_U0: + return "U0"; + case DWC3_LINK_STATE_U1: + return "U1"; + case DWC3_LINK_STATE_U2: + return "U2"; + case DWC3_LINK_STATE_U3: + return "U3"; + case DWC3_LINK_STATE_SS_DIS: + return "SS.Disabled"; + case DWC3_LINK_STATE_RX_DET: + return "RX.Detect"; + case DWC3_LINK_STATE_SS_INACT: + return "SS.Inactive"; + case DWC3_LINK_STATE_POLL: + return "Polling"; + case DWC3_LINK_STATE_RECOV: + return "Recovery"; + case DWC3_LINK_STATE_HRESET: + return "Hot Reset"; + case DWC3_LINK_STATE_CMPLY: + return "Compliance"; + case DWC3_LINK_STATE_LPBK: + return "Loopback"; + case DWC3_LINK_STATE_RESET: + return "Reset"; + case DWC3_LINK_STATE_RESUME: + return "Resume"; + default: + return "UNKNOWN link state\n"; + } +} + +/** + * dwc3_gadget_event_string - returns event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connection Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "WakeUp"; + case DWC3_DEVICE_EVENT_EOPF: + return "End-Of-Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start-Of-Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + } + + return "UNKNOWN"; +} + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char *dwc3_ep_event_string(u8 event) +{ + switch (event) { + case DWC3_DEPEVT_XFERCOMPLETE: + return "Transfer Complete"; + case DWC3_DEPEVT_XFERINPROGRESS: + return "Transfer In-Progress"; + case DWC3_DEPEVT_XFERNOTREADY: + return "Transfer Not Ready"; + case DWC3_DEPEVT_RXTXFIFOEVT: + return "FIFO"; + case DWC3_DEPEVT_STREAMEVT: + return "Stream"; + case DWC3_DEPEVT_EPCMDCMPLT: + return "Endpoint Command Complete"; + } + + return "UNKNOWN"; +} + +/** + * dwc3_gadget_event_type_string - return event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_type_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connect Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "Wake-Up"; + case DWC3_DEVICE_EVENT_HIBER_REQ: + return "Hibernation"; + case DWC3_DEVICE_EVENT_EOPF: + return "End of Periodic Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start of Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + default: + return "UNKNOWN"; + } +} + +void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...); + #ifdef CONFIG_DEBUG_FS extern int dwc3_debugfs_init(struct dwc3 *); extern void dwc3_debugfs_exit(struct dwc3 *); @@ -27,4 +225,4 @@ static inline int dwc3_debugfs_init(struct dwc3 *d) static inline void dwc3_debugfs_exit(struct dwc3 *d) { } #endif - +#endif /* __DWC3_DEBUG_H */ diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index f9fb8adb785b..3951a65fea04 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -113,10 +113,8 @@ static int dwc3_exynos_probe(struct platform_device *pdev) int ret; exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL); - if (!exynos) { - dev_err(dev, "not enough memory\n"); + if (!exynos) return -ENOMEM; - } /* * Right now device-tree probed devices don't get dma_mask set. diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 1fad1618df6e..7ec8495f4523 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -189,7 +189,6 @@ static struct platform_driver kdwc3_driver = { .remove = kdwc3_remove, .driver = { .name = "keystone-dwc3", - .owner = THIS_MODULE, .of_match_table = kdwc3_of_match, }, }; diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 07a736acd0f2..2f537d588225 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -77,10 +77,6 @@ #define USBOTGSS_DEV_EBC_EN 0x0110 #define USBOTGSS_DEBUG_OFFSET 0x0600 -/* REVISION REGISTER */ -#define USBOTGSS_REVISION_XMAJOR(reg) ((reg >> 8) & 0x7) -#define USBOTGSS_REVISION_XMAJOR1 1 -#define USBOTGSS_REVISION_XMAJOR2 2 /* SYSCONFIG REGISTER */ #define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) @@ -129,7 +125,6 @@ struct dwc3_omap { u32 irq_eoi_offset; u32 debug_offset; u32 irq0_offset; - u32 revision; u32 dma_status:1; @@ -383,6 +378,87 @@ static int dwc3_omap_vbus_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static void dwc3_omap_map_offset(struct dwc3_omap *omap) +{ + struct device_node *node = omap->dev->of_node; + + /* + * Differentiate between OMAP5 and AM437x. + * + * For OMAP5(ES2.0) and AM437x wrapper revision is same, even + * though there are changes in wrapper register offsets. + * + * Using dt compatible to differentiate AM437x. + */ + if (of_device_is_compatible(node, "ti,am437x-dwc3")) { + omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; + omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; + omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; + omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; + omap->debug_offset = USBOTGSS_DEBUG_OFFSET; + } +} + +static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap) +{ + u32 reg; + struct device_node *node = omap->dev->of_node; + int utmi_mode = 0; + + reg = dwc3_omap_read_utmi_status(omap); + + of_property_read_u32(node, "utmi-mode", &utmi_mode); + + switch (utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode); + } + + dwc3_omap_write_utmi_status(omap, reg); +} + +static int dwc3_omap_extcon_register(struct dwc3_omap *omap) +{ + int ret; + struct device_node *node = omap->dev->of_node; + struct extcon_dev *edev; + + if (of_property_read_bool(node, "extcon")) { + edev = extcon_get_edev_by_phandle(omap->dev, 0); + if (IS_ERR(edev)) { + dev_vdbg(omap->dev, "couldn't get extcon device\n"); + return -EPROBE_DEFER; + } + + omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; + ret = extcon_register_interest(&omap->extcon_vbus_dev, + edev->name, "USB", + &omap->vbus_nb); + if (ret < 0) + dev_vdbg(omap->dev, "failed to register notifier for USB\n"); + + omap->id_nb.notifier_call = dwc3_omap_id_notifier; + ret = extcon_register_interest(&omap->extcon_id_dev, + edev->name, "USB-HOST", + &omap->id_nb); + if (ret < 0) + dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n"); + + if (extcon_get_cable_state(edev, "USB") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); + if (extcon_get_cable_state(edev, "USB-HOST") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); + } + + return 0; +} + static int dwc3_omap_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -390,15 +466,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct dwc3_omap *omap; struct resource *res; struct device *dev = &pdev->dev; - struct extcon_dev *edev; struct regulator *vbus_reg = NULL; int ret; int irq; - int utmi_mode = 0; - int x_major; - u32 reg; void __iomem *base; @@ -409,10 +481,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) } omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); - if (!omap) { - dev_err(dev, "not enough memory\n"); + if (!omap) return -ENOMEM; - } platform_set_drvdata(pdev, omap); @@ -448,58 +518,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) goto err0; } - reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION); - omap->revision = reg; - x_major = USBOTGSS_REVISION_XMAJOR(reg); - - /* Differentiate between OMAP5 and AM437x */ - switch (x_major) { - case USBOTGSS_REVISION_XMAJOR1: - case USBOTGSS_REVISION_XMAJOR2: - omap->irq_eoi_offset = 0; - omap->irq0_offset = 0; - omap->irqmisc_offset = 0; - omap->utmi_otg_offset = 0; - omap->debug_offset = 0; - break; - default: - /* Default to the latest revision */ - omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; - omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; - omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; - omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; - omap->debug_offset = USBOTGSS_DEBUG_OFFSET; - break; - } - - /* For OMAP5(ES2.0) and AM437x x_major is 2 even though there are - * changes in wrapper registers, Using dt compatible for aegis - */ - - if (of_device_is_compatible(node, "ti,am437x-dwc3")) { - omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; - omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; - omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; - omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; - omap->debug_offset = USBOTGSS_DEBUG_OFFSET; - } - - reg = dwc3_omap_read_utmi_status(omap); - - of_property_read_u32(node, "utmi-mode", &utmi_mode); - - switch (utmi_mode) { - case DWC3_OMAP_UTMI_MODE_SW: - reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - case DWC3_OMAP_UTMI_MODE_HW: - reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - default: - dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode); - } - - dwc3_omap_write_utmi_status(omap, reg); + dwc3_omap_map_offset(omap); + dwc3_omap_set_utmi_mode(omap); /* check the DMA Status */ reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); @@ -515,31 +535,9 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_enable_irqs(omap); - if (of_property_read_bool(node, "extcon")) { - edev = extcon_get_edev_by_phandle(dev, 0); - if (IS_ERR(edev)) { - dev_vdbg(dev, "couldn't get extcon device\n"); - ret = -EPROBE_DEFER; - goto err2; - } - - omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; - ret = extcon_register_interest(&omap->extcon_vbus_dev, - edev->name, "USB", &omap->vbus_nb); - if (ret < 0) - dev_vdbg(dev, "failed to register notifier for USB\n"); - omap->id_nb.notifier_call = dwc3_omap_id_notifier; - ret = extcon_register_interest(&omap->extcon_id_dev, edev->name, - "USB-HOST", &omap->id_nb); - if (ret < 0) - dev_vdbg(dev, - "failed to register notifier for USB-HOST\n"); - - if (extcon_get_cable_state(edev, "USB") == true) - dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); - if (extcon_get_cable_state(edev, "USB-HOST") == true) - dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); - } + ret = dwc3_omap_extcon_register(omap); + if (ret < 0) + goto err2; ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { @@ -576,9 +574,9 @@ static int dwc3_omap_remove(struct platform_device *pdev) if (omap->extcon_id_dev.edev) extcon_unregister_interest(&omap->extcon_id_dev); dwc3_omap_disable_irqs(omap); + device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core); return 0; } diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index a60bab7dfa0a..436fb08c40b8 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -103,10 +103,8 @@ static int dwc3_pci_probe(struct pci_dev *pci, struct device *dev = &pci->dev; glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); - if (!glue) { - dev_err(dev, "not enough memory\n"); + if (!glue) return -ENOMEM; - } glue->dev = dev; diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c new file mode 100644 index 000000000000..8c2e8eec80c2 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -0,0 +1,130 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> + +struct dwc3_qcom { + struct device *dev; + + struct clk *core_clk; + struct clk *iface_clk; + struct clk *sleep_clk; +}; + +static int dwc3_qcom_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct dwc3_qcom *qdwc; + int ret; + + qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL); + if (!qdwc) + return -ENOMEM; + + platform_set_drvdata(pdev, qdwc); + + qdwc->dev = &pdev->dev; + + qdwc->core_clk = devm_clk_get(qdwc->dev, "core"); + if (IS_ERR(qdwc->core_clk)) { + dev_err(qdwc->dev, "failed to get core clock\n"); + return PTR_ERR(qdwc->core_clk); + } + + qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface"); + if (IS_ERR(qdwc->iface_clk)) { + dev_dbg(qdwc->dev, "failed to get optional iface clock\n"); + qdwc->iface_clk = NULL; + } + + qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep"); + if (IS_ERR(qdwc->sleep_clk)) { + dev_dbg(qdwc->dev, "failed to get optional sleep clock\n"); + qdwc->sleep_clk = NULL; + } + + ret = clk_prepare_enable(qdwc->core_clk); + if (ret) { + dev_err(qdwc->dev, "failed to enable core clock\n"); + goto err_core; + } + + ret = clk_prepare_enable(qdwc->iface_clk); + if (ret) { + dev_err(qdwc->dev, "failed to enable optional iface clock\n"); + goto err_iface; + } + + ret = clk_prepare_enable(qdwc->sleep_clk); + if (ret) { + dev_err(qdwc->dev, "failed to enable optional sleep clock\n"); + goto err_sleep; + } + + ret = of_platform_populate(node, NULL, NULL, qdwc->dev); + if (ret) { + dev_err(qdwc->dev, "failed to register core - %d\n", ret); + goto err_clks; + } + + return 0; + +err_clks: + clk_disable_unprepare(qdwc->sleep_clk); +err_sleep: + clk_disable_unprepare(qdwc->iface_clk); +err_iface: + clk_disable_unprepare(qdwc->core_clk); +err_core: + return ret; +} + +static int dwc3_qcom_remove(struct platform_device *pdev) +{ + struct dwc3_qcom *qdwc = platform_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + + clk_disable_unprepare(qdwc->sleep_clk); + clk_disable_unprepare(qdwc->iface_clk); + clk_disable_unprepare(qdwc->core_clk); + + return 0; +} + +static const struct of_device_id of_dwc3_match[] = { + { .compatible = "qcom,dwc3" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_dwc3_match); + +static struct platform_driver dwc3_qcom_driver = { + .probe = dwc3_qcom_probe, + .remove = dwc3_qcom_remove, + .driver = { + .name = "qcom-dwc3", + .of_match_table = of_dwc3_match, + }, +}; + +module_platform_driver(dwc3_qcom_driver); + +MODULE_ALIAS("platform:qcom-dwc3"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer"); +MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c new file mode 100644 index 000000000000..c7602b5362ad --- /dev/null +++ b/drivers/usb/dwc3/dwc3-st.c @@ -0,0 +1,367 @@ +/** + * dwc3-st.c Support for dwc3 platform devices on ST Microelectronics platforms + * + * This is a small driver for the dwc3 to provide the glue logic + * to configure the controller. Tested on STi platforms. + * + * Copyright (C) 2014 Stmicroelectronics + * + * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> + * Contributors: Aymen Bouattay <aymen.bouattay@st.com> + * Peter Griffin <peter.griffin@linaro.org> + * + * 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. + * + * Inspired by dwc3-omap.c and dwc3-exynos.c. + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/usb/of.h> + +#include "core.h" +#include "io.h" + +/* glue registers */ +#define CLKRST_CTRL 0x00 +#define AUX_CLK_EN BIT(0) +#define SW_PIPEW_RESET_N BIT(4) +#define EXT_CFG_RESET_N BIT(8) +/* + * 1'b0 : The host controller complies with the xHCI revision 0.96 + * 1'b1 : The host controller complies with the xHCI revision 1.0 + */ +#define XHCI_REVISION BIT(12) + +#define USB2_VBUS_MNGMNT_SEL1 0x2C +/* + * For all fields in USB2_VBUS_MNGMNT_SEL1 + * 2’b00 : Override value from Reg 0x30 is selected + * 2’b01 : utmiotg_<signal_name> from usb3_top is selected + * 2’b10 : pipew_<signal_name> from PIPEW instance is selected + * 2’b11 : value is 1'b0 + */ +#define USB2_VBUS_REG30 0x0 +#define USB2_VBUS_UTMIOTG 0x1 +#define USB2_VBUS_PIPEW 0x2 +#define USB2_VBUS_ZERO 0x3 + +#define SEL_OVERRIDE_VBUSVALID(n) (n << 0) +#define SEL_OVERRIDE_POWERPRESENT(n) (n << 4) +#define SEL_OVERRIDE_BVALID(n) (n << 8) + +/* Static DRD configuration */ +#define USB3_CONTROL_MASK 0xf77 + +#define USB3_DEVICE_NOT_HOST BIT(0) +#define USB3_FORCE_VBUSVALID BIT(1) +#define USB3_DELAY_VBUSVALID BIT(2) +#define USB3_SEL_FORCE_OPMODE BIT(4) +#define USB3_FORCE_OPMODE(n) (n << 5) +#define USB3_SEL_FORCE_DPPULLDOWN2 BIT(8) +#define USB3_FORCE_DPPULLDOWN2 BIT(9) +#define USB3_SEL_FORCE_DMPULLDOWN2 BIT(10) +#define USB3_FORCE_DMPULLDOWN2 BIT(11) + +/** + * struct st_dwc3 - dwc3-st driver private structure + * @dev: device pointer + * @glue_base: ioaddr for the glue registers + * @regmap: regmap pointer for getting syscfg + * @syscfg_reg_off: usb syscfg control offset + * @dr_mode: drd static host/device config + * @rstc_pwrdn: rest controller for powerdown signal + * @rstc_rst: reset controller for softreset signal + */ + +struct st_dwc3 { + struct device *dev; + void __iomem *glue_base; + struct regmap *regmap; + int syscfg_reg_off; + enum usb_dr_mode dr_mode; + struct reset_control *rstc_pwrdn; + struct reset_control *rstc_rst; +}; + +static inline u32 st_dwc3_readl(void __iomem *base, u32 offset) +{ + return readl_relaxed(base + offset); +} + +static inline void st_dwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + writel_relaxed(value, base + offset); +} + +/** + * st_dwc3_drd_init: program the port + * @dwc3_data: driver private structure + * Description: this function is to program the port as either host or device + * according to the static configuration passed from devicetree. + * OTG and dual role are not yet supported! + */ +static int st_dwc3_drd_init(struct st_dwc3 *dwc3_data) +{ + u32 val; + int err; + + err = regmap_read(dwc3_data->regmap, dwc3_data->syscfg_reg_off, &val); + if (err) + return err; + + val &= USB3_CONTROL_MASK; + + switch (dwc3_data->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + + val &= ~(USB3_FORCE_VBUSVALID | USB3_DELAY_VBUSVALID + | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3) + | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2 + | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2); + + val |= USB3_DEVICE_NOT_HOST; + + dev_dbg(dwc3_data->dev, "Configuring as Device\n"); + break; + + case USB_DR_MODE_HOST: + + val &= ~(USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID + | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3) + | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2 + | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2); + + /* + * USB3_DELAY_VBUSVALID is ANDed with USB_C_VBUSVALID. Thus, + * when set to ‘0‘, it can delay the arrival of VBUSVALID + * information to VBUSVLDEXT2 input of the pico PHY. + * We don't want to do that so we set the bit to '1'. + */ + + val |= USB3_DELAY_VBUSVALID; + + dev_dbg(dwc3_data->dev, "Configuring as Host\n"); + break; + + default: + dev_err(dwc3_data->dev, "Unsupported mode of operation %d\n", + dwc3_data->dr_mode); + return -EINVAL; + } + + return regmap_write(dwc3_data->regmap, dwc3_data->syscfg_reg_off, val); +} + +/** + * st_dwc3_init: init the controller via glue logic + * @dwc3_data: driver private structure + */ +static void st_dwc3_init(struct st_dwc3 *dwc3_data) +{ + u32 reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL); + + reg |= AUX_CLK_EN | EXT_CFG_RESET_N | XHCI_REVISION; + reg &= ~SW_PIPEW_RESET_N; + st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg); + + /* configure mux for vbus, powerpresent and bvalid signals */ + reg = st_dwc3_readl(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1); + + reg |= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG) | + SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG) | + SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG); + + st_dwc3_writel(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1, reg); + + reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL); + reg |= SW_PIPEW_RESET_N; + st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg); +} + +static int st_dwc3_probe(struct platform_device *pdev) +{ + struct st_dwc3 *dwc3_data; + struct resource *res; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node, *child; + struct regmap *regmap; + int ret; + + dwc3_data = devm_kzalloc(dev, sizeof(*dwc3_data), GFP_KERNEL); + if (!dwc3_data) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg-glue"); + dwc3_data->glue_base = devm_ioremap_resource(dev, res); + if (IS_ERR(dwc3_data->glue_base)) + return PTR_ERR(dwc3_data->glue_base); + + regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg"); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + dma_set_coherent_mask(dev, dev->coherent_dma_mask); + dwc3_data->dev = dev; + dwc3_data->regmap = regmap; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "syscfg-reg"); + if (!res) { + ret = -ENXIO; + goto undo_platform_dev_alloc; + } + + dwc3_data->syscfg_reg_off = res->start; + + dev_vdbg(&pdev->dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n", + dwc3_data->glue_base, dwc3_data->syscfg_reg_off); + + dwc3_data->rstc_pwrdn = devm_reset_control_get(dev, "powerdown"); + if (IS_ERR(dwc3_data->rstc_pwrdn)) { + dev_err(&pdev->dev, "could not get power controller\n"); + ret = PTR_ERR(dwc3_data->rstc_pwrdn); + goto undo_platform_dev_alloc; + } + + /* Manage PowerDown */ + reset_control_deassert(dwc3_data->rstc_pwrdn); + + dwc3_data->rstc_rst = devm_reset_control_get(dev, "softreset"); + if (IS_ERR(dwc3_data->rstc_rst)) { + dev_err(&pdev->dev, "could not get reset controller\n"); + ret = PTR_ERR(dwc3_data->rstc_pwrdn); + goto undo_powerdown; + } + + /* Manage SoftReset */ + reset_control_deassert(dwc3_data->rstc_rst); + + child = of_get_child_by_name(node, "dwc3"); + if (!child) { + dev_err(&pdev->dev, "failed to find dwc3 core node\n"); + ret = -ENODEV; + goto undo_softreset; + } + + dwc3_data->dr_mode = of_usb_get_dr_mode(child); + + /* Allocate and initialize the core */ + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to add dwc3 core\n"); + goto undo_softreset; + } + + /* + * Configure the USB port as device or host according to the static + * configuration passed from DT. + * DRD is the only mode currently supported so this will be enhanced + * as soon as OTG is available. + */ + ret = st_dwc3_drd_init(dwc3_data); + if (ret) { + dev_err(dev, "drd initialisation failed\n"); + goto undo_softreset; + } + + /* ST glue logic init */ + st_dwc3_init(dwc3_data); + + platform_set_drvdata(pdev, dwc3_data); + return 0; + +undo_softreset: + reset_control_assert(dwc3_data->rstc_rst); +undo_powerdown: + reset_control_assert(dwc3_data->rstc_pwrdn); +undo_platform_dev_alloc: + platform_device_put(pdev); + return ret; +} + +static int st_dwc3_remove(struct platform_device *pdev) +{ + struct st_dwc3 *dwc3_data = platform_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + + reset_control_assert(dwc3_data->rstc_pwrdn); + reset_control_assert(dwc3_data->rstc_rst); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st_dwc3_suspend(struct device *dev) +{ + struct st_dwc3 *dwc3_data = dev_get_drvdata(dev); + + reset_control_assert(dwc3_data->rstc_pwrdn); + reset_control_assert(dwc3_data->rstc_rst); + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int st_dwc3_resume(struct device *dev) +{ + struct st_dwc3 *dwc3_data = dev_get_drvdata(dev); + int ret; + + pinctrl_pm_select_default_state(dev); + + reset_control_deassert(dwc3_data->rstc_pwrdn); + reset_control_deassert(dwc3_data->rstc_rst); + + ret = st_dwc3_drd_init(dwc3_data); + if (ret) { + dev_err(dev, "drd initialisation failed\n"); + return ret; + } + + /* ST glue logic init */ + st_dwc3_init(dwc3_data); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume); + +static const struct of_device_id st_dwc3_match[] = { + { .compatible = "st,stih407-dwc3" }, + { /* sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, st_dwc3_match); + +static struct platform_driver st_dwc3_driver = { + .probe = st_dwc3_probe, + .remove = st_dwc3_remove, + .driver = { + .name = "usb-st-dwc3", + .of_match_table = st_dwc3_match, + .pm = &st_dwc3_dev_pm_ops, + }, +}; + +module_platform_driver(st_dwc3_driver); + +MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); +MODULE_DESCRIPTION("DesignWare USB3 STi Glue Layer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 21a352079bc2..b35938777dde 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -31,6 +31,7 @@ #include <linux/usb/composite.h> #include "core.h" +#include "debug.h" #include "gadget.h" #include "io.h" @@ -65,7 +66,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, dep = dwc->eps[epnum]; if (dep->flags & DWC3_EP_BUSY) { - dev_vdbg(dwc->dev, "%s: still busy\n", dep->name); + dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name); return 0; } @@ -88,7 +89,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_STARTTRANSFER, ¶ms); if (ret < 0) { - dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed", + dep->name); return ret; } @@ -153,7 +155,8 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); else - dev_dbg(dwc->dev, "too early for delayed status\n"); + dwc3_trace(trace_dwc3_ep0, + "too early for delayed status"); return 0; } @@ -217,7 +220,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, spin_lock_irqsave(&dwc->lock, flags); if (!dep->endpoint.desc) { - dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + dwc3_trace(trace_dwc3_ep0, + "trying to queue request %p to disabled %s", request, dep->name); ret = -ESHUTDOWN; goto out; @@ -229,7 +233,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, goto out; } - dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n", + dwc3_trace(trace_dwc3_ep0, + "queueing request %p to %s length %d state '%s'", request, dep->name, request->length, dwc3_ep0_state_string(dwc->ep0state)); @@ -485,12 +490,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) addr = le16_to_cpu(ctrl->wValue); if (addr > 127) { - dev_dbg(dwc->dev, "invalid device address %d\n", addr); + dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr); return -EINVAL; } if (state == USB_STATE_CONFIGURED) { - dev_dbg(dwc->dev, "trying to set address when configured\n"); + dwc3_trace(trace_dwc3_ep0, + "trying to set address when configured"); return -EINVAL; } @@ -556,7 +562,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->resize_fifos = true; - dev_dbg(dwc->dev, "resize fifos flag SET\n"); + dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET"); } break; @@ -680,35 +686,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_GET_STATUS: - dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS\n"); ret = dwc3_ep0_handle_status(dwc, ctrl); break; case USB_REQ_CLEAR_FEATURE: - dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); break; case USB_REQ_SET_FEATURE: - dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); break; case USB_REQ_SET_ADDRESS: - dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS\n"); ret = dwc3_ep0_set_address(dwc, ctrl); break; case USB_REQ_SET_CONFIGURATION: - dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION\n"); ret = dwc3_ep0_set_config(dwc, ctrl); break; case USB_REQ_SET_SEL: - dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL\n"); ret = dwc3_ep0_set_sel(dwc, ctrl); break; case USB_REQ_SET_ISOCH_DELAY: - dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY\n"); ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); break; default: - dev_vdbg(dwc->dev, "Forwarding to gadget driver\n"); + dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver\n"); ret = dwc3_ep0_delegate_req(dwc, ctrl); break; } @@ -726,6 +732,8 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, if (!dwc->gadget_driver) goto out; + trace_dwc3_ctrl_req(ctrl); + len = le16_to_cpu(ctrl->wLength); if (!len) { dwc->three_stage_setup = false; @@ -774,7 +782,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { - dev_dbg(dwc->dev, "Setup Pending received\n"); + dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); if (r) dwc3_gadget_giveback(ep0, r, -ECONNRESET); @@ -834,7 +842,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr); if (ret < 0) { - dev_dbg(dwc->dev, "Invalid Test #%d\n", + dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d", dwc->test_mode_nr); dwc3_ep0_stall_and_restart(dwc); return; @@ -843,7 +851,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) - dev_dbg(dwc->dev, "Setup Pending received\n"); + dwc3_trace(trace_dwc3_ep0, "Setup Pending received\n"); dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); @@ -860,17 +868,17 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, switch (dwc->ep0state) { case EP0_SETUP_PHASE: - dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n"); + dwc3_trace(trace_dwc3_ep0, "Setup Phase"); dwc3_ep0_inspect_setup(dwc, event); break; case EP0_DATA_PHASE: - dev_vdbg(dwc->dev, "Data Phase\n"); + dwc3_trace(trace_dwc3_ep0, "Data Phase"); dwc3_ep0_complete_data(dwc, event); break; case EP0_STATUS_PHASE: - dev_vdbg(dwc->dev, "Status Phase\n"); + dwc3_trace(trace_dwc3_ep0, "Status Phase"); dwc3_ep0_complete_status(dwc, event); break; default: @@ -946,7 +954,7 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) { if (dwc->resize_fifos) { - dev_dbg(dwc->dev, "starting to resize fifos\n"); + dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs"); dwc3_gadget_resize_tx_fifos(dwc); dwc->resize_fifos = 0; } @@ -987,7 +995,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: - dev_vdbg(dwc->dev, "Control Data\n"); + dwc3_trace(trace_dwc3_ep0, "Control Data"); /* * We already have a DATA transfer in the controller's cache, @@ -1001,7 +1009,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_expect_in != event->endpoint_number) { struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; - dev_vdbg(dwc->dev, "Wrong direction for Data phase\n"); + dwc3_trace(trace_dwc3_ep0, + "Wrong direction for Data phase"); dwc3_ep0_end_control_data(dwc, dep); dwc3_ep0_stall_and_restart(dwc); return; @@ -1013,13 +1022,13 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; - dev_vdbg(dwc->dev, "Control Status\n"); + dwc3_trace(trace_dwc3_ep0, "Control Status"); dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status) { WARN_ON_ONCE(event->endpoint_number != 1); - dev_vdbg(dwc->dev, "Mass Storage delayed status\n"); + dwc3_trace(trace_dwc3_ep0, "Delayed Status"); return; } @@ -1032,7 +1041,7 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, { u8 epnum = event->endpoint_number; - dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", + dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'", dwc3_ep_event_string(event->endpoint_event), epnum >> 1, (epnum & 1) ? "in" : "out", dwc3_ep0_state_string(dwc->ep0state)); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index dab7927d1009..3818b26bfc05 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -30,6 +30,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include "debug.h" #include "core.h" #include "gadget.h" #include "io.h" @@ -266,107 +267,19 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", req, dep->name, req->request.actual, req->request.length, status); + trace_dwc3_gadget_giveback(req); spin_unlock(&dwc->lock); - req->request.complete(&dep->endpoint, &req->request); + usb_gadget_giveback_request(&dep->endpoint, &req->request); spin_lock(&dwc->lock); } -static const char *dwc3_gadget_ep_cmd_string(u8 cmd) -{ - switch (cmd) { - case DWC3_DEPCMD_DEPSTARTCFG: - return "Start New Configuration"; - case DWC3_DEPCMD_ENDTRANSFER: - return "End Transfer"; - case DWC3_DEPCMD_UPDATETRANSFER: - return "Update Transfer"; - case DWC3_DEPCMD_STARTTRANSFER: - return "Start Transfer"; - case DWC3_DEPCMD_CLEARSTALL: - return "Clear Stall"; - case DWC3_DEPCMD_SETSTALL: - return "Set Stall"; - case DWC3_DEPCMD_GETEPSTATE: - return "Get Endpoint State"; - case DWC3_DEPCMD_SETTRANSFRESOURCE: - return "Set Endpoint Transfer Resource"; - case DWC3_DEPCMD_SETEPCONFIG: - return "Set Endpoint Configuration"; - default: - return "UNKNOWN command"; - } -} - -static const char *dwc3_gadget_generic_cmd_string(u8 cmd) -{ - switch (cmd) { - case DWC3_DGCMD_SET_LMP: - return "Set LMP"; - case DWC3_DGCMD_SET_PERIODIC_PAR: - return "Set Periodic Parameters"; - case DWC3_DGCMD_XMIT_FUNCTION: - return "Transmit Function Wake Device Notification"; - case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO: - return "Set Scratchpad Buffer Array Address Lo"; - case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI: - return "Set Scratchpad Buffer Array Address Hi"; - case DWC3_DGCMD_SELECTED_FIFO_FLUSH: - return "Selected FIFO Flush"; - case DWC3_DGCMD_ALL_FIFO_FLUSH: - return "All FIFO Flush"; - case DWC3_DGCMD_SET_ENDPOINT_NRDY: - return "Set Endpoint NRDY"; - case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: - return "Run SoC Bus Loopback Test"; - default: - return "UNKNOWN"; - } -} - -static const char *dwc3_gadget_link_string(enum dwc3_link_state link_state) -{ - switch (link_state) { - case DWC3_LINK_STATE_U0: - return "U0"; - case DWC3_LINK_STATE_U1: - return "U1"; - case DWC3_LINK_STATE_U2: - return "U2"; - case DWC3_LINK_STATE_U3: - return "U3"; - case DWC3_LINK_STATE_SS_DIS: - return "SS.Disabled"; - case DWC3_LINK_STATE_RX_DET: - return "RX.Detect"; - case DWC3_LINK_STATE_SS_INACT: - return "SS.Inactive"; - case DWC3_LINK_STATE_POLL: - return "Polling"; - case DWC3_LINK_STATE_RECOV: - return "Recovery"; - case DWC3_LINK_STATE_HRESET: - return "Hot Reset"; - case DWC3_LINK_STATE_CMPLY: - return "Compliance"; - case DWC3_LINK_STATE_LPBK: - return "Loopback"; - case DWC3_LINK_STATE_RESET: - return "Reset"; - case DWC3_LINK_STATE_RESUME: - return "Resume"; - default: - return "UNKNOWN link state\n"; - } -} - -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) { u32 timeout = 500; u32 reg; - dev_vdbg(dwc->dev, "generic cmd '%s' [%d] param %08x\n", - dwc3_gadget_generic_cmd_string(cmd), cmd, param); + trace_dwc3_gadget_generic_cmd(cmd, param); dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); @@ -397,10 +310,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, u32 timeout = 500; u32 reg; - dev_vdbg(dwc->dev, "%s: cmd '%s' [%d] params %08x %08x %08x\n", - dep->name, - dwc3_gadget_ep_cmd_string(cmd), cmd, params->param0, - params->param1, params->param2); + trace_dwc3_gadget_ep_cmd(dep, cmd, params); dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); @@ -527,7 +437,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, dep->stream_capable = true; } - if (usb_endpoint_xfer_isoc(desc)) + if (!usb_endpoint_xfer_control(desc)) params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN; /* @@ -789,17 +699,16 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, { struct dwc3_request *req; struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; req = kzalloc(sizeof(*req), gfp_flags); - if (!req) { - dev_err(dwc->dev, "not enough memory\n"); + if (!req) return NULL; - } req->epnum = dep->number; req->dep = dep; + trace_dwc3_alloc_request(req); + return &req->request; } @@ -808,6 +717,7 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, { struct dwc3_request *req = to_dwc3_request(request); + trace_dwc3_free_request(req); kfree(req); } @@ -889,6 +799,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id); trb->ctrl |= DWC3_TRB_CTRL_HWO; + + trace_dwc3_prepare_trb(dep, trb); } /* @@ -1225,16 +1137,18 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, int ret; + spin_lock_irqsave(&dwc->lock, flags); if (!dep->endpoint.desc) { dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", request, ep->name); + spin_unlock_irqrestore(&dwc->lock, flags); return -ESHUTDOWN; } dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", request, ep->name, request->length); + trace_dwc3_ep_queue(req); - spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_queue(dep, req); spin_unlock_irqrestore(&dwc->lock, flags); @@ -1253,6 +1167,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, unsigned long flags; int ret = 0; + trace_dwc3_ep_dequeue(req); + spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(r, &dep->request_list, list) { @@ -1743,11 +1659,8 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, u8 epnum = (i << 1) | (!!direction); dep = kzalloc(sizeof(*dep), GFP_KERNEL); - if (!dep) { - dev_err(dwc->dev, "can't allocate endpoint %d\n", - epnum); + if (!dep) return -ENOMEM; - } dep->dwc = dwc; dep->number = epnum; @@ -1846,6 +1759,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int s_pkt = 0; unsigned int trb_status; + trace_dwc3_complete_trb(dep, trb); + if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) /* * We continue despite the error. There is not much we @@ -1971,8 +1886,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, } static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, - struct dwc3_ep *dep, const struct dwc3_event_depevt *event, - int start_new) + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) { unsigned status = 0; int clean_busy; @@ -2021,9 +1935,6 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (!(dep->flags & DWC3_EP_ENABLED)) return; - dev_vdbg(dwc->dev, "%s: %s\n", dep->name, - dwc3_ep_event_string(event->endpoint_event)); - if (epnum == 0 || epnum == 1) { dwc3_ep0_interrupt(dwc, event); return; @@ -2039,16 +1950,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, return; } - dwc3_endpoint_transfer_complete(dwc, dep, event, 1); + dwc3_endpoint_transfer_complete(dwc, dep, event); break; case DWC3_DEPEVT_XFERINPROGRESS: - if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { - dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n", - dep->name); - return; - } - - dwc3_endpoint_transfer_complete(dwc, dep, event, 0); + dwc3_endpoint_transfer_complete(dwc, dep, event); break; case DWC3_DEPEVT_XFERNOTREADY: if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { @@ -2216,8 +2121,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { int reg; - dev_vdbg(dwc->dev, "%s\n", __func__); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); @@ -2236,8 +2139,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { u32 reg; - dev_vdbg(dwc->dev, "%s\n", __func__); - /* * WORKAROUND: DWC3 revisions <1.88a have an issue which * would cause a missing Disconnect Event if there's a @@ -2322,8 +2223,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) u32 reg; u8 speed; - dev_vdbg(dwc->dev, "%s\n", __func__); - reg = dwc3_readl(dwc->regs, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; dwc->speed = speed; @@ -2421,8 +2320,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) { - dev_vdbg(dwc->dev, "%s\n", __func__); - /* * TODO take core out of low power mode when that's * implemented. @@ -2527,10 +2424,6 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, break; } - dev_vdbg(dwc->dev, "link change: %s [%d] -> %s [%d]\n", - dwc3_gadget_link_string(dwc->link_state), - dwc->link_state, dwc3_gadget_link_string(next), next); - dwc->link_state = next; } @@ -2607,6 +2500,8 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, static void dwc3_process_event_entry(struct dwc3 *dwc, const union dwc3_event *event) { + trace_dwc3_event(event->raw); + /* Endpoint IRQ, handle it and return early */ if (event->type.is_devspec == 0) { /* depevt */ @@ -2760,7 +2655,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL); if (!dwc->setup_buf) { - dev_err(dwc->dev, "failed to allocate setup buffer\n"); ret = -ENOMEM; goto err2; } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index a0ee75b68a80..178ad8982206 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -103,60 +103,4 @@ static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) return DWC3_DEPCMD_GET_RSC_IDX(res_id); } -/** - * dwc3_gadget_event_string - returns event name - * @event: the event code - */ -static inline const char *dwc3_gadget_event_string(u8 event) -{ - switch (event) { - case DWC3_DEVICE_EVENT_DISCONNECT: - return "Disconnect"; - case DWC3_DEVICE_EVENT_RESET: - return "Reset"; - case DWC3_DEVICE_EVENT_CONNECT_DONE: - return "Connection Done"; - case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: - return "Link Status Change"; - case DWC3_DEVICE_EVENT_WAKEUP: - return "WakeUp"; - case DWC3_DEVICE_EVENT_EOPF: - return "End-Of-Frame"; - case DWC3_DEVICE_EVENT_SOF: - return "Start-Of-Frame"; - case DWC3_DEVICE_EVENT_ERRATIC_ERROR: - return "Erratic Error"; - case DWC3_DEVICE_EVENT_CMD_CMPL: - return "Command Complete"; - case DWC3_DEVICE_EVENT_OVERFLOW: - return "Overflow"; - } - - return "UNKNOWN"; -} - -/** - * dwc3_ep_event_string - returns event name - * @event: then event code - */ -static inline const char *dwc3_ep_event_string(u8 event) -{ - switch (event) { - case DWC3_DEPEVT_XFERCOMPLETE: - return "Transfer Complete"; - case DWC3_DEPEVT_XFERINPROGRESS: - return "Transfer In-Progress"; - case DWC3_DEPEVT_XFERNOTREADY: - return "Transfer Not Ready"; - case DWC3_DEPEVT_RXTXFIFOEVT: - return "FIFO"; - case DWC3_DEPEVT_STREAMEVT: - return "Stream"; - case DWC3_DEPEVT_EPCMDCMPLT: - return "Endpoint Command Complete"; - } - - return "UNKNOWN"; -} - #endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 32db328cc769..dcb8ca084598 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -16,12 +16,14 @@ */ #include <linux/platform_device.h> +#include <linux/usb/xhci_pdriver.h> #include "core.h" int dwc3_host_init(struct dwc3 *dwc) { struct platform_device *xhci; + struct usb_xhci_pdata pdata; int ret; xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); @@ -46,6 +48,18 @@ int dwc3_host_init(struct dwc3 *dwc) goto err1; } + memset(&pdata, 0, sizeof(pdata)); + +#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE + pdata.usb3_lpm_capable = 1; +#endif + + ret = platform_device_add_data(xhci, &pdata, sizeof(pdata)); + if (ret) { + dev_err(dwc->dev, "couldn't add platform data to xHCI device\n"); + goto err1; + } + ret = platform_device_add(xhci); if (ret) { dev_err(dwc->dev, "failed to register xHCI device\n"); diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index d94441c14d8c..6a79c8e66bbc 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -20,27 +20,51 @@ #define __DRIVERS_USB_DWC3_IO_H #include <linux/io.h> - +#include "trace.h" +#include "debug.h" #include "core.h" static inline u32 dwc3_readl(void __iomem *base, u32 offset) { + u32 offs = offset - DWC3_GLOBALS_REGS_START; + u32 value; + /* * We requested the mem region starting from the Globals address * space, see dwc3_probe in core.c. * However, the offsets are given starting from xHCI address space. */ - return readl(base + (offset - DWC3_GLOBALS_REGS_START)); + value = readl(base + offs); + + /* + * When tracing we want to make it easy to find the correct address on + * documentation, so we revert it back to the proper addresses, the + * same way they are described on SNPS documentation + */ + dwc3_trace(trace_dwc3_readl, "addr %p value %08x", + base - DWC3_GLOBALS_REGS_START + offset, value); + + return value; } static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) { + u32 offs = offset - DWC3_GLOBALS_REGS_START; + /* * We requested the mem region starting from the Globals address * space, see dwc3_probe in core.c. * However, the offsets are given starting from xHCI address space. */ - writel(value, base + (offset - DWC3_GLOBALS_REGS_START)); + writel(value, base + offs); + + /* + * When tracing we want to make it easy to find the correct address on + * documentation, so we revert it back to the proper addresses, the + * same way they are described on SNPS documentation + */ + dwc3_trace(trace_dwc3_writel, "addr %p value %08x", + base - DWC3_GLOBALS_REGS_START + offset, value); } #endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c new file mode 100644 index 000000000000..6cd166412ad0 --- /dev/null +++ b/drivers/usb/dwc3/trace.c @@ -0,0 +1,19 @@ +/** + * trace.c - DesignWare USB3 DRD Controller Trace Support + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi <balbi@ti.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h new file mode 100644 index 000000000000..78aff1da089a --- /dev/null +++ b/drivers/usb/dwc3/trace.h @@ -0,0 +1,220 @@ +/** + * trace.h - DesignWare USB3 DRD Controller Trace Support + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi <balbi@ti.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM dwc3 + +#if !defined(__DWC3_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __DWC3_TRACE_H + +#include <linux/types.h> +#include <linux/tracepoint.h> +#include <asm/byteorder.h> +#include "core.h" +#include "debug.h" + +DECLARE_EVENT_CLASS(dwc3_log_msg, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry(__dynamic_array(char, msg, DWC3_MSG_MAX)), + TP_fast_assign( + vsnprintf(__get_str(msg), DWC3_MSG_MAX, vaf->fmt, *vaf->va); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(dwc3_log_msg, dwc3_readl, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(dwc3_log_msg, dwc3_writel, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(dwc3_log_msg, dwc3_ep0, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DECLARE_EVENT_CLASS(dwc3_log_event, + TP_PROTO(u32 event), + TP_ARGS(event), + TP_STRUCT__entry( + __field(u32, event) + ), + TP_fast_assign( + __entry->event = event; + ), + TP_printk("event %08x\n", __entry->event) +); + +DEFINE_EVENT(dwc3_log_event, dwc3_event, + TP_PROTO(u32 event), + TP_ARGS(event) +); + +DECLARE_EVENT_CLASS(dwc3_log_ctrl, + TP_PROTO(struct usb_ctrlrequest *ctrl), + TP_ARGS(ctrl), + TP_STRUCT__entry( + __field(struct usb_ctrlrequest *, ctrl) + ), + TP_fast_assign( + __entry->ctrl = ctrl; + ), + TP_printk("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %d", + __entry->ctrl->bRequestType, __entry->ctrl->bRequest, + le16_to_cpu(__entry->ctrl->wValue), le16_to_cpu(__entry->ctrl->wIndex), + le16_to_cpu(__entry->ctrl->wLength) + ) +); + +DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req, + TP_PROTO(struct usb_ctrlrequest *ctrl), + TP_ARGS(ctrl) +); + +DECLARE_EVENT_CLASS(dwc3_log_request, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req), + TP_STRUCT__entry( + __field(struct dwc3_request *, req) + ), + TP_fast_assign( + __entry->req = req; + ), + TP_printk("%s: req %p length %u/%u ==> %d", + __entry->req->dep->name, __entry->req, + __entry->req->request.actual, __entry->req->request.length, + __entry->req->request.status + ) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_alloc_request, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_free_request, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_ep_queue, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_ep_dequeue, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DECLARE_EVENT_CLASS(dwc3_log_generic_cmd, + TP_PROTO(unsigned int cmd, u32 param), + TP_ARGS(cmd, param), + TP_STRUCT__entry( + __field(unsigned int, cmd) + __field(u32, param) + ), + TP_fast_assign( + __entry->cmd = cmd; + __entry->param = param; + ), + TP_printk("cmd '%s' [%d] param %08x\n", + dwc3_gadget_generic_cmd_string(__entry->cmd), + __entry->cmd, __entry->param + ) +); + +DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd, + TP_PROTO(unsigned int cmd, u32 param), + TP_ARGS(cmd, param) +); + +DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, + TP_PROTO(struct dwc3_ep *dep, unsigned int cmd, + struct dwc3_gadget_ep_cmd_params *params), + TP_ARGS(dep, cmd, params), + TP_STRUCT__entry( + __field(struct dwc3_ep *, dep) + __field(unsigned int, cmd) + __field(struct dwc3_gadget_ep_cmd_params *, params) + ), + TP_fast_assign( + __entry->dep = dep; + __entry->cmd = cmd; + __entry->params = params; + ), + TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x\n", + __entry->dep->name, dwc3_gadget_ep_cmd_string(__entry->cmd), + __entry->cmd, __entry->params->param0, + __entry->params->param1, __entry->params->param2 + ) +); + +DEFINE_EVENT(dwc3_log_gadget_ep_cmd, dwc3_gadget_ep_cmd, + TP_PROTO(struct dwc3_ep *dep, unsigned int cmd, + struct dwc3_gadget_ep_cmd_params *params), + TP_ARGS(dep, cmd, params) +); + +DECLARE_EVENT_CLASS(dwc3_log_trb, + TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb), + TP_ARGS(dep, trb), + TP_STRUCT__entry( + __field(struct dwc3_ep *, dep) + __field(struct dwc3_trb *, trb) + ), + TP_fast_assign( + __entry->dep = dep; + __entry->trb = trb; + ), + TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x\n", + __entry->dep->name, __entry->trb, __entry->trb->bph, + __entry->trb->bpl, __entry->trb->size, __entry->trb->ctrl + ) +); + +DEFINE_EVENT(dwc3_log_trb, dwc3_prepare_trb, + TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb), + TP_ARGS(dep, trb) +); + +DEFINE_EVENT(dwc3_log_trb, dwc3_complete_trb, + TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb), + TP_ARGS(dep, trb) +); + +#endif /* __DWC3_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include <trace/define_trace.h> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ba18e9c110cc..c4880fc0d86e 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -127,368 +127,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS a module parameter as well. If unsure, say 2. -# -# USB Peripheral Controller Support -# -# The order here is alphabetical, except that integrated controllers go -# before discrete ones so they will be the initial/default value: -# - integrated/SOC controllers first -# - licensed IP used in both SOC and discrete versions -# - discrete ones (including all PCI-only controllers) -# - debug/dummy gadget+hcd is last. -# -menu "USB Peripheral Controller" - -# -# Integrated controllers -# - -config USB_AT91 - tristate "Atmel AT91 USB Device Port" - depends on ARCH_AT91 - help - Many Atmel AT91 processors (such as the AT91RM2000) have a - full speed USB Device Port with support for five configurable - endpoints (plus endpoint zero). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "at91_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_LPC32XX - tristate "LPC32XX USB Peripheral Controller" - depends on ARCH_LPC32XX && I2C - select USB_ISP1301 - help - This option selects the USB device controller in the LPC32xx SoC. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "lpc32xx_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_ATMEL_USBA - tristate "Atmel USBA" - depends on AVR32 || ARCH_AT91 - help - USBA is the integrated high-speed USB Device controller on - the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. - -config USB_BCM63XX_UDC - tristate "Broadcom BCM63xx Peripheral Controller" - depends on BCM63XX - help - Many Broadcom BCM63xx chipsets (such as the BCM6328) have a - high speed USB Device Port with support for four fixed endpoints - (plus endpoint zero). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "bcm63xx_udc". - -config USB_FSL_USB2 - tristate "Freescale Highspeed USB DR Peripheral Controller" - depends on FSL_SOC || ARCH_MXC - select USB_FSL_MPH_DR_OF if OF - help - Some of Freescale PowerPC and i.MX processors have a High Speed - Dual-Role(DR) USB controller, which supports device mode. - - The number of programmable endpoints is different through - SOC revisions. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "fsl_usb2_udc" and force - all gadget drivers to also be dynamically linked. - -config USB_FUSB300 - tristate "Faraday FUSB300 USB Peripheral Controller" - depends on !PHYS_ADDR_T_64BIT && HAS_DMA - help - Faraday usb device controller FUSB300 driver - -config USB_FOTG210_UDC - depends on HAS_DMA - tristate "Faraday FOTG210 USB Peripheral Controller" - help - Faraday USB2.0 OTG controller which can be configured as - high speed or full speed USB device. This driver supppors - Bulk Transfer so far. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "fotg210_udc". - -config USB_GR_UDC - tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" - depends on HAS_DMA - help - Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB - VHDL IP core library. - -config USB_OMAP - tristate "OMAP USB Device Controller" - depends on ARCH_OMAP1 - depends on ISP1301_OMAP || !(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 - endpoints (plus endpoint zero). This driver supports the - controller in the OMAP 1611, and should work with controllers - in other OMAP processors too, given minor tweaks. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "omap_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_PXA25X - tristate "PXA 25x or IXP 4xx" - depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX - help - Intel's PXA 25x series XScale ARM-5TE processors include - an integrated full speed USB 1.1 device controller. The - controller in the IXP 4xx series is register-compatible. - - It has fifteen fixed-function endpoints, as well as endpoint - zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "pxa25x_udc" and force all - gadget drivers to also be dynamically linked. - -# if there's only one gadget driver, using only two bulk endpoints, -# don't waste memory for the other endpoints -config USB_PXA25X_SMALL - depends on USB_PXA25X - bool - default n if USB_ETH_RNDIS - default y if USB_ZERO - default y if USB_ETH - default y if USB_G_SERIAL - -config USB_R8A66597 - tristate "Renesas R8A66597 USB Peripheral Controller" - depends on HAS_DMA - help - R8A66597 is a discrete USB host and peripheral controller chip that - supports both full and high speed USB 2.0 data transfers. - It has nine configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "r8a66597_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_RENESAS_USBHS_UDC - tristate 'Renesas USBHS controller' - depends on USB_RENESAS_USBHS - help - Renesas USBHS is a discrete USB host and peripheral controller chip - that supports both full and high speed USB 2.0 data transfers. - It has nine or more configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "renesas_usbhs" and force all - gadget drivers to also be dynamically linked. - -config USB_PXA27X - tristate "PXA 27x" - help - Intel's PXA 27x series XScale ARM v5TE processors include - an integrated full speed USB 1.1 device controller. - - It has up to 23 endpoints, as well as endpoint zero (for - control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "pxa27x_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_S3C2410 - tristate "S3C2410 USB Device Controller" - depends on ARCH_S3C24XX - help - Samsung's S3C2410 is an ARM-4 processor with an integrated - full speed USB 1.1 device controller. It has 4 configurable - endpoints, as well as endpoint zero (for control transfers). - - This driver has been tested on the S3C2410, S3C2412, and - S3C2440 processors. - -config USB_S3C2410_DEBUG - boolean "S3C2410 udc debug messages" - depends on USB_S3C2410 - -config USB_S3C_HSUDC - tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" - depends on ARCH_S3C24XX - help - Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC - integrated with dual speed USB 2.0 device controller. It has - 8 endpoints, as well as endpoint zero. - - This driver has been tested on S3C2416 and S3C2450 processors. - -config USB_MV_UDC - tristate "Marvell USB2.0 Device Controller" - depends on HAS_DMA - help - Marvell Socs (including PXA and MMP series) include a high speed - USB2.0 OTG controller, which can be configured as high speed or - full speed USB peripheral. - -config USB_MV_U3D - depends on HAS_DMA - tristate "MARVELL PXA2128 USB 3.0 controller" - help - MARVELL PXA2128 Processor series include a super speed USB3.0 device - controller, which support super speed USB peripheral. - -# -# Controllers available in both integrated and discrete versions -# - -config USB_M66592 - tristate "Renesas M66592 USB Peripheral Controller" - help - M66592 is a discrete USB peripheral controller chip that - supports both full and high speed USB 2.0 data transfers. - It has seven configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "m66592_udc" and force all - gadget drivers to also be dynamically linked. - -# -# Controllers available only in discrete form (and all PCI controllers) -# - -config USB_AMD5536UDC - tristate "AMD5536 UDC" - depends on PCI - help - The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. - It is a USB Highspeed DMA capable USB device controller. Beside ep0 - it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). - The UDC port supports OTG operation, and may be used as a host port - if it's not being used to implement peripheral or OTG roles. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "amd5536udc" and force all - gadget drivers to also be dynamically linked. - -config USB_FSL_QE - tristate "Freescale QE/CPM USB Device Controller" - depends on FSL_SOC && (QUICC_ENGINE || CPM) - help - Some of Freescale PowerPC processors have a Full Speed - QE/CPM2 USB controller, which support device mode with 4 - programmable endpoints. This driver supports the - controller in the MPC8360 and MPC8272, and should work with - controllers having QE or CPM2, given minor tweaks. - - Set CONFIG_USB_GADGET to "m" to build this driver as a - dynamically linked module called "fsl_qe_udc". - -config USB_NET2272 - tristate "PLX NET2272" - help - PLX NET2272 is a USB peripheral controller which supports - both full and high speed USB 2.0 data transfers. - - It has three configurable endpoints, as well as endpoint zero - (for control transfer). - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "net2272" and force all - gadget drivers to also be dynamically linked. - -config USB_NET2272_DMA - boolean "Support external DMA controller" - depends on USB_NET2272 && HAS_DMA - help - The NET2272 part can optionally support an external DMA - controller, but your board has to have support in the - driver itself. - - If unsure, say "N" here. The driver works fine in PIO mode. - -config USB_NET2280 - tristate "NetChip 228x" - depends on PCI - help - NetChip 2280 / 2282 is a PCI based USB peripheral controller which - supports both full and high speed USB 2.0 data transfers. - - It has six configurable endpoints, as well as endpoint zero - (for control transfers) and several endpoints with dedicated - functions. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "net2280" and force all - gadget drivers to also be dynamically linked. - -config USB_GOKU - tristate "Toshiba TC86C001 'Goku-S'" - depends on PCI - help - The Toshiba TC86C001 is a PCI device which includes controllers - for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). - - The device controller has three configurable (bulk or interrupt) - endpoints, plus endpoint zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "goku_udc" and to force all - gadget drivers to also be dynamically linked. - -config USB_EG20T - tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" - depends on PCI - help - This is a USB device driver for EG20T PCH. - EG20T PCH is the platform controller hub that is used in Intel's - general embedded platform. EG20T PCH has USB device interface. - Using this interface, it is able to access system devices connected - to USB device. - This driver enables USB device function. - USB device is a USB peripheral controller which - supports both full and high speed USB 2.0 data transfers. - This driver supports both control transfer and bulk transfer modes. - This driver dose not support interrupt transfer or isochronous - transfer modes. - - This driver also can be used for LAPIS Semiconductor's ML7213 which is - for IVI(In-Vehicle Infotainment) use. - ML7831 is for general purpose use. - ML7213/ML7831 is companion chip for Intel Atom E6xx series. - ML7213/ML7831 is completely compatible for Intel EG20T PCH. - -# -# LAST -- dummy/emulated controller -# - -config USB_DUMMY_HCD - tristate "Dummy HCD (DEVELOPMENT)" - depends on USB=y || (USB=m && USB_GADGET=m) - help - This host controller driver emulates USB, looping all data transfer - requests back to a USB "gadget driver" in the same host. The host - side is the master; the gadget side is the slave. Gadget drivers - can be high, full, or low speed; and they have access to endpoints - like those from NET2280, PXA2xx, or SA1100 hardware. - - This may help in some stages of creating a driver to embed in a - Linux device, since it lets you debug several parts of the gadget - driver without its hardware or drivers being involved. - - Since such a gadget side driver needs to interoperate with a host - side Linux-USB device driver, this may help to debug both sides - of a USB protocol stack. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "dummy_hcd" and force all - gadget drivers to also be dynamically linked. - -# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears -# first and will be selected by default. - -endmenu +source "drivers/usb/gadget/udc/Kconfig" # # USB Gadget Drivers @@ -542,6 +181,15 @@ config USB_F_MASS_STORAGE config USB_F_FS tristate +config USB_F_UAC1 + tristate + +config USB_F_UAC2 + tristate + +config USB_F_UVC + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -714,466 +362,7 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. -config USB_ZERO - tristate "Gadget Zero (DEVELOPMENT)" - select USB_LIBCOMPOSITE - select USB_F_SS_LB - help - Gadget Zero is a two-configuration device. It either sinks and - sources bulk data; or it loops back a configurable number of - transfers. It also implements control requests, for "chapter 9" - conformance. The driver needs only two bulk-capable endpoints, so - it can work on top of most device-side usb controllers. It's - useful for testing, and is also a working example showing how - USB "gadget drivers" can be written. - - Make this be the first driver you try using on top of any new - USB peripheral controller driver. Then you can use host-side - test software, like the "usbtest" driver, to put your hardware - and its driver through a basic set of functional tests. - - Gadget Zero also works with the host-side "usb-skeleton" driver, - and with many kinds of host-side test software. You may need - to tweak product and vendor IDs before host software knows about - this device, and arrange to select an appropriate configuration. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_zero". - -config USB_ZERO_HNPTEST - boolean "HNP Test Device" - depends on USB_ZERO && USB_OTG - help - You can configure this device to enumerate using the device - identifiers of the USB-OTG test device. That means that when - this gadget connects to another OTG device, with this one using - the "B-Peripheral" role, that device will use HNP to let this - one serve as the USB host instead (in the "B-Host" role). - -config USB_AUDIO - tristate "Audio Gadget" - depends on SND - select USB_LIBCOMPOSITE - select SND_PCM - help - This Gadget Audio driver is compatible with USB Audio Class - specification 2.0. It implements 1 AudioControl interface, - 1 AudioStreaming Interface each for USB-OUT and USB-IN. - Number of channels, sample rate and sample size can be - specified as module parameters. - This driver doesn't expect any real Audio codec to be present - on the device - the audio streams are simply sinked to and - sourced from a virtual ALSA sound card created. The user-space - application may choose to do whatever it wants with the data - received from the USB Host and choose to provide whatever it - wants as audio data to the USB Host. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_audio". - -config GADGET_UAC1 - bool "UAC 1.0 (Legacy)" - depends on USB_AUDIO - help - If you instead want older UAC Spec-1.0 driver that also has audio - paths hardwired to the Audio codec chip on-board and doesn't work - without one. - -config USB_ETH - tristate "Ethernet Gadget (with CDC Ethernet support)" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_ETHER - select USB_F_ECM - select USB_F_SUBSET - select CRC32 - help - This driver implements Ethernet style communication, in one of - several ways: - - - The "Communication Device Class" (CDC) Ethernet Control Model. - That protocol is often avoided with pure Ethernet adapters, in - favor of simpler vendor-specific hardware, but is widely - supported by firmware for smart network devices. - - - On hardware can't implement that protocol, a simple CDC subset - is used, placing fewer demands on USB. - - - CDC Ethernet Emulation Model (EEM) is a newer standard that has - a simpler interface that can be used by more USB hardware. - - RNDIS support is an additional option, more demanding than than - subset. - - Within the USB device, this gadget driver exposes a network device - "usbX", where X depends on what other networking devices you have. - Treat it like a two-node Ethernet link: host, and gadget. - - The Linux-USB host-side "usbnet" driver interoperates with this - driver, so that deep I/O queues can be supported. On 2.4 kernels, - use "CDCEther" instead, if you're using the CDC option. That CDC - mode should also interoperate with standard CDC Ethernet class - drivers on other host operating systems. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_ether". - -config USB_ETH_RNDIS - bool "RNDIS support" - depends on USB_ETH - select USB_LIBCOMPOSITE - select USB_F_RNDIS - default y - help - Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, - and Microsoft provides redistributable binary RNDIS drivers for - older versions of Windows. - - If you say "y" here, the Ethernet gadget driver will try to provide - a second device configuration, supporting RNDIS to talk to such - Microsoft USB hosts. - - To make MS-Windows work with this, use Documentation/usb/linux.inf - as the "driver info file". For versions of MS-Windows older than - XP, you'll need to download drivers from Microsoft's website; a URL - is given in comments found in that info file. - -config USB_ETH_EEM - bool "Ethernet Emulation Model (EEM) support" - depends on USB_ETH - select USB_LIBCOMPOSITE - select USB_F_EEM - default n - help - CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM - and therefore can be supported by more hardware. Technically ECM and - EEM are designed for different applications. The ECM model extends - the network interface to the target (e.g. a USB cable modem), and the - EEM model is for mobile devices to communicate with hosts using - ethernet over USB. For Linux gadgets, however, the interface with - the host is the same (a usbX device), so the differences are minimal. - - If you say "y" here, the Ethernet gadget driver will use the EEM - protocol rather than ECM. If unsure, say "n". - -config USB_G_NCM - tristate "Network Control Model (NCM) support" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_ETHER - select USB_F_NCM - select CRC32 - help - This driver implements USB CDC NCM subclass standard. NCM is - an advanced protocol for Ethernet encapsulation, allows grouping - of several ethernet frames into one USB transfer and different - alignment possibilities. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_ncm". - -config USB_GADGETFS - tristate "Gadget Filesystem" - help - This driver provides a filesystem based API that lets user mode - programs implement a single-configuration USB device, including - endpoint I/O and control requests that don't relate to enumeration. - All endpoints, transfer speeds, and transfer types supported by - the hardware are available, through read() and write() calls. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "gadgetfs". - -config USB_FUNCTIONFS - tristate "Function Filesystem" - select USB_LIBCOMPOSITE - select USB_F_FS - select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) - help - The Function Filesystem (FunctionFS) lets one create USB - composite functions in user space in the same way GadgetFS - lets one create USB gadgets in user space. This allows creation - of composite gadgets such that some of the functions are - implemented in kernel space (for instance Ethernet, serial or - mass storage) and other are implemented in user space. - - If you say "y" or "m" here you will be able what kind of - configurations the gadget will provide. - - Say "y" to link the driver statically, or "m" to build - a dynamically linked module called "g_ffs". - -config USB_FUNCTIONFS_ETH - bool "Include configuration with CDC ECM (Ethernet)" - depends on USB_FUNCTIONFS && NET - select USB_U_ETHER - select USB_F_ECM - select USB_F_SUBSET - help - Include a configuration with CDC ECM function (Ethernet) and the - Function Filesystem. - -config USB_FUNCTIONFS_RNDIS - bool "Include configuration with RNDIS (Ethernet)" - depends on USB_FUNCTIONFS && NET - select USB_U_ETHER - select USB_F_RNDIS - help - Include a configuration with RNDIS function (Ethernet) and the Filesystem. - -config USB_FUNCTIONFS_GENERIC - bool "Include 'pure' configuration" - depends on USB_FUNCTIONFS - help - Include a configuration with the Function Filesystem alone with - no Ethernet interface. - -config USB_MASS_STORAGE - tristate "Mass Storage Gadget" - depends on BLOCK - select USB_LIBCOMPOSITE - select USB_F_MASS_STORAGE - help - The Mass Storage Gadget acts as a USB Mass Storage disk drive. - As its storage repository it can use a regular file or a block - device (in much the same way as the "loop" device driver), - specified as a module parameter or sysfs option. - - This driver is a replacement for now removed File-backed - Storage Gadget (g_file_storage). - - Say "y" to link the driver statically, or "m" to build - a dynamically linked module called "g_mass_storage". - -config USB_GADGET_TARGET - tristate "USB Gadget Target Fabric Module" - depends on TARGET_CORE - select USB_LIBCOMPOSITE - help - This fabric is an USB gadget. Two USB protocols are supported that is - BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is - advertised on alternative interface 0 (primary) and UAS is on - alternative interface 1. Both protocols can work on USB2.0 and USB3.0. - UAS utilizes the USB 3.0 feature called streams support. - -config USB_G_SERIAL - tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" - depends on TTY - select USB_U_SERIAL - select USB_F_ACM - select USB_F_SERIAL - select USB_F_OBEX - select USB_LIBCOMPOSITE - help - The Serial Gadget talks to the Linux-USB generic serial driver. - This driver supports a CDC-ACM module option, which can be used - to interoperate with MS-Windows hosts or with the Linux-USB - "cdc-acm" driver. - - This driver also supports a CDC-OBEX option. You will need a - user space OBEX server talking to /dev/ttyGS*, since the kernel - itself doesn't implement the OBEX protocol. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_serial". - - For more information, see Documentation/usb/gadget_serial.txt - which includes instructions and a "driver info file" needed to - make MS-Windows work with CDC ACM. - -config USB_MIDI_GADGET - tristate "MIDI Gadget" - depends on SND - select USB_LIBCOMPOSITE - select SND_RAWMIDI - help - The MIDI Gadget acts as a USB Audio device, with one MIDI - input and one MIDI output. These MIDI jacks appear as - a sound "card" in the ALSA sound system. Other MIDI - connections can then be made on the gadget system, using - ALSA's aconnect utility etc. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_midi". - -config USB_G_PRINTER - tristate "Printer Gadget" - select USB_LIBCOMPOSITE - help - The Printer Gadget channels data between the USB host and a - userspace program driving the print engine. The user space - program reads and writes the device file /dev/g_printer to - receive or send printer data. It can use ioctl calls to - the device file to get or set printer status. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_printer". - - For more information, see Documentation/usb/gadget_printer.txt - which includes sample code for accessing the device file. - -if TTY - -config USB_CDC_COMPOSITE - tristate "CDC Composite Device (Ethernet and ACM)" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_ECM - help - This driver provides two functions in one configuration: - a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. - - This driver requires four bulk and two interrupt endpoints, - plus the ability to handle altsettings. Not all peripheral - controllers are that capable. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module. - -config USB_G_NOKIA - tristate "Nokia composite gadget" - depends on PHONET - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_OBEX - select USB_F_PHONET - select USB_F_ECM - help - The Nokia composite gadget provides support for acm, obex - and phonet in only one composite gadget driver. - - It's only really useful for N900 hardware. If you're building - a kernel for N900, say Y or M here. If unsure, say N. - -config USB_G_ACM_MS - tristate "CDC Composite Device (ACM and mass storage)" - depends on BLOCK - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_F_ACM - select USB_F_MASS_STORAGE - help - This driver provides two functions in one configuration: - a mass storage, and a CDC ACM (serial port) link. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_acm_ms". - -config USB_G_MULTI - tristate "Multifunction Composite Gadget" - depends on BLOCK && NET - select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_MASS_STORAGE - help - The Multifunction Composite Gadget provides Ethernet (RNDIS - and/or CDC Ethernet), mass storage and ACM serial link - interfaces. - - You will be asked to choose which of the two configurations is - to be available in the gadget. At least one configuration must - be chosen to make the gadget usable. Selecting more than one - configuration will prevent Windows from automatically detecting - the gadget as a composite gadget, so an INF file will be needed to - use the gadget. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_multi". - -config USB_G_MULTI_RNDIS - bool "RNDIS + CDC Serial + Storage configuration" - depends on USB_G_MULTI - select USB_F_RNDIS - default y - help - This option enables a configuration with RNDIS, CDC Serial and - Mass Storage functions available in the Multifunction Composite - Gadget. This is the configuration dedicated for Windows since RNDIS - is Microsoft's protocol. - - If unsure, say "y". - -config USB_G_MULTI_CDC - bool "CDC Ethernet + CDC Serial + Storage configuration" - depends on USB_G_MULTI - default n - select USB_F_ECM - help - This option enables a configuration with CDC Ethernet (ECM), CDC - Serial and Mass Storage functions available in the Multifunction - Composite Gadget. - - If unsure, say "y". - -endif # TTY - -config USB_G_HID - tristate "HID Gadget" - select USB_LIBCOMPOSITE - help - The HID gadget driver provides generic emulation of USB - Human Interface Devices (HID). - - For more information, see Documentation/usb/gadget_hid.txt which - includes sample code for accessing the device files. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_hid". - -# Standalone / single function gadgets -config USB_G_DBGP - tristate "EHCI Debug Device Gadget" - depends on TTY - select USB_LIBCOMPOSITE - help - This gadget emulates an EHCI Debug device. This is useful when you want - to interact with an EHCI Debug Port. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_dbgp". - -if USB_G_DBGP -choice - prompt "EHCI Debug Device mode" - default USB_G_DBGP_SERIAL - -config USB_G_DBGP_PRINTK - depends on USB_G_DBGP - bool "printk" - help - Directly printk() received data. No interaction. - -config USB_G_DBGP_SERIAL - depends on USB_G_DBGP - select USB_U_SERIAL - bool "serial" - help - Userland can interact using /dev/ttyGSxxx. -endchoice -endif - -# put drivers that need isochronous transfer support (for audio -# or video class gadget drivers), or specific hardware, here. -config USB_G_WEBCAM - tristate "USB Webcam Gadget" - depends on VIDEO_DEV - select USB_LIBCOMPOSITE - select VIDEOBUF2_VMALLOC - help - The Webcam Gadget acts as a composite USB Audio and Video Class - device. It provides a userspace API to process UVC control requests - and stream video data to the host. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_webcam". +source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 49514ea60a98..598a67d6ba05 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,105 +1,12 @@ # # USB peripheral controller drivers # -ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG -ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG +subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc -obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o -obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o -obj-$(CONFIG_USB_NET2272) += net2272.o -obj-$(CONFIG_USB_NET2280) += net2280.o -obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o -obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o -obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o -obj-$(CONFIG_USB_GOKU) += goku_udc.o -obj-$(CONFIG_USB_OMAP) += omap_udc.o -obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o -obj-$(CONFIG_USB_AT91) += at91_udc.o -obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o -obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o -obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o -fsl_usb2_udc-y := fsl_udc_core.o -fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o -obj-$(CONFIG_USB_M66592) += m66592-udc.o -obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o -obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o -obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o -obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o -obj-$(CONFIG_USB_EG20T) += pch_udc.o -obj-$(CONFIG_USB_MV_UDC) += mv_udc.o -mv_udc-y := mv_udc_core.o -obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o -obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o -obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o -obj-$(CONFIG_USB_GR_UDC) += gr_udc.o -# USB Functions -usb_f_acm-y := f_acm.o -obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o -usb_f_ss_lb-y := f_loopback.o f_sourcesink.o -obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o -obj-$(CONFIG_USB_U_SERIAL) += u_serial.o -usb_f_serial-y := f_serial.o -obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o -usb_f_obex-y := f_obex.o -obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o -obj-$(CONFIG_USB_U_ETHER) += u_ether.o -usb_f_ncm-y := f_ncm.o -obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o -usb_f_ecm-y := f_ecm.o -obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o -usb_f_phonet-y := f_phonet.o -obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o -usb_f_eem-y := f_eem.o -obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o -usb_f_ecm_subset-y := f_subset.o -obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o -usb_f_rndis-y := f_rndis.o rndis.o -obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o -usb_f_mass_storage-y := f_mass_storage.o storage_common.o -obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o -usb_f_fs-y := f_fs.o -obj-$(CONFIG_USB_F_FS) += usb_f_fs.o - -# -# USB gadget drivers -# -g_zero-y := zero.o -g_audio-y := audio.o -g_ether-y := ether.o -g_serial-y := serial.o -g_midi-y := gmidi.o -gadgetfs-y := inode.o -g_mass_storage-y := mass_storage.o -g_printer-y := printer.o -g_cdc-y := cdc2.o -g_multi-y := multi.o -g_hid-y := hid.o -g_dbgp-y := dbgp.o -g_nokia-y := nokia.o -g_webcam-y := webcam.o -g_ncm-y := ncm.o -g_acm_ms-y := acm_ms.o -g_tcm_usb_gadget-y := tcm_usb_gadget.o - -obj-$(CONFIG_USB_ZERO) += g_zero.o -obj-$(CONFIG_USB_AUDIO) += g_audio.o -obj-$(CONFIG_USB_ETH) += g_ether.o -obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o -obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o -obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o -obj-$(CONFIG_USB_G_SERIAL) += g_serial.o -obj-$(CONFIG_USB_G_PRINTER) += g_printer.o -obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o -obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o -obj-$(CONFIG_USB_G_HID) += g_hid.o -obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o -obj-$(CONFIG_USB_G_MULTI) += g_multi.o -obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o -obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o -obj-$(CONFIG_USB_G_NCM) += g_ncm.o -obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o -obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o +obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f80151932053..a8c18df171c3 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2072,6 +2072,7 @@ static const struct usb_gadget_driver composite_driver_template = { .unbind = composite_unbind, .setup = composite_setup, + .reset = composite_disconnect, .disconnect = composite_disconnect, .suspend = composite_suspend, diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 97142146eead..34034333f7f6 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1021,12 +1021,10 @@ static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop, if (page[len - 1] == '\n' || page[len - 1] == '\0') --len; - new_data = kzalloc(len, GFP_KERNEL); + new_data = kmemdup(page, len, GFP_KERNEL); if (!new_data) return -ENOMEM; - memcpy(new_data, page, len); - if (desc->opts_mutex) mutex_lock(desc->opts_mutex); kfree(ext_prop->data); @@ -1452,6 +1450,7 @@ static const struct usb_gadget_driver configfs_driver_template = { .unbind = configfs_composite_unbind, .setup = composite_setup, + .reset = composite_disconnect, .disconnect = composite_disconnect, .max_speed = USB_SPEED_SUPER, diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile new file mode 100644 index 000000000000..90701aa5a826 --- /dev/null +++ b/drivers/usb/gadget/function/Makefile @@ -0,0 +1,40 @@ +# +# USB peripheral controller drivers +# + +ccflags-y := -I$(srctree)/drivers/usb/gadget/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ + +# USB Functions +usb_f_acm-y := f_acm.o +obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o +usb_f_ss_lb-y := f_loopback.o f_sourcesink.o +obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o +obj-$(CONFIG_USB_U_SERIAL) += u_serial.o +usb_f_serial-y := f_serial.o +obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o +usb_f_obex-y := f_obex.o +obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o +obj-$(CONFIG_USB_U_ETHER) += u_ether.o +usb_f_ncm-y := f_ncm.o +obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o +usb_f_ecm-y := f_ecm.o +obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o +usb_f_phonet-y := f_phonet.o +obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o +usb_f_eem-y := f_eem.o +obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o +usb_f_ecm_subset-y := f_subset.o +obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o +usb_f_rndis-y := f_rndis.o rndis.o +obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o +usb_f_mass_storage-y := f_mass_storage.o storage_common.o +obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o +usb_f_fs-y := f_fs.o +obj-$(CONFIG_USB_F_FS) += usb_f_fs.o +usb_f_uac1-y := f_uac1.o u_uac1.o +obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o +usb_f_uac2-y := f_uac2.o +obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o +usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o +obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/function/f_acm.c index ab1065afbbd0..6da4685490ef 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -313,15 +313,15 @@ static void acm_complete_set_line_coding(struct usb_ep *ep, struct usb_composite_dev *cdev = acm->port.func.config->cdev; if (req->status != 0) { - DBG(cdev, "acm ttyGS%d completion, err %d\n", - acm->port_num, req->status); + dev_dbg(&cdev->gadget->dev, "acm ttyGS%d completion, err %d\n", + acm->port_num, req->status); return; } /* normal completion */ if (req->actual != sizeof(acm->port_line_coding)) { - DBG(cdev, "acm ttyGS%d short resp, len %d\n", - acm->port_num, req->actual); + dev_dbg(&cdev->gadget->dev, "acm ttyGS%d short resp, len %d\n", + acm->port_num, req->actual); usb_ep_set_halt(ep); } else { struct usb_cdc_line_coding *value = req->buf; @@ -397,14 +397,16 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) default: invalid: - VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); + dev_vdbg(&cdev->gadget->dev, + "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); } /* respond with data transfer or status phase? */ if (value >= 0) { - DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", + dev_dbg(&cdev->gadget->dev, + "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", acm->port_num, ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); req->zero = 0; @@ -428,10 +430,12 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (intf == acm->ctrl_id) { if (acm->notify->driver_data) { - VDBG(cdev, "reset acm control interface %d\n", intf); + dev_vdbg(&cdev->gadget->dev, + "reset acm control interface %d\n", intf); usb_ep_disable(acm->notify); } else { - VDBG(cdev, "init acm ctrl interface %d\n", intf); + dev_vdbg(&cdev->gadget->dev, + "init acm ctrl interface %d\n", intf); if (config_ep_by_speed(cdev->gadget, f, acm->notify)) return -EINVAL; } @@ -440,11 +444,13 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } else if (intf == acm->data_id) { if (acm->port.in->driver_data) { - DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); + dev_dbg(&cdev->gadget->dev, + "reset acm ttyGS%d\n", acm->port_num); gserial_disconnect(&acm->port); } if (!acm->port.in->desc || !acm->port.out->desc) { - DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); + dev_dbg(&cdev->gadget->dev, + "activate acm ttyGS%d\n", acm->port_num); if (config_ep_by_speed(cdev->gadget, f, acm->port.in) || config_ep_by_speed(cdev->gadget, f, @@ -467,7 +473,7 @@ static void acm_disable(struct usb_function *f) struct f_acm *acm = func_to_acm(f); struct usb_composite_dev *cdev = f->config->cdev; - DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); + dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num); gserial_disconnect(&acm->port); usb_ep_disable(acm->notify); acm->notify->driver_data = NULL; @@ -537,8 +543,8 @@ static int acm_notify_serial_state(struct f_acm *acm) spin_lock(&acm->lock); if (acm->notify_req) { - DBG(cdev, "acm ttyGS%d serial state %04x\n", - acm->port_num, acm->serial_state); + dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n", + acm->port_num, acm->serial_state); status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE, 0, &acm->serial_state, sizeof(acm->serial_state)); } else { @@ -691,12 +697,13 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto fail; - DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", - acm->port_num, - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - acm->port.in->name, acm->port.out->name, - acm->notify->name); + dev_dbg(&cdev->gadget->dev, + "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", + acm->port_num, + gadget_is_superspeed(c->cdev->gadget) ? "super" : + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + acm->port.in->name, acm->port.out->name, + acm->notify->name); return 0; fail: diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 798760fa7e70..798760fa7e70 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/function/f_eem.c index d61c11d765d0..4d8b236ea608 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -355,20 +355,18 @@ static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb) int padlen = 0; u16 len = skb->len; - if (!skb_cloned(skb)) { - int headroom = skb_headroom(skb); - int tailroom = skb_tailroom(skb); + int headroom = skb_headroom(skb); + int tailroom = skb_tailroom(skb); - /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, - * stick two bytes of zero-length EEM packet on the end. - */ - if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) - padlen += 2; + /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, + * stick two bytes of zero-length EEM packet on the end. + */ + if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) + padlen += 2; - if ((tailroom >= (ETH_FCS_LEN + padlen)) && - (headroom >= EEM_HLEN)) - goto done; - } + if ((tailroom >= (ETH_FCS_LEN + padlen)) && + (headroom >= EEM_HLEN) && !skb_cloned(skb)) + goto done; skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC); dev_kfree_skb_any(skb); diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 8598c27c7d43..4ad11e03cf54 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -34,6 +34,7 @@ #include "u_fs.h" #include "u_f.h" +#include "u_os_desc.h" #include "configfs.h" #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ @@ -154,6 +155,12 @@ struct ffs_io_data { struct usb_request *req; }; +struct ffs_desc_helper { + struct ffs_data *ffs; + unsigned interfaces_count; + unsigned eps_count; +}; + static int __must_check ffs_epfiles_create(struct ffs_data *ffs); static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); @@ -1025,6 +1032,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, case FUNCTIONFS_ENDPOINT_REVMAP: ret = epfile->ep->num; break; + case FUNCTIONFS_ENDPOINT_DESC: + { + int desc_idx; + struct usb_endpoint_descriptor *desc; + + switch (epfile->ffs->gadget->speed) { + case USB_SPEED_SUPER: + desc_idx = 2; + break; + case USB_SPEED_HIGH: + desc_idx = 1; + break; + default: + desc_idx = 0; + } + desc = epfile->ep->descs[desc_idx]; + + spin_unlock_irq(&epfile->ffs->eps_lock); + ret = copy_to_user((void *)value, desc, sizeof(*desc)); + if (ret) + ret = -EFAULT; + return ret; + } default: ret = -ENOTTY; } @@ -1527,7 +1557,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs) epfile->ffs = ffs; mutex_init(&epfile->mutex); init_waitqueue_head(&epfile->wait); - sprintf(epfiles->name, "ep%u", i); + if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) + sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]); + else + sprintf(epfiles->name, "ep%u", i); if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile, &ffs_epfile_operations, &epfile->dentry))) { @@ -1646,13 +1679,22 @@ enum ffs_entity_type { FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT }; +enum ffs_os_desc_type { + FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP +}; + typedef int (*ffs_entity_callback)(enum ffs_entity_type entity, u8 *valuep, struct usb_descriptor_header *desc, void *priv); -static int __must_check ffs_do_desc(char *data, unsigned len, - ffs_entity_callback entity, void *priv) +typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv); + +static int __must_check ffs_do_single_desc(char *data, unsigned len, + ffs_entity_callback entity, + void *priv) { struct usb_descriptor_header *_ds = (void *)data; u8 length; @@ -1804,7 +1846,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, if (!data) return _len - len; - ret = ffs_do_desc(data, len, entity, priv); + ret = ffs_do_single_desc(data, len, entity, priv); if (unlikely(ret < 0)) { pr_debug("%s returns %d\n", __func__, ret); return ret; @@ -1820,7 +1862,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, u8 *valuep, struct usb_descriptor_header *desc, void *priv) { - struct ffs_data *ffs = priv; + struct ffs_desc_helper *helper = priv; + struct usb_endpoint_descriptor *d; ENTER(); @@ -1834,8 +1877,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, * encountered interface "n" then there are at least * "n+1" interfaces. */ - if (*valuep >= ffs->interfaces_count) - ffs->interfaces_count = *valuep + 1; + if (*valuep >= helper->interfaces_count) + helper->interfaces_count = *valuep + 1; break; case FFS_STRING: @@ -1843,26 +1886,215 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, * Strings are indexed from 1 (0 is magic ;) reserved * for languages list or some such) */ - if (*valuep > ffs->strings_count) - ffs->strings_count = *valuep; + if (*valuep > helper->ffs->strings_count) + helper->ffs->strings_count = *valuep; break; case FFS_ENDPOINT: - /* Endpoints are indexed from 1 as well. */ - if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count) - ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK); + d = (void *)desc; + helper->eps_count++; + if (helper->eps_count >= 15) + return -EINVAL; + /* Check if descriptors for any speed were already parsed */ + if (!helper->ffs->eps_count && !helper->ffs->interfaces_count) + helper->ffs->eps_addrmap[helper->eps_count] = + d->bEndpointAddress; + else if (helper->ffs->eps_addrmap[helper->eps_count] != + d->bEndpointAddress) + return -EINVAL; break; } return 0; } +static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type, + struct usb_os_desc_header *desc) +{ + u16 bcd_version = le16_to_cpu(desc->bcdVersion); + u16 w_index = le16_to_cpu(desc->wIndex); + + if (bcd_version != 1) { + pr_vdebug("unsupported os descriptors version: %d", + bcd_version); + return -EINVAL; + } + switch (w_index) { + case 0x4: + *next_type = FFS_OS_DESC_EXT_COMPAT; + break; + case 0x5: + *next_type = FFS_OS_DESC_EXT_PROP; + break; + default: + pr_vdebug("unsupported os descriptor type: %d", w_index); + return -EINVAL; + } + + return sizeof(*desc); +} + +/* + * Process all extended compatibility/extended property descriptors + * of a feature descriptor + */ +static int __must_check ffs_do_single_os_desc(char *data, unsigned len, + enum ffs_os_desc_type type, + u16 feature_count, + ffs_os_desc_callback entity, + void *priv, + struct usb_os_desc_header *h) +{ + int ret; + const unsigned _len = len; + + ENTER(); + + /* loop over all ext compat/ext prop descriptors */ + while (feature_count--) { + ret = entity(type, h, data, len, priv); + if (unlikely(ret < 0)) { + pr_debug("bad OS descriptor, type: %d\n", type); + return ret; + } + data += ret; + len -= ret; + } + return _len - len; +} + +/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */ +static int __must_check ffs_do_os_descs(unsigned count, + char *data, unsigned len, + ffs_os_desc_callback entity, void *priv) +{ + const unsigned _len = len; + unsigned long num = 0; + + ENTER(); + + for (num = 0; num < count; ++num) { + int ret; + enum ffs_os_desc_type type; + u16 feature_count; + struct usb_os_desc_header *desc = (void *)data; + + if (len < sizeof(*desc)) + return -EINVAL; + + /* + * Record "descriptor" entity. + * Process dwLength, bcdVersion, wIndex, get b/wCount. + * Move the data pointer to the beginning of extended + * compatibilities proper or extended properties proper + * portions of the data + */ + if (le32_to_cpu(desc->dwLength) > len) + return -EINVAL; + + ret = __ffs_do_os_desc_header(&type, desc); + if (unlikely(ret < 0)) { + pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n", + num, ret); + return ret; + } + /* + * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??" + */ + feature_count = le16_to_cpu(desc->wCount); + if (type == FFS_OS_DESC_EXT_COMPAT && + (feature_count > 255 || desc->Reserved)) + return -EINVAL; + len -= ret; + data += ret; + + /* + * Process all function/property descriptors + * of this Feature Descriptor + */ + ret = ffs_do_single_os_desc(data, len, type, + feature_count, entity, priv, desc); + if (unlikely(ret < 0)) { + pr_debug("%s returns %d\n", __func__, ret); + return ret; + } + + len -= ret; + data += ret; + } + return _len - len; +} + +/** + * Validate contents of the buffer from userspace related to OS descriptors. + */ +static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv) +{ + struct ffs_data *ffs = priv; + u8 length; + + ENTER(); + + switch (type) { + case FFS_OS_DESC_EXT_COMPAT: { + struct usb_ext_compat_desc *d = data; + int i; + + if (len < sizeof(*d) || + d->bFirstInterfaceNumber >= ffs->interfaces_count || + d->Reserved1) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i) + if (d->Reserved2[i]) + return -EINVAL; + + length = sizeof(struct usb_ext_compat_desc); + } + break; + case FFS_OS_DESC_EXT_PROP: { + struct usb_ext_prop_desc *d = data; + u32 type, pdl; + u16 pnl; + + if (len < sizeof(*d) || h->interface >= ffs->interfaces_count) + return -EINVAL; + length = le32_to_cpu(d->dwSize); + type = le32_to_cpu(d->dwPropertyDataType); + if (type < USB_EXT_PROP_UNICODE || + type > USB_EXT_PROP_UNICODE_MULTI) { + pr_vdebug("unsupported os descriptor property type: %d", + type); + return -EINVAL; + } + pnl = le16_to_cpu(d->wPropertyNameLength); + pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); + if (length != 14 + pnl + pdl) { + pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", + length, pnl, pdl, type); + return -EINVAL; + } + ++ffs->ms_os_descs_ext_prop_count; + /* property name reported to the host as "WCHAR"s */ + ffs->ms_os_descs_ext_prop_name_len += pnl * 2; + ffs->ms_os_descs_ext_prop_data_len += pdl; + } + break; + default: + pr_vdebug("unknown descriptor: %d\n", type); + return -EINVAL; + } + return length; +} + static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { char *data = _data, *raw_descs; - unsigned counts[3], flags; + unsigned os_descs_count = 0, counts[3], flags; int ret = -EINVAL, i; + struct ffs_desc_helper helper; ENTER(); @@ -1877,9 +2109,12 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, break; case FUNCTIONFS_DESCRIPTORS_MAGIC_V2: flags = get_unaligned_le32(data + 8); + ffs->user_flags = flags; if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | - FUNCTIONFS_HAS_SS_DESC)) { + FUNCTIONFS_HAS_SS_DESC | + FUNCTIONFS_HAS_MS_OS_DESC | + FUNCTIONFS_VIRTUAL_ADDR)) { ret = -ENOSYS; goto error; } @@ -1902,19 +2137,48 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, len -= 4; } } + if (flags & (1 << i)) { + os_descs_count = get_unaligned_le32(data); + data += 4; + len -= 4; + }; /* Read descriptors */ raw_descs = data; + helper.ffs = ffs; for (i = 0; i < 3; ++i) { if (!counts[i]) continue; + helper.interfaces_count = 0; + helper.eps_count = 0; ret = ffs_do_descs(counts[i], data, len, - __ffs_data_do_entity, ffs); + __ffs_data_do_entity, &helper); if (ret < 0) goto error; + if (!ffs->eps_count && !ffs->interfaces_count) { + ffs->eps_count = helper.eps_count; + ffs->interfaces_count = helper.interfaces_count; + } else { + if (ffs->eps_count != helper.eps_count) { + ret = -EINVAL; + goto error; + } + if (ffs->interfaces_count != helper.interfaces_count) { + ret = -EINVAL; + goto error; + } + } data += ret; len -= ret; } + if (os_descs_count) { + ret = ffs_do_os_descs(os_descs_count, data, len, + __ffs_data_do_os_desc, ffs); + if (ret < 0) + goto error; + data += ret; + len -= ret; + } if (raw_descs == data || len) { ret = -EINVAL; @@ -1927,6 +2191,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, ffs->fs_descs_count = counts[0]; ffs->hs_descs_count = counts[1]; ffs->ss_descs_count = counts[2]; + ffs->ms_os_descs_count = os_descs_count; return 0; @@ -2109,7 +2374,8 @@ static void __ffs_event_add(struct ffs_data *ffs, break; default: - BUG(); + WARN(1, "%d: unknown event, this should not happen\n", type); + return; } { @@ -2137,9 +2403,18 @@ static void ffs_event_add(struct ffs_data *ffs, spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); } - /* Bind/unbind USB function hooks *******************************************/ +static int ffs_ep_addr2idx(struct ffs_data *ffs, u8 endpoint_address) +{ + int i; + + for (i = 1; i < ARRAY_SIZE(ffs->eps_addrmap); ++i) + if (ffs->eps_addrmap[i] == endpoint_address) + return i; + return -ENOENT; +} + static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, struct usb_descriptor_header *desc, void *priv) @@ -2147,7 +2422,8 @@ 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; - unsigned ep_desc_id, idx; + unsigned ep_desc_id; + int idx; static const char *speed_names[] = { "full", "high", "super" }; if (type != FFS_DESCRIPTOR) @@ -2173,7 +2449,10 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) return 0; - idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; + idx = ffs_ep_addr2idx(func->ffs, ds->bEndpointAddress) - 1; + if (idx < 0) + return idx; + ffs_ep = func->eps + idx; if (unlikely(ffs_ep->descs[ep_desc_id])) { @@ -2192,7 +2471,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, } else { struct usb_request *req; struct usb_ep *ep; + u8 bEndpointAddress; + /* + * We back up bEndpointAddress because autoconfig overwrites + * it with physical endpoint address. + */ + bEndpointAddress = ds->bEndpointAddress; pr_vdebug("autoconfig\n"); ep = usb_ep_autoconfig(func->gadget, ds); if (unlikely(!ep)) @@ -2207,6 +2492,12 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, ffs_ep->req = req; func->eps_revmap[ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] = idx + 1; + /* + * If we use virtual address mapping, we restore + * original bEndpointAddress value. + */ + if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) + ds->bEndpointAddress = bEndpointAddress; } ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength); @@ -2268,6 +2559,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; } +static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv) +{ + struct ffs_function *func = priv; + u8 length = 0; + + switch (type) { + case FFS_OS_DESC_EXT_COMPAT: { + struct usb_ext_compat_desc *desc = data; + struct usb_os_desc_table *t; + + t = &func->function.os_desc_table[desc->bFirstInterfaceNumber]; + t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber]; + memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID, + ARRAY_SIZE(desc->CompatibleID) + + ARRAY_SIZE(desc->SubCompatibleID)); + length = sizeof(*desc); + } + break; + case FFS_OS_DESC_EXT_PROP: { + struct usb_ext_prop_desc *desc = data; + struct usb_os_desc_table *t; + struct usb_os_desc_ext_prop *ext_prop; + char *ext_prop_name; + char *ext_prop_data; + + t = &func->function.os_desc_table[h->interface]; + t->if_id = func->interfaces_nums[h->interface]; + + ext_prop = func->ffs->ms_os_descs_ext_prop_avail; + func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop); + + ext_prop->type = le32_to_cpu(desc->dwPropertyDataType); + ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength); + ext_prop->data_len = le32_to_cpu(*(u32 *) + usb_ext_prop_data_len_ptr(data, ext_prop->name_len)); + length = ext_prop->name_len + ext_prop->data_len + 14; + + ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail; + func->ffs->ms_os_descs_ext_prop_name_avail += + ext_prop->name_len; + + ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail; + func->ffs->ms_os_descs_ext_prop_data_avail += + ext_prop->data_len; + memcpy(ext_prop_data, + usb_ext_prop_data_ptr(data, ext_prop->name_len), + ext_prop->data_len); + /* unicode data reported to the host as "WCHAR"s */ + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + case USB_EXT_PROP_UNICODE_MULTI: + ext_prop->data_len *= 2; + break; + } + ext_prop->data = ext_prop_data; + + memcpy(ext_prop_name, usb_ext_prop_name_ptr(data), + ext_prop->name_len); + /* property name reported to the host as "WCHAR"s */ + ext_prop->name_len *= 2; + ext_prop->name = ext_prop_name; + + t->os_desc->ext_prop_len += + ext_prop->name_len + ext_prop->data_len + 14; + ++t->os_desc->ext_prop_count; + list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop); + } + break; + default: + pr_vdebug("unknown descriptor: %d\n", type); + } + + return length; +} + static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, struct usb_configuration *c) { @@ -2329,7 +2699,7 @@ static int _ffs_func_bind(struct usb_configuration *c, const int super = gadget_is_superspeed(func->gadget) && func->ffs->ss_descs_count; - int fs_len, hs_len, ret; + int fs_len, hs_len, ss_len, ret, i; /* Make it a single chunk, less management later on */ vla_group(d); @@ -2341,6 +2711,18 @@ static int _ffs_func_bind(struct usb_configuration *c, 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, struct usb_os_desc_table, os_desc_table, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, char[16], ext_compat, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, struct usb_os_desc, os_desc, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop, + ffs->ms_os_descs_ext_prop_count); + vla_item_with_sz(d, char, ext_prop_name, + ffs->ms_os_descs_ext_prop_name_len); + vla_item_with_sz(d, char, ext_prop_data, + ffs->ms_os_descs_ext_prop_data_len); vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); char *vlabuf; @@ -2351,12 +2733,16 @@ static int _ffs_func_bind(struct usb_configuration *c, return -ENOTSUPP; /* Allocate a single chunk, less management later on */ - vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL); if (unlikely(!vlabuf)) return -ENOMEM; - /* Zero */ - memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); + ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop); + ffs->ms_os_descs_ext_prop_name_avail = + vla_ptr(vlabuf, d, ext_prop_name); + ffs->ms_os_descs_ext_prop_data_avail = + vla_ptr(vlabuf, d, ext_prop_data); + /* Copy descriptors */ memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, ffs->raw_descs_length); @@ -2410,12 +2796,16 @@ static int _ffs_func_bind(struct usb_configuration *c, if (likely(super)) { func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); - ret = ffs_do_descs(ffs->ss_descs_count, + ss_len = 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)) + if (unlikely(ss_len < 0)) { + ret = ss_len; goto error; + } + } else { + ss_len = 0; } /* @@ -2431,6 +2821,28 @@ static int _ffs_func_bind(struct usb_configuration *c, if (unlikely(ret < 0)) goto error; + func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table); + if (c->cdev->use_os_string) + for (i = 0; i < ffs->interfaces_count; ++i) { + struct usb_os_desc *desc; + + desc = func->function.os_desc_table[i].os_desc = + vla_ptr(vlabuf, d, os_desc) + + i * sizeof(struct usb_os_desc); + desc->ext_compat_id = + vla_ptr(vlabuf, d, ext_compat) + i * 16; + INIT_LIST_HEAD(&desc->ext_prop); + } + ret = ffs_do_os_descs(ffs->ms_os_descs_count, + vla_ptr(vlabuf, d, raw_descs) + + fs_len + hs_len + ss_len, + d_raw_descs__sz - fs_len - hs_len - ss_len, + __ffs_func_bind_do_os_desc, func); + if (unlikely(ret < 0)) + goto error; + func->function.os_desc_n = + c->cdev->use_os_string ? ffs->interfaces_count : 0; + /* And we're done */ ffs_event_add(ffs, FUNCTIONFS_BIND); return 0; @@ -2530,6 +2942,8 @@ static int ffs_func_setup(struct usb_function *f, ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex)); if (unlikely(ret < 0)) return ret; + if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) + ret = func->ffs->eps_addrmap[ret]; break; default: @@ -2901,12 +3315,12 @@ static void *ffs_acquire_dev(const char *dev_name) ffs_dev = _ffs_find_dev(dev_name); if (!ffs_dev) - ffs_dev = ERR_PTR(-ENODEV); + ffs_dev = ERR_PTR(-ENOENT); else if (ffs_dev->mounted) ffs_dev = ERR_PTR(-EBUSY); else if (ffs_dev->ffs_acquire_dev_callback && ffs_dev->ffs_acquire_dev_callback(ffs_dev)) - ffs_dev = ERR_PTR(-ENODEV); + ffs_dev = ERR_PTR(-ENOENT); else ffs_dev->mounted = true; diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/function/f_hid.c index a95290a1289f..a95290a1289f 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 4557cd03f0b1..bf04389137e6 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -298,7 +298,8 @@ static void disable_loopback(struct f_loopback *loop) struct usb_composite_dev *cdev; cdev = loop->function.config->cdev; - disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL); + disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL, + NULL); VDBG(cdev, "%s disabled\n", loop->function.name); } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index b96393908860..811929cd4c9e 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -566,22 +566,22 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, *pbusy = 1; *state = BUF_STATE_BUSY; spin_unlock_irq(&fsg->common->lock); + rc = usb_ep_queue(ep, req, GFP_KERNEL); - if (rc != 0) { - *pbusy = 0; - *state = BUF_STATE_EMPTY; + if (rc == 0) + return; /* All good, we're done */ - /* We can't do much more than wait for a reset */ + *pbusy = 0; + *state = BUF_STATE_EMPTY; - /* - * Note: currently the net2280 driver fails zero-length - * submissions if DMA is enabled. - */ - if (rc != -ESHUTDOWN && - !(rc == -EOPNOTSUPP && req->length == 0)) - WARNING(fsg, "error in submission: %s --> %d\n", - ep->name, rc); - } + /* We can't do much more than wait for a reset */ + + /* + * Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. + */ + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc); } static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh) @@ -3665,4 +3665,3 @@ void fsg_config_from_params(struct fsg_config *cfg, cfg->fsg_num_buffers = fsg_num_buffers; } EXPORT_SYMBOL_GPL(fsg_config_from_params); - diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h index b4866fcef30b..b4866fcef30b 100644 --- a/drivers/usb/gadget/f_mass_storage.h +++ b/drivers/usb/gadget/function/f_mass_storage.h diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 807b31c0edc3..807b31c0edc3 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index a9499fd30792..146f48cc65d7 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -68,6 +68,18 @@ struct f_ncm { * callback and ethernet open/close */ spinlock_t lock; + + struct net_device *netdev; + + /* For multi-frame NDP TX */ + struct sk_buff *skb_tx_data; + struct sk_buff *skb_tx_ndp; + u16 ndp_dgram_count; + bool timer_force_tx; + struct tasklet_struct tx_tasklet; + struct hrtimer task_timer; + + bool timer_stopping; }; static inline struct f_ncm *func_to_ncm(struct usb_function *f) @@ -92,15 +104,20 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g) * If the host can group frames, allow it to do that, 16K is selected, * because it's used by default by the current linux host driver */ -#define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE +#define NTB_DEFAULT_IN_SIZE 16384 #define NTB_OUT_SIZE 16384 -/* - * skbs of size less than that will not be aligned - * to NCM's dwNtbInMaxSize to save bus bandwidth +/* Allocation for storing the NDP, 32 should suffice for a + * 16k packet. This allows a maximum of 32 * 507 Byte packets to + * be transmitted in a single 16kB skb, though when sending full size + * packets this limit will be plenty. + * Smaller packets are not likely to be trying to maximize the + * throughput and will be mstly sending smaller infrequent frames. */ +#define TX_MAX_NUM_DPE 32 -#define MAX_TX_NONFIXED (512 * 3) +/* Delay for the transmit to wait before sending an unfilled NTB frame. */ +#define TX_TIMEOUT_NSECS 300000 #define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \ USB_CDC_NCM_NTB32_SUPPORTED) @@ -355,14 +372,15 @@ struct ndp_parser_opts { u32 ndp_sign; unsigned nth_size; unsigned ndp_size; + unsigned dpe_size; unsigned ndplen_align; /* sizes in u16 units */ unsigned dgram_item_len; /* index or length */ unsigned block_length; - unsigned fp_index; + unsigned ndp_index; unsigned reserved1; unsigned reserved2; - unsigned next_fp_index; + unsigned next_ndp_index; }; #define INIT_NDP16_OPTS { \ @@ -370,13 +388,14 @@ struct ndp_parser_opts { .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \ .nth_size = sizeof(struct usb_cdc_ncm_nth16), \ .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \ + .dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \ .ndplen_align = 4, \ .dgram_item_len = 1, \ .block_length = 1, \ - .fp_index = 1, \ + .ndp_index = 1, \ .reserved1 = 0, \ .reserved2 = 0, \ - .next_fp_index = 1, \ + .next_ndp_index = 1, \ } @@ -385,13 +404,14 @@ struct ndp_parser_opts { .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \ .nth_size = sizeof(struct usb_cdc_ncm_nth32), \ .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \ + .dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \ .ndplen_align = 8, \ .dgram_item_len = 2, \ .block_length = 2, \ - .fp_index = 2, \ + .ndp_index = 2, \ .reserved1 = 1, \ .reserved2 = 2, \ - .next_fp_index = 2, \ + .next_ndp_index = 2, \ } static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS; @@ -803,6 +823,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (ncm->port.in_ep->driver_data) { DBG(cdev, "reset ncm\n"); + ncm->timer_stopping = true; + ncm->netdev = NULL; gether_disconnect(&ncm->port); ncm_reset_values(ncm); } @@ -839,6 +861,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) net = gether_connect(&ncm->port); if (IS_ERR(net)) return PTR_ERR(net); + ncm->netdev = net; + ncm->timer_stopping = false; } spin_lock(&ncm->lock); @@ -865,95 +889,240 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf) return ncm->port.in_ep->driver_data ? 1 : 0; } +static struct sk_buff *package_for_tx(struct f_ncm *ncm) +{ + __le16 *ntb_iter; + struct sk_buff *skb2 = NULL; + unsigned ndp_pad; + unsigned ndp_index; + unsigned new_len; + + const struct ndp_parser_opts *opts = ncm->parser_opts; + const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; + + /* Stop the timer */ + hrtimer_try_to_cancel(&ncm->task_timer); + + ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) - + ncm->skb_tx_data->len; + ndp_index = ncm->skb_tx_data->len + ndp_pad; + new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len; + + /* Set the final BlockLength and wNdpIndex */ + ntb_iter = (void *) ncm->skb_tx_data->data; + /* Increment pointer to BlockLength */ + ntb_iter += 2 + 1 + 1; + put_ncm(&ntb_iter, opts->block_length, new_len); + put_ncm(&ntb_iter, opts->ndp_index, ndp_index); + + /* Set the final NDP wLength */ + new_len = opts->ndp_size + + (ncm->ndp_dgram_count * dgram_idx_len); + ncm->ndp_dgram_count = 0; + /* Increment from start to wLength */ + ntb_iter = (void *) ncm->skb_tx_ndp->data; + ntb_iter += 2; + put_unaligned_le16(new_len, ntb_iter); + + /* Merge the skbs */ + swap(skb2, ncm->skb_tx_data); + if (ncm->skb_tx_data) { + dev_kfree_skb_any(ncm->skb_tx_data); + ncm->skb_tx_data = NULL; + } + + /* Insert NDP alignment. */ + ntb_iter = (void *) skb_put(skb2, ndp_pad); + memset(ntb_iter, 0, ndp_pad); + + /* Copy NTB across. */ + ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len); + memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len); + dev_kfree_skb_any(ncm->skb_tx_ndp); + ncm->skb_tx_ndp = NULL; + + /* Insert zero'd datagram. */ + ntb_iter = (void *) skb_put(skb2, dgram_idx_len); + memset(ntb_iter, 0, dgram_idx_len); + + return skb2; +} + static struct sk_buff *ncm_wrap_ntb(struct gether *port, struct sk_buff *skb) { struct f_ncm *ncm = func_to_ncm(&port->func); - struct sk_buff *skb2; + struct sk_buff *skb2 = NULL; int ncb_len = 0; - __le16 *tmp; - int div; - int rem; - int pad; - int ndp_align; - int ndp_pad; + __le16 *ntb_data; + __le16 *ntb_ndp; + int dgram_pad; + unsigned max_size = ncm->port.fixed_in_len; const struct ndp_parser_opts *opts = ncm->parser_opts; - unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; + const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor); + const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); + const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; + + if (!skb && !ncm->skb_tx_data) + return NULL; + + if (skb) { + /* Add the CRC if required up front */ + if (ncm->is_crc) { + uint32_t crc; + __le16 *crc_pos; + + crc = ~crc32_le(~0, + skb->data, + skb->len); + crc_pos = (void *) skb_put(skb, sizeof(uint32_t)); + put_unaligned_le32(crc, crc_pos); + } + + /* If the new skb is too big for the current NCM NTB then + * set the current stored skb to be sent now and clear it + * ready for new data. + * NOTE: Assume maximum align for speed of calculation. + */ + if (ncm->skb_tx_data + && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE + || (ncm->skb_tx_data->len + + div + rem + skb->len + + ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len)) + > max_size)) { + skb2 = package_for_tx(ncm); + if (!skb2) + goto err; + } + + if (!ncm->skb_tx_data) { + ncb_len = opts->nth_size; + dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len; + ncb_len += dgram_pad; + + /* Create a new skb for the NTH and datagrams. */ + ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC); + if (!ncm->skb_tx_data) + goto err; - div = le16_to_cpu(ntb_parameters.wNdpInDivisor); - rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); - ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len); + memset(ntb_data, 0, ncb_len); + /* dwSignature */ + put_unaligned_le32(opts->nth_sign, ntb_data); + ntb_data += 2; + /* wHeaderLength */ + put_unaligned_le16(opts->nth_size, ntb_data++); + + /* Allocate an skb for storing the NDP, + * TX_MAX_NUM_DPE should easily suffice for a + * 16k packet. + */ + ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size + + opts->dpe_size + * TX_MAX_NUM_DPE), + GFP_ATOMIC); + if (!ncm->skb_tx_ndp) + goto err; + ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, + opts->ndp_size); + memset(ntb_ndp, 0, ncb_len); + /* dwSignature */ + put_unaligned_le32(ncm->ndp_sign, ntb_ndp); + ntb_ndp += 2; + + /* There is always a zeroed entry */ + ncm->ndp_dgram_count = 1; - ncb_len += opts->nth_size; - ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len; - ncb_len += ndp_pad; - ncb_len += opts->ndp_size; - ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */ - ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */ - pad = ALIGN(ncb_len, div) + rem - ncb_len; - ncb_len += pad; + /* Note: we skip opts->next_ndp_index */ + } - if (ncb_len + skb->len + crc_len > max_size) { + /* Delay the timer. */ + hrtimer_start(&ncm->task_timer, + ktime_set(0, TX_TIMEOUT_NSECS), + HRTIMER_MODE_REL); + + /* Add the datagram position entries */ + ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len); + memset(ntb_ndp, 0, dgram_idx_len); + + ncb_len = ncm->skb_tx_data->len; + dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len; + ncb_len += dgram_pad; + + /* (d)wDatagramIndex */ + put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len); + /* (d)wDatagramLength */ + put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len); + ncm->ndp_dgram_count++; + + /* Add the new data to the skb */ + ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad); + memset(ntb_data, 0, dgram_pad); + ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len); + memcpy(ntb_data, skb->data, skb->len); dev_kfree_skb_any(skb); - return NULL; + skb = NULL; + + } else if (ncm->skb_tx_data && ncm->timer_force_tx) { + /* If the tx was requested because of a timeout then send */ + skb2 = package_for_tx(ncm); + if (!skb2) + goto err; } - skb2 = skb_copy_expand(skb, ncb_len, - max_size - skb->len - ncb_len - crc_len, - GFP_ATOMIC); - dev_kfree_skb_any(skb); - if (!skb2) - return NULL; + return skb2; - skb = skb2; +err: + ncm->netdev->stats.tx_dropped++; - tmp = (void *) skb_push(skb, ncb_len); - memset(tmp, 0, ncb_len); + if (skb) + dev_kfree_skb_any(skb); + if (ncm->skb_tx_data) + dev_kfree_skb_any(ncm->skb_tx_data); + if (ncm->skb_tx_ndp) + dev_kfree_skb_any(ncm->skb_tx_ndp); - put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */ - tmp += 2; - /* wHeaderLength */ - put_unaligned_le16(opts->nth_size, tmp++); - tmp++; /* skip wSequence */ - put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */ - /* (d)wFpIndex */ - /* the first pointer is right after the NTH + align */ - put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad); + return NULL; +} - tmp = (void *)tmp + ndp_pad; +/* + * This transmits the NTB if there are frames waiting. + */ +static void ncm_tx_tasklet(unsigned long data) +{ + struct f_ncm *ncm = (void *)data; - /* NDP */ - put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */ - tmp += 2; - /* wLength */ - put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++); + if (ncm->timer_stopping) + return; - tmp += opts->reserved1; - tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ - tmp += opts->reserved2; + /* Only send if data is available. */ + if (ncm->skb_tx_data) { + ncm->timer_force_tx = true; - if (ncm->is_crc) { - uint32_t crc; + /* XXX This allowance of a NULL skb argument to ndo_start_xmit + * XXX is not sane. The gadget layer should be redesigned so + * XXX that the dev->wrap() invocations to build SKBs is transparent + * XXX and performed in some way outside of the ndo_start_xmit + * XXX interface. + */ + ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev); - crc = ~crc32_le(~0, - skb->data + ncb_len, - skb->len - ncb_len); - put_unaligned_le32(crc, skb->data + skb->len); - skb_put(skb, crc_len); + ncm->timer_force_tx = false; } +} - /* (d)wDatagramIndex[0] */ - put_ncm(&tmp, opts->dgram_item_len, ncb_len); - /* (d)wDatagramLength[0] */ - put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len); - /* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */ - - if (skb->len > MAX_TX_NONFIXED) - memset(skb_put(skb, max_size - skb->len), - 0, max_size - skb->len); - - return skb; +/* + * The transmit should only be run if no skb data has been sent + * for a certain duration. + */ +static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data) +{ + struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer); + tasklet_schedule(&ncm->tx_tasklet); + return HRTIMER_NORESTART; } static int ncm_unwrap_ntb(struct gether *port, @@ -963,6 +1132,7 @@ static int ncm_unwrap_ntb(struct gether *port, struct f_ncm *ncm = func_to_ncm(&port->func); __le16 *tmp = (void *) skb->data; unsigned index, index2; + int ndp_index; unsigned dg_len, dg_len2; unsigned ndp_len; struct sk_buff *skb2; @@ -995,91 +1165,101 @@ static int ncm_unwrap_ntb(struct gether *port, goto err; } - index = get_ncm(&tmp, opts->fp_index); - /* NCM 3.2 */ - if (((index % 4) != 0) && (index < opts->nth_size)) { - INFO(port->func.config->cdev, "Bad index: %x\n", - index); - goto err; - } - - /* walk through NDP */ - tmp = ((void *)skb->data) + index; - if (get_unaligned_le32(tmp) != ncm->ndp_sign) { - INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); - goto err; - } - tmp += 2; - - ndp_len = get_unaligned_le16(tmp++); - /* - * NCM 3.3.1 - * entry is 2 items - * item size is 16/32 bits, opts->dgram_item_len * 2 bytes - * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry - */ - if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2)) - || (ndp_len % opts->ndplen_align != 0)) { - INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len); - goto err; - } - tmp += opts->reserved1; - tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ - tmp += opts->reserved2; - - ndp_len -= opts->ndp_size; - index2 = get_ncm(&tmp, opts->dgram_item_len); - dg_len2 = get_ncm(&tmp, opts->dgram_item_len); - dgram_counter = 0; + ndp_index = get_ncm(&tmp, opts->ndp_index); + /* Run through all the NDP's in the NTB */ do { - index = index2; - dg_len = dg_len2; - if (dg_len < 14 + crc_len) { /* ethernet header + crc */ - INFO(port->func.config->cdev, "Bad dgram length: %x\n", - dg_len); + /* NCM 3.2 */ + if (((ndp_index % 4) != 0) && + (ndp_index < opts->nth_size)) { + INFO(port->func.config->cdev, "Bad index: %#X\n", + ndp_index); goto err; } - if (ncm->is_crc) { - uint32_t crc, crc2; - - crc = get_unaligned_le32(skb->data + - index + dg_len - crc_len); - crc2 = ~crc32_le(~0, - skb->data + index, - dg_len - crc_len); - if (crc != crc2) { - INFO(port->func.config->cdev, "Bad CRC\n"); - goto err; - } + + /* walk through NDP */ + tmp = (void *)(skb->data + ndp_index); + if (get_unaligned_le32(tmp) != ncm->ndp_sign) { + INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); + goto err; + } + tmp += 2; + + ndp_len = get_unaligned_le16(tmp++); + /* + * NCM 3.3.1 + * entry is 2 items + * item size is 16/32 bits, opts->dgram_item_len * 2 bytes + * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry + * Each entry is a dgram index and a dgram length. + */ + if ((ndp_len < opts->ndp_size + + 2 * 2 * (opts->dgram_item_len * 2)) + || (ndp_len % opts->ndplen_align != 0)) { + INFO(port->func.config->cdev, "Bad NDP length: %#X\n", + ndp_len); + goto err; } + tmp += opts->reserved1; + /* Check for another NDP (d)wNextNdpIndex */ + ndp_index = get_ncm(&tmp, opts->next_ndp_index); + tmp += opts->reserved2; + ndp_len -= opts->ndp_size; index2 = get_ncm(&tmp, opts->dgram_item_len); dg_len2 = get_ncm(&tmp, opts->dgram_item_len); + dgram_counter = 0; + + do { + index = index2; + dg_len = dg_len2; + if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */ + INFO(port->func.config->cdev, + "Bad dgram length: %#X\n", dg_len); + goto err; + } + if (ncm->is_crc) { + uint32_t crc, crc2; + + crc = get_unaligned_le32(skb->data + + index + dg_len - + crc_len); + crc2 = ~crc32_le(~0, + skb->data + index, + dg_len - crc_len); + if (crc != crc2) { + INFO(port->func.config->cdev, + "Bad CRC\n"); + goto err; + } + } + + index2 = get_ncm(&tmp, opts->dgram_item_len); + dg_len2 = get_ncm(&tmp, opts->dgram_item_len); - if (index2 == 0 || dg_len2 == 0) { - skb2 = skb; - } else { - skb2 = skb_clone(skb, GFP_ATOMIC); + /* + * Copy the data into a new skb. + * This ensures the truesize is correct + */ + skb2 = netdev_alloc_skb_ip_align(ncm->netdev, + dg_len - crc_len); if (skb2 == NULL) goto err; - } + memcpy(skb_put(skb2, dg_len - crc_len), + skb->data + index, dg_len - crc_len); - if (!skb_pull(skb2, index)) { - ret = -EOVERFLOW; - goto err; - } + skb_queue_tail(list, skb2); - skb_trim(skb2, dg_len - crc_len); - skb_queue_tail(list, skb2); + ndp_len -= 2 * (opts->dgram_item_len * 2); - ndp_len -= 2 * (opts->dgram_item_len * 2); + dgram_counter++; - dgram_counter++; + if (index2 == 0 || dg_len2 == 0) + break; + } while (ndp_len > 2 * (opts->dgram_item_len * 2)); + } while (ndp_index); - if (index2 == 0 || dg_len2 == 0) - break; - } while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */ + dev_kfree_skb_any(skb); VDBG(port->func.config->cdev, "Parsed NTB with %d frames\n", dgram_counter); @@ -1097,8 +1277,11 @@ static void ncm_disable(struct usb_function *f) DBG(cdev, "ncm deactivated\n"); - if (ncm->port.in_ep->driver_data) + if (ncm->port.in_ep->driver_data) { + ncm->timer_stopping = true; + ncm->netdev = NULL; gether_disconnect(&ncm->port); + } if (ncm->notify->driver_data) { usb_ep_disable(ncm->notify); @@ -1267,6 +1450,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm->port.open = ncm_open; ncm->port.close = ncm_close; + tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm); + hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ncm->task_timer.function = ncm_tx_timeout; + DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ncm->port.in_ep->name, ncm->port.out_ep->name, @@ -1380,6 +1567,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ncm unbind\n"); + hrtimer_cancel(&ncm->task_timer); + tasklet_kill(&ncm->tx_tasklet); + + ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ncm->notify_req->buf); @@ -1416,6 +1607,7 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) ncm->port.ioport = netdev_priv(opts->net); mutex_unlock(&opts->lock); ncm->port.is_fixed = true; + ncm->port.supports_multi_frame = true; ncm->port.func.name = "cdc_network"; /* descriptors are per-instance copies */ diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/function/f_obex.c index aebae1853bce..5f40080c92cc 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -200,19 +200,22 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (alt != 0) goto fail; /* NOP */ - DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, + "reset obex ttyGS%d control\n", obex->port_num); } else if (intf == obex->data_id) { if (alt > 1) goto fail; if (obex->port.in->driver_data) { - DBG(cdev, "reset obex ttyGS%d\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, + "reset obex ttyGS%d\n", obex->port_num); gserial_disconnect(&obex->port); } if (!obex->port.in->desc || !obex->port.out->desc) { - DBG(cdev, "init obex ttyGS%d\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, + "init obex ttyGS%d\n", obex->port_num); if (config_ep_by_speed(cdev->gadget, f, obex->port.in) || config_ep_by_speed(cdev->gadget, f, @@ -224,7 +227,8 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } if (alt == 1) { - DBG(cdev, "activate obex ttyGS%d\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, + "activate obex ttyGS%d\n", obex->port_num); gserial_connect(&obex->port, obex->port_num); } @@ -252,7 +256,7 @@ static void obex_disable(struct usb_function *f) struct f_obex *obex = func_to_obex(f); struct usb_composite_dev *cdev = f->config->cdev; - DBG(cdev, "obex ttyGS%d disable\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num); gserial_disconnect(&obex->port); } @@ -269,7 +273,8 @@ static void obex_connect(struct gserial *g) status = usb_function_activate(&g->func); if (status) - DBG(cdev, "obex ttyGS%d function activate --> %d\n", + dev_dbg(&cdev->gadget->dev, + "obex ttyGS%d function activate --> %d\n", obex->port_num, status); } @@ -284,7 +289,8 @@ static void obex_disconnect(struct gserial *g) status = usb_function_deactivate(&g->func); if (status) - DBG(cdev, "obex ttyGS%d function deactivate --> %d\n", + dev_dbg(&cdev->gadget->dev, + "obex ttyGS%d function deactivate --> %d\n", obex->port_num, status); } @@ -383,10 +389,10 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) obex->can_activate = true; - DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n", - obex->port_num, - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - obex->port.in->name, obex->port.out->name); + dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n", + obex->port_num, + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + obex->port.in->name, obex->port.out->name); return 0; diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index f2b781773eed..b9cfc1571d71 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -721,7 +721,8 @@ struct net_device *gphonet_setup_default(void) struct phonet_port *port; /* Create net device */ - dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup); + dev = alloc_netdev(sizeof(*port), "upnlink%d", NET_NAME_UNKNOWN, + pn_net_setup); if (!dev) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 9c41e9515b8e..ddb09dc6d1f2 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -727,6 +727,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_control_intf.bInterfaceNumber = status; rndis_union_desc.bMasterInterface0 = status; + if (cdev->use_os_string) + f->os_desc_table[0].if_id = + rndis_iad_descriptor.bFirstInterface; + status = usb_interface_id(c, f); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 9ecbcbf36a45..2e02dfabc7ae 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -155,11 +155,13 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* we know alt == 0, so this is an activation or a reset */ if (gser->port.in->driver_data) { - DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); + dev_dbg(&cdev->gadget->dev, + "reset generic ttyGS%d\n", gser->port_num); gserial_disconnect(&gser->port); } if (!gser->port.in->desc || !gser->port.out->desc) { - DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); + dev_dbg(&cdev->gadget->dev, + "activate generic ttyGS%d\n", gser->port_num); if (config_ep_by_speed(cdev->gadget, f, gser->port.in) || config_ep_by_speed(cdev->gadget, f, gser->port.out)) { gser->port.in->desc = NULL; @@ -176,7 +178,8 @@ static void gser_disable(struct usb_function *f) struct f_gser *gser = func_to_gser(f); struct usb_composite_dev *cdev = f->config->cdev; - DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); + dev_dbg(&cdev->gadget->dev, + "generic ttyGS%d deactivated\n", gser->port_num); gserial_disconnect(&gser->port); } @@ -239,11 +242,11 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f) gser_ss_function); if (status) goto fail; - DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", - gser->port_num, - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - gser->port.in->name, gser->port.out->name); + dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", + gser->port_num, + gadget_is_superspeed(c->cdev->gadget) ? "super" : + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + gser->port.in->name, gser->port.out->name); return 0; fail: diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index d3cd52db78fe..80be25b32cd7 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -23,6 +23,15 @@ #include "gadget_chips.h" #include "u_f.h" +#define USB_MS_TO_SS_INTERVAL(x) USB_MS_TO_HS_INTERVAL(x) + +enum eptype { + EP_CONTROL = 0, + EP_BULK, + EP_ISOC, + EP_INTERRUPT, +}; + /* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral * controller drivers. @@ -55,6 +64,8 @@ struct f_sourcesink { struct usb_ep *out_ep; struct usb_ep *iso_in_ep; struct usb_ep *iso_out_ep; + struct usb_ep *int_in_ep; + struct usb_ep *int_out_ep; int cur_alt; }; @@ -68,6 +79,10 @@ static unsigned isoc_interval; static unsigned isoc_maxpacket; static unsigned isoc_mult; static unsigned isoc_maxburst; +static unsigned int_interval; /* In ms */ +static unsigned int_maxpacket; +static unsigned int_mult; +static unsigned int_maxburst; static unsigned buflen; /*-------------------------------------------------------------------------*/ @@ -92,6 +107,16 @@ static struct usb_interface_descriptor source_sink_intf_alt1 = { /* .iInterface = DYNAMIC */ }; +static struct usb_interface_descriptor source_sink_intf_alt2 = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 2, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + /* full speed support: */ static struct usb_endpoint_descriptor fs_source_desc = { @@ -130,6 +155,26 @@ static struct usb_endpoint_descriptor fs_iso_sink_desc = { .bInterval = 4, }; +static struct usb_endpoint_descriptor fs_int_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = GZERO_INT_INTERVAL, +}; + +static struct usb_endpoint_descriptor fs_int_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = GZERO_INT_INTERVAL, +}; + static struct usb_descriptor_header *fs_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &fs_sink_desc, @@ -140,6 +185,10 @@ static struct usb_descriptor_header *fs_source_sink_descs[] = { (struct usb_descriptor_header *) &fs_source_desc, (struct usb_descriptor_header *) &fs_iso_sink_desc, (struct usb_descriptor_header *) &fs_iso_source_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt2, +#define FS_ALT_IFC_2_OFFSET 8 + (struct usb_descriptor_header *) &fs_int_sink_desc, + (struct usb_descriptor_header *) &fs_int_source_desc, NULL, }; @@ -179,6 +228,24 @@ static struct usb_endpoint_descriptor hs_iso_sink_desc = { .bInterval = 4, }; +static struct usb_endpoint_descriptor hs_int_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL), +}; + +static struct usb_endpoint_descriptor hs_int_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL), +}; + static struct usb_descriptor_header *hs_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &hs_source_desc, @@ -189,6 +256,10 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = { (struct usb_descriptor_header *) &hs_sink_desc, (struct usb_descriptor_header *) &hs_iso_source_desc, (struct usb_descriptor_header *) &hs_iso_sink_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt2, +#define HS_ALT_IFC_2_OFFSET 8 + (struct usb_descriptor_header *) &hs_int_source_desc, + (struct usb_descriptor_header *) &hs_int_sink_desc, NULL, }; @@ -264,6 +335,42 @@ static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { .wBytesPerInterval = cpu_to_le16(1024), }; +static struct usb_endpoint_descriptor ss_int_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL), +}; + +struct usb_ss_ep_comp_descriptor ss_int_source_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_int_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL), +}; + +struct usb_ss_ep_comp_descriptor ss_int_sink_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(1024), +}; + static struct usb_descriptor_header *ss_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &ss_source_desc, @@ -280,6 +387,12 @@ static struct usb_descriptor_header *ss_source_sink_descs[] = { (struct usb_descriptor_header *) &ss_iso_source_comp_desc, (struct usb_descriptor_header *) &ss_iso_sink_desc, (struct usb_descriptor_header *) &ss_iso_sink_comp_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt2, +#define SS_ALT_IFC_2_OFFSET 14 + (struct usb_descriptor_header *) &ss_int_source_desc, + (struct usb_descriptor_header *) &ss_int_source_comp_desc, + (struct usb_descriptor_header *) &ss_int_sink_desc, + (struct usb_descriptor_header *) &ss_int_sink_comp_desc, NULL, }; @@ -301,6 +414,21 @@ static struct usb_gadget_strings *sourcesink_strings[] = { }; /*-------------------------------------------------------------------------*/ +static const char *get_ep_string(enum eptype ep_type) +{ + switch (ep_type) { + case EP_ISOC: + return "ISOC-"; + case EP_INTERRUPT: + return "INTERRUPT-"; + case EP_CONTROL: + return "CTRL-"; + case EP_BULK: + return "BULK-"; + default: + return "UNKNOWN-"; + } +} static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) { @@ -328,7 +456,8 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, - struct usb_ep *iso_in, struct usb_ep *iso_out) + struct usb_ep *iso_in, struct usb_ep *iso_out, + struct usb_ep *int_in, struct usb_ep *int_out) { disable_ep(cdev, in); disable_ep(cdev, out); @@ -336,6 +465,10 @@ void disable_endpoints(struct usb_composite_dev *cdev, disable_ep(cdev, iso_in); if (iso_out) disable_ep(cdev, iso_out); + if (int_in) + disable_ep(cdev, int_in); + if (int_out) + disable_ep(cdev, int_out); } static int @@ -352,6 +485,7 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) return id; source_sink_intf_alt0.bInterfaceNumber = id; source_sink_intf_alt1.bInterfaceNumber = id; + source_sink_intf_alt2.bInterfaceNumber = id; /* allocate bulk endpoints */ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); @@ -412,14 +546,55 @@ no_iso: if (isoc_maxpacket > 1024) isoc_maxpacket = 1024; + /* sanity check the interrupt module parameters */ + if (int_interval < 1) + int_interval = 1; + if (int_interval > 4096) + int_interval = 4096; + if (int_mult > 2) + int_mult = 2; + if (int_maxburst > 15) + int_maxburst = 15; + + /* fill in the FS interrupt descriptors from the module parameters */ + fs_int_source_desc.wMaxPacketSize = int_maxpacket > 64 ? + 64 : int_maxpacket; + fs_int_source_desc.bInterval = int_interval > 255 ? + 255 : int_interval; + fs_int_sink_desc.wMaxPacketSize = int_maxpacket > 64 ? + 64 : int_maxpacket; + fs_int_sink_desc.bInterval = int_interval > 255 ? + 255 : int_interval; + + /* allocate int endpoints */ + ss->int_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_source_desc); + if (!ss->int_in_ep) + goto no_int; + ss->int_in_ep->driver_data = cdev; /* claim */ + + ss->int_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_sink_desc); + if (ss->int_out_ep) { + ss->int_out_ep->driver_data = cdev; /* claim */ + } else { + ss->int_in_ep->driver_data = NULL; + ss->int_in_ep = NULL; +no_int: + fs_source_sink_descs[FS_ALT_IFC_2_OFFSET] = NULL; + hs_source_sink_descs[HS_ALT_IFC_2_OFFSET] = NULL; + ss_source_sink_descs[SS_ALT_IFC_2_OFFSET] = NULL; + } + + if (int_maxpacket > 1024) + int_maxpacket = 1024; + /* support high speed hardware */ hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; /* - * Fill in the HS isoc descriptors from the module parameters. - * We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. + * Fill in the HS isoc and interrupt descriptors from the module + * parameters. We assume that the user knows what they are doing and + * won't give parameters that their UDC doesn't support. */ hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; @@ -432,6 +607,17 @@ no_iso: hs_iso_sink_desc.bInterval = isoc_interval; hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; + hs_int_source_desc.wMaxPacketSize = int_maxpacket; + hs_int_source_desc.wMaxPacketSize |= int_mult << 11; + hs_int_source_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval); + hs_int_source_desc.bEndpointAddress = + fs_int_source_desc.bEndpointAddress; + + hs_int_sink_desc.wMaxPacketSize = int_maxpacket; + hs_int_sink_desc.wMaxPacketSize |= int_mult << 11; + hs_int_sink_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval); + hs_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress; + /* support super speed hardware */ ss_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; @@ -439,9 +625,9 @@ no_iso: fs_sink_desc.bEndpointAddress; /* - * Fill in the SS isoc descriptors from the module parameters. - * We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. + * Fill in the SS isoc and interrupt descriptors from the module + * parameters. We assume that the user knows what they are doing and + * won't give parameters that their UDC doesn't support. */ ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; ss_iso_source_desc.bInterval = isoc_interval; @@ -460,17 +646,37 @@ no_iso: isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; + ss_int_source_desc.wMaxPacketSize = int_maxpacket; + ss_int_source_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval); + ss_int_source_comp_desc.bmAttributes = int_mult; + ss_int_source_comp_desc.bMaxBurst = int_maxburst; + ss_int_source_comp_desc.wBytesPerInterval = + int_maxpacket * (int_mult + 1) * (int_maxburst + 1); + ss_int_source_desc.bEndpointAddress = + fs_int_source_desc.bEndpointAddress; + + ss_int_sink_desc.wMaxPacketSize = int_maxpacket; + ss_int_sink_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval); + ss_int_sink_comp_desc.bmAttributes = int_mult; + ss_int_sink_comp_desc.bMaxBurst = int_maxburst; + ss_int_sink_comp_desc.wBytesPerInterval = + int_maxpacket * (int_mult + 1) * (int_maxburst + 1); + ss_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress; + ret = usb_assign_descriptors(f, fs_source_sink_descs, hs_source_sink_descs, ss_source_sink_descs); if (ret) return ret; - DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n", + DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, " + "INT-IN/%s, INT-OUT/%s\n", (gadget_is_superspeed(c->cdev->gadget) ? "super" : (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), f->name, ss->in_ep->name, ss->out_ep->name, ss->iso_in_ep ? ss->iso_in_ep->name : "<none>", - ss->iso_out_ep ? ss->iso_out_ep->name : "<none>"); + ss->iso_out_ep ? ss->iso_out_ep->name : "<none>", + ss->int_in_ep ? ss->int_in_ep->name : "<none>", + ss->int_out_ep ? ss->int_out_ep->name : "<none>"); return 0; } @@ -601,14 +807,15 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) } static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, - bool is_iso, int speed) + enum eptype ep_type, int speed) { struct usb_ep *ep; struct usb_request *req; int i, size, status; for (i = 0; i < 8; i++) { - if (is_iso) { + switch (ep_type) { + case EP_ISOC: switch (speed) { case USB_SPEED_SUPER: size = isoc_maxpacket * (isoc_mult + 1) * @@ -624,9 +831,28 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, } ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; req = ss_alloc_ep_req(ep, size); - } else { + break; + case EP_INTERRUPT: + switch (speed) { + case USB_SPEED_SUPER: + size = int_maxpacket * (int_mult + 1) * + (int_maxburst + 1); + break; + case USB_SPEED_HIGH: + size = int_maxpacket * (int_mult + 1); + break; + default: + size = int_maxpacket > 1023 ? + 1023 : int_maxpacket; + break; + } + ep = is_in ? ss->int_in_ep : ss->int_out_ep; + req = ss_alloc_ep_req(ep, size); + break; + default: ep = is_in ? ss->in_ep : ss->out_ep; req = ss_alloc_ep_req(ep, 0); + break; } if (!req) @@ -644,12 +870,12 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, cdev = ss->function.config->cdev; ERROR(cdev, "start %s%s %s --> %d\n", - is_iso ? "ISO-" : "", is_in ? "IN" : "OUT", - ep->name, status); + get_ep_string(ep_type), is_in ? "IN" : "OUT", + ep->name, status); free_ep_req(ep, req); } - if (!is_iso) + if (!(ep_type == EP_ISOC)) break; } @@ -662,7 +888,7 @@ static void disable_source_sink(struct f_sourcesink *ss) cdev = ss->function.config->cdev; disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep, - ss->iso_out_ep); + ss->iso_out_ep, ss->int_in_ep, ss->int_out_ep); VDBG(cdev, "%s disabled\n", ss->function.name); } @@ -674,6 +900,62 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, int speed = cdev->gadget->speed; struct usb_ep *ep; + if (alt == 2) { + /* Configure for periodic interrupt endpoint */ + ep = ss->int_in_ep; + if (ep) { + result = config_ep_by_speed(cdev->gadget, + &(ss->function), ep); + if (result) + return result; + + result = usb_ep_enable(ep); + if (result < 0) + return result; + + ep->driver_data = ss; + result = source_sink_start_ep(ss, true, EP_INTERRUPT, + speed); + if (result < 0) { +fail1: + ep = ss->int_in_ep; + if (ep) { + usb_ep_disable(ep); + ep->driver_data = NULL; + } + return result; + } + } + + /* + * one interrupt endpoint reads (sinks) anything OUT (from the + * host) + */ + ep = ss->int_out_ep; + if (ep) { + result = config_ep_by_speed(cdev->gadget, + &(ss->function), ep); + if (result) + goto fail1; + + result = usb_ep_enable(ep); + if (result < 0) + goto fail1; + + ep->driver_data = ss; + result = source_sink_start_ep(ss, false, EP_INTERRUPT, + speed); + if (result < 0) { + ep = ss->int_out_ep; + usb_ep_disable(ep); + ep->driver_data = NULL; + goto fail1; + } + } + + goto out; + } + /* one bulk endpoint writes (sources) zeroes IN (to the host) */ ep = ss->in_ep; result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); @@ -684,7 +966,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, return result; ep->driver_data = ss; - result = source_sink_start_ep(ss, true, false, speed); + result = source_sink_start_ep(ss, true, EP_BULK, speed); if (result < 0) { fail: ep = ss->in_ep; @@ -703,7 +985,7 @@ fail: goto fail; ep->driver_data = ss; - result = source_sink_start_ep(ss, false, false, speed); + result = source_sink_start_ep(ss, false, EP_BULK, speed); if (result < 0) { fail2: ep = ss->out_ep; @@ -726,7 +1008,7 @@ fail2: goto fail2; ep->driver_data = ss; - result = source_sink_start_ep(ss, true, true, speed); + result = source_sink_start_ep(ss, true, EP_ISOC, speed); if (result < 0) { fail3: ep = ss->iso_in_ep; @@ -749,13 +1031,14 @@ fail3: goto fail3; ep->driver_data = ss; - result = source_sink_start_ep(ss, false, true, speed); + result = source_sink_start_ep(ss, false, EP_ISOC, speed); if (result < 0) { usb_ep_disable(ep); ep->driver_data = NULL; goto fail3; } } + out: ss->cur_alt = alt; @@ -771,6 +1054,8 @@ static int sourcesink_set_alt(struct usb_function *f, if (ss->in_ep->driver_data) disable_source_sink(ss); + else if (alt == 2 && ss->int_in_ep->driver_data) + disable_source_sink(ss); return enable_source_sink(cdev, ss, alt); } @@ -883,6 +1168,10 @@ static struct usb_function *source_sink_alloc_func( isoc_maxpacket = ss_opts->isoc_maxpacket; isoc_mult = ss_opts->isoc_mult; isoc_maxburst = ss_opts->isoc_maxburst; + int_interval = ss_opts->int_interval; + int_maxpacket = ss_opts->int_maxpacket; + int_mult = ss_opts->int_mult; + int_maxburst = ss_opts->int_maxburst; buflen = ss_opts->bulk_buflen; ss->function.name = "source/sink"; @@ -1179,6 +1468,182 @@ static struct f_ss_opts_attribute f_ss_opts_bulk_buflen = f_ss_opts_bulk_buflen_show, f_ss_opts_bulk_buflen_store); +static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_interval); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u32 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou32(page, 0, &num); + if (ret) + goto end; + + if (num > 4096) { + ret = -EINVAL; + goto end; + } + + opts->int_interval = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_interval = + __CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR, + f_ss_opts_int_interval_show, + f_ss_opts_int_interval_store); + +static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_maxpacket); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u16 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou16(page, 0, &num); + if (ret) + goto end; + + if (num > 1024) { + ret = -EINVAL; + goto end; + } + + opts->int_maxpacket = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_maxpacket = + __CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR, + f_ss_opts_int_maxpacket_show, + f_ss_opts_int_maxpacket_store); + +static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_mult); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 2) { + ret = -EINVAL; + goto end; + } + + opts->int_mult = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_mult = + __CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR, + f_ss_opts_int_mult_show, + f_ss_opts_int_mult_store); + +static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_maxburst); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 15) { + ret = -EINVAL; + goto end; + } + + opts->int_maxburst = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_maxburst = + __CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR, + f_ss_opts_int_maxburst_show, + f_ss_opts_int_maxburst_store); + static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_pattern.attr, &f_ss_opts_isoc_interval.attr, @@ -1186,6 +1651,10 @@ static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_isoc_mult.attr, &f_ss_opts_isoc_maxburst.attr, &f_ss_opts_bulk_buflen.attr, + &f_ss_opts_int_interval.attr, + &f_ss_opts_int_maxpacket.attr, + &f_ss_opts_int_mult.attr, + &f_ss_opts_int_maxburst.attr, NULL, }; @@ -1215,6 +1684,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void) ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; + ss_opts->int_interval = GZERO_INT_INTERVAL; + ss_opts->int_maxpacket = GZERO_INT_MAXPACKET; config_group_init_type_name(&ss_opts->func_inst.group, "", &ss_func_type); diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 1ea8baf33333..1ea8baf33333 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 2b4c82d84bfc..f7b203293205 100644 --- a/drivers/usb/gadget/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -11,24 +11,12 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/atomic.h> #include "u_uac1.h" -#define OUT_EP_MAX_PACKET_SIZE 200 -static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; -module_param(req_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); - -static int req_count = 256; -module_param(req_count, int, S_IRUGO); -MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); - -static int audio_buf_size = 48000; -module_param(audio_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); - static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); @@ -46,7 +34,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); #define F_AUDIO_NUM_INTERFACES 2 /* B.3.1 Standard AC Interface Descriptor */ -static struct usb_interface_descriptor ac_interface_desc __initdata = { +static struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 0, @@ -183,12 +171,12 @@ static struct usb_endpoint_descriptor as_out_ep_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE), + .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), .bInterval = 4, }; /* Class-specific AS ISO OUT Endpoint Descriptor */ -static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { +static struct uac_iso_endpoint_descriptor as_iso_out_desc = { .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = UAC_EP_GENERAL, @@ -197,7 +185,7 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { .wLockDelay = __constant_cpu_to_le16(1), }; -static struct usb_descriptor_header *f_audio_desc[] __initdata = { +static struct usb_descriptor_header *f_audio_desc[] = { (struct usb_descriptor_header *)&ac_interface_desc, (struct usb_descriptor_header *)&ac_header_desc, @@ -216,6 +204,37 @@ static struct usb_descriptor_header *f_audio_desc[] __initdata = { NULL, }; +enum { + STR_AC_IF, + STR_INPUT_TERMINAL, + STR_INPUT_TERMINAL_CH_NAMES, + STR_FEAT_DESC_0, + STR_OUTPUT_TERMINAL, + STR_AS_IF_ALT0, + STR_AS_IF_ALT1, +}; + +static struct usb_string strings_uac1[] = { + [STR_AC_IF].s = "AC Interface", + [STR_INPUT_TERMINAL].s = "Input terminal", + [STR_INPUT_TERMINAL_CH_NAMES].s = "Channels", + [STR_FEAT_DESC_0].s = "Volume control & mute", + [STR_OUTPUT_TERMINAL].s = "Output terminal", + [STR_AS_IF_ALT0].s = "AS Interface", + [STR_AS_IF_ALT1].s = "AS Interface", + { }, +}; + +static struct usb_gadget_strings str_uac1 = { + .language = 0x0409, /* en-us */ + .strings = strings_uac1, +}; + +static struct usb_gadget_strings *uac1_strings[] = { + &str_uac1, + NULL, +}; + /* * This function is an ALSA sound card following USB Audio Class Spec 1.0. */ @@ -300,8 +319,14 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) struct f_audio *audio = req->context; struct usb_composite_dev *cdev = audio->card.func.config->cdev; struct f_audio_buf *copy_buf = audio->copy_buf; + struct f_uac1_opts *opts; + int audio_buf_size; int err; + opts = container_of(audio->card.func.fi, struct f_uac1_opts, + func_inst); + audio_buf_size = opts->audio_buf_size; + if (!copy_buf) return -EINVAL; @@ -546,10 +571,17 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct usb_composite_dev *cdev = f->config->cdev; struct usb_ep *out_ep = audio->out_ep; struct usb_request *req; + struct f_uac1_opts *opts; + int req_buf_size, req_count, audio_buf_size; int i = 0, err = 0; DBG(cdev, "intf %d, alt %d\n", intf, alt); + opts = container_of(f->fi, struct f_uac1_opts, func_inst); + req_buf_size = opts->req_buf_size; + req_count = opts->req_count; + audio_buf_size = opts->audio_buf_size; + if (intf == 1) { if (alt == 1) { usb_ep_enable(out_ep); @@ -625,13 +657,37 @@ static void f_audio_build_desc(struct f_audio *audio) } /* audio function driver setup/binding */ -static int __init +static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_audio *audio = func_to_audio(f); + struct usb_string *us; int status; struct usb_ep *ep = NULL; + struct f_uac1_opts *audio_opts; + + audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); + audio->card.gadget = c->cdev->gadget; + audio_opts->card = &audio->card; + /* set up ASLA audio devices */ + if (!audio_opts->bound) { + status = gaudio_setup(&audio->card); + if (status < 0) + return status; + audio_opts->bound = true; + } + us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); + if (IS_ERR(us)) + return PTR_ERR(us); + ac_interface_desc.iInterface = us[STR_AC_IF].id; + input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id; + input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id; + feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id; + output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id; + as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id; + as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id; + f_audio_build_desc(audio); @@ -666,20 +722,12 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: + gaudio_cleanup(&audio->card); if (ep) ep->driver_data = NULL; return status; } -static void -f_audio_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_audio *audio = func_to_audio(f); - - usb_free_all_descriptors(f); - kfree(audio); -} - /*-------------------------------------------------------------------------*/ static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) @@ -695,7 +743,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) } /* Todo: add more control selecotor dynamically */ -static int __init control_selector_init(struct f_audio *audio) +static int control_selector_init(struct f_audio *audio) { INIT_LIST_HEAD(&audio->cs); list_add(&feature_unit.list, &audio->cs); @@ -712,57 +760,226 @@ static int __init control_selector_init(struct f_audio *audio) return 0; } -/** - * audio_bind_config - add USB audio function to a configuration - * @c: the configuration to supcard the USB audio function - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - */ -static int __init audio_bind_config(struct usb_configuration *c) +static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uac1_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_uac1_opts); +CONFIGFS_ATTR_OPS(f_uac1_opts); + +static void f_uac1_attr_release(struct config_item *item) +{ + struct f_uac1_opts *opts = to_f_uac1_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations f_uac1_item_ops = { + .release = f_uac1_attr_release, + .show_attribute = f_uac1_opts_attr_show, + .store_attribute = f_uac1_opts_attr_store, +}; + +#define UAC1_INT_ATTRIBUTE(name) \ +static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \ + char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + u32 num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou32(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_uac1_opts_attribute f_uac1_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + f_uac1_opts_##name##_show, \ + f_uac1_opts_##name##_store) + +UAC1_INT_ATTRIBUTE(req_buf_size); +UAC1_INT_ATTRIBUTE(req_count); +UAC1_INT_ATTRIBUTE(audio_buf_size); + +#define UAC1_STR_ATTRIBUTE(name) \ +static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \ + char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%s\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret = -EBUSY; \ + char *tmp; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) \ + goto end; \ + \ + tmp = kstrndup(page, len, GFP_KERNEL); \ + if (tmp) { \ + ret = -ENOMEM; \ + goto end; \ + } \ + if (opts->name##_alloc) \ + kfree(opts->name); \ + opts->name##_alloc = true; \ + opts->name = tmp; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_uac1_opts_attribute f_uac1_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + f_uac1_opts_##name##_show, \ + f_uac1_opts_##name##_store) + +UAC1_STR_ATTRIBUTE(fn_play); +UAC1_STR_ATTRIBUTE(fn_cap); +UAC1_STR_ATTRIBUTE(fn_cntl); + +static struct configfs_attribute *f_uac1_attrs[] = { + &f_uac1_opts_req_buf_size.attr, + &f_uac1_opts_req_count.attr, + &f_uac1_opts_audio_buf_size.attr, + &f_uac1_opts_fn_play.attr, + &f_uac1_opts_fn_cap.attr, + &f_uac1_opts_fn_cntl.attr, + NULL, +}; + +static struct config_item_type f_uac1_func_type = { + .ct_item_ops = &f_uac1_item_ops, + .ct_attrs = f_uac1_attrs, + .ct_owner = THIS_MODULE, +}; + +static void f_audio_free_inst(struct usb_function_instance *f) +{ + struct f_uac1_opts *opts; + + opts = container_of(f, struct f_uac1_opts, func_inst); + gaudio_cleanup(opts->card); + if (opts->fn_play_alloc) + kfree(opts->fn_play); + if (opts->fn_cap_alloc) + kfree(opts->fn_cap); + if (opts->fn_cntl_alloc) + kfree(opts->fn_cntl); + kfree(opts); +} + +static struct usb_function_instance *f_audio_alloc_inst(void) +{ + struct f_uac1_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = f_audio_free_inst; + + config_group_init_type_name(&opts->func_inst.group, "", + &f_uac1_func_type); + + opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; + opts->req_count = UAC1_REQ_COUNT; + opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE; + opts->fn_play = FILE_PCM_PLAYBACK; + opts->fn_cap = FILE_PCM_CAPTURE; + opts->fn_cntl = FILE_CONTROL; + return &opts->func_inst; +} + +static void f_audio_free(struct usb_function *f) +{ + struct f_audio *audio = func_to_audio(f); + struct f_uac1_opts *opts; + + opts = container_of(f->fi, struct f_uac1_opts, func_inst); + kfree(audio); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + usb_free_all_descriptors(f); +} + +static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) { struct f_audio *audio; - int status; + struct f_uac1_opts *opts; /* allocate and initialize one new instance */ - audio = kzalloc(sizeof *audio, GFP_KERNEL); + audio = kzalloc(sizeof(*audio), GFP_KERNEL); if (!audio) - return -ENOMEM; + return ERR_PTR(-ENOMEM); audio->card.func.name = "g_audio"; - audio->card.gadget = c->cdev->gadget; + opts = container_of(fi, struct f_uac1_opts, func_inst); + mutex_lock(&opts->lock); + ++opts->refcnt; + mutex_unlock(&opts->lock); INIT_LIST_HEAD(&audio->play_queue); spin_lock_init(&audio->lock); - /* set up ASLA audio devices */ - status = gaudio_setup(&audio->card); - if (status < 0) - goto setup_fail; - - audio->card.func.strings = audio_strings; audio->card.func.bind = f_audio_bind; audio->card.func.unbind = f_audio_unbind; audio->card.func.set_alt = f_audio_set_alt; audio->card.func.setup = f_audio_setup; audio->card.func.disable = f_audio_disable; + audio->card.func.free_func = f_audio_free; control_selector_init(audio); INIT_WORK(&audio->playback_work, f_audio_playback_work); - status = usb_add_function(c, &audio->card.func); - if (status) - goto add_fail; - - INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n", - audio_buf_size, req_buf_size, req_count); - - return status; - -add_fail: - gaudio_cleanup(); -setup_fail: - kfree(audio); - return status; + return &audio->card.func; } + +DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bryan Wu"); diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 6261db4a9910..a5a27a504d67 100644 --- a/drivers/usb/gadget/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -20,35 +20,7 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> -/* Playback(USB-IN) Default Stereo - Fl/Fr */ -static int p_chmask = 0x3; -module_param(p_chmask, uint, S_IRUGO); -MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); - -/* Playback Default 48 KHz */ -static int p_srate = 48000; -module_param(p_srate, uint, S_IRUGO); -MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); - -/* Playback Default 16bits/sample */ -static int p_ssize = 2; -module_param(p_ssize, uint, S_IRUGO); -MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); - -/* Capture(USB-OUT) Default Stereo - Fl/Fr */ -static int c_chmask = 0x3; -module_param(c_chmask, uint, S_IRUGO); -MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); - -/* Capture Default 64 KHz */ -static int c_srate = 64000; -module_param(c_srate, uint, S_IRUGO); -MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); - -/* Capture Default 16bits/sample */ -static int c_ssize = 2; -module_param(c_ssize, uint, S_IRUGO); -MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); +#include "u_uac2.h" /* Keep everyone on toes */ #define USB_XFERS 2 @@ -120,6 +92,15 @@ struct snd_uac2_chip { struct snd_card *card; struct snd_pcm *pcm; + + /* timekeeping for the playback endpoint */ + unsigned int p_interval; + unsigned int p_residue; + + /* pre-calculated values for playback iso completion */ + unsigned int p_pktsize; + unsigned int p_pktsize_residue; + unsigned int p_framesize; }; #define BUFF_SIZE_MAX (PAGE_SIZE * 16) @@ -149,8 +130,6 @@ struct audio_dev { struct snd_uac2_chip uac2; }; -static struct audio_dev *agdev_g; - static inline struct audio_dev *func_to_agdev(struct usb_function *f) { @@ -170,6 +149,12 @@ struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p) } static inline +struct f_uac2_opts *agdev_to_uac2_opts(struct audio_dev *agdev) +{ + return container_of(agdev->func.fi, struct f_uac2_opts, func_inst); +} + +static inline uint num_channels(uint chanmask) { uint num = 0; @@ -187,8 +172,8 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) { unsigned pending; unsigned long flags; + unsigned int hw_ptr; bool update_alsa = false; - unsigned char *src, *dst; int status = req->status; struct uac2_req *ur = req->context; struct snd_pcm_substream *substream; @@ -216,12 +201,27 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) spin_lock_irqsave(&prm->lock, flags); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - src = prm->dma_area + prm->hw_ptr; + /* + * For each IN packet, take the quotient of the current data + * rate and the endpoint's interval as the base packet size. + * If there is a residue from this division, add it to the + * residue accumulator. + */ + req->length = uac2->p_pktsize; + uac2->p_residue += uac2->p_pktsize_residue; + + /* + * Whenever there are more bytes in the accumulator than we + * need to add one more sample frame, increase this packet's + * size and decrease the accumulator. + */ + if (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) { + req->length += uac2->p_framesize; + uac2->p_residue -= uac2->p_framesize * + uac2->p_interval; + } + req->actual = req->length; - dst = req->buf; - } else { - dst = prm->dma_area + prm->hw_ptr; - src = req->buf; } pending = prm->hw_ptr % prm->period_size; @@ -229,12 +229,32 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) if (pending >= prm->period_size) update_alsa = true; + hw_ptr = prm->hw_ptr; prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes; spin_unlock_irqrestore(&prm->lock, flags); /* Pack USB load in ALSA ring buffer */ - memcpy(dst, src, req->actual); + pending = prm->dma_bytes - hw_ptr; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (unlikely(pending < req->actual)) { + memcpy(req->buf, prm->dma_area + hw_ptr, pending); + memcpy(req->buf + pending, prm->dma_area, + req->actual - pending); + } else { + memcpy(req->buf, prm->dma_area + hw_ptr, req->actual); + } + } else { + if (unlikely(pending < req->actual)) { + memcpy(prm->dma_area + hw_ptr, req->buf, pending); + memcpy(prm->dma_area, req->buf + pending, + req->actual - pending); + } else { + memcpy(prm->dma_area + hw_ptr, req->buf, req->actual); + } + } + exit: if (usb_ep_queue(ep, req, GFP_ATOMIC)) dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__); @@ -342,20 +362,55 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream) { struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio_dev; + struct f_uac2_opts *opts; + int p_ssize, c_ssize; + int p_srate, c_srate; + int p_chmask, c_chmask; + + audio_dev = uac2_to_agdev(uac2); + opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst); + p_ssize = opts->p_ssize; + c_ssize = opts->c_ssize; + p_srate = opts->p_srate; + c_srate = opts->c_srate; + p_chmask = opts->p_chmask; + c_chmask = opts->c_chmask; + uac2->p_residue = 0; runtime->hw = uac2_pcm_hardware; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { spin_lock_init(&uac2->p_prm.lock); runtime->hw.rate_min = p_srate; - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */ + switch (p_ssize) { + case 3: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE; + break; + case 4: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + break; + } runtime->hw.channels_min = num_channels(p_chmask); runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize / runtime->hw.periods_min; } else { spin_lock_init(&uac2->c_prm.lock); runtime->hw.rate_min = c_srate; - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */ + switch (c_ssize) { + case 3: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE; + break; + case 4: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + break; + } runtime->hw.channels_min = num_channels(c_chmask); runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize / runtime->hw.periods_min; @@ -391,7 +446,15 @@ static int snd_uac2_probe(struct platform_device *pdev) struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev); struct snd_card *card; struct snd_pcm *pcm; + struct audio_dev *audio_dev; + struct f_uac2_opts *opts; int err; + int p_chmask, c_chmask; + + audio_dev = uac2_to_agdev(uac2); + opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst); + p_chmask = opts->p_chmask; + c_chmask = opts->c_chmask; /* Choose any slot, with no id */ err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); @@ -899,20 +962,58 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) "%s:%d Error!\n", __func__, __LINE__); } -static int __init +static int afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) { struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; struct usb_composite_dev *cdev = cfg->cdev; struct usb_gadget *gadget = cdev->gadget; + struct device *dev = &uac2->pdev.dev; struct uac2_rtd_params *prm; + struct f_uac2_opts *uac2_opts; + struct usb_string *us; int ret; + uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); + + us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); + if (IS_ERR(us)) + return PTR_ERR(us); + iad_desc.iFunction = us[STR_ASSOC].id; + std_ac_if_desc.iInterface = us[STR_IF_CTRL].id; + in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id; + out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id; + usb_out_it_desc.iTerminal = us[STR_USB_IT].id; + io_in_it_desc.iTerminal = us[STR_IO_IT].id; + usb_in_ot_desc.iTerminal = us[STR_USB_OT].id; + io_out_ot_desc.iTerminal = us[STR_IO_OT].id; + std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id; + std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id; + std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id; + std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id; + + + /* Initialize the configurable parameters */ + usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask); + usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); + io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask); + io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask); + as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask); + as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); + as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask); + as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask); + as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize; + as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8; + as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize; + as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8; + + snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate); + snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate); + ret = usb_interface_id(cfg, fn); if (ret < 0) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } std_ac_if_desc.bInterfaceNumber = ret; @@ -921,8 +1022,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_interface_id(cfg, fn); if (ret < 0) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } std_as_out_if0_desc.bInterfaceNumber = ret; @@ -932,8 +1032,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_interface_id(cfg, fn); if (ret < 0) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } std_as_in_if0_desc.bInterfaceNumber = ret; @@ -943,16 +1042,14 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); if (!agdev->out_ep) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err; } agdev->out_ep->driver_data = agdev; agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); if (!agdev->in_ep) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err; } agdev->in_ep->driver_data = agdev; @@ -1000,27 +1097,6 @@ err: return -EINVAL; } -static void -afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) -{ - struct audio_dev *agdev = func_to_agdev(fn); - struct uac2_rtd_params *prm; - - alsa_uac2_exit(agdev); - - prm = &agdev->uac2.p_prm; - kfree(prm->rbuf); - - prm = &agdev->uac2.c_prm; - kfree(prm->rbuf); - usb_free_all_descriptors(fn); - - if (agdev->in_ep) - agdev->in_ep->driver_data = NULL; - if (agdev->out_ep) - agdev->out_ep->driver_data = NULL; -} - static int afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) { @@ -1028,23 +1104,22 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; struct usb_gadget *gadget = cdev->gadget; + struct device *dev = &uac2->pdev.dev; struct usb_request *req; struct usb_ep *ep; struct uac2_rtd_params *prm; - int i; + int req_len, i; /* No i/f has more than 2 alt settings */ if (alt > 1) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; } if (intf == agdev->ac_intf) { /* Control I/f has only 1 AltSetting - 0 */ if (alt) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; } return 0; @@ -1055,14 +1130,43 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) prm = &uac2->c_prm; config_ep_by_speed(gadget, fn, ep); agdev->as_out_alt = alt; + req_len = prm->max_psize; } else if (intf == agdev->as_in_intf) { + struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev); + unsigned int factor, rate; + struct usb_endpoint_descriptor *ep_desc; + ep = agdev->in_ep; prm = &uac2->p_prm; config_ep_by_speed(gadget, fn, ep); agdev->as_in_alt = alt; + + /* pre-calculate the playback endpoint's interval */ + if (gadget->speed == USB_SPEED_FULL) { + ep_desc = &fs_epin_desc; + factor = 1000; + } else { + ep_desc = &hs_epin_desc; + factor = 125; + } + + /* pre-compute some values for iso_complete() */ + uac2->p_framesize = opts->p_ssize * + num_channels(opts->p_chmask); + rate = opts->p_srate * uac2->p_framesize; + uac2->p_interval = (1 << (ep_desc->bInterval - 1)) * factor; + uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval, + prm->max_psize); + + if (uac2->p_pktsize < prm->max_psize) + uac2->p_pktsize_residue = rate % uac2->p_interval; + else + uac2->p_pktsize_residue = 0; + + req_len = uac2->p_pktsize; + uac2->p_residue = 0; } else { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; } @@ -1075,31 +1179,23 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) usb_ep_enable(ep); for (i = 0; i < USB_XFERS; i++) { - if (prm->ureq[i].req) { - if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) - dev_err(&uac2->pdev.dev, "%d Error!\n", - __LINE__); - continue; + if (!prm->ureq[i].req) { + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req == NULL) + return -ENOMEM; + + prm->ureq[i].req = req; + prm->ureq[i].pp = prm; + + req->zero = 0; + req->context = &prm->ureq[i]; + req->length = req_len; + req->complete = agdev_iso_complete; + req->buf = prm->rbuf + i * prm->max_psize; } - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req == NULL) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); - return -EINVAL; - } - - prm->ureq[i].req = req; - prm->ureq[i].pp = prm; - - req->zero = 0; - req->context = &prm->ureq[i]; - req->length = prm->max_psize; - req->complete = agdev_iso_complete; - req->buf = prm->rbuf + i * req->length; - - if (usb_ep_queue(ep, req, GFP_ATOMIC)) - dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__); + if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); } return 0; @@ -1144,12 +1240,18 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) struct usb_request *req = fn->config->cdev->req; struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; + struct f_uac2_opts *opts; u16 w_length = le16_to_cpu(cr->wLength); u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; + int p_srate, c_srate; + + opts = agdev_to_uac2_opts(agdev); + p_srate = opts->p_srate; + c_srate = opts->c_srate; if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { struct cntrl_cur_lay3 c; @@ -1179,6 +1281,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) struct usb_request *req = fn->config->cdev->req; struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; + struct f_uac2_opts *opts; u16 w_length = le16_to_cpu(cr->wLength); u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); @@ -1186,6 +1289,11 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 control_selector = w_value >> 8; struct cntrl_range_lay3 r; int value = -EOPNOTSUPP; + int p_srate, c_srate; + + opts = agdev_to_uac2_opts(agdev); + p_srate = opts->p_srate; + c_srate = opts->c_srate; if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { if (entity_id == USB_IN_CLK_ID) @@ -1289,66 +1397,184 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) return value; } -static int audio_bind_config(struct usb_configuration *cfg) +static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item) { - int res; - - agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL); - if (agdev_g == NULL) - return -ENOMEM; - - res = usb_string_ids_tab(cfg->cdev, strings_fn); - if (res) - return res; - iad_desc.iFunction = strings_fn[STR_ASSOC].id; - std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id; - in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id; - out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id; - usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id; - io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id; - usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id; - io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id; - std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id; - std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id; - std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id; - std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id; - - agdev_g->func.name = "uac2_func"; - agdev_g->func.strings = fn_strings; - agdev_g->func.bind = afunc_bind; - agdev_g->func.unbind = afunc_unbind; - agdev_g->func.set_alt = afunc_set_alt; - agdev_g->func.get_alt = afunc_get_alt; - agdev_g->func.disable = afunc_disable; - agdev_g->func.setup = afunc_setup; + return container_of(to_config_group(item), struct f_uac2_opts, + func_inst.group); +} - /* Initialize the configurable parameters */ - usb_out_it_desc.bNrChannels = num_channels(c_chmask); - usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask); - io_in_it_desc.bNrChannels = num_channels(p_chmask); - io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask); - as_out_hdr_desc.bNrChannels = num_channels(c_chmask); - as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask); - as_in_hdr_desc.bNrChannels = num_channels(p_chmask); - as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask); - as_out_fmt1_desc.bSubslotSize = c_ssize; - as_out_fmt1_desc.bBitResolution = c_ssize * 8; - as_in_fmt1_desc.bSubslotSize = p_ssize; - as_in_fmt1_desc.bBitResolution = p_ssize * 8; - - snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate); - snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate); - - res = usb_add_function(cfg, &agdev_g->func); - if (res < 0) - kfree(agdev_g); - - return res; +CONFIGFS_ATTR_STRUCT(f_uac2_opts); +CONFIGFS_ATTR_OPS(f_uac2_opts); + +static void f_uac2_attr_release(struct config_item *item) +{ + struct f_uac2_opts *opts = to_f_uac2_opts(item); + + usb_put_function_instance(&opts->func_inst); } -static void -uac2_unbind_config(struct usb_configuration *cfg) +static struct configfs_item_operations f_uac2_item_ops = { + .release = f_uac2_attr_release, + .show_attribute = f_uac2_opts_attr_show, + .store_attribute = f_uac2_opts_attr_store, +}; + +#define UAC2_ATTRIBUTE(name) \ +static ssize_t f_uac2_opts_##name##_show(struct f_uac2_opts *opts, \ + char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_uac2_opts_##name##_store(struct f_uac2_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + u32 num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou32(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_uac2_opts_attribute f_uac2_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + f_uac2_opts_##name##_show, \ + f_uac2_opts_##name##_store) + +UAC2_ATTRIBUTE(p_chmask); +UAC2_ATTRIBUTE(p_srate); +UAC2_ATTRIBUTE(p_ssize); +UAC2_ATTRIBUTE(c_chmask); +UAC2_ATTRIBUTE(c_srate); +UAC2_ATTRIBUTE(c_ssize); + +static struct configfs_attribute *f_uac2_attrs[] = { + &f_uac2_opts_p_chmask.attr, + &f_uac2_opts_p_srate.attr, + &f_uac2_opts_p_ssize.attr, + &f_uac2_opts_c_chmask.attr, + &f_uac2_opts_c_srate.attr, + &f_uac2_opts_c_ssize.attr, + NULL, +}; + +static struct config_item_type f_uac2_func_type = { + .ct_item_ops = &f_uac2_item_ops, + .ct_attrs = f_uac2_attrs, + .ct_owner = THIS_MODULE, +}; + +static void afunc_free_inst(struct usb_function_instance *f) +{ + struct f_uac2_opts *opts; + + opts = container_of(f, struct f_uac2_opts, func_inst); + kfree(opts); +} + +static struct usb_function_instance *afunc_alloc_inst(void) +{ + struct f_uac2_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = afunc_free_inst; + + config_group_init_type_name(&opts->func_inst.group, "", + &f_uac2_func_type); + + opts->p_chmask = UAC2_DEF_PCHMASK; + opts->p_srate = UAC2_DEF_PSRATE; + opts->p_ssize = UAC2_DEF_PSSIZE; + opts->c_chmask = UAC2_DEF_CCHMASK; + opts->c_srate = UAC2_DEF_CSRATE; + opts->c_ssize = UAC2_DEF_CSSIZE; + return &opts->func_inst; +} + +static void afunc_free(struct usb_function *f) { - kfree(agdev_g); - agdev_g = NULL; + struct audio_dev *agdev; + struct f_uac2_opts *opts; + + agdev = func_to_agdev(f); + opts = container_of(f->fi, struct f_uac2_opts, func_inst); + kfree(agdev); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); } + +static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct audio_dev *agdev = func_to_agdev(f); + struct uac2_rtd_params *prm; + + alsa_uac2_exit(agdev); + + prm = &agdev->uac2.p_prm; + kfree(prm->rbuf); + + prm = &agdev->uac2.c_prm; + kfree(prm->rbuf); + usb_free_all_descriptors(f); + + if (agdev->in_ep) + agdev->in_ep->driver_data = NULL; + if (agdev->out_ep) + agdev->out_ep->driver_data = NULL; +} + +struct usb_function *afunc_alloc(struct usb_function_instance *fi) +{ + struct audio_dev *agdev; + struct f_uac2_opts *opts; + + agdev = kzalloc(sizeof(*agdev), GFP_KERNEL); + if (agdev == NULL) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_uac2_opts, func_inst); + mutex_lock(&opts->lock); + ++opts->refcnt; + mutex_unlock(&opts->lock); + + agdev->func.name = "uac2_func"; + agdev->func.bind = afunc_bind; + agdev->func.unbind = afunc_unbind; + agdev->func.set_alt = afunc_set_alt; + agdev->func.get_alt = afunc_get_alt; + agdev->func.disable = afunc_disable; + agdev->func.setup = afunc_setup; + agdev->func.free_func = afunc_free; + + return &agdev->func; +} + +DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yadwinder Singh"); +MODULE_AUTHOR("Jaswinder Singh"); diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e2a1f50bd93c..e126439e4b65 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -11,6 +11,7 @@ */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/fs.h> @@ -27,24 +28,12 @@ #include <media/v4l2-event.h> #include "uvc.h" +#include "uvc_v4l2.h" +#include "uvc_video.h" +#include "u_uvc.h" unsigned int uvc_gadget_trace_param; -/*-------------------------------------------------------------------------*/ - -/* module parameters specific to the Video streaming endpoint */ -static unsigned int streaming_interval = 1; -module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_interval, "1 - 16"); - -static unsigned int streaming_maxpacket = 1024; -module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); - -static unsigned int streaming_maxburst; -module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); - /* -------------------------------------------------------------------------- * Function descriptors */ @@ -75,7 +64,7 @@ static struct usb_gadget_strings *uvc_function_strings[] = { #define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */ -static struct usb_interface_assoc_descriptor uvc_iad __initdata = { +static struct usb_interface_assoc_descriptor uvc_iad = { .bLength = sizeof(uvc_iad), .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, .bFirstInterface = 0, @@ -86,7 +75,7 @@ static struct usb_interface_assoc_descriptor uvc_iad __initdata = { .iFunction = 0, }; -static struct usb_interface_descriptor uvc_control_intf __initdata = { +static struct usb_interface_descriptor uvc_control_intf = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = UVC_INTF_VIDEO_CONTROL, @@ -98,7 +87,7 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_control_ep __initdata = { +static struct usb_endpoint_descriptor uvc_control_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -107,7 +96,7 @@ static struct usb_endpoint_descriptor uvc_control_ep __initdata = { .bInterval = 8, }; -static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { +static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp = { .bLength = sizeof(uvc_ss_control_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* The following 3 values can be tweaked if necessary. */ @@ -116,14 +105,14 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), }; -static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = { +static struct uvc_control_endpoint_descriptor uvc_control_cs_ep = { .bLength = UVC_DT_CONTROL_ENDPOINT_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubType = UVC_EP_INTERRUPT, .wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), }; -static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { +static struct usb_interface_descriptor uvc_streaming_intf_alt0 = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, @@ -135,7 +124,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { .iInterface = 0, }; -static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { +static struct usb_interface_descriptor uvc_streaming_intf_alt1 = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, @@ -147,7 +136,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { +static struct usb_endpoint_descriptor uvc_fs_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -158,7 +147,7 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { */ }; -static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { +static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -169,7 +158,7 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { */ }; -static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { +static struct usb_endpoint_descriptor uvc_ss_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -181,7 +170,7 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { */ }; -static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = { +static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { .bLength = sizeof(uvc_ss_streaming_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* The bMaxBurst, bmAttributes and wBytesPerInterval values will be @@ -208,6 +197,12 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = { NULL, }; +void uvc_set_trace_param(unsigned int trace) +{ + uvc_gadget_trace_param = trace; +} +EXPORT_SYMBOL(uvc_set_trace_param); + /* -------------------------------------------------------------------------- * Control requests */ @@ -251,6 +246,12 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE) return -EINVAL; + /* Tell the complete callback to generate an event for the next request + * that will be enqueued by UVCIOC_SEND_RESPONSE. + */ + uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN); + uvc->event_length = le16_to_cpu(ctrl->wLength); + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_SETUP; memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req)); @@ -408,7 +409,9 @@ uvc_register_video(struct uvc_device *uvc) video->v4l2_dev = &uvc->v4l2_dev; video->fops = &uvc_v4l2_fops; + video->ioctl_ops = &uvc_v4l2_ioctl_ops; video->release = video_device_release; + video->vfl_dir = VFL_DIR_TX; strlcpy(video->name, cdev->gadget->name, sizeof(video->name)); uvc->vdev = video; @@ -434,7 +437,7 @@ uvc_register_video(struct uvc_device *uvc) } \ } while (0) -static struct usb_descriptor_header ** __init +static struct usb_descriptor_header ** uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) { struct uvc_input_header_descriptor *uvc_streaming_header; @@ -554,45 +557,26 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) return hdr; } -static void -uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct uvc_device *uvc = to_uvc(f); - - INFO(cdev, "uvc_function_unbind\n"); - - video_unregister_device(uvc->vdev); - v4l2_device_unregister(&uvc->v4l2_dev); - uvc->control_ep->driver_data = NULL; - uvc->video.ep->driver_data = NULL; - - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0; - usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); - kfree(uvc->control_buf); - - usb_free_all_descriptors(f); - - kfree(uvc); -} - -static int __init +static int uvc_function_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); + struct usb_string *us; unsigned int max_packet_mult; unsigned int max_packet_size; struct usb_ep *ep; + struct f_uvc_opts *opts; int ret = -EINVAL; INFO(cdev, "uvc_function_bind\n"); + opts = to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ - streaming_interval = clamp(streaming_interval, 1U, 16U); - streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U); - streaming_maxburst = min(streaming_maxburst, 15U); + opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); + opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); + opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); /* Fill in the FS/HS/SS Video Streaming specific descriptors from the * module parameters. @@ -600,30 +584,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) * NOTE: We assume that the user knows what they are doing and won't * give parameters that their UDC doesn't support. */ - if (streaming_maxpacket <= 1024) { + if (opts->streaming_maxpacket <= 1024) { max_packet_mult = 1; - max_packet_size = streaming_maxpacket; - } else if (streaming_maxpacket <= 2048) { + max_packet_size = opts->streaming_maxpacket; + } else if (opts->streaming_maxpacket <= 2048) { max_packet_mult = 2; - max_packet_size = streaming_maxpacket / 2; + max_packet_size = opts->streaming_maxpacket / 2; } else { max_packet_mult = 3; - max_packet_size = streaming_maxpacket / 3; + max_packet_size = opts->streaming_maxpacket / 3; } - uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U); - uvc_fs_streaming_ep.bInterval = streaming_interval; + uvc_fs_streaming_ep.wMaxPacketSize = + cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); + uvc_fs_streaming_ep.bInterval = opts->streaming_interval; - uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size; - uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11); - uvc_hs_streaming_ep.bInterval = streaming_interval; + uvc_hs_streaming_ep.wMaxPacketSize = + cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); + uvc_hs_streaming_ep.bInterval = opts->streaming_interval; - uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size; - uvc_ss_streaming_ep.bInterval = streaming_interval; + uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); + uvc_ss_streaming_ep.bInterval = opts->streaming_interval; uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; - uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; + uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = - max_packet_size * max_packet_mult * streaming_maxburst; + cpu_to_le16(max_packet_size * max_packet_mult * + opts->streaming_maxburst); /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); @@ -653,6 +639,18 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; + us = usb_gstrings_attach(cdev, uvc_function_strings, + ARRAY_SIZE(uvc_en_us_strings)); + if (IS_ERR(us)) { + ret = PTR_ERR(us); + goto error; + } + uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id; + uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id; + ret = us[UVC_STRING_STREAMING_IDX].id; + uvc_streaming_intf_alt0.iInterface = ret; + uvc_streaming_intf_alt1.iInterface = ret; + /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) goto error; @@ -697,7 +695,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) } /* Initialise video. */ - ret = uvc_video_init(&uvc->video); + ret = uvcg_video_init(&uvc->video); if (ret < 0) goto error; @@ -720,10 +718,9 @@ error: if (uvc->video.ep) uvc->video.ep->driver_data = NULL; - if (uvc->control_req) { + if (uvc->control_req) usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); - kfree(uvc->control_buf); - } + kfree(uvc->control_buf); usb_free_all_descriptors(f); return ret; @@ -733,104 +730,81 @@ error: * USB gadget function */ -/** - * uvc_bind_config - add a UVC function to a configuration - * @c: the configuration to support the UVC instance - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @uvc_setup(). Caller is also responsible for - * calling @uvc_cleanup() before module unload. - */ -int __init -uvc_bind_config(struct usb_configuration *c, - const struct uvc_descriptor_header * const *fs_control, - const struct uvc_descriptor_header * const *ss_control, - const struct uvc_descriptor_header * const *fs_streaming, - const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming) +static void uvc_free_inst(struct usb_function_instance *f) { - struct uvc_device *uvc; - int ret = 0; + struct f_uvc_opts *opts = to_f_uvc_opts(f); - /* TODO Check if the USB device controller supports the required - * features. - */ - if (!gadget_is_dualspeed(c->cdev->gadget)) - return -EINVAL; + kfree(opts); +} - uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); - if (uvc == NULL) - return -ENOMEM; +static struct usb_function_instance *uvc_alloc_inst(void) +{ + struct f_uvc_opts *opts; - uvc->state = UVC_STATE_DISCONNECTED; + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + opts->func_inst.free_func_inst = uvc_free_inst; - /* Validate the descriptors. */ - if (fs_control == NULL || fs_control[0] == NULL || - fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) - goto error; + return &opts->func_inst; +} - if (ss_control == NULL || ss_control[0] == NULL || - ss_control[0]->bDescriptorSubType != UVC_VC_HEADER) - goto error; +static void uvc_free(struct usb_function *f) +{ + struct uvc_device *uvc = to_uvc(f); - if (fs_streaming == NULL || fs_streaming[0] == NULL || - fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) - goto error; + kfree(uvc); +} - if (hs_streaming == NULL || hs_streaming[0] == NULL || - hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) - goto error; +static void uvc_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct uvc_device *uvc = to_uvc(f); - if (ss_streaming == NULL || ss_streaming[0] == NULL || - ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) - goto error; + INFO(cdev, "%s\n", __func__); - uvc->desc.fs_control = fs_control; - uvc->desc.ss_control = ss_control; - uvc->desc.fs_streaming = fs_streaming; - uvc->desc.hs_streaming = hs_streaming; - uvc->desc.ss_streaming = ss_streaming; + video_unregister_device(uvc->vdev); + v4l2_device_unregister(&uvc->v4l2_dev); + uvc->control_ep->driver_data = NULL; + uvc->video.ep->driver_data = NULL; - /* String descriptors are global, we only need to allocate string IDs - * for the first UVC function. UVC functions beyond the first (if any) - * will reuse the same IDs. - */ - if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) { - ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings); - if (ret) - goto error; - uvc_iad.iFunction = - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; - uvc_control_intf.iInterface = - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; - ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id; - uvc_streaming_intf_alt0.iInterface = ret; - uvc_streaming_intf_alt1.iInterface = ret; - } + usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); + kfree(uvc->control_buf); + + usb_free_all_descriptors(f); +} + +static struct usb_function *uvc_alloc(struct usb_function_instance *fi) +{ + struct uvc_device *uvc; + struct f_uvc_opts *opts; + + uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); + if (uvc == NULL) + return ERR_PTR(-ENOMEM); + + uvc->state = UVC_STATE_DISCONNECTED; + opts = to_f_uvc_opts(fi); + + uvc->desc.fs_control = opts->fs_control; + uvc->desc.ss_control = opts->ss_control; + uvc->desc.fs_streaming = opts->fs_streaming; + uvc->desc.hs_streaming = opts->hs_streaming; + uvc->desc.ss_streaming = opts->ss_streaming; /* Register the function. */ uvc->func.name = "uvc"; - uvc->func.strings = uvc_function_strings; uvc->func.bind = uvc_function_bind; - uvc->func.unbind = uvc_function_unbind; + uvc->func.unbind = uvc_unbind; uvc->func.get_alt = uvc_function_get_alt; uvc->func.set_alt = uvc_function_set_alt; uvc->func.disable = uvc_function_disable; uvc->func.setup = uvc_function_setup; + uvc->func.free_func = uvc_free; - ret = usb_add_function(c, &uvc->func); - if (ret) - kfree(uvc); - - return ret; - -error: - kfree(uvc); - return ret; + return &uvc->func; } -module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(trace, "Trace level bitmask"); - +DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Laurent Pinchart"); diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index ec52752f7326..d0a73bdcbba1 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -16,12 +16,13 @@ #include <linux/usb/composite.h> #include <linux/usb/video.h> -int uvc_bind_config(struct usb_configuration *c, - const struct uvc_descriptor_header * const *fs_control, - const struct uvc_descriptor_header * const *hs_control, - const struct uvc_descriptor_header * const *fs_streaming, - const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming); +#include "uvc.h" + +void uvc_function_setup_continue(struct uvc_device *uvc); + +void uvc_function_connect(struct uvc_device *uvc); + +void uvc_function_disconnect(struct uvc_device *uvc); #endif /* _F_UVC_H_ */ diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/function/g_zero.h index 15f180904f8a..2ce28b9d97cc 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/function/g_zero.h @@ -10,6 +10,8 @@ #define GZERO_QLEN 32 #define GZERO_ISOC_INTERVAL 4 #define GZERO_ISOC_MAXPACKET 1024 +#define GZERO_INT_INTERVAL 1 /* Default interrupt interval = 1 ms */ +#define GZERO_INT_MAXPACKET 1024 struct usb_zero_options { unsigned pattern; @@ -17,6 +19,10 @@ struct usb_zero_options { unsigned isoc_maxpacket; unsigned isoc_mult; unsigned isoc_maxburst; + unsigned int_interval; /* In ms */ + unsigned int_maxpacket; + unsigned int_mult; + unsigned int_maxburst; unsigned bulk_buflen; unsigned qlen; }; @@ -28,6 +34,10 @@ struct f_ss_opts { unsigned isoc_maxpacket; unsigned isoc_mult; unsigned isoc_maxburst; + unsigned int_interval; /* In ms */ + unsigned int_maxpacket; + unsigned int_mult; + unsigned int_maxburst; unsigned bulk_buflen; /* @@ -62,6 +72,7 @@ int lb_modinit(void); void free_ep_req(struct usb_ep *ep, struct usb_request *req); void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, - struct usb_ep *iso_in, struct usb_ep *iso_out); + struct usb_ep *iso_in, struct usb_ep *iso_out, + struct usb_ep *int_in, struct usb_ep *int_out); #endif /* __G_ZERO_H */ diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/function/ndis.h index a19f72dec0cd..a19f72dec0cd 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/function/ndis.h diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/function/rndis.c index 95d2324f6977..95d2324f6977 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/function/rndis.c diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/function/rndis.h index 0f4abb4c3775..0f4abb4c3775 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/function/rndis.h diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/function/storage_common.c index 648f9e489b39..648f9e489b39 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/function/storage_common.h index 70c891469f57..70c891469f57 100644 --- a/drivers/usb/gadget/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h index 262cc03cc2c0..262cc03cc2c0 100644 --- a/drivers/usb/gadget/u_ecm.h +++ b/drivers/usb/gadget/function/u_ecm.h diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/function/u_eem.h index e3ae97874c4f..e3ae97874c4f 100644 --- a/drivers/usb/gadget/u_eem.h +++ b/drivers/usb/gadget/function/u_eem.h diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 97b027724ee7..6e6f87656e7b 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -483,7 +483,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, struct net_device *net) { struct eth_dev *dev = netdev_priv(net); - int length = skb->len; + int length = 0; int retval; struct usb_request *req = NULL; unsigned long flags; @@ -500,13 +500,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, } spin_unlock_irqrestore(&dev->lock, flags); - if (!in) { + if (skb && !in) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } /* apply outgoing CDC or RNDIS filters */ - if (!is_promisc(cdc_filter)) { + if (skb && !is_promisc(cdc_filter)) { u8 *dest = skb->data; if (is_multicast_ether_addr(dest)) { @@ -557,11 +557,17 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, if (dev->port_usb) skb = dev->wrap(dev->port_usb, skb); spin_unlock_irqrestore(&dev->lock, flags); - if (!skb) + if (!skb) { + /* Multi frame CDC protocols may store the frame for + * later which is not a dropped frame. + */ + if (dev->port_usb->supports_multi_frame) + goto multiframe; goto drop; - - length = skb->len; + } } + + length = skb->len; req->buf = skb->data; req->context = skb; req->complete = tx_complete; @@ -604,6 +610,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; +multiframe: spin_lock_irqsave(&dev->req_lock, flags); if (list_empty(&dev->tx_reqs)) netif_start_queue(net); @@ -1120,10 +1127,7 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); - netif_tx_lock(dev->net); netif_stop_queue(dev->net); - netif_tx_unlock(dev->net); - netif_carrier_off(dev->net); /* disable endpoints, forcing (synchronous) completion diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/function/u_ether.h index 0f0290acea7e..334b38947916 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -18,6 +18,7 @@ #include <linux/if_ether.h> #include <linux/usb/composite.h> #include <linux/usb/cdc.h> +#include <linux/netdevice.h> #include "gadget_chips.h" @@ -74,6 +75,7 @@ struct gether { bool is_fixed; u32 fixed_out_len; u32 fixed_in_len; + bool supports_multi_frame; struct sk_buff *(*wrap)(struct gether *port, struct sk_buff *skb); int (*unwrap)(struct gether *port, diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h index bcbd30146cfd..bcbd30146cfd 100644 --- a/drivers/usb/gadget/u_ether_configfs.h +++ b/drivers/usb/gadget/function/u_ether_configfs.h diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/function/u_fs.h index bf0ba375d459..cd128e31f808 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -216,6 +216,17 @@ struct ffs_data { unsigned fs_descs_count; unsigned hs_descs_count; unsigned ss_descs_count; + unsigned ms_os_descs_count; + unsigned ms_os_descs_ext_prop_count; + unsigned ms_os_descs_ext_prop_name_len; + unsigned ms_os_descs_ext_prop_data_len; + void *ms_os_descs_ext_prop_avail; + void *ms_os_descs_ext_prop_name_avail; + void *ms_os_descs_ext_prop_data_avail; + + unsigned user_flags; + + u8 eps_addrmap[15]; unsigned short strings_count; unsigned short interfaces_count; diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/function/u_gether.h index d4078426ba5d..d4078426ba5d 100644 --- a/drivers/usb/gadget/u_gether.h +++ b/drivers/usb/gadget/function/u_gether.h diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index ce0f3a78ca13..ce0f3a78ca13 100644 --- a/drivers/usb/gadget/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h index 98ced18779ea..98ced18779ea 100644 --- a/drivers/usb/gadget/u_phonet.h +++ b/drivers/usb/gadget/function/u_phonet.h diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index e902aa42a297..e902aa42a297 100644 --- a/drivers/usb/gadget/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/function/u_serial.c index ad0aca812002..491082aaf103 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -55,11 +55,8 @@ * for a telephone or fax link. And ttyGS2 might be something that just * needs a simple byte stream interface for some messaging protocol that * is managed in userspace ... OBEX, PTP, and MTP have been mentioned. - */ - -#define PREFIX "ttyGS" - -/* + * + * * gserial is the lifecycle interface, used by USB functions * gs_port is the I/O nexus, used by the tty driver * tty_struct links to the tty/filesystem framework @@ -385,9 +382,9 @@ __acquires(&port->port_lock) list_del(&req->list); req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0); - pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", - port->port_num, len, *((u8 *)req->buf), - *((u8 *)req->buf+1), *((u8 *)req->buf+2)); + pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", + port->port_num, len, *((u8 *)req->buf), + *((u8 *)req->buf+1), *((u8 *)req->buf+2)); /* Drop lock while we call out of driver; completions * could be issued while we do so. Disconnection may @@ -503,13 +500,13 @@ static void gs_rx_push(unsigned long _port) switch (req->status) { case -ESHUTDOWN: disconnect = true; - pr_vdebug(PREFIX "%d: shutdown\n", port->port_num); + pr_vdebug("ttyGS%d: shutdown\n", port->port_num); break; default: /* presumably a transient fault */ - pr_warning(PREFIX "%d: unexpected RX status %d\n", - port->port_num, req->status); + pr_warn("ttyGS%d: unexpected RX status %d\n", + port->port_num, req->status); /* FALLTHROUGH */ case 0: /* normal completion */ @@ -537,9 +534,8 @@ static void gs_rx_push(unsigned long _port) if (count != size) { /* stop pushing; TTY layer can't handle more */ port->n_read += count; - pr_vdebug(PREFIX "%d: rx block %d/%d\n", - port->port_num, - count, req->actual); + pr_vdebug("ttyGS%d: rx block %d/%d\n", + port->port_num, count, req->actual); break; } port->n_read = 0; @@ -569,7 +565,7 @@ static void gs_rx_push(unsigned long _port) if (do_push) tasklet_schedule(&port->push); else - pr_warning(PREFIX "%d: RX not scheduled?\n", + pr_warn("ttyGS%d: RX not scheduled?\n", port->port_num); } } @@ -985,7 +981,7 @@ static void gs_unthrottle(struct tty_struct *tty) * read queue backs up enough we'll be NAKing OUT packets. */ tasklet_schedule(&port->push); - pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num); + pr_vdebug("ttyGS%d: unthrottle\n", port->port_num); } spin_unlock_irqrestore(&port->port_lock, flags); } @@ -1295,7 +1291,7 @@ static int userial_init(void) return -ENOMEM; gs_tty_driver->driver_name = "g_serial"; - gs_tty_driver->name = PREFIX; + gs_tty_driver->name = "ttyGS"; /* uses dynamically assigned dev_t values */ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/function/u_serial.h index c20210c0babd..c20210c0babd 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c index 7a55fea43430..a44a07f30281 100644 --- a/drivers/usb/gadget/u_uac1.c +++ b/drivers/usb/gadget/function/u_uac1.c @@ -10,6 +10,7 @@ */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/delay.h> @@ -23,22 +24,6 @@ * This component encapsulates the ALSA devices for USB audio gadget */ -#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" -#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" -#define FILE_CONTROL "/dev/snd/controlC0" - -static char *fn_play = FILE_PCM_PLAYBACK; -module_param(fn_play, charp, S_IRUGO); -MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); - -static char *fn_cap = FILE_PCM_CAPTURE; -module_param(fn_cap, charp, S_IRUGO); -MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); - -static char *fn_cntl = FILE_CONTROL; -module_param(fn_cntl, charp, S_IRUGO); -MODULE_PARM_DESC(fn_cntl, "Control device file name"); - /*-------------------------------------------------------------------------*/ /** @@ -167,7 +152,7 @@ static int playback_default_hw_params(struct gaudio_snd_dev *snd) /** * Playback audio buffer data by ALSA PCM device */ -static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) +size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) { struct gaudio_snd_dev *snd = &card->playback; struct snd_pcm_substream *substream = snd->substream; @@ -202,12 +187,12 @@ try_again: return 0; } -static int u_audio_get_playback_channels(struct gaudio *card) +int u_audio_get_playback_channels(struct gaudio *card) { return card->playback.channels; } -static int u_audio_get_playback_rate(struct gaudio *card) +int u_audio_get_playback_rate(struct gaudio *card) { return card->playback.rate; } @@ -220,6 +205,13 @@ static int gaudio_open_snd_dev(struct gaudio *card) { struct snd_pcm_file *pcm_file; struct gaudio_snd_dev *snd; + struct f_uac1_opts *opts; + char *fn_play, *fn_cap, *fn_cntl; + + opts = container_of(card->func.fi, struct f_uac1_opts, func_inst); + fn_play = opts->fn_play; + fn_cap = opts->fn_cap; + fn_cntl = opts->fn_cntl; if (!card) return -ENODEV; @@ -293,7 +285,6 @@ static int gaudio_close_snd_dev(struct gaudio *gau) return 0; } -static struct gaudio *the_card; /** * gaudio_setup - setup ALSA interface and preparing for USB transfer * @@ -301,15 +292,13 @@ static struct gaudio *the_card; * * Returns negative errno, or zero on success */ -int __init gaudio_setup(struct gaudio *card) +int gaudio_setup(struct gaudio *card) { int ret; ret = gaudio_open_snd_dev(card); if (ret) ERROR(card, "we need at least one control device\n"); - else if (!the_card) - the_card = card; return ret; @@ -320,11 +309,10 @@ int __init gaudio_setup(struct gaudio *card) * * This is called to free all resources allocated by @gaudio_setup(). */ -void gaudio_cleanup(void) +void gaudio_cleanup(struct gaudio *the_card) { if (the_card) { gaudio_close_snd_dev(the_card); - the_card = NULL; } } diff --git a/drivers/usb/gadget/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 18c2e729faf6..f8b17fe82efe 100644 --- a/drivers/usb/gadget/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -23,6 +23,14 @@ #include "gadget_chips.h" +#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" +#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" +#define FILE_CONTROL "/dev/snd/controlC0" + +#define UAC1_OUT_EP_MAX_PACKET_SIZE 200 +#define UAC1_REQ_COUNT 256 +#define UAC1_AUDIO_BUF_SIZE 48000 + /* * This represents the USB side of an audio card device, managed by a USB * function which provides control and stream interfaces. @@ -50,7 +58,28 @@ struct gaudio { /* TODO */ }; +struct f_uac1_opts { + struct usb_function_instance func_inst; + int req_buf_size; + int req_count; + int audio_buf_size; + char *fn_play; + char *fn_cap; + char *fn_cntl; + unsigned bound:1; + unsigned fn_play_alloc:1; + unsigned fn_cap_alloc:1; + unsigned fn_cntl_alloc:1; + struct gaudio *card; + struct mutex lock; + int refcnt; +}; + int gaudio_setup(struct gaudio *card); -void gaudio_cleanup(void); +void gaudio_cleanup(struct gaudio *the_card); + +size_t u_audio_playback(struct gaudio *card, void *buf, size_t count); +int u_audio_get_playback_channels(struct gaudio *card); +int u_audio_get_playback_rate(struct gaudio *card); #endif /* __U_AUDIO_H */ diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h new file mode 100644 index 000000000000..78dd37279bd4 --- /dev/null +++ b/drivers/usb/gadget/function/u_uac2.h @@ -0,0 +1,42 @@ +/* + * u_uac2.h + * + * Utility definitions for UAC2 function + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_UAC2_H +#define U_UAC2_H + +#include <linux/usb/composite.h> + +#define UAC2_DEF_PCHMASK 0x3 +#define UAC2_DEF_PSRATE 48000 +#define UAC2_DEF_PSSIZE 2 +#define UAC2_DEF_CCHMASK 0x3 +#define UAC2_DEF_CSRATE 64000 +#define UAC2_DEF_CSSIZE 2 + +struct f_uac2_opts { + struct usb_function_instance func_inst; + int p_chmask; + int p_srate; + int p_ssize; + int c_chmask; + int c_srate; + int c_ssize; + bool bound; + + struct mutex lock; + int refcnt; +}; + +#endif diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h new file mode 100644 index 000000000000..2a8dfdff0332 --- /dev/null +++ b/drivers/usb/gadget/function/u_uvc.h @@ -0,0 +1,39 @@ +/* + * u_uvc.h + * + * Utility definitions for the uvc function + * + * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_UVC_H +#define U_UVC_H + +#include <linux/usb/composite.h> + +#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst) + +struct f_uvc_opts { + struct usb_function_instance func_inst; + unsigned int uvc_gadget_trace_param; + unsigned int streaming_interval; + unsigned int streaming_maxpacket; + unsigned int streaming_maxburst; + const struct uvc_descriptor_header * const *fs_control; + const struct uvc_descriptor_header * const *ss_control; + const struct uvc_descriptor_header * const *fs_streaming; + const struct uvc_descriptor_header * const *hs_streaming; + const struct uvc_descriptor_header * const *ss_streaming; +}; + +void uvc_set_trace_param(unsigned int trace); + +#endif /* U_UVC_H */ + diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/function/uvc.h index 7a9111de8054..f67695cb28f8 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -53,6 +53,7 @@ struct uvc_event #ifdef __KERNEL__ #include <linux/usb.h> /* For usb_endpoint_* */ +#include <linux/usb/composite.h> #include <linux/usb/gadget.h> #include <linux/videodev2.h> #include <linux/version.h> @@ -96,9 +97,6 @@ extern unsigned int uvc_gadget_trace_param; * Driver specific constants */ -#define DRIVER_VERSION "0.1.0" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) - #define UVC_NUM_REQUESTS 4 #define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_EVENTS 4 diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 1c29bc954db9..8ea8b3b227b4 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -28,7 +28,7 @@ /* ------------------------------------------------------------------------ * Video buffers queue management. * - * Video queues is initialized by uvc_queue_init(). The function performs + * Video queues is initialized by uvcg_queue_init(). The function performs * basic initialization of the uvc_video_queue struct and never fails. * * Video buffers are managed by videobuf2. The driver uses a mutex to protect @@ -126,13 +126,12 @@ static struct vb2_ops uvc_queue_qops = { .wait_finish = uvc_wait_finish, }; -static int uvc_queue_init(struct uvc_video_queue *queue, - enum v4l2_buf_type type) +int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) { int ret; queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; queue->queue.drv_priv = queue; queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; @@ -154,7 +153,7 @@ static int uvc_queue_init(struct uvc_video_queue *queue, /* * Free the video buffers. */ -static void uvc_free_buffers(struct uvc_video_queue *queue) +void uvcg_free_buffers(struct uvc_video_queue *queue) { mutex_lock(&queue->mutex); vb2_queue_release(&queue->queue); @@ -164,8 +163,8 @@ static void uvc_free_buffers(struct uvc_video_queue *queue) /* * Allocate the video buffers. */ -static int uvc_alloc_buffers(struct uvc_video_queue *queue, - struct v4l2_requestbuffers *rb) +int uvcg_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb) { int ret; @@ -176,8 +175,7 @@ static int uvc_alloc_buffers(struct uvc_video_queue *queue, return ret ? ret : rb->count; } -static int uvc_query_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf) +int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) { int ret; @@ -188,8 +186,7 @@ static int uvc_query_buffer(struct uvc_video_queue *queue, return ret; } -static int uvc_queue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf) +int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) { unsigned long flags; int ret; @@ -213,8 +210,8 @@ done: * Dequeue a video buffer. If nonblocking is false, block until a buffer is * available. */ -static int uvc_dequeue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf, int nonblocking) +int uvcg_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf, + int nonblocking) { int ret; @@ -231,8 +228,8 @@ static int uvc_dequeue_buffer(struct uvc_video_queue *queue, * This function implements video queue polling and is intended to be used by * the device poll handler. */ -static unsigned int uvc_queue_poll(struct uvc_video_queue *queue, - struct file *file, poll_table *wait) +unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, struct file *file, + poll_table *wait) { unsigned int ret; @@ -243,8 +240,7 @@ static unsigned int uvc_queue_poll(struct uvc_video_queue *queue, return ret; } -static int uvc_queue_mmap(struct uvc_video_queue *queue, - struct vm_area_struct *vma) +int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) { int ret; @@ -261,8 +257,8 @@ static int uvc_queue_mmap(struct uvc_video_queue *queue, * * NO-MMU arch need this function to make mmap() work correctly. */ -static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, - unsigned long pgoff) +unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) { unsigned long ret; @@ -285,7 +281,7 @@ static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, * This function acquires the irq spinlock and can be called from interrupt * context. */ -static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) +void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect) { struct uvc_buffer *buf; unsigned long flags; @@ -324,9 +320,9 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) * the main queue. * * This function can't be called from interrupt context. Use - * uvc_queue_cancel() instead. + * uvcg_queue_cancel() instead. */ -static int uvc_queue_enable(struct uvc_video_queue *queue, int enable) +int uvcg_queue_enable(struct uvc_video_queue *queue, int enable) { unsigned long flags; int ret = 0; @@ -363,8 +359,8 @@ done: } /* called with &queue_irqlock held.. */ -static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, - struct uvc_buffer *buf) +struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) { struct uvc_buffer *nextbuf; @@ -392,7 +388,7 @@ static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, return nextbuf; } -static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue) +struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue) { struct uvc_buffer *buf = NULL; diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 8e76ce982f1e..03919c724961 100644 --- a/drivers/usb/gadget/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -57,6 +57,39 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) return vb2_is_streaming(&queue->queue); } +int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type); + +void uvcg_free_buffers(struct uvc_video_queue *queue); + +int uvcg_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb); + +int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf); + +int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf); + +int uvcg_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf, int nonblocking); + +unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait); + +int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma); + +#ifndef CONFIG_MMU +unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff); +#endif /* CONFIG_MMU */ + +void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect); + +int uvcg_queue_enable(struct uvc_video_queue *queue, int enable); + +struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf); + +struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue); + #endif /* __KERNEL__ */ #endif /* _UVC_QUEUE_H_ */ diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index ad48e81155e2..5aad7fededa5 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -23,8 +23,10 @@ #include <media/v4l2-event.h> #include <media/v4l2-ioctl.h> +#include "f_uvc.h" #include "uvc.h" #include "uvc_queue.h" +#include "uvc_video.h" /* -------------------------------------------------------------------------- * Requests handling @@ -48,7 +50,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) } /* -------------------------------------------------------------------------- - * V4L2 + * V4L2 ioctls */ struct uvc_format @@ -63,8 +65,29 @@ static struct uvc_format uvc_formats[] = { }; static int -uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt) +uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct usb_composite_dev *cdev = uvc->func.config->cdev; + + strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); + strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), + sizeof(cap->bus_info)); + + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + return 0; +} + +static int +uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + fmt->fmt.pix.pixelformat = video->fcc; fmt->fmt.pix.width = video->width; fmt->fmt.pix.height = video->height; @@ -78,8 +101,11 @@ uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt) } static int -uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt) +uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) { + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; struct uvc_format *format; unsigned int imagesize; unsigned int bpl; @@ -116,209 +142,184 @@ uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt) } static int -uvc_v4l2_open(struct file *file) +uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle; - - handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (handle == NULL) - return -ENOMEM; - - v4l2_fh_init(&handle->vfh, vdev); - v4l2_fh_add(&handle->vfh); + struct uvc_video *video = &uvc->video; - handle->device = &uvc->video; - file->private_data = &handle->vfh; + if (b->type != video->queue.queue.type) + return -EINVAL; - uvc_function_connect(uvc); - return 0; + return uvcg_alloc_buffers(&video->queue, b); } static int -uvc_v4l2_release(struct file *file) +uvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); - struct uvc_video *video = handle->device; - - uvc_function_disconnect(uvc); - - uvc_video_enable(video, 0); - uvc_free_buffers(&video->queue); - - file->private_data = NULL; - v4l2_fh_del(&handle->vfh); - v4l2_fh_exit(&handle->vfh); - kfree(handle); + struct uvc_video *video = &uvc->video; - return 0; + return uvcg_query_buffer(&video->queue, b); } -static long -uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int +uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); - struct usb_composite_dev *cdev = uvc->func.config->cdev; struct uvc_video *video = &uvc->video; - int ret = 0; - - switch (cmd) { - /* Query capabilities */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap, 0, sizeof *cap); - strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); - strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), - sizeof cap->bus_info); - cap->version = DRIVER_VERSION_NUMBER; - cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; - break; - } - - /* Get & Set format */ - case VIDIOC_G_FMT: - { - struct v4l2_format *fmt = arg; + int ret; - if (fmt->type != video->queue.queue.type) - return -EINVAL; + ret = uvcg_queue_buffer(&video->queue, b); + if (ret < 0) + return ret; - return uvc_v4l2_get_format(video, fmt); - } + return uvcg_video_pump(video); +} - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt = arg; +static int +uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; - if (fmt->type != video->queue.queue.type) - return -EINVAL; + return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK); +} - return uvc_v4l2_set_format(video, fmt); - } +static int +uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + int ret; - /* Buffers & streaming */ - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *rb = arg; + if (type != video->queue.queue.type) + return -EINVAL; - if (rb->type != video->queue.queue.type) - return -EINVAL; + /* Enable UVC video. */ + ret = uvcg_video_enable(video, 1); + if (ret < 0) + return ret; - ret = uvc_alloc_buffers(&video->queue, rb); - if (ret < 0) - return ret; + /* + * Complete the alternate setting selection setup phase now that + * userspace is ready to provide video frames. + */ + uvc_function_setup_continue(uvc); + uvc->state = UVC_STATE_STREAMING; - ret = 0; - break; - } - - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; + return 0; +} - return uvc_query_buffer(&video->queue, buf); - } +static int +uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; - case VIDIOC_QBUF: - if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0) - return ret; + if (type != video->queue.queue.type) + return -EINVAL; - return uvc_video_pump(video); + return uvcg_video_enable(video, 0); +} - case VIDIOC_DQBUF: - return uvc_dequeue_buffer(&video->queue, arg, - file->f_flags & O_NONBLOCK); +static int +uvc_v4l2_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) + return -EINVAL; - case VIDIOC_STREAMON: - { - int *type = arg; + return v4l2_event_subscribe(fh, sub, 2, NULL); +} - if (*type != video->queue.queue.type) - return -EINVAL; +static int +uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} - /* Enable UVC video. */ - ret = uvc_video_enable(video, 1); - if (ret < 0) - return ret; +static long +uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); - /* - * Complete the alternate setting selection setup phase now that - * userspace is ready to provide video frames. - */ - uvc_function_setup_continue(uvc); - uvc->state = UVC_STATE_STREAMING; + switch (cmd) { + case UVCIOC_SEND_RESPONSE: + return uvc_send_response(uvc, arg); - return 0; + default: + return -ENOIOCTLCMD; } +} - case VIDIOC_STREAMOFF: - { - int *type = arg; - - if (*type != video->queue.queue.type) - return -EINVAL; +const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { + .vidioc_querycap = uvc_v4l2_querycap, + .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, + .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, + .vidioc_reqbufs = uvc_v4l2_reqbufs, + .vidioc_querybuf = uvc_v4l2_querybuf, + .vidioc_qbuf = uvc_v4l2_qbuf, + .vidioc_dqbuf = uvc_v4l2_dqbuf, + .vidioc_streamon = uvc_v4l2_streamon, + .vidioc_streamoff = uvc_v4l2_streamoff, + .vidioc_subscribe_event = uvc_v4l2_subscribe_event, + .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event, + .vidioc_default = uvc_v4l2_ioctl_default, +}; - return uvc_video_enable(video, 0); - } +/* -------------------------------------------------------------------------- + * V4L2 + */ - /* Events */ - case VIDIOC_DQEVENT: - { - struct v4l2_event *event = arg; - - ret = v4l2_event_dequeue(&handle->vfh, event, - file->f_flags & O_NONBLOCK); - if (ret == 0 && event->type == UVC_EVENT_SETUP) { - struct uvc_event *uvc_event = (void *)&event->u.data; - - /* Tell the complete callback to generate an event for - * the next request that will be enqueued by - * uvc_event_write. - */ - uvc->event_setup_out = - !(uvc_event->req.bRequestType & USB_DIR_IN); - uvc->event_length = uvc_event->req.wLength; - } +static int +uvc_v4l2_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_file_handle *handle; - return ret; - } + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (handle == NULL) + return -ENOMEM; - case VIDIOC_SUBSCRIBE_EVENT: - { - struct v4l2_event_subscription *sub = arg; + v4l2_fh_init(&handle->vfh, vdev); + v4l2_fh_add(&handle->vfh); - if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) - return -EINVAL; + handle->device = &uvc->video; + file->private_data = &handle->vfh; - return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL); - } + uvc_function_connect(uvc); + return 0; +} - case VIDIOC_UNSUBSCRIBE_EVENT: - return v4l2_event_unsubscribe(&handle->vfh, arg); +static int +uvc_v4l2_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); + struct uvc_video *video = handle->device; - case UVCIOC_SEND_RESPONSE: - ret = uvc_send_response(uvc, arg); - break; + uvc_function_disconnect(uvc); - default: - return -ENOIOCTLCMD; - } + uvcg_video_enable(video, 0); + uvcg_free_buffers(&video->queue); - return ret; -} + file->private_data = NULL; + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); + kfree(handle); -static long -uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); + return 0; } static int @@ -327,7 +328,7 @@ uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - return uvc_queue_mmap(&uvc->video.queue, vma); + return uvcg_queue_mmap(&uvc->video.queue, vma); } static unsigned int @@ -336,30 +337,30 @@ uvc_v4l2_poll(struct file *file, poll_table *wait) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - return uvc_queue_poll(&uvc->video.queue, file, wait); + return uvcg_queue_poll(&uvc->video.queue, file, wait); } #ifndef CONFIG_MMU -static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, +static unsigned long uvcg_v4l2_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff); + return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff); } #endif -static struct v4l2_file_operations uvc_v4l2_fops = { +struct v4l2_file_operations uvc_v4l2_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, .release = uvc_v4l2_release, - .ioctl = uvc_v4l2_ioctl, + .ioctl = video_ioctl2, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, #ifndef CONFIG_MMU - .get_unmapped_area = uvc_v4l2_get_unmapped_area, + .get_unmapped_area = uvcg_v4l2_get_unmapped_area, #endif }; diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h new file mode 100644 index 000000000000..2683b92fda65 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_v4l2.h @@ -0,0 +1,22 @@ +/* + * uvc_v4l2.h -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UVC_V4L2_H__ +#define __UVC_V4L2_H__ + +extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops; +extern struct v4l2_file_operations uvc_v4l2_fops; + +#endif /* __UVC_V4L2_H__ */ diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 71e896d4c5ae..c3e1f27dbbef 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -15,6 +15,7 @@ #include <linux/errno.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/video.h> #include <media/v4l2-dev.h> @@ -85,7 +86,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvc_queue_next_buffer(&video->queue, buf); + uvcg_queue_next_buffer(&video->queue, buf); video->fid ^= UVC_STREAM_FID; video->payload_size = 0; @@ -118,7 +119,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvc_queue_next_buffer(&video->queue, buf); + uvcg_queue_next_buffer(&video->queue, buf); video->fid ^= UVC_STREAM_FID; } } @@ -171,19 +172,19 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) break; case -ESHUTDOWN: /* disconnect from host. */ - printk(KERN_INFO "VS request cancelled.\n"); - uvc_queue_cancel(queue, 1); + printk(KERN_DEBUG "VS request cancelled.\n"); + uvcg_queue_cancel(queue, 1); goto requeue; default: printk(KERN_INFO "VS request completed with status %d.\n", req->status); - uvc_queue_cancel(queue, 0); + uvcg_queue_cancel(queue, 0); goto requeue; } spin_lock_irqsave(&video->queue.irqlock, flags); - buf = uvc_queue_head(&video->queue); + buf = uvcg_queue_head(&video->queue); if (buf == NULL) { spin_unlock_irqrestore(&video->queue.irqlock, flags); goto requeue; @@ -195,6 +196,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) printk(KERN_INFO "Failed to queue request (%d).\n", ret); usb_ep_set_halt(ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); + uvcg_queue_cancel(queue, 0); goto requeue; } spin_unlock_irqrestore(&video->queue.irqlock, flags); @@ -273,20 +275,20 @@ error: */ /* - * uvc_video_pump - Pump video data into the USB requests + * uvcg_video_pump - Pump video data into the USB requests * * This function fills the available USB requests (listed in req_free) with * video data from the queued buffers. */ -static int -uvc_video_pump(struct uvc_video *video) +int uvcg_video_pump(struct uvc_video *video) { + struct uvc_video_queue *queue = &video->queue; struct usb_request *req; struct uvc_buffer *buf; unsigned long flags; int ret; - /* FIXME TODO Race between uvc_video_pump and requests completion + /* FIXME TODO Race between uvcg_video_pump and requests completion * handler ??? */ @@ -307,10 +309,10 @@ uvc_video_pump(struct uvc_video *video) /* Retrieve the first available video buffer and fill the * request, protected by the video queue irqlock. */ - spin_lock_irqsave(&video->queue.irqlock, flags); - buf = uvc_queue_head(&video->queue); + spin_lock_irqsave(&queue->irqlock, flags); + buf = uvcg_queue_head(queue); if (buf == NULL) { - spin_unlock_irqrestore(&video->queue.irqlock, flags); + spin_unlock_irqrestore(&queue->irqlock, flags); break; } @@ -321,10 +323,11 @@ uvc_video_pump(struct uvc_video *video) if (ret < 0) { printk(KERN_INFO "Failed to queue request (%d)\n", ret); usb_ep_set_halt(video->ep); - spin_unlock_irqrestore(&video->queue.irqlock, flags); + spin_unlock_irqrestore(&queue->irqlock, flags); + uvcg_queue_cancel(queue, 0); break; } - spin_unlock_irqrestore(&video->queue.irqlock, flags); + spin_unlock_irqrestore(&queue->irqlock, flags); } spin_lock_irqsave(&video->req_lock, flags); @@ -336,8 +339,7 @@ uvc_video_pump(struct uvc_video *video) /* * Enable or disable the video stream. */ -static int -uvc_video_enable(struct uvc_video *video, int enable) +int uvcg_video_enable(struct uvc_video *video, int enable) { unsigned int i; int ret; @@ -353,11 +355,11 @@ uvc_video_enable(struct uvc_video *video, int enable) usb_ep_dequeue(video->ep, video->req[i]); uvc_video_free_requests(video); - uvc_queue_enable(&video->queue, 0); + uvcg_queue_enable(&video->queue, 0); return 0; } - if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) return ret; if ((ret = uvc_video_alloc_requests(video)) < 0) @@ -369,14 +371,13 @@ uvc_video_enable(struct uvc_video *video, int enable) } else video->encode = uvc_video_encode_isoc; - return uvc_video_pump(video); + return uvcg_video_pump(video); } /* * Initialize the UVC video stream. */ -static int -uvc_video_init(struct uvc_video *video) +int uvcg_video_init(struct uvc_video *video) { INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); @@ -388,7 +389,7 @@ uvc_video_init(struct uvc_video *video) video->imagesize = 320 * 240 * 2; /* Initialize the video buffers queue. */ - uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT); + uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT); return 0; } diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h new file mode 100644 index 000000000000..ef00f06fa00b --- /dev/null +++ b/drivers/usb/gadget/function/uvc_video.h @@ -0,0 +1,24 @@ +/* + * uvc_video.h -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UVC_VIDEO_H__ +#define __UVC_VIDEO_H__ + +int uvcg_video_pump(struct uvc_video *video); + +int uvcg_video_enable(struct uvc_video *video, int enable); + +int uvcg_video_init(struct uvc_video *video); + +#endif /* __UVC_VIDEO_H__ */ diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig new file mode 100644 index 000000000000..24392d269709 --- /dev/null +++ b/drivers/usb/gadget/legacy/Kconfig @@ -0,0 +1,478 @@ +# +# USB Gadget support on a system involves +# (a) a peripheral controller, and +# (b) the gadget driver using it. +# +# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !! +# +# - Host systems (like PCs) need CONFIG_USB (with "A" jacks). +# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks). +# - Some systems have both kinds of controllers. +# +# With help from a special transceiver and a "Mini-AB" jack, systems with +# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). +# + +config USB_ZERO + tristate "Gadget Zero (DEVELOPMENT)" + select USB_LIBCOMPOSITE + select USB_F_SS_LB + help + Gadget Zero is a two-configuration device. It either sinks and + sources bulk data; or it loops back a configurable number of + transfers. It also implements control requests, for "chapter 9" + conformance. The driver needs only two bulk-capable endpoints, so + it can work on top of most device-side usb controllers. It's + useful for testing, and is also a working example showing how + USB "gadget drivers" can be written. + + Make this be the first driver you try using on top of any new + USB peripheral controller driver. Then you can use host-side + test software, like the "usbtest" driver, to put your hardware + and its driver through a basic set of functional tests. + + Gadget Zero also works with the host-side "usb-skeleton" driver, + and with many kinds of host-side test software. You may need + to tweak product and vendor IDs before host software knows about + this device, and arrange to select an appropriate configuration. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_zero". + +config USB_ZERO_HNPTEST + boolean "HNP Test Device" + depends on USB_ZERO && USB_OTG + help + You can configure this device to enumerate using the device + identifiers of the USB-OTG test device. That means that when + this gadget connects to another OTG device, with this one using + the "B-Peripheral" role, that device will use HNP to let this + one serve as the USB host instead (in the "B-Host" role). + +config USB_AUDIO + tristate "Audio Gadget" + depends on SND + select USB_LIBCOMPOSITE + select SND_PCM + select USB_F_UAC1 if GADGET_UAC1 + select USB_F_UAC2 if !GADGET_UAC1 + help + This Gadget Audio driver is compatible with USB Audio Class + specification 2.0. It implements 1 AudioControl interface, + 1 AudioStreaming Interface each for USB-OUT and USB-IN. + Number of channels, sample rate and sample size can be + specified as module parameters. + This driver doesn't expect any real Audio codec to be present + on the device - the audio streams are simply sinked to and + sourced from a virtual ALSA sound card created. The user-space + application may choose to do whatever it wants with the data + received from the USB Host and choose to provide whatever it + wants as audio data to the USB Host. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_audio". + +config GADGET_UAC1 + bool "UAC 1.0 (Legacy)" + depends on USB_AUDIO + help + If you instead want older UAC Spec-1.0 driver that also has audio + paths hardwired to the Audio codec chip on-board and doesn't work + without one. + +config USB_ETH + tristate "Ethernet Gadget (with CDC Ethernet support)" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_F_ECM + select USB_F_SUBSET + select CRC32 + help + This driver implements Ethernet style communication, in one of + several ways: + + - The "Communication Device Class" (CDC) Ethernet Control Model. + That protocol is often avoided with pure Ethernet adapters, in + favor of simpler vendor-specific hardware, but is widely + supported by firmware for smart network devices. + + - On hardware can't implement that protocol, a simple CDC subset + is used, placing fewer demands on USB. + + - CDC Ethernet Emulation Model (EEM) is a newer standard that has + a simpler interface that can be used by more USB hardware. + + RNDIS support is an additional option, more demanding than than + subset. + + Within the USB device, this gadget driver exposes a network device + "usbX", where X depends on what other networking devices you have. + Treat it like a two-node Ethernet link: host, and gadget. + + The Linux-USB host-side "usbnet" driver interoperates with this + driver, so that deep I/O queues can be supported. On 2.4 kernels, + use "CDCEther" instead, if you're using the CDC option. That CDC + mode should also interoperate with standard CDC Ethernet class + drivers on other host operating systems. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ether". + +config USB_ETH_RNDIS + bool "RNDIS support" + depends on USB_ETH + select USB_LIBCOMPOSITE + select USB_F_RNDIS + default y + help + Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, + and Microsoft provides redistributable binary RNDIS drivers for + older versions of Windows. + + If you say "y" here, the Ethernet gadget driver will try to provide + a second device configuration, supporting RNDIS to talk to such + Microsoft USB hosts. + + To make MS-Windows work with this, use Documentation/usb/linux.inf + as the "driver info file". For versions of MS-Windows older than + XP, you'll need to download drivers from Microsoft's website; a URL + is given in comments found in that info file. + +config USB_ETH_EEM + bool "Ethernet Emulation Model (EEM) support" + depends on USB_ETH + select USB_LIBCOMPOSITE + select USB_F_EEM + default n + help + CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM + and therefore can be supported by more hardware. Technically ECM and + EEM are designed for different applications. The ECM model extends + the network interface to the target (e.g. a USB cable modem), and the + EEM model is for mobile devices to communicate with hosts using + ethernet over USB. For Linux gadgets, however, the interface with + the host is the same (a usbX device), so the differences are minimal. + + If you say "y" here, the Ethernet gadget driver will use the EEM + protocol rather than ECM. If unsure, say "n". + +config USB_G_NCM + tristate "Network Control Model (NCM) support" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_F_NCM + select CRC32 + help + This driver implements USB CDC NCM subclass standard. NCM is + an advanced protocol for Ethernet encapsulation, allows grouping + of several ethernet frames into one USB transfer and different + alignment possibilities. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ncm". + +config USB_GADGETFS + tristate "Gadget Filesystem" + help + This driver provides a filesystem based API that lets user mode + programs implement a single-configuration USB device, including + endpoint I/O and control requests that don't relate to enumeration. + All endpoints, transfer speeds, and transfer types supported by + the hardware are available, through read() and write() calls. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "gadgetfs". + +config USB_FUNCTIONFS + tristate "Function Filesystem" + select USB_LIBCOMPOSITE + select USB_F_FS + select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) + help + The Function Filesystem (FunctionFS) lets one create USB + composite functions in user space in the same way GadgetFS + lets one create USB gadgets in user space. This allows creation + of composite gadgets such that some of the functions are + implemented in kernel space (for instance Ethernet, serial or + mass storage) and other are implemented in user space. + + If you say "y" or "m" here you will be able what kind of + configurations the gadget will provide. + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_ffs". + +config USB_FUNCTIONFS_ETH + bool "Include configuration with CDC ECM (Ethernet)" + depends on USB_FUNCTIONFS && NET + select USB_U_ETHER + select USB_F_ECM + select USB_F_SUBSET + help + Include a configuration with CDC ECM function (Ethernet) and the + Function Filesystem. + +config USB_FUNCTIONFS_RNDIS + bool "Include configuration with RNDIS (Ethernet)" + depends on USB_FUNCTIONFS && NET + select USB_U_ETHER + select USB_F_RNDIS + help + Include a configuration with RNDIS function (Ethernet) and the Filesystem. + +config USB_FUNCTIONFS_GENERIC + bool "Include 'pure' configuration" + depends on USB_FUNCTIONFS + help + Include a configuration with the Function Filesystem alone with + no Ethernet interface. + +config USB_MASS_STORAGE + tristate "Mass Storage Gadget" + depends on BLOCK + select USB_LIBCOMPOSITE + select USB_F_MASS_STORAGE + help + The Mass Storage Gadget acts as a USB Mass Storage disk drive. + As its storage repository it can use a regular file or a block + device (in much the same way as the "loop" device driver), + specified as a module parameter or sysfs option. + + This driver is a replacement for now removed File-backed + Storage Gadget (g_file_storage). + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_mass_storage". + +config USB_GADGET_TARGET + tristate "USB Gadget Target Fabric Module" + depends on TARGET_CORE + select USB_LIBCOMPOSITE + help + This fabric is an USB gadget. Two USB protocols are supported that is + BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is + advertised on alternative interface 0 (primary) and UAS is on + alternative interface 1. Both protocols can work on USB2.0 and USB3.0. + UAS utilizes the USB 3.0 feature called streams support. + +config USB_G_SERIAL + tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" + depends on TTY + select USB_U_SERIAL + select USB_F_ACM + select USB_F_SERIAL + select USB_F_OBEX + select USB_LIBCOMPOSITE + help + The Serial Gadget talks to the Linux-USB generic serial driver. + This driver supports a CDC-ACM module option, which can be used + to interoperate with MS-Windows hosts or with the Linux-USB + "cdc-acm" driver. + + This driver also supports a CDC-OBEX option. You will need a + user space OBEX server talking to /dev/ttyGS*, since the kernel + itself doesn't implement the OBEX protocol. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_serial". + + For more information, see Documentation/usb/gadget_serial.txt + which includes instructions and a "driver info file" needed to + make MS-Windows work with CDC ACM. + +config USB_MIDI_GADGET + tristate "MIDI Gadget" + depends on SND + select USB_LIBCOMPOSITE + select SND_RAWMIDI + help + The MIDI Gadget acts as a USB Audio device, with one MIDI + input and one MIDI output. These MIDI jacks appear as + a sound "card" in the ALSA sound system. Other MIDI + connections can then be made on the gadget system, using + ALSA's aconnect utility etc. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_midi". + +config USB_G_PRINTER + tristate "Printer Gadget" + select USB_LIBCOMPOSITE + help + The Printer Gadget channels data between the USB host and a + userspace program driving the print engine. The user space + program reads and writes the device file /dev/g_printer to + receive or send printer data. It can use ioctl calls to + the device file to get or set printer status. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_printer". + + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + +if TTY + +config USB_CDC_COMPOSITE + tristate "CDC Composite Device (Ethernet and ACM)" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_ECM + help + This driver provides two functions in one configuration: + a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. + + This driver requires four bulk and two interrupt endpoints, + plus the ability to handle altsettings. Not all peripheral + controllers are that capable. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module. + +config USB_G_NOKIA + tristate "Nokia composite gadget" + depends on PHONET + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_OBEX + select USB_F_PHONET + select USB_F_ECM + help + The Nokia composite gadget provides support for acm, obex + and phonet in only one composite gadget driver. + + It's only really useful for N900 hardware. If you're building + a kernel for N900, say Y or M here. If unsure, say N. + +config USB_G_ACM_MS + tristate "CDC Composite Device (ACM and mass storage)" + depends on BLOCK + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_F_ACM + select USB_F_MASS_STORAGE + help + This driver provides two functions in one configuration: + a mass storage, and a CDC ACM (serial port) link. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_acm_ms". + +config USB_G_MULTI + tristate "Multifunction Composite Gadget" + depends on BLOCK && NET + select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_MASS_STORAGE + help + The Multifunction Composite Gadget provides Ethernet (RNDIS + and/or CDC Ethernet), mass storage and ACM serial link + interfaces. + + You will be asked to choose which of the two configurations is + to be available in the gadget. At least one configuration must + be chosen to make the gadget usable. Selecting more than one + configuration will prevent Windows from automatically detecting + the gadget as a composite gadget, so an INF file will be needed to + use the gadget. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_multi". + +config USB_G_MULTI_RNDIS + bool "RNDIS + CDC Serial + Storage configuration" + depends on USB_G_MULTI + select USB_F_RNDIS + default y + help + This option enables a configuration with RNDIS, CDC Serial and + Mass Storage functions available in the Multifunction Composite + Gadget. This is the configuration dedicated for Windows since RNDIS + is Microsoft's protocol. + + If unsure, say "y". + +config USB_G_MULTI_CDC + bool "CDC Ethernet + CDC Serial + Storage configuration" + depends on USB_G_MULTI + default n + select USB_F_ECM + help + This option enables a configuration with CDC Ethernet (ECM), CDC + Serial and Mass Storage functions available in the Multifunction + Composite Gadget. + + If unsure, say "y". + +endif # TTY + +config USB_G_HID + tristate "HID Gadget" + select USB_LIBCOMPOSITE + help + The HID gadget driver provides generic emulation of USB + Human Interface Devices (HID). + + For more information, see Documentation/usb/gadget_hid.txt which + includes sample code for accessing the device files. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_hid". + +# Standalone / single function gadgets +config USB_G_DBGP + tristate "EHCI Debug Device Gadget" + depends on TTY + select USB_LIBCOMPOSITE + help + This gadget emulates an EHCI Debug device. This is useful when you want + to interact with an EHCI Debug Port. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_dbgp". + +if USB_G_DBGP +choice + prompt "EHCI Debug Device mode" + default USB_G_DBGP_SERIAL + +config USB_G_DBGP_PRINTK + depends on USB_G_DBGP + bool "printk" + help + Directly printk() received data. No interaction. + +config USB_G_DBGP_SERIAL + depends on USB_G_DBGP + select USB_U_SERIAL + bool "serial" + help + Userland can interact using /dev/ttyGSxxx. +endchoice +endif + +# put drivers that need isochronous transfer support (for audio +# or video class gadget drivers), or specific hardware, here. +config USB_G_WEBCAM + tristate "USB Webcam Gadget" + depends on VIDEO_DEV + select USB_LIBCOMPOSITE + select VIDEOBUF2_VMALLOC + select USB_F_UVC + help + The Webcam Gadget acts as a composite USB Audio and Video Class + device. It provides a userspace API to process UVC control requests + and stream video data to the host. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_webcam". diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile new file mode 100644 index 000000000000..7f485f25705e --- /dev/null +++ b/drivers/usb/gadget/legacy/Makefile @@ -0,0 +1,44 @@ +# +# USB gadget drivers +# + +ccflags-y := -I$(srctree)/drivers/usb/gadget/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/function/ + +g_zero-y := zero.o +g_audio-y := audio.o +g_ether-y := ether.o +g_serial-y := serial.o +g_midi-y := gmidi.o +gadgetfs-y := inode.o +g_mass_storage-y := mass_storage.o +g_printer-y := printer.o +g_cdc-y := cdc2.o +g_multi-y := multi.o +g_hid-y := hid.o +g_dbgp-y := dbgp.o +g_nokia-y := nokia.o +g_webcam-y := webcam.o +g_ncm-y := ncm.o +g_acm_ms-y := acm_ms.o +g_tcm_usb_gadget-y := tcm_usb_gadget.o + +obj-$(CONFIG_USB_ZERO) += g_zero.o +obj-$(CONFIG_USB_AUDIO) += g_audio.o +obj-$(CONFIG_USB_ETH) += g_ether.o +obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o +obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o +obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o +obj-$(CONFIG_USB_G_SERIAL) += g_serial.o +obj-$(CONFIG_USB_G_PRINTER) += g_printer.o +obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o +obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o +obj-$(CONFIG_USB_G_HID) += g_hid.o +obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o +obj-$(CONFIG_USB_G_MULTI) += g_multi.o +obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o +obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o +obj-$(CONFIG_USB_G_NCM) += g_ncm.o +obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o +obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c index a252444cc0a7..c30b7b572465 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/legacy/acm_ms.c @@ -267,18 +267,8 @@ static __refdata struct usb_composite_driver acm_ms_driver = { .unbind = __exit_p(acm_ms_unbind), }; +module_usb_composite_driver(acm_ms_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>"); MODULE_LICENSE("GPL v2"); - -static int __init init(void) -{ - return usb_composite_probe(&acm_ms_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&acm_ms_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/legacy/audio.c index 231b0efe8fdc..f46a3956e43d 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -21,6 +21,66 @@ USB_GADGET_COMPOSITE_OPTIONS(); +#ifndef CONFIG_GADGET_UAC1 +#include "u_uac2.h" + +/* Playback(USB-IN) Default Stereo - Fl/Fr */ +static int p_chmask = UAC2_DEF_PCHMASK; +module_param(p_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); + +/* Playback Default 48 KHz */ +static int p_srate = UAC2_DEF_PSRATE; +module_param(p_srate, uint, S_IRUGO); +MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); + +/* Playback Default 16bits/sample */ +static int p_ssize = UAC2_DEF_PSSIZE; +module_param(p_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); + +/* Capture(USB-OUT) Default Stereo - Fl/Fr */ +static int c_chmask = UAC2_DEF_CCHMASK; +module_param(c_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); + +/* Capture Default 64 KHz */ +static int c_srate = UAC2_DEF_CSRATE; +module_param(c_srate, uint, S_IRUGO); +MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); + +/* Capture Default 16bits/sample */ +static int c_ssize = UAC2_DEF_CSSIZE; +module_param(c_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); +#else +#include "u_uac1.h" + +static char *fn_play = FILE_PCM_PLAYBACK; +module_param(fn_play, charp, S_IRUGO); +MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); + +static char *fn_cap = FILE_PCM_CAPTURE; +module_param(fn_cap, charp, S_IRUGO); +MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); + +static char *fn_cntl = FILE_CONTROL; +module_param(fn_cntl, charp, S_IRUGO); +MODULE_PARM_DESC(fn_cntl, "Control device file name"); + +static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; +module_param(req_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); + +static int req_count = UAC1_REQ_COUNT; +module_param(req_count, int, S_IRUGO); +MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); + +static int audio_buf_size = UAC1_AUDIO_BUF_SIZE; +module_param(audio_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); +#endif + /* string IDs are assigned dynamically */ static struct usb_string strings_dev[] = { @@ -40,12 +100,12 @@ static struct usb_gadget_strings *audio_strings[] = { NULL, }; -#ifdef CONFIG_GADGET_UAC1 -#include "u_uac1.h" -#include "u_uac1.c" -#include "f_uac1.c" +#ifndef CONFIG_GADGET_UAC1 +static struct usb_function_instance *fi_uac2; +static struct usb_function *f_uac2; #else -#include "f_uac2.c" +static struct usb_function_instance *fi_uac1; +static struct usb_function *f_uac1; #endif /*-------------------------------------------------------------------------*/ @@ -109,6 +169,8 @@ static const struct usb_descriptor_header *otg_desc[] = { static int __init audio_do_config(struct usb_configuration *c) { + int status; + /* FIXME alloc iConfiguration string, set it in c->strings */ if (gadget_is_otg(c->cdev->gadget)) { @@ -116,7 +178,31 @@ static int __init audio_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - audio_bind_config(c); +#ifdef CONFIG_GADGET_UAC1 + f_uac1 = usb_get_function(fi_uac1); + if (IS_ERR(f_uac1)) { + status = PTR_ERR(f_uac1); + return status; + } + + status = usb_add_function(c, f_uac1); + if (status < 0) { + usb_put_function(f_uac1); + return status; + } +#else + f_uac2 = usb_get_function(fi_uac2); + if (IS_ERR(f_uac2)) { + status = PTR_ERR(f_uac2); + return status; + } + + status = usb_add_function(c, f_uac2); + if (status < 0) { + usb_put_function(f_uac2); + return status; + } +#endif return 0; } @@ -126,17 +212,47 @@ static struct usb_configuration audio_config_driver = { .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -#ifndef CONFIG_GADGET_UAC1 - .unbind = uac2_unbind_config, -#endif }; /*-------------------------------------------------------------------------*/ static int __init audio_bind(struct usb_composite_dev *cdev) { +#ifndef CONFIG_GADGET_UAC1 + struct f_uac2_opts *uac2_opts; +#else + struct f_uac1_opts *uac1_opts; +#endif int status; +#ifndef CONFIG_GADGET_UAC1 + fi_uac2 = usb_get_function_instance("uac2"); + if (IS_ERR(fi_uac2)) + return PTR_ERR(fi_uac2); +#else + fi_uac1 = usb_get_function_instance("uac1"); + if (IS_ERR(fi_uac1)) + return PTR_ERR(fi_uac1); +#endif + +#ifndef CONFIG_GADGET_UAC1 + uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst); + uac2_opts->p_chmask = p_chmask; + uac2_opts->p_srate = p_srate; + uac2_opts->p_ssize = p_ssize; + uac2_opts->c_chmask = c_chmask; + uac2_opts->c_srate = c_srate; + uac2_opts->c_ssize = c_ssize; +#else + uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst); + uac1_opts->fn_play = fn_play; + uac1_opts->fn_cap = fn_cap; + uac1_opts->fn_cntl = fn_cntl; + uac1_opts->req_buf_size = req_buf_size; + uac1_opts->req_count = req_count; + uac1_opts->audio_buf_size = audio_buf_size; +#endif + status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; @@ -152,13 +268,26 @@ static int __init audio_bind(struct usb_composite_dev *cdev) return 0; fail: +#ifndef CONFIG_GADGET_UAC1 + usb_put_function_instance(fi_uac2); +#else + usb_put_function_instance(fi_uac1); +#endif return status; } static int __exit audio_unbind(struct usb_composite_dev *cdev) { #ifdef CONFIG_GADGET_UAC1 - gaudio_cleanup(); + if (!IS_ERR_OR_NULL(f_uac1)) + usb_put_function(f_uac1); + if (!IS_ERR_OR_NULL(fi_uac1)) + usb_put_function_instance(fi_uac1); +#else + if (!IS_ERR_OR_NULL(f_uac2)) + usb_put_function(f_uac2); + if (!IS_ERR_OR_NULL(fi_uac2)) + usb_put_function_instance(fi_uac2); #endif return 0; } @@ -172,17 +301,7 @@ static __refdata struct usb_composite_driver audio_driver = { .unbind = __exit_p(audio_unbind), }; -static int __init init(void) -{ - return usb_composite_probe(&audio_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&audio_driver); -} -module_exit(cleanup); +module_usb_composite_driver(audio_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>"); diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c index e126b6b248e6..2e85d9473478 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/legacy/cdc2.c @@ -231,18 +231,8 @@ static __refdata struct usb_composite_driver cdc_driver = { .unbind = __exit_p(cdc_unbind), }; +module_usb_composite_driver(cdc_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&cdc_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&cdc_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 986fc511a2ed..1b075132f8f1 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -222,10 +222,12 @@ static void dbgp_unbind(struct usb_gadget *gadget) { #ifdef CONFIG_USB_G_DBGP_SERIAL kfree(dbgp.serial); + dbgp.serial = NULL; #endif if (dbgp.req) { kfree(dbgp.req->buf); usb_ep_free_request(gadget->ep0, dbgp.req); + dbgp.req = NULL; } gadget->ep0->driver_data = NULL; @@ -408,6 +410,7 @@ static __refdata struct usb_gadget_driver dbgp_driver = { .bind = dbgp_bind, .unbind = dbgp_unbind, .setup = dbgp_setup, + .reset = dbgp_disconnect, .disconnect = dbgp_disconnect, .driver = { .owner = THIS_MODULE, diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/legacy/ether.c index c1c113ef950c..c5fdc61cdc4a 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/legacy/ether.c @@ -475,18 +475,8 @@ static __refdata struct usb_composite_driver eth_driver = { .unbind = __exit_p(eth_unbind), }; +module_usb_composite_driver(eth_driver); + MODULE_DESCRIPTION(PREFIX DRIVER_DESC); MODULE_AUTHOR("David Brownell, Benedikt Spanger"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(ð_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(ð_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index fe12e6a27448..06acfa55864a 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -276,7 +276,7 @@ module_exit(gfs_exit); static void *functionfs_acquire_dev(struct ffs_dev *dev) { if (!try_module_get(THIS_MODULE)) - return ERR_PTR(-ENODEV); + return ERR_PTR(-ENOENT); return 0; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index e879e2c9f461..3d696b86ff76 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -163,15 +163,4 @@ static __refdata struct usb_composite_driver midi_driver = { .unbind = __exit_p(midi_unbind), }; -static int __init midi_init(void) -{ - return usb_composite_probe(&midi_driver); -} -module_init(midi_init); - -static void __exit midi_cleanup(void) -{ - usb_composite_unregister(&midi_driver); -} -module_exit(midi_cleanup); - +module_usb_composite_driver(midi_driver); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/legacy/hid.c index 778613eb37af..778613eb37af 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/legacy/hid.c diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/legacy/inode.c index 2e4ce7704908..edefec2cc584 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -440,7 +440,7 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) value = -ENOMEM; kbuf = memdup_user(buf, len); - if (!kbuf) { + if (IS_ERR(kbuf)) { value = PTR_ERR(kbuf); goto free1; } @@ -1775,6 +1775,7 @@ static struct usb_gadget_driver gadgetfs_driver = { .bind = gadgetfs_bind, .unbind = gadgetfs_unbind, .setup = gadgetfs_setup, + .reset = gadgetfs_disconnect, .disconnect = gadgetfs_disconnect, .suspend = gadgetfs_suspend, diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index 8e27a8c96444..8e27a8c96444 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/legacy/multi.c index 940f6cde8e89..39d27bb343b4 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -507,15 +507,4 @@ static __refdata struct usb_composite_driver multi_driver = { .needs_serial = 1, }; - -static int __init multi_init(void) -{ - return usb_composite_probe(&multi_driver); -} -module_init(multi_init); - -static void __exit multi_exit(void) -{ - usb_composite_unregister(&multi_driver); -} -module_exit(multi_exit); +module_usb_composite_driver(multi_driver); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/legacy/ncm.c index 81956feca1bd..e90e23db2acb 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -204,18 +204,8 @@ static __refdata struct usb_composite_driver ncm_driver = { .unbind = __exit_p(gncm_unbind), }; +module_usb_composite_driver(ncm_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Yauheni Kaliuta"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&ncm_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&ncm_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/legacy/nokia.c index 3ab386167519..9b8fd701648c 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/legacy/nokia.c @@ -347,14 +347,4 @@ static __refdata struct usb_composite_driver nokia_driver = { .unbind = __exit_p(nokia_unbind), }; -static int __init nokia_init(void) -{ - return usb_composite_probe(&nokia_driver); -} -module_init(nokia_init); - -static void __exit nokia_cleanup(void) -{ - usb_composite_unregister(&nokia_driver); -} -module_exit(nokia_cleanup); +module_usb_composite_driver(nokia_driver); diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/legacy/printer.c index 6474081dcbaf..6474081dcbaf 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/legacy/printer.c diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/legacy/serial.c index 1f5f978d35d5..1f5f978d35d5 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/legacy/serial.c diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 6cdb7a534f23..6cdb7a534f23 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/legacy/tcm_usb_gadget.h index 8289219925b8..8289219925b8 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.h +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.h diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/legacy/webcam.c index 8cef1e658c29..04a3da20f742 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -12,23 +12,31 @@ #include <linux/kernel.h> #include <linux/device.h> +#include <linux/module.h> #include <linux/usb/video.h> -#include "f_uvc.h" - -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "uvc_queue.c" -#include "uvc_video.c" -#include "uvc_v4l2.c" -#include "f_uvc.c" +#include "u_uvc.h" USB_GADGET_COMPOSITE_OPTIONS(); + +/*-------------------------------------------------------------------------*/ + +/* module parameters specific to the Video streaming endpoint */ +static unsigned int streaming_interval = 1; +module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_interval, "1 - 16"); + +static unsigned int streaming_maxpacket = 1024; +module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); + +static unsigned int streaming_maxburst; +module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); + +static unsigned int trace; +module_param(trace, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); /* -------------------------------------------------------------------------- * Device descriptor */ @@ -63,6 +71,9 @@ static struct usb_gadget_strings *webcam_device_strings[] = { NULL, }; +static struct usb_function_instance *fi_uvc; +static struct usb_function *f_uvc; + static struct usb_device_descriptor webcam_device_descriptor = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, @@ -326,9 +337,17 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { static int __init webcam_config_bind(struct usb_configuration *c) { - return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls, - uvc_fs_streaming_cls, uvc_hs_streaming_cls, - uvc_ss_streaming_cls); + int status = 0; + + f_uvc = usb_get_function(fi_uvc); + if (IS_ERR(f_uvc)) + return PTR_ERR(f_uvc); + + status = usb_add_function(c, f_uvc); + if (status < 0) + usb_put_function(f_uvc); + + return status; } static struct usb_configuration webcam_config_driver = { @@ -342,14 +361,36 @@ static struct usb_configuration webcam_config_driver = { static int /* __init_or_exit */ webcam_unbind(struct usb_composite_dev *cdev) { + if (!IS_ERR_OR_NULL(f_uvc)) + usb_put_function(f_uvc); + if (!IS_ERR_OR_NULL(fi_uvc)) + usb_put_function_instance(fi_uvc); return 0; } static int __init webcam_bind(struct usb_composite_dev *cdev) { + struct f_uvc_opts *uvc_opts; int ret; + fi_uvc = usb_get_function_instance("uvc"); + if (IS_ERR(fi_uvc)) + return PTR_ERR(fi_uvc); + + uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst); + + uvc_opts->streaming_interval = streaming_interval; + uvc_opts->streaming_maxpacket = streaming_maxpacket; + uvc_opts->streaming_maxburst = streaming_maxburst; + uvc_set_trace_param(trace); + + uvc_opts->fs_control = uvc_fs_control_cls; + uvc_opts->ss_control = uvc_ss_control_cls; + uvc_opts->fs_streaming = uvc_fs_streaming_cls; + uvc_opts->hs_streaming = uvc_hs_streaming_cls; + uvc_opts->ss_streaming = uvc_ss_streaming_cls; + /* Allocate string descriptor numbers ... note that string contents * can be overridden by the composite_dev glue. */ @@ -373,7 +414,7 @@ webcam_bind(struct usb_composite_dev *cdev) return 0; error: - webcam_unbind(cdev); + usb_put_function_instance(fi_uvc); return ret; } @@ -390,20 +431,7 @@ static __refdata struct usb_composite_driver webcam_driver = { .unbind = webcam_unbind, }; -static int __init -webcam_init(void) -{ - return usb_composite_probe(&webcam_driver); -} - -static void __exit -webcam_cleanup(void) -{ - usb_composite_unregister(&webcam_driver); -} - -module_init(webcam_init); -module_exit(webcam_cleanup); +module_usb_composite_driver(webcam_driver); MODULE_AUTHOR("Laurent Pinchart"); MODULE_DESCRIPTION("Webcam Video Gadget"); diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/legacy/zero.c index 134f354ede62..ebf09f439f3a 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -68,6 +68,8 @@ static struct usb_zero_options gzero_options = { .isoc_maxpacket = GZERO_ISOC_MAXPACKET, .bulk_buflen = GZERO_BULK_BUFLEN, .qlen = GZERO_QLEN, + .int_interval = GZERO_INT_INTERVAL, + .int_maxpacket = GZERO_INT_MAXPACKET, }; /*-------------------------------------------------------------------------*/ @@ -266,6 +268,21 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); +module_param_named(int_interval, gzero_options.int_interval, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(int_interval, "1 - 16"); + +module_param_named(int_maxpacket, gzero_options.int_maxpacket, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(int_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); + +module_param_named(int_mult, gzero_options.int_mult, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(int_mult, "0 - 2 (hs/ss only)"); + +module_param_named(int_maxburst, gzero_options.int_maxburst, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(int_maxburst, "0 - 15 (ss only)"); + static struct usb_function *func_lb; static struct usb_function_instance *func_inst_lb; @@ -301,6 +318,10 @@ static int __init zero_bind(struct usb_composite_dev *cdev) ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket; ss_opts->isoc_mult = gzero_options.isoc_mult; ss_opts->isoc_maxburst = gzero_options.isoc_maxburst; + ss_opts->int_interval = gzero_options.int_interval; + ss_opts->int_maxpacket = gzero_options.int_maxpacket; + ss_opts->int_mult = gzero_options.int_mult; + ss_opts->int_maxburst = gzero_options.int_maxburst; ss_opts->bulk_buflen = gzero_options.bulk_buflen; func_ss = usb_get_function(func_inst_ss); @@ -411,17 +432,7 @@ static __refdata struct usb_composite_driver zero_driver = { .resume = zero_resume, }; +module_usb_composite_driver(zero_driver); + MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&zero_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&zero_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c deleted file mode 100644 index 300b3a71383b..000000000000 --- a/drivers/usb/gadget/net2280.c +++ /dev/null @@ -1,2905 +0,0 @@ -/* - * Driver for the PLX NET2280 USB device controller. - * Specs and errata are available from <http://www.plxtech.com>. - * - * PLX Technology Inc. (formerly NetChip Technology) supported the - * development of this driver. - * - * - * CODE STATUS HIGHLIGHTS - * - * This driver should work well with most "gadget" drivers, including - * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers - * as well as Gadget Zero and Gadgetfs. - * - * DMA is enabled by default. Drivers using transfer queues might use - * DMA chaining to remove IRQ latencies between transfers. (Except when - * short OUT transfers happen.) Drivers can use the req->no_interrupt - * hint to completely eliminate some IRQs, if a later IRQ is guaranteed - * and DMA chaining is enabled. - * - * Note that almost all the errata workarounds here are only needed for - * rev1 chips. Rev1a silicon (0110) fixes almost all of them. - */ - -/* - * Copyright (C) 2003 David Brownell - * Copyright (C) 2003-2005 PLX Technology, Inc. - * - * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility - * with 2282 chip - * - * 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. - */ - -#undef DEBUG /* messages on error and most fault paths */ -#undef VERBOSE /* extra debug messages (success too) */ - -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/dma-mapping.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> -#include <linux/prefetch.h> - -#include <asm/byteorder.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/unaligned.h> - - -#define DRIVER_DESC "PLX NET228x USB Peripheral Controller" -#define DRIVER_VERSION "2005 Sept 27" - -#define EP_DONTUSE 13 /* nonzero */ - -#define USE_RDK_LEDS /* GPIO pins control three LEDs */ - - -static const char driver_name [] = "net2280"; -static const char driver_desc [] = DRIVER_DESC; - -static const char ep0name [] = "ep0"; -static const char *const ep_name [] = { - ep0name, - "ep-a", "ep-b", "ep-c", "ep-d", - "ep-e", "ep-f", -}; - -/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) - * use_dma_chaining -- dma descriptor queueing gives even more irq reduction - * - * The net2280 DMA engines are not tightly integrated with their FIFOs; - * not all cases are (yet) handled well in this driver or the silicon. - * Some gadget drivers work better with the dma support here than others. - * These two parameters let you use PIO or more aggressive DMA. - */ -static bool use_dma = 1; -static bool use_dma_chaining = 0; - -/* "modprobe net2280 use_dma=n" etc */ -module_param (use_dma, bool, S_IRUGO); -module_param (use_dma_chaining, bool, S_IRUGO); - - -/* mode 0 == ep-{a,b,c,d} 1K fifo each - * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable - * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable - */ -static ushort fifo_mode = 0; - -/* "modprobe net2280 fifo_mode=1" etc */ -module_param (fifo_mode, ushort, 0644); - -/* enable_suspend -- When enabled, the driver will respond to - * USB suspend requests by powering down the NET2280. Otherwise, - * USB suspend requests will be ignored. This is acceptable for - * self-powered devices - */ -static bool enable_suspend = 0; - -/* "modprobe net2280 enable_suspend=1" etc */ -module_param (enable_suspend, bool, S_IRUGO); - -/* force full-speed operation */ -static bool full_speed; -module_param(full_speed, bool, 0444); -MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!"); - -#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") - -#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG) -static char *type_string (u8 bmAttributes) -{ - switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { - case USB_ENDPOINT_XFER_BULK: return "bulk"; - case USB_ENDPOINT_XFER_ISOC: return "iso"; - case USB_ENDPOINT_XFER_INT: return "intr"; - } - return "control"; -} -#endif - -#include "net2280.h" - -#define valid_bit cpu_to_le32 (1 << VALID_BIT) -#define dma_done_ie cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE) - -/*-------------------------------------------------------------------------*/ - -static int -net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) -{ - struct net2280 *dev; - struct net2280_ep *ep; - u32 max, tmp; - unsigned long flags; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || !desc || ep->desc || _ep->name == ep0name - || desc->bDescriptorType != USB_DT_ENDPOINT) - return -EINVAL; - dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - /* erratum 0119 workaround ties up an endpoint number */ - if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) - return -EDOM; - - /* sanity check ep-e/ep-f since their fifos are small */ - max = usb_endpoint_maxp (desc) & 0x1fff; - if (ep->num > 4 && max > 64) - return -ERANGE; - - spin_lock_irqsave (&dev->lock, flags); - _ep->maxpacket = max & 0x7ff; - ep->desc = desc; - - /* ep_reset() has already been called */ - ep->stopped = 0; - ep->wedged = 0; - ep->out_overflow = 0; - - /* set speed-dependent max packet; may kick in high bandwidth */ - set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max); - - /* FIFO lines can't go to different packets. PIO is ok, so - * use it instead of troublesome (non-bulk) multi-packet DMA. - */ - if (ep->dma && (max % 4) != 0 && use_dma_chaining) { - DEBUG (ep->dev, "%s, no dma for maxpacket %d\n", - ep->ep.name, ep->ep.maxpacket); - ep->dma = NULL; - } - - /* set type, direction, address; reset fifo counters */ - writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); - tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - if (tmp == USB_ENDPOINT_XFER_INT) { - /* erratum 0105 workaround prevents hs NYET */ - if (dev->chiprev == 0100 - && dev->gadget.speed == USB_SPEED_HIGH - && !(desc->bEndpointAddress & USB_DIR_IN)) - writel ((1 << CLEAR_NAK_OUT_PACKETS_MODE), - &ep->regs->ep_rsp); - } else if (tmp == USB_ENDPOINT_XFER_BULK) { - /* catch some particularly blatant driver bugs */ - if ((dev->gadget.speed == USB_SPEED_HIGH - && max != 512) - || (dev->gadget.speed == USB_SPEED_FULL - && max > 64)) { - spin_unlock_irqrestore (&dev->lock, flags); - return -ERANGE; - } - } - ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0; - tmp <<= ENDPOINT_TYPE; - tmp |= desc->bEndpointAddress; - tmp |= (4 << ENDPOINT_BYTE_COUNT); /* default full fifo lines */ - tmp |= 1 << ENDPOINT_ENABLE; - wmb (); - - /* for OUT transfers, block the rx fifo until a read is posted */ - ep->is_in = (tmp & USB_DIR_IN) != 0; - if (!ep->is_in) - writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - else if (dev->pdev->device != 0x2280) { - /* Added for 2282, Don't use nak packets on an in endpoint, - * this was ignored on 2280 - */ - writel ((1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); - } - - writel (tmp, &ep->regs->ep_cfg); - - /* enable irqs */ - if (!ep->dma) { /* pio, per-packet */ - tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); - writel (tmp, &dev->regs->pciirqenb0); - - tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); - if (dev->pdev->device == 0x2280) - tmp |= readl (&ep->regs->ep_irqenb); - writel (tmp, &ep->regs->ep_irqenb); - } else { /* dma, per-request */ - tmp = (1 << (8 + ep->num)); /* completion */ - tmp |= readl (&dev->regs->pciirqenb1); - writel (tmp, &dev->regs->pciirqenb1); - - /* for short OUT transfers, dma completions can't - * advance the queue; do it pio-style, by hand. - * NOTE erratum 0112 workaround #2 - */ - if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { - tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); - writel (tmp, &ep->regs->ep_irqenb); - - tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); - writel (tmp, &dev->regs->pciirqenb0); - } - } - - tmp = desc->bEndpointAddress; - DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n", - _ep->name, tmp & 0x0f, DIR_STRING (tmp), - type_string (desc->bmAttributes), - ep->dma ? "dma" : "pio", max); - - /* pci writes may still be posted */ - spin_unlock_irqrestore (&dev->lock, flags); - return 0; -} - -static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec) -{ - u32 result; - - do { - result = readl (ptr); - if (result == ~(u32)0) /* "device unplugged" */ - return -ENODEV; - result &= mask; - if (result == done) - return 0; - udelay (1); - usec--; - } while (usec > 0); - return -ETIMEDOUT; -} - -static const struct usb_ep_ops net2280_ep_ops; - -static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) -{ - u32 tmp; - - ep->desc = NULL; - INIT_LIST_HEAD (&ep->queue); - - usb_ep_set_maxpacket_limit(&ep->ep, ~0); - ep->ep.ops = &net2280_ep_ops; - - /* disable the dma, irqs, endpoint... */ - if (ep->dma) { - writel (0, &ep->dma->dmactl); - writel ( (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) - | (1 << DMA_TRANSACTION_DONE_INTERRUPT) - | (1 << DMA_ABORT) - , &ep->dma->dmastat); - - tmp = readl (®s->pciirqenb0); - tmp &= ~(1 << ep->num); - writel (tmp, ®s->pciirqenb0); - } else { - tmp = readl (®s->pciirqenb1); - tmp &= ~(1 << (8 + ep->num)); /* completion */ - writel (tmp, ®s->pciirqenb1); - } - writel (0, &ep->regs->ep_irqenb); - - /* init to our chosen defaults, notably so that we NAK OUT - * packets until the driver queues a read (+note erratum 0112) - */ - if (!ep->is_in || ep->dev->pdev->device == 0x2280) { - tmp = (1 << SET_NAK_OUT_PACKETS_MODE) - | (1 << SET_NAK_OUT_PACKETS) - | (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_INTERRUPT_MODE); - } else { - /* added for 2282 */ - tmp = (1 << CLEAR_NAK_OUT_PACKETS_MODE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_INTERRUPT_MODE); - } - - if (ep->num != 0) { - tmp |= (1 << CLEAR_ENDPOINT_TOGGLE) - | (1 << CLEAR_ENDPOINT_HALT); - } - writel (tmp, &ep->regs->ep_rsp); - - /* scrub most status bits, and flush any fifo state */ - if (ep->dev->pdev->device == 0x2280) - tmp = (1 << FIFO_OVERFLOW) - | (1 << FIFO_UNDERFLOW); - else - tmp = 0; - - writel (tmp | (1 << TIMEOUT) - | (1 << USB_STALL_SENT) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_OUT_PING_NAK_SENT) - | (1 << USB_OUT_ACK_SENT) - | (1 << FIFO_FLUSH) - | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT) - , &ep->regs->ep_stat); - - /* fifo size is handled separately */ -} - -static void nuke (struct net2280_ep *); - -static int net2280_disable (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - unsigned long flags; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || !ep->desc || _ep->name == ep0name) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - nuke (ep); - ep_reset (ep->dev->regs, ep); - - VDEBUG (ep->dev, "disabled %s %s\n", - ep->dma ? "dma" : "pio", _ep->name); - - /* synch memory views with the device */ - (void) readl (&ep->regs->ep_cfg); - - if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) - ep->dma = &ep->dev->dma [ep->num - 1]; - - spin_unlock_irqrestore (&ep->dev->lock, flags); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct usb_request * -net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) -{ - struct net2280_ep *ep; - struct net2280_request *req; - - if (!_ep) - return NULL; - ep = container_of (_ep, struct net2280_ep, ep); - - req = kzalloc(sizeof(*req), gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD (&req->queue); - - /* this dma descriptor may be swapped with the previous dummy */ - if (ep->dma) { - struct net2280_dma *td; - - td = pci_pool_alloc (ep->dev->requests, gfp_flags, - &req->td_dma); - if (!td) { - kfree (req); - return NULL; - } - td->dmacount = 0; /* not VALID */ - td->dmadesc = td->dmaaddr; - req->td = td; - } - return &req->req; -} - -static void -net2280_free_request (struct usb_ep *_ep, struct usb_request *_req) -{ - struct net2280_ep *ep; - struct net2280_request *req; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || !_req) - return; - - req = container_of (_req, struct net2280_request, req); - WARN_ON (!list_empty (&req->queue)); - if (req->td) - pci_pool_free (ep->dev->requests, req->td, req->td_dma); - kfree (req); -} - -/*-------------------------------------------------------------------------*/ - -/* load a packet into the fifo we use for usb IN transfers. - * works for all endpoints. - * - * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo - * at a time, but this code is simpler because it knows it only writes - * one packet. ep-a..ep-d should use dma instead. - */ -static void -write_fifo (struct net2280_ep *ep, struct usb_request *req) -{ - struct net2280_ep_regs __iomem *regs = ep->regs; - u8 *buf; - u32 tmp; - unsigned count, total; - - /* INVARIANT: fifo is currently empty. (testable) */ - - if (req) { - buf = req->buf + req->actual; - prefetch (buf); - total = req->length - req->actual; - } else { - total = 0; - buf = NULL; - } - - /* write just one packet at a time */ - count = ep->ep.maxpacket; - if (count > total) /* min() cannot be used on a bitfield */ - count = total; - - VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", - ep->ep.name, count, - (count != ep->ep.maxpacket) ? " (short)" : "", - req); - while (count >= 4) { - /* NOTE be careful if you try to align these. fifo lines - * should normally be full (4 bytes) and successive partial - * lines are ok only in certain cases. - */ - tmp = get_unaligned ((u32 *)buf); - cpu_to_le32s (&tmp); - writel (tmp, ®s->ep_data); - buf += 4; - count -= 4; - } - - /* last fifo entry is "short" unless we wrote a full packet. - * also explicitly validate last word in (periodic) transfers - * when maxpacket is not a multiple of 4 bytes. - */ - if (count || total < ep->ep.maxpacket) { - tmp = count ? get_unaligned ((u32 *)buf) : count; - cpu_to_le32s (&tmp); - set_fifo_bytecount (ep, count & 0x03); - writel (tmp, ®s->ep_data); - } - - /* pci writes may still be posted */ -} - -/* work around erratum 0106: PCI and USB race over the OUT fifo. - * caller guarantees chiprev 0100, out endpoint is NAKing, and - * there's no real data in the fifo. - * - * NOTE: also used in cases where that erratum doesn't apply: - * where the host wrote "too much" data to us. - */ -static void out_flush (struct net2280_ep *ep) -{ - u32 __iomem *statp; - u32 tmp; - - ASSERT_OUT_NAKING (ep); - - statp = &ep->regs->ep_stat; - writel ( (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - , statp); - writel ((1 << FIFO_FLUSH), statp); - mb (); - tmp = readl (statp); - if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - /* high speed did bulk NYET; fifo isn't filling */ - && ep->dev->gadget.speed == USB_SPEED_FULL) { - unsigned usec; - - usec = 50; /* 64 byte bulk/interrupt */ - handshake (statp, (1 << USB_OUT_PING_NAK_SENT), - (1 << USB_OUT_PING_NAK_SENT), usec); - /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ - } -} - -/* unload packet(s) from the fifo we use for usb OUT transfers. - * returns true iff the request completed, because of short packet - * or the request buffer having filled with full packets. - * - * for ep-a..ep-d this will read multiple packets out when they - * have been accepted. - */ -static int -read_fifo (struct net2280_ep *ep, struct net2280_request *req) -{ - struct net2280_ep_regs __iomem *regs = ep->regs; - u8 *buf = req->req.buf + req->req.actual; - unsigned count, tmp, is_short; - unsigned cleanup = 0, prevent = 0; - - /* erratum 0106 ... packets coming in during fifo reads might - * be incompletely rejected. not all cases have workarounds. - */ - if (ep->dev->chiprev == 0x0100 - && ep->dev->gadget.speed == USB_SPEED_FULL) { - udelay (1); - tmp = readl (&ep->regs->ep_stat); - if ((tmp & (1 << NAK_OUT_PACKETS))) - cleanup = 1; - else if ((tmp & (1 << FIFO_FULL))) { - start_out_naking (ep); - prevent = 1; - } - /* else: hope we don't see the problem */ - } - - /* never overflow the rx buffer. the fifo reads packets until - * it sees a short one; we might not be ready for them all. - */ - prefetchw (buf); - count = readl (®s->ep_avail); - if (unlikely (count == 0)) { - udelay (1); - tmp = readl (&ep->regs->ep_stat); - count = readl (®s->ep_avail); - /* handled that data already? */ - if (count == 0 && (tmp & (1 << NAK_OUT_PACKETS)) == 0) - return 0; - } - - tmp = req->req.length - req->req.actual; - if (count > tmp) { - /* as with DMA, data overflow gets flushed */ - if ((tmp % ep->ep.maxpacket) != 0) { - ERROR (ep->dev, - "%s out fifo %d bytes, expected %d\n", - ep->ep.name, count, tmp); - req->req.status = -EOVERFLOW; - cleanup = 1; - /* NAK_OUT_PACKETS will be set, so flushing is safe; - * the next read will start with the next packet - */ - } /* else it's a ZLP, no worries */ - count = tmp; - } - req->req.actual += count; - - is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); - - VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", - ep->ep.name, count, is_short ? " (short)" : "", - cleanup ? " flush" : "", prevent ? " nak" : "", - req, req->req.actual, req->req.length); - - while (count >= 4) { - tmp = readl (®s->ep_data); - cpu_to_le32s (&tmp); - put_unaligned (tmp, (u32 *)buf); - buf += 4; - count -= 4; - } - if (count) { - tmp = readl (®s->ep_data); - /* LE conversion is implicit here: */ - do { - *buf++ = (u8) tmp; - tmp >>= 8; - } while (--count); - } - if (cleanup) - out_flush (ep); - if (prevent) { - writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - (void) readl (&ep->regs->ep_rsp); - } - - return is_short || ((req->req.actual == req->req.length) - && !req->req.zero); -} - -/* fill out dma descriptor to match a given request */ -static void -fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid) -{ - struct net2280_dma *td = req->td; - u32 dmacount = req->req.length; - - /* don't let DMA continue after a short OUT packet, - * so overruns can't affect the next transfer. - * in case of overruns on max-size packets, we can't - * stop the fifo from filling but we can flush it. - */ - if (ep->is_in) - dmacount |= (1 << DMA_DIRECTION); - if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) - || ep->dev->pdev->device != 0x2280) - dmacount |= (1 << END_OF_CHAIN); - - req->valid = valid; - if (valid) - dmacount |= (1 << VALID_BIT); - if (likely(!req->req.no_interrupt || !use_dma_chaining)) - dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE); - - /* td->dmadesc = previously set by caller */ - td->dmaaddr = cpu_to_le32 (req->req.dma); - - /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ - wmb (); - td->dmacount = cpu_to_le32(dmacount); -} - -static const u32 dmactl_default = - (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) - | (1 << DMA_CLEAR_COUNT_ENABLE) - /* erratum 0116 workaround part 1 (use POLLING) */ - | (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) - | (1 << DMA_VALID_BIT_POLLING_ENABLE) - | (1 << DMA_VALID_BIT_ENABLE) - | (1 << DMA_SCATTER_GATHER_ENABLE) - /* erratum 0116 workaround part 2 (no AUTOSTART) */ - | (1 << DMA_ENABLE); - -static inline void spin_stop_dma (struct net2280_dma_regs __iomem *dma) -{ - handshake (&dma->dmactl, (1 << DMA_ENABLE), 0, 50); -} - -static inline void stop_dma (struct net2280_dma_regs __iomem *dma) -{ - writel (readl (&dma->dmactl) & ~(1 << DMA_ENABLE), &dma->dmactl); - spin_stop_dma (dma); -} - -static void start_queue (struct net2280_ep *ep, u32 dmactl, u32 td_dma) -{ - struct net2280_dma_regs __iomem *dma = ep->dma; - unsigned int tmp = (1 << VALID_BIT) | (ep->is_in << DMA_DIRECTION); - - if (ep->dev->pdev->device != 0x2280) - tmp |= (1 << END_OF_CHAIN); - - writel (tmp, &dma->dmacount); - writel (readl (&dma->dmastat), &dma->dmastat); - - writel (td_dma, &dma->dmadesc); - writel (dmactl, &dma->dmactl); - - /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ - (void) readl (&ep->dev->pci->pcimstctl); - - writel ((1 << DMA_START), &dma->dmastat); - - if (!ep->is_in) - stop_out_naking (ep); -} - -static void start_dma (struct net2280_ep *ep, struct net2280_request *req) -{ - u32 tmp; - struct net2280_dma_regs __iomem *dma = ep->dma; - - /* FIXME can't use DMA for ZLPs */ - - /* on this path we "know" there's no dma active (yet) */ - WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE)); - writel (0, &ep->dma->dmactl); - - /* previous OUT packet might have been short */ - if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat)) - & (1 << NAK_OUT_PACKETS)) != 0) { - writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT), - &ep->regs->ep_stat); - - tmp = readl (&ep->regs->ep_avail); - if (tmp) { - writel (readl (&dma->dmastat), &dma->dmastat); - - /* transfer all/some fifo data */ - writel (req->req.dma, &dma->dmaaddr); - tmp = min (tmp, req->req.length); - - /* dma irq, faking scatterlist status */ - req->td->dmacount = cpu_to_le32 (req->req.length - tmp); - writel ((1 << DMA_DONE_INTERRUPT_ENABLE) - | tmp, &dma->dmacount); - req->td->dmadesc = 0; - req->valid = 1; - - writel ((1 << DMA_ENABLE), &dma->dmactl); - writel ((1 << DMA_START), &dma->dmastat); - return; - } - } - - tmp = dmactl_default; - - /* force packet boundaries between dma requests, but prevent the - * controller from automagically writing a last "short" packet - * (zero length) unless the driver explicitly said to do that. - */ - if (ep->is_in) { - if (likely ((req->req.length % ep->ep.maxpacket) != 0 - || req->req.zero)) { - tmp |= (1 << DMA_FIFO_VALIDATE); - ep->in_fifo_validate = 1; - } else - ep->in_fifo_validate = 0; - } - - /* init req->td, pointing to the current dummy */ - req->td->dmadesc = cpu_to_le32 (ep->td_dma); - fill_dma_desc (ep, req, 1); - - if (!use_dma_chaining) - req->td->dmacount |= cpu_to_le32 (1 << END_OF_CHAIN); - - start_queue (ep, tmp, req->td_dma); -} - -static inline void -queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid) -{ - struct net2280_dma *end; - dma_addr_t tmp; - - /* swap new dummy for old, link; fill and maybe activate */ - end = ep->dummy; - ep->dummy = req->td; - req->td = end; - - tmp = ep->td_dma; - ep->td_dma = req->td_dma; - req->td_dma = tmp; - - end->dmadesc = cpu_to_le32 (ep->td_dma); - - fill_dma_desc (ep, req, valid); -} - -static void -done (struct net2280_ep *ep, struct net2280_request *req, int status) -{ - struct net2280 *dev; - unsigned stopped = ep->stopped; - - list_del_init (&req->queue); - - if (req->req.status == -EINPROGRESS) - req->req.status = status; - else - status = req->req.status; - - dev = ep->dev; - if (ep->dma) - usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); - - if (status && status != -ESHUTDOWN) - VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - - /* don't modify queue heads during completion callback */ - ep->stopped = 1; - spin_unlock (&dev->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dev->lock); - ep->stopped = stopped; -} - -/*-------------------------------------------------------------------------*/ - -static int -net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) -{ - struct net2280_request *req; - struct net2280_ep *ep; - struct net2280 *dev; - unsigned long flags; - - /* we always require a cpu-view buffer, so that we can - * always use pio (as fallback or whatever). - */ - req = container_of (_req, struct net2280_request, req); - if (!_req || !_req->complete || !_req->buf - || !list_empty (&req->queue)) - return -EINVAL; - if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) - return -EDOM; - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -EINVAL; - dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - /* FIXME implement PIO fallback for ZLPs with DMA */ - if (ep->dma && _req->length == 0) - return -EOPNOTSUPP; - - /* set up dma mapping in case the caller didn't */ - if (ep->dma) { - int ret; - - ret = usb_gadget_map_request(&dev->gadget, _req, - ep->is_in); - if (ret) - return ret; - } - -#if 0 - VDEBUG (dev, "%s queue req %p, len %d buf %p\n", - _ep->name, _req, _req->length, _req->buf); -#endif - - spin_lock_irqsave (&dev->lock, flags); - - _req->status = -EINPROGRESS; - _req->actual = 0; - - /* kickstart this i/o queue? */ - if (list_empty (&ep->queue) && !ep->stopped) { - /* use DMA if the endpoint supports it, else pio */ - if (ep->dma) - start_dma (ep, req); - else { - /* maybe there's no control data, just status ack */ - if (ep->num == 0 && _req->length == 0) { - allow_status (ep); - done (ep, req, 0); - VDEBUG (dev, "%s status ack\n", ep->ep.name); - goto done; - } - - /* PIO ... stuff the fifo, or unblock it. */ - if (ep->is_in) - write_fifo (ep, _req); - else if (list_empty (&ep->queue)) { - u32 s; - - /* OUT FIFO might have packet(s) buffered */ - s = readl (&ep->regs->ep_stat); - if ((s & (1 << FIFO_EMPTY)) == 0) { - /* note: _req->short_not_ok is - * ignored here since PIO _always_ - * stops queue advance here, and - * _req->status doesn't change for - * short reads (only _req->actual) - */ - if (read_fifo (ep, req)) { - done (ep, req, 0); - if (ep->num == 0) - allow_status (ep); - /* don't queue it */ - req = NULL; - } else - s = readl (&ep->regs->ep_stat); - } - - /* don't NAK, let the fifo fill */ - if (req && (s & (1 << NAK_OUT_PACKETS))) - writel ((1 << CLEAR_NAK_OUT_PACKETS), - &ep->regs->ep_rsp); - } - } - - } else if (ep->dma) { - int valid = 1; - - if (ep->is_in) { - int expect; - - /* preventing magic zlps is per-engine state, not - * per-transfer; irq logic must recover hiccups. - */ - expect = likely (req->req.zero - || (req->req.length % ep->ep.maxpacket) != 0); - if (expect != ep->in_fifo_validate) - valid = 0; - } - queue_dma (ep, req, valid); - - } /* else the irq handler advances the queue. */ - - ep->responded = 1; - if (req) - list_add_tail (&req->queue, &ep->queue); -done: - spin_unlock_irqrestore (&dev->lock, flags); - - /* pci writes may still be posted */ - return 0; -} - -static inline void -dma_done ( - struct net2280_ep *ep, - struct net2280_request *req, - u32 dmacount, - int status -) -{ - req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); - done (ep, req, status); -} - -static void restart_dma (struct net2280_ep *ep); - -static void scan_dma_completions (struct net2280_ep *ep) -{ - /* only look at descriptors that were "naturally" retired, - * so fifo and list head state won't matter - */ - while (!list_empty (&ep->queue)) { - struct net2280_request *req; - u32 tmp; - - req = list_entry (ep->queue.next, - struct net2280_request, queue); - if (!req->valid) - break; - rmb (); - tmp = le32_to_cpup (&req->td->dmacount); - if ((tmp & (1 << VALID_BIT)) != 0) - break; - - /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" - * cases where DMA must be aborted; this code handles - * all non-abort DMA completions. - */ - if (unlikely (req->td->dmadesc == 0)) { - /* paranoia */ - tmp = readl (&ep->dma->dmacount); - if (tmp & DMA_BYTE_COUNT_MASK) - break; - /* single transfer mode */ - dma_done (ep, req, tmp, 0); - break; - } else if (!ep->is_in - && (req->req.length % ep->ep.maxpacket) != 0) { - tmp = readl (&ep->regs->ep_stat); - - /* AVOID TROUBLE HERE by not issuing short reads from - * your gadget driver. That helps avoids errata 0121, - * 0122, and 0124; not all cases trigger the warning. - */ - if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - WARNING (ep->dev, "%s lost packet sync!\n", - ep->ep.name); - req->req.status = -EOVERFLOW; - } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { - /* fifo gets flushed later */ - ep->out_overflow = 1; - DEBUG (ep->dev, "%s dma, discard %d len %d\n", - ep->ep.name, tmp, - req->req.length); - req->req.status = -EOVERFLOW; - } - } - dma_done (ep, req, tmp, 0); - } -} - -static void restart_dma (struct net2280_ep *ep) -{ - struct net2280_request *req; - u32 dmactl = dmactl_default; - - if (ep->stopped) - return; - req = list_entry (ep->queue.next, struct net2280_request, queue); - - if (!use_dma_chaining) { - start_dma (ep, req); - return; - } - - /* the 2280 will be processing the queue unless queue hiccups after - * the previous transfer: - * IN: wanted automagic zlp, head doesn't (or vice versa) - * DMA_FIFO_VALIDATE doesn't init from dma descriptors. - * OUT: was "usb-short", we must restart. - */ - if (ep->is_in && !req->valid) { - struct net2280_request *entry, *prev = NULL; - int reqmode, done = 0; - - DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); - ep->in_fifo_validate = likely (req->req.zero - || (req->req.length % ep->ep.maxpacket) != 0); - if (ep->in_fifo_validate) - dmactl |= (1 << DMA_FIFO_VALIDATE); - list_for_each_entry (entry, &ep->queue, queue) { - __le32 dmacount; - - if (entry == req) - continue; - dmacount = entry->td->dmacount; - if (!done) { - reqmode = likely (entry->req.zero - || (entry->req.length - % ep->ep.maxpacket) != 0); - if (reqmode == ep->in_fifo_validate) { - entry->valid = 1; - dmacount |= valid_bit; - entry->td->dmacount = dmacount; - prev = entry; - continue; - } else { - /* force a hiccup */ - prev->td->dmacount |= dma_done_ie; - done = 1; - } - } - - /* walk the rest of the queue so unlinks behave */ - entry->valid = 0; - dmacount &= ~valid_bit; - entry->td->dmacount = dmacount; - prev = entry; - } - } - - writel (0, &ep->dma->dmactl); - start_queue (ep, dmactl, req->td_dma); -} - -static void abort_dma (struct net2280_ep *ep) -{ - /* abort the current transfer */ - if (likely (!list_empty (&ep->queue))) { - /* FIXME work around errata 0121, 0122, 0124 */ - writel ((1 << DMA_ABORT), &ep->dma->dmastat); - spin_stop_dma (ep->dma); - } else - stop_dma (ep->dma); - scan_dma_completions (ep); -} - -/* dequeue ALL requests */ -static void nuke (struct net2280_ep *ep) -{ - struct net2280_request *req; - - /* called with spinlock held */ - ep->stopped = 1; - if (ep->dma) - abort_dma (ep); - while (!list_empty (&ep->queue)) { - req = list_entry (ep->queue.next, - struct net2280_request, - queue); - done (ep, req, -ESHUTDOWN); - } -} - -/* dequeue JUST ONE request */ -static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req) -{ - struct net2280_ep *ep; - struct net2280_request *req; - unsigned long flags; - u32 dmactl; - int stopped; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0) || !_req) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - stopped = ep->stopped; - - /* quiesce dma while we patch the queue */ - dmactl = 0; - ep->stopped = 1; - if (ep->dma) { - dmactl = readl (&ep->dma->dmactl); - /* WARNING erratum 0127 may kick in ... */ - stop_dma (ep->dma); - scan_dma_completions (ep); - } - - /* make sure it's still queued on this endpoint */ - list_for_each_entry (req, &ep->queue, queue) { - if (&req->req == _req) - break; - } - if (&req->req != _req) { - spin_unlock_irqrestore (&ep->dev->lock, flags); - return -EINVAL; - } - - /* queue head may be partially complete. */ - if (ep->queue.next == &req->queue) { - if (ep->dma) { - DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name); - _req->status = -ECONNRESET; - abort_dma (ep); - if (likely (ep->queue.next == &req->queue)) { - // NOTE: misreports single-transfer mode - req->td->dmacount = 0; /* invalidate */ - dma_done (ep, req, - readl (&ep->dma->dmacount), - -ECONNRESET); - } - } else { - DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name); - done (ep, req, -ECONNRESET); - } - req = NULL; - - /* patch up hardware chaining data */ - } else if (ep->dma && use_dma_chaining) { - if (req->queue.prev == ep->queue.next) { - writel (le32_to_cpu (req->td->dmadesc), - &ep->dma->dmadesc); - if (req->td->dmacount & dma_done_ie) - writel (readl (&ep->dma->dmacount) - | le32_to_cpu(dma_done_ie), - &ep->dma->dmacount); - } else { - struct net2280_request *prev; - - prev = list_entry (req->queue.prev, - struct net2280_request, queue); - prev->td->dmadesc = req->td->dmadesc; - if (req->td->dmacount & dma_done_ie) - prev->td->dmacount |= dma_done_ie; - } - } - - if (req) - done (ep, req, -ECONNRESET); - ep->stopped = stopped; - - if (ep->dma) { - /* turn off dma on inactive queues */ - if (list_empty (&ep->queue)) - stop_dma (ep->dma); - else if (!ep->stopped) { - /* resume current request, or start new one */ - if (req) - writel (dmactl, &ep->dma->dmactl); - else - start_dma (ep, list_entry (ep->queue.next, - struct net2280_request, queue)); - } - } - - spin_unlock_irqrestore (&ep->dev->lock, flags); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int net2280_fifo_status (struct usb_ep *_ep); - -static int -net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) -{ - struct net2280_ep *ep; - unsigned long flags; - int retval = 0; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -EINVAL; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) - == USB_ENDPOINT_XFER_ISOC) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - if (!list_empty (&ep->queue)) - retval = -EAGAIN; - else if (ep->is_in && value && net2280_fifo_status (_ep) != 0) - retval = -EAGAIN; - else { - VDEBUG (ep->dev, "%s %s %s\n", _ep->name, - value ? "set" : "clear", - wedged ? "wedge" : "halt"); - /* set/clear, then synch memory views with the device */ - if (value) { - if (ep->num == 0) - ep->dev->protocol_stall = 1; - else - set_halt (ep); - if (wedged) - ep->wedged = 1; - } else { - clear_halt (ep); - ep->wedged = 0; - } - (void) readl (&ep->regs->ep_rsp); - } - spin_unlock_irqrestore (&ep->dev->lock, flags); - - return retval; -} - -static int -net2280_set_halt(struct usb_ep *_ep, int value) -{ - return net2280_set_halt_and_wedge(_ep, value, 0); -} - -static int -net2280_set_wedge(struct usb_ep *_ep) -{ - if (!_ep || _ep->name == ep0name) - return -EINVAL; - return net2280_set_halt_and_wedge(_ep, 1, 1); -} - -static int -net2280_fifo_status (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - u32 avail; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -ENODEV; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - avail = readl (&ep->regs->ep_avail) & ((1 << 12) - 1); - if (avail > ep->fifo_size) - return -EOVERFLOW; - if (ep->is_in) - avail = ep->fifo_size - avail; - return avail; -} - -static void -net2280_fifo_flush (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return; - - writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); - (void) readl (&ep->regs->ep_rsp); -} - -static const struct usb_ep_ops net2280_ep_ops = { - .enable = net2280_enable, - .disable = net2280_disable, - - .alloc_request = net2280_alloc_request, - .free_request = net2280_free_request, - - .queue = net2280_queue, - .dequeue = net2280_dequeue, - - .set_halt = net2280_set_halt, - .set_wedge = net2280_set_wedge, - .fifo_status = net2280_fifo_status, - .fifo_flush = net2280_fifo_flush, -}; - -/*-------------------------------------------------------------------------*/ - -static int net2280_get_frame (struct usb_gadget *_gadget) -{ - struct net2280 *dev; - unsigned long flags; - u16 retval; - - if (!_gadget) - return -ENODEV; - dev = container_of (_gadget, struct net2280, gadget); - spin_lock_irqsave (&dev->lock, flags); - retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff; - spin_unlock_irqrestore (&dev->lock, flags); - return retval; -} - -static int net2280_wakeup (struct usb_gadget *_gadget) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return 0; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - if (tmp & (1 << DEVICE_REMOTE_WAKEUP_ENABLE)) - writel (1 << GENERATE_RESUME, &dev->usb->usbstat); - spin_unlock_irqrestore (&dev->lock, flags); - - /* pci writes may still be posted */ - return 0; -} - -static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return 0; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - if (value) - tmp |= (1 << SELF_POWERED_STATUS); - else - tmp &= ~(1 << SELF_POWERED_STATUS); - writel (tmp, &dev->usb->usbctl); - spin_unlock_irqrestore (&dev->lock, flags); - - return 0; -} - -static int net2280_pullup(struct usb_gadget *_gadget, int is_on) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return -ENODEV; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - dev->softconnect = (is_on != 0); - if (is_on) - tmp |= (1 << USB_DETECT_ENABLE); - else - tmp &= ~(1 << USB_DETECT_ENABLE); - writel (tmp, &dev->usb->usbctl); - spin_unlock_irqrestore (&dev->lock, flags); - - return 0; -} - -static int net2280_start(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver); -static int net2280_stop(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver); - -static const struct usb_gadget_ops net2280_ops = { - .get_frame = net2280_get_frame, - .wakeup = net2280_wakeup, - .set_selfpowered = net2280_set_selfpowered, - .pullup = net2280_pullup, - .udc_start = net2280_start, - .udc_stop = net2280_stop, -}; - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - -/* FIXME move these into procfs, and use seq_file. - * Sysfs _still_ doesn't behave for arbitrarily sized files, - * and also doesn't help products using this with 2.4 kernels. - */ - -/* "function" sysfs attribute */ -static ssize_t function_show(struct device *_dev, struct device_attribute *attr, - char *buf) -{ - struct net2280 *dev = dev_get_drvdata (_dev); - - if (!dev->driver - || !dev->driver->function - || strlen (dev->driver->function) > PAGE_SIZE) - return 0; - return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); -} -static DEVICE_ATTR_RO(function); - -static ssize_t registers_show(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct net2280 *dev; - char *next; - unsigned size, t; - unsigned long flags; - int i; - u32 t1, t2; - const char *s; - - dev = dev_get_drvdata (_dev); - next = buf; - size = PAGE_SIZE; - spin_lock_irqsave (&dev->lock, flags); - - if (dev->driver) - s = dev->driver->driver.name; - else - s = "(none)"; - - /* Main Control Registers */ - t = scnprintf (next, size, "%s version " DRIVER_VERSION - ", chiprev %04x, dma %s\n\n" - "devinit %03x fifoctl %08x gadget '%s'\n" - "pci irqenb0 %02x irqenb1 %08x " - "irqstat0 %04x irqstat1 %08x\n", - driver_name, dev->chiprev, - use_dma - ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled", - readl (&dev->regs->devinit), - readl (&dev->regs->fifoctl), - s, - readl (&dev->regs->pciirqenb0), - readl (&dev->regs->pciirqenb1), - readl (&dev->regs->irqstat0), - readl (&dev->regs->irqstat1)); - size -= t; - next += t; - - /* USB Control Registers */ - t1 = readl (&dev->usb->usbctl); - t2 = readl (&dev->usb->usbstat); - if (t1 & (1 << VBUS_PIN)) { - if (t2 & (1 << HIGH_SPEED)) - s = "high speed"; - else if (dev->gadget.speed == USB_SPEED_UNKNOWN) - s = "powered"; - else - s = "full speed"; - /* full speed bit (6) not working?? */ - } else - s = "not attached"; - t = scnprintf (next, size, - "stdrsp %08x usbctl %08x usbstat %08x " - "addr 0x%02x (%s)\n", - readl (&dev->usb->stdrsp), t1, t2, - readl (&dev->usb->ouraddr), s); - size -= t; - next += t; - - /* PCI Master Control Registers */ - - /* DMA Control Registers */ - - /* Configurable EP Control Registers */ - for (i = 0; i < 7; i++) { - struct net2280_ep *ep; - - ep = &dev->ep [i]; - if (i && !ep->desc) - continue; - - t1 = readl (&ep->regs->ep_cfg); - t2 = readl (&ep->regs->ep_rsp) & 0xff; - t = scnprintf (next, size, - "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" - "irqenb %02x\n", - ep->ep.name, t1, t2, - (t2 & (1 << CLEAR_NAK_OUT_PACKETS)) - ? "NAK " : "", - (t2 & (1 << CLEAR_EP_HIDE_STATUS_PHASE)) - ? "hide " : "", - (t2 & (1 << CLEAR_EP_FORCE_CRC_ERROR)) - ? "CRC " : "", - (t2 & (1 << CLEAR_INTERRUPT_MODE)) - ? "interrupt " : "", - (t2 & (1<<CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) - ? "status " : "", - (t2 & (1 << CLEAR_NAK_OUT_PACKETS_MODE)) - ? "NAKmode " : "", - (t2 & (1 << CLEAR_ENDPOINT_TOGGLE)) - ? "DATA1 " : "DATA0 ", - (t2 & (1 << CLEAR_ENDPOINT_HALT)) - ? "HALT " : "", - readl (&ep->regs->ep_irqenb)); - size -= t; - next += t; - - t = scnprintf (next, size, - "\tstat %08x avail %04x " - "(ep%d%s-%s)%s\n", - readl (&ep->regs->ep_stat), - readl (&ep->regs->ep_avail), - t1 & 0x0f, DIR_STRING (t1), - type_string (t1 >> 8), - ep->stopped ? "*" : ""); - size -= t; - next += t; - - if (!ep->dma) - continue; - - t = scnprintf (next, size, - " dma\tctl %08x stat %08x count %08x\n" - "\taddr %08x desc %08x\n", - readl (&ep->dma->dmactl), - readl (&ep->dma->dmastat), - readl (&ep->dma->dmacount), - readl (&ep->dma->dmaaddr), - readl (&ep->dma->dmadesc)); - size -= t; - next += t; - - } - - /* Indexed Registers */ - // none yet - - /* Statistics */ - t = scnprintf (next, size, "\nirqs: "); - size -= t; - next += t; - for (i = 0; i < 7; i++) { - struct net2280_ep *ep; - - ep = &dev->ep [i]; - if (i && !ep->irqs) - continue; - t = scnprintf (next, size, " %s/%lu", ep->ep.name, ep->irqs); - size -= t; - next += t; - - } - t = scnprintf (next, size, "\n"); - size -= t; - next += t; - - spin_unlock_irqrestore (&dev->lock, flags); - - return PAGE_SIZE - size; -} -static DEVICE_ATTR_RO(registers); - -static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, - char *buf) -{ - struct net2280 *dev; - char *next; - unsigned size; - unsigned long flags; - int i; - - dev = dev_get_drvdata (_dev); - next = buf; - size = PAGE_SIZE; - spin_lock_irqsave (&dev->lock, flags); - - for (i = 0; i < 7; i++) { - struct net2280_ep *ep = &dev->ep [i]; - struct net2280_request *req; - int t; - - if (i != 0) { - const struct usb_endpoint_descriptor *d; - - d = ep->desc; - if (!d) - continue; - t = d->bEndpointAddress; - t = scnprintf (next, size, - "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", - ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, - (t & USB_DIR_IN) ? "in" : "out", - ({ char *val; - switch (d->bmAttributes & 0x03) { - case USB_ENDPOINT_XFER_BULK: - val = "bulk"; break; - case USB_ENDPOINT_XFER_INT: - val = "intr"; break; - default: - val = "iso"; break; - } val; }), - usb_endpoint_maxp (d) & 0x1fff, - ep->dma ? "dma" : "pio", ep->fifo_size - ); - } else /* ep0 should only have one transfer queued */ - t = scnprintf (next, size, "ep0 max 64 pio %s\n", - ep->is_in ? "in" : "out"); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - - if (list_empty (&ep->queue)) { - t = scnprintf (next, size, "\t(nothing queued)\n"); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - continue; - } - list_for_each_entry (req, &ep->queue, queue) { - if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc)) - t = scnprintf (next, size, - "\treq %p len %d/%d " - "buf %p (dmacount %08x)\n", - &req->req, req->req.actual, - req->req.length, req->req.buf, - readl (&ep->dma->dmacount)); - else - t = scnprintf (next, size, - "\treq %p len %d/%d buf %p\n", - &req->req, req->req.actual, - req->req.length, req->req.buf); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - - if (ep->dma) { - struct net2280_dma *td; - - td = req->td; - t = scnprintf (next, size, "\t td %08x " - " count %08x buf %08x desc %08x\n", - (u32) req->td_dma, - le32_to_cpu (td->dmacount), - le32_to_cpu (td->dmaaddr), - le32_to_cpu (td->dmadesc)); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - } - } - } - -done: - spin_unlock_irqrestore (&dev->lock, flags); - return PAGE_SIZE - size; -} -static DEVICE_ATTR_RO(queues); - - -#else - -#define device_create_file(a,b) (0) -#define device_remove_file(a,b) do { } while (0) - -#endif - -/*-------------------------------------------------------------------------*/ - -/* another driver-specific mode might be a request type doing dma - * to/from another device fifo instead of to/from memory. - */ - -static void set_fifo_mode (struct net2280 *dev, int mode) -{ - /* keeping high bits preserves BAR2 */ - writel ((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); - - /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ - INIT_LIST_HEAD (&dev->gadget.ep_list); - list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list); - switch (mode) { - case 0: - list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [4].ep.ep_list, &dev->gadget.ep_list); - dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024; - break; - case 1: - dev->ep [1].fifo_size = dev->ep [2].fifo_size = 2048; - break; - case 2: - list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list); - dev->ep [1].fifo_size = 2048; - dev->ep [2].fifo_size = 1024; - break; - } - /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ - list_add_tail (&dev->ep [5].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list); -} - -/* keeping it simple: - * - one bus driver, initted first; - * - one function driver, initted second - * - * most of the work to support multiple net2280 controllers would - * be to associate this gadget driver (yes?) with all of them, or - * perhaps to bind specific drivers to specific devices. - */ - -static void usb_reset (struct net2280 *dev) -{ - u32 tmp; - - dev->gadget.speed = USB_SPEED_UNKNOWN; - (void) readl (&dev->usb->usbctl); - - net2280_led_init (dev); - - /* disable automatic responses, and irqs */ - writel (0, &dev->usb->stdrsp); - writel (0, &dev->regs->pciirqenb0); - writel (0, &dev->regs->pciirqenb1); - - /* clear old dma and irq state */ - for (tmp = 0; tmp < 4; tmp++) { - struct net2280_ep *ep = &dev->ep [tmp + 1]; - - if (ep->dma) - abort_dma (ep); - } - writel (~0, &dev->regs->irqstat0), - writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), - - /* reset, and enable pci */ - tmp = readl (&dev->regs->devinit) - | (1 << PCI_ENABLE) - | (1 << FIFO_SOFT_RESET) - | (1 << USB_SOFT_RESET) - | (1 << M8051_RESET); - writel (tmp, &dev->regs->devinit); - - /* standard fifo and endpoint allocations */ - set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0); -} - -static void usb_reinit (struct net2280 *dev) -{ - u32 tmp; - int init_dma; - - /* use_dma changes are ignored till next device re-init */ - init_dma = use_dma; - - /* basic endpoint init */ - for (tmp = 0; tmp < 7; tmp++) { - struct net2280_ep *ep = &dev->ep [tmp]; - - ep->ep.name = ep_name [tmp]; - ep->dev = dev; - ep->num = tmp; - - if (tmp > 0 && tmp <= 4) { - ep->fifo_size = 1024; - if (init_dma) - ep->dma = &dev->dma [tmp - 1]; - } else - ep->fifo_size = 64; - ep->regs = &dev->epregs [tmp]; - ep_reset (dev->regs, ep); - } - usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64); - usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64); - usb_ep_set_maxpacket_limit(&dev->ep [6].ep, 64); - - dev->gadget.ep0 = &dev->ep [0].ep; - dev->ep [0].stopped = 0; - INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); - - /* we want to prevent lowlevel/insecure access from the USB host, - * but erratum 0119 means this enable bit is ignored - */ - for (tmp = 0; tmp < 5; tmp++) - writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg); -} - -static void ep0_start (struct net2280 *dev) -{ - writel ( (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - , &dev->epregs [0].ep_rsp); - - /* - * hardware optionally handles a bunch of standard requests - * that the API hides from drivers anyway. have it do so. - * endpoint status/features are handled in software, to - * help pass tests for some dubious behavior. - */ - writel ( (1 << SET_TEST_MODE) - | (1 << SET_ADDRESS) - | (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) - | (1 << GET_DEVICE_STATUS) - | (1 << GET_INTERFACE_STATUS) - , &dev->usb->stdrsp); - writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE) - | (1 << SELF_POWERED_USB_DEVICE) - | (1 << REMOTE_WAKEUP_SUPPORT) - | (dev->softconnect << USB_DETECT_ENABLE) - | (1 << SELF_POWERED_STATUS) - , &dev->usb->usbctl); - - /* enable irqs so we can see ep0 and general operation */ - writel ( (1 << SETUP_PACKET_INTERRUPT_ENABLE) - | (1 << ENDPOINT_0_INTERRUPT_ENABLE) - , &dev->regs->pciirqenb0); - writel ( (1 << PCI_INTERRUPT_ENABLE) - | (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) - | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) - | (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE) - | (1 << VBUS_INTERRUPT_ENABLE) - | (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) - | (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) - , &dev->regs->pciirqenb1); - - /* don't leave any writes posted */ - (void) readl (&dev->usb->usbctl); -} - -/* when a driver is successfully registered, it will receive - * control requests including set_configuration(), which enables - * non-control requests. then usb traffic follows until a - * disconnect is reported. then a host may connect again, or - * the driver might get unbound. - */ -static int net2280_start(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver) -{ - struct net2280 *dev; - int retval; - unsigned i; - - /* insist on high speed support from the driver, since - * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) - * "must not be used in normal operation" - */ - if (!driver || driver->max_speed < USB_SPEED_HIGH - || !driver->setup) - return -EINVAL; - - dev = container_of (_gadget, struct net2280, gadget); - - for (i = 0; i < 7; i++) - dev->ep [i].irqs = 0; - - /* hook up the driver ... */ - dev->softconnect = 1; - driver->driver.bus = NULL; - dev->driver = driver; - - retval = device_create_file (&dev->pdev->dev, &dev_attr_function); - if (retval) goto err_unbind; - retval = device_create_file (&dev->pdev->dev, &dev_attr_queues); - if (retval) goto err_func; - - /* Enable force-full-speed testing mode, if desired */ - if (full_speed) - writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag); - - /* ... then enable host detection and ep0; and we're ready - * for set_configuration as well as eventual disconnect. - */ - net2280_led_active (dev, 1); - ep0_start (dev); - - DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n", - driver->driver.name, - readl (&dev->usb->usbctl), - readl (&dev->usb->stdrsp)); - - /* pci writes may still be posted */ - return 0; - -err_func: - device_remove_file (&dev->pdev->dev, &dev_attr_function); -err_unbind: - dev->driver = NULL; - return retval; -} - -static void -stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) -{ - int i; - - /* don't disconnect if it's not connected */ - if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; - - /* stop hardware; prevent new request submissions; - * and kill any outstanding requests. - */ - usb_reset (dev); - for (i = 0; i < 7; i++) - nuke (&dev->ep [i]); - - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } - - usb_reinit (dev); -} - -static int net2280_stop(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver) -{ - struct net2280 *dev; - unsigned long flags; - - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - stop_activity (dev, driver); - spin_unlock_irqrestore (&dev->lock, flags); - - dev->driver = NULL; - - net2280_led_active (dev, 0); - - /* Disable full-speed test mode */ - writel(0, &dev->usb->xcvrdiag); - - device_remove_file (&dev->pdev->dev, &dev_attr_function); - device_remove_file (&dev->pdev->dev, &dev_attr_queues); - - DEBUG(dev, "unregistered driver '%s'\n", - driver ? driver->driver.name : ""); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. - * also works for dma-capable endpoints, in pio mode or just - * to manually advance the queue after short OUT transfers. - */ -static void handle_ep_small (struct net2280_ep *ep) -{ - struct net2280_request *req; - u32 t; - /* 0 error, 1 mid-data, 2 done */ - int mode = 1; - - if (!list_empty (&ep->queue)) - req = list_entry (ep->queue.next, - struct net2280_request, queue); - else - req = NULL; - - /* ack all, and handle what we care about */ - t = readl (&ep->regs->ep_stat); - ep->irqs++; -#if 0 - VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n", - ep->ep.name, t, req ? &req->req : 0); -#endif - if (!ep->is_in || ep->dev->pdev->device == 0x2280) - writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat); - else - /* Added for 2282 */ - writel (t, &ep->regs->ep_stat); - - /* for ep0, monitor token irqs to catch data stage length errors - * and to synchronize on status. - * - * also, to defer reporting of protocol stalls ... here's where - * data or status first appears, handling stalls here should never - * cause trouble on the host side.. - * - * control requests could be slightly faster without token synch for - * status, but status can jam up that way. - */ - if (unlikely (ep->num == 0)) { - if (ep->is_in) { - /* status; stop NAKing */ - if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - } - if (!req) - allow_status (ep); - mode = 2; - /* reply to extra IN data tokens with a zlp */ - } else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - mode = 2; - } else if (ep->responded && - !req && !ep->stopped) - write_fifo (ep, NULL); - } - } else { - /* status; stop NAKing */ - if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - } - mode = 2; - /* an extra OUT token is an error */ - } else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) - && req - && req->req.actual == req->req.length) - || (ep->responded && !req)) { - ep->dev->protocol_stall = 1; - set_halt (ep); - ep->stopped = 1; - if (req) - done (ep, req, -EOVERFLOW); - req = NULL; - } - } - } - - if (unlikely (!req)) - return; - - /* manual DMA queue advance after short OUT */ - if (likely (ep->dma)) { - if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { - u32 count; - int stopped = ep->stopped; - - /* TRANSFERRED works around OUT_DONE erratum 0112. - * we expect (N <= maxpacket) bytes; host wrote M. - * iff (M < N) we won't ever see a DMA interrupt. - */ - ep->stopped = 1; - for (count = 0; ; t = readl (&ep->regs->ep_stat)) { - - /* any preceding dma transfers must finish. - * dma handles (M >= N), may empty the queue - */ - scan_dma_completions (ep); - if (unlikely (list_empty (&ep->queue) - || ep->out_overflow)) { - req = NULL; - break; - } - req = list_entry (ep->queue.next, - struct net2280_request, queue); - - /* here either (M < N), a "real" short rx; - * or (M == N) and the queue didn't empty - */ - if (likely (t & (1 << FIFO_EMPTY))) { - count = readl (&ep->dma->dmacount); - count &= DMA_BYTE_COUNT_MASK; - if (readl (&ep->dma->dmadesc) - != req->td_dma) - req = NULL; - break; - } - udelay(1); - } - - /* stop DMA, leave ep NAKing */ - writel ((1 << DMA_ABORT), &ep->dma->dmastat); - spin_stop_dma (ep->dma); - - if (likely (req)) { - req->td->dmacount = 0; - t = readl (&ep->regs->ep_avail); - dma_done (ep, req, count, - (ep->out_overflow || t) - ? -EOVERFLOW : 0); - } - - /* also flush to prevent erratum 0106 trouble */ - if (unlikely (ep->out_overflow - || (ep->dev->chiprev == 0x0100 - && ep->dev->gadget.speed - == USB_SPEED_FULL))) { - out_flush (ep); - ep->out_overflow = 0; - } - - /* (re)start dma if needed, stop NAKing */ - ep->stopped = stopped; - if (!list_empty (&ep->queue)) - restart_dma (ep); - } else - DEBUG (ep->dev, "%s dma ep_stat %08x ??\n", - ep->ep.name, t); - return; - - /* data packet(s) received (in the fifo, OUT) */ - } else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) { - if (read_fifo (ep, req) && ep->num != 0) - mode = 2; - - /* data packet(s) transmitted (IN) */ - } else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) { - unsigned len; - - len = req->req.length - req->req.actual; - if (len > ep->ep.maxpacket) - len = ep->ep.maxpacket; - req->req.actual += len; - - /* if we wrote it all, we're usually done */ - if (req->req.actual == req->req.length) { - if (ep->num == 0) { - /* send zlps until the status stage */ - } else if (!req->req.zero || len != ep->ep.maxpacket) - mode = 2; - } - - /* there was nothing to do ... */ - } else if (mode == 1) - return; - - /* done */ - if (mode == 2) { - /* stream endpoints often resubmit/unlink in completion */ - done (ep, req, 0); - - /* maybe advance queue to next request */ - if (ep->num == 0) { - /* NOTE: net2280 could let gadget driver start the - * status stage later. since not all controllers let - * them control that, the api doesn't (yet) allow it. - */ - if (!ep->stopped) - allow_status (ep); - req = NULL; - } else { - if (!list_empty (&ep->queue) && !ep->stopped) - req = list_entry (ep->queue.next, - struct net2280_request, queue); - else - req = NULL; - if (req && !ep->is_in) - stop_out_naking (ep); - } - } - - /* is there a buffer for the next packet? - * for best streaming performance, make sure there is one. - */ - if (req && !ep->stopped) { - - /* load IN fifo with next packet (may be zlp) */ - if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) - write_fifo (ep, &req->req); - } -} - -static struct net2280_ep * -get_ep_by_addr (struct net2280 *dev, u16 wIndex) -{ - struct net2280_ep *ep; - - if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) - return &dev->ep [0]; - list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) { - u8 bEndpointAddress; - - if (!ep->desc) - continue; - bEndpointAddress = ep->desc->bEndpointAddress; - if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) - continue; - if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) - return ep; - } - return NULL; -} - -static void handle_stat0_irqs (struct net2280 *dev, u32 stat) -{ - struct net2280_ep *ep; - u32 num, scratch; - - /* most of these don't need individual acks */ - stat &= ~(1 << INTA_ASSERTED); - if (!stat) - return; - // DEBUG (dev, "irqstat0 %04x\n", stat); - - /* starting a control request? */ - if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) { - union { - u32 raw [2]; - struct usb_ctrlrequest r; - } u; - int tmp; - struct net2280_request *req; - - if (dev->gadget.speed == USB_SPEED_UNKNOWN) { - if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED)) - dev->gadget.speed = USB_SPEED_HIGH; - else - dev->gadget.speed = USB_SPEED_FULL; - net2280_led_speed (dev, dev->gadget.speed); - DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed)); - } - - ep = &dev->ep [0]; - ep->irqs++; - - /* make sure any leftover request state is cleared */ - stat &= ~(1 << ENDPOINT_0_INTERRUPT); - while (!list_empty (&ep->queue)) { - req = list_entry (ep->queue.next, - struct net2280_request, queue); - done (ep, req, (req->req.actual == req->req.length) - ? 0 : -EPROTO); - } - ep->stopped = 0; - dev->protocol_stall = 0; - - if (ep->dev->pdev->device == 0x2280) - tmp = (1 << FIFO_OVERFLOW) - | (1 << FIFO_UNDERFLOW); - else - tmp = 0; - - writel (tmp | (1 << TIMEOUT) - | (1 << USB_STALL_SENT) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_OUT_PING_NAK_SENT) - | (1 << USB_OUT_ACK_SENT) - | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT) - , &ep->regs->ep_stat); - u.raw [0] = readl (&dev->usb->setup0123); - u.raw [1] = readl (&dev->usb->setup4567); - - cpu_to_le32s (&u.raw [0]); - cpu_to_le32s (&u.raw [1]); - - tmp = 0; - -#define w_value le16_to_cpu(u.r.wValue) -#define w_index le16_to_cpu(u.r.wIndex) -#define w_length le16_to_cpu(u.r.wLength) - - /* ack the irq */ - writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0); - stat ^= (1 << SETUP_PACKET_INTERRUPT); - - /* watch control traffic at the token level, and force - * synchronization before letting the status stage happen. - * FIXME ignore tokens we'll NAK, until driver responds. - * that'll mean a lot less irqs for some drivers. - */ - ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; - if (ep->is_in) { - scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT); - stop_out_naking (ep); - } else - scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT); - writel (scratch, &dev->epregs [0].ep_irqenb); - - /* we made the hardware handle most lowlevel requests; - * everything else goes uplevel to the gadget code. - */ - ep->responded = 1; - switch (u.r.bRequest) { - case USB_REQ_GET_STATUS: { - struct net2280_ep *e; - __le32 status; - - /* hw handles device and interface status */ - if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) - goto delegate; - if ((e = get_ep_by_addr (dev, w_index)) == NULL - || w_length > 2) - goto do_stall; - - if (readl (&e->regs->ep_rsp) - & (1 << SET_ENDPOINT_HALT)) - status = cpu_to_le32 (1); - else - status = cpu_to_le32 (0); - - /* don't bother with a request object! */ - writel (0, &dev->epregs [0].ep_irqenb); - set_fifo_bytecount (ep, w_length); - writel ((__force u32)status, &dev->epregs [0].ep_data); - allow_status (ep); - VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status); - goto next_endpoints; - } - break; - case USB_REQ_CLEAR_FEATURE: { - struct net2280_ep *e; - - /* hw handles device features */ - if (u.r.bRequestType != USB_RECIP_ENDPOINT) - goto delegate; - if (w_value != USB_ENDPOINT_HALT - || w_length != 0) - goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == NULL) - goto do_stall; - if (e->wedged) { - VDEBUG(dev, "%s wedged, halt not cleared\n", - ep->ep.name); - } else { - VDEBUG(dev, "%s clear halt\n", ep->ep.name); - clear_halt(e); - } - allow_status (ep); - goto next_endpoints; - } - break; - case USB_REQ_SET_FEATURE: { - struct net2280_ep *e; - - /* hw handles device features */ - if (u.r.bRequestType != USB_RECIP_ENDPOINT) - goto delegate; - if (w_value != USB_ENDPOINT_HALT - || w_length != 0) - goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == NULL) - goto do_stall; - if (e->ep.name == ep0name) - goto do_stall; - set_halt (e); - allow_status (ep); - VDEBUG (dev, "%s set halt\n", ep->ep.name); - goto next_endpoints; - } - break; - default: -delegate: - VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x " - "ep_cfg %08x\n", - u.r.bRequestType, u.r.bRequest, - w_value, w_index, w_length, - readl (&ep->regs->ep_cfg)); - ep->responded = 0; - spin_unlock (&dev->lock); - tmp = dev->driver->setup (&dev->gadget, &u.r); - spin_lock (&dev->lock); - } - - /* stall ep0 on error */ - if (tmp < 0) { -do_stall: - VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n", - u.r.bRequestType, u.r.bRequest, tmp); - dev->protocol_stall = 1; - } - - /* some in/out token irq should follow; maybe stall then. - * driver must queue a request (even zlp) or halt ep0 - * before the host times out. - */ - } - -#undef w_value -#undef w_index -#undef w_length - -next_endpoints: - /* endpoint data irq ? */ - scratch = stat & 0x7f; - stat &= ~0x7f; - for (num = 0; scratch; num++) { - u32 t; - - /* do this endpoint's FIFO and queue need tending? */ - t = 1 << num; - if ((scratch & t) == 0) - continue; - scratch ^= t; - - ep = &dev->ep [num]; - handle_ep_small (ep); - } - - if (stat) - DEBUG (dev, "unhandled irqstat0 %08x\n", stat); -} - -#define DMA_INTERRUPTS ( \ - (1 << DMA_D_INTERRUPT) \ - | (1 << DMA_C_INTERRUPT) \ - | (1 << DMA_B_INTERRUPT) \ - | (1 << DMA_A_INTERRUPT)) -#define PCI_ERROR_INTERRUPTS ( \ - (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \ - | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \ - | (1 << PCI_RETRY_ABORT_INTERRUPT)) - -static void handle_stat1_irqs (struct net2280 *dev, u32 stat) -{ - struct net2280_ep *ep; - u32 tmp, num, mask, scratch; - - /* after disconnect there's nothing else to do! */ - tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); - mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED); - - /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. - * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and - * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT - * only indicates a change in the reset state). - */ - if (stat & tmp) { - writel (tmp, &dev->regs->irqstat1); - if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) - && ((readl (&dev->usb->usbstat) & mask) - == 0)) - || ((readl (&dev->usb->usbctl) - & (1 << VBUS_PIN)) == 0) - ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) { - DEBUG (dev, "disconnect %s\n", - dev->driver->driver.name); - stop_activity (dev, dev->driver); - ep0_start (dev); - return; - } - stat &= ~tmp; - - /* vBUS can bounce ... one of many reasons to ignore the - * notion of hotplug events on bus connect/disconnect! - */ - if (!stat) - return; - } - - /* NOTE: chip stays in PCI D0 state for now, but it could - * enter D1 to save more power - */ - tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT); - if (stat & tmp) { - writel (tmp, &dev->regs->irqstat1); - if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { - if (dev->driver->suspend) - dev->driver->suspend (&dev->gadget); - if (!enable_suspend) - stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); - } else { - if (dev->driver->resume) - dev->driver->resume (&dev->gadget); - /* at high speed, note erratum 0133 */ - } - stat &= ~tmp; - } - - /* clear any other status/irqs */ - if (stat) - writel (stat, &dev->regs->irqstat1); - - /* some status we can just ignore */ - if (dev->pdev->device == 0x2280) - stat &= ~((1 << CONTROL_STATUS_INTERRUPT) - | (1 << SUSPEND_REQUEST_INTERRUPT) - | (1 << RESUME_INTERRUPT) - | (1 << SOF_INTERRUPT)); - else - stat &= ~((1 << CONTROL_STATUS_INTERRUPT) - | (1 << RESUME_INTERRUPT) - | (1 << SOF_DOWN_INTERRUPT) - | (1 << SOF_INTERRUPT)); - - if (!stat) - return; - // DEBUG (dev, "irqstat1 %08x\n", stat); - - /* DMA status, for ep-{a,b,c,d} */ - scratch = stat & DMA_INTERRUPTS; - stat &= ~DMA_INTERRUPTS; - scratch >>= 9; - for (num = 0; scratch; num++) { - struct net2280_dma_regs __iomem *dma; - - tmp = 1 << num; - if ((tmp & scratch) == 0) - continue; - scratch ^= tmp; - - ep = &dev->ep [num + 1]; - dma = ep->dma; - - if (!dma) - continue; - - /* clear ep's dma status */ - tmp = readl (&dma->dmastat); - writel (tmp, &dma->dmastat); - - /* chaining should stop on abort, short OUT from fifo, - * or (stat0 codepath) short OUT transfer. - */ - if (!use_dma_chaining) { - if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) - == 0) { - DEBUG (ep->dev, "%s no xact done? %08x\n", - ep->ep.name, tmp); - continue; - } - stop_dma (ep->dma); - } - - /* OUT transfers terminate when the data from the - * host is in our memory. Process whatever's done. - * On this path, we know transfer's last packet wasn't - * less than req->length. NAK_OUT_PACKETS may be set, - * or the FIFO may already be holding new packets. - * - * IN transfers can linger in the FIFO for a very - * long time ... we ignore that for now, accounting - * precisely (like PIO does) needs per-packet irqs - */ - scan_dma_completions (ep); - - /* disable dma on inactive queues; else maybe restart */ - if (list_empty (&ep->queue)) { - if (use_dma_chaining) - stop_dma (ep->dma); - } else { - tmp = readl (&dma->dmactl); - if (!use_dma_chaining - || (tmp & (1 << DMA_ENABLE)) == 0) - restart_dma (ep); - else if (ep->is_in && use_dma_chaining) { - struct net2280_request *req; - __le32 dmacount; - - /* the descriptor at the head of the chain - * may still have VALID_BIT clear; that's - * used to trigger changing DMA_FIFO_VALIDATE - * (affects automagic zlp writes). - */ - req = list_entry (ep->queue.next, - struct net2280_request, queue); - dmacount = req->td->dmacount; - dmacount &= cpu_to_le32 ( - (1 << VALID_BIT) - | DMA_BYTE_COUNT_MASK); - if (dmacount && (dmacount & valid_bit) == 0) - restart_dma (ep); - } - } - ep->irqs++; - } - - /* NOTE: there are other PCI errors we might usefully notice. - * if they appear very often, here's where to try recovering. - */ - if (stat & PCI_ERROR_INTERRUPTS) { - ERROR (dev, "pci dma error; stat %08x\n", stat); - stat &= ~PCI_ERROR_INTERRUPTS; - /* these are fatal errors, but "maybe" they won't - * happen again ... - */ - stop_activity (dev, dev->driver); - ep0_start (dev); - stat = 0; - } - - if (stat) - DEBUG (dev, "unhandled irqstat1 %08x\n", stat); -} - -static irqreturn_t net2280_irq (int irq, void *_dev) -{ - struct net2280 *dev = _dev; - - /* shared interrupt, not ours */ - if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED))) - return IRQ_NONE; - - spin_lock (&dev->lock); - - /* handle disconnect, dma, and more */ - handle_stat1_irqs (dev, readl (&dev->regs->irqstat1)); - - /* control requests and PIO */ - handle_stat0_irqs (dev, readl (&dev->regs->irqstat0)); - - spin_unlock (&dev->lock); - - return IRQ_HANDLED; -} - -/*-------------------------------------------------------------------------*/ - -static void gadget_release (struct device *_dev) -{ - struct net2280 *dev = dev_get_drvdata (_dev); - - kfree (dev); -} - -/* tear down the binding between this driver and the pci device */ - -static void net2280_remove (struct pci_dev *pdev) -{ - struct net2280 *dev = pci_get_drvdata (pdev); - - usb_del_gadget_udc(&dev->gadget); - - BUG_ON(dev->driver); - - /* then clean up the resources we allocated during probe() */ - net2280_led_shutdown (dev); - if (dev->requests) { - int i; - for (i = 1; i < 5; i++) { - if (!dev->ep [i].dummy) - continue; - pci_pool_free (dev->requests, dev->ep [i].dummy, - dev->ep [i].td_dma); - } - pci_pool_destroy (dev->requests); - } - if (dev->got_irq) - free_irq (pdev->irq, dev); - if (dev->regs) - iounmap (dev->regs); - if (dev->region) - release_mem_region (pci_resource_start (pdev, 0), - pci_resource_len (pdev, 0)); - if (dev->enabled) - pci_disable_device (pdev); - device_remove_file (&pdev->dev, &dev_attr_registers); - - INFO (dev, "unbind\n"); -} - -/* wrap this driver around the specified device, but - * don't respond over USB until a gadget driver binds to us. - */ - -static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net2280 *dev; - unsigned long resource, len; - void __iomem *base = NULL; - int retval, i; - - /* alloc, and start init */ - dev = kzalloc (sizeof *dev, GFP_KERNEL); - if (dev == NULL){ - retval = -ENOMEM; - goto done; - } - - pci_set_drvdata (pdev, dev); - spin_lock_init (&dev->lock); - dev->pdev = pdev; - dev->gadget.ops = &net2280_ops; - dev->gadget.max_speed = USB_SPEED_HIGH; - - /* the "gadget" abstracts/virtualizes the controller */ - dev->gadget.name = driver_name; - - /* now all the pci goodies ... */ - if (pci_enable_device (pdev) < 0) { - retval = -ENODEV; - goto done; - } - dev->enabled = 1; - - /* BAR 0 holds all the registers - * BAR 1 is 8051 memory; unused here (note erratum 0103) - * BAR 2 is fifo memory; unused here - */ - resource = pci_resource_start (pdev, 0); - len = pci_resource_len (pdev, 0); - if (!request_mem_region (resource, len, driver_name)) { - DEBUG (dev, "controller already in use\n"); - retval = -EBUSY; - goto done; - } - dev->region = 1; - - /* FIXME provide firmware download interface to put - * 8051 code into the chip, e.g. to turn on PCI PM. - */ - - base = ioremap_nocache (resource, len); - if (base == NULL) { - DEBUG (dev, "can't map memory\n"); - retval = -EFAULT; - goto done; - } - dev->regs = (struct net2280_regs __iomem *) base; - dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080); - dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100); - dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180); - dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); - dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); - - /* put into initial config, link up all endpoints */ - writel (0, &dev->usb->usbctl); - usb_reset (dev); - usb_reinit (dev); - - /* irq setup after old hardware is cleaned up */ - if (!pdev->irq) { - ERROR (dev, "No IRQ. Check PCI setup!\n"); - retval = -ENODEV; - goto done; - } - - if (request_irq (pdev->irq, net2280_irq, IRQF_SHARED, driver_name, dev) - != 0) { - ERROR (dev, "request interrupt %d failed\n", pdev->irq); - retval = -EBUSY; - goto done; - } - dev->got_irq = 1; - - /* DMA setup */ - /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ - dev->requests = pci_pool_create ("requests", pdev, - sizeof (struct net2280_dma), - 0 /* no alignment requirements */, - 0 /* or page-crossing issues */); - if (!dev->requests) { - DEBUG (dev, "can't get request pool\n"); - retval = -ENOMEM; - goto done; - } - for (i = 1; i < 5; i++) { - struct net2280_dma *td; - - td = pci_pool_alloc (dev->requests, GFP_KERNEL, - &dev->ep [i].td_dma); - if (!td) { - DEBUG (dev, "can't get dummy %d\n", i); - retval = -ENOMEM; - goto done; - } - td->dmacount = 0; /* not VALID */ - td->dmadesc = td->dmaaddr; - dev->ep [i].dummy = td; - } - - /* enable lower-overhead pci memory bursts during DMA */ - writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) - // 256 write retries may not be enough... - // | (1 << PCI_RETRY_ABORT_ENABLE) - | (1 << DMA_READ_MULTIPLE_ENABLE) - | (1 << DMA_READ_LINE_ENABLE) - , &dev->pci->pcimstctl); - /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ - pci_set_master (pdev); - pci_try_set_mwi (pdev); - - /* ... also flushes any posted pci writes */ - dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff; - - /* done */ - INFO (dev, "%s\n", driver_desc); - INFO (dev, "irq %d, pci mem %p, chip rev %04x\n", - pdev->irq, base, dev->chiprev); - INFO (dev, "version: " DRIVER_VERSION "; dma %s\n", - use_dma - ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled"); - retval = device_create_file (&pdev->dev, &dev_attr_registers); - if (retval) goto done; - - retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, - gadget_release); - if (retval) - goto done; - return 0; - -done: - if (dev) - net2280_remove (pdev); - return retval; -} - -/* make sure the board is quiescent; otherwise it will continue - * generating IRQs across the upcoming reboot. - */ - -static void net2280_shutdown (struct pci_dev *pdev) -{ - struct net2280 *dev = pci_get_drvdata (pdev); - - /* disable IRQs */ - writel (0, &dev->regs->pciirqenb0); - writel (0, &dev->regs->pciirqenb1); - - /* disable the pullup so the host will think we're gone */ - writel (0, &dev->usb->usbctl); - - /* Disable full-speed test mode */ - writel(0, &dev->usb->xcvrdiag); -} - - -/*-------------------------------------------------------------------------*/ - -static const struct pci_device_id pci_ids [] = { { - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x17cc, - .device = 0x2280, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, -}, { - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x17cc, - .device = 0x2282, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - -}, { /* end: all zeroes */ } -}; -MODULE_DEVICE_TABLE (pci, pci_ids); - -/* pci driver glue; this is a "new style" PCI driver module */ -static struct pci_driver net2280_pci_driver = { - .name = (char *) driver_name, - .id_table = pci_ids, - - .probe = net2280_probe, - .remove = net2280_remove, - .shutdown = net2280_shutdown, - - /* FIXME add power management support */ -}; - -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell"); -MODULE_LICENSE ("GPL"); - -static int __init init (void) -{ - if (!use_dma) - use_dma_chaining = 0; - return pci_register_driver (&net2280_pci_driver); -} -module_init (init); - -static void __exit cleanup (void) -{ - pci_unregister_driver (&net2280_pci_driver); -} -module_exit (cleanup); diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h deleted file mode 100644 index a844be0d683a..000000000000 --- a/drivers/usb/gadget/net2280.h +++ /dev/null @@ -1,308 +0,0 @@ -/* - * NetChip 2280 high/full speed USB device controller. - * Unlike many such controllers, this one talks PCI. - */ - -/* - * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) - * Copyright (C) 2003 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/usb/net2280.h> - -/*-------------------------------------------------------------------------*/ - -#ifdef __KERNEL__ - -/* indexed registers [11.10] are accessed indirectly - * caller must own the device lock. - */ - -static inline u32 -get_idx_reg (struct net2280_regs __iomem *regs, u32 index) -{ - writel (index, ®s->idxaddr); - /* NOTE: synchs device/cpu memory views */ - return readl (®s->idxdata); -} - -static inline void -set_idx_reg (struct net2280_regs __iomem *regs, u32 index, u32 value) -{ - writel (index, ®s->idxaddr); - writel (value, ®s->idxdata); - /* posted, may not be visible yet */ -} - -#endif /* __KERNEL__ */ - - -#define REG_DIAG 0x0 -#define RETRY_COUNTER 16 -#define FORCE_PCI_SERR 11 -#define FORCE_PCI_INTERRUPT 10 -#define FORCE_USB_INTERRUPT 9 -#define FORCE_CPU_INTERRUPT 8 -#define ILLEGAL_BYTE_ENABLES 5 -#define FAST_TIMES 4 -#define FORCE_RECEIVE_ERROR 2 -#define FORCE_TRANSMIT_CRC_ERROR 0 -#define REG_FRAME 0x02 /* from last sof */ -#define REG_CHIPREV 0x03 /* in bcd */ -#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */ - -#define CHIPREV_1 0x0100 -#define CHIPREV_1A 0x0110 - -#ifdef __KERNEL__ - -/* ep a-f highspeed and fullspeed maxpacket, addresses - * computed from ep->num - */ -#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \ - (((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1)) - -/*-------------------------------------------------------------------------*/ - -/* [8.3] for scatter/gather i/o - * use struct net2280_dma_regs bitfields - */ -struct net2280_dma { - __le32 dmacount; - __le32 dmaaddr; /* the buffer */ - __le32 dmadesc; /* next dma descriptor */ - __le32 _reserved; -} __attribute__ ((aligned (16))); - -/*-------------------------------------------------------------------------*/ - -/* DRIVER DATA STRUCTURES and UTILITIES */ - -struct net2280_ep { - struct usb_ep ep; - struct net2280_ep_regs __iomem *regs; - struct net2280_dma_regs __iomem *dma; - struct net2280_dma *dummy; - dma_addr_t td_dma; /* of dummy */ - struct net2280 *dev; - unsigned long irqs; - - /* analogous to a host-side qh */ - struct list_head queue; - const struct usb_endpoint_descriptor *desc; - unsigned num : 8, - fifo_size : 12, - in_fifo_validate : 1, - out_overflow : 1, - stopped : 1, - wedged : 1, - is_in : 1, - is_iso : 1, - responded : 1; -}; - -static inline void allow_status (struct net2280_ep *ep) -{ - /* ep0 only */ - writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_NAK_OUT_PACKETS_MODE) - , &ep->regs->ep_rsp); - ep->stopped = 1; -} - -/* count (<= 4) bytes in the next fifo write will be valid */ -static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count) -{ - writeb (count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); -} - -struct net2280_request { - struct usb_request req; - struct net2280_dma *td; - dma_addr_t td_dma; - struct list_head queue; - unsigned mapped : 1, - valid : 1; -}; - -struct net2280 { - /* each pci device provides one gadget, several endpoints */ - struct usb_gadget gadget; - spinlock_t lock; - struct net2280_ep ep [7]; - struct usb_gadget_driver *driver; - unsigned enabled : 1, - protocol_stall : 1, - softconnect : 1, - got_irq : 1, - region : 1; - u16 chiprev; - - /* pci state used to access those endpoints */ - struct pci_dev *pdev; - struct net2280_regs __iomem *regs; - struct net2280_usb_regs __iomem *usb; - struct net2280_pci_regs __iomem *pci; - struct net2280_dma_regs __iomem *dma; - struct net2280_dep_regs __iomem *dep; - struct net2280_ep_regs __iomem *epregs; - - struct pci_pool *requests; - // statistics... -}; - -static inline void set_halt (struct net2280_ep *ep) -{ - /* ep0 and bulk/intr endpoints */ - writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - /* set NAK_OUT for erratum 0114 */ - | ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) - | (1 << SET_ENDPOINT_HALT) - , &ep->regs->ep_rsp); -} - -static inline void clear_halt (struct net2280_ep *ep) -{ - /* ep0 and bulk/intr endpoints */ - writel ( (1 << CLEAR_ENDPOINT_HALT) - | (1 << CLEAR_ENDPOINT_TOGGLE) - /* unless the gadget driver left a short packet in the - * fifo, this reverses the erratum 0114 workaround. - */ - | ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS) - , &ep->regs->ep_rsp); -} - -#ifdef USE_RDK_LEDS - -static inline void net2280_led_init (struct net2280 *dev) -{ - /* LED3 (green) is on during USB activity. note erratum 0113. */ - writel ((1 << GPIO3_LED_SELECT) - | (1 << GPIO3_OUTPUT_ENABLE) - | (1 << GPIO2_OUTPUT_ENABLE) - | (1 << GPIO1_OUTPUT_ENABLE) - | (1 << GPIO0_OUTPUT_ENABLE) - , &dev->regs->gpioctl); -} - -/* indicate speed with bi-color LED 0/1 */ -static inline -void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed) -{ - u32 val = readl (&dev->regs->gpioctl); - switch (speed) { - case USB_SPEED_HIGH: /* green */ - val &= ~(1 << GPIO0_DATA); - val |= (1 << GPIO1_DATA); - break; - case USB_SPEED_FULL: /* red */ - val &= ~(1 << GPIO1_DATA); - val |= (1 << GPIO0_DATA); - break; - default: /* (off/black) */ - val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA)); - break; - } - writel (val, &dev->regs->gpioctl); -} - -/* indicate power with LED 2 */ -static inline void net2280_led_active (struct net2280 *dev, int is_active) -{ - u32 val = readl (&dev->regs->gpioctl); - - // FIXME this LED never seems to turn on. - if (is_active) - val |= GPIO2_DATA; - else - val &= ~GPIO2_DATA; - writel (val, &dev->regs->gpioctl); -} -static inline void net2280_led_shutdown (struct net2280 *dev) -{ - /* turn off all four GPIO*_DATA bits */ - writel (readl (&dev->regs->gpioctl) & ~0x0f, - &dev->regs->gpioctl); -} - -#else - -#define net2280_led_init(dev) do { } while (0) -#define net2280_led_speed(dev, speed) do { } while (0) -#define net2280_led_shutdown(dev) do { } while (0) - -#endif - -/*-------------------------------------------------------------------------*/ - -#define xprintk(dev,level,fmt,args...) \ - printk(level "%s %s: " fmt , driver_name , \ - pci_name(dev->pdev) , ## args) - -#ifdef DEBUG -#undef DEBUG -#define DEBUG(dev,fmt,args...) \ - xprintk(dev , KERN_DEBUG , fmt , ## args) -#else -#define DEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* DEBUG */ - -#ifdef VERBOSE -#define VDEBUG DEBUG -#else -#define VDEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* VERBOSE */ - -#define ERROR(dev,fmt,args...) \ - xprintk(dev , KERN_ERR , fmt , ## args) -#define WARNING(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) -#define INFO(dev,fmt,args...) \ - xprintk(dev , KERN_INFO , fmt , ## args) - -/*-------------------------------------------------------------------------*/ - -static inline void start_out_naking (struct net2280_ep *ep) -{ - /* NOTE: hardware races lurk here, and PING protocol issues */ - writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - /* synch with device */ - readl (&ep->regs->ep_rsp); -} - -#ifdef DEBUG -static inline void assert_out_naking (struct net2280_ep *ep, const char *where) -{ - u32 tmp = readl (&ep->regs->ep_stat); - - if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - DEBUG (ep->dev, "%s %s %08x !NAK\n", - ep->ep.name, where, tmp); - writel ((1 << SET_NAK_OUT_PACKETS), - &ep->regs->ep_rsp); - } -} -#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__func__) -#else -#define ASSERT_OUT_NAKING(ep) do {} while (0) -#endif - -static inline void stop_out_naking (struct net2280_ep *ep) -{ - u32 tmp; - - tmp = readl (&ep->regs->ep_stat); - if ((tmp & (1 << NAK_OUT_PACKETS)) != 0) - writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); -} - -#endif /* __KERNEL__ */ diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h index ea5cf8c2da28..947b7ddff691 100644 --- a/drivers/usb/gadget/u_os_desc.h +++ b/drivers/usb/gadget/u_os_desc.h @@ -35,27 +35,63 @@ #define USB_EXT_PROP_UNICODE_LINK 6 #define USB_EXT_PROP_UNICODE_MULTI 7 +static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset) +{ + return buf + offset; +} + +static inline u8 *usb_ext_prop_size_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE); +} + +static inline u8 *usb_ext_prop_type_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE); +} + +static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH); +} + +static inline u8 *usb_ext_prop_name_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME); +} + +static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, + USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off); +} + +static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off); +} + static inline void usb_ext_prop_put_size(u8 *buf, int dw_size) { - put_unaligned_le32(dw_size, &buf[USB_EXT_PROP_DW_SIZE]); + put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf)); } static inline void usb_ext_prop_put_type(u8 *buf, int type) { - put_unaligned_le32(type, &buf[USB_EXT_PROP_DW_PROPERTY_DATA_TYPE]); + put_unaligned_le32(type, usb_ext_prop_type_ptr(buf)); } static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) { int result; - put_unaligned_le16(pnl, &buf[USB_EXT_PROP_W_PROPERTY_NAME_LENGTH]); + put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf)); result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN, - (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_NAME], pnl - 2); + (wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2); if (result < 0) return result; - put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl]); + put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]); return pnl; } @@ -63,26 +99,23 @@ static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data, int data_len) { - put_unaligned_le32(data_len, - &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); - memcpy(&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], data, data_len); + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len); } static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string, int data_len) { int result; - put_unaligned_le32(data_len, - &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); - + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN, - (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], + (wchar_t *) usb_ext_prop_data_ptr(buf, pnl), data_len - 2); if (result < 0) return result; put_unaligned_le16(0, - &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len]); + &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]); return data_len; } diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig new file mode 100644 index 000000000000..3ea287b0e448 --- /dev/null +++ b/drivers/usb/gadget/udc/Kconfig @@ -0,0 +1,401 @@ +# +# USB Gadget support on a system involves +# (a) a peripheral controller, and +# (b) the gadget driver using it. +# +# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !! +# +# - Host systems (like PCs) need CONFIG_USB (with "A" jacks). +# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks). +# - Some systems have both kinds of controllers. +# +# With help from a special transceiver and a "Mini-AB" jack, systems with +# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). +# + +# +# USB Peripheral Controller Support +# +# The order here is alphabetical, except that integrated controllers go +# before discrete ones so they will be the initial/default value: +# - integrated/SOC controllers first +# - licensed IP used in both SOC and discrete versions +# - discrete ones (including all PCI-only controllers) +# - debug/dummy gadget+hcd is last. +# +menu "USB Peripheral Controller" + +# +# Integrated controllers +# + +config USB_AT91 + tristate "Atmel AT91 USB Device Port" + depends on ARCH_AT91 + help + Many Atmel AT91 processors (such as the AT91RM2000) have a + full speed USB Device Port with support for five configurable + endpoints (plus endpoint zero). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "at91_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_LPC32XX + tristate "LPC32XX USB Peripheral Controller" + depends on ARCH_LPC32XX && I2C + select USB_ISP1301 + help + This option selects the USB device controller in the LPC32xx SoC. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "lpc32xx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_ATMEL_USBA + tristate "Atmel USBA" + depends on AVR32 || ARCH_AT91 + help + USBA is the integrated high-speed USB Device controller on + the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. + +config USB_BCM63XX_UDC + tristate "Broadcom BCM63xx Peripheral Controller" + depends on BCM63XX + help + Many Broadcom BCM63xx chipsets (such as the BCM6328) have a + high speed USB Device Port with support for four fixed endpoints + (plus endpoint zero). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "bcm63xx_udc". + +config USB_FSL_USB2 + tristate "Freescale Highspeed USB DR Peripheral Controller" + depends on FSL_SOC || ARCH_MXC + select USB_FSL_MPH_DR_OF if OF + help + Some of Freescale PowerPC and i.MX processors have a High Speed + Dual-Role(DR) USB controller, which supports device mode. + + The number of programmable endpoints is different through + SOC revisions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fsl_usb2_udc" and force + all gadget drivers to also be dynamically linked. + +config USB_FUSB300 + tristate "Faraday FUSB300 USB Peripheral Controller" + depends on !PHYS_ADDR_T_64BIT && HAS_DMA + help + Faraday usb device controller FUSB300 driver + +config USB_FOTG210_UDC + depends on HAS_DMA + tristate "Faraday FOTG210 USB Peripheral Controller" + help + Faraday USB2.0 OTG controller which can be configured as + high speed or full speed USB device. This driver supppors + Bulk Transfer so far. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fotg210_udc". + +config USB_GR_UDC + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA + help + Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB + VHDL IP core library. + +config USB_OMAP + tristate "OMAP USB Device Controller" + depends on ARCH_OMAP1 + depends on ISP1301_OMAP || !(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 + endpoints (plus endpoint zero). This driver supports the + controller in the OMAP 1611, and should work with controllers + in other OMAP processors too, given minor tweaks. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "omap_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_PXA25X + tristate "PXA 25x or IXP 4xx" + depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX + help + Intel's PXA 25x series XScale ARM-5TE processors include + an integrated full speed USB 1.1 device controller. The + controller in the IXP 4xx series is register-compatible. + + It has fifteen fixed-function endpoints, as well as endpoint + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "pxa25x_udc" and force all + gadget drivers to also be dynamically linked. + +# if there's only one gadget driver, using only two bulk endpoints, +# don't waste memory for the other endpoints +config USB_PXA25X_SMALL + depends on USB_PXA25X + bool + default n if USB_ETH_RNDIS + default y if USB_ZERO + default y if USB_ETH + default y if USB_G_SERIAL + +config USB_R8A66597 + tristate "Renesas R8A66597 USB Peripheral Controller" + depends on HAS_DMA + help + R8A66597 is a discrete USB host and peripheral controller chip that + supports both full and high speed USB 2.0 data transfers. + It has nine configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "r8a66597_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_RENESAS_USBHS_UDC + tristate 'Renesas USBHS controller' + depends on USB_RENESAS_USBHS && HAS_DMA + help + Renesas USBHS is a discrete USB host and peripheral controller chip + that supports both full and high speed USB 2.0 data transfers. + It has nine or more configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "renesas_usbhs" and force all + gadget drivers to also be dynamically linked. + +config USB_PXA27X + tristate "PXA 27x" + help + Intel's PXA 27x series XScale ARM v5TE processors include + an integrated full speed USB 1.1 device controller. + + It has up to 23 endpoints, as well as endpoint zero (for + control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "pxa27x_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_S3C2410 + tristate "S3C2410 USB Device Controller" + depends on ARCH_S3C24XX + help + Samsung's S3C2410 is an ARM-4 processor with an integrated + full speed USB 1.1 device controller. It has 4 configurable + endpoints, as well as endpoint zero (for control transfers). + + This driver has been tested on the S3C2410, S3C2412, and + S3C2440 processors. + +config USB_S3C2410_DEBUG + boolean "S3C2410 udc debug messages" + depends on USB_S3C2410 + +config USB_S3C_HSUDC + tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" + depends on ARCH_S3C24XX + help + Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC + integrated with dual speed USB 2.0 device controller. It has + 8 endpoints, as well as endpoint zero. + + This driver has been tested on S3C2416 and S3C2450 processors. + +config USB_MV_UDC + tristate "Marvell USB2.0 Device Controller" + depends on HAS_DMA + help + Marvell Socs (including PXA and MMP series) include a high speed + USB2.0 OTG controller, which can be configured as high speed or + full speed USB peripheral. + +config USB_MV_U3D + depends on HAS_DMA + tristate "MARVELL PXA2128 USB 3.0 controller" + help + MARVELL PXA2128 Processor series include a super speed USB3.0 device + controller, which support super speed USB peripheral. + +# +# Controllers available in both integrated and discrete versions +# + +config USB_M66592 + tristate "Renesas M66592 USB Peripheral Controller" + help + M66592 is a discrete USB peripheral controller chip that + supports both full and high speed USB 2.0 data transfers. + It has seven configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "m66592_udc" and force all + gadget drivers to also be dynamically linked. + +# +# Controllers available only in discrete form (and all PCI controllers) +# + +config USB_AMD5536UDC + tristate "AMD5536 UDC" + depends on PCI + help + The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. + It is a USB Highspeed DMA capable USB device controller. Beside ep0 + it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). + The UDC port supports OTG operation, and may be used as a host port + if it's not being used to implement peripheral or OTG roles. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "amd5536udc" and force all + gadget drivers to also be dynamically linked. + +config USB_FSL_QE + tristate "Freescale QE/CPM USB Device Controller" + depends on FSL_SOC && (QUICC_ENGINE || CPM) + help + Some of Freescale PowerPC processors have a Full Speed + QE/CPM2 USB controller, which support device mode with 4 + programmable endpoints. This driver supports the + controller in the MPC8360 and MPC8272, and should work with + controllers having QE or CPM2, given minor tweaks. + + Set CONFIG_USB_GADGET to "m" to build this driver as a + dynamically linked module called "fsl_qe_udc". + +config USB_NET2272 + tristate "PLX NET2272" + help + PLX NET2272 is a USB peripheral controller which supports + both full and high speed USB 2.0 data transfers. + + It has three configurable endpoints, as well as endpoint zero + (for control transfer). + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "net2272" and force all + gadget drivers to also be dynamically linked. + +config USB_NET2272_DMA + boolean "Support external DMA controller" + depends on USB_NET2272 && HAS_DMA + help + The NET2272 part can optionally support an external DMA + controller, but your board has to have support in the + driver itself. + + If unsure, say "N" here. The driver works fine in PIO mode. + +config USB_NET2280 + tristate "NetChip 228x / PLX USB338x" + depends on PCI + help + NetChip 2280 / 2282 is a PCI based USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + + It has six configurable endpoints, as well as endpoint zero + (for control transfers) and several endpoints with dedicated + functions. + + PLX 3380 / 3382 is a PCIe based USB peripheral controller which + supports full, high speed USB 2.0 and super speed USB 3.0 + data transfers. + + It has eight configurable endpoints, as well as endpoint zero + (for control transfers) and several endpoints with dedicated + functions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "net2280" and force all + gadget drivers to also be dynamically linked. + +config USB_GOKU + tristate "Toshiba TC86C001 'Goku-S'" + depends on PCI + help + The Toshiba TC86C001 is a PCI device which includes controllers + for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). + + The device controller has three configurable (bulk or interrupt) + endpoints, plus endpoint zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "goku_udc" and to force all + gadget drivers to also be dynamically linked. + +config USB_EG20T + tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" + depends on PCI + help + This is a USB device driver for EG20T PCH. + EG20T PCH is the platform controller hub that is used in Intel's + general embedded platform. EG20T PCH has USB device interface. + Using this interface, it is able to access system devices connected + to USB device. + This driver enables USB device function. + USB device is a USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + This driver supports both control transfer and bulk transfer modes. + This driver dose not support interrupt transfer or isochronous + transfer modes. + + This driver also can be used for LAPIS Semiconductor's ML7213 which is + for IVI(In-Vehicle Infotainment) use. + ML7831 is for general purpose use. + ML7213/ML7831 is companion chip for Intel Atom E6xx series. + ML7213/ML7831 is completely compatible for Intel EG20T PCH. + + This driver can be used with Intel's Quark X1000 SOC platform + +config USB_GADGET_XILINX + tristate "Xilinx USB Driver" + depends on OF || COMPILE_TEST + help + USB peripheral controller driver for Xilinx USB2 device. + Xilinx USB2 device is a soft IP which supports both full + and high speed USB 2.0 data transfers. It has seven configurable + endpoints(bulk or interrupt or isochronous), as well as + endpoint zero(for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "udc-xilinx" and force all + gadget drivers to also be dynamically linked. + +# +# LAST -- dummy/emulated controller +# + +config USB_DUMMY_HCD + tristate "Dummy HCD (DEVELOPMENT)" + depends on USB=y || (USB=m && USB_GADGET=m) + help + This host controller driver emulates USB, looping all data transfer + requests back to a USB "gadget driver" in the same host. The host + side is the master; the gadget side is the slave. Gadget drivers + can be high, full, or low speed; and they have access to endpoints + like those from NET2280, PXA2xx, or SA1100 hardware. + + This may help in some stages of creating a driver to embed in a + Linux device, since it lets you debug several parts of the gadget + driver without its hardware or drivers being involved. + + Since such a gadget side driver needs to interoperate with a host + side Linux-USB device driver, this may help to debug both sides + of a USB protocol stack. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "dummy_hcd" and force all + gadget drivers to also be dynamically linked. + +# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears +# first and will be selected by default. + +endmenu diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile new file mode 100644 index 000000000000..a7f4491593f1 --- /dev/null +++ b/drivers/usb/gadget/udc/Makefile @@ -0,0 +1,32 @@ +# +# USB peripheral controller drivers +# +obj-$(CONFIG_USB_GADGET) += udc-core.o +obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o +obj-$(CONFIG_USB_NET2272) += net2272.o +obj-$(CONFIG_USB_NET2280) += net2280.o +obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o +obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o +obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o +obj-$(CONFIG_USB_GOKU) += goku_udc.o +obj-$(CONFIG_USB_OMAP) += omap_udc.o +obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o +obj-$(CONFIG_USB_AT91) += at91_udc.o +obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o +obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o +obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o +fsl_usb2_udc-y := fsl_udc_core.o +fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o +obj-$(CONFIG_USB_M66592) += m66592-udc.o +obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o +obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o +obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o +obj-$(CONFIG_USB_EG20T) += pch_udc.o +obj-$(CONFIG_USB_MV_UDC) += mv_udc.o +mv_udc-y := mv_udc_core.o +obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o +obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o +obj-$(CONFIG_USB_GR_UDC) += gr_udc.o +obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c index 41b062eb4de0..3b9d13848a4f 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/udc/amd5536udc.c @@ -841,7 +841,7 @@ __acquires(ep->dev->lock) &req->req, req->req.length, ep->ep.name, sts); spin_unlock(&dev->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dev->lock); ep->halted = halted; } diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index 6744d3b83109..6744d3b83109 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index cfd18bcca723..9968f5331fe4 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -267,7 +267,7 @@ static void done(struct at91_ep *ep, struct at91_request *req, int status) ep->stopped = 1; spin_unlock(&udc->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&udc->lock); ep->stopped = stopped; diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index 017524663381..017524663381 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 76023ce449a3..1529926e20a0 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -463,7 +463,7 @@ static void receive_data(struct usba_ep *ep) list_del_init(&req->queue); usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); spin_unlock(&udc->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&udc->lock); } @@ -495,7 +495,7 @@ request_complete(struct usba_ep *ep, struct usba_request *req, int status) ep->ep.name, req, req->req.status, req->req.actual); spin_unlock(&udc->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&udc->lock); } @@ -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_DMAS; i++) + for (i = 1; i <= USBA_NR_DMAS; i++) if (dma_status & (1 << i)) usba_dma_irq(udc, &udc->usba_ep[i]); } @@ -1979,7 +1979,7 @@ static struct usba_ep * usba_udc_pdata(struct platform_device *pdev, return eps; } -static int __init usba_udc_probe(struct platform_device *pdev) +static int usba_udc_probe(struct platform_device *pdev) { struct resource *regs, *fifo; struct clk *pclk, *hclk; diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index a70706e8cb02..a70706e8cb02 100644 --- a/drivers/usb/gadget/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index e969eb809a85..2235b8808700 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -1088,7 +1088,7 @@ static int bcm63xx_ep_disable(struct usb_ep *ep) breq->req.status = -ESHUTDOWN; spin_unlock_irqrestore(&udc->lock, flags); - breq->req.complete(&iudma->bep->ep, &breq->req); + usb_gadget_giveback_request(&iudma->bep->ep, &breq->req); spin_lock_irqsave(&udc->lock, flags); } } diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 2b54955d3166..81dc5959e36b 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -258,7 +258,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep) req->req.status = -ESHUTDOWN; spin_unlock(&dum->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dum->lock); } } @@ -658,7 +658,7 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req, spin_unlock(&dum->lock); _req->actual = _req->length; _req->status = 0; - _req->complete(_ep, _req); + usb_gadget_giveback_request(_ep, _req); spin_lock(&dum->lock); } else list_add_tail(&req->queue, &ep->queue); @@ -702,7 +702,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req) dev_dbg(udc_dev(dum), "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); - _req->complete(_ep, _req); + usb_gadget_giveback_request(_ep, _req); } local_irq_restore(flags); return retval; @@ -1385,7 +1385,7 @@ top: list_del_init(&req->queue); spin_unlock(&dum->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dum->lock); /* requests might have been unlinked... */ @@ -1761,7 +1761,7 @@ restart: req); spin_unlock(&dum->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dum->lock); ep->already_seen = 0; goto restart; diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index e143d69f6017..1d315921bf34 100644 --- a/drivers/usb/gadget/fotg210-udc.c +++ b/drivers/usb/gadget/udc/fotg210-udc.c @@ -70,7 +70,7 @@ static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, req->req.status = status; spin_unlock(&ep->fotg210->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&ep->fotg210->lock); if (ep->epnum) { diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/udc/fotg210.h index bbf991bcbe7c..bbf991bcbe7c 100644 --- a/drivers/usb/gadget/fotg210.h +++ b/drivers/usb/gadget/udc/fotg210.h diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c index 9b140fc4d3bc..f16e149c5b3e 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/udc/fsl_mxc_udc.c @@ -18,6 +18,8 @@ #include <linux/platform_device.h> #include <linux/io.h> +#include "fsl_usb2_udc.h" + static struct clk *mxc_ahb_clk; static struct clk *mxc_per_clk; static struct clk *mxc_ipg_clk; diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index ad5483335167..dd18ea38e391 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -118,10 +118,7 @@ static void done(struct qe_ep *ep, struct qe_req *req, int status) ep->stopped = 1; spin_unlock(&udc->lock); - /* this complete() should a func implemented by gadget layer, - * eg fsg->bulk_in_complete() */ - if (req->req.complete) - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&udc->lock); @@ -2539,7 +2536,7 @@ static int qe_udc_probe(struct platform_device *ofdev) goto err2; /* create a buf for ZLP send, need to remain zeroed */ - udc->nullbuf = kzalloc(256, GFP_KERNEL); + udc->nullbuf = devm_kzalloc(&ofdev->dev, 256, GFP_KERNEL); if (udc->nullbuf == NULL) { dev_err(udc->dev, "cannot alloc nullbuf\n"); ret = -ENOMEM; @@ -2547,10 +2544,10 @@ static int qe_udc_probe(struct platform_device *ofdev) } /* buffer for data of get_status request */ - udc->statusbuf = kzalloc(2, GFP_KERNEL); + udc->statusbuf = devm_kzalloc(&ofdev->dev, 2, GFP_KERNEL); if (udc->statusbuf == NULL) { ret = -ENOMEM; - goto err4; + goto err3; } udc->nullp = virt_to_phys((void *)udc->nullbuf); @@ -2581,13 +2578,13 @@ static int qe_udc_probe(struct platform_device *ofdev) if (ret) { dev_err(udc->dev, "cannot request irq %d err %d\n", udc->usb_irq, ret); - goto err5; + goto err4; } ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget, qe_udc_release); if (ret) - goto err6; + goto err5; platform_set_drvdata(ofdev, udc); dev_info(udc->dev, @@ -2595,9 +2592,9 @@ static int qe_udc_probe(struct platform_device *ofdev) (udc->soc_type == PORT_QE) ? "QE" : "CPM"); return 0; -err6: - free_irq(udc->usb_irq, udc); err5: + free_irq(udc->usb_irq, udc); +err4: irq_dispose_mapping(udc->usb_irq); err_noirq: if (udc->nullmap) { @@ -2610,9 +2607,6 @@ err_noirq: udc->nullp, 256, DMA_TO_DEVICE); } - kfree(udc->statusbuf); -err4: - kfree(udc->nullbuf); err3: ep = &udc->eps[0]; cpm_muram_free(cpm_muram_offset(ep->rxbase)); @@ -2660,8 +2654,6 @@ static int qe_udc_remove(struct platform_device *ofdev) udc->nullp, 256, DMA_TO_DEVICE); } - kfree(udc->statusbuf); - kfree(udc->nullbuf); ep = &udc->eps[0]; cpm_muram_free(cpm_muram_offset(ep->rxbase)); @@ -2733,4 +2725,3 @@ module_platform_driver(udc_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_LICENSE("GPL"); - diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h index 7026919fc901..7026919fc901 100644 --- a/drivers/usb/gadget/fsl_qe_udc.h +++ b/drivers/usb/gadget/udc/fsl_qe_udc.h diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 28e4fc957026..c3620791a315 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -59,9 +59,9 @@ static const char driver_name[] = "fsl-usb2-udc"; static const char driver_desc[] = DRIVER_DESC; -static struct usb_dr_device *dr_regs; +static struct usb_dr_device __iomem *dr_regs; -static struct usb_sys_interface *usb_sys_regs; +static struct usb_sys_interface __iomem *usb_sys_regs; /* it is initialized in probe() */ static struct fsl_udc *udc_controller = NULL; @@ -159,6 +159,8 @@ static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata) {} * request is still in progress. *--------------------------------------------------------------*/ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) +__releases(ep->udc->lock) +__acquires(ep->udc->lock) { struct fsl_udc *udc = NULL; unsigned char stopped = ep->stopped; @@ -195,10 +197,8 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) ep->stopped = 1; spin_unlock(&ep->udc->lock); - /* complete() is from gadget layer, - * eg fsg->bulk_in_complete() */ - if (req->req.complete) - req->req.complete(&ep->ep, &req->req); + + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&ep->udc->lock); ep->stopped = stopped; @@ -1392,6 +1392,8 @@ stall: static void setup_received_irq(struct fsl_udc *udc, struct usb_ctrlrequest *setup) +__releases(udc->lock) +__acquires(udc->lock) { u16 wValue = le16_to_cpu(setup->wValue); u16 wIndex = le16_to_cpu(setup->wIndex); @@ -1957,8 +1959,7 @@ static int fsl_udc_start(struct usb_gadget *g, &udc_controller->gadget); if (retval < 0) { ERR("can't bind to transceiver\n"); - driver->unbind(&udc_controller->gadget); - udc_controller->driver = 0; + udc_controller->driver = NULL; return retval; } } @@ -2246,7 +2247,7 @@ static void fsl_udc_release(struct device *dev) * init resource for globle controller * Return the udc handle on success or NULL on failure ------------------------------------------------------------------*/ -static int __init struct_udc_setup(struct fsl_udc *udc, +static int struct_udc_setup(struct fsl_udc *udc, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata; @@ -2298,7 +2299,7 @@ static int __init struct_udc_setup(struct fsl_udc *udc, * ep0out is not used so do nothing here * ep0in should be taken care *--------------------------------------------------------------*/ -static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, +static int struct_ep_setup(struct fsl_udc *udc, unsigned char index, char *name, int link) { struct fsl_ep *ep = &udc->eps[index]; @@ -2331,7 +2332,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, * all intialization operations implemented here except enabling usb_intr reg * board setup should have been done in the platform code */ -static int __init fsl_udc_probe(struct platform_device *pdev) +static int fsl_udc_probe(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata; struct resource *res; @@ -2380,7 +2381,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_release_mem_region; } - pdata->regs = (void *)dr_regs; + pdata->regs = (void __iomem *)dr_regs; /* * do platform specific init: check the clock, grab/config pins, etc. diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h index c6703bb07b23..84715625b2b3 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h @@ -12,6 +12,9 @@ #ifndef __FSL_USB2_UDC_H #define __FSL_USB2_UDC_H +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + /* ### define USB registers here */ #define USB_MAX_CTRL_PAYLOAD 64 diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 3deb4e938071..8286df72add4 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -876,7 +876,7 @@ static void done(struct fusb300_ep *ep, struct fusb300_request *req, req->req.status = status; spin_unlock(&ep->fusb300->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&ep->fusb300->lock); if (ep->epnum) { @@ -1325,8 +1325,6 @@ static int fusb300_udc_stop(struct usb_gadget *g, { struct fusb300 *fusb300 = to_fusb300(g); - driver->unbind(&fusb300->gadget); - init_controller(fusb300); fusb300->driver = NULL; @@ -1359,7 +1357,7 @@ static int __exit fusb300_remove(struct platform_device *pdev) return 0; } -static int __init fusb300_probe(struct platform_device *pdev) +static int fusb300_probe(struct platform_device *pdev) { struct resource *res, *ires, *ires1; void __iomem *reg = NULL; @@ -1400,13 +1398,17 @@ static int __init fusb300_probe(struct platform_device *pdev) /* initialize udc */ fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL); - if (fusb300 == NULL) + if (fusb300 == NULL) { + ret = -ENOMEM; goto clean_up; + } for (i = 0; i < FUSB300_MAX_NUM_EP; i++) { _ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL); - if (_ep[i] == NULL) + if (_ep[i] == NULL) { + ret = -ENOMEM; goto clean_up; + } fusb300->ep[i] = _ep[i]; } diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h index ae811d8d38b4..ad39f892d200 100644 --- a/drivers/usb/gadget/fusb300_udc.h +++ b/drivers/usb/gadget/udc/fusb300_udc.h @@ -12,7 +12,7 @@ #ifndef __FUSB300_UDC_H__ -#define __FUSB300_UDC_H_ +#define __FUSB300_UDC_H__ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/udc/gadget_chips.h index bcd04bc66b98..bcd04bc66b98 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/udc/gadget_chips.h diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 6c85839e15ad..bf9c5ef8b56b 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -320,7 +320,7 @@ done(struct goku_ep *ep, struct goku_request *req, int status) /* don't modify queue heads during completion callback */ ep->stopped = 1; spin_unlock(&dev->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dev->lock); ep->stopped = stopped; } diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h index 86d2adafe149..86d2adafe149 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/udc/goku_udc.h diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index c7004ee89c90..1b3048a6a2a3 100644 --- a/drivers/usb/gadget/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -318,8 +318,26 @@ static void gr_finish_request(struct gr_ep *ep, struct gr_request *req, usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); gr_free_dma_desc_chain(dev, req); - if (ep->is_in) /* For OUT, actual gets updated bit by bit */ + if (ep->is_in) { /* For OUT, req->req.actual gets updated bit by bit */ req->req.actual = req->req.length; + } else if (req->oddlen && req->req.actual > req->evenlen) { + /* + * Copy to user buffer in this case where length was not evenly + * divisible by ep->ep.maxpacket and the last descriptor was + * actually used. + */ + char *buftail = ((char *)req->req.buf + req->evenlen); + + memcpy(buftail, ep->tailbuf, req->oddlen); + + if (req->req.actual > req->req.length) { + /* We got more data than was requested */ + dev_dbg(ep->dev->dev, "Overflow for ep %s\n", + ep->ep.name); + gr_dbgprint_request("OVFL", ep, req); + req->req.status = -EOVERFLOW; + } + } if (!status) { if (ep->is_in) @@ -339,7 +357,7 @@ static void gr_finish_request(struct gr_ep *ep, struct gr_request *req, } else if (req->req.complete) { spin_unlock(&dev->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dev->lock); } @@ -379,6 +397,15 @@ static void gr_start_dma(struct gr_ep *ep) /* A descriptor should already have been allocated */ BUG_ON(!req->curr_desc); + /* + * The DMA controller can not handle smaller OUT buffers than + * ep->ep.maxpacket. It could lead to buffer overruns if an unexpectedly + * long packet are received. Therefore an internal bounce buffer gets + * used when such a request gets enabled. + */ + if (!ep->is_in && req->oddlen) + req->last_desc->data = ep->tailbuf_paddr; + wmb(); /* Make sure all is settled before handing it over to DMA */ /* Set the descriptor pointer in the hardware */ @@ -480,11 +507,11 @@ static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req, dma_addr_t start = req->req.dma + bytes_used; u16 size = min(bytes_left, ep->bytes_per_buffer); - /* Should not happen however - gr_queue stops such lengths */ - if (size < ep->bytes_per_buffer) - dev_warn(ep->dev->dev, - "Buffer overrun risk: %u < %u bytes/buffer\n", - size, ep->bytes_per_buffer); + if (size < ep->bytes_per_buffer) { + /* Prepare using bounce buffer */ + req->evenlen = req->req.length - bytes_left; + req->oddlen = size; + } ret = gr_add_dma_desc(ep, req, start, size, gfp_flags); if (ret) @@ -584,18 +611,6 @@ static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags) return -EINVAL; } - /* - * The DMA controller can not handle smaller OUT buffers than - * maxpacket. It could lead to buffer overruns if unexpectedly long - * packet are received. - */ - if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) { - dev_err(dev->dev, - "OUT request length %d is not multiple of maxpacket\n", - req->req.length); - return -EMSGSIZE; - } - if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { dev_err(dev->dev, "-ESHUTDOWN"); return -ESHUTDOWN; @@ -1286,8 +1301,8 @@ static int gr_handle_out_ep(struct gr_ep *ep) if (ctrl & GR_DESC_OUT_CTRL_SE) req->setup = 1; - if (len < ep->ep.maxpacket || req->req.actual == req->req.length) { - /* Short packet or the expected size - we are done */ + if (len < ep->ep.maxpacket || req->req.actual >= req->req.length) { + /* Short packet or >= expected size - we are done */ if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) { /* @@ -2015,6 +2030,11 @@ static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit) } list_add_tail(&ep->ep_list, &dev->ep_list); + ep->tailbuf = dma_alloc_coherent(dev->dev, ep->ep.maxpacket_limit, + &ep->tailbuf_paddr, GFP_ATOMIC); + if (!ep->tailbuf) + return -ENOMEM; + return 0; } @@ -2067,9 +2087,24 @@ static int gr_udc_init(struct gr_udc *dev) return 0; } +static void gr_ep_remove(struct gr_udc *dev, int num, int is_in) +{ + struct gr_ep *ep; + + if (is_in) + ep = &dev->epi[num]; + else + ep = &dev->epo[num]; + + if (ep->tailbuf) + dma_free_coherent(dev->dev, ep->ep.maxpacket_limit, + ep->tailbuf, ep->tailbuf_paddr); +} + static int gr_remove(struct platform_device *pdev) { struct gr_udc *dev = platform_get_drvdata(pdev); + int i; if (dev->added) usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */ @@ -2084,6 +2119,11 @@ static int gr_remove(struct platform_device *pdev) gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req); gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req); + for (i = 0; i < dev->nepo; i++) + gr_ep_remove(dev, i, 0); + for (i = 0; i < dev->nepi; i++) + gr_ep_remove(dev, i, 1); + return 0; } static int gr_request_irq(struct gr_udc *dev, int irq) @@ -2131,7 +2171,6 @@ static int gr_probe(struct platform_device *pdev) dev->gadget.name = driver_name; dev->gadget.max_speed = USB_SPEED_HIGH; dev->gadget.ops = &gr_ops; - dev->gadget.quirk_ep_out_aligned_size = true; spin_lock_init(&dev->lock); dev->regs = regs; @@ -2213,7 +2252,7 @@ out: return retval; } -static struct of_device_id gr_match[] = { +static const struct of_device_id gr_match[] = { {.name = "GAISLER_USBDC"}, {.name = "01_021"}, {}, diff --git a/drivers/usb/gadget/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h index 8388897d9ec3..4297c4e8021f 100644 --- a/drivers/usb/gadget/gr_udc.h +++ b/drivers/usb/gadget/udc/gr_udc.h @@ -156,6 +156,10 @@ struct gr_ep { struct list_head queue; struct list_head ep_list; + + /* Bounce buffer for end of "odd" sized OUT requests */ + void *tailbuf; + dma_addr_t tailbuf_paddr; }; struct gr_request { @@ -167,6 +171,9 @@ struct gr_request { struct gr_dma_desc *curr_desc; /* Current descriptor */ struct gr_dma_desc *last_desc; /* Last in the chain */ + u16 evenlen; /* Size of even length head (if oddlen != 0) */ + u16 oddlen; /* Size of odd length tail if buffer length is "odd" */ + u8 setup; /* Setup packet */ }; diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index e471580a2a3b..feab0bac8fdc 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -1479,7 +1479,7 @@ static void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status) ep->req_pending = 0; spin_unlock(&udc->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&udc->lock); } @@ -3036,7 +3036,7 @@ struct lpc32xx_usbd_cfg lpc32xx_usbddata = { static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F; -static int __init lpc32xx_udc_probe(struct platform_device *pdev) +static int lpc32xx_udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct lpc32xx_udc *udc; @@ -3045,11 +3045,10 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) dma_addr_t dma_handle; struct device_node *isp1301_node; - udc = kzalloc(sizeof(*udc), GFP_KERNEL); + udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL); if (!udc) return -ENOMEM; - memcpy(udc, &controller_template, sizeof(*udc)); for (i = 0; i <= 15; i++) udc->ep[i].udc = udc; udc->gadget.ep0 = &udc->ep[0].ep; @@ -3397,7 +3396,7 @@ static int lpc32xx_udc_resume(struct platform_device *pdev) #endif #ifdef CONFIG_OF -static struct of_device_id lpc32xx_udc_of_match[] = { +static const struct of_device_id lpc32xx_udc_of_match[] = { { .compatible = "nxp,lpc3220-udc", }, { }, }; diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index 0d17174b86f8..898565687a8c 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -729,7 +729,7 @@ __acquires(m66592->lock) restart = 1; spin_unlock(&ep->m66592->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&ep->m66592->lock); if (restart) { @@ -1492,8 +1492,6 @@ static int m66592_udc_stop(struct usb_gadget *g, m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); - driver->unbind(&m66592->gadget); - init_controller(m66592); disable_controller(m66592); @@ -1553,7 +1551,7 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } -static int __init m66592_probe(struct platform_device *pdev) +static int m66592_probe(struct platform_device *pdev) { struct resource *res, *ires; void __iomem *reg = NULL; diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/udc/m66592-udc.h index 96d49d7bfb6b..96d49d7bfb6b 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/udc/m66592-udc.h diff --git a/drivers/usb/gadget/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h index e32a787ac373..e32a787ac373 100644 --- a/drivers/usb/gadget/mv_u3d.h +++ b/drivers/usb/gadget/udc/mv_u3d.h diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index 16248711c152..046a1f808b0d 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -222,12 +222,8 @@ void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status) } spin_unlock(&ep->u3d->lock); - /* - * complete() is from gadget layer, - * eg fsg->bulk_in_complete() - */ - if (req->req.complete) - req->req.complete(&ep->ep, &req->req); + + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&ep->u3d->lock); } diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h index be77f207dbaf..be77f207dbaf 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/udc/mv_udc.h diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index fcff3a571b45..3c5db80ae325 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -248,12 +248,8 @@ static void done(struct mv_ep *ep, struct mv_req *req, int status) ep->stopped = 1; spin_unlock(&ep->udc->lock); - /* - * complete() is from gadget layer, - * eg fsg->bulk_in_complete() - */ - if (req->req.complete) - req->req.complete(&ep->ep, &req->req); + + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&ep->udc->lock); ep->stopped = stopped; @@ -332,7 +328,7 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req) /* clear active and halt bit, in case set from a previous error */ dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); - /* Ensure that updates to the QH will occure before priming. */ + /* Ensure that updates to the QH will occur before priming. */ wmb(); /* Prime the Endpoint */ @@ -1656,7 +1652,7 @@ static void handle_setup_packet(struct mv_udc *udc, u8 ep_num, dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", setup->bRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength); - /* We process some stardard setup requests here */ + /* We process some standard setup requests here */ if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (setup->bRequest) { case USB_REQ_GET_STATUS: diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/udc/net2272.c index ca15405583e2..84d7162a8022 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -394,7 +394,7 @@ net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status) /* don't modify queue heads during completion callback */ ep->stopped = 1; spin_unlock(&dev->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dev->lock); ep->stopped = stopped; } @@ -1453,7 +1453,7 @@ static int net2272_start(struct usb_gadget *_gadget, struct net2272 *dev; unsigned i; - if (!driver || !driver->unbind || !driver->setup || + if (!driver || !driver->setup || driver->max_speed != USB_SPEED_HIGH) return -EINVAL; diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/udc/net2272.h index e59505789359..e59505789359 100644 --- a/drivers/usb/gadget/net2272.h +++ b/drivers/usb/gadget/udc/net2272.h diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c new file mode 100644 index 000000000000..8d13337e2dde --- /dev/null +++ b/drivers/usb/gadget/udc/net2280.c @@ -0,0 +1,3827 @@ +/* + * Driver for the PLX NET2280 USB device controller. + * Specs and errata are available from <http://www.plxtech.com>. + * + * PLX Technology Inc. (formerly NetChip Technology) supported the + * development of this driver. + * + * + * CODE STATUS HIGHLIGHTS + * + * This driver should work well with most "gadget" drivers, including + * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers + * as well as Gadget Zero and Gadgetfs. + * + * DMA is enabled by default. Drivers using transfer queues might use + * DMA chaining to remove IRQ latencies between transfers. (Except when + * short OUT transfers happen.) Drivers can use the req->no_interrupt + * hint to completely eliminate some IRQs, if a later IRQ is guaranteed + * and DMA chaining is enabled. + * + * MSI is enabled by default. The legacy IRQ is used if MSI couldn't + * be enabled. + * + * Note that almost all the errata workarounds here are only needed for + * rev1 chips. Rev1a silicon (0110) fixes almost all of them. + */ + +/* + * Copyright (C) 2003 David Brownell + * Copyright (C) 2003-2005 PLX Technology, Inc. + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS + * + * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility + * with 2282 chip + * + * Modified Ricardo Ribalda Qtechnology AS to provide compatibility + * with usb 338x chip. Based on PLX driver + * + * 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. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/prefetch.h> +#include <linux/io.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/unaligned.h> + +#define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller" +#define DRIVER_VERSION "2005 Sept 27/v3.0" + +#define EP_DONTUSE 13 /* nonzero */ + +#define USE_RDK_LEDS /* GPIO pins control three LEDs */ + + +static const char driver_name[] = "net2280"; +static const char driver_desc[] = DRIVER_DESC; + +static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 }; +static const char ep0name[] = "ep0"; +static const char *const ep_name[] = { + ep0name, + "ep-a", "ep-b", "ep-c", "ep-d", + "ep-e", "ep-f", "ep-g", "ep-h", +}; + +/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) + * use_dma_chaining -- dma descriptor queueing gives even more irq reduction + * + * The net2280 DMA engines are not tightly integrated with their FIFOs; + * not all cases are (yet) handled well in this driver or the silicon. + * Some gadget drivers work better with the dma support here than others. + * These two parameters let you use PIO or more aggressive DMA. + */ +static bool use_dma = true; +static bool use_dma_chaining; +static bool use_msi = true; + +/* "modprobe net2280 use_dma=n" etc */ +module_param(use_dma, bool, 0444); +module_param(use_dma_chaining, bool, 0444); +module_param(use_msi, bool, 0444); + +/* mode 0 == ep-{a,b,c,d} 1K fifo each + * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable + * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable + */ +static ushort fifo_mode; + +/* "modprobe net2280 fifo_mode=1" etc */ +module_param(fifo_mode, ushort, 0644); + +/* enable_suspend -- When enabled, the driver will respond to + * USB suspend requests by powering down the NET2280. Otherwise, + * USB suspend requests will be ignored. This is acceptable for + * self-powered devices + */ +static bool enable_suspend; + +/* "modprobe net2280 enable_suspend=1" etc */ +module_param(enable_suspend, bool, 0444); + +/* force full-speed operation */ +static bool full_speed; +module_param(full_speed, bool, 0444); +MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!"); + +#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") + +static char *type_string(u8 bmAttributes) +{ + switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: return "bulk"; + case USB_ENDPOINT_XFER_ISOC: return "iso"; + case USB_ENDPOINT_XFER_INT: return "intr"; + } + return "control"; +} + +#include "net2280.h" + +#define valid_bit cpu_to_le32(BIT(VALID_BIT)) +#define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) + +/*-------------------------------------------------------------------------*/ +static inline void enable_pciirqenb(struct net2280_ep *ep) +{ + u32 tmp = readl(&ep->dev->regs->pciirqenb0); + + if (ep->dev->quirks & PLX_LEGACY) + tmp |= BIT(ep->num); + else + tmp |= BIT(ep_bit[ep->num]); + writel(tmp, &ep->dev->regs->pciirqenb0); + + return; +} + +static int +net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct net2280 *dev; + struct net2280_ep *ep; + u32 max, tmp; + unsigned long flags; + static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name || + desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* erratum 0119 workaround ties up an endpoint number */ + if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) + return -EDOM; + + if (dev->quirks & PLX_SUPERSPEED) { + if ((desc->bEndpointAddress & 0x0f) >= 0x0c) + return -EDOM; + ep->is_in = !!usb_endpoint_dir_in(desc); + if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) + return -EINVAL; + } + + /* sanity check ep-e/ep-f since their fifos are small */ + max = usb_endpoint_maxp(desc) & 0x1fff; + if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) + return -ERANGE; + + spin_lock_irqsave(&dev->lock, flags); + _ep->maxpacket = max & 0x7ff; + ep->desc = desc; + + /* ep_reset() has already been called */ + ep->stopped = 0; + ep->wedged = 0; + ep->out_overflow = 0; + + /* set speed-dependent max packet; may kick in high bandwidth */ + set_max_speed(ep, max); + + /* FIFO lines can't go to different packets. PIO is ok, so + * use it instead of troublesome (non-bulk) multi-packet DMA. + */ + if (ep->dma && (max % 4) != 0 && use_dma_chaining) { + ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n", + ep->ep.name, ep->ep.maxpacket); + ep->dma = NULL; + } + + /* set type, direction, address; reset fifo counters */ + writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); + tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + if (tmp == USB_ENDPOINT_XFER_INT) { + /* erratum 0105 workaround prevents hs NYET */ + if (dev->chiprev == 0100 && + dev->gadget.speed == USB_SPEED_HIGH && + !(desc->bEndpointAddress & USB_DIR_IN)) + writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), + &ep->regs->ep_rsp); + } else if (tmp == USB_ENDPOINT_XFER_BULK) { + /* catch some particularly blatant driver bugs */ + if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || + (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || + (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ERANGE; + } + } + ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); + /* Enable this endpoint */ + if (dev->quirks & PLX_LEGACY) { + tmp <<= ENDPOINT_TYPE; + tmp |= desc->bEndpointAddress; + /* default full fifo lines */ + tmp |= (4 << ENDPOINT_BYTE_COUNT); + tmp |= BIT(ENDPOINT_ENABLE); + ep->is_in = (tmp & USB_DIR_IN) != 0; + } else { + /* In Legacy mode, only OUT endpoints are used */ + if (dev->enhanced_mode && ep->is_in) { + tmp <<= IN_ENDPOINT_TYPE; + tmp |= BIT(IN_ENDPOINT_ENABLE); + /* Not applicable to Legacy */ + tmp |= BIT(ENDPOINT_DIRECTION); + } else { + tmp <<= OUT_ENDPOINT_TYPE; + tmp |= BIT(OUT_ENDPOINT_ENABLE); + tmp |= (ep->is_in << ENDPOINT_DIRECTION); + } + + tmp |= usb_endpoint_num(desc); + tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); + } + + /* Make sure all the registers are written before ep_rsp*/ + wmb(); + + /* for OUT transfers, block the rx fifo until a read is posted */ + if (!ep->is_in) + writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + else if (!(dev->quirks & PLX_2280)) { + /* Added for 2282, Don't use nak packets on an in endpoint, + * this was ignored on 2280 + */ + writel(BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); + } + + writel(tmp, &ep->cfg->ep_cfg); + + /* enable irqs */ + if (!ep->dma) { /* pio, per-packet */ + enable_pciirqenb(ep); + + tmp = BIT(DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); + if (dev->quirks & PLX_2280) + tmp |= readl(&ep->regs->ep_irqenb); + writel(tmp, &ep->regs->ep_irqenb); + } else { /* dma, per-request */ + tmp = BIT((8 + ep->num)); /* completion */ + tmp |= readl(&dev->regs->pciirqenb1); + writel(tmp, &dev->regs->pciirqenb1); + + /* for short OUT transfers, dma completions can't + * advance the queue; do it pio-style, by hand. + * NOTE erratum 0112 workaround #2 + */ + if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { + tmp = BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); + writel(tmp, &ep->regs->ep_irqenb); + + enable_pciirqenb(ep); + } + } + + tmp = desc->bEndpointAddress; + ep_dbg(dev, "enabled %s (ep%d%s-%s) %s max %04x\n", + _ep->name, tmp & 0x0f, DIR_STRING(tmp), + type_string(desc->bmAttributes), + ep->dma ? "dma" : "pio", max); + + /* pci writes may still be posted */ + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + +static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl(ptr); + if (result == ~(u32)0) /* "device unplugged" */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +static const struct usb_ep_ops net2280_ep_ops; + +static void ep_reset_228x(struct net2280_regs __iomem *regs, + struct net2280_ep *ep) +{ + u32 tmp; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.ops = &net2280_ep_ops; + + /* disable the dma, irqs, endpoint... */ + if (ep->dma) { + writel(0, &ep->dma->dmactl); + writel(BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_TRANSACTION_DONE_INTERRUPT) | + BIT(DMA_ABORT), + &ep->dma->dmastat); + + tmp = readl(®s->pciirqenb0); + tmp &= ~BIT(ep->num); + writel(tmp, ®s->pciirqenb0); + } else { + tmp = readl(®s->pciirqenb1); + tmp &= ~BIT((8 + ep->num)); /* completion */ + writel(tmp, ®s->pciirqenb1); + } + writel(0, &ep->regs->ep_irqenb); + + /* init to our chosen defaults, notably so that we NAK OUT + * packets until the driver queues a read (+note erratum 0112) + */ + if (!ep->is_in || (ep->dev->quirks & PLX_2280)) { + tmp = BIT(SET_NAK_OUT_PACKETS_MODE) | + BIT(SET_NAK_OUT_PACKETS) | + BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_INTERRUPT_MODE); + } else { + /* added for 2282 */ + tmp = BIT(CLEAR_NAK_OUT_PACKETS_MODE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_INTERRUPT_MODE); + } + + if (ep->num != 0) { + tmp |= BIT(CLEAR_ENDPOINT_TOGGLE) | + BIT(CLEAR_ENDPOINT_HALT); + } + writel(tmp, &ep->regs->ep_rsp); + + /* scrub most status bits, and flush any fifo state */ + if (ep->dev->quirks & PLX_2280) + tmp = BIT(FIFO_OVERFLOW) | + BIT(FIFO_UNDERFLOW); + else + tmp = 0; + + writel(tmp | BIT(TIMEOUT) | + BIT(USB_STALL_SENT) | + BIT(USB_IN_NAK_SENT) | + BIT(USB_IN_ACK_RCVD) | + BIT(USB_OUT_PING_NAK_SENT) | + BIT(USB_OUT_ACK_SENT) | + BIT(FIFO_FLUSH) | + BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), + &ep->regs->ep_stat); + + /* fifo size is handled separately */ +} + +static void ep_reset_338x(struct net2280_regs __iomem *regs, + struct net2280_ep *ep) +{ + u32 tmp, dmastat; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.ops = &net2280_ep_ops; + + /* disable the dma, irqs, endpoint... */ + if (ep->dma) { + writel(0, &ep->dma->dmactl); + writel(BIT(DMA_ABORT_DONE_INTERRUPT) | + BIT(DMA_PAUSE_DONE_INTERRUPT) | + BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_TRANSACTION_DONE_INTERRUPT), + /* | BIT(DMA_ABORT), */ + &ep->dma->dmastat); + + dmastat = readl(&ep->dma->dmastat); + if (dmastat == 0x5002) { + ep_warn(ep->dev, "The dmastat return = %x!!\n", + dmastat); + writel(0x5a, &ep->dma->dmastat); + } + + tmp = readl(®s->pciirqenb0); + tmp &= ~BIT(ep_bit[ep->num]); + writel(tmp, ®s->pciirqenb0); + } else { + if (ep->num < 5) { + tmp = readl(®s->pciirqenb1); + tmp &= ~BIT((8 + ep->num)); /* completion */ + writel(tmp, ®s->pciirqenb1); + } + } + writel(0, &ep->regs->ep_irqenb); + + writel(BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(FIFO_OVERFLOW) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); +} + +static void nuke(struct net2280_ep *); + +static int net2280_disable(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || !ep->desc || _ep->name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + nuke(ep); + + if (ep->dev->quirks & PLX_SUPERSPEED) + ep_reset_338x(ep->dev->regs, ep); + else + ep_reset_228x(ep->dev->regs, ep); + + ep_vdbg(ep->dev, "disabled %s %s\n", + ep->dma ? "dma" : "pio", _ep->name); + + /* synch memory views with the device */ + (void)readl(&ep->cfg->ep_cfg); + + if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) + ep->dma = &ep->dev->dma[ep->num - 1]; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request +*net2280_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct net2280_ep *ep; + struct net2280_request *req; + + if (!_ep) + return NULL; + ep = container_of(_ep, struct net2280_ep, ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + /* this dma descriptor may be swapped with the previous dummy */ + if (ep->dma) { + struct net2280_dma *td; + + td = pci_pool_alloc(ep->dev->requests, gfp_flags, + &req->td_dma); + if (!td) { + kfree(req); + return NULL; + } + td->dmacount = 0; /* not VALID */ + td->dmadesc = td->dmaaddr; + req->td = td; + } + return &req->req; +} + +static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2280_ep *ep; + struct net2280_request *req; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || !_req) + return; + + req = container_of(_req, struct net2280_request, req); + WARN_ON(!list_empty(&req->queue)); + if (req->td) + pci_pool_free(ep->dev->requests, req->td, req->td_dma); + kfree(req); +} + +/*-------------------------------------------------------------------------*/ + +/* load a packet into the fifo we use for usb IN transfers. + * works for all endpoints. + * + * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo + * at a time, but this code is simpler because it knows it only writes + * one packet. ep-a..ep-d should use dma instead. + */ +static void write_fifo(struct net2280_ep *ep, struct usb_request *req) +{ + struct net2280_ep_regs __iomem *regs = ep->regs; + u8 *buf; + u32 tmp; + unsigned count, total; + + /* INVARIANT: fifo is currently empty. (testable) */ + + if (req) { + buf = req->buf + req->actual; + prefetch(buf); + total = req->length - req->actual; + } else { + total = 0; + buf = NULL; + } + + /* write just one packet at a time */ + count = ep->ep.maxpacket; + if (count > total) /* min() cannot be used on a bitfield */ + count = total; + + ep_vdbg(ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", + ep->ep.name, count, + (count != ep->ep.maxpacket) ? " (short)" : "", + req); + while (count >= 4) { + /* NOTE be careful if you try to align these. fifo lines + * should normally be full (4 bytes) and successive partial + * lines are ok only in certain cases. + */ + tmp = get_unaligned((u32 *)buf); + cpu_to_le32s(&tmp); + writel(tmp, ®s->ep_data); + buf += 4; + count -= 4; + } + + /* last fifo entry is "short" unless we wrote a full packet. + * also explicitly validate last word in (periodic) transfers + * when maxpacket is not a multiple of 4 bytes. + */ + if (count || total < ep->ep.maxpacket) { + tmp = count ? get_unaligned((u32 *)buf) : count; + cpu_to_le32s(&tmp); + set_fifo_bytecount(ep, count & 0x03); + writel(tmp, ®s->ep_data); + } + + /* pci writes may still be posted */ +} + +/* work around erratum 0106: PCI and USB race over the OUT fifo. + * caller guarantees chiprev 0100, out endpoint is NAKing, and + * there's no real data in the fifo. + * + * NOTE: also used in cases where that erratum doesn't apply: + * where the host wrote "too much" data to us. + */ +static void out_flush(struct net2280_ep *ep) +{ + u32 __iomem *statp; + u32 tmp; + + ASSERT_OUT_NAKING(ep); + + statp = &ep->regs->ep_stat; + writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT), + statp); + writel(BIT(FIFO_FLUSH), statp); + /* Make sure that stap is written */ + mb(); + tmp = readl(statp); + if (tmp & BIT(DATA_OUT_PING_TOKEN_INTERRUPT) && + /* high speed did bulk NYET; fifo isn't filling */ + ep->dev->gadget.speed == USB_SPEED_FULL) { + unsigned usec; + + usec = 50; /* 64 byte bulk/interrupt */ + handshake(statp, BIT(USB_OUT_PING_NAK_SENT), + BIT(USB_OUT_PING_NAK_SENT), usec); + /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ + } +} + +/* unload packet(s) from the fifo we use for usb OUT transfers. + * returns true iff the request completed, because of short packet + * or the request buffer having filled with full packets. + * + * for ep-a..ep-d this will read multiple packets out when they + * have been accepted. + */ +static int read_fifo(struct net2280_ep *ep, struct net2280_request *req) +{ + struct net2280_ep_regs __iomem *regs = ep->regs; + u8 *buf = req->req.buf + req->req.actual; + unsigned count, tmp, is_short; + unsigned cleanup = 0, prevent = 0; + + /* erratum 0106 ... packets coming in during fifo reads might + * be incompletely rejected. not all cases have workarounds. + */ + if (ep->dev->chiprev == 0x0100 && + ep->dev->gadget.speed == USB_SPEED_FULL) { + udelay(1); + tmp = readl(&ep->regs->ep_stat); + if ((tmp & BIT(NAK_OUT_PACKETS))) + cleanup = 1; + else if ((tmp & BIT(FIFO_FULL))) { + start_out_naking(ep); + prevent = 1; + } + /* else: hope we don't see the problem */ + } + + /* never overflow the rx buffer. the fifo reads packets until + * it sees a short one; we might not be ready for them all. + */ + prefetchw(buf); + count = readl(®s->ep_avail); + if (unlikely(count == 0)) { + udelay(1); + tmp = readl(&ep->regs->ep_stat); + count = readl(®s->ep_avail); + /* handled that data already? */ + if (count == 0 && (tmp & BIT(NAK_OUT_PACKETS)) == 0) + return 0; + } + + tmp = req->req.length - req->req.actual; + if (count > tmp) { + /* as with DMA, data overflow gets flushed */ + if ((tmp % ep->ep.maxpacket) != 0) { + ep_err(ep->dev, + "%s out fifo %d bytes, expected %d\n", + ep->ep.name, count, tmp); + req->req.status = -EOVERFLOW; + cleanup = 1; + /* NAK_OUT_PACKETS will be set, so flushing is safe; + * the next read will start with the next packet + */ + } /* else it's a ZLP, no worries */ + count = tmp; + } + req->req.actual += count; + + is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); + + ep_vdbg(ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", + ep->ep.name, count, is_short ? " (short)" : "", + cleanup ? " flush" : "", prevent ? " nak" : "", + req, req->req.actual, req->req.length); + + while (count >= 4) { + tmp = readl(®s->ep_data); + cpu_to_le32s(&tmp); + put_unaligned(tmp, (u32 *)buf); + buf += 4; + count -= 4; + } + if (count) { + tmp = readl(®s->ep_data); + /* LE conversion is implicit here: */ + do { + *buf++ = (u8) tmp; + tmp >>= 8; + } while (--count); + } + if (cleanup) + out_flush(ep); + if (prevent) { + writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + (void) readl(&ep->regs->ep_rsp); + } + + return is_short || ((req->req.actual == req->req.length) && + !req->req.zero); +} + +/* fill out dma descriptor to match a given request */ +static void fill_dma_desc(struct net2280_ep *ep, + struct net2280_request *req, int valid) +{ + struct net2280_dma *td = req->td; + u32 dmacount = req->req.length; + + /* don't let DMA continue after a short OUT packet, + * so overruns can't affect the next transfer. + * in case of overruns on max-size packets, we can't + * stop the fifo from filling but we can flush it. + */ + if (ep->is_in) + dmacount |= BIT(DMA_DIRECTION); + if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || + !(ep->dev->quirks & PLX_2280)) + dmacount |= BIT(END_OF_CHAIN); + + req->valid = valid; + if (valid) + dmacount |= BIT(VALID_BIT); + if (likely(!req->req.no_interrupt || !use_dma_chaining)) + dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE); + + /* td->dmadesc = previously set by caller */ + td->dmaaddr = cpu_to_le32 (req->req.dma); + + /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ + wmb(); + td->dmacount = cpu_to_le32(dmacount); +} + +static const u32 dmactl_default = + BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_CLEAR_COUNT_ENABLE) | + /* erratum 0116 workaround part 1 (use POLLING) */ + (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) | + BIT(DMA_VALID_BIT_POLLING_ENABLE) | + BIT(DMA_VALID_BIT_ENABLE) | + BIT(DMA_SCATTER_GATHER_ENABLE) | + /* erratum 0116 workaround part 2 (no AUTOSTART) */ + BIT(DMA_ENABLE); + +static inline void spin_stop_dma(struct net2280_dma_regs __iomem *dma) +{ + handshake(&dma->dmactl, BIT(DMA_ENABLE), 0, 50); +} + +static inline void stop_dma(struct net2280_dma_regs __iomem *dma) +{ + writel(readl(&dma->dmactl) & ~BIT(DMA_ENABLE), &dma->dmactl); + spin_stop_dma(dma); +} + +static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma) +{ + struct net2280_dma_regs __iomem *dma = ep->dma; + unsigned int tmp = BIT(VALID_BIT) | (ep->is_in << DMA_DIRECTION); + + if (!(ep->dev->quirks & PLX_2280)) + tmp |= BIT(END_OF_CHAIN); + + writel(tmp, &dma->dmacount); + writel(readl(&dma->dmastat), &dma->dmastat); + + writel(td_dma, &dma->dmadesc); + if (ep->dev->quirks & PLX_SUPERSPEED) + dmactl |= BIT(DMA_REQUEST_OUTSTANDING); + writel(dmactl, &dma->dmactl); + + /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ + (void) readl(&ep->dev->pci->pcimstctl); + + writel(BIT(DMA_START), &dma->dmastat); + + if (!ep->is_in) + stop_out_naking(ep); +} + +static void start_dma(struct net2280_ep *ep, struct net2280_request *req) +{ + u32 tmp; + struct net2280_dma_regs __iomem *dma = ep->dma; + + /* FIXME can't use DMA for ZLPs */ + + /* on this path we "know" there's no dma active (yet) */ + WARN_ON(readl(&dma->dmactl) & BIT(DMA_ENABLE)); + writel(0, &ep->dma->dmactl); + + /* previous OUT packet might have been short */ + if (!ep->is_in && (readl(&ep->regs->ep_stat) & + BIT(NAK_OUT_PACKETS))) { + writel(BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT), + &ep->regs->ep_stat); + + tmp = readl(&ep->regs->ep_avail); + if (tmp) { + writel(readl(&dma->dmastat), &dma->dmastat); + + /* transfer all/some fifo data */ + writel(req->req.dma, &dma->dmaaddr); + tmp = min(tmp, req->req.length); + + /* dma irq, faking scatterlist status */ + req->td->dmacount = cpu_to_le32(req->req.length - tmp); + writel(BIT(DMA_DONE_INTERRUPT_ENABLE) | tmp, + &dma->dmacount); + req->td->dmadesc = 0; + req->valid = 1; + + writel(BIT(DMA_ENABLE), &dma->dmactl); + writel(BIT(DMA_START), &dma->dmastat); + return; + } + } + + tmp = dmactl_default; + + /* force packet boundaries between dma requests, but prevent the + * controller from automagically writing a last "short" packet + * (zero length) unless the driver explicitly said to do that. + */ + if (ep->is_in) { + if (likely((req->req.length % ep->ep.maxpacket) || + req->req.zero)){ + tmp |= BIT(DMA_FIFO_VALIDATE); + ep->in_fifo_validate = 1; + } else + ep->in_fifo_validate = 0; + } + + /* init req->td, pointing to the current dummy */ + req->td->dmadesc = cpu_to_le32 (ep->td_dma); + fill_dma_desc(ep, req, 1); + + if (!use_dma_chaining) + req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN)); + + start_queue(ep, tmp, req->td_dma); +} + +static inline void resume_dma(struct net2280_ep *ep) +{ + writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl); + + ep->dma_started = true; +} + +static inline void ep_stop_dma(struct net2280_ep *ep) +{ + writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl); + spin_stop_dma(ep->dma); + + ep->dma_started = false; +} + +static inline void +queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid) +{ + struct net2280_dma *end; + dma_addr_t tmp; + + /* swap new dummy for old, link; fill and maybe activate */ + end = ep->dummy; + ep->dummy = req->td; + req->td = end; + + tmp = ep->td_dma; + ep->td_dma = req->td_dma; + req->td_dma = tmp; + + end->dmadesc = cpu_to_le32 (ep->td_dma); + + fill_dma_desc(ep, req, valid); +} + +static void +done(struct net2280_ep *ep, struct net2280_request *req, int status) +{ + struct net2280 *dev; + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + dev = ep->dev; + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); + + if (status && status != -ESHUTDOWN) + ep_vdbg(dev, "complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&dev->lock); + usb_gadget_giveback_request(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->stopped = stopped; +} + +/*-------------------------------------------------------------------------*/ + +static int +net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct net2280_request *req; + struct net2280_ep *ep; + struct net2280 *dev; + unsigned long flags; + + /* we always require a cpu-view buffer, so that we can + * always use pio (as fallback or whatever). + */ + req = container_of(_req, struct net2280_request, req); + if (!_req || !_req->complete || !_req->buf || + !list_empty(&req->queue)) + return -EINVAL; + if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) + return -EDOM; + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -EINVAL; + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* FIXME implement PIO fallback for ZLPs with DMA */ + if (ep->dma && _req->length == 0) + return -EOPNOTSUPP; + + /* set up dma mapping in case the caller didn't */ + if (ep->dma) { + int ret; + + ret = usb_gadget_map_request(&dev->gadget, _req, + ep->is_in); + if (ret) + return ret; + } + +#if 0 + ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", + _ep->name, _req, _req->length, _req->buf); +#endif + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { + /* DMA request while EP halted */ + if (ep->dma && + (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) && + (dev->quirks & PLX_SUPERSPEED)) { + int valid = 1; + if (ep->is_in) { + int expect; + expect = likely(req->req.zero || + ((req->req.length % + ep->ep.maxpacket) != 0)); + if (expect != ep->in_fifo_validate) + valid = 0; + } + queue_dma(ep, req, valid); + } + /* use DMA if the endpoint supports it, else pio */ + else if (ep->dma) + start_dma(ep, req); + else { + /* maybe there's no control data, just status ack */ + if (ep->num == 0 && _req->length == 0) { + allow_status(ep); + done(ep, req, 0); + ep_vdbg(dev, "%s status ack\n", ep->ep.name); + goto done; + } + + /* PIO ... stuff the fifo, or unblock it. */ + if (ep->is_in) + write_fifo(ep, _req); + else if (list_empty(&ep->queue)) { + u32 s; + + /* OUT FIFO might have packet(s) buffered */ + s = readl(&ep->regs->ep_stat); + if ((s & BIT(FIFO_EMPTY)) == 0) { + /* note: _req->short_not_ok is + * ignored here since PIO _always_ + * stops queue advance here, and + * _req->status doesn't change for + * short reads (only _req->actual) + */ + if (read_fifo(ep, req) && + ep->num == 0) { + done(ep, req, 0); + allow_status(ep); + /* don't queue it */ + req = NULL; + } else if (read_fifo(ep, req) && + ep->num != 0) { + done(ep, req, 0); + req = NULL; + } else + s = readl(&ep->regs->ep_stat); + } + + /* don't NAK, let the fifo fill */ + if (req && (s & BIT(NAK_OUT_PACKETS))) + writel(BIT(CLEAR_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); + } + } + + } else if (ep->dma) { + int valid = 1; + + if (ep->is_in) { + int expect; + + /* preventing magic zlps is per-engine state, not + * per-transfer; irq logic must recover hiccups. + */ + expect = likely(req->req.zero || + (req->req.length % ep->ep.maxpacket)); + if (expect != ep->in_fifo_validate) + valid = 0; + } + queue_dma(ep, req, valid); + + } /* else the irq handler advances the queue. */ + + ep->responded = 1; + if (req) + list_add_tail(&req->queue, &ep->queue); +done: + spin_unlock_irqrestore(&dev->lock, flags); + + /* pci writes may still be posted */ + return 0; +} + +static inline void +dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, + int status) +{ + req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); + done(ep, req, status); +} + +static void restart_dma(struct net2280_ep *ep); + +static void scan_dma_completions(struct net2280_ep *ep) +{ + /* only look at descriptors that were "naturally" retired, + * so fifo and list head state won't matter + */ + while (!list_empty(&ep->queue)) { + struct net2280_request *req; + u32 tmp; + + req = list_entry(ep->queue.next, + struct net2280_request, queue); + if (!req->valid) + break; + rmb(); + tmp = le32_to_cpup(&req->td->dmacount); + if ((tmp & BIT(VALID_BIT)) != 0) + break; + + /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" + * cases where DMA must be aborted; this code handles + * all non-abort DMA completions. + */ + if (unlikely(req->td->dmadesc == 0)) { + /* paranoia */ + tmp = readl(&ep->dma->dmacount); + if (tmp & DMA_BYTE_COUNT_MASK) + break; + /* single transfer mode */ + dma_done(ep, req, tmp, 0); + break; + } else if (!ep->is_in && + (req->req.length % ep->ep.maxpacket) != 0) { + tmp = readl(&ep->regs->ep_stat); + if (ep->dev->quirks & PLX_SUPERSPEED) + return dma_done(ep, req, tmp, 0); + + /* AVOID TROUBLE HERE by not issuing short reads from + * your gadget driver. That helps avoids errata 0121, + * 0122, and 0124; not all cases trigger the warning. + */ + if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) { + ep_warn(ep->dev, "%s lost packet sync!\n", + ep->ep.name); + req->req.status = -EOVERFLOW; + } else { + tmp = readl(&ep->regs->ep_avail); + if (tmp) { + /* fifo gets flushed later */ + ep->out_overflow = 1; + ep_dbg(ep->dev, + "%s dma, discard %d len %d\n", + ep->ep.name, tmp, + req->req.length); + req->req.status = -EOVERFLOW; + } + } + } + dma_done(ep, req, tmp, 0); + } +} + +static void restart_dma(struct net2280_ep *ep) +{ + struct net2280_request *req; + u32 dmactl = dmactl_default; + + if (ep->stopped) + return; + req = list_entry(ep->queue.next, struct net2280_request, queue); + + if (!use_dma_chaining) { + start_dma(ep, req); + return; + } + + /* the 2280 will be processing the queue unless queue hiccups after + * the previous transfer: + * IN: wanted automagic zlp, head doesn't (or vice versa) + * DMA_FIFO_VALIDATE doesn't init from dma descriptors. + * OUT: was "usb-short", we must restart. + */ + if (ep->is_in && !req->valid) { + struct net2280_request *entry, *prev = NULL; + int reqmode, done = 0; + + ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); + ep->in_fifo_validate = likely(req->req.zero || + (req->req.length % ep->ep.maxpacket) != 0); + if (ep->in_fifo_validate) + dmactl |= BIT(DMA_FIFO_VALIDATE); + list_for_each_entry(entry, &ep->queue, queue) { + __le32 dmacount; + + if (entry == req) + continue; + dmacount = entry->td->dmacount; + if (!done) { + reqmode = likely(entry->req.zero || + (entry->req.length % ep->ep.maxpacket)); + if (reqmode == ep->in_fifo_validate) { + entry->valid = 1; + dmacount |= valid_bit; + entry->td->dmacount = dmacount; + prev = entry; + continue; + } else { + /* force a hiccup */ + prev->td->dmacount |= dma_done_ie; + done = 1; + } + } + + /* walk the rest of the queue so unlinks behave */ + entry->valid = 0; + dmacount &= ~valid_bit; + entry->td->dmacount = dmacount; + prev = entry; + } + } + + writel(0, &ep->dma->dmactl); + start_queue(ep, dmactl, req->td_dma); +} + +static void abort_dma_228x(struct net2280_ep *ep) +{ + /* abort the current transfer */ + if (likely(!list_empty(&ep->queue))) { + /* FIXME work around errata 0121, 0122, 0124 */ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); + } else + stop_dma(ep->dma); + scan_dma_completions(ep); +} + +static void abort_dma_338x(struct net2280_ep *ep) +{ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); +} + +static void abort_dma(struct net2280_ep *ep) +{ + if (ep->dev->quirks & PLX_LEGACY) + return abort_dma_228x(ep); + return abort_dma_338x(ep); +} + +/* dequeue ALL requests */ +static void nuke(struct net2280_ep *ep) +{ + struct net2280_request *req; + + /* called with spinlock held */ + ep->stopped = 1; + if (ep->dma) + abort_dma(ep); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2280_request, + queue); + done(ep, req, -ESHUTDOWN); + } +} + +/* dequeue JUST ONE request */ +static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2280_ep *ep; + struct net2280_request *req; + unsigned long flags; + u32 dmactl; + int stopped; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0) || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + stopped = ep->stopped; + + /* quiesce dma while we patch the queue */ + dmactl = 0; + ep->stopped = 1; + if (ep->dma) { + dmactl = readl(&ep->dma->dmactl); + /* WARNING erratum 0127 may kick in ... */ + stop_dma(ep->dma); + scan_dma_completions(ep); + } + + /* make sure it's still queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + /* queue head may be partially complete. */ + if (ep->queue.next == &req->queue) { + if (ep->dma) { + ep_dbg(ep->dev, "unlink (%s) dma\n", _ep->name); + _req->status = -ECONNRESET; + abort_dma(ep); + if (likely(ep->queue.next == &req->queue)) { + /* NOTE: misreports single-transfer mode*/ + req->td->dmacount = 0; /* invalidate */ + dma_done(ep, req, + readl(&ep->dma->dmacount), + -ECONNRESET); + } + } else { + ep_dbg(ep->dev, "unlink (%s) pio\n", _ep->name); + done(ep, req, -ECONNRESET); + } + req = NULL; + + /* patch up hardware chaining data */ + } else if (ep->dma && use_dma_chaining) { + if (req->queue.prev == ep->queue.next) { + writel(le32_to_cpu(req->td->dmadesc), + &ep->dma->dmadesc); + if (req->td->dmacount & dma_done_ie) + writel(readl(&ep->dma->dmacount) | + le32_to_cpu(dma_done_ie), + &ep->dma->dmacount); + } else { + struct net2280_request *prev; + + prev = list_entry(req->queue.prev, + struct net2280_request, queue); + prev->td->dmadesc = req->td->dmadesc; + if (req->td->dmacount & dma_done_ie) + prev->td->dmacount |= dma_done_ie; + } + } + + if (req) + done(ep, req, -ECONNRESET); + ep->stopped = stopped; + + if (ep->dma) { + /* turn off dma on inactive queues */ + if (list_empty(&ep->queue)) + stop_dma(ep->dma); + else if (!ep->stopped) { + /* resume current request, or start new one */ + if (req) + writel(dmactl, &ep->dma->dmactl); + else + start_dma(ep, list_entry(ep->queue.next, + struct net2280_request, queue)); + } + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int net2280_fifo_status(struct usb_ep *_ep); + +static int +net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) +{ + struct net2280_ep *ep; + unsigned long flags; + int retval = 0; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -EINVAL; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) + == USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + if (!list_empty(&ep->queue)) + retval = -EAGAIN; + else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) + retval = -EAGAIN; + else { + ep_vdbg(ep->dev, "%s %s %s\n", _ep->name, + value ? "set" : "clear", + wedged ? "wedge" : "halt"); + /* set/clear, then synch memory views with the device */ + if (value) { + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else + set_halt(ep); + if (wedged) + ep->wedged = 1; + } else { + clear_halt(ep); + if (ep->dev->quirks & PLX_SUPERSPEED && + !list_empty(&ep->queue) && ep->td_dma) + restart_dma(ep); + ep->wedged = 0; + } + (void) readl(&ep->regs->ep_rsp); + } + spin_unlock_irqrestore(&ep->dev->lock, flags); + + return retval; +} + +static int net2280_set_halt(struct usb_ep *_ep, int value) +{ + return net2280_set_halt_and_wedge(_ep, value, 0); +} + +static int net2280_set_wedge(struct usb_ep *_ep) +{ + if (!_ep || _ep->name == ep0name) + return -EINVAL; + return net2280_set_halt_and_wedge(_ep, 1, 1); +} + +static int net2280_fifo_status(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + u32 avail; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -ENODEV; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1); + if (avail > ep->fifo_size) + return -EOVERFLOW; + if (ep->is_in) + avail = ep->fifo_size - avail; + return avail; +} + +static void net2280_fifo_flush(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return; + + writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); + (void) readl(&ep->regs->ep_rsp); +} + +static const struct usb_ep_ops net2280_ep_ops = { + .enable = net2280_enable, + .disable = net2280_disable, + + .alloc_request = net2280_alloc_request, + .free_request = net2280_free_request, + + .queue = net2280_queue, + .dequeue = net2280_dequeue, + + .set_halt = net2280_set_halt, + .set_wedge = net2280_set_wedge, + .fifo_status = net2280_fifo_status, + .fifo_flush = net2280_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +static int net2280_get_frame(struct usb_gadget *_gadget) +{ + struct net2280 *dev; + unsigned long flags; + u16 retval; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2280, gadget); + spin_lock_irqsave(&dev->lock, flags); + retval = get_idx_reg(dev->regs, REG_FRAME) & 0x03ff; + spin_unlock_irqrestore(&dev->lock, flags); + return retval; +} + +static int net2280_wakeup(struct usb_gadget *_gadget) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + if (tmp & BIT(DEVICE_REMOTE_WAKEUP_ENABLE)) + writel(BIT(GENERATE_RESUME), &dev->usb->usbstat); + spin_unlock_irqrestore(&dev->lock, flags); + + /* pci writes may still be posted */ + return 0; +} + +static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + if (value) { + tmp |= BIT(SELF_POWERED_STATUS); + dev->selfpowered = 1; + } else { + tmp &= ~BIT(SELF_POWERED_STATUS); + dev->selfpowered = 0; + } + writel(tmp, &dev->usb->usbctl); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int net2280_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + dev->softconnect = (is_on != 0); + if (is_on) + tmp |= BIT(USB_DETECT_ENABLE); + else + tmp &= ~BIT(USB_DETECT_ENABLE); + writel(tmp, &dev->usb->usbctl); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); + +static const struct usb_gadget_ops net2280_ops = { + .get_frame = net2280_get_frame, + .wakeup = net2280_wakeup, + .set_selfpowered = net2280_set_selfpowered, + .pullup = net2280_pullup, + .udc_start = net2280_start, + .udc_stop = net2280_stop, +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +/* FIXME move these into procfs, and use seq_file. + * Sysfs _still_ doesn't behave for arbitrarily sized files, + * and also doesn't help products using this with 2.4 kernels. + */ + +/* "function" sysfs attribute */ +static ssize_t function_show(struct device *_dev, struct device_attribute *attr, + char *buf) +{ + struct net2280 *dev = dev_get_drvdata(_dev); + + if (!dev->driver || !dev->driver->function || + strlen(dev->driver->function) > PAGE_SIZE) + return 0; + return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); +} +static DEVICE_ATTR_RO(function); + +static ssize_t registers_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct net2280 *dev; + char *next; + unsigned size, t; + unsigned long flags; + int i; + u32 t1, t2; + const char *s; + + dev = dev_get_drvdata(_dev); + next = buf; + size = PAGE_SIZE; + spin_lock_irqsave(&dev->lock, flags); + + if (dev->driver) + s = dev->driver->driver.name; + else + s = "(none)"; + + /* Main Control Registers */ + t = scnprintf(next, size, "%s version " DRIVER_VERSION + ", chiprev %04x, dma %s\n\n" + "devinit %03x fifoctl %08x gadget '%s'\n" + "pci irqenb0 %02x irqenb1 %08x " + "irqstat0 %04x irqstat1 %08x\n", + driver_name, dev->chiprev, + use_dma + ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled", + readl(&dev->regs->devinit), + readl(&dev->regs->fifoctl), + s, + readl(&dev->regs->pciirqenb0), + readl(&dev->regs->pciirqenb1), + readl(&dev->regs->irqstat0), + readl(&dev->regs->irqstat1)); + size -= t; + next += t; + + /* USB Control Registers */ + t1 = readl(&dev->usb->usbctl); + t2 = readl(&dev->usb->usbstat); + if (t1 & BIT(VBUS_PIN)) { + if (t2 & BIT(HIGH_SPEED)) + s = "high speed"; + else if (dev->gadget.speed == USB_SPEED_UNKNOWN) + s = "powered"; + else + s = "full speed"; + /* full speed bit (6) not working?? */ + } else + s = "not attached"; + t = scnprintf(next, size, + "stdrsp %08x usbctl %08x usbstat %08x " + "addr 0x%02x (%s)\n", + readl(&dev->usb->stdrsp), t1, t2, + readl(&dev->usb->ouraddr), s); + size -= t; + next += t; + + /* PCI Master Control Registers */ + + /* DMA Control Registers */ + + /* Configurable EP Control Registers */ + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep; + + ep = &dev->ep[i]; + if (i && !ep->desc) + continue; + + t1 = readl(&ep->cfg->ep_cfg); + t2 = readl(&ep->regs->ep_rsp) & 0xff; + t = scnprintf(next, size, + "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" + "irqenb %02x\n", + ep->ep.name, t1, t2, + (t2 & BIT(CLEAR_NAK_OUT_PACKETS)) + ? "NAK " : "", + (t2 & BIT(CLEAR_EP_HIDE_STATUS_PHASE)) + ? "hide " : "", + (t2 & BIT(CLEAR_EP_FORCE_CRC_ERROR)) + ? "CRC " : "", + (t2 & BIT(CLEAR_INTERRUPT_MODE)) + ? "interrupt " : "", + (t2 & BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) + ? "status " : "", + (t2 & BIT(CLEAR_NAK_OUT_PACKETS_MODE)) + ? "NAKmode " : "", + (t2 & BIT(CLEAR_ENDPOINT_TOGGLE)) + ? "DATA1 " : "DATA0 ", + (t2 & BIT(CLEAR_ENDPOINT_HALT)) + ? "HALT " : "", + readl(&ep->regs->ep_irqenb)); + size -= t; + next += t; + + t = scnprintf(next, size, + "\tstat %08x avail %04x " + "(ep%d%s-%s)%s\n", + readl(&ep->regs->ep_stat), + readl(&ep->regs->ep_avail), + t1 & 0x0f, DIR_STRING(t1), + type_string(t1 >> 8), + ep->stopped ? "*" : ""); + size -= t; + next += t; + + if (!ep->dma) + continue; + + t = scnprintf(next, size, + " dma\tctl %08x stat %08x count %08x\n" + "\taddr %08x desc %08x\n", + readl(&ep->dma->dmactl), + readl(&ep->dma->dmastat), + readl(&ep->dma->dmacount), + readl(&ep->dma->dmaaddr), + readl(&ep->dma->dmadesc)); + size -= t; + next += t; + + } + + /* Indexed Registers (none yet) */ + + /* Statistics */ + t = scnprintf(next, size, "\nirqs: "); + size -= t; + next += t; + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep; + + ep = &dev->ep[i]; + if (i && !ep->irqs) + continue; + t = scnprintf(next, size, " %s/%lu", ep->ep.name, ep->irqs); + size -= t; + next += t; + + } + t = scnprintf(next, size, "\n"); + size -= t; + next += t; + + spin_unlock_irqrestore(&dev->lock, flags); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR_RO(registers); + +static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, + char *buf) +{ + struct net2280 *dev; + char *next; + unsigned size; + unsigned long flags; + int i; + + dev = dev_get_drvdata(_dev); + next = buf; + size = PAGE_SIZE; + spin_lock_irqsave(&dev->lock, flags); + + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep = &dev->ep[i]; + struct net2280_request *req; + int t; + + if (i != 0) { + const struct usb_endpoint_descriptor *d; + + d = ep->desc; + if (!d) + continue; + t = d->bEndpointAddress; + t = scnprintf(next, size, + "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", + ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, + (t & USB_DIR_IN) ? "in" : "out", + type_string(d->bmAttributes), + usb_endpoint_maxp(d) & 0x1fff, + ep->dma ? "dma" : "pio", ep->fifo_size + ); + } else /* ep0 should only have one transfer queued */ + t = scnprintf(next, size, "ep0 max 64 pio %s\n", + ep->is_in ? "in" : "out"); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "\t(nothing queued)\n"); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + continue; + } + list_for_each_entry(req, &ep->queue, queue) { + if (ep->dma && req->td_dma == readl(&ep->dma->dmadesc)) + t = scnprintf(next, size, + "\treq %p len %d/%d " + "buf %p (dmacount %08x)\n", + &req->req, req->req.actual, + req->req.length, req->req.buf, + readl(&ep->dma->dmacount)); + else + t = scnprintf(next, size, + "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + + if (ep->dma) { + struct net2280_dma *td; + + td = req->td; + t = scnprintf(next, size, "\t td %08x " + " count %08x buf %08x desc %08x\n", + (u32) req->td_dma, + le32_to_cpu(td->dmacount), + le32_to_cpu(td->dmaaddr), + le32_to_cpu(td->dmadesc)); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + } + } + } + +done: + spin_unlock_irqrestore(&dev->lock, flags); + return PAGE_SIZE - size; +} +static DEVICE_ATTR_RO(queues); + + +#else + +#define device_create_file(a, b) (0) +#define device_remove_file(a, b) do { } while (0) + +#endif + +/*-------------------------------------------------------------------------*/ + +/* another driver-specific mode might be a request type doing dma + * to/from another device fifo instead of to/from memory. + */ + +static void set_fifo_mode(struct net2280 *dev, int mode) +{ + /* keeping high bits preserves BAR2 */ + writel((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); + + /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + switch (mode) { + case 0: + list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[4].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024; + break; + case 1: + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 2048; + break; + case 2: + list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = 2048; + dev->ep[2].fifo_size = 1024; + break; + } + /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ + list_add_tail(&dev->ep[5].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[6].ep.ep_list, &dev->gadget.ep_list); +} + +static void defect7374_disable_data_eps(struct net2280 *dev) +{ + /* + * For Defect 7374, disable data EPs (and more): + * - This phase undoes the earlier phase of the Defect 7374 workaround, + * returing ep regs back to normal. + */ + struct net2280_ep *ep; + int i; + unsigned char ep_sel; + u32 tmp_reg; + + for (i = 1; i < 5; i++) { + ep = &dev->ep[i]; + writel(0, &ep->cfg->ep_cfg); + } + + /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */ + for (i = 0; i < 6; i++) + writel(0, &dev->dep[i].dep_cfg); + + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl); + + if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + /* Change settings on some selected endpoints */ + tmp_reg = readl(&dev->plregs->pl_ep_cfg_4); + tmp_reg &= ~BIT(NON_CTRL_IN_TOLERATE_BAD_DIR); + writel(tmp_reg, &dev->plregs->pl_ep_cfg_4); + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + tmp_reg |= BIT(EP_INITIALIZED); + writel(tmp_reg, &dev->plregs->pl_ep_ctrl); + } +} + +static void defect7374_enable_data_eps_zero(struct net2280 *dev) +{ + u32 tmp = 0, tmp_reg; + u32 fsmvalue, scratch; + int i; + unsigned char ep_sel; + + scratch = get_idx_reg(dev->regs, SCRATCH); + fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); + + /*See if firmware needs to set up for workaround*/ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { + ep_warn(dev, "Operate Defect 7374 workaround soft this time"); + ep_warn(dev, "It will operate on cold-reboot and SS connect"); + + /*GPEPs:*/ + tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | + (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | + ((dev->enhanced_mode) ? + BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | + BIT(IN_ENDPOINT_ENABLE)); + + for (i = 1; i < 5; i++) + writel(tmp, &dev->ep[i].cfg->ep_cfg); + + /* CSRIN, PCIIN, STATIN, RCIN*/ + tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE)); + writel(tmp, &dev->dep[1].dep_cfg); + writel(tmp, &dev->dep[3].dep_cfg); + writel(tmp, &dev->dep[4].dep_cfg); + writel(tmp, &dev->dep[5].dep_cfg); + + /*Implemented for development and debug. + * Can be refined/tuned later.*/ + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), + &dev->plregs->pl_ep_ctrl); + + if (ep_sel == 1) { + tmp = + (readl(&dev->plregs->pl_ep_ctrl) | + BIT(CLEAR_ACK_ERROR_CODE) | 0); + writel(tmp, &dev->plregs->pl_ep_ctrl); + continue; + } + + if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + tmp = (readl(&dev->plregs->pl_ep_cfg_4) | + BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0); + writel(tmp, &dev->plregs->pl_ep_cfg_4); + + tmp = readl(&dev->plregs->pl_ep_ctrl) & + ~BIT(EP_INITIALIZED); + writel(tmp, &dev->plregs->pl_ep_ctrl); + + } + + /* Set FSM to focus on the first Control Read: + * - Tip: Connection speed is known upon the first + * setup request.*/ + scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ; + set_idx_reg(dev->regs, SCRATCH, scratch); + + } else{ + ep_warn(dev, "Defect 7374 workaround soft will NOT operate"); + ep_warn(dev, "It will operate on cold-reboot and SS connect"); + } +} + +/* keeping it simple: + * - one bus driver, initted first; + * - one function driver, initted second + * + * most of the work to support multiple net2280 controllers would + * be to associate this gadget driver (yes?) with all of them, or + * perhaps to bind specific drivers to specific devices. + */ + +static void usb_reset_228x(struct net2280 *dev) +{ + u32 tmp; + + dev->gadget.speed = USB_SPEED_UNKNOWN; + (void) readl(&dev->usb->usbctl); + + net2280_led_init(dev); + + /* disable automatic responses, and irqs */ + writel(0, &dev->usb->stdrsp); + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + + /* clear old dma and irq state */ + for (tmp = 0; tmp < 4; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp + 1]; + if (ep->dma) + abort_dma(ep); + } + + writel(~0, &dev->regs->irqstat0), + writel(~(u32)BIT(SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), + + /* reset, and enable pci */ + tmp = readl(&dev->regs->devinit) | + BIT(PCI_ENABLE) | + BIT(FIFO_SOFT_RESET) | + BIT(USB_SOFT_RESET) | + BIT(M8051_RESET); + writel(tmp, &dev->regs->devinit); + + /* standard fifo and endpoint allocations */ + set_fifo_mode(dev, (fifo_mode <= 2) ? fifo_mode : 0); +} + +static void usb_reset_338x(struct net2280 *dev) +{ + u32 tmp; + u32 fsmvalue; + + dev->gadget.speed = USB_SPEED_UNKNOWN; + (void)readl(&dev->usb->usbctl); + + net2280_led_init(dev); + + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + /* See if firmware needs to set up for workaround: */ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { + ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__, + fsmvalue); + } else { + /* disable automatic responses, and irqs */ + writel(0, &dev->usb->stdrsp); + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + } + + /* clear old dma and irq state */ + for (tmp = 0; tmp < 4; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp + 1]; + + if (ep->dma) + abort_dma(ep); + } + + writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); + + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) { + /* reset, and enable pci */ + tmp = readl(&dev->regs->devinit) | + BIT(PCI_ENABLE) | + BIT(FIFO_SOFT_RESET) | + BIT(USB_SOFT_RESET) | + BIT(M8051_RESET); + + writel(tmp, &dev->regs->devinit); + } + + /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + + for (tmp = 1; tmp < dev->n_ep; tmp++) + list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list); + +} + +static void usb_reset(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return usb_reset_228x(dev); + return usb_reset_338x(dev); +} + +static void usb_reinit_228x(struct net2280 *dev) +{ + u32 tmp; + int init_dma; + + /* use_dma changes are ignored till next device re-init */ + init_dma = use_dma; + + /* basic endpoint init */ + for (tmp = 0; tmp < 7; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp]; + + ep->ep.name = ep_name[tmp]; + ep->dev = dev; + ep->num = tmp; + + if (tmp > 0 && tmp <= 4) { + ep->fifo_size = 1024; + if (init_dma) + ep->dma = &dev->dma[tmp - 1]; + } else + ep->fifo_size = 64; + ep->regs = &dev->epregs[tmp]; + ep->cfg = &dev->epregs[tmp]; + ep_reset_228x(dev->regs, ep); + } + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep[5].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep[6].ep, 64); + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + + /* we want to prevent lowlevel/insecure access from the USB host, + * but erratum 0119 means this enable bit is ignored + */ + for (tmp = 0; tmp < 5; tmp++) + writel(EP_DONTUSE, &dev->dep[tmp].dep_cfg); +} + +static void usb_reinit_338x(struct net2280 *dev) +{ + int init_dma; + int i; + u32 tmp, val; + u32 fsmvalue; + static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 }; + static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0xC0 }; + + /* use_dma changes are ignored till next device re-init */ + init_dma = use_dma; + + /* basic endpoint init */ + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep = &dev->ep[i]; + + ep->ep.name = ep_name[i]; + ep->dev = dev; + ep->num = i; + + if (i > 0 && i <= 4 && init_dma) + ep->dma = &dev->dma[i - 1]; + + if (dev->enhanced_mode) { + ep->cfg = &dev->epregs[ne[i]]; + ep->regs = (struct net2280_ep_regs __iomem *) + (((void __iomem *)&dev->epregs[ne[i]]) + + ep_reg_addr[i]); + ep->fiforegs = &dev->fiforegs[i]; + } else { + ep->cfg = &dev->epregs[i]; + ep->regs = &dev->epregs[i]; + ep->fiforegs = &dev->fiforegs[i]; + } + + ep->fifo_size = (i != 0) ? 2048 : 512; + + ep_reset_338x(dev->regs, ep); + } + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512); + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + + /* Link layer set up */ + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + /* See if driver needs to set up for workaround: */ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) + ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", + __func__, fsmvalue); + else { + tmp = readl(&dev->usb_ext->usbctl2) & + ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE)); + writel(tmp, &dev->usb_ext->usbctl2); + } + + /* Hardware Defect and Workaround */ + val = readl(&dev->ll_lfps_regs->ll_lfps_5); + val &= ~(0xf << TIMER_LFPS_6US); + val |= 0x5 << TIMER_LFPS_6US; + writel(val, &dev->ll_lfps_regs->ll_lfps_5); + + val = readl(&dev->ll_lfps_regs->ll_lfps_6); + val &= ~(0xffff << TIMER_LFPS_80US); + val |= 0x0100 << TIMER_LFPS_80US; + writel(val, &dev->ll_lfps_regs->ll_lfps_6); + + /* + * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB + * Hot Reset Exit Handshake may Fail in Specific Case using + * Default Register Settings. Workaround for Enumeration test. + */ + val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2); + val &= ~(0x1f << HOT_TX_NORESET_TS2); + val |= 0x10 << HOT_TX_NORESET_TS2; + writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2); + + val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3); + val &= ~(0x1f << HOT_RX_RESET_TS2); + val |= 0x3 << HOT_RX_RESET_TS2; + writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3); + + /* + * Set Recovery Idle to Recover bit: + * - On SS connections, setting Recovery Idle to Recover Fmw improves + * link robustness with various hosts and hubs. + * - It is safe to set for all connection speeds; all chip revisions. + * - R-M-W to leave other bits undisturbed. + * - Reference PLX TT-7372 + */ + val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit); + val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW); + writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit); + + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + + /* disable dedicated endpoints */ + writel(0x0D, &dev->dep[0].dep_cfg); + writel(0x0D, &dev->dep[1].dep_cfg); + writel(0x0E, &dev->dep[2].dep_cfg); + writel(0x0E, &dev->dep[3].dep_cfg); + writel(0x0F, &dev->dep[4].dep_cfg); + writel(0x0C, &dev->dep[5].dep_cfg); +} + +static void usb_reinit(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return usb_reinit_228x(dev); + return usb_reinit_338x(dev); +} + +static void ep0_start_228x(struct net2280 *dev) +{ + writel(BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), + &dev->epregs[0].ep_rsp); + + /* + * hardware optionally handles a bunch of standard requests + * that the API hides from drivers anyway. have it do so. + * endpoint status/features are handled in software, to + * help pass tests for some dubious behavior. + */ + writel(BIT(SET_TEST_MODE) | + BIT(SET_ADDRESS) | + BIT(DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) | + BIT(GET_DEVICE_STATUS) | + BIT(GET_INTERFACE_STATUS), + &dev->usb->stdrsp); + writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | + BIT(SELF_POWERED_USB_DEVICE) | + BIT(REMOTE_WAKEUP_SUPPORT) | + (dev->softconnect << USB_DETECT_ENABLE) | + BIT(SELF_POWERED_STATUS), + &dev->usb->usbctl); + + /* enable irqs so we can see ep0 and general operation */ + writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | + BIT(ENDPOINT_0_INTERRUPT_ENABLE), + &dev->regs->pciirqenb0); + writel(BIT(PCI_INTERRUPT_ENABLE) | + BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) | + BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) | + BIT(PCI_RETRY_ABORT_INTERRUPT_ENABLE) | + BIT(VBUS_INTERRUPT_ENABLE) | + BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | + BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE), + &dev->regs->pciirqenb1); + + /* don't leave any writes posted */ + (void) readl(&dev->usb->usbctl); +} + +static void ep0_start_338x(struct net2280 *dev) +{ + u32 fsmvalue; + + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) + ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__, + fsmvalue); + else + writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) | + BIT(SET_EP_HIDE_STATUS_PHASE), + &dev->epregs[0].ep_rsp); + + /* + * hardware optionally handles a bunch of standard requests + * that the API hides from drivers anyway. have it do so. + * endpoint status/features are handled in software, to + * help pass tests for some dubious behavior. + */ + writel(BIT(SET_ISOCHRONOUS_DELAY) | + BIT(SET_SEL) | + BIT(SET_TEST_MODE) | + BIT(SET_ADDRESS) | + BIT(GET_INTERFACE_STATUS) | + BIT(GET_DEVICE_STATUS), + &dev->usb->stdrsp); + dev->wakeup_enable = 1; + writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | + (dev->softconnect << USB_DETECT_ENABLE) | + BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + + /* enable irqs so we can see ep0 and general operation */ + writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | + BIT(ENDPOINT_0_INTERRUPT_ENABLE), + &dev->regs->pciirqenb0); + writel(BIT(PCI_INTERRUPT_ENABLE) | + BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | + BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) | + BIT(VBUS_INTERRUPT_ENABLE), + &dev->regs->pciirqenb1); + + /* don't leave any writes posted */ + (void)readl(&dev->usb->usbctl); +} + +static void ep0_start(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return ep0_start_228x(dev); + return ep0_start_338x(dev); +} + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) +{ + struct net2280 *dev; + int retval; + unsigned i; + + /* insist on high speed support from the driver, since + * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) + * "must not be used in normal operation" + */ + if (!driver || driver->max_speed < USB_SPEED_HIGH || + !driver->setup) + return -EINVAL; + + dev = container_of(_gadget, struct net2280, gadget); + + for (i = 0; i < dev->n_ep; i++) + dev->ep[i].irqs = 0; + + /* hook up the driver ... */ + dev->softconnect = 1; + driver->driver.bus = NULL; + dev->driver = driver; + + retval = device_create_file(&dev->pdev->dev, &dev_attr_function); + if (retval) + goto err_unbind; + retval = device_create_file(&dev->pdev->dev, &dev_attr_queues); + if (retval) + goto err_func; + + /* Enable force-full-speed testing mode, if desired */ + if (full_speed && (dev->quirks & PLX_LEGACY)) + writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag); + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + net2280_led_active(dev, 1); + + if (dev->quirks & PLX_SUPERSPEED) + defect7374_enable_data_eps_zero(dev); + + ep0_start(dev); + + ep_dbg(dev, "%s ready, usbctl %08x stdrsp %08x\n", + driver->driver.name, + readl(&dev->usb->usbctl), + readl(&dev->usb->stdrsp)); + + /* pci writes may still be posted */ + return 0; + +err_func: + device_remove_file(&dev->pdev->dev, &dev_attr_function); +err_unbind: + dev->driver = NULL; + return retval; +} + +static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect if it's not connected */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + /* stop hardware; prevent new request submissions; + * and kill any outstanding requests. + */ + usb_reset(dev); + for (i = 0; i < dev->n_ep; i++) + nuke(&dev->ep[i]); + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + usb_reinit(dev); +} + +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) +{ + struct net2280 *dev; + unsigned long flags; + + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + dev->driver = NULL; + + net2280_led_active(dev, 0); + + /* Disable full-speed test mode */ + if (dev->quirks & PLX_LEGACY) + writel(0, &dev->usb->xcvrdiag); + + device_remove_file(&dev->pdev->dev, &dev_attr_function); + device_remove_file(&dev->pdev->dev, &dev_attr_queues); + + ep_dbg(dev, "unregistered driver '%s'\n", + driver ? driver->driver.name : ""); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. + * also works for dma-capable endpoints, in pio mode or just + * to manually advance the queue after short OUT transfers. + */ +static void handle_ep_small(struct net2280_ep *ep) +{ + struct net2280_request *req; + u32 t; + /* 0 error, 1 mid-data, 2 done */ + int mode = 1; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct net2280_request, queue); + else + req = NULL; + + /* ack all, and handle what we care about */ + t = readl(&ep->regs->ep_stat); + ep->irqs++; +#if 0 + ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n", + ep->ep.name, t, req ? &req->req : 0); +#endif + if (!ep->is_in || (ep->dev->quirks & PLX_2280)) + writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat); + else + /* Added for 2282 */ + writel(t, &ep->regs->ep_stat); + + /* for ep0, monitor token irqs to catch data stage length errors + * and to synchronize on status. + * + * also, to defer reporting of protocol stalls ... here's where + * data or status first appears, handling stalls here should never + * cause trouble on the host side.. + * + * control requests could be slightly faster without token synch for + * status, but status can jam up that way. + */ + if (unlikely(ep->num == 0)) { + if (ep->is_in) { + /* status; stop NAKing */ + if (t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + } + if (!req) + allow_status(ep); + mode = 2; + /* reply to extra IN data tokens with a zlp */ + } else if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + mode = 2; + } else if (ep->responded && + !req && !ep->stopped) + write_fifo(ep, NULL); + } + } else { + /* status; stop NAKing */ + if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + } + mode = 2; + /* an extra OUT token is an error */ + } else if (((t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) && + req && + req->req.actual == req->req.length) || + (ep->responded && !req)) { + ep->dev->protocol_stall = 1; + set_halt(ep); + ep->stopped = 1; + if (req) + done(ep, req, -EOVERFLOW); + req = NULL; + } + } + } + + if (unlikely(!req)) + return; + + /* manual DMA queue advance after short OUT */ + if (likely(ep->dma)) { + if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { + u32 count; + int stopped = ep->stopped; + + /* TRANSFERRED works around OUT_DONE erratum 0112. + * we expect (N <= maxpacket) bytes; host wrote M. + * iff (M < N) we won't ever see a DMA interrupt. + */ + ep->stopped = 1; + for (count = 0; ; t = readl(&ep->regs->ep_stat)) { + + /* any preceding dma transfers must finish. + * dma handles (M >= N), may empty the queue + */ + scan_dma_completions(ep); + if (unlikely(list_empty(&ep->queue) || + ep->out_overflow)) { + req = NULL; + break; + } + req = list_entry(ep->queue.next, + struct net2280_request, queue); + + /* here either (M < N), a "real" short rx; + * or (M == N) and the queue didn't empty + */ + if (likely(t & BIT(FIFO_EMPTY))) { + count = readl(&ep->dma->dmacount); + count &= DMA_BYTE_COUNT_MASK; + if (readl(&ep->dma->dmadesc) + != req->td_dma) + req = NULL; + break; + } + udelay(1); + } + + /* stop DMA, leave ep NAKing */ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); + + if (likely(req)) { + req->td->dmacount = 0; + t = readl(&ep->regs->ep_avail); + dma_done(ep, req, count, + (ep->out_overflow || t) + ? -EOVERFLOW : 0); + } + + /* also flush to prevent erratum 0106 trouble */ + if (unlikely(ep->out_overflow || + (ep->dev->chiprev == 0x0100 && + ep->dev->gadget.speed + == USB_SPEED_FULL))) { + out_flush(ep); + ep->out_overflow = 0; + } + + /* (re)start dma if needed, stop NAKing */ + ep->stopped = stopped; + if (!list_empty(&ep->queue)) + restart_dma(ep); + } else + ep_dbg(ep->dev, "%s dma ep_stat %08x ??\n", + ep->ep.name, t); + return; + + /* data packet(s) received (in the fifo, OUT) */ + } else if (t & BIT(DATA_PACKET_RECEIVED_INTERRUPT)) { + if (read_fifo(ep, req) && ep->num != 0) + mode = 2; + + /* data packet(s) transmitted (IN) */ + } else if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) { + unsigned len; + + len = req->req.length - req->req.actual; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + req->req.actual += len; + + /* if we wrote it all, we're usually done */ + /* send zlps until the status stage */ + if ((req->req.actual == req->req.length) && + (!req->req.zero || len != ep->ep.maxpacket) && ep->num) + mode = 2; + + /* there was nothing to do ... */ + } else if (mode == 1) + return; + + /* done */ + if (mode == 2) { + /* stream endpoints often resubmit/unlink in completion */ + done(ep, req, 0); + + /* maybe advance queue to next request */ + if (ep->num == 0) { + /* NOTE: net2280 could let gadget driver start the + * status stage later. since not all controllers let + * them control that, the api doesn't (yet) allow it. + */ + if (!ep->stopped) + allow_status(ep); + req = NULL; + } else { + if (!list_empty(&ep->queue) && !ep->stopped) + req = list_entry(ep->queue.next, + struct net2280_request, queue); + else + req = NULL; + if (req && !ep->is_in) + stop_out_naking(ep); + } + } + + /* is there a buffer for the next packet? + * for best streaming performance, make sure there is one. + */ + if (req && !ep->stopped) { + + /* load IN fifo with next packet (may be zlp) */ + if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) + write_fifo(ep, &req->req); + } +} + +static struct net2280_ep *get_ep_by_addr(struct net2280 *dev, u16 wIndex) +{ + struct net2280_ep *ep; + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) + return &dev->ep[0]; + list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { + u8 bEndpointAddress; + + if (!ep->desc) + continue; + bEndpointAddress = ep->desc->bEndpointAddress; + if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) + continue; + if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) + return ep; + } + return NULL; +} + +static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r) +{ + u32 scratch, fsmvalue; + u32 ack_wait_timeout, state; + + /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */ + scratch = get_idx_reg(dev->regs, SCRATCH); + fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); + + if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) && + (r.bRequestType & USB_DIR_IN))) + return; + + /* This is the first Control Read for this connection: */ + if (!(readl(&dev->usb->usbstat) & BIT(SUPER_SPEED_MODE))) { + /* + * Connection is NOT SS: + * - Connection must be FS or HS. + * - This FSM state should allow workaround software to + * run after the next USB connection. + */ + scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ; + goto restore_data_eps; + } + + /* Connection is SS: */ + for (ack_wait_timeout = 0; + ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS; + ack_wait_timeout++) { + + state = readl(&dev->plregs->pl_ep_status_1) + & (0xff << STATE); + if ((state >= (ACK_GOOD_NORMAL << STATE)) && + (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) { + scratch |= DEFECT7374_FSM_SS_CONTROL_READ; + break; + } + + /* + * We have not yet received host's Data Phase ACK + * - Wait and try again. + */ + udelay(DEFECT_7374_PROCESSOR_WAIT_TIME); + + continue; + } + + + if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) { + ep_err(dev, "FAIL: Defect 7374 workaround waited but failed " + "to detect SS host's data phase ACK."); + ep_err(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16" + "got 0x%2.2x.\n", state >> STATE); + } else { + ep_warn(dev, "INFO: Defect 7374 workaround waited about\n" + "%duSec for Control Read Data Phase ACK\n", + DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout); + } + +restore_data_eps: + /* + * Restore data EPs to their pre-workaround settings (disabled, + * initialized, and other details). + */ + defect7374_disable_data_eps(dev); + + set_idx_reg(dev->regs, SCRATCH, scratch); + + return; +} + +static void ep_stall(struct net2280_ep *ep, int stall) +{ + struct net2280 *dev = ep->dev; + u32 val; + static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 }; + + if (stall) { + writel(BIT(SET_ENDPOINT_HALT) | + /* BIT(SET_NAK_PACKETS) | */ + BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), + &ep->regs->ep_rsp); + ep->is_halt = 1; + } else { + if (dev->gadget.speed == USB_SPEED_SUPER) { + /* + * Workaround for SS SeqNum not cleared via + * Endpoint Halt (Clear) bit. select endpoint + */ + val = readl(&dev->plregs->pl_ep_ctrl); + val = (val & ~0x1f) | ep_pl[ep->num]; + writel(val, &dev->plregs->pl_ep_ctrl); + + val |= BIT(SEQUENCE_NUMBER_RESET); + writel(val, &dev->plregs->pl_ep_ctrl); + } + val = readl(&ep->regs->ep_rsp); + val |= BIT(CLEAR_ENDPOINT_HALT) | + BIT(CLEAR_ENDPOINT_TOGGLE); + writel(val, + /* | BIT(CLEAR_NAK_PACKETS),*/ + &ep->regs->ep_rsp); + ep->is_halt = 0; + val = readl(&ep->regs->ep_rsp); + } +} + +static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged) +{ + /* set/clear, then synch memory views with the device */ + if (value) { + ep->stopped = 1; + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else { + if (ep->dma) + ep_stop_dma(ep); + ep_stall(ep, true); + } + + if (wedged) + ep->wedged = 1; + } else { + ep->stopped = 0; + ep->wedged = 0; + + ep_stall(ep, false); + + /* Flush the queue */ + if (!list_empty(&ep->queue)) { + struct net2280_request *req = + list_entry(ep->queue.next, struct net2280_request, + queue); + if (ep->dma) + resume_dma(ep); + else { + if (ep->is_in) + write_fifo(ep, &req->req); + else { + if (read_fifo(ep, req)) + done(ep, req, 0); + } + } + } + } +} + +static void handle_stat0_irqs_superspeed(struct net2280 *dev, + struct net2280_ep *ep, struct usb_ctrlrequest r) +{ + int tmp = 0; + +#define w_value le16_to_cpu(r.wValue) +#define w_index le16_to_cpu(r.wIndex) +#define w_length le16_to_cpu(r.wLength) + + switch (r.bRequest) { + struct net2280_ep *e; + u16 status; + + case USB_REQ_SET_CONFIGURATION: + dev->addressed_state = !w_value; + goto usb3_delegate; + + case USB_REQ_GET_STATUS: + switch (r.bRequestType) { + case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + status = dev->wakeup_enable ? 0x02 : 0x00; + if (dev->selfpowered) + status |= BIT(0); + status |= (dev->u1_enable << 2 | dev->u2_enable << 3 | + dev->ltm_enable << 4); + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, sizeof(status)); + writel((__force u32) status, &dev->epregs[0].ep_data); + allow_status_338x(ep); + break; + + case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall3; + status = readl(&e->regs->ep_rsp) & + BIT(CLEAR_ENDPOINT_HALT); + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, sizeof(status)); + writel((__force u32) status, &dev->epregs[0].ep_data); + allow_status_338x(ep); + break; + + default: + goto usb3_delegate; + } + break; + + case USB_REQ_CLEAR_FEATURE: + switch (r.bRequestType) { + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (!dev->addressed_state) { + switch (w_value) { + case USB_DEVICE_U1_ENABLE: + dev->u1_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(U1_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_U2_ENABLE: + dev->u2_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(U2_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_LTM_ENABLE: + dev->ltm_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(LTM_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + default: + break; + } + } + if (w_value == USB_DEVICE_REMOTE_WAKEUP) { + dev->wakeup_enable = 0; + writel(readl(&dev->usb->usbctl) & + ~BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + allow_status_338x(ep); + break; + } + goto usb3_delegate; + + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall3; + if (w_value != USB_ENDPOINT_HALT) + goto do_stall3; + ep_vdbg(dev, "%s clear halt\n", e->ep.name); + ep_stall(e, false); + if (!list_empty(&e->queue) && e->td_dma) + restart_dma(e); + allow_status(ep); + ep->stopped = 1; + break; + + default: + goto usb3_delegate; + } + break; + case USB_REQ_SET_FEATURE: + switch (r.bRequestType) { + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (!dev->addressed_state) { + switch (w_value) { + case USB_DEVICE_U1_ENABLE: + dev->u1_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(U1_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_U2_ENABLE: + dev->u2_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(U2_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_LTM_ENABLE: + dev->ltm_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(LTM_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + default: + break; + } + } + + if (w_value == USB_DEVICE_REMOTE_WAKEUP) { + dev->wakeup_enable = 1; + writel(readl(&dev->usb->usbctl) | + BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + allow_status_338x(ep); + break; + } + goto usb3_delegate; + + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e || (w_value != USB_ENDPOINT_HALT)) + goto do_stall3; + ep_stdrsp(e, true, false); + allow_status_338x(ep); + break; + + default: + goto usb3_delegate; + } + + break; + default: + +usb3_delegate: + ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n", + r.bRequestType, r.bRequest, + w_value, w_index, w_length, + readl(&ep->cfg->ep_cfg)); + + ep->responded = 0; + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &r); + spin_lock(&dev->lock); + } +do_stall3: + if (tmp < 0) { + ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", + r.bRequestType, r.bRequest, tmp); + dev->protocol_stall = 1; + /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */ + ep_stall(ep, true); + } + +next_endpoints3: + +#undef w_value +#undef w_index +#undef w_length + + return; +} + +static void handle_stat0_irqs(struct net2280 *dev, u32 stat) +{ + struct net2280_ep *ep; + u32 num, scratch; + + /* most of these don't need individual acks */ + stat &= ~BIT(INTA_ASSERTED); + if (!stat) + return; + /* ep_dbg(dev, "irqstat0 %04x\n", stat); */ + + /* starting a control request? */ + if (unlikely(stat & BIT(SETUP_PACKET_INTERRUPT))) { + union { + u32 raw[2]; + struct usb_ctrlrequest r; + } u; + int tmp; + struct net2280_request *req; + + if (dev->gadget.speed == USB_SPEED_UNKNOWN) { + u32 val = readl(&dev->usb->usbstat); + if (val & BIT(SUPER_SPEED)) { + dev->gadget.speed = USB_SPEED_SUPER; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_SS_MAX_PACKET_SIZE); + } else if (val & BIT(HIGH_SPEED)) { + dev->gadget.speed = USB_SPEED_HIGH; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_HS_MAX_PACKET_SIZE); + } else { + dev->gadget.speed = USB_SPEED_FULL; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_HS_MAX_PACKET_SIZE); + } + net2280_led_speed(dev, dev->gadget.speed); + ep_dbg(dev, "%s\n", + usb_speed_string(dev->gadget.speed)); + } + + ep = &dev->ep[0]; + ep->irqs++; + + /* make sure any leftover request state is cleared */ + stat &= ~BIT(ENDPOINT_0_INTERRUPT); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2280_request, queue); + done(ep, req, (req->req.actual == req->req.length) + ? 0 : -EPROTO); + } + ep->stopped = 0; + dev->protocol_stall = 0; + if (dev->quirks & PLX_SUPERSPEED) + ep->is_halt = 0; + else{ + if (ep->dev->quirks & PLX_2280) + tmp = BIT(FIFO_OVERFLOW) | + BIT(FIFO_UNDERFLOW); + else + tmp = 0; + + writel(tmp | BIT(TIMEOUT) | + BIT(USB_STALL_SENT) | + BIT(USB_IN_NAK_SENT) | + BIT(USB_IN_ACK_RCVD) | + BIT(USB_OUT_PING_NAK_SENT) | + BIT(USB_OUT_ACK_SENT) | + BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), + &ep->regs->ep_stat); + } + u.raw[0] = readl(&dev->usb->setup0123); + u.raw[1] = readl(&dev->usb->setup4567); + + cpu_to_le32s(&u.raw[0]); + cpu_to_le32s(&u.raw[1]); + + if (dev->quirks & PLX_SUPERSPEED) + defect7374_workaround(dev, u.r); + + tmp = 0; + +#define w_value le16_to_cpu(u.r.wValue) +#define w_index le16_to_cpu(u.r.wIndex) +#define w_length le16_to_cpu(u.r.wLength) + + /* ack the irq */ + writel(BIT(SETUP_PACKET_INTERRUPT), &dev->regs->irqstat0); + stat ^= BIT(SETUP_PACKET_INTERRUPT); + + /* watch control traffic at the token level, and force + * synchronization before letting the status stage happen. + * FIXME ignore tokens we'll NAK, until driver responds. + * that'll mean a lot less irqs for some drivers. + */ + ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; + if (ep->is_in) { + scratch = BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT); + stop_out_naking(ep); + } else + scratch = BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT); + writel(scratch, &dev->epregs[0].ep_irqenb); + + /* we made the hardware handle most lowlevel requests; + * everything else goes uplevel to the gadget code. + */ + ep->responded = 1; + + if (dev->gadget.speed == USB_SPEED_SUPER) { + handle_stat0_irqs_superspeed(dev, ep, u.r); + goto next_endpoints; + } + + switch (u.r.bRequest) { + case USB_REQ_GET_STATUS: { + struct net2280_ep *e; + __le32 status; + + /* hw handles device and interface status */ + if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) + goto delegate; + e = get_ep_by_addr(dev, w_index); + if (!e || w_length > 2) + goto do_stall; + + if (readl(&e->regs->ep_rsp) & BIT(SET_ENDPOINT_HALT)) + status = cpu_to_le32(1); + else + status = cpu_to_le32(0); + + /* don't bother with a request object! */ + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, w_length); + writel((__force u32)status, &dev->epregs[0].ep_data); + allow_status(ep); + ep_vdbg(dev, "%s stat %02x\n", ep->ep.name, status); + goto next_endpoints; + } + break; + case USB_REQ_CLEAR_FEATURE: { + struct net2280_ep *e; + + /* hw handles device features */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (w_value != USB_ENDPOINT_HALT || w_length != 0) + goto do_stall; + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall; + if (e->wedged) { + ep_vdbg(dev, "%s wedged, halt not cleared\n", + ep->ep.name); + } else { + ep_vdbg(dev, "%s clear halt\n", e->ep.name); + clear_halt(e); + if ((ep->dev->quirks & PLX_SUPERSPEED) && + !list_empty(&e->queue) && e->td_dma) + restart_dma(e); + } + allow_status(ep); + goto next_endpoints; + } + break; + case USB_REQ_SET_FEATURE: { + struct net2280_ep *e; + + /* hw handles device features */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (w_value != USB_ENDPOINT_HALT || w_length != 0) + goto do_stall; + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall; + if (e->ep.name == ep0name) + goto do_stall; + set_halt(e); + if ((dev->quirks & PLX_SUPERSPEED) && e->dma) + abort_dma(e); + allow_status(ep); + ep_vdbg(dev, "%s set halt\n", ep->ep.name); + goto next_endpoints; + } + break; + default: +delegate: + ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x " + "ep_cfg %08x\n", + u.r.bRequestType, u.r.bRequest, + w_value, w_index, w_length, + readl(&ep->cfg->ep_cfg)); + ep->responded = 0; + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &u.r); + spin_lock(&dev->lock); + } + + /* stall ep0 on error */ + if (tmp < 0) { +do_stall: + ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", + u.r.bRequestType, u.r.bRequest, tmp); + dev->protocol_stall = 1; + } + + /* some in/out token irq should follow; maybe stall then. + * driver must queue a request (even zlp) or halt ep0 + * before the host times out. + */ + } + +#undef w_value +#undef w_index +#undef w_length + +next_endpoints: + /* endpoint data irq ? */ + scratch = stat & 0x7f; + stat &= ~0x7f; + for (num = 0; scratch; num++) { + u32 t; + + /* do this endpoint's FIFO and queue need tending? */ + t = BIT(num); + if ((scratch & t) == 0) + continue; + scratch ^= t; + + ep = &dev->ep[num]; + handle_ep_small(ep); + } + + if (stat) + ep_dbg(dev, "unhandled irqstat0 %08x\n", stat); +} + +#define DMA_INTERRUPTS (BIT(DMA_D_INTERRUPT) | \ + BIT(DMA_C_INTERRUPT) | \ + BIT(DMA_B_INTERRUPT) | \ + BIT(DMA_A_INTERRUPT)) +#define PCI_ERROR_INTERRUPTS ( \ + BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT) | \ + BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT) | \ + BIT(PCI_RETRY_ABORT_INTERRUPT)) + +static void handle_stat1_irqs(struct net2280 *dev, u32 stat) +{ + struct net2280_ep *ep; + u32 tmp, num, mask, scratch; + + /* after disconnect there's nothing else to do! */ + tmp = BIT(VBUS_INTERRUPT) | BIT(ROOT_PORT_RESET_INTERRUPT); + mask = BIT(SUPER_SPEED) | BIT(HIGH_SPEED) | BIT(FULL_SPEED); + + /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. + * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and + * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT + * only indicates a change in the reset state). + */ + if (stat & tmp) { + writel(tmp, &dev->regs->irqstat1); + if ((((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) && + ((readl(&dev->usb->usbstat) & mask) == 0)) || + ((readl(&dev->usb->usbctl) & + BIT(VBUS_PIN)) == 0)) && + (dev->gadget.speed != USB_SPEED_UNKNOWN)) { + ep_dbg(dev, "disconnect %s\n", + dev->driver->driver.name); + stop_activity(dev, dev->driver); + ep0_start(dev); + return; + } + stat &= ~tmp; + + /* vBUS can bounce ... one of many reasons to ignore the + * notion of hotplug events on bus connect/disconnect! + */ + if (!stat) + return; + } + + /* NOTE: chip stays in PCI D0 state for now, but it could + * enter D1 to save more power + */ + tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT); + if (stat & tmp) { + writel(tmp, &dev->regs->irqstat1); + if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) { + if (dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + if (!enable_suspend) + stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT); + } else { + if (dev->driver->resume) + dev->driver->resume(&dev->gadget); + /* at high speed, note erratum 0133 */ + } + stat &= ~tmp; + } + + /* clear any other status/irqs */ + if (stat) + writel(stat, &dev->regs->irqstat1); + + /* some status we can just ignore */ + if (dev->quirks & PLX_2280) + stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | + BIT(SUSPEND_REQUEST_INTERRUPT) | + BIT(RESUME_INTERRUPT) | + BIT(SOF_INTERRUPT)); + else + stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | + BIT(RESUME_INTERRUPT) | + BIT(SOF_DOWN_INTERRUPT) | + BIT(SOF_INTERRUPT)); + + if (!stat) + return; + /* ep_dbg(dev, "irqstat1 %08x\n", stat);*/ + + /* DMA status, for ep-{a,b,c,d} */ + scratch = stat & DMA_INTERRUPTS; + stat &= ~DMA_INTERRUPTS; + scratch >>= 9; + for (num = 0; scratch; num++) { + struct net2280_dma_regs __iomem *dma; + + tmp = BIT(num); + if ((tmp & scratch) == 0) + continue; + scratch ^= tmp; + + ep = &dev->ep[num + 1]; + dma = ep->dma; + + if (!dma) + continue; + + /* clear ep's dma status */ + tmp = readl(&dma->dmastat); + writel(tmp, &dma->dmastat); + + /* dma sync*/ + if (dev->quirks & PLX_SUPERSPEED) { + u32 r_dmacount = readl(&dma->dmacount); + if (!ep->is_in && (r_dmacount & 0x00FFFFFF) && + (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) + continue; + } + + /* chaining should stop on abort, short OUT from fifo, + * or (stat0 codepath) short OUT transfer. + */ + if (!use_dma_chaining) { + if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) { + ep_dbg(ep->dev, "%s no xact done? %08x\n", + ep->ep.name, tmp); + continue; + } + stop_dma(ep->dma); + } + + /* OUT transfers terminate when the data from the + * host is in our memory. Process whatever's done. + * On this path, we know transfer's last packet wasn't + * less than req->length. NAK_OUT_PACKETS may be set, + * or the FIFO may already be holding new packets. + * + * IN transfers can linger in the FIFO for a very + * long time ... we ignore that for now, accounting + * precisely (like PIO does) needs per-packet irqs + */ + scan_dma_completions(ep); + + /* disable dma on inactive queues; else maybe restart */ + if (list_empty(&ep->queue)) { + if (use_dma_chaining) + stop_dma(ep->dma); + } else { + tmp = readl(&dma->dmactl); + if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0) + restart_dma(ep); + else if (ep->is_in && use_dma_chaining) { + struct net2280_request *req; + __le32 dmacount; + + /* the descriptor at the head of the chain + * may still have VALID_BIT clear; that's + * used to trigger changing DMA_FIFO_VALIDATE + * (affects automagic zlp writes). + */ + req = list_entry(ep->queue.next, + struct net2280_request, queue); + dmacount = req->td->dmacount; + dmacount &= cpu_to_le32(BIT(VALID_BIT) | + DMA_BYTE_COUNT_MASK); + if (dmacount && (dmacount & valid_bit) == 0) + restart_dma(ep); + } + } + ep->irqs++; + } + + /* NOTE: there are other PCI errors we might usefully notice. + * if they appear very often, here's where to try recovering. + */ + if (stat & PCI_ERROR_INTERRUPTS) { + ep_err(dev, "pci dma error; stat %08x\n", stat); + stat &= ~PCI_ERROR_INTERRUPTS; + /* these are fatal errors, but "maybe" they won't + * happen again ... + */ + stop_activity(dev, dev->driver); + ep0_start(dev); + stat = 0; + } + + if (stat) + ep_dbg(dev, "unhandled irqstat1 %08x\n", stat); +} + +static irqreturn_t net2280_irq(int irq, void *_dev) +{ + struct net2280 *dev = _dev; + + /* shared interrupt, not ours */ + if ((dev->quirks & PLX_LEGACY) && + (!(readl(&dev->regs->irqstat0) & BIT(INTA_ASSERTED)))) + return IRQ_NONE; + + spin_lock(&dev->lock); + + /* handle disconnect, dma, and more */ + handle_stat1_irqs(dev, readl(&dev->regs->irqstat1)); + + /* control requests and PIO */ + handle_stat0_irqs(dev, readl(&dev->regs->irqstat0)); + + if (dev->quirks & PLX_SUPERSPEED) { + /* re-enable interrupt to trigger any possible new interrupt */ + u32 pciirqenb1 = readl(&dev->regs->pciirqenb1); + writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1); + writel(pciirqenb1, &dev->regs->pciirqenb1); + } + + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static void gadget_release(struct device *_dev) +{ + struct net2280 *dev = dev_get_drvdata(_dev); + + kfree(dev); +} + +/* tear down the binding between this driver and the pci device */ + +static void net2280_remove(struct pci_dev *pdev) +{ + struct net2280 *dev = pci_get_drvdata(pdev); + + usb_del_gadget_udc(&dev->gadget); + + BUG_ON(dev->driver); + + /* then clean up the resources we allocated during probe() */ + net2280_led_shutdown(dev); + if (dev->requests) { + int i; + for (i = 1; i < 5; i++) { + if (!dev->ep[i].dummy) + continue; + pci_pool_free(dev->requests, dev->ep[i].dummy, + dev->ep[i].td_dma); + } + pci_pool_destroy(dev->requests); + } + if (dev->got_irq) + free_irq(pdev->irq, dev); + if (use_msi && dev->quirks & PLX_SUPERSPEED) + pci_disable_msi(pdev); + if (dev->regs) + iounmap(dev->regs); + if (dev->region) + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (dev->enabled) + pci_disable_device(pdev); + device_remove_file(&pdev->dev, &dev_attr_registers); + + ep_info(dev, "unbind\n"); +} + +/* wrap this driver around the specified device, but + * don't respond over USB until a gadget driver binds to us. + */ + +static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net2280 *dev; + unsigned long resource, len; + void __iomem *base = NULL; + int retval, i; + + if (!use_dma) + use_dma_chaining = 0; + + /* alloc, and start init */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + retval = -ENOMEM; + goto done; + } + + pci_set_drvdata(pdev, dev); + spin_lock_init(&dev->lock); + dev->quirks = id->driver_data; + dev->pdev = pdev; + dev->gadget.ops = &net2280_ops; + dev->gadget.max_speed = (dev->quirks & PLX_SUPERSPEED) ? + USB_SPEED_SUPER : USB_SPEED_HIGH; + + /* the "gadget" abstracts/virtualizes the controller */ + dev->gadget.name = driver_name; + + /* now all the pci goodies ... */ + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto done; + } + dev->enabled = 1; + + /* BAR 0 holds all the registers + * BAR 1 is 8051 memory; unused here (note erratum 0103) + * BAR 2 is fifo memory; unused here + */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!request_mem_region(resource, len, driver_name)) { + ep_dbg(dev, "controller already in use\n"); + retval = -EBUSY; + goto done; + } + dev->region = 1; + + /* FIXME provide firmware download interface to put + * 8051 code into the chip, e.g. to turn on PCI PM. + */ + + base = ioremap_nocache(resource, len); + if (base == NULL) { + ep_dbg(dev, "can't map memory\n"); + retval = -EFAULT; + goto done; + } + dev->regs = (struct net2280_regs __iomem *) base; + dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080); + dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100); + dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180); + dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); + dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); + + if (dev->quirks & PLX_SUPERSPEED) { + u32 fsmvalue; + u32 usbstat; + dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) + (base + 0x00b4); + dev->fiforegs = (struct usb338x_fifo_regs __iomem *) + (base + 0x0500); + dev->llregs = (struct usb338x_ll_regs __iomem *) + (base + 0x0700); + dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) + (base + 0x0748); + dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *) + (base + 0x077c); + dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *) + (base + 0x079c); + dev->plregs = (struct usb338x_pl_regs __iomem *) + (base + 0x0800); + usbstat = readl(&dev->usb->usbstat); + dev->enhanced_mode = !!(usbstat & BIT(11)); + dev->n_ep = (dev->enhanced_mode) ? 9 : 5; + /* put into initial config, link up all endpoints */ + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + /* See if firmware needs to set up for workaround: */ + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) + writel(0, &dev->usb->usbctl); + } else{ + dev->enhanced_mode = 0; + dev->n_ep = 7; + /* put into initial config, link up all endpoints */ + writel(0, &dev->usb->usbctl); + } + + usb_reset(dev); + usb_reinit(dev); + + /* irq setup after old hardware is cleaned up */ + if (!pdev->irq) { + ep_err(dev, "No IRQ. Check PCI setup!\n"); + retval = -ENODEV; + goto done; + } + + if (use_msi && (dev->quirks & PLX_SUPERSPEED)) + if (pci_enable_msi(pdev)) + ep_err(dev, "Failed to enable MSI mode\n"); + + if (request_irq(pdev->irq, net2280_irq, IRQF_SHARED, + driver_name, dev)) { + ep_err(dev, "request interrupt %d failed\n", pdev->irq); + retval = -EBUSY; + goto done; + } + dev->got_irq = 1; + + /* DMA setup */ + /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ + dev->requests = pci_pool_create("requests", pdev, + sizeof(struct net2280_dma), + 0 /* no alignment requirements */, + 0 /* or page-crossing issues */); + if (!dev->requests) { + ep_dbg(dev, "can't get request pool\n"); + retval = -ENOMEM; + goto done; + } + for (i = 1; i < 5; i++) { + struct net2280_dma *td; + + td = pci_pool_alloc(dev->requests, GFP_KERNEL, + &dev->ep[i].td_dma); + if (!td) { + ep_dbg(dev, "can't get dummy %d\n", i); + retval = -ENOMEM; + goto done; + } + td->dmacount = 0; /* not VALID */ + td->dmadesc = td->dmaaddr; + dev->ep[i].dummy = td; + } + + /* enable lower-overhead pci memory bursts during DMA */ + if (dev->quirks & PLX_LEGACY) + writel(BIT(DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) | + /* + * 256 write retries may not be enough... + BIT(PCI_RETRY_ABORT_ENABLE) | + */ + BIT(DMA_READ_MULTIPLE_ENABLE) | + BIT(DMA_READ_LINE_ENABLE), + &dev->pci->pcimstctl); + /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + /* ... also flushes any posted pci writes */ + dev->chiprev = get_idx_reg(dev->regs, REG_CHIPREV) & 0xffff; + + /* done */ + ep_info(dev, "%s\n", driver_desc); + ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n", + pdev->irq, base, dev->chiprev); + ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n", + use_dma ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled", + dev->enhanced_mode ? "enhanced mode" : "legacy mode"); + retval = device_create_file(&pdev->dev, &dev_attr_registers); + if (retval) + goto done; + + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); + if (retval) + goto done; + return 0; + +done: + if (dev) + net2280_remove(pdev); + return retval; +} + +/* make sure the board is quiescent; otherwise it will continue + * generating IRQs across the upcoming reboot. + */ + +static void net2280_shutdown(struct pci_dev *pdev) +{ + struct net2280 *dev = pci_get_drvdata(pdev); + + /* disable IRQs */ + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + + /* disable the pullup so the host will think we're gone */ + writel(0, &dev->usb->usbctl); + + /* Disable full-speed test mode */ + if (dev->quirks & PLX_LEGACY) + writel(0, &dev->usb->xcvrdiag); +} + + +/*-------------------------------------------------------------------------*/ + +static const struct pci_device_id pci_ids[] = { { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX_LEGACY, + .device = 0x2280, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_LEGACY | PLX_2280, + }, { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX_LEGACY, + .device = 0x2282, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_LEGACY, + }, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX, + .device = 0x3380, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_SUPERSPEED, + }, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX, + .device = 0x3382, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_SUPERSPEED, + }, +{ /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +/* pci driver glue; this is a "new style" PCI driver module */ +static struct pci_driver net2280_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_ids, + + .probe = net2280_probe, + .remove = net2280_remove, + .shutdown = net2280_shutdown, + + /* FIXME add power management support */ +}; + +module_pci_driver(net2280_pci_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h new file mode 100644 index 000000000000..03f15242d794 --- /dev/null +++ b/drivers/usb/gadget/udc/net2280.h @@ -0,0 +1,403 @@ +/* + * NetChip 2280 high/full speed USB device controller. + * Unlike many such controllers, this one talks PCI. + */ + +/* + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS + * + * 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. + */ + +#include <linux/usb/net2280.h> +#include <linux/usb/usb338x.h> + +/*-------------------------------------------------------------------------*/ + +#ifdef __KERNEL__ + +/* indexed registers [11.10] are accessed indirectly + * caller must own the device lock. + */ + +static inline u32 get_idx_reg(struct net2280_regs __iomem *regs, u32 index) +{ + writel(index, ®s->idxaddr); + /* NOTE: synchs device/cpu memory views */ + return readl(®s->idxdata); +} + +static inline void +set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value) +{ + writel(index, ®s->idxaddr); + writel(value, ®s->idxdata); + /* posted, may not be visible yet */ +} + +#endif /* __KERNEL__ */ + +#define PCI_VENDOR_ID_PLX_LEGACY 0x17cc + +#define PLX_LEGACY BIT(0) +#define PLX_2280 BIT(1) +#define PLX_SUPERSPEED BIT(2) + +#define REG_DIAG 0x0 +#define RETRY_COUNTER 16 +#define FORCE_PCI_SERR 11 +#define FORCE_PCI_INTERRUPT 10 +#define FORCE_USB_INTERRUPT 9 +#define FORCE_CPU_INTERRUPT 8 +#define ILLEGAL_BYTE_ENABLES 5 +#define FAST_TIMES 4 +#define FORCE_RECEIVE_ERROR 2 +#define FORCE_TRANSMIT_CRC_ERROR 0 +#define REG_FRAME 0x02 /* from last sof */ +#define REG_CHIPREV 0x03 /* in bcd */ +#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */ + +#define CHIPREV_1 0x0100 +#define CHIPREV_1A 0x0110 + +/* DEFECT 7374 */ +#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS 200 +#define DEFECT_7374_PROCESSOR_WAIT_TIME 10 + +/* ep0 max packet size */ +#define EP0_SS_MAX_PACKET_SIZE 0x200 +#define EP0_HS_MAX_PACKET_SIZE 0x40 +#ifdef __KERNEL__ + +/*-------------------------------------------------------------------------*/ + +/* [8.3] for scatter/gather i/o + * use struct net2280_dma_regs bitfields + */ +struct net2280_dma { + __le32 dmacount; + __le32 dmaaddr; /* the buffer */ + __le32 dmadesc; /* next dma descriptor */ + __le32 _reserved; +} __aligned(16); + +/*-------------------------------------------------------------------------*/ + +/* DRIVER DATA STRUCTURES and UTILITIES */ + +struct net2280_ep { + struct usb_ep ep; + struct net2280_ep_regs __iomem *cfg; + struct net2280_ep_regs __iomem *regs; + struct net2280_dma_regs __iomem *dma; + struct net2280_dma *dummy; + struct usb338x_fifo_regs __iomem *fiforegs; + dma_addr_t td_dma; /* of dummy */ + struct net2280 *dev; + unsigned long irqs; + unsigned is_halt:1, dma_started:1; + + /* analogous to a host-side qh */ + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + unsigned num : 8, + fifo_size : 12, + in_fifo_validate : 1, + out_overflow : 1, + stopped : 1, + wedged : 1, + is_in : 1, + is_iso : 1, + responded : 1; +}; + +static inline void allow_status(struct net2280_ep *ep) +{ + /* ep0 only */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_NAK_OUT_PACKETS_MODE), + &ep->regs->ep_rsp); + ep->stopped = 1; +} + +static void allow_status_338x(struct net2280_ep *ep) +{ + /* + * Control Status Phase Handshake was set by the chip when the setup + * packet arrived. While set, the chip automatically NAKs the host's + * Status Phase tokens. + */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &ep->regs->ep_rsp); + + ep->stopped = 1; + + /* TD 9.9 Halt Endpoint test. TD 9.22 set feature test. */ + ep->responded = 0; +} + +struct net2280_request { + struct usb_request req; + struct net2280_dma *td; + dma_addr_t td_dma; + struct list_head queue; + unsigned mapped : 1, + valid : 1; +}; + +struct net2280 { + /* each pci device provides one gadget, several endpoints */ + struct usb_gadget gadget; + spinlock_t lock; + struct net2280_ep ep[9]; + struct usb_gadget_driver *driver; + unsigned enabled : 1, + protocol_stall : 1, + softconnect : 1, + got_irq : 1, + region:1, + u1_enable:1, + u2_enable:1, + ltm_enable:1, + wakeup_enable:1, + selfpowered:1, + addressed_state:1; + u16 chiprev; + int enhanced_mode; + int n_ep; + kernel_ulong_t quirks; + + + /* pci state used to access those endpoints */ + struct pci_dev *pdev; + struct net2280_regs __iomem *regs; + struct net2280_usb_regs __iomem *usb; + struct usb338x_usb_ext_regs __iomem *usb_ext; + struct net2280_pci_regs __iomem *pci; + struct net2280_dma_regs __iomem *dma; + struct net2280_dep_regs __iomem *dep; + struct net2280_ep_regs __iomem *epregs; + struct usb338x_fifo_regs __iomem *fiforegs; + struct usb338x_ll_regs __iomem *llregs; + struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; + struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; + struct usb338x_ll_chi_regs __iomem *ll_chicken_reg; + struct usb338x_pl_regs __iomem *plregs; + + struct pci_pool *requests; + /* statistics...*/ +}; + +static inline void set_halt(struct net2280_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) | + /* set NAK_OUT for erratum 0114 */ + ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) | + BIT(SET_ENDPOINT_HALT), + &ep->regs->ep_rsp); +} + +static inline void clear_halt(struct net2280_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + writel(BIT(CLEAR_ENDPOINT_HALT) | + BIT(CLEAR_ENDPOINT_TOGGLE) | + /* + * unless the gadget driver left a short packet in the + * fifo, this reverses the erratum 0114 workaround. + */ + ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); +} + +/* + * FSM value for Defect 7374 (U1U2 Test) is managed in + * chip's SCRATCH register: + */ +#define DEFECT7374_FSM_FIELD 28 + +/* Waiting for Control Read: + * - A transition to this state indicates a fresh USB connection, + * before the first Setup Packet. The connection speed is not + * known. Firmware is waiting for the first Control Read. + * - Starting state: This state can be thought of as the FSM's typical + * starting state. + * - Tip: Upon the first SS Control Read the FSM never + * returns to this state. + */ +#define DEFECT7374_FSM_WAITING_FOR_CONTROL_READ BIT(DEFECT7374_FSM_FIELD) + +/* Non-SS Control Read: + * - A transition to this state indicates detection of the first HS + * or FS Control Read. + * - Tip: Upon the first SS Control Read the FSM never + * returns to this state. + */ +#define DEFECT7374_FSM_NON_SS_CONTROL_READ (2 << DEFECT7374_FSM_FIELD) + +/* SS Control Read: + * - A transition to this state indicates detection of the + * first SS Control Read. + * - This state indicates workaround completion. Workarounds no longer + * need to be applied (as long as the chip remains powered up). + * - Tip: Once in this state the FSM state does not change (until + * the chip's power is lost and restored). + * - This can be thought of as the final state of the FSM; + * the FSM 'locks-up' in this state until the chip loses power. + */ +#define DEFECT7374_FSM_SS_CONTROL_READ (3 << DEFECT7374_FSM_FIELD) + +#ifdef USE_RDK_LEDS + +static inline void net2280_led_init(struct net2280 *dev) +{ + /* LED3 (green) is on during USB activity. note erratum 0113. */ + writel(BIT(GPIO3_LED_SELECT) | + BIT(GPIO3_OUTPUT_ENABLE) | + BIT(GPIO2_OUTPUT_ENABLE) | + BIT(GPIO1_OUTPUT_ENABLE) | + BIT(GPIO0_OUTPUT_ENABLE), + &dev->regs->gpioctl); +} + +/* indicate speed with bi-color LED 0/1 */ +static inline +void net2280_led_speed(struct net2280 *dev, enum usb_device_speed speed) +{ + u32 val = readl(&dev->regs->gpioctl); + switch (speed) { + case USB_SPEED_SUPER: /* green + red */ + val |= BIT(GPIO0_DATA) | BIT(GPIO1_DATA); + break; + case USB_SPEED_HIGH: /* green */ + val &= ~BIT(GPIO0_DATA); + val |= BIT(GPIO1_DATA); + break; + case USB_SPEED_FULL: /* red */ + val &= ~BIT(GPIO1_DATA); + val |= BIT(GPIO0_DATA); + break; + default: /* (off/black) */ + val &= ~(BIT(GPIO1_DATA) | BIT(GPIO0_DATA)); + break; + } + writel(val, &dev->regs->gpioctl); +} + +/* indicate power with LED 2 */ +static inline void net2280_led_active(struct net2280 *dev, int is_active) +{ + u32 val = readl(&dev->regs->gpioctl); + + /* FIXME this LED never seems to turn on.*/ + if (is_active) + val |= GPIO2_DATA; + else + val &= ~GPIO2_DATA; + writel(val, &dev->regs->gpioctl); +} + +static inline void net2280_led_shutdown(struct net2280 *dev) +{ + /* turn off all four GPIO*_DATA bits */ + writel(readl(&dev->regs->gpioctl) & ~0x0f, + &dev->regs->gpioctl); +} + +#else + +#define net2280_led_init(dev) do { } while (0) +#define net2280_led_speed(dev, speed) do { } while (0) +#define net2280_led_shutdown(dev) do { } while (0) + +#endif + +/*-------------------------------------------------------------------------*/ + +#define ep_dbg(ndev, fmt, args...) \ + dev_dbg((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_vdbg(ndev, fmt, args...) \ + dev_vdbg((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_info(ndev, fmt, args...) \ + dev_info((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_warn(ndev, fmt, args...) \ + dev_warn((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_err(ndev, fmt, args...) \ + dev_err((&((ndev)->pdev->dev)), fmt, ##args) + +/*-------------------------------------------------------------------------*/ + +static inline void set_fifo_bytecount(struct net2280_ep *ep, unsigned count) +{ + if (ep->dev->pdev->vendor == 0x17cc) + writeb(count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); + else{ + u32 tmp = readl(&ep->cfg->ep_cfg) & + (~(0x07 << EP_FIFO_BYTE_COUNT)); + writel(tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg); + } +} + +static inline void start_out_naking(struct net2280_ep *ep) +{ + /* NOTE: hardware races lurk here, and PING protocol issues */ + writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + /* synch with device */ + readl(&ep->regs->ep_rsp); +} + +#ifdef DEBUG +static inline void assert_out_naking(struct net2280_ep *ep, const char *where) +{ + u32 tmp = readl(&ep->regs->ep_stat); + + if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) { + ep_dbg(ep->dev, "%s %s %08x !NAK\n", + ep->ep.name, where, tmp); + writel(BIT(SET_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); + } +} +#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__) +#else +#define ASSERT_OUT_NAKING(ep) do {} while (0) +#endif + +static inline void stop_out_naking(struct net2280_ep *ep) +{ + u32 tmp; + + tmp = readl(&ep->regs->ep_stat); + if ((tmp & BIT(NAK_OUT_PACKETS)) != 0) + writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); +} + + +static inline void set_max_speed(struct net2280_ep *ep, u32 max) +{ + u32 reg; + static const u32 ep_enhanced[9] = { 0x10, 0x60, 0x30, 0x80, + 0x50, 0x20, 0x70, 0x40, 0x90 }; + + if (ep->dev->enhanced_mode) + reg = ep_enhanced[ep->num]; + else{ + reg = (ep->num + 1) * 0x10; + if (ep->dev->gadget.speed != USB_SPEED_HIGH) + reg += 1; + } + + set_idx_reg(ep->dev->regs, reg, max); +} + +#endif /* __KERNEL__ */ diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 2ae4f6d69f74..dcdfea46003b 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -315,7 +315,7 @@ done(struct omap_ep *ep, struct omap_req *req, int status) /* don't modify queue heads during completion callback */ ep->stopped = 1; spin_unlock(&ep->udc->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&ep->udc->lock); ep->stopped = stopped; } @@ -2079,10 +2079,7 @@ static int omap_udc_start(struct usb_gadget *g, &udc->gadget); if (status < 0) { ERR("can't bind to transceiver\n"); - if (driver->unbind) { - driver->unbind(&udc->gadget); - udc->driver = NULL; - } + udc->driver = NULL; goto done; } } else { diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h index cfadeb5fc5de..cfadeb5fc5de 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/udc/omap_udc.h diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index eb8c3bedb57a..ccbe3d4a2a50 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -343,6 +343,7 @@ struct pch_vbus_gpio_data { * @setup_data: Received setup data * @phys_addr: of device memory * @base_addr: for mapped device memory + * @bar: Indicates which PCI BAR for USB regs * @irq: IRQ line for the device * @cfg_data: current cfg, intf, and alt in use * @vbus_gpio: GPIO informaton for detecting VBUS @@ -370,14 +371,17 @@ struct pch_udc_dev { struct usb_ctrlrequest setup_data; unsigned long phys_addr; void __iomem *base_addr; + unsigned bar; unsigned irq; struct pch_udc_cfg_data cfg_data; struct pch_vbus_gpio_data vbus_gpio; }; #define to_pch_udc(g) (container_of((g), struct pch_udc_dev, gadget)) +#define PCH_UDC_PCI_BAR_QUARK_X1000 0 #define PCH_UDC_PCI_BAR 1 #define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808 +#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939 #define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D #define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808 @@ -1486,7 +1490,7 @@ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req, spin_unlock(&dev->lock); if (!ep->in) pch_udc_ep_clear_rrdy(ep); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dev->lock); ep->halted = halted; } @@ -3076,7 +3080,7 @@ static void pch_udc_remove(struct pci_dev *pdev) iounmap(dev->base_addr); if (dev->mem_region) release_mem_region(dev->phys_addr, - pci_resource_len(pdev, PCH_UDC_PCI_BAR)); + pci_resource_len(pdev, dev->bar)); if (dev->active) pci_disable_device(pdev); kfree(dev); @@ -3144,9 +3148,15 @@ static int pch_udc_probe(struct pci_dev *pdev, dev->active = 1; pci_set_drvdata(pdev, dev); + /* Determine BAR based on PCI ID */ + if (id->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC) + dev->bar = PCH_UDC_PCI_BAR_QUARK_X1000; + else + dev->bar = PCH_UDC_PCI_BAR; + /* PCI resource allocation */ - resource = pci_resource_start(pdev, 1); - len = pci_resource_len(pdev, 1); + resource = pci_resource_start(pdev, dev->bar); + len = pci_resource_len(pdev, dev->bar); if (!request_mem_region(resource, len, KBUILD_MODNAME)) { dev_err(&pdev->dev, "%s: pci device used already\n", __func__); @@ -3212,6 +3222,12 @@ finished: static const struct pci_device_id pch_udc_pcidev_id[] = { { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC), + .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, + .class_mask = 0xffffffff, + }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC), .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, .class_mask = 0xffffffff, diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 9984437d2952..42f7eeb8ff6f 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -16,6 +16,7 @@ /* #define VERBOSE_DEBUG */ #include <linux/device.h> +#include <linux/gpio.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/ioport.h> @@ -40,7 +41,6 @@ #include <asm/byteorder.h> #include <asm/dma.h> -#include <asm/gpio.h> #include <asm/mach-types.h> #include <asm/unaligned.h> @@ -347,7 +347,7 @@ static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status) /* don't modify queue heads during completion callback */ ep->stopped = 1; - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); ep->stopped = stopped; } @@ -2105,11 +2105,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - dev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(dev->clk)) { - retval = PTR_ERR(dev->clk); - goto err_clk; - } + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, dev->has_cfr ? "" : " (!cfr)", @@ -2120,15 +2118,16 @@ static int pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = dev_get_platdata(&pdev->dev); - dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { - if ((retval = gpio_request(dev->mach->gpio_pullup, - "pca25x_udc GPIO PULLUP"))) { + retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup, + "pca25x_udc GPIO PULLUP"); + if (retval) { dev_dbg(&pdev->dev, "can't get pullup gpio %d, err: %d\n", dev->mach->gpio_pullup, retval); - goto err_gpio_pullup; + goto err; } gpio_direction_output(dev->mach->gpio_pullup, 0); } @@ -2146,30 +2145,32 @@ static int pxa25x_udc_probe(struct platform_device *pdev) dev->vbus = 0; /* irq setup after old hardware state is cleaned up */ - retval = request_irq(irq, pxa25x_udc_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, irq, pxa25x_udc_irq, 0, + driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %d, err %d\n", driver_name, irq, retval); - goto err_irq1; + goto err; } dev->got_irq = 1; #ifdef CONFIG_ARCH_LUBBOCK if (machine_is_lubbock()) { - retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_DISC_IRQ, + lubbock_vbus_irq, 0, driver_name, + dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval); - goto err_irq_lub; + goto err; } - retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_IRQ, + lubbock_vbus_irq, 0, driver_name, + dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_IRQ, retval); - goto lubbock_fail0; + goto err; } } else #endif @@ -2180,22 +2181,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev) return retval; remove_debug_files(dev); -#ifdef CONFIG_ARCH_LUBBOCK -lubbock_fail0: - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - err_irq_lub: - free_irq(irq, dev); -#endif - err_irq1: - if (gpio_is_valid(dev->mach->gpio_pullup)) - gpio_free(dev->mach->gpio_pullup); - err_gpio_pullup: - if (!IS_ERR_OR_NULL(dev->transceiver)) { - usb_put_phy(dev->transceiver); + err: + if (!IS_ERR_OR_NULL(dev->transceiver)) dev->transceiver = NULL; - } - clk_put(dev->clk); - err_clk: return retval; } @@ -2217,25 +2205,8 @@ static int pxa25x_udc_remove(struct platform_device *pdev) remove_debug_files(dev); - if (dev->got_irq) { - free_irq(platform_get_irq(pdev, 0), dev); - dev->got_irq = 0; - } -#ifdef CONFIG_ARCH_LUBBOCK - if (machine_is_lubbock()) { - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - free_irq(LUBBOCK_USB_IRQ, dev); - } -#endif - if (gpio_is_valid(dev->mach->gpio_pullup)) - gpio_free(dev->mach->gpio_pullup); - - clk_put(dev->clk); - - if (!IS_ERR_OR_NULL(dev->transceiver)) { - usb_put_phy(dev->transceiver); + if (!IS_ERR_OR_NULL(dev->transceiver)) dev->transceiver = NULL; - } the_controller = NULL; return 0; diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h index 3fe5931dc21a..3fe5931dc21a 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/udc/pxa25x_udc.h diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index cdf4d678be96..4868369eeec6 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -758,7 +758,7 @@ static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status, if (pflags) spin_unlock_irqrestore(&ep->lock, *pflags); local_irq_save(flags); - req->req.complete(&req->udc_usb_ep->usb_ep, &req->req); + usb_gadget_giveback_request(&req->udc_usb_ep->usb_ep, &req->req); local_irq_restore(flags); if (pflags) spin_lock_irqsave(&ep->lock, *pflags); @@ -2446,6 +2446,9 @@ static int pxa_udc_probe(struct platform_device *pdev) retval = PTR_ERR(udc->clk); goto err_clk; } + retval = clk_prepare(udc->clk); + if (retval) + goto err_clk_prepare; retval = -ENOMEM; udc->regs = ioremap(regs->start, resource_size(regs)); @@ -2483,6 +2486,8 @@ err_add_udc: err_irq: iounmap(udc->regs); err_map: + clk_unprepare(udc->clk); +err_clk_prepare: clk_put(udc->clk); udc->clk = NULL; err_clk: @@ -2509,6 +2514,7 @@ static int pxa_udc_remove(struct platform_device *_dev) udc->transceiver = NULL; the_controller = NULL; + clk_unprepare(udc->clk); clk_put(udc->clk); iounmap(udc->regs); diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index 28f2b53530f5..28f2b53530f5 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index b698a490cc7d..f8186613b53e 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -430,7 +430,7 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597, ep->pipenum = pipenum; ep->ep.maxpacket = usb_endpoint_maxp(desc); r8a66597->pipenum2ep[pipenum] = ep; - r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] + r8a66597->epaddr2ep[usb_endpoint_num(desc)] = ep; INIT_LIST_HEAD(&ep->queue); } @@ -464,7 +464,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, if (ep->pipenum) /* already allocated pipe */ return 0; - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) { if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { @@ -509,7 +509,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, } ep->type = info.type; - info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + info.epnum = usb_endpoint_num(desc); info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) @@ -925,7 +925,7 @@ __acquires(r8a66597->lock) sudmac_free_channel(ep->r8a66597, ep, req); spin_unlock(&ep->r8a66597->lock); - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&ep->r8a66597->lock); if (restart) { @@ -1826,18 +1826,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev) usb_del_gadget_udc(&r8a66597->gadget); del_timer_sync(&r8a66597->timer); - iounmap(r8a66597->reg); - if (r8a66597->pdata->sudmac) - iounmap(r8a66597->sudmac_reg); - free_irq(platform_get_irq(pdev, 0), r8a66597); r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); if (r8a66597->pdata->on_chip) { clk_disable_unprepare(r8a66597->clk); - clk_put(r8a66597->clk); } - kfree(r8a66597); return 0; } @@ -1845,28 +1839,22 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } -static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, +static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, struct platform_device *pdev) { struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac"); - if (!res) { - dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n"); - return -ENODEV; - } - - r8a66597->sudmac_reg = ioremap(res->start, resource_size(res)); - if (r8a66597->sudmac_reg == NULL) { - dev_err(&pdev->dev, "ioremap error(sudmac).\n"); - return -ENOMEM; - } + r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(r8a66597->sudmac_reg)) + return PTR_ERR(r8a66597->sudmac_reg); return 0; } -static int __init r8a66597_probe(struct platform_device *pdev) +static int r8a66597_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; char clk_name[8]; struct resource *res, *ires; int irq; @@ -1877,39 +1865,27 @@ static int __init r8a66597_probe(struct platform_device *pdev) unsigned long irq_trigger; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - dev_err(&pdev->dev, "platform_get_resource error.\n"); - goto clean_up; - } + reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); irq = ires->start; irq_trigger = ires->flags & IRQF_TRIGGER_MASK; if (irq < 0) { - ret = -ENODEV; - dev_err(&pdev->dev, "platform_get_irq error.\n"); - goto clean_up; - } - - reg = ioremap(res->start, resource_size(res)); - if (reg == NULL) { - ret = -ENOMEM; - dev_err(&pdev->dev, "ioremap error.\n"); - goto clean_up; + dev_err(dev, "platform_get_irq error.\n"); + return -ENODEV; } /* initialize ucd */ - r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); - if (r8a66597 == NULL) { - ret = -ENOMEM; - goto clean_up; - } + r8a66597 = devm_kzalloc(dev, sizeof(struct r8a66597), GFP_KERNEL); + if (r8a66597 == NULL) + return -ENOMEM; spin_lock_init(&r8a66597->lock); platform_set_drvdata(pdev, r8a66597); - r8a66597->pdata = dev_get_platdata(&pdev->dev); + r8a66597->pdata = dev_get_platdata(dev); r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; r8a66597->gadget.ops = &r8a66597_gadget_ops; @@ -1923,12 +1899,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) if (r8a66597->pdata->on_chip) { snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); - r8a66597->clk = clk_get(&pdev->dev, clk_name); + r8a66597->clk = devm_clk_get(dev, clk_name); if (IS_ERR(r8a66597->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", - clk_name); - ret = PTR_ERR(r8a66597->clk); - goto clean_up; + dev_err(dev, "cannot get clock \"%s\"\n", clk_name); + return PTR_ERR(r8a66597->clk); } clk_prepare_enable(r8a66597->clk); } @@ -1941,10 +1915,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) disable_controller(r8a66597); /* make sure controller is disabled */ - ret = request_irq(irq, r8a66597_irq, IRQF_SHARED, - udc_name, r8a66597); + ret = devm_request_irq(dev, irq, r8a66597_irq, IRQF_SHARED, + udc_name, r8a66597); if (ret < 0) { - dev_err(&pdev->dev, "request_irq error (%d)\n", ret); + dev_err(dev, "request_irq error (%d)\n", ret); goto clean_up2; } @@ -1978,37 +1952,25 @@ static int __init r8a66597_probe(struct platform_device *pdev) GFP_KERNEL); if (r8a66597->ep0_req == NULL) { ret = -ENOMEM; - goto clean_up3; + goto clean_up2; } r8a66597->ep0_req->complete = nop_completion; - ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget); + ret = usb_add_gadget_udc(dev, &r8a66597->gadget); if (ret) goto err_add_udc; - dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + dev_info(dev, "version %s\n", DRIVER_VERSION); return 0; err_add_udc: r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); -clean_up3: - free_irq(irq, r8a66597); clean_up2: - if (r8a66597->pdata->on_chip) { + if (r8a66597->pdata->on_chip) clk_disable_unprepare(r8a66597->clk); - clk_put(r8a66597->clk); - } -clean_up: - if (r8a66597) { - if (r8a66597->sudmac_reg) - iounmap(r8a66597->sudmac_reg); - if (r8a66597->ep0_req) - r8a66597_free_request(&r8a66597->ep[0].ep, - r8a66597->ep0_req); - kfree(r8a66597); - } - if (reg) - iounmap(reg); + + if (r8a66597->ep0_req) + r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); return ret; } diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/udc/r8a66597-udc.h index 45c4b2df1785..45c4b2df1785 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/udc/r8a66597-udc.h diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index 10c6a128250c..dfbf55797360 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c @@ -258,8 +258,7 @@ static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep, hsep->stopped = 1; spin_unlock(&hsudc->lock); - if (hsreq->req.complete != NULL) - hsreq->req.complete(&hsep->ep, &hsreq->req); + usb_gadget_giveback_request(&hsep->ep, &hsreq->req); spin_lock(&hsudc->lock); hsep->stopped = stopped; } diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 7987aa049fab..ff423d15beff 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -272,7 +272,7 @@ static void s3c2410_udc_done(struct s3c2410_ep *ep, status = req->req.status; ep->halted = 1; - req->req.complete(&ep->ep, &req->req); + usb_gadget_giveback_request(&ep->ep, &req->req); ep->halted = halted; } @@ -1788,7 +1788,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return PTR_ERR(usb_bus_clock); } - clk_enable(usb_bus_clock); + clk_prepare_enable(usb_bus_clock); udc_clock = clk_get(NULL, "usb-device"); if (IS_ERR(udc_clock)) { @@ -1796,7 +1796,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return PTR_ERR(udc_clock); } - clk_enable(udc_clock); + clk_prepare_enable(udc_clock); mdelay(10); @@ -1952,13 +1952,13 @@ static int s3c2410_udc_remove(struct platform_device *pdev) release_mem_region(rsrc_start, rsrc_len); if (!IS_ERR(udc_clock) && udc_clock != NULL) { - clk_disable(udc_clock); + clk_disable_unprepare(udc_clock); clk_put(udc_clock); udc_clock = NULL; } if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) { - clk_disable(usb_bus_clock); + clk_disable_unprepare(usb_bus_clock); clk_put(usb_bus_clock); usb_bus_clock = NULL; } diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h index 93bf225f1969..93bf225f1969 100644 --- a/drivers/usb/gadget/s3c2410_udc.h +++ b/drivers/usb/gadget/udc/s3c2410_udc.h diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index b0d98172bc07..f107bb60a5ab 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -27,6 +27,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb.h> /** * struct usb_udc - describes one usb device controller @@ -106,11 +107,42 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); /* ------------------------------------------------------------------------- */ +/** + * usb_gadget_giveback_request - give the request back to the gadget layer + * Context: in_interrupt() + * + * This is called by device controller drivers in order to return the + * completed request back to the gadget layer. + */ +void usb_gadget_giveback_request(struct usb_ep *ep, + struct usb_request *req) +{ + if (likely(req->status == 0)) + usb_led_activity(USB_LED_EVENT_GADGET); + + req->complete(ep, req); +} +EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); + +/* ------------------------------------------------------------------------- */ + static void usb_gadget_state_work(struct work_struct *work) { struct usb_gadget *gadget = work_to_gadget(work); + struct usb_udc *udc = NULL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) + if (udc->gadget == gadget) + goto found; + mutex_unlock(&udc_lock); + + return; + +found: + mutex_unlock(&udc_lock); - sysfs_notify(&gadget->dev.kobj, NULL, "state"); + sysfs_notify(&udc->dev.kobj, NULL, "state"); } void usb_gadget_set_state(struct usb_gadget *gadget, @@ -124,6 +156,23 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state); /* ------------------------------------------------------------------------- */ /** + * usb_gadget_udc_reset - notifies the udc core that bus reset occurs + * @gadget: The gadget which bus reset occurs + * @driver: The gadget driver we want to notify + * + * If the udc driver has bus reset handler, it needs to call this when the bus + * reset occurs, it notifies the gadget driver that the bus reset occurs as + * well as updates gadget state. + */ +void usb_gadget_udc_reset(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + driver->reset(gadget); + usb_gadget_set_state(gadget, USB_STATE_DEFAULT); +} +EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); + +/** * usb_gadget_udc_start - tells usb device controller to start up * @gadget: The gadget we want to get started * @driver: The driver we want to bind to @gadget diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c new file mode 100644 index 000000000000..ed27e1687a4e --- /dev/null +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -0,0 +1,2180 @@ +/* + * Xilinx USB peripheral controller driver + * + * Copyright (C) 2004 by Thomas Rathbone + * Copyright (C) 2005 by HP Labs + * Copyright (C) 2005 by David Brownell + * Copyright (C) 2010 - 2014 Xilinx, Inc. + * + * Some parts of this driver code is based on the driver for at91-series + * USB peripheral controller (at91_udc.c). + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/prefetch.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +/* Register offsets for the USB device.*/ +#define XUSB_EP0_CONFIG_OFFSET 0x0000 /* EP0 Config Reg Offset */ +#define XUSB_SETUP_PKT_ADDR_OFFSET 0x0080 /* Setup Packet Address */ +#define XUSB_ADDRESS_OFFSET 0x0100 /* Address Register */ +#define XUSB_CONTROL_OFFSET 0x0104 /* Control Register */ +#define XUSB_STATUS_OFFSET 0x0108 /* Status Register */ +#define XUSB_FRAMENUM_OFFSET 0x010C /* Frame Number Register */ +#define XUSB_IER_OFFSET 0x0110 /* Interrupt Enable Register */ +#define XUSB_BUFFREADY_OFFSET 0x0114 /* Buffer Ready Register */ +#define XUSB_TESTMODE_OFFSET 0x0118 /* Test Mode Register */ +#define XUSB_DMA_RESET_OFFSET 0x0200 /* DMA Soft Reset Register */ +#define XUSB_DMA_CONTROL_OFFSET 0x0204 /* DMA Control Register */ +#define XUSB_DMA_DSAR_ADDR_OFFSET 0x0208 /* DMA source Address Reg */ +#define XUSB_DMA_DDAR_ADDR_OFFSET 0x020C /* DMA destination Addr Reg */ +#define XUSB_DMA_LENGTH_OFFSET 0x0210 /* DMA Length Register */ +#define XUSB_DMA_STATUS_OFFSET 0x0214 /* DMA Status Register */ + +/* Endpoint Configuration Space offsets */ +#define XUSB_EP_CFGSTATUS_OFFSET 0x00 /* Endpoint Config Status */ +#define XUSB_EP_BUF0COUNT_OFFSET 0x08 /* Buffer 0 Count */ +#define XUSB_EP_BUF1COUNT_OFFSET 0x0C /* Buffer 1 Count */ + +#define XUSB_CONTROL_USB_READY_MASK 0x80000000 /* USB ready Mask */ +#define XUSB_CONTROL_USB_RMTWAKE_MASK 0x40000000 /* Remote wake up mask */ + +/* Interrupt register related masks.*/ +#define XUSB_STATUS_GLOBAL_INTR_MASK 0x80000000 /* Global Intr Enable */ +#define XUSB_STATUS_DMADONE_MASK 0x04000000 /* DMA done Mask */ +#define XUSB_STATUS_DMAERR_MASK 0x02000000 /* DMA Error Mask */ +#define XUSB_STATUS_DMABUSY_MASK 0x80000000 /* DMA Error Mask */ +#define XUSB_STATUS_RESUME_MASK 0x01000000 /* USB Resume Mask */ +#define XUSB_STATUS_RESET_MASK 0x00800000 /* USB Reset Mask */ +#define XUSB_STATUS_SUSPEND_MASK 0x00400000 /* USB Suspend Mask */ +#define XUSB_STATUS_DISCONNECT_MASK 0x00200000 /* USB Disconnect Mask */ +#define XUSB_STATUS_FIFO_BUFF_RDY_MASK 0x00100000 /* FIFO Buff Ready Mask */ +#define XUSB_STATUS_FIFO_BUFF_FREE_MASK 0x00080000 /* FIFO Buff Free Mask */ +#define XUSB_STATUS_SETUP_PACKET_MASK 0x00040000 /* Setup packet received */ +#define XUSB_STATUS_EP1_BUFF2_COMP_MASK 0x00000200 /* EP 1 Buff 2 Processed */ +#define XUSB_STATUS_EP1_BUFF1_COMP_MASK 0x00000002 /* EP 1 Buff 1 Processed */ +#define XUSB_STATUS_EP0_BUFF2_COMP_MASK 0x00000100 /* EP 0 Buff 2 Processed */ +#define XUSB_STATUS_EP0_BUFF1_COMP_MASK 0x00000001 /* EP 0 Buff 1 Processed */ +#define XUSB_STATUS_HIGH_SPEED_MASK 0x00010000 /* USB Speed Mask */ +/* Suspend,Reset,Suspend and Disconnect Mask */ +#define XUSB_STATUS_INTR_EVENT_MASK 0x01E00000 +/* Buffers completion Mask */ +#define XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK 0x0000FEFF +/* Mask for buffer 0 and buffer 1 completion for all Endpoints */ +#define XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK 0x00000101 +#define XUSB_STATUS_EP_BUFF2_SHIFT 8 /* EP buffer offset */ + +/* Endpoint Configuration Status Register */ +#define XUSB_EP_CFG_VALID_MASK 0x80000000 /* Endpoint Valid bit */ +#define XUSB_EP_CFG_STALL_MASK 0x40000000 /* Endpoint Stall bit */ +#define XUSB_EP_CFG_DATA_TOGGLE_MASK 0x08000000 /* Endpoint Data toggle */ + +/* USB device specific global configuration constants.*/ +#define XUSB_MAX_ENDPOINTS 8 /* Maximum End Points */ +#define XUSB_EP_NUMBER_ZERO 0 /* End point Zero */ +/* DPRAM is the source address for DMA transfer */ +#define XUSB_DMA_READ_FROM_DPRAM 0x80000000 +#define XUSB_DMA_DMASR_BUSY 0x80000000 /* DMA busy */ +#define XUSB_DMA_DMASR_ERROR 0x40000000 /* DMA Error */ +/* + * When this bit is set, the DMA buffer ready bit is set by hardware upon + * DMA transfer completion. + */ +#define XUSB_DMA_BRR_CTRL 0x40000000 /* DMA bufready ctrl bit */ +/* Phase States */ +#define SETUP_PHASE 0x0000 /* Setup Phase */ +#define DATA_PHASE 0x0001 /* Data Phase */ +#define STATUS_PHASE 0x0002 /* Status Phase */ + +#define EP0_MAX_PACKET 64 /* Endpoint 0 maximum packet length */ +#define STATUSBUFF_SIZE 2 /* Buffer size for GET_STATUS command */ +#define EPNAME_SIZE 4 /* Buffer size for endpoint name */ + +/* container_of helper macros */ +#define to_udc(g) container_of((g), struct xusb_udc, gadget) +#define to_xusb_ep(ep) container_of((ep), struct xusb_ep, ep_usb) +#define to_xusb_req(req) container_of((req), struct xusb_req, usb_req) + +/** + * struct xusb_req - Xilinx USB device request structure + * @usb_req: Linux usb request structure + * @queue: usb device request queue + * @ep: pointer to xusb_endpoint structure + */ +struct xusb_req { + struct usb_request usb_req; + struct list_head queue; + struct xusb_ep *ep; +}; + +/** + * struct xusb_ep - USB end point structure. + * @ep_usb: usb endpoint instance + * @queue: endpoint message queue + * @udc: xilinx usb peripheral driver instance pointer + * @desc: pointer to the usb endpoint descriptor + * @rambase: the endpoint buffer address + * @offset: the endpoint register offset value + * @name: name of the endpoint + * @epnumber: endpoint number + * @maxpacket: maximum packet size the endpoint can store + * @buffer0count: the size of the packet recieved in the first buffer + * @buffer1count: the size of the packet received in the second buffer + * @curbufnum: current buffer of endpoint that will be processed next + * @buffer0ready: the busy state of first buffer + * @buffer1ready: the busy state of second buffer + * @is_in: endpoint direction (IN or OUT) + * @is_iso: endpoint type(isochronous or non isochronous) + */ +struct xusb_ep { + struct usb_ep ep_usb; + struct list_head queue; + struct xusb_udc *udc; + const struct usb_endpoint_descriptor *desc; + u32 rambase; + u32 offset; + char name[4]; + u16 epnumber; + u16 maxpacket; + u16 buffer0count; + u16 buffer1count; + u8 curbufnum; + bool buffer0ready; + bool buffer1ready; + bool is_in; + bool is_iso; +}; + +/** + * struct xusb_udc - USB peripheral driver structure + * @gadget: USB gadget driver instance + * @ep: an array of endpoint structures + * @driver: pointer to the usb gadget driver instance + * @setup: usb_ctrlrequest structure for control requests + * @req: pointer to dummy request for get status command + * @dev: pointer to device structure in gadget + * @usb_state: device in suspended state or not + * @remote_wkp: remote wakeup enabled by host + * @setupseqtx: tx status + * @setupseqrx: rx status + * @addr: the usb device base address + * @lock: instance of spinlock + * @dma_enabled: flag indicating whether the dma is included in the system + * @read_fn: function pointer to read device registers + * @write_fn: function pointer to write to device registers + */ +struct xusb_udc { + struct usb_gadget gadget; + struct xusb_ep ep[8]; + struct usb_gadget_driver *driver; + struct usb_ctrlrequest setup; + struct xusb_req *req; + struct device *dev; + u32 usb_state; + u32 remote_wkp; + u32 setupseqtx; + u32 setupseqrx; + void __iomem *addr; + spinlock_t lock; + bool dma_enabled; + + unsigned int (*read_fn)(void __iomem *); + void (*write_fn)(void __iomem *, u32, u32); +}; + +/* Endpoint buffer start addresses in the core */ +static u32 rambase[8] = { 0x22, 0x1000, 0x1100, 0x1200, 0x1300, 0x1400, 0x1500, + 0x1600 }; + +static const char driver_name[] = "xilinx-udc"; +static const char ep0name[] = "ep0"; + +/* Control endpoint configuration.*/ +static const struct usb_endpoint_descriptor config_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(EP0_MAX_PACKET), +}; + +/** + * xudc_write32 - little endian write to device registers + * @addr: base addr of device registers + * @offset: register offset + * @val: data to be written + */ +static void xudc_write32(void __iomem *addr, u32 offset, u32 val) +{ + iowrite32(val, addr + offset); +} + +/** + * xudc_read32 - little endian read from device registers + * @addr: addr of device register + * Return: value at addr + */ +static unsigned int xudc_read32(void __iomem *addr) +{ + return ioread32(addr); +} + +/** + * xudc_write32_be - big endian write to device registers + * @addr: base addr of device registers + * @offset: register offset + * @val: data to be written + */ +static void xudc_write32_be(void __iomem *addr, u32 offset, u32 val) +{ + iowrite32be(val, addr + offset); +} + +/** + * xudc_read32_be - big endian read from device registers + * @addr: addr of device register + * Return: value at addr + */ +static unsigned int xudc_read32_be(void __iomem *addr) +{ + return ioread32be(addr); +} + +/** + * xudc_wrstatus - Sets up the usb device status stages. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_wrstatus(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; + u32 epcfgreg; + + epcfgreg = udc->read_fn(udc->addr + ep0->offset)| + XUSB_EP_CFG_DATA_TOGGLE_MASK; + udc->write_fn(udc->addr, ep0->offset, epcfgreg); + udc->write_fn(udc->addr, ep0->offset + XUSB_EP_BUF0COUNT_OFFSET, 0); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); +} + +/** + * xudc_epconfig - Configures the given endpoint. + * @ep: pointer to the usb device endpoint structure. + * @udc: pointer to the usb peripheral controller structure. + * + * This function configures a specific endpoint with the given configuration + * data. + */ +static void xudc_epconfig(struct xusb_ep *ep, struct xusb_udc *udc) +{ + u32 epcfgreg; + + /* + * Configure the end point direction, type, Max Packet Size and the + * EP buffer location. + */ + epcfgreg = ((ep->is_in << 29) | (ep->is_iso << 28) | + (ep->ep_usb.maxpacket << 15) | (ep->rambase)); + udc->write_fn(udc->addr, ep->offset, epcfgreg); + + /* Set the Buffer count and the Buffer ready bits.*/ + udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF0COUNT_OFFSET, + ep->buffer0count); + udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF1COUNT_OFFSET, + ep->buffer1count); + if (ep->buffer0ready) + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << ep->epnumber); + if (ep->buffer1ready) + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT)); +} + +/** + * xudc_start_dma - Starts DMA transfer. + * @ep: pointer to the usb device endpoint structure. + * @src: DMA source address. + * @dst: DMA destination address. + * @length: number of bytes to transfer. + * + * Return: 0 on success, error code on failure + * + * This function starts DMA transfer by writing to DMA source, + * destination and lenth registers. + */ +static int xudc_start_dma(struct xusb_ep *ep, dma_addr_t src, + dma_addr_t dst, u32 length) +{ + struct xusb_udc *udc = ep->udc; + int rc = 0; + u32 timeout = 500; + u32 reg; + + /* + * Set the addresses in the DMA source and + * destination registers and then set the length + * into the DMA length register. + */ + udc->write_fn(udc->addr, XUSB_DMA_DSAR_ADDR_OFFSET, src); + udc->write_fn(udc->addr, XUSB_DMA_DDAR_ADDR_OFFSET, dst); + udc->write_fn(udc->addr, XUSB_DMA_LENGTH_OFFSET, length); + + /* + * Wait till DMA transaction is complete and + * check whether the DMA transaction was + * successful. + */ + do { + reg = udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET); + if (!(reg & XUSB_DMA_DMASR_BUSY)) + break; + + /* + * We can't sleep here, because it's also called from + * interrupt context. + */ + timeout--; + if (!timeout) { + dev_err(udc->dev, "DMA timeout\n"); + return -ETIMEDOUT; + } + udelay(1); + } while (1); + + if ((udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET) & + XUSB_DMA_DMASR_ERROR) == XUSB_DMA_DMASR_ERROR){ + dev_err(udc->dev, "DMA Error\n"); + rc = -EINVAL; + } + + return rc; +} + +/** + * xudc_dma_send - Sends IN data using DMA. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * @buffer: pointer to data to be sent. + * @length: number of bytes to send. + * + * Return: 0 on success, -EAGAIN if no buffer is free and error + * code on failure. + * + * This function sends data using DMA. + */ +static int xudc_dma_send(struct xusb_ep *ep, struct xusb_req *req, + u8 *buffer, u32 length) +{ + u32 *eprambase; + dma_addr_t src; + dma_addr_t dst; + struct xusb_udc *udc = ep->udc; + + src = req->usb_req.dma + req->usb_req.actual; + if (req->usb_req.length) + dma_sync_single_for_device(udc->dev, src, + length, DMA_TO_DEVICE); + if (!ep->curbufnum && !ep->buffer0ready) { + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase); + dst = virt_to_phys(eprambase); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF0COUNT_OFFSET, length); + udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, + XUSB_DMA_BRR_CTRL | (1 << ep->epnumber)); + ep->buffer0ready = 1; + ep->curbufnum = 1; + } else if (ep->curbufnum && !ep->buffer1ready) { + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase + + ep->ep_usb.maxpacket); + dst = virt_to_phys(eprambase); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF1COUNT_OFFSET, length); + udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, + XUSB_DMA_BRR_CTRL | (1 << (ep->epnumber + + XUSB_STATUS_EP_BUFF2_SHIFT))); + ep->buffer1ready = 1; + ep->curbufnum = 0; + } else { + /* None of ping pong buffers are ready currently .*/ + return -EAGAIN; + } + + return xudc_start_dma(ep, src, dst, length); +} + +/** + * xudc_dma_receive - Receives OUT data using DMA. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * @buffer: pointer to storage buffer of received data. + * @length: number of bytes to receive. + * + * Return: 0 on success, -EAGAIN if no buffer is free and error + * code on failure. + * + * This function receives data using DMA. + */ +static int xudc_dma_receive(struct xusb_ep *ep, struct xusb_req *req, + u8 *buffer, u32 length) +{ + u32 *eprambase; + dma_addr_t src; + dma_addr_t dst; + struct xusb_udc *udc = ep->udc; + + dst = req->usb_req.dma + req->usb_req.actual; + if (!ep->curbufnum && !ep->buffer0ready) { + /* Get the Buffer address and copy the transmit data */ + eprambase = (u32 __force *)(udc->addr + ep->rambase); + src = virt_to_phys(eprambase); + udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, + XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM | + (1 << ep->epnumber)); + ep->buffer0ready = 1; + ep->curbufnum = 1; + } else if (ep->curbufnum && !ep->buffer1ready) { + /* Get the Buffer address and copy the transmit data */ + eprambase = (u32 __force *)(udc->addr + + ep->rambase + ep->ep_usb.maxpacket); + src = virt_to_phys(eprambase); + udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, + XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM | + (1 << (ep->epnumber + + XUSB_STATUS_EP_BUFF2_SHIFT))); + ep->buffer1ready = 1; + ep->curbufnum = 0; + } else { + /* None of the ping-pong buffers are ready currently */ + return -EAGAIN; + } + + return xudc_start_dma(ep, src, dst, length); +} + +/** + * xudc_eptxrx - Transmits or receives data to or from an endpoint. + * @ep: pointer to the usb endpoint configuration structure. + * @req: pointer to the usb request structure. + * @bufferptr: pointer to buffer containing the data to be sent. + * @bufferlen: The number of data bytes to be sent. + * + * Return: 0 on success, -EAGAIN if no buffer is free. + * + * This function copies the transmit/receive data to/from the end point buffer + * and enables the buffer for transmission/reception. + */ +static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req, + u8 *bufferptr, u32 bufferlen) +{ + u32 *eprambase; + u32 bytestosend; + int rc = 0; + struct xusb_udc *udc = ep->udc; + + bytestosend = bufferlen; + if (udc->dma_enabled) { + if (ep->is_in) + rc = xudc_dma_send(ep, req, bufferptr, bufferlen); + else + rc = xudc_dma_receive(ep, req, bufferptr, bufferlen); + return rc; + } + /* Put the transmit buffer into the correct ping-pong buffer.*/ + if (!ep->curbufnum && !ep->buffer0ready) { + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase); + if (ep->is_in) { + memcpy(eprambase, bufferptr, bytestosend); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF0COUNT_OFFSET, bufferlen); + } else { + memcpy(bufferptr, eprambase, bytestosend); + } + /* + * Enable the buffer for transmission. + */ + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << ep->epnumber); + ep->buffer0ready = 1; + ep->curbufnum = 1; + } else if (ep->curbufnum && !ep->buffer1ready) { + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase + + ep->ep_usb.maxpacket); + if (ep->is_in) { + memcpy(eprambase, bufferptr, bytestosend); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF1COUNT_OFFSET, bufferlen); + } else { + memcpy(bufferptr, eprambase, bytestosend); + } + /* + * Enable the buffer for transmission. + */ + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT)); + ep->buffer1ready = 1; + ep->curbufnum = 0; + } else { + /* None of the ping-pong buffers are ready currently */ + return -EAGAIN; + } + return rc; +} + +/** + * xudc_done - Exeutes the endpoint data transfer completion tasks. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * @status: Status of the data transfer. + * + * Deletes the message from the queue and updates data transfer completion + * status. + */ +static void xudc_done(struct xusb_ep *ep, struct xusb_req *req, int status) +{ + struct xusb_udc *udc = ep->udc; + + list_del_init(&req->queue); + + if (req->usb_req.status == -EINPROGRESS) + req->usb_req.status = status; + else + status = req->usb_req.status; + + if (status && status != -ESHUTDOWN) + dev_dbg(udc->dev, "%s done %p, status %d\n", + ep->ep_usb.name, req, status); + /* unmap request if DMA is present*/ + if (udc->dma_enabled && ep->epnumber && req->usb_req.length) + usb_gadget_unmap_request(&udc->gadget, &req->usb_req, + ep->is_in); + + if (req->usb_req.complete) { + spin_unlock(&udc->lock); + req->usb_req.complete(&ep->ep_usb, &req->usb_req); + spin_lock(&udc->lock); + } +} + +/** + * xudc_read_fifo - Reads the data from the given endpoint buffer. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * + * Return: 0 if request is completed and -EAGAIN if not completed. + * + * Pulls OUT packet data from the endpoint buffer. + */ +static int xudc_read_fifo(struct xusb_ep *ep, struct xusb_req *req) +{ + u8 *buf; + u32 is_short, count, bufferspace; + u8 bufoffset; + u8 two_pkts = 0; + int ret; + int retval = -EAGAIN; + struct xusb_udc *udc = ep->udc; + + if (ep->buffer0ready && ep->buffer1ready) { + dev_dbg(udc->dev, "Packet NOT ready!\n"); + return retval; + } +top: + if (ep->curbufnum) + bufoffset = XUSB_EP_BUF1COUNT_OFFSET; + else + bufoffset = XUSB_EP_BUF0COUNT_OFFSET; + + count = udc->read_fn(udc->addr + ep->offset + bufoffset); + + if (!ep->buffer0ready && !ep->buffer1ready) + two_pkts = 1; + + buf = req->usb_req.buf + req->usb_req.actual; + prefetchw(buf); + bufferspace = req->usb_req.length - req->usb_req.actual; + is_short = count < ep->ep_usb.maxpacket; + + if (unlikely(!bufferspace)) { + /* + * This happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->usb_req.status != -EOVERFLOW) + dev_dbg(udc->dev, "%s overflow %d\n", + ep->ep_usb.name, count); + req->usb_req.status = -EOVERFLOW; + xudc_done(ep, req, -EOVERFLOW); + return 0; + } + + ret = xudc_eptxrx(ep, req, buf, count); + switch (ret) { + case 0: + req->usb_req.actual += min(count, bufferspace); + dev_dbg(udc->dev, "read %s, %d bytes%s req %p %d/%d\n", + ep->ep_usb.name, count, is_short ? "/S" : "", req, + req->usb_req.actual, req->usb_req.length); + bufferspace -= count; + /* Completion */ + if ((req->usb_req.actual == req->usb_req.length) || is_short) { + if (udc->dma_enabled && req->usb_req.length) + dma_sync_single_for_cpu(udc->dev, + req->usb_req.dma, + req->usb_req.actual, + DMA_FROM_DEVICE); + xudc_done(ep, req, 0); + return 0; + } + if (two_pkts) { + two_pkts = 0; + goto top; + } + break; + case -EAGAIN: + dev_dbg(udc->dev, "receive busy\n"); + break; + case -EINVAL: + case -ETIMEDOUT: + /* DMA error, dequeue the request */ + xudc_done(ep, req, -ECONNRESET); + retval = 0; + break; + } + + return retval; +} + +/** + * xudc_write_fifo - Writes data into the given endpoint buffer. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * + * Return: 0 if request is completed and -EAGAIN if not completed. + * + * Loads endpoint buffer for an IN packet. + */ +static int xudc_write_fifo(struct xusb_ep *ep, struct xusb_req *req) +{ + u32 max; + u32 length; + int ret; + int retval = -EAGAIN; + struct xusb_udc *udc = ep->udc; + int is_last, is_short = 0; + u8 *buf; + + max = le16_to_cpu(ep->desc->wMaxPacketSize); + buf = req->usb_req.buf + req->usb_req.actual; + prefetch(buf); + length = req->usb_req.length - req->usb_req.actual; + length = min(length, max); + + ret = xudc_eptxrx(ep, req, buf, length); + switch (ret) { + case 0: + req->usb_req.actual += length; + if (unlikely(length != max)) { + is_last = is_short = 1; + } else { + if (likely(req->usb_req.length != + req->usb_req.actual) || req->usb_req.zero) + is_last = 0; + else + is_last = 1; + } + dev_dbg(udc->dev, "%s: wrote %s %d bytes%s%s %d left %p\n", + __func__, ep->ep_usb.name, length, is_last ? "/L" : "", + is_short ? "/S" : "", + req->usb_req.length - req->usb_req.actual, req); + /* completion */ + if (is_last) { + xudc_done(ep, req, 0); + retval = 0; + } + break; + case -EAGAIN: + dev_dbg(udc->dev, "Send busy\n"); + break; + case -EINVAL: + case -ETIMEDOUT: + /* DMA error, dequeue the request */ + xudc_done(ep, req, -ECONNRESET); + retval = 0; + break; + } + + return retval; +} + +/** + * xudc_nuke - Cleans up the data transfer message list. + * @ep: pointer to the usb device endpoint structure. + * @status: Status of the data transfer. + */ +static void xudc_nuke(struct xusb_ep *ep, int status) +{ + struct xusb_req *req; + + while (!list_empty(&ep->queue)) { + req = list_first_entry(&ep->queue, struct xusb_req, queue); + xudc_done(ep, req, status); + } +} + +/** + * xudc_ep_set_halt - Stalls/unstalls the given endpoint. + * @_ep: pointer to the usb device endpoint structure. + * @value: value to indicate stall/unstall. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct xusb_ep *ep = to_xusb_ep(_ep); + struct xusb_udc *udc; + unsigned long flags; + u32 epcfgreg; + + if (!_ep || (!ep->desc && ep->epnumber)) { + pr_debug("%s: bad ep or descriptor\n", __func__); + return -EINVAL; + } + udc = ep->udc; + + if (ep->is_in && (!list_empty(&ep->queue)) && value) { + dev_dbg(udc->dev, "requests pending can't halt\n"); + return -EAGAIN; + } + + if (ep->buffer0ready || ep->buffer1ready) { + dev_dbg(udc->dev, "HW buffers busy can't halt\n"); + return -EAGAIN; + } + + spin_lock_irqsave(&udc->lock, flags); + + if (value) { + /* Stall the device.*/ + epcfgreg = udc->read_fn(udc->addr + ep->offset); + epcfgreg |= XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + } else { + /* Unstall the device.*/ + epcfgreg = udc->read_fn(udc->addr + ep->offset); + epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + if (ep->epnumber) { + /* Reset the toggle bit.*/ + epcfgreg = udc->read_fn(ep->udc->addr + ep->offset); + epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + } + } + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/** + * xudc_ep_enable - Enables the given endpoint. + * @ep: pointer to the xusb endpoint structure. + * @desc: pointer to usb endpoint descriptor. + * + * Return: 0 for success and error value on failure + */ +static int __xudc_ep_enable(struct xusb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct xusb_udc *udc = ep->udc; + u32 tmp; + u32 epcfg; + u32 ier; + u16 maxpacket; + + ep->is_in = ((desc->bEndpointAddress & USB_DIR_IN) != 0); + /* Bit 3...0:endpoint number */ + ep->epnumber = (desc->bEndpointAddress & 0x0f); + ep->desc = desc; + ep->ep_usb.desc = desc; + tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + ep->ep_usb.maxpacket = maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + switch (tmp) { + case USB_ENDPOINT_XFER_CONTROL: + dev_dbg(udc->dev, "only one control endpoint\n"); + /* NON- ISO */ + ep->is_iso = 0; + return -EINVAL; + case USB_ENDPOINT_XFER_INT: + /* NON- ISO */ + ep->is_iso = 0; + if (maxpacket > 64) { + dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket); + return -EINVAL; + } + break; + case USB_ENDPOINT_XFER_BULK: + /* NON- ISO */ + ep->is_iso = 0; + if (!(is_power_of_2(maxpacket) && maxpacket >= 8 && + maxpacket <= 512)) { + dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket); + return -EINVAL; + } + break; + case USB_ENDPOINT_XFER_ISOC: + /* ISO */ + ep->is_iso = 1; + break; + } + + ep->buffer0ready = 0; + ep->buffer1ready = 0; + ep->curbufnum = 0; + ep->rambase = rambase[ep->epnumber]; + xudc_epconfig(ep, udc); + + dev_dbg(udc->dev, "Enable Endpoint %d max pkt is %d\n", + ep->epnumber, maxpacket); + + /* Enable the End point.*/ + epcfg = udc->read_fn(udc->addr + ep->offset); + epcfg |= XUSB_EP_CFG_VALID_MASK; + udc->write_fn(udc->addr, ep->offset, epcfg); + if (ep->epnumber) + ep->rambase <<= 2; + + /* Enable buffer completion interrupts for endpoint */ + ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + ier |= (XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK << ep->epnumber); + udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); + + /* for OUT endpoint set buffers ready to receive */ + if (ep->epnumber && !ep->is_in) { + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << ep->epnumber); + ep->buffer0ready = 1; + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + (1 << (ep->epnumber + + XUSB_STATUS_EP_BUFF2_SHIFT))); + ep->buffer1ready = 1; + } + + return 0; +} + +/** + * xudc_ep_enable - Enables the given endpoint. + * @_ep: pointer to the usb endpoint structure. + * @desc: pointer to usb endpoint descriptor. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct xusb_ep *ep; + struct xusb_udc *udc; + unsigned long flags; + int ret; + + if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_debug("%s: bad ep or descriptor\n", __func__); + return -EINVAL; + } + + ep = to_xusb_ep(_ep); + udc = ep->udc; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + dev_dbg(udc->dev, "bogus device state\n"); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&udc->lock, flags); + ret = __xudc_ep_enable(ep, desc); + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +/** + * xudc_ep_disable - Disables the given endpoint. + * @_ep: pointer to the usb endpoint structure. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_disable(struct usb_ep *_ep) +{ + struct xusb_ep *ep; + unsigned long flags; + u32 epcfg; + struct xusb_udc *udc; + + if (!_ep) { + pr_debug("%s: invalid ep\n", __func__); + return -EINVAL; + } + + ep = to_xusb_ep(_ep); + udc = ep->udc; + + spin_lock_irqsave(&udc->lock, flags); + + xudc_nuke(ep, -ESHUTDOWN); + + /* Restore the endpoint's pristine config */ + ep->desc = NULL; + ep->ep_usb.desc = NULL; + + dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber); + /* Disable the endpoint.*/ + epcfg = udc->read_fn(udc->addr + ep->offset); + epcfg &= ~XUSB_EP_CFG_VALID_MASK; + udc->write_fn(udc->addr, ep->offset, epcfg); + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/** + * xudc_ep_alloc_request - Initializes the request queue. + * @_ep: pointer to the usb endpoint structure. + * @gfp_flags: Flags related to the request call. + * + * Return: pointer to request structure on success and a NULL on failure. + */ +static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct xusb_ep *ep = to_xusb_ep(_ep); + struct xusb_udc *udc; + struct xusb_req *req; + + udc = ep->udc; + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) { + dev_err(udc->dev, "%s:not enough memory", __func__); + return NULL; + } + + req->ep = ep; + INIT_LIST_HEAD(&req->queue); + return &req->usb_req; +} + +/** + * xudc_free_request - Releases the request from queue. + * @_ep: pointer to the usb device endpoint structure. + * @_req: pointer to the usb request structure. + */ +static void xudc_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct xusb_req *req = to_xusb_req(_req); + + kfree(req); +} + +/** + * xudc_ep0_queue - Adds the request to endpoint 0 queue. + * @ep0: pointer to the xusb endpoint 0 structure. + * @req: pointer to the xusb request structure. + * + * Return: 0 for success and error value on failure + */ +static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req) +{ + struct xusb_udc *udc = ep0->udc; + u32 length; + u8 *corebuf; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + dev_dbg(udc->dev, "%s, bogus device state\n", __func__); + return -EINVAL; + } + if (!list_empty(&ep0->queue)) { + dev_dbg(udc->dev, "%s:ep0 busy\n", __func__); + return -EBUSY; + } + + req->usb_req.status = -EINPROGRESS; + req->usb_req.actual = 0; + + list_add_tail(&req->queue, &ep0->queue); + + if (udc->setup.bRequestType & USB_DIR_IN) { + prefetch(req->usb_req.buf); + length = req->usb_req.length; + corebuf = (void __force *) ((ep0->rambase << 2) + + udc->addr); + length = req->usb_req.actual = min_t(u32, length, + EP0_MAX_PACKET); + memcpy(corebuf, req->usb_req.buf, length); + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + } else { + if (udc->setup.wLength) { + /* Enable EP0 buffer to receive data */ + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + } else { + xudc_wrstatus(udc); + } + } + + return 0; +} + +/** + * xudc_ep0_queue - Adds the request to endpoint 0 queue. + * @_ep: pointer to the usb endpoint 0 structure. + * @_req: pointer to the usb request structure. + * @gfp_flags: Flags related to the request call. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep0_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct xusb_req *req = to_xusb_req(_req); + struct xusb_ep *ep0 = to_xusb_ep(_ep); + struct xusb_udc *udc = ep0->udc; + unsigned long flags; + int ret; + + spin_lock_irqsave(&udc->lock, flags); + ret = __xudc_ep0_queue(ep0, req); + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +/** + * xudc_ep_queue - Adds the request to endpoint queue. + * @_ep: pointer to the usb endpoint structure. + * @_req: pointer to the usb request structure. + * @gfp_flags: Flags related to the request call. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct xusb_req *req = to_xusb_req(_req); + struct xusb_ep *ep = to_xusb_ep(_ep); + struct xusb_udc *udc = ep->udc; + int ret; + unsigned long flags; + + if (!ep->desc) { + dev_dbg(udc->dev, "%s:queing request to disabled %s\n", + __func__, ep->name); + return -ESHUTDOWN; + } + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + dev_dbg(udc->dev, "%s, bogus device state\n", __func__); + return -EINVAL; + } + + spin_lock_irqsave(&udc->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + if (udc->dma_enabled) { + ret = usb_gadget_map_request(&udc->gadget, &req->usb_req, + ep->is_in); + if (ret) { + dev_dbg(udc->dev, "gadget_map failed ep%d\n", + ep->epnumber); + spin_unlock_irqrestore(&udc->lock, flags); + return -EAGAIN; + } + } + + if (list_empty(&ep->queue)) { + if (ep->is_in) { + dev_dbg(udc->dev, "xudc_write_fifo from ep_queue\n"); + if (!xudc_write_fifo(ep, req)) + req = NULL; + } else { + dev_dbg(udc->dev, "xudc_read_fifo from ep_queue\n"); + if (!xudc_read_fifo(ep, req)) + req = NULL; + } + } + + if (req != NULL) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/** + * xudc_ep_dequeue - Removes the request from the queue. + * @_ep: pointer to the usb device endpoint structure. + * @_req: pointer to the usb request structure. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct xusb_ep *ep = to_xusb_ep(_ep); + struct xusb_req *req = to_xusb_req(_req); + struct xusb_udc *udc = ep->udc; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + /* Make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->usb_req == _req) + break; + } + if (&req->usb_req != _req) { + spin_unlock_irqrestore(&ep->udc->lock, flags); + return -EINVAL; + } + xudc_done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/** + * xudc_ep0_enable - Enables the given endpoint. + * @ep: pointer to the usb endpoint structure. + * @desc: pointer to usb endpoint descriptor. + * + * Return: error always. + * + * endpoint 0 enable should not be called by gadget layer. + */ +static int xudc_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +/** + * xudc_ep0_disable - Disables the given endpoint. + * @ep: pointer to the usb endpoint structure. + * + * Return: error always. + * + * endpoint 0 disable should not be called by gadget layer. + */ +static int xudc_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +static const struct usb_ep_ops xusb_ep0_ops = { + .enable = xudc_ep0_enable, + .disable = xudc_ep0_disable, + .alloc_request = xudc_ep_alloc_request, + .free_request = xudc_free_request, + .queue = xudc_ep0_queue, + .dequeue = xudc_ep_dequeue, + .set_halt = xudc_ep_set_halt, +}; + +static const struct usb_ep_ops xusb_ep_ops = { + .enable = xudc_ep_enable, + .disable = xudc_ep_disable, + .alloc_request = xudc_ep_alloc_request, + .free_request = xudc_free_request, + .queue = xudc_ep_queue, + .dequeue = xudc_ep_dequeue, + .set_halt = xudc_ep_set_halt, +}; + +/** + * xudc_get_frame - Reads the current usb frame number. + * @gadget: pointer to the usb gadget structure. + * + * Return: current frame number for success and error value on failure. + */ +static int xudc_get_frame(struct usb_gadget *gadget) +{ + struct xusb_udc *udc; + int frame; + + if (!gadget) + return -ENODEV; + + udc = to_udc(gadget); + frame = udc->read_fn(udc->addr + XUSB_FRAMENUM_OFFSET); + return frame; +} + +/** + * xudc_wakeup - Send remote wakeup signal to host + * @gadget: pointer to the usb gadget structure. + * + * Return: 0 on success and error on failure + */ +static int xudc_wakeup(struct usb_gadget *gadget) +{ + struct xusb_udc *udc = to_udc(gadget); + u32 crtlreg; + int status = -EINVAL; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + /* Remote wake up not enabled by host */ + if (!udc->remote_wkp) + goto done; + + crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET); + crtlreg |= XUSB_CONTROL_USB_RMTWAKE_MASK; + /* set remote wake up bit */ + udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); + /* + * wait for a while and reset remote wake up bit since this bit + * is not cleared by HW after sending remote wakeup to host. + */ + mdelay(2); + + crtlreg &= ~XUSB_CONTROL_USB_RMTWAKE_MASK; + udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); + status = 0; +done: + spin_unlock_irqrestore(&udc->lock, flags); + return status; +} + +/** + * xudc_pullup - start/stop USB traffic + * @gadget: pointer to the usb gadget structure. + * @is_on: flag to start or stop + * + * Return: 0 always + * + * This function starts/stops SIE engine of IP based on is_on. + */ +static int xudc_pullup(struct usb_gadget *gadget, int is_on) +{ + struct xusb_udc *udc = to_udc(gadget); + unsigned long flags; + u32 crtlreg; + + spin_lock_irqsave(&udc->lock, flags); + + crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET); + if (is_on) + crtlreg |= XUSB_CONTROL_USB_READY_MASK; + else + crtlreg &= ~XUSB_CONTROL_USB_READY_MASK; + + udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/** + * xudc_eps_init - initialize endpoints. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_eps_init(struct xusb_udc *udc) +{ + u32 ep_number; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + + for (ep_number = 0; ep_number < XUSB_MAX_ENDPOINTS; ep_number++) { + struct xusb_ep *ep = &udc->ep[ep_number]; + + if (ep_number) { + list_add_tail(&ep->ep_usb.ep_list, + &udc->gadget.ep_list); + usb_ep_set_maxpacket_limit(&ep->ep_usb, + (unsigned short) ~0); + snprintf(ep->name, EPNAME_SIZE, "ep%d", ep_number); + ep->ep_usb.name = ep->name; + ep->ep_usb.ops = &xusb_ep_ops; + } else { + ep->ep_usb.name = ep0name; + usb_ep_set_maxpacket_limit(&ep->ep_usb, EP0_MAX_PACKET); + ep->ep_usb.ops = &xusb_ep0_ops; + } + + ep->udc = udc; + ep->epnumber = ep_number; + ep->desc = NULL; + /* + * The configuration register address offset between + * each endpoint is 0x10. + */ + ep->offset = XUSB_EP0_CONFIG_OFFSET + (ep_number * 0x10); + ep->is_in = 0; + ep->is_iso = 0; + ep->maxpacket = 0; + xudc_epconfig(ep, udc); + + /* Initialize one queue per endpoint */ + INIT_LIST_HEAD(&ep->queue); + } +} + +/** + * xudc_stop_activity - Stops any further activity on the device. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_stop_activity(struct xusb_udc *udc) +{ + int i; + struct xusb_ep *ep; + + for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) { + ep = &udc->ep[i]; + xudc_nuke(ep, -ESHUTDOWN); + } +} + +/** + * xudc_start - Starts the device. + * @gadget: pointer to the usb gadget structure + * @driver: pointer to gadget driver structure + * + * Return: zero on success and error on failure + */ +static int xudc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct xusb_udc *udc = to_udc(gadget); + struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; + const struct usb_endpoint_descriptor *desc = &config_bulk_out_desc; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&udc->lock, flags); + + if (udc->driver) { + dev_err(udc->dev, "%s is already bound to %s\n", + udc->gadget.name, udc->driver->driver.name); + ret = -EBUSY; + goto err; + } + + /* hook up the driver */ + udc->driver = driver; + udc->gadget.speed = driver->max_speed; + + /* Enable the control endpoint. */ + ret = __xudc_ep_enable(ep0, desc); + + /* Set device address and remote wakeup to 0 */ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); + udc->remote_wkp = 0; +err: + spin_unlock_irqrestore(&udc->lock, flags); + return ret; +} + +/** + * xudc_stop - stops the device. + * @gadget: pointer to the usb gadget structure + * @driver: pointer to usb gadget driver structure + * + * Return: zero always + */ +static int xudc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct xusb_udc *udc = to_udc(gadget); + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->driver = NULL; + + /* Set device address and remote wakeup to 0 */ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); + udc->remote_wkp = 0; + + xudc_stop_activity(udc); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops xusb_udc_ops = { + .get_frame = xudc_get_frame, + .wakeup = xudc_wakeup, + .pullup = xudc_pullup, + .udc_start = xudc_start, + .udc_stop = xudc_stop, +}; + +/** + * xudc_clear_stall_all_ep - clears stall of every endpoint. + * @udc: pointer to the udc structure. + */ +static void xudc_clear_stall_all_ep(struct xusb_udc *udc) +{ + struct xusb_ep *ep; + u32 epcfgreg; + int i; + + for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) { + ep = &udc->ep[i]; + epcfgreg = udc->read_fn(udc->addr + ep->offset); + epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + if (ep->epnumber) { + /* Reset the toggle bit.*/ + epcfgreg = udc->read_fn(udc->addr + ep->offset); + epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + } + } +} + +/** + * xudc_startup_handler - The usb device controller interrupt handler. + * @udc: pointer to the udc structure. + * @intrstatus: The mask value containing the interrupt sources. + * + * This function handles the RESET,SUSPEND,RESUME and DISCONNECT interrupts. + */ +static void xudc_startup_handler(struct xusb_udc *udc, u32 intrstatus) +{ + u32 intrreg; + + if (intrstatus & XUSB_STATUS_RESET_MASK) { + + dev_dbg(udc->dev, "Reset\n"); + + if (intrstatus & XUSB_STATUS_HIGH_SPEED_MASK) + udc->gadget.speed = USB_SPEED_HIGH; + else + udc->gadget.speed = USB_SPEED_FULL; + + xudc_stop_activity(udc); + xudc_clear_stall_all_ep(udc); + udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0); + + /* Set device address and remote wakeup to 0 */ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); + udc->remote_wkp = 0; + + /* Enable the suspend, resume and disconnect */ + intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + intrreg |= XUSB_STATUS_SUSPEND_MASK | XUSB_STATUS_RESUME_MASK | + XUSB_STATUS_DISCONNECT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); + } + if (intrstatus & XUSB_STATUS_SUSPEND_MASK) { + + dev_dbg(udc->dev, "Suspend\n"); + + /* Enable the reset, resume and disconnect */ + intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK | + XUSB_STATUS_DISCONNECT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); + + udc->usb_state = USB_STATE_SUSPENDED; + + if (udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } + } + if (intrstatus & XUSB_STATUS_RESUME_MASK) { + bool condition = (udc->usb_state != USB_STATE_SUSPENDED); + + dev_WARN_ONCE(udc->dev, condition, + "Resume IRQ while not suspended\n"); + + dev_dbg(udc->dev, "Resume\n"); + + /* Enable the reset, suspend and disconnect */ + intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_SUSPEND_MASK | + XUSB_STATUS_DISCONNECT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); + + udc->usb_state = 0; + + if (udc->driver->resume) { + spin_unlock(&udc->lock); + udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } + } + if (intrstatus & XUSB_STATUS_DISCONNECT_MASK) { + + dev_dbg(udc->dev, "Disconnect\n"); + + /* Enable the reset, resume and suspend */ + intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK | + XUSB_STATUS_SUSPEND_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); + + if (udc->driver && udc->driver->disconnect) { + spin_unlock(&udc->lock); + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } + } +} + +/** + * xudc_ep0_stall - Stall endpoint zero. + * @udc: pointer to the udc structure. + * + * This function stalls endpoint zero. + */ +static void xudc_ep0_stall(struct xusb_udc *udc) +{ + u32 epcfgreg; + struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; + + epcfgreg = udc->read_fn(udc->addr + ep0->offset); + epcfgreg |= XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, ep0->offset, epcfgreg); +} + +/** + * xudc_setaddress - executes SET_ADDRESS command + * @udc: pointer to the udc structure. + * + * This function executes USB SET_ADDRESS command + */ +static void xudc_setaddress(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req = udc->req; + int ret; + + req->usb_req.length = 0; + ret = __xudc_ep0_queue(ep0, req); + if (ret == 0) + return; + + dev_err(udc->dev, "Can't respond to SET ADDRESS request\n"); + xudc_ep0_stall(udc); +} + +/** + * xudc_getstatus - executes GET_STATUS command + * @udc: pointer to the udc structure. + * + * This function executes USB GET_STATUS command + */ +static void xudc_getstatus(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req = udc->req; + struct xusb_ep *target_ep; + u16 status = 0; + u32 epcfgreg; + int epnum; + u32 halt; + int ret; + + switch (udc->setup.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + /* Get device status */ + status = 1 << USB_DEVICE_SELF_POWERED; + if (udc->remote_wkp) + status |= (1 << USB_DEVICE_REMOTE_WAKEUP); + break; + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT: + epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; + target_ep = &udc->ep[epnum]; + epcfgreg = udc->read_fn(udc->addr + target_ep->offset); + halt = epcfgreg & XUSB_EP_CFG_STALL_MASK; + if (udc->setup.wIndex & USB_DIR_IN) { + if (!target_ep->is_in) + goto stall; + } else { + if (target_ep->is_in) + goto stall; + } + if (halt) + status = 1 << USB_ENDPOINT_HALT; + break; + default: + goto stall; + } + + req->usb_req.length = 2; + *(u16 *)req->usb_req.buf = cpu_to_le16(status); + ret = __xudc_ep0_queue(ep0, req); + if (ret == 0) + return; +stall: + dev_err(udc->dev, "Can't respond to getstatus request\n"); + xudc_ep0_stall(udc); +} + +/** + * xudc_set_clear_feature - Executes the set feature and clear feature commands. + * @udc: pointer to the usb device controller structure. + * + * Processes the SET_FEATURE and CLEAR_FEATURE commands. + */ +static void xudc_set_clear_feature(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req = udc->req; + struct xusb_ep *target_ep; + u8 endpoint; + u8 outinbit; + u32 epcfgreg; + int flag = (udc->setup.bRequest == USB_REQ_SET_FEATURE ? 1 : 0); + int ret; + + switch (udc->setup.bRequestType) { + case USB_RECIP_DEVICE: + switch (udc->setup.wValue) { + case USB_DEVICE_TEST_MODE: + /* + * The Test Mode will be executed + * after the status phase. + */ + break; + case USB_DEVICE_REMOTE_WAKEUP: + if (flag) + udc->remote_wkp = 1; + else + udc->remote_wkp = 0; + break; + default: + xudc_ep0_stall(udc); + break; + } + break; + case USB_RECIP_ENDPOINT: + if (!udc->setup.wValue) { + endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; + target_ep = &udc->ep[endpoint]; + outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK; + outinbit = outinbit >> 7; + + /* Make sure direction matches.*/ + if (outinbit != target_ep->is_in) { + xudc_ep0_stall(udc); + return; + } + epcfgreg = udc->read_fn(udc->addr + target_ep->offset); + if (!endpoint) { + /* Clear the stall.*/ + epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, + target_ep->offset, epcfgreg); + } else { + if (flag) { + epcfgreg |= XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, + target_ep->offset, + epcfgreg); + } else { + /* Unstall the endpoint.*/ + epcfgreg &= ~(XUSB_EP_CFG_STALL_MASK | + XUSB_EP_CFG_DATA_TOGGLE_MASK); + udc->write_fn(udc->addr, + target_ep->offset, + epcfgreg); + } + } + } + break; + default: + xudc_ep0_stall(udc); + return; + } + + req->usb_req.length = 0; + ret = __xudc_ep0_queue(ep0, req); + if (ret == 0) + return; + + dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); + xudc_ep0_stall(udc); +} + +/** + * xudc_handle_setup - Processes the setup packet. + * @udc: pointer to the usb device controller structure. + * + * Process setup packet and delegate to gadget layer. + */ +static void xudc_handle_setup(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct usb_ctrlrequest setup; + u32 *ep0rambase; + + /* Load up the chapter 9 command buffer.*/ + ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET); + memcpy(&setup, ep0rambase, 8); + + udc->setup = setup; + udc->setup.wValue = cpu_to_le16(setup.wValue); + udc->setup.wIndex = cpu_to_le16(setup.wIndex); + udc->setup.wLength = cpu_to_le16(setup.wLength); + + /* Clear previous requests */ + xudc_nuke(ep0, -ECONNRESET); + + if (udc->setup.bRequestType & USB_DIR_IN) { + /* Execute the get command.*/ + udc->setupseqrx = STATUS_PHASE; + udc->setupseqtx = DATA_PHASE; + } else { + /* Execute the put command.*/ + udc->setupseqrx = DATA_PHASE; + udc->setupseqtx = STATUS_PHASE; + } + + switch (udc->setup.bRequest) { + case USB_REQ_GET_STATUS: + /* Data+Status phase form udc */ + if ((udc->setup.bRequestType & + (USB_DIR_IN | USB_TYPE_MASK)) != + (USB_DIR_IN | USB_TYPE_STANDARD)) + break; + xudc_getstatus(udc); + return; + case USB_REQ_SET_ADDRESS: + /* Status phase from udc */ + if (udc->setup.bRequestType != (USB_DIR_OUT | + USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + xudc_setaddress(udc); + return; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* Requests with no data phase, status phase from udc */ + if ((udc->setup.bRequestType & USB_TYPE_MASK) + != USB_TYPE_STANDARD) + break; + xudc_set_clear_feature(udc); + return; + default: + break; + } + + spin_unlock(&udc->lock); + if (udc->driver->setup(&udc->gadget, &setup) < 0) + xudc_ep0_stall(udc); + spin_lock(&udc->lock); +} + +/** + * xudc_ep0_out - Processes the endpoint 0 OUT token. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_ep0_out(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req; + u8 *ep0rambase; + unsigned int bytes_to_rx; + void *buffer; + + req = list_first_entry(&ep0->queue, struct xusb_req, queue); + + switch (udc->setupseqrx) { + case STATUS_PHASE: + /* + * This resets both state machines for the next + * Setup packet. + */ + udc->setupseqrx = SETUP_PHASE; + udc->setupseqtx = SETUP_PHASE; + req->usb_req.actual = req->usb_req.length; + xudc_done(ep0, req, 0); + break; + case DATA_PHASE: + bytes_to_rx = udc->read_fn(udc->addr + + XUSB_EP_BUF0COUNT_OFFSET); + /* Copy the data to be received from the DPRAM. */ + ep0rambase = (u8 __force *) (udc->addr + + (ep0->rambase << 2)); + buffer = req->usb_req.buf + req->usb_req.actual; + req->usb_req.actual = req->usb_req.actual + bytes_to_rx; + memcpy(buffer, ep0rambase, bytes_to_rx); + + if (req->usb_req.length == req->usb_req.actual) { + /* Data transfer completed get ready for Status stage */ + xudc_wrstatus(udc); + } else { + /* Enable EP0 buffer to receive data */ + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + } + break; + default: + break; + } +} + +/** + * xudc_ep0_in - Processes the endpoint 0 IN token. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_ep0_in(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req; + unsigned int bytes_to_tx; + void *buffer; + u32 epcfgreg; + u16 count = 0; + u16 length; + u8 *ep0rambase; + u8 test_mode = udc->setup.wIndex >> 8; + + req = list_first_entry(&ep0->queue, struct xusb_req, queue); + bytes_to_tx = req->usb_req.length - req->usb_req.actual; + + switch (udc->setupseqtx) { + case STATUS_PHASE: + switch (udc->setup.bRequest) { + case USB_REQ_SET_ADDRESS: + /* Set the address of the device.*/ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, + udc->setup.wValue); + break; + case USB_REQ_SET_FEATURE: + if (udc->setup.bRequestType == + USB_RECIP_DEVICE) { + if (udc->setup.wValue == + USB_DEVICE_TEST_MODE) + udc->write_fn(udc->addr, + XUSB_TESTMODE_OFFSET, + test_mode); + } + break; + } + req->usb_req.actual = req->usb_req.length; + xudc_done(ep0, req, 0); + break; + case DATA_PHASE: + if (!bytes_to_tx) { + /* + * We're done with data transfer, next + * will be zero length OUT with data toggle of + * 1. Setup data_toggle. + */ + epcfgreg = udc->read_fn(udc->addr + ep0->offset); + epcfgreg |= XUSB_EP_CFG_DATA_TOGGLE_MASK; + udc->write_fn(udc->addr, ep0->offset, epcfgreg); + udc->setupseqtx = STATUS_PHASE; + } else { + length = count = min_t(u32, bytes_to_tx, + EP0_MAX_PACKET); + /* Copy the data to be transmitted into the DPRAM. */ + ep0rambase = (u8 __force *) (udc->addr + + (ep0->rambase << 2)); + buffer = req->usb_req.buf + req->usb_req.actual; + req->usb_req.actual = req->usb_req.actual + length; + memcpy(ep0rambase, buffer, length); + } + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + break; + default: + break; + } +} + +/** + * xudc_ctrl_ep_handler - Endpoint 0 interrupt handler. + * @udc: pointer to the udc structure. + * @intrstatus: It's the mask value for the interrupt sources on endpoint 0. + * + * Processes the commands received during enumeration phase. + */ +static void xudc_ctrl_ep_handler(struct xusb_udc *udc, u32 intrstatus) +{ + + if (intrstatus & XUSB_STATUS_SETUP_PACKET_MASK) { + xudc_handle_setup(udc); + } else { + if (intrstatus & XUSB_STATUS_FIFO_BUFF_RDY_MASK) + xudc_ep0_out(udc); + else if (intrstatus & XUSB_STATUS_FIFO_BUFF_FREE_MASK) + xudc_ep0_in(udc); + } +} + +/** + * xudc_nonctrl_ep_handler - Non control endpoint interrupt handler. + * @udc: pointer to the udc structure. + * @epnum: End point number for which the interrupt is to be processed + * @intrstatus: mask value for interrupt sources of endpoints other + * than endpoint 0. + * + * Processes the buffer completion interrupts. + */ +static void xudc_nonctrl_ep_handler(struct xusb_udc *udc, u8 epnum, + u32 intrstatus) +{ + + struct xusb_req *req; + struct xusb_ep *ep; + + ep = &udc->ep[epnum]; + /* Process the End point interrupts.*/ + if (intrstatus & (XUSB_STATUS_EP0_BUFF1_COMP_MASK << epnum)) + ep->buffer0ready = 0; + if (intrstatus & (XUSB_STATUS_EP0_BUFF2_COMP_MASK << epnum)) + ep->buffer1ready = 0; + + if (list_empty(&ep->queue)) + return; + + req = list_first_entry(&ep->queue, struct xusb_req, queue); + + if (ep->is_in) + xudc_write_fifo(ep, req); + else + xudc_read_fifo(ep, req); +} + +/** + * xudc_irq - The main interrupt handler. + * @irq: The interrupt number. + * @_udc: pointer to the usb device controller structure. + * + * Return: IRQ_HANDLED after the interrupt is handled. + */ +static irqreturn_t xudc_irq(int irq, void *_udc) +{ + struct xusb_udc *udc = _udc; + u32 intrstatus; + u32 ier; + u8 index; + u32 bufintr; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + /* + * Event interrupts are level sensitive hence first disable + * IER, read ISR and figure out active interrupts. + */ + ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + ier &= ~XUSB_STATUS_INTR_EVENT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); + + /* Read the Interrupt Status Register.*/ + intrstatus = udc->read_fn(udc->addr + XUSB_STATUS_OFFSET); + + /* Call the handler for the event interrupt.*/ + if (intrstatus & XUSB_STATUS_INTR_EVENT_MASK) { + /* + * Check if there is any action to be done for : + * - USB Reset received {XUSB_STATUS_RESET_MASK} + * - USB Suspend received {XUSB_STATUS_SUSPEND_MASK} + * - USB Resume received {XUSB_STATUS_RESUME_MASK} + * - USB Disconnect received {XUSB_STATUS_DISCONNECT_MASK} + */ + xudc_startup_handler(udc, intrstatus); + } + + /* Check the buffer completion interrupts */ + if (intrstatus & XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK) { + /* Enable Reset, Suspend, Resume and Disconnect */ + ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + ier |= XUSB_STATUS_INTR_EVENT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); + + if (intrstatus & XUSB_STATUS_EP0_BUFF1_COMP_MASK) + xudc_ctrl_ep_handler(udc, intrstatus); + + for (index = 1; index < 8; index++) { + bufintr = ((intrstatus & + (XUSB_STATUS_EP1_BUFF1_COMP_MASK << + (index - 1))) || (intrstatus & + (XUSB_STATUS_EP1_BUFF2_COMP_MASK << + (index - 1)))); + if (bufintr) { + xudc_nonctrl_ep_handler(udc, index, + intrstatus); + } + } + } + + spin_unlock_irqrestore(&udc->lock, flags); + return IRQ_HANDLED; +} + +/** + * xudc_probe - The device probe function for driver initialization. + * @pdev: pointer to the platform device structure. + * + * Return: 0 for success and error value on failure + */ +static int xudc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *res; + struct xusb_udc *udc; + struct xusb_ep *ep0; + int irq; + int ret; + u32 ier; + u8 *buff; + + udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); + if (!udc) + return -ENOMEM; + + /* Create a dummy request for GET_STATUS, SET_ADDRESS */ + udc->req = devm_kzalloc(&pdev->dev, sizeof(struct xusb_req), + GFP_KERNEL); + if (!udc->req) + return -ENOMEM; + + buff = devm_kzalloc(&pdev->dev, STATUSBUFF_SIZE, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + udc->req->usb_req.buf = buff; + + /* Map the registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + udc->addr = devm_ioremap_resource(&pdev->dev, res); + if (!udc->addr) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "unable to get irq\n"); + return irq; + } + ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0, + dev_name(&pdev->dev), udc); + if (ret < 0) { + dev_dbg(&pdev->dev, "unable to request irq %d", irq); + goto fail; + } + + udc->dma_enabled = of_property_read_bool(np, "xlnx,has-builtin-dma"); + + /* Setup gadget structure */ + udc->gadget.ops = &xusb_udc_ops; + udc->gadget.max_speed = USB_SPEED_HIGH; + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO].ep_usb; + udc->gadget.name = driver_name; + + spin_lock_init(&udc->lock); + + /* Check for IP endianness */ + udc->write_fn = xudc_write32_be; + udc->read_fn = xudc_read32_be; + udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, TEST_J); + if ((udc->read_fn(udc->addr + XUSB_TESTMODE_OFFSET)) + != TEST_J) { + udc->write_fn = xudc_write32; + udc->read_fn = xudc_read32; + } + udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0); + + xudc_eps_init(udc); + + ep0 = &udc->ep[0]; + + /* Set device address to 0.*/ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); + + ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (ret) + goto fail; + + udc->dev = &udc->gadget.dev; + + /* Enable the interrupts.*/ + ier = XUSB_STATUS_GLOBAL_INTR_MASK | XUSB_STATUS_INTR_EVENT_MASK | + XUSB_STATUS_FIFO_BUFF_RDY_MASK | XUSB_STATUS_FIFO_BUFF_FREE_MASK | + XUSB_STATUS_SETUP_PACKET_MASK | + XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK; + + udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); + + platform_set_drvdata(pdev, udc); + + dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to 0x%08X %s\n", + driver_name, (u32)res->start, (u32 __force)udc->addr, + udc->dma_enabled ? "with DMA" : "without DMA"); + + return 0; +fail: + dev_err(&pdev->dev, "probe failed, %d\n", ret); + return ret; +} + +/** + * xudc_remove - Releases the resources allocated during the initialization. + * @pdev: pointer to the platform device structure. + * + * Return: 0 always + */ +static int xudc_remove(struct platform_device *pdev) +{ + struct xusb_udc *udc = platform_get_drvdata(pdev); + + usb_del_gadget_udc(&udc->gadget); + + return 0; +} + +/* Match table for of_platform binding */ +static const struct of_device_id usb_of_match[] = { + { .compatible = "xlnx,usb2-device-4.00.a", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, usb_of_match); + +static struct platform_driver xudc_driver = { + .driver = { + .name = driver_name, + .of_match_table = usb_of_match, + }, + .probe = xudc_probe, + .remove = xudc_remove, +}; + +module_platform_driver(xudc_driver); + +MODULE_DESCRIPTION("Xilinx udc driver"); +MODULE_AUTHOR("Xilinx, Inc"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 03314f861bee..a8a30b1d4167 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -26,6 +26,11 @@ config USB_XHCI_HCD if USB_XHCI_HCD +config USB_XHCI_PCI + tristate + depends on PCI + default y + config USB_XHCI_PLATFORM tristate @@ -37,6 +42,14 @@ config USB_XHCI_MVEBU Say 'Y' to enable the support for the xHCI host controller found in Marvell Armada 375/38x ARM SOCs. +config USB_XHCI_RCAR + tristate "xHCI support for Renesas R-Car SoCs" + select USB_XHCI_PLATFORM + depends on ARCH_SHMOBILE || COMPILE_TEST + ---help--- + Say 'Y' to enable the support for the xHCI host controller + found in Renesas R-Car ARM SoCs. + endif # USB_XHCI_HCD config USB_EHCI_HCD @@ -118,7 +131,7 @@ config XPS_USB_HCD_XILINX select USB_EHCI_BIG_ENDIAN_DESC select USB_EHCI_BIG_ENDIAN_MMIO ---help--- - Xilinx xps USB host controller core is EHCI compilant and has + Xilinx xps USB host controller core is EHCI compliant and has transaction translator built-in. It can be configured to either support both high speed and full speed devices, or high speed devices only. @@ -166,6 +179,15 @@ config USB_EHCI_HCD_SPEAR Enables support for the on-chip EHCI controller on ST SPEAr chips. +config USB_EHCI_HCD_STI + tristate "Support for ST STiHxxx on-chip EHCI USB controller" + depends on ARCH_STI && OF + select GENERIC_PHY + select USB_EHCI_HCD_PLATFORM + help + Enable support for the on-chip EHCI controller found on + STMicroelectronics consumer electronics SoC's. + config USB_EHCI_HCD_AT91 tristate "Support for Atmel on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_AT91 @@ -394,6 +416,15 @@ config USB_OHCI_HCD_SPEAR Enables support for the on-chip OHCI controller on ST SPEAr chips. +config USB_OHCI_HCD_STI + tristate "Support for ST STiHxxx on-chip OHCI USB controller" + depends on ARCH_STI && OF + select GENERIC_PHY + select USB_OHCI_HCD_PLATFORM + help + Enable support for the on-chip OHCI controller found on + STMicroelectronics consumer electronics SoC's. + config USB_OHCI_HCD_S3C2410 tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series" depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX) @@ -701,7 +732,7 @@ config USB_WHCI_HCD config USB_HWA_HCD tristate "Host Wire Adapter (HWA) driver" - depends on UWB + depends on USB && UWB select USB_WUSB select UWB_HWA help diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index af89a903d97e..348c24321562 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -15,19 +15,22 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o xhci-hcd-y := xhci.o xhci-mem.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o xhci-hcd-y += xhci-trace.o -xhci-hcd-$(CONFIG_PCI) += xhci-pci.o -ifneq ($(CONFIG_USB_XHCI_PLATFORM), ) - xhci-hcd-y += xhci-plat.o +xhci-plat-hcd-y := xhci-plat.o ifneq ($(CONFIG_USB_XHCI_MVEBU), ) - xhci-hcd-y += xhci-mvebu.o + xhci-plat-hcd-y += xhci-mvebu.o endif +ifneq ($(CONFIG_USB_XHCI_RCAR), ) + xhci-plat-hcd-y += xhci-rcar.o endif obj-$(CONFIG_USB_WHCI_HCD) += whci/ obj-$(CONFIG_PCI) += pci-quirks.o +obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o +obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o + obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o @@ -35,6 +38,7 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o +obj-$(CONFIG_USB_EHCI_HCD_STI) += ehci-st.o obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o @@ -52,6 +56,7 @@ obj-$(CONFIG_USB_OHCI_EXYNOS) += ohci-exynos.o obj-$(CONFIG_USB_OHCI_HCD_OMAP1) += ohci-omap.o obj-$(CONFIG_USB_OHCI_HCD_OMAP3) += ohci-omap3.o obj-$(CONFIG_USB_OHCI_HCD_SPEAR) += ohci-spear.o +obj-$(CONFIG_USB_OHCI_HCD_STI) += ohci-st.o obj-$(CONFIG_USB_OHCI_HCD_AT91) += ohci-at91.o obj-$(CONFIG_USB_OHCI_HCD_S3C2410) += ohci-s3c2410.o obj-$(CONFIG_USB_OHCI_HCD_LPC32XX) += ohci-nxp.o diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c index 205f4a336583..cd6d0afb6b8f 100644 --- a/drivers/usb/host/bcma-hcd.c +++ b/drivers/usb/host/bcma-hcd.c @@ -237,7 +237,7 @@ static int bcma_hcd_probe(struct bcma_device *dev) bcma_hcd_init_chip(dev); /* In AI chips EHCI is addrspace 0, OHCI is 1 */ - ohci_addr = dev->addr1; + ohci_addr = dev->addr_s[0]; if ((chipinfo->id == 0x5357 || chipinfo->id == 0x4749) && chipinfo->rev == 0) ohci_addr = 0x18009000; diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index d1c76216350f..7189f2e32ac2 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -21,11 +21,8 @@ #include <linux/of_gpio.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> -#include <linux/usb/phy.h> -#include <linux/usb/samsung_usb_phy.h> #include <linux/usb.h> #include <linux/usb/hcd.h> -#include <linux/usb/otg.h> #include "ehci.h" @@ -47,9 +44,7 @@ static struct hc_driver __read_mostly exynos_ehci_hc_driver; struct exynos_ehci_hcd { struct clk *clk; - struct usb_phy *phy; - struct usb_otg *otg; - struct phy *phy_g[PHY_NUMBER]; + struct phy *phy[PHY_NUMBER]; }; #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv) @@ -60,20 +55,9 @@ static int exynos_ehci_get_phy(struct device *dev, struct device_node *child; struct phy *phy; int phy_number; - int ret = 0; - - exynos_ehci->phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); - if (IS_ERR(exynos_ehci->phy)) { - ret = PTR_ERR(exynos_ehci->phy); - if (ret != -ENXIO && ret != -ENODEV) { - dev_err(dev, "no usb2 phy configured\n"); - return ret; - } - dev_dbg(dev, "Failed to get usb2 phy\n"); - } else { - exynos_ehci->otg = exynos_ehci->phy->otg; - } + int ret; + /* Get PHYs for the controller */ for_each_available_child_of_node(dev->of_node, child) { ret = of_property_read_u32(child, "reg", &phy_number); if (ret) { @@ -88,20 +72,22 @@ static int exynos_ehci_get_phy(struct device *dev, return -EINVAL; } - phy = devm_of_phy_get(dev, child, 0); + phy = devm_of_phy_get(dev, child, NULL); + exynos_ehci->phy[phy_number] = phy; of_node_put(child); if (IS_ERR(phy)) { ret = PTR_ERR(phy); - if (ret != -ENOSYS && ret != -ENODEV) { - dev_err(dev, "no usb2 phy configured\n"); + if (ret == -EPROBE_DEFER) { + return ret; + } else if (ret != -ENOSYS && ret != -ENODEV) { + dev_err(dev, + "Error retrieving usb2 phy: %d\n", ret); return ret; } - dev_dbg(dev, "Failed to get usb2 phy\n"); } - exynos_ehci->phy_g[phy_number] = phy; } - return ret; + return 0; } static int exynos_ehci_phy_enable(struct device *dev) @@ -111,16 +97,13 @@ static int exynos_ehci_phy_enable(struct device *dev) int i; int ret = 0; - if (!IS_ERR(exynos_ehci->phy)) - return usb_phy_init(exynos_ehci->phy); - for (i = 0; ret == 0 && i < PHY_NUMBER; i++) - if (!IS_ERR(exynos_ehci->phy_g[i])) - ret = phy_power_on(exynos_ehci->phy_g[i]); + if (!IS_ERR(exynos_ehci->phy[i])) + ret = phy_power_on(exynos_ehci->phy[i]); if (ret) for (i--; i >= 0; i--) - if (!IS_ERR(exynos_ehci->phy_g[i])) - phy_power_off(exynos_ehci->phy_g[i]); + if (!IS_ERR(exynos_ehci->phy[i])) + phy_power_off(exynos_ehci->phy[i]); return ret; } @@ -131,14 +114,9 @@ static void exynos_ehci_phy_disable(struct device *dev) struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd); int i; - if (!IS_ERR(exynos_ehci->phy)) { - usb_phy_shutdown(exynos_ehci->phy); - return; - } - for (i = 0; i < PHY_NUMBER; i++) - if (!IS_ERR(exynos_ehci->phy_g[i])) - phy_power_off(exynos_ehci->phy_g[i]); + if (!IS_ERR(exynos_ehci->phy[i])) + phy_power_off(exynos_ehci->phy[i]); } static void exynos_setup_vbus_gpio(struct device *dev) @@ -231,9 +209,6 @@ skip_phy: goto fail_io; } - if (exynos_ehci->otg) - exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self); - err = exynos_ehci_phy_enable(&pdev->dev); if (err) { dev_err(&pdev->dev, "Failed to enable USB phy\n"); @@ -273,9 +248,6 @@ static int exynos_ehci_remove(struct platform_device *pdev) usb_remove_hcd(hcd); - if (exynos_ehci->otg) - exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self); - exynos_ehci_phy_disable(&pdev->dev); clk_disable_unprepare(exynos_ehci->clk); @@ -298,9 +270,6 @@ static int exynos_ehci_suspend(struct device *dev) if (rc) return rc; - if (exynos_ehci->otg) - exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self); - exynos_ehci_phy_disable(dev); clk_disable_unprepare(exynos_ehci->clk); @@ -316,9 +285,6 @@ static int exynos_ehci_resume(struct device *dev) clk_prepare_enable(exynos_ehci->clk); - if (exynos_ehci->otg) - exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self); - ret = exynos_ehci_phy_enable(dev); if (ret) { dev_err(dev, "Failed to enable USB phy\n"); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index cf2734b532a7..2d2ae8db439e 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -136,15 +136,15 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - hcd->phy = usb_get_phy(USB_PHY_TYPE_USB2); + hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, phy=0x%p\n", - hcd, ehci, hcd->phy); + hcd, ehci, hcd->usb_phy); - if (!IS_ERR_OR_NULL(hcd->phy)) { - retval = otg_set_host(hcd->phy->otg, + if (!IS_ERR_OR_NULL(hcd->usb_phy)) { + retval = otg_set_host(hcd->usb_phy->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_phy(hcd->phy); + usb_put_phy(hcd->usb_phy); goto err2; } } else { @@ -181,9 +181,9 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, { struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); - if (!IS_ERR_OR_NULL(hcd->phy)) { - otg_set_host(hcd->phy->otg, NULL); - usb_put_phy(hcd->phy); + if (!IS_ERR_OR_NULL(hcd->usb_phy)) { + otg_set_host(hcd->usb_phy->otg, NULL); + usb_put_phy(hcd->usb_phy); } usb_remove_hcd(hcd); @@ -627,7 +627,7 @@ static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) if (!(status & PORT_CONNECT)) return -ENODEV; - /* khubd will finish the reset later */ + /* hub_wq will finish the reset later */ if (ehci_is_TDI(ehci)) { writel(PORT_RESET | (status & ~(PORT_CSC | PORT_PEC | PORT_OCC)), diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 81cda09b47e3..15feaf924b71 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -788,7 +788,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) continue; /* start 20 msec resume signaling from this port, - * and make khubd collect PORT_STAT_C_SUSPEND to + * and make hub_wq collect PORT_STAT_C_SUSPEND to * stop that signaling. Use 5 ms extra for safety, * like usb_port_resume() does. */ @@ -965,8 +965,6 @@ rescan: } qh->exception = 1; - if (ehci->rh_state < EHCI_RH_RUNNING) - qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: WARN_ON(!list_empty(&qh->qtd_list)); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index cc305c71ac3d..5728829cf6ef 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -656,7 +656,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) /* * Return status information even for ports with OWNER set. - * Otherwise khubd wouldn't see the disconnect event when a + * Otherwise hub_wq wouldn't see the disconnect event when a * high-speed device is switched over to the companion * controller by the user. */ @@ -902,7 +902,7 @@ int ehci_hub_control( /* * Even if OWNER is set, so the port is owned by the - * companion controller, khubd needs to be able to clear + * companion controller, hub_wq needs to be able to clear * the port-change status bits (especially * USB_PORT_STAT_C_CONNECTION). */ @@ -922,7 +922,7 @@ int ehci_hub_control( #ifdef CONFIG_USB_OTG if ((hcd->self.otg_port == (wIndex + 1)) && hcd->self.b_hnp_enable) { - otg_start_hnp(hcd->phy->otg); + otg_start_hnp(hcd->usb_phy->otg); break; } #endif @@ -1000,7 +1000,7 @@ int ehci_hub_control( * However, not all EHCI implementations do this * automatically, even if they _do_ support per-port * power switching; they're allowed to just limit the - * current. khubd will turn the power back on. + * current. hub_wq will turn the power back on. */ if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle)) && HCS_PPC(ehci->hcs_params)) { @@ -1085,7 +1085,7 @@ int ehci_hub_control( } /* - * Even if OWNER is set, there's no harm letting khubd + * Even if OWNER is set, there's no harm letting hub_wq * see the wPortStatus values (they should all be 0 except * for PORT_POWER anyway). */ @@ -1230,7 +1230,7 @@ int ehci_hub_control( if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) { spin_unlock_irqrestore(&ehci->lock, flags); retval = ehset_single_step_set_feature(hcd, - wIndex); + wIndex + 1); spin_lock_irqsave(&ehci->lock, flags); break; } diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index c0fb6a8ae6a3..b6205fac3567 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -209,7 +209,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) ehci->periodic = (__le32 *) dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller, ehci->periodic_size * sizeof(__le32), - &ehci->periodic_dma, 0); + &ehci->periodic_dma, flags); if (ehci->periodic == NULL) { goto fail; } diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 982c09bebe0f..9dc2118ae808 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -124,7 +124,7 @@ static int ehci_msm_probe(struct platform_device *pdev) goto put_hcd; } - hcd->phy = phy; + hcd->usb_phy = phy; device_init_wakeup(&pdev->dev, 1); /* * OTG device parent of HCD takes care of putting @@ -151,7 +151,7 @@ static int ehci_msm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - otg_set_host(hcd->phy->otg, NULL); + otg_set_host(hcd->usb_phy->otg, NULL); /* FIXME: need to call usb_remove_hcd() here? */ @@ -190,7 +190,7 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = { .resume = ehci_msm_pm_resume, }; -static struct of_device_id msm_ehci_dt_match[] = { +static const struct of_device_id msm_ehci_dt_match[] = { { .compatible = "qcom,ehci-host", }, {} }; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 3e86bf4371b3..ca7b964124af 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -35,6 +35,21 @@ static const char hcd_name[] = "ehci-pci"; #define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70 /*-------------------------------------------------------------------------*/ +#define PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC 0x0939 +static inline bool is_intel_quark_x1000(struct pci_dev *pdev) +{ + return pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC; +} + +/* + * 0x84 is the offset of in/out threshold register, + * and it is the same offset as the register of 'hostpc'. + */ +#define intel_quark_x1000_insnreg01 hostpc + +/* Maximum usable threshold value is 0x7f dwords for both IN and OUT */ +#define INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD 0x007f007f /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) @@ -50,6 +65,16 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) if (!retval) ehci_dbg(ehci, "MWI active\n"); + /* Reset the threshold limit */ + if (is_intel_quark_x1000(pdev)) { + /* + * For the Intel QUARK X1000, raise the I/O threshold to the + * maximum usable value in order to improve performance. + */ + ehci_writel(ehci, INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD, + ehci->regs->intel_quark_x1000_insnreg01); + } + return 0; } diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 1d59958ad0ce..1355ff0946b9 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -150,7 +150,7 @@ static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) return 0; } -static struct of_device_id spear_ehci_id_table[] = { +static const struct of_device_id spear_ehci_id_table[] = { { .compatible = "st,spear600-ehci", }, { }, }; diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c new file mode 100644 index 000000000000..7e4bd39cf757 --- /dev/null +++ b/drivers/usb/host/ehci-st.c @@ -0,0 +1,375 @@ +/* + * ST EHCI driver + * + * Copyright (C) 2014 STMicroelectronics – All Rights Reserved + * + * Author: Peter Griffin <peter.griffin@linaro.org> + * + * Derived from ehci-platform.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/hrtimer.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/usb/ehci_pdriver.h> + +#include "ehci.h" + +#define USB_MAX_CLKS 3 + +struct st_ehci_platform_priv { + struct clk *clks[USB_MAX_CLKS]; + struct clk *clk48; + struct reset_control *rst; + struct reset_control *pwr; + struct phy *phy; +}; + +#define DRIVER_DESC "EHCI STMicroelectronics driver" + +#define hcd_to_ehci_priv(h) \ + ((struct st_ehci_platform_priv *)hcd_to_ehci(h)->priv) + +static const char hcd_name[] = "ehci-st"; + +#define EHCI_CAPS_SIZE 0x10 +#define AHB2STBUS_INSREG01 (EHCI_CAPS_SIZE + 0x84) + +static int st_ehci_platform_reset(struct usb_hcd *hcd) +{ + struct platform_device *pdev = to_platform_device(hcd->self.controller); + struct usb_ehci_pdata *pdata = dev_get_platdata(&pdev->dev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + u32 threshold; + + /* Set EHCI packet buffer IN/OUT threshold to 128 bytes */ + threshold = 128 | (128 << 16); + writel(threshold, hcd->regs + AHB2STBUS_INSREG01); + + ehci->caps = hcd->regs + pdata->caps_offset; + retval = ehci_setup(hcd); + if (retval) + return retval; + + return 0; +} + +static int st_ehci_platform_power_on(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct st_ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk, ret; + + ret = reset_control_deassert(priv->pwr); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rst); + if (ret) + goto err_assert_power; + + /* some SoCs don't have a dedicated 48Mhz clock, but those that do + need the rate to be explicitly set */ + if (priv->clk48) { + ret = clk_set_rate(priv->clk48, 48000000); + if (ret) + goto err_assert_reset; + } + + for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); + if (ret) + goto err_disable_clks; + } + + 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]); +err_assert_reset: + reset_control_assert(priv->rst); +err_assert_power: + reset_control_assert(priv->pwr); + + return ret; +} + +static void st_ehci_platform_power_off(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct st_ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk; + + reset_control_assert(priv->pwr); + + reset_control_assert(priv->rst); + + phy_power_off(priv->phy); + + phy_exit(priv->phy); + + for (clk = USB_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 = st_ehci_platform_reset, + .extra_priv_size = sizeof(struct st_ehci_platform_priv), +}; + +static struct usb_ehci_pdata ehci_platform_defaults = { + .power_on = st_ehci_platform_power_on, + .power_suspend = st_ehci_platform_power_off, + .power_off = st_ehci_platform_power_off, +}; + +static int st_ehci_platform_probe(struct platform_device *dev) +{ + struct usb_hcd *hcd; + struct resource *res_mem; + struct usb_ehci_pdata *pdata = &ehci_platform_defaults; + struct st_ehci_platform_priv *priv; + struct ehci_hcd *ehci; + int err, irq, clk = 0; + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no irq provided"); + return irq; + } + res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res_mem) { + dev_err(&dev->dev, "no memory resource provided"); + 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); + + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); + goto err_put_hcd; + } + + for (clk = 0; clk < USB_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; + } + } + + /* some SoCs don't have a dedicated 48Mhz clock, but those that + do need the rate to be explicitly set */ + priv->clk48 = devm_clk_get(&dev->dev, "clk48"); + if (IS_ERR(priv->clk48)) { + dev_info(&dev->dev, "48MHz clk not found\n"); + priv->clk48 = NULL; + } + + priv->pwr = devm_reset_control_get_optional(&dev->dev, "power"); + if (IS_ERR(priv->pwr)) { + err = PTR_ERR(priv->pwr); + if (err == -EPROBE_DEFER) + goto err_put_clks; + priv->pwr = NULL; + } + + priv->rst = devm_reset_control_get_optional(&dev->dev, "softreset"); + if (IS_ERR(priv->rst)) { + err = PTR_ERR(priv->rst); + if (err == -EPROBE_DEFER) + goto err_put_clks; + priv->rst = NULL; + } + + if (pdata->power_on) { + err = pdata->power_on(dev); + if (err < 0) + goto err_put_clks; + } + + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + + hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + if (IS_ERR(hcd->regs)) { + err = PTR_ERR(hcd->regs); + goto err_put_clks; + } + + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) + goto err_put_clks; + + device_wakeup_enable(hcd->self.controller); + platform_set_drvdata(dev, hcd); + + return err; + +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; +} + +static int st_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 st_ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk; + + usb_remove_hcd(hcd); + + if (pdata->power_off) + pdata->power_off(dev); + + for (clk = 0; clk < USB_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; + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int st_ehci_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_ehci_pdata *pdata = dev_get_platdata(dev); + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + bool do_wakeup = device_may_wakeup(dev); + int ret; + + ret = ehci_suspend(hcd, do_wakeup); + if (ret) + return ret; + + if (pdata->power_suspend) + pdata->power_suspend(pdev); + + pinctrl_pm_select_sleep_state(dev); + + return ret; +} + +static int st_ehci_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_ehci_pdata *pdata = dev_get_platdata(dev); + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + int err; + + pinctrl_pm_select_default_state(dev); + + if (pdata->power_on) { + err = pdata->power_on(pdev); + if (err < 0) + return err; + } + + ehci_resume(hcd, false); + return 0; +} + +static SIMPLE_DEV_PM_OPS(st_ehci_pm_ops, st_ehci_suspend, st_ehci_resume); + +#endif /* CONFIG_PM_SLEEP */ + +static const struct of_device_id st_ehci_ids[] = { + { .compatible = "st,st-ehci-300x", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, st_ehci_ids); + +static struct platform_driver ehci_platform_driver = { + .probe = st_ehci_platform_probe, + .remove = st_ehci_platform_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "st-ehci", +#ifdef CONFIG_PM_SLEEP + .pm = &st_ehci_pm_ops, +#endif + .of_match_table = st_ehci_ids, + } +}; + +static int __init ehci_platform_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides); + return platform_driver_register(&ehci_platform_driver); +} +module_init(ehci_platform_init); + +static void __exit ehci_platform_cleanup(void) +{ + platform_driver_unregister(&ehci_platform_driver); +} +module_exit(ehci_platform_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 6fdcb8ad2296..aaa01971efe9 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -46,6 +46,7 @@ #define DRV_NAME "tegra-ehci" static struct hc_driver __read_mostly tegra_ehci_hc_driver; +static bool usb1_reset_attempted; struct tegra_ehci_soc_config { bool has_hostpc; @@ -60,6 +61,61 @@ struct tegra_ehci_hcd { enum tegra_usb_phy_port_speed port_speed; }; +/* + * The 1st USB controller contains some UTMI pad registers that are global for + * all the controllers on the chip. Those registers are also cleared when + * reset is asserted to the 1st controller. This means that the 1st controller + * can only be reset when no other controlled has finished probing. So we'll + * reset the 1st controller before doing any other setup on any of the + * controllers, and then never again. + * + * Since this is a PHY issue, the Tegra PHY driver should probably be doing + * the resetting of the USB controllers. But to keep compatibility with old + * device trees that don't have reset phandles in the PHYs, do it here. + * Those old DTs will be vulnerable to total USB breakage if the 1st EHCI + * device isn't the first one to finish probing, so warn them. + */ +static int tegra_reset_usb_controller(struct platform_device *pdev) +{ + struct device_node *phy_np; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct tegra_ehci_hcd *tegra = + (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv; + + phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0); + if (!phy_np) + return -ENOENT; + + if (!usb1_reset_attempted) { + struct reset_control *usb1_reset; + + usb1_reset = of_reset_control_get(phy_np, "usb"); + if (IS_ERR(usb1_reset)) { + dev_warn(&pdev->dev, + "can't get utmi-pads reset from the PHY\n"); + dev_warn(&pdev->dev, + "continuing, but please update your DT\n"); + } else { + reset_control_assert(usb1_reset); + udelay(1); + reset_control_deassert(usb1_reset); + } + + reset_control_put(usb1_reset); + usb1_reset_attempted = true; + } + + if (!of_property_read_bool(phy_np, "nvidia,has-utmi-pad-registers")) { + reset_control_assert(tegra->rst); + udelay(1); + reset_control_deassert(tegra->rst); + } + + of_node_put(phy_np); + + return 0; +} + static int tegra_ehci_internal_port_reset( struct ehci_hcd *ehci, u32 __iomem *portsc_reg @@ -150,7 +206,7 @@ static int tegra_ehci_hub_control( if (tegra->port_resuming && !(temp & PORT_SUSPEND)) { /* Resume completed, re-enable disconnect detection */ tegra->port_resuming = 0; - tegra_usb_phy_postresume(hcd->phy); + tegra_usb_phy_postresume(hcd->usb_phy); } } @@ -203,7 +259,7 @@ static int tegra_ehci_hub_control( goto done; /* Disable disconnect detection during port resume */ - tegra_usb_phy_preresume(hcd->phy); + tegra_usb_phy_preresume(hcd->usb_phy); ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); @@ -326,7 +382,7 @@ static const struct tegra_ehci_soc_config tegra20_soc_config = { .has_hostpc = false, }; -static struct of_device_id tegra_ehci_of_match[] = { +static const struct of_device_id tegra_ehci_of_match[] = { { .compatible = "nvidia,tegra30-ehci", .data = &tegra30_soc_config }, { .compatible = "nvidia,tegra20-ehci", .data = &tegra20_soc_config }, { }, @@ -389,16 +445,16 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (err) goto cleanup_hcd_create; - reset_control_assert(tegra->rst); - udelay(1); - reset_control_deassert(tegra->rst); + err = tegra_reset_usb_controller(pdev); + if (err) + goto cleanup_clk_en; u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); if (IS_ERR(u_phy)) { err = PTR_ERR(u_phy); goto cleanup_clk_en; } - hcd->phy = u_phy; + hcd->usb_phy = u_phy; tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node, "nvidia,needs-double-reset"); @@ -419,7 +475,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) ehci->caps = hcd->regs + 0x100; ehci->has_hostpc = soc_config->has_hostpc; - err = usb_phy_init(hcd->phy); + err = usb_phy_init(hcd->usb_phy); if (err) { dev_err(&pdev->dev, "Failed to initialize phy\n"); goto cleanup_clk_en; @@ -434,7 +490,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) } u_phy->otg->host = hcd_to_bus(hcd); - err = usb_phy_set_suspend(hcd->phy, 0); + err = usb_phy_set_suspend(hcd->usb_phy, 0); if (err) { dev_err(&pdev->dev, "Failed to power on the phy\n"); goto cleanup_phy; @@ -461,7 +517,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) cleanup_otg_set_host: otg_set_host(u_phy->otg, NULL); cleanup_phy: - usb_phy_shutdown(hcd->phy); + usb_phy_shutdown(hcd->usb_phy); cleanup_clk_en: clk_disable_unprepare(tegra->clk); cleanup_hcd_create: @@ -475,14 +531,15 @@ static int tegra_ehci_remove(struct platform_device *pdev) struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv; - otg_set_host(hcd->phy->otg, NULL); + otg_set_host(hcd->usb_phy->otg, NULL); - usb_phy_shutdown(hcd->phy); + usb_phy_shutdown(hcd->usb_phy); usb_remove_hcd(hcd); - usb_put_hcd(hcd); clk_disable_unprepare(tegra->clk); + usb_put_hcd(hcd); + return 0; } diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index fe57710753e8..a2328361dc80 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -31,6 +31,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_address.h> +#include <linux/of_irq.h> /** * ehci_xilinx_port_handed_over - hand the port out if failed to enable it diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c index f238cb37305c..b58e7a60913a 100644 --- a/drivers/usb/host/fhci-dbg.c +++ b/drivers/usb/host/fhci-dbg.c @@ -129,11 +129,7 @@ void fhci_dfs_destroy(struct fhci_hcd *fhci) if (!fhci->dfs_root) return; - if (fhci->dfs_irq_stat) - debugfs_remove(fhci->dfs_irq_stat); - - if (fhci->dfs_regs) - debugfs_remove(fhci->dfs_regs); - + debugfs_remove(fhci->dfs_irq_stat); + debugfs_remove(fhci->dfs_regs); debugfs_remove(fhci->dfs_root); } diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 1cf68eaf2ed8..a1a1ef521436 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -360,12 +360,12 @@ static int fhci_start(struct usb_hcd *hcd) hcd->state = HC_STATE_RUNNING; /* - * From here on, khubd concurrently accesses the root + * From here on, hub_wq concurrently accesses the root * hub; drivers will be talking to enumerated devices. - * (On restart paths, khubd already knows about the root + * (On restart paths, hub_wq already knows about the root * hub and could find work as soon as we wrote FLAG_CF.) * - * Before this point the HC was idle/ready. After, khubd + * Before this point the HC was idle/ready. After, hub_wq * and device drivers may start it running. */ fhci_usb_enable(fhci); diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 98a89d16cc3e..3de1278677d0 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1483,7 +1483,7 @@ fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) /* * Return status information even for ports with OWNER set. - * Otherwise khubd wouldn't see the disconnect event when a + * Otherwise hub_wq wouldn't see the disconnect event when a * high-speed device is switched over to the companion * controller by the user. */ @@ -1572,7 +1572,7 @@ static int fotg210_hub_control( /* * Even if OWNER is set, so the port is owned by the - * companion controller, khubd needs to be able to clear + * companion controller, hub_wq needs to be able to clear * the port-change status bits (especially * USB_PORT_STAT_C_CONNECTION). */ @@ -1723,7 +1723,7 @@ static int fotg210_hub_control( } /* - * Even if OWNER is set, there's no harm letting khubd + * Even if OWNER is set, there's no harm letting hub_wq * see the wPortStatus values (they should all be 0 except * for PORT_POWER anyway). */ @@ -5445,7 +5445,7 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) fotg210->reset_done[0] == 0) { /* start 20 msec resume signaling from this port, - * and make khubd collect PORT_STAT_C_SUSPEND to + * and make hub_wq collect PORT_STAT_C_SUSPEND to * stop that signaling. Use 5 ms extra for safety, * like usb_port_resume() does. */ @@ -5838,41 +5838,17 @@ static int fotg210_hcd_probe(struct platform_device *pdev) goto fail_create_hcd; } + hcd->has_tt = 1; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); + goto failed; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - hcd->has_tt = 1; - - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - fotg210_fotg210_hc_driver.description)) { - dev_dbg(dev, "controller already in use\n"); - retval = -EBUSY; - goto fail_request_resource; - } - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; - } - - hcd->regs = ioremap_nocache(res->start, resource_size(res)); - if (hcd->regs == NULL) { - dev_dbg(dev, "error mapping memory\n"); - retval = -EFAULT; - goto fail_ioremap; - } fotg210 = hcd_to_fotg210(hcd); @@ -5880,24 +5856,20 @@ static int fotg210_hcd_probe(struct platform_device *pdev) retval = fotg210_setup(hcd); if (retval) - goto fail_add_hcd; + goto failed; fotg210_init(fotg210); retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) { dev_err(dev, "failed to add hcd with err %d\n", retval); - goto fail_add_hcd; + goto failed; } device_wakeup_enable(hcd->self.controller); return retval; -fail_add_hcd: - iounmap(hcd->regs); -fail_ioremap: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -fail_request_resource: +failed: usb_put_hcd(hcd); fail_create_hcd: dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); @@ -5918,8 +5890,6 @@ static int fotg210_hcd_remove(struct platform_device *pdev) return 0; usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/fusbh200-hcd.c b/drivers/usb/host/fusbh200-hcd.c index ba9499060f63..abe42f31559f 100644 --- a/drivers/usb/host/fusbh200-hcd.c +++ b/drivers/usb/host/fusbh200-hcd.c @@ -1441,7 +1441,7 @@ fusbh200_hub_status_data (struct usb_hcd *hcd, char *buf) /* * Return status information even for ports with OWNER set. - * Otherwise khubd wouldn't see the disconnect event when a + * Otherwise hub_wq wouldn't see the disconnect event when a * high-speed device is switched over to the companion * controller by the user. */ @@ -1530,7 +1530,7 @@ static int fusbh200_hub_control ( /* * Even if OWNER is set, so the port is owned by the - * companion controller, khubd needs to be able to clear + * companion controller, hub_wq needs to be able to clear * the port-change status bits (especially * USB_PORT_STAT_C_CONNECTION). */ @@ -1678,7 +1678,7 @@ static int fusbh200_hub_control ( } /* - * Even if OWNER is set, there's no harm letting khubd + * Even if OWNER is set, there's no harm letting hub_wq * see the wPortStatus values (they should all be 0 except * for PORT_POWER anyway). */ @@ -5355,7 +5355,7 @@ static irqreturn_t fusbh200_irq (struct usb_hcd *hcd) fusbh200->reset_done[0] == 0) { /* start 20 msec resume signaling from this port, - * and make khubd collect PORT_STAT_C_SUSPEND to + * and make hub_wq collect PORT_STAT_C_SUSPEND to * stop that signaling. Use 5 ms extra for safety, * like usb_port_resume() does. */ diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 875bcfd3ec1a..4bb37982855e 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2616,30 +2616,10 @@ static int isp1362_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - struct resource *res; remove_debug_file(isp1362_hcd); DBG(0, "%s: Removing HCD\n", __func__); usb_remove_hcd(hcd); - - DBG(0, "%s: Unmapping data_reg @ %p\n", __func__, - isp1362_hcd->data_reg); - iounmap(isp1362_hcd->data_reg); - - DBG(0, "%s: Unmapping addr_reg @ %p\n", __func__, - isp1362_hcd->addr_reg); - iounmap(isp1362_hcd->addr_reg); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - DBG(0, "%s: release mem_region: %08lx\n", __func__, (long unsigned int)res->start); - if (res) - release_mem_region(res->start, resource_size(res)); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - DBG(0, "%s: release mem_region: %08lx\n", __func__, (long unsigned int)res->start); - if (res) - release_mem_region(res->start, resource_size(res)); - DBG(0, "%s: put_hcd\n", __func__); usb_put_hcd(hcd); DBG(0, "%s: Done\n", __func__); @@ -2651,12 +2631,11 @@ static int isp1362_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct isp1362_hcd *isp1362_hcd; - struct resource *addr, *data; + struct resource *addr, *data, *irq_res; void __iomem *addr_reg; void __iomem *data_reg; int irq; int retval = 0; - struct resource *irq_res; unsigned int irq_flags = 0; if (usb_disabled()) @@ -2667,52 +2646,35 @@ static int isp1362_probe(struct platform_device *pdev) * specific platform_data. we don't probe for IRQs, and do only * minimal sanity checking. */ - if (pdev->num_resources < 3) { - retval = -ENODEV; - goto err1; - } - - data = platform_get_resource(pdev, IORESOURCE_MEM, 0); - addr = platform_get_resource(pdev, IORESOURCE_MEM, 1); - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!addr || !data || !irq_res) { - retval = -ENODEV; - goto err1; - } - irq = irq_res->start; + if (pdev->num_resources < 3) + return -ENODEV; if (pdev->dev.dma_mask) { DBG(1, "won't do DMA"); - retval = -ENODEV; - goto err1; + return -ENODEV; } - if (!request_mem_region(addr->start, resource_size(addr), hcd_name)) { - retval = -EBUSY; - goto err1; - } - addr_reg = ioremap(addr->start, resource_size(addr)); - if (addr_reg == NULL) { - retval = -ENOMEM; - goto err2; - } + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq_res) + return -ENODEV; - if (!request_mem_region(data->start, resource_size(data), hcd_name)) { - retval = -EBUSY; - goto err3; - } - data_reg = ioremap(data->start, resource_size(data)); - if (data_reg == NULL) { - retval = -ENOMEM; - goto err4; - } + irq = irq_res->start; + + addr = platform_get_resource(pdev, IORESOURCE_MEM, 1); + addr_reg = devm_ioremap_resource(&pdev->dev, addr); + if (IS_ERR(addr_reg)) + return PTR_ERR(addr_reg); + + data = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data_reg = devm_ioremap_resource(&pdev->dev, data); + if (IS_ERR(data_reg)) + return PTR_ERR(data_reg); /* allocate and initialize hcd */ hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd) { - retval = -ENOMEM; - goto err5; - } + if (!hcd) + return -ENOMEM; + hcd->rsrc_start = data->start; isp1362_hcd = hcd_to_isp1362_hcd(hcd); isp1362_hcd->data_reg = data_reg; @@ -2729,7 +2691,7 @@ static int isp1362_probe(struct platform_device *pdev) if (!isp1362_hcd->board->delay) { dev_err(hcd->self.controller, "No platform delay function given\n"); retval = -ENODEV; - goto err6; + goto err; } #endif @@ -2744,32 +2706,17 @@ static int isp1362_probe(struct platform_device *pdev) retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED); if (retval != 0) - goto err6; + goto err; device_wakeup_enable(hcd->self.controller); - pr_info("%s, irq %d\n", hcd->product_desc, irq); + dev_info(&pdev->dev, "%s, irq %d\n", hcd->product_desc, irq); create_debug_file(isp1362_hcd); return 0; - err6: - DBG(0, "%s: Freeing dev %p\n", __func__, isp1362_hcd); + err: usb_put_hcd(hcd); - err5: - DBG(0, "%s: Unmapping data_reg @ %p\n", __func__, data_reg); - iounmap(data_reg); - err4: - DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)data->start); - release_mem_region(data->start, resource_size(data)); - err3: - DBG(0, "%s: Unmapping addr_reg @ %p\n", __func__, addr_reg); - iounmap(addr_reg); - err2: - DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)addr->start); - release_mem_region(addr->start, resource_size(addr)); - err1: - pr_err("%s: init error, %d\n", __func__, retval); return retval; } diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 51a0ae9cdd1d..e752c3098f38 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -1760,7 +1760,7 @@ static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf) /* * Return status information even for ports with OWNER set. - * Otherwise khubd wouldn't see the disconnect event when a + * Otherwise hub_wq wouldn't see the disconnect event when a * high-speed device is switched over to the companion * controller by the user. */ @@ -1871,7 +1871,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, /* * Even if OWNER is set, so the port is owned by the - * companion controller, khubd needs to be able to clear + * companion controller, hub_wq needs to be able to clear * the port-change status bits (especially * USB_PORT_STAT_C_CONNECTION). */ @@ -2000,7 +2000,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, reg_read32(hcd->regs, HC_PORTSC1)); } /* - * Even if OWNER is set, there's no harm letting khubd + * Even if OWNER is set, there's no harm letting hub_wq * see the wPortStatus values (they should all be 0 except * for PORT_POWER anyway). */ diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 858efcfda50b..6234c75da33f 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -102,6 +102,15 @@ enum scheduling_pass { SCHED_PASS_DONE }; +/* Bit numbers for max3421_hcd->todo: */ +enum { + ENABLE_IRQ = 0, + RESET_HCD, + RESET_PORT, + CHECK_UNLINK, + IOPIN_UPDATE +}; + struct max3421_dma_buf { u8 data[2]; }; @@ -146,11 +155,7 @@ struct max3421_hcd { u8 hien; u8 mode; u8 iopins[2]; - unsigned int do_enable_irq:1; - unsigned int do_reset_hcd:1; - unsigned int do_reset_port:1; - unsigned int do_check_unlink:1; - unsigned int do_iopin_update:1; + unsigned long todo; #ifdef DEBUG unsigned long err_stat[16]; #endif @@ -1165,10 +1170,8 @@ max3421_irq_handler(int irq, void *dev_id) if (max3421_hcd->spi_thread && max3421_hcd->spi_thread->state != TASK_RUNNING) wake_up_process(max3421_hcd->spi_thread); - if (!max3421_hcd->do_enable_irq) { - max3421_hcd->do_enable_irq = 1; + if (!test_and_set_bit(ENABLE_IRQ, &max3421_hcd->todo)) disable_irq_nosync(spi->irq); - } return IRQ_HANDLED; } @@ -1423,10 +1426,8 @@ max3421_spi_thread(void *dev_id) spi_wr8(hcd, MAX3421_REG_HIEN, max3421_hcd->hien); set_current_state(TASK_INTERRUPTIBLE); - if (max3421_hcd->do_enable_irq) { - max3421_hcd->do_enable_irq = 0; + if (test_and_clear_bit(ENABLE_IRQ, &max3421_hcd->todo)) enable_irq(spi->irq); - } schedule(); __set_current_state(TASK_RUNNING); } @@ -1440,23 +1441,18 @@ max3421_spi_thread(void *dev_id) else if (!max3421_hcd->curr_urb) i_worked |= max3421_select_and_start_urb(hcd); - if (max3421_hcd->do_reset_hcd) { + if (test_and_clear_bit(RESET_HCD, &max3421_hcd->todo)) /* reset the HCD: */ - max3421_hcd->do_reset_hcd = 0; i_worked |= max3421_reset_hcd(hcd); - } - if (max3421_hcd->do_reset_port) { + if (test_and_clear_bit(RESET_PORT, &max3421_hcd->todo)) { /* perform a USB bus reset: */ - max3421_hcd->do_reset_port = 0; spi_wr8(hcd, MAX3421_REG_HCTL, BIT(MAX3421_HCTL_BUSRST_BIT)); i_worked = 1; } - if (max3421_hcd->do_check_unlink) { - max3421_hcd->do_check_unlink = 0; + if (test_and_clear_bit(CHECK_UNLINK, &max3421_hcd->todo)) i_worked |= max3421_check_unlink(hcd); - } - if (max3421_hcd->do_iopin_update) { + if (test_and_clear_bit(IOPIN_UPDATE, &max3421_hcd->todo)) { /* * IOPINS1/IOPINS2 do not auto-increment, so we can't * use spi_wr_buf(). @@ -1469,7 +1465,6 @@ max3421_spi_thread(void *dev_id) spi_wr8(hcd, MAX3421_REG_IOPINS1 + i, val); max3421_hcd->iopins[i] = val; } - max3421_hcd->do_iopin_update = 0; i_worked = 1; } } @@ -1485,7 +1480,8 @@ max3421_reset_port(struct usb_hcd *hcd) max3421_hcd->port_status &= ~(USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED); - max3421_hcd->do_reset_port = 1; + max3421_hcd->port_status |= USB_PORT_STAT_RESET; + set_bit(RESET_PORT, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); return 0; } @@ -1498,7 +1494,7 @@ max3421_reset(struct usb_hcd *hcd) hcd->self.sg_tablesize = 0; hcd->speed = HCD_USB2; hcd->self.root_hub->speed = USB_SPEED_FULL; - max3421_hcd->do_reset_hcd = 1; + set_bit(RESET_HCD, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); return 0; } @@ -1551,7 +1547,7 @@ max3421_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) max3421_ep = urb->ep->hcpriv; if (!max3421_ep) { /* gets freed in max3421_endpoint_disable: */ - max3421_ep = kzalloc(sizeof(struct max3421_ep), mem_flags); + max3421_ep = kzalloc(sizeof(struct max3421_ep), GFP_ATOMIC); if (!max3421_ep) { retval = -ENOMEM; goto out; @@ -1590,7 +1586,7 @@ max3421_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) */ retval = usb_hcd_check_unlink_urb(hcd, urb, status); if (retval == 0) { - max3421_hcd->do_check_unlink = 1; + set_bit(CHECK_UNLINK, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); } spin_unlock_irqrestore(&max3421_hcd->lock, flags); @@ -1690,7 +1686,7 @@ max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value) max3421_hcd->iopins[idx] |= mask; else max3421_hcd->iopins[idx] &= ~mask; - max3421_hcd->do_iopin_update = 1; + set_bit(IOPIN_UPDATE, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); } diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 45032e933e18..04f2186939d2 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -236,7 +236,7 @@ ohci_dump_roothub ( } } -static void ohci_dump (struct ohci_hcd *controller, int verbose) +static void ohci_dump(struct ohci_hcd *controller) { ohci_dbg (controller, "OHCI controller state\n"); @@ -464,15 +464,16 @@ show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed) static ssize_t fill_async_buffer(struct debug_buffer *buf) { struct ohci_hcd *ohci; - size_t temp; + size_t temp, size; unsigned long flags; ohci = buf->ohci; + size = PAGE_SIZE; /* display control and bulk lists together, for simplicity */ spin_lock_irqsave (&ohci->lock, flags); - temp = show_list(ohci, buf->page, buf->count, ohci->ed_controltail); - temp += show_list(ohci, buf->page + temp, buf->count - temp, + temp = show_list(ohci, buf->page, size, ohci->ed_controltail); + temp += show_list(ohci, buf->page + temp, size - temp, ohci->ed_bulktail); spin_unlock_irqrestore (&ohci->lock, flags); diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 060a6a414750..d28b6583ba02 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -19,11 +19,8 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> -#include <linux/usb/phy.h> -#include <linux/usb/samsung_usb_phy.h> #include <linux/usb.h> #include <linux/usb/hcd.h> -#include <linux/usb/otg.h> #include "ohci.h" @@ -38,9 +35,7 @@ static struct hc_driver __read_mostly exynos_ohci_hc_driver; struct exynos_ohci_hcd { struct clk *clk; - struct usb_phy *phy; - struct usb_otg *otg; - struct phy *phy_g[PHY_NUMBER]; + struct phy *phy[PHY_NUMBER]; }; static int exynos_ohci_get_phy(struct device *dev, @@ -49,30 +44,9 @@ static int exynos_ohci_get_phy(struct device *dev, struct device_node *child; struct phy *phy; int phy_number; - int ret = 0; - - exynos_ohci->phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); - if (IS_ERR(exynos_ohci->phy)) { - ret = PTR_ERR(exynos_ohci->phy); - if (ret != -ENXIO && ret != -ENODEV) { - dev_err(dev, "no usb2 phy configured\n"); - return ret; - } - dev_dbg(dev, "Failed to get usb2 phy\n"); - } else { - exynos_ohci->otg = exynos_ohci->phy->otg; - } + int ret; - /* - * Getting generic phy: - * We are keeping both types of phys as a part of transiting OHCI - * to generic phy framework, so as to maintain backward compatibilty - * with old DTB. - * If there are existing devices using DTB files built from them, - * to remove the support for old bindings in this driver, - * we need to make sure that such devices have their DTBs - * updated to ones built from new DTS. - */ + /* Get PHYs for the controller */ for_each_available_child_of_node(dev->of_node, child) { ret = of_property_read_u32(child, "reg", &phy_number); if (ret) { @@ -87,20 +61,22 @@ static int exynos_ohci_get_phy(struct device *dev, return -EINVAL; } - phy = devm_of_phy_get(dev, child, 0); + phy = devm_of_phy_get(dev, child, NULL); + exynos_ohci->phy[phy_number] = phy; of_node_put(child); if (IS_ERR(phy)) { ret = PTR_ERR(phy); - if (ret != -ENOSYS && ret != -ENODEV) { - dev_err(dev, "no usb2 phy configured\n"); + if (ret == -EPROBE_DEFER) { + return ret; + } else if (ret != -ENOSYS && ret != -ENODEV) { + dev_err(dev, + "Error retrieving usb2 phy: %d\n", ret); return ret; } - dev_dbg(dev, "Failed to get usb2 phy\n"); } - exynos_ohci->phy_g[phy_number] = phy; } - return ret; + return 0; } static int exynos_ohci_phy_enable(struct device *dev) @@ -110,16 +86,13 @@ static int exynos_ohci_phy_enable(struct device *dev) int i; int ret = 0; - if (!IS_ERR(exynos_ohci->phy)) - return usb_phy_init(exynos_ohci->phy); - for (i = 0; ret == 0 && i < PHY_NUMBER; i++) - if (!IS_ERR(exynos_ohci->phy_g[i])) - ret = phy_power_on(exynos_ohci->phy_g[i]); + if (!IS_ERR(exynos_ohci->phy[i])) + ret = phy_power_on(exynos_ohci->phy[i]); if (ret) for (i--; i >= 0; i--) - if (!IS_ERR(exynos_ohci->phy_g[i])) - phy_power_off(exynos_ohci->phy_g[i]); + if (!IS_ERR(exynos_ohci->phy[i])) + phy_power_off(exynos_ohci->phy[i]); return ret; } @@ -130,14 +103,9 @@ static void exynos_ohci_phy_disable(struct device *dev) struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd); int i; - if (!IS_ERR(exynos_ohci->phy)) { - usb_phy_shutdown(exynos_ohci->phy); - return; - } - for (i = 0; i < PHY_NUMBER; i++) - if (!IS_ERR(exynos_ohci->phy_g[i])) - phy_power_off(exynos_ohci->phy_g[i]); + if (!IS_ERR(exynos_ohci->phy[i])) + phy_power_off(exynos_ohci->phy[i]); } static int exynos_ohci_probe(struct platform_device *pdev) @@ -209,9 +177,6 @@ skip_phy: goto fail_io; } - if (exynos_ohci->otg) - exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); - platform_set_drvdata(pdev, hcd); err = exynos_ohci_phy_enable(&pdev->dev); @@ -244,9 +209,6 @@ static int exynos_ohci_remove(struct platform_device *pdev) usb_remove_hcd(hcd); - if (exynos_ohci->otg) - exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); - exynos_ohci_phy_disable(&pdev->dev); clk_disable_unprepare(exynos_ohci->clk); @@ -275,9 +237,6 @@ static int exynos_ohci_suspend(struct device *dev) if (rc) return rc; - if (exynos_ohci->otg) - exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); - exynos_ohci_phy_disable(dev); clk_disable_unprepare(exynos_ohci->clk); @@ -293,9 +252,6 @@ static int exynos_ohci_resume(struct device *dev) clk_prepare_enable(exynos_ohci->clk); - if (exynos_ohci->otg) - exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); - ret = exynos_ohci_phy_enable(dev); if (ret) { dev_err(dev, "Failed to enable USB phy\n"); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f98d03f3144c..d664edabf14e 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -72,12 +72,14 @@ static const char hcd_name [] = "ohci_hcd"; #define STATECHANGE_DELAY msecs_to_jiffies(300) +#define IO_WATCHDOG_DELAY msecs_to_jiffies(250) #include "ohci.h" #include "pci-quirks.h" -static void ohci_dump (struct ohci_hcd *ohci, int verbose); -static void ohci_stop (struct usb_hcd *hcd); +static void ohci_dump(struct ohci_hcd *ohci); +static void ohci_stop(struct usb_hcd *hcd); +static void io_watchdog_func(unsigned long _ohci); #include "ohci-hub.c" #include "ohci-dbg.c" @@ -109,6 +111,33 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake"); /*-------------------------------------------------------------------------*/ +static int number_of_tds(struct urb *urb) +{ + int len, i, num, this_sg_len; + struct scatterlist *sg; + + len = urb->transfer_buffer_length; + i = urb->num_mapped_sgs; + + if (len > 0 && i > 0) { /* Scatter-gather transfer */ + num = 0; + sg = urb->sg; + for (;;) { + this_sg_len = min_t(int, sg_dma_len(sg), len); + num += DIV_ROUND_UP(this_sg_len, 4096); + len -= this_sg_len; + if (--i <= 0 || len <= 0) + break; + sg = sg_next(sg); + } + + } else { /* Non-SG transfer */ + /* one TD for every 4096 Bytes (could be up to 8K) */ + num = DIV_ROUND_UP(len, 4096); + } + return num; +} + /* * queue up an urb for anything except the root hub */ @@ -142,12 +171,8 @@ static int ohci_urb_enqueue ( // case PIPE_INTERRUPT: // case PIPE_BULK: default: - /* one TD for every 4096 Bytes (can be up to 8K) */ - size += urb->transfer_buffer_length / 4096; - /* ... and for any remaining bytes ... */ - if ((urb->transfer_buffer_length % 4096) != 0) - size++; - /* ... and maybe a zero length packet to wrap it up */ + size += number_of_tds(urb); + /* maybe a zero-length packet to wrap it up */ if (size == 0) size++; else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 @@ -202,6 +227,16 @@ static int ohci_urb_enqueue ( usb_hcd_unlink_urb_from_ep(hcd, urb); goto fail; } + + /* Start up the I/O watchdog timer, if it's not running */ + if (!timer_pending(&ohci->io_watchdog) && + list_empty(&ohci->eds_in_use)) { + ohci->prev_frame_no = ohci_frame_no(ohci); + mod_timer(&ohci->io_watchdog, + jiffies + IO_WATCHDOG_DELAY); + } + list_add(&ed->in_use_list, &ohci->eds_in_use); + if (ed->type == PIPE_ISOCHRONOUS) { u16 frame = ohci_frame_no(ohci); @@ -277,30 +312,24 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) struct ohci_hcd *ohci = hcd_to_ohci (hcd); unsigned long flags; int rc; + urb_priv_t *urb_priv; spin_lock_irqsave (&ohci->lock, flags); rc = usb_hcd_check_unlink_urb(hcd, urb, status); - if (rc) { - ; /* Do nothing */ - } else if (ohci->rh_state == OHCI_RH_RUNNING) { - urb_priv_t *urb_priv; + if (rc == 0) { /* Unless an IRQ completed the unlink while it was being * handed to us, flag it for unlink and giveback, and force * some upcoming INTR_SF to call finish_unlinks() */ urb_priv = urb->hcpriv; - if (urb_priv) { - if (urb_priv->ed->state == ED_OPER) - start_ed_unlink (ohci, urb_priv->ed); + if (urb_priv->ed->state == ED_OPER) + start_ed_unlink(ohci, urb_priv->ed); + + if (ohci->rh_state != OHCI_RH_RUNNING) { + /* With HC dead, we can clean up right away */ + ohci_work(ohci); } - } else { - /* - * with HC dead, we won't respect hc queue pointers - * any more ... just clean up every urb's memory. - */ - if (urb->hcpriv) - finish_urb(ohci, urb, status); } spin_unlock_irqrestore (&ohci->lock, flags); return rc; @@ -332,9 +361,7 @@ rescan: if (ohci->rh_state != OHCI_RH_RUNNING) { sanitize: ed->state = ED_IDLE; - if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) - ohci->eds_scheduled--; - finish_unlinks (ohci, 0); + ohci_work(ohci); } switch (ed->state) { @@ -342,11 +369,6 @@ sanitize: /* major IRQ delivery trouble loses INTR_SF too... */ if (limit-- == 0) { ohci_warn(ohci, "ED unlink timeout\n"); - if (quirk_zfmicro(ohci)) { - ohci_warn(ohci, "Attempting ZF TD recovery\n"); - ohci->ed_to_check = ed; - ohci->zf_delay = 2; - } goto sanitize; } spin_unlock_irqrestore (&ohci->lock, flags); @@ -406,93 +428,7 @@ ohci_shutdown (struct usb_hcd *hcd) udelay(10); ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval); -} - -static int check_ed(struct ohci_hcd *ohci, struct ed *ed) -{ - return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0 - && (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK) - == (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK) - && !list_empty(&ed->td_list); -} - -/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes - * an interrupt TD but neglects to add it to the donelist. On systems with - * this chipset, we need to periodically check the state of the queues to look - * for such "lost" TDs. - */ -static void unlink_watchdog_func(unsigned long _ohci) -{ - unsigned long flags; - unsigned max; - unsigned seen_count = 0; - unsigned i; - struct ed **seen = NULL; - struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci; - - spin_lock_irqsave(&ohci->lock, flags); - max = ohci->eds_scheduled; - if (!max) - goto done; - - if (ohci->ed_to_check) - goto out; - - seen = kcalloc(max, sizeof *seen, GFP_ATOMIC); - if (!seen) - goto out; - - for (i = 0; i < NUM_INTS; i++) { - struct ed *ed = ohci->periodic[i]; - - while (ed) { - unsigned temp; - - /* scan this branch of the periodic schedule tree */ - for (temp = 0; temp < seen_count; temp++) { - if (seen[temp] == ed) { - /* we've checked it and what's after */ - ed = NULL; - break; - } - } - if (!ed) - break; - seen[seen_count++] = ed; - if (!check_ed(ohci, ed)) { - ed = ed->ed_next; - continue; - } - - /* HC's TD list is empty, but HCD sees at least one - * TD that's not been sent through the donelist. - */ - ohci->ed_to_check = ed; - ohci->zf_delay = 2; - - /* The HC may wait until the next frame to report the - * TD as done through the donelist and INTR_WDH. (We - * just *assume* it's not a multi-TD interrupt URB; - * those could defer the IRQ more than one frame, using - * DI...) Check again after the next INTR_SF. - */ - ohci_writel(ohci, OHCI_INTR_SF, - &ohci->regs->intrstatus); - ohci_writel(ohci, OHCI_INTR_SF, - &ohci->regs->intrenable); - - /* flush those writes */ - (void) ohci_readl(ohci, &ohci->regs->control); - - goto out; - } - } -out: - kfree(seen); - if (ohci->eds_scheduled) - mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ)); -done: - spin_unlock_irqrestore(&ohci->lock, flags); + ohci->rh_state = OHCI_RH_HALTED; } /*-------------------------------------------------------------------------* @@ -506,6 +442,9 @@ static int ohci_init (struct ohci_hcd *ohci) int ret; struct usb_hcd *hcd = ohci_to_hcd(ohci); + /* Accept arbitrarily long scatter-gather lists */ + hcd->self.sg_tablesize = ~0; + if (distrust_firmware) ohci->flags |= OHCI_QUIRK_HUB_POWER; @@ -558,8 +497,12 @@ static int ohci_init (struct ohci_hcd *ohci) if (ohci->hcca) return 0; + setup_timer(&ohci->io_watchdog, io_watchdog_func, + (unsigned long) ohci); + set_timer_slack(&ohci->io_watchdog, msecs_to_jiffies(20)); + ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); + sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL); if (!ohci->hcca) return -ENOMEM; @@ -689,7 +632,7 @@ retry: return -EOVERFLOW; } - /* use rhsc irqs after khubd is fully initialized */ + /* use rhsc irqs after hub_wq is allocated */ set_bit(HCD_FLAG_POLL_RH, &hcd->flags); hcd->uses_new_polling = 1; @@ -735,16 +678,7 @@ retry: // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((val >> 23) & 0x1fe); - if (quirk_zfmicro(ohci)) { - /* Create timer to watch for bad queue state on ZF Micro */ - setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func, - (unsigned long) ohci); - - ohci->eds_scheduled = 0; - ohci->ed_to_check = NULL; - } - - ohci_dump (ohci, 1); + ohci_dump(ohci); return 0; } @@ -777,6 +711,142 @@ static int ohci_start(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ +/* + * Some OHCI controllers are known to lose track of completed TDs. They + * don't add the TDs to the hardware done queue, which means we never see + * them as being completed. + * + * This watchdog routine checks for such problems. Without some way to + * tell when those TDs have completed, we would never take their EDs off + * the unlink list. As a result, URBs could never be dequeued and + * endpoints could never be released. + */ +static void io_watchdog_func(unsigned long _ohci) +{ + struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci; + bool takeback_all_pending = false; + u32 status; + u32 head; + struct ed *ed; + struct td *td, *td_start, *td_next; + unsigned frame_no; + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + + /* + * One way to lose track of completed TDs is if the controller + * never writes back the done queue head. If it hasn't been + * written back since the last time this function ran and if it + * was non-empty at that time, something is badly wrong with the + * hardware. + */ + status = ohci_readl(ohci, &ohci->regs->intrstatus); + if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { + if (ohci->prev_donehead) { + ohci_err(ohci, "HcDoneHead not written back; disabled\n"); + died: + usb_hc_died(ohci_to_hcd(ohci)); + ohci_dump(ohci); + ohci_shutdown(ohci_to_hcd(ohci)); + goto done; + } else { + /* No write back because the done queue was empty */ + takeback_all_pending = true; + } + } + + /* Check every ED which might have pending TDs */ + list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) { + if (ed->pending_td) { + if (takeback_all_pending || + OKAY_TO_TAKEBACK(ohci, ed)) { + unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO); + + ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n", + 0x007f & tmp, + (0x000f & (tmp >> 7)) + + ((tmp & ED_IN) >> 5)); + add_to_done_list(ohci, ed->pending_td); + } + } + + /* Starting from the latest pending TD, */ + td = ed->pending_td; + + /* or the last TD on the done list, */ + if (!td) { + list_for_each_entry(td_next, &ed->td_list, td_list) { + if (!td_next->next_dl_td) + break; + td = td_next; + } + } + + /* find the last TD processed by the controller. */ + head = hc32_to_cpu(ohci, ACCESS_ONCE(ed->hwHeadP)) & TD_MASK; + td_start = td; + td_next = list_prepare_entry(td, &ed->td_list, td_list); + list_for_each_entry_continue(td_next, &ed->td_list, td_list) { + if (head == (u32) td_next->td_dma) + break; + td = td_next; /* head pointer has passed this TD */ + } + if (td != td_start) { + /* + * In case a WDH cycle is in progress, we will wait + * for the next two cycles to complete before assuming + * this TD will never get on the done queue. + */ + ed->takeback_wdh_cnt = ohci->wdh_cnt + 2; + ed->pending_td = td; + } + } + + ohci_work(ohci); + + if (ohci->rh_state == OHCI_RH_RUNNING) { + + /* + * Sometimes a controller just stops working. We can tell + * by checking that the frame counter has advanced since + * the last time we ran. + * + * But be careful: Some controllers violate the spec by + * stopping their frame counter when no ports are active. + */ + frame_no = ohci_frame_no(ohci); + if (frame_no == ohci->prev_frame_no) { + int active_cnt = 0; + int i; + unsigned tmp; + + for (i = 0; i < ohci->num_ports; ++i) { + tmp = roothub_portstatus(ohci, i); + /* Enabled and not suspended? */ + if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS)) + ++active_cnt; + } + + if (active_cnt > 0) { + ohci_err(ohci, "frame counter not updating; disabled\n"); + goto died; + } + } + if (!list_empty(&ohci->eds_in_use)) { + ohci->prev_frame_no = frame_no; + ohci->prev_wdh_cnt = ohci->wdh_cnt; + ohci->prev_donehead = ohci_readl(ohci, + &ohci->regs->donehead); + mod_timer(&ohci->io_watchdog, + jiffies + IO_WATCHDOG_DELAY); + } + } + + done: + spin_unlock_irqrestore(&ohci->lock, flags); +} + /* an interrupt happens */ static irqreturn_t ohci_irq (struct usb_hcd *hcd) @@ -825,7 +895,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) usb_hc_died(hcd); } - ohci_dump (ohci, 1); + ohci_dump(ohci); ohci_usb_reset (ohci); } @@ -839,8 +909,8 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) * choices for RHSC. Many followed the spec; RHSC triggers * on an edge, like setting and maybe clearing a port status * change bit. With others it's level-triggered, active - * until khubd clears all the port status change bits. We'll - * always disable it here and rely on polling until khubd + * until hub_wq clears all the port status change bits. We'll + * always disable it here and rely on polling until hub_wq * re-enables it. */ ohci_writel(ohci, OHCI_INTR_RHSC, ®s->intrdisable); @@ -863,58 +933,30 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) usb_hcd_resume_root_hub(hcd); } - if (ints & OHCI_INTR_WDH) { - spin_lock (&ohci->lock); - dl_done_list (ohci); - spin_unlock (&ohci->lock); - } - - if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) { - spin_lock(&ohci->lock); - if (ohci->ed_to_check) { - struct ed *ed = ohci->ed_to_check; - - if (check_ed(ohci, ed)) { - /* HC thinks the TD list is empty; HCD knows - * at least one TD is outstanding - */ - if (--ohci->zf_delay == 0) { - struct td *td = list_entry( - ed->td_list.next, - struct td, td_list); - ohci_warn(ohci, - "Reclaiming orphan TD %p\n", - td); - takeback_td(ohci, td); - ohci->ed_to_check = NULL; - } - } else - ohci->ed_to_check = NULL; - } - spin_unlock(&ohci->lock); - } + spin_lock(&ohci->lock); + if (ints & OHCI_INTR_WDH) + update_done_list(ohci); /* could track INTR_SO to reduce available PCI/... bandwidth */ /* handle any pending URB/ED unlinks, leaving INTR_SF enabled * when there's still unlinking to be done (next frame). */ - spin_lock (&ohci->lock); - if (ohci->ed_rm_list) - finish_unlinks (ohci, ohci_frame_no(ohci)); - if ((ints & OHCI_INTR_SF) != 0 - && !ohci->ed_rm_list - && !ohci->ed_to_check + ohci_work(ohci); + if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list && ohci->rh_state == OHCI_RH_RUNNING) ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); - spin_unlock (&ohci->lock); if (ohci->rh_state == OHCI_RH_RUNNING) { ohci_writel (ohci, ints, ®s->intrstatus); + if (ints & OHCI_INTR_WDH) + ++ohci->wdh_cnt; + ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable); // flush those writes (void) ohci_readl (ohci, &ohci->regs->control); } + spin_unlock(&ohci->lock); return IRQ_HANDLED; } @@ -925,18 +967,17 @@ static void ohci_stop (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci_dump (ohci, 1); + ohci_dump(ohci); if (quirk_nec(ohci)) flush_work(&ohci->nec_work); + del_timer_sync(&ohci->io_watchdog); ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); ohci_usb_reset(ohci); free_irq(hcd->irq, hcd); hcd->irq = 0; - if (quirk_zfmicro(ohci)) - del_timer(&ohci->unlink_watchdog); if (quirk_amdiso(ohci)) usb_amd_dev_put(); @@ -993,7 +1034,7 @@ int ohci_restart(struct ohci_hcd *ohci) if (!urb->unlinked) urb->unlinked = -ESHUTDOWN; } - finish_unlinks (ohci, 0); + ohci_work(ohci); spin_unlock_irq(&ohci->lock); /* paranoia, in case that didn't work: */ diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index b4940de1eba3..0aa17c937115 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -39,8 +39,8 @@ #define OHCI_SCHED_ENABLES \ (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) -static void dl_done_list (struct ohci_hcd *); -static void finish_unlinks (struct ohci_hcd *, u16); +static void update_done_list(struct ohci_hcd *); +static void ohci_work(struct ohci_hcd *); #ifdef CONFIG_PM static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) @@ -87,8 +87,8 @@ __acquires(ohci->lock) msleep (8); spin_lock_irq (&ohci->lock); } - dl_done_list (ohci); - finish_unlinks (ohci, ohci_frame_no(ohci)); + update_done_list(ohci); + ohci_work(ohci); /* * Some controllers don't handle "global" suspend properly if @@ -309,6 +309,9 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) else rc = ohci_rh_suspend (ohci, 0); spin_unlock_irq (&ohci->lock); + + if (rc == 0) + del_timer_sync(&ohci->io_watchdog); return rc; } @@ -582,7 +585,7 @@ static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port) if (!(status & RH_PS_CCS)) return -ENODEV; - /* khubd will finish the reset later */ + /* hub_wq will finish the reset later */ ohci_writel(ohci, RH_PS_PRS, &ohci->regs->roothub.portstatus [port]); return 0; } @@ -607,7 +610,7 @@ static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port) /* wrap-aware logic morphed from <linux/jiffies.h> */ #define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0) -/* called from some task, normally khubd */ +/* called from some task, normally hub_wq */ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port) { __hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port]; diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 2f20d3dc895b..c9e315c6808a 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -28,6 +28,7 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) ohci->next_statechange = jiffies; spin_lock_init (&ohci->lock); INIT_LIST_HEAD (&ohci->pending); + INIT_LIST_HEAD(&ohci->eds_in_use); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index c923cafcaca7..0231606d47c2 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -180,10 +180,10 @@ static void start_hnp(struct ohci_hcd *ohci) unsigned long flags; u32 l; - otg_start_hnp(hcd->phy->otg); + otg_start_hnp(hcd->usb_phy->otg); local_irq_save(flags); - hcd->phy->state = OTG_STATE_A_SUSPEND; + hcd->usb_phy->state = OTG_STATE_A_SUSPEND; writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); l = omap_readl(OTG_CTRL); l &= ~OTG_A_BUSREQ; @@ -220,14 +220,14 @@ static int ohci_omap_reset(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - hcd->phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!IS_ERR_OR_NULL(hcd->phy)) { - int status = otg_set_host(hcd->phy->otg, + hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); + if (!IS_ERR_OR_NULL(hcd->usb_phy)) { + int status = otg_set_host(hcd->usb_phy->otg, &ohci_to_hcd(ohci)->self); dev_dbg(hcd->self.controller, "init %s phy, status %d\n", - hcd->phy->label, status); + hcd->usb_phy->label, status); if (status) { - usb_put_phy(hcd->phy); + usb_put_phy(hcd->usb_phy); return status; } } else { @@ -283,7 +283,7 @@ static int ohci_omap_reset(struct usb_hcd *hcd) ohci_to_hcd(ohci)->power_budget = 0; } - /* FIXME khubd hub requests should manage power switching */ + /* FIXME hub_wq hub requests should manage power switching */ omap_ohci_transceiver_power(1); /* board init will have already handled HMC and mux setup. @@ -399,9 +399,9 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) dev_dbg(hcd->self.controller, "stopping USB Controller\n"); usb_remove_hcd(hcd); omap_ohci_clock_power(0); - if (!IS_ERR_OR_NULL(hcd->phy)) { - (void) otg_set_host(hcd->phy->otg, 0); - usb_put_phy(hcd->phy); + if (!IS_ERR_OR_NULL(hcd->usb_phy)) { + (void) otg_set_host(hcd->usb_phy->otg, 0); + usb_put_phy(hcd->usb_phy); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index d4253e319428..1463c398d322 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -187,10 +187,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed) ed->ed_prev = NULL; ed->ed_next = NULL; ed->hwNextED = 0; - if (quirk_zfmicro(ohci) - && (ed->type == PIPE_INTERRUPT) - && !(ohci->eds_scheduled++)) - mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ)); wmb (); /* we care about rm_list when setting CLE/BLE in case the HC was at @@ -311,8 +307,7 @@ static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed) * - ED_OPER: when there's any request queued, the ED gets rescheduled * immediately. HC should be working on them. * - * - ED_IDLE: when there's no TD queue. there's no reason for the HC - * to care about this ED; safe to disable the endpoint. + * - ED_IDLE: when there's no TD queue or the HC isn't running. * * When finish_unlinks() runs later, after SOF interrupt, it will often * complete one or more URB unlinks before making that state change. @@ -602,6 +597,8 @@ static void td_submit_urb ( u32 info = 0; int is_out = usb_pipeout (urb->pipe); int periodic = 0; + int i, this_sg_len, n; + struct scatterlist *sg; /* OHCI handles the bulk/interrupt data toggles itself. We just * use the device toggle bits for resetting, and rely on the fact @@ -615,10 +612,24 @@ static void td_submit_urb ( list_add (&urb_priv->pending, &ohci->pending); - if (data_len) - data = urb->transfer_dma; - else - data = 0; + i = urb->num_mapped_sgs; + if (data_len > 0 && i > 0) { + sg = urb->sg; + data = sg_dma_address(sg); + + /* + * urb->transfer_buffer_length may be smaller than the + * size of the scatterlist (or vice versa) + */ + this_sg_len = min_t(int, sg_dma_len(sg), data_len); + } else { + sg = NULL; + if (data_len) + data = urb->transfer_dma; + else + data = 0; + this_sg_len = data_len; + } /* NOTE: TD_CC is set so we can tell which TDs the HC processed by * using TD_CC_GET, as well as by seeing them on the done list. @@ -639,17 +650,29 @@ static void td_submit_urb ( ? TD_T_TOGGLE | TD_CC | TD_DP_OUT : TD_T_TOGGLE | TD_CC | TD_DP_IN; /* TDs _could_ transfer up to 8K each */ - while (data_len > 4096) { - td_fill (ohci, info, data, 4096, urb, cnt); - data += 4096; - data_len -= 4096; + for (;;) { + n = min(this_sg_len, 4096); + + /* maybe avoid ED halt on final TD short read */ + if (n >= data_len || (i == 1 && n >= this_sg_len)) { + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + info |= TD_R; + } + td_fill(ohci, info, data, n, urb, cnt); + this_sg_len -= n; + data_len -= n; + data += n; cnt++; + + if (this_sg_len <= 0) { + if (--i <= 0 || data_len <= 0) + break; + sg = sg_next(sg); + data = sg_dma_address(sg); + this_sg_len = min_t(int, sg_dma_len(sg), + data_len); + } } - /* maybe avoid ED halt on final TD short read */ - if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) - info |= TD_R; - td_fill (ohci, info, data, data_len, urb, cnt); - cnt++; if ((urb->transfer_flags & URB_ZERO_PACKET) && cnt < urb_priv->length) { td_fill (ohci, info, 0, 0, urb, cnt); @@ -869,13 +892,46 @@ static void ed_halted(struct ohci_hcd *ohci, struct td *td, int cc) } } -/* replies to the request have to be on a FIFO basis so - * we unreverse the hc-reversed done-list - */ -static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) +/* Add a TD to the done list */ +static void add_to_done_list(struct ohci_hcd *ohci, struct td *td) +{ + struct td *td2, *td_prev; + struct ed *ed; + + if (td->next_dl_td) + return; /* Already on the list */ + + /* Add all the TDs going back until we reach one that's on the list */ + ed = td->ed; + td2 = td_prev = td; + list_for_each_entry_continue_reverse(td2, &ed->td_list, td_list) { + if (td2->next_dl_td) + break; + td2->next_dl_td = td_prev; + td_prev = td2; + } + + if (ohci->dl_end) + ohci->dl_end->next_dl_td = td_prev; + else + ohci->dl_start = td_prev; + + /* + * Make td->next_dl_td point to td itself, to mark the fact + * that td is on the done list. + */ + ohci->dl_end = td->next_dl_td = td; + + /* Did we just add the latest pending TD? */ + td2 = ed->pending_td; + if (td2 && td2->next_dl_td) + ed->pending_td = NULL; +} + +/* Get the entries on the hardware done queue and put them on our list */ +static void update_done_list(struct ohci_hcd *ohci) { u32 td_dma; - struct td *td_rev = NULL; struct td *td = NULL; td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head); @@ -883,7 +939,7 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) wmb(); /* get TD from hc's singly linked list, and - * prepend to ours. ed->td_list changes later. + * add to ours. ed->td_list changes later. */ while (td_dma) { int cc; @@ -905,19 +961,17 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H))) ed_halted(ohci, td, cc); - td->next_dl_td = td_rev; - td_rev = td; td_dma = hc32_to_cpup (ohci, &td->hwNextTD); + add_to_done_list(ohci, td); } - return td_rev; } /*-------------------------------------------------------------------------*/ /* there are some urbs/eds to unlink; called in_irq(), with HCD locked */ -static void -finish_unlinks (struct ohci_hcd *ohci, u16 tick) +static void finish_unlinks(struct ohci_hcd *ohci) { + unsigned tick = ohci_frame_no(ohci); struct ed *ed, **last; rescan_all: @@ -926,41 +980,48 @@ rescan_all: int completed, modified; __hc32 *prev; + /* Is this ED already invisible to the hardware? */ + if (ed->state == ED_IDLE) + goto ed_idle; + /* only take off EDs that the HC isn't using, accounting for * frame counter wraps and EDs with partially retired TDs */ - if (likely(ohci->rh_state == OHCI_RH_RUNNING)) { - if (tick_before (tick, ed->tick)) { + if (likely(ohci->rh_state == OHCI_RH_RUNNING) && + tick_before(tick, ed->tick)) { skip_ed: - last = &ed->ed_next; - continue; - } + last = &ed->ed_next; + continue; + } + if (!list_empty(&ed->td_list)) { + struct td *td; + u32 head; - if (!list_empty (&ed->td_list)) { - struct td *td; - u32 head; - - td = list_entry (ed->td_list.next, struct td, - td_list); - head = hc32_to_cpu (ohci, ed->hwHeadP) & - TD_MASK; - - /* INTR_WDH may need to clean up first */ - if (td->td_dma != head) { - if (ed == ohci->ed_to_check) - ohci->ed_to_check = NULL; - else - goto skip_ed; - } - } + td = list_first_entry(&ed->td_list, struct td, td_list); + + /* INTR_WDH may need to clean up first */ + head = hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK; + if (td->td_dma != head && + ohci->rh_state == OHCI_RH_RUNNING) + goto skip_ed; + + /* Don't mess up anything already on the done list */ + if (td->next_dl_td) + goto skip_ed; } + /* ED's now officially unlinked, hc doesn't see */ + ed->state = ED_IDLE; + ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); + ed->hwNextED = 0; + wmb(); + ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE); +ed_idle: + /* reentrancy: if we drop the schedule lock, someone might * have modified this list. normally it's just prepending * entries (which we'd ignore), but paranoia won't hurt. */ - *last = ed->ed_next; - ed->ed_next = NULL; modified = 0; /* unlink urbs as requested, but rescan the list after @@ -1018,19 +1079,21 @@ rescan_this: if (completed && !list_empty (&ed->td_list)) goto rescan_this; - /* ED's now officially unlinked, hc doesn't see */ - ed->state = ED_IDLE; - if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) - ohci->eds_scheduled--; - ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); - ed->hwNextED = 0; - wmb (); - ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE); - - /* but if there's work queued, reschedule */ - if (!list_empty (&ed->td_list)) { - if (ohci->rh_state == OHCI_RH_RUNNING) - ed_schedule (ohci, ed); + /* + * If no TDs are queued, take ED off the ed_rm_list. + * Otherwise, if the HC is running, reschedule. + * If not, leave it on the list for further dequeues. + */ + if (list_empty(&ed->td_list)) { + *last = ed->ed_next; + ed->ed_next = NULL; + list_del(&ed->in_use_list); + } else if (ohci->rh_state == OHCI_RH_RUNNING) { + *last = ed->ed_next; + ed->ed_next = NULL; + ed_schedule(ohci, ed); + } else { + last = &ed->ed_next; } if (modified) @@ -1082,12 +1145,7 @@ rescan_this: /*-------------------------------------------------------------------------*/ -/* - * Used to take back a TD from the host controller. This would normally be - * called from within dl_done_list, however it may be called directly if the - * HC no longer sees the TD and it has not appeared on the donelist (after - * two frames). This bug has been observed on ZF Micro systems. - */ +/* Take back a TD from the host controller */ static void takeback_td(struct ohci_hcd *ohci, struct td *td) { struct urb *urb = td->urb; @@ -1134,37 +1192,43 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td) * * This is the main path for handing urbs back to drivers. The only other * normal path is finish_unlinks(), which unlinks URBs using ed_rm_list, - * instead of scanning the (re-reversed) donelist as this does. There's - * an abnormal path too, handling a quirk in some Compaq silicon: URBs - * with TDs that appear to be orphaned are directly reclaimed. + * instead of scanning the (re-reversed) donelist as this does. */ -static void -dl_done_list (struct ohci_hcd *ohci) +static void process_done_list(struct ohci_hcd *ohci) { - struct td *td = dl_reverse_done_list (ohci); + struct td *td; - while (td) { - struct td *td_next = td->next_dl_td; - struct ed *ed = td->ed; + while (ohci->dl_start) { + td = ohci->dl_start; + if (td == ohci->dl_end) + ohci->dl_start = ohci->dl_end = NULL; + else + ohci->dl_start = td->next_dl_td; - /* - * Some OHCI controllers (NVIDIA for sure, maybe others) - * occasionally forget to add TDs to the done queue. Since - * TDs for a given endpoint are always processed in order, - * if we find a TD on the donelist then all of its - * predecessors must be finished as well. - */ - for (;;) { - struct td *td2; + takeback_td(ohci, td); + } +} - td2 = list_first_entry(&ed->td_list, struct td, - td_list); - if (td2 == td) - break; - takeback_td(ohci, td2); - } +/* + * TD takeback and URB giveback must be single-threaded. + * This routine takes care of it all. + */ +static void ohci_work(struct ohci_hcd *ohci) +{ + if (ohci->working) { + ohci->restart_work = 1; + return; + } + ohci->working = 1; - takeback_td(ohci, td); - td = td_next; + restart: + process_done_list(ohci); + if (ohci->ed_rm_list) + finish_unlinks(ohci); + + if (ohci->restart_work) { + ohci->restart_work = 0; + goto restart; } + ohci->working = 0; } diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 8b29a0c04c23..8d5876692e7c 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -162,7 +162,7 @@ static int spear_ohci_hcd_drv_resume(struct platform_device *dev) } #endif -static struct of_device_id spear_ohci_id_table[] = { +static const struct of_device_id spear_ohci_id_table[] = { { .compatible = "st,spear600-ohci", }, { }, }; diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c new file mode 100644 index 000000000000..df9028e0d9b4 --- /dev/null +++ b/drivers/usb/host/ohci-st.c @@ -0,0 +1,349 @@ +/* + * ST OHCI driver + * + * Copyright (C) 2014 STMicroelectronics – All Rights Reserved + * + * Author: Peter Griffin <peter.griffin@linaro.org> + * + * Derived from ohci-platform.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/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/reset.h> +#include <linux/usb/ohci_pdriver.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ohci.h" + +#define USB_MAX_CLKS 3 + +struct st_ohci_platform_priv { + struct clk *clks[USB_MAX_CLKS]; + struct clk *clk48; + struct reset_control *rst; + struct reset_control *pwr; + struct phy *phy; +}; + +#define DRIVER_DESC "OHCI STMicroelectronics driver" + +#define hcd_to_ohci_priv(h) \ + ((struct st_ohci_platform_priv *)hcd_to_ohci(h)->priv) + +static const char hcd_name[] = "ohci-st"; + +static int st_ohci_platform_power_on(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk, ret; + + ret = reset_control_deassert(priv->pwr); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rst); + if (ret) + goto err_assert_power; + + /* some SoCs don't have a dedicated 48Mhz clock, but those that do + need the rate to be explicitly set */ + if (priv->clk48) { + ret = clk_set_rate(priv->clk48, 48000000); + if (ret) + goto err_assert_reset; + } + + for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); + if (ret) + goto err_disable_clks; + } + + 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]); +err_assert_reset: + reset_control_assert(priv->rst); +err_assert_power: + reset_control_assert(priv->pwr); + + return ret; +} + +static void st_ohci_platform_power_off(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + + int clk; + + reset_control_assert(priv->pwr); + + reset_control_assert(priv->rst); + + phy_power_off(priv->phy); + + phy_exit(priv->phy); + + for (clk = USB_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 = "ST OHCI controller", + .extra_priv_size = sizeof(struct st_ohci_platform_priv), +}; + +static struct usb_ohci_pdata ohci_platform_defaults = { + .power_on = st_ohci_platform_power_on, + .power_suspend = st_ohci_platform_power_off, + .power_off = st_ohci_platform_power_off, +}; + +static int st_ohci_platform_probe(struct platform_device *dev) +{ + struct usb_hcd *hcd; + struct resource *res_mem; + struct usb_ohci_pdata *pdata = &ohci_platform_defaults; + struct st_ohci_platform_priv *priv; + struct ohci_hcd *ohci; + int err, irq, clk = 0; + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no irq provided"); + return irq; + } + + res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res_mem) { + dev_err(&dev->dev, "no memory resource provided"); + 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); + + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); + goto err_put_hcd; + } + + for (clk = 0; clk < USB_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; + } + } + + /* some SoCs don't have a dedicated 48Mhz clock, but those that + do need the rate to be explicitly set */ + priv->clk48 = devm_clk_get(&dev->dev, "clk48"); + if (IS_ERR(priv->clk48)) { + dev_info(&dev->dev, "48MHz clk not found\n"); + priv->clk48 = NULL; + } + + priv->pwr = devm_reset_control_get_optional(&dev->dev, "power"); + if (IS_ERR(priv->pwr)) { + err = PTR_ERR(priv->pwr); + goto err_put_clks; + } + + priv->rst = devm_reset_control_get_optional(&dev->dev, "softreset"); + if (IS_ERR(priv->rst)) { + err = PTR_ERR(priv->rst); + goto err_put_clks; + } + + if (pdata->power_on) { + err = pdata->power_on(dev); + if (err < 0) + goto err_power; + } + + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + + hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + if (IS_ERR(hcd->regs)) { + err = PTR_ERR(hcd->regs); + goto err_power; + } + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) + goto err_power; + + device_wakeup_enable(hcd->self.controller); + + platform_set_drvdata(dev, hcd); + + return err; + +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; +} + +static int st_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 st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk; + + usb_remove_hcd(hcd); + + if (pdata->power_off) + pdata->power_off(dev); + + + for (clk = 0; clk < USB_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; +} + +#ifdef CONFIG_PM_SLEEP + +static int st_ohci_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_ohci_pdata *pdata = dev->platform_data; + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + bool do_wakeup = device_may_wakeup(dev); + int ret; + + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + + if (pdata->power_suspend) + pdata->power_suspend(pdev); + + return ret; +} + +static int st_ohci_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_ohci_pdata *pdata = dev_get_platdata(dev); + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + int err; + + if (pdata->power_on) { + err = pdata->power_on(pdev); + if (err < 0) + return err; + } + + ohci_resume(hcd, false); + return 0; +} + +static SIMPLE_DEV_PM_OPS(st_ohci_pm_ops, st_ohci_suspend, st_ohci_resume); + +#endif /* CONFIG_PM_SLEEP */ + +static const struct of_device_id st_ohci_platform_ids[] = { + { .compatible = "st,st-ohci-300x", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, st_ohci_platform_ids); + +static struct platform_driver ohci_platform_driver = { + .probe = st_ohci_platform_probe, + .remove = st_ohci_platform_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "st-ohci", +#ifdef CONFIG_PM_SLEEP + .pm = &st_ohci_pm_ops, +#endif + .of_match_table = st_ohci_platform_ids, + } +}; + +static int __init ohci_platform_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ohci_init_driver(&ohci_platform_hc_driver, &platform_overrides); + return platform_driver_register(&ohci_platform_driver); +} +module_init(ohci_platform_init); + +static void __exit ohci_platform_cleanup(void) +{ + platform_driver_unregister(&ohci_platform_driver); +} +module_exit(ohci_platform_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 05e02a709d4f..59f424567a8d 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -47,6 +47,7 @@ struct ed { struct ed *ed_next; /* on schedule or rm_list */ struct ed *ed_prev; /* for non-interrupt EDs */ struct list_head td_list; /* "shadow list" of our TDs */ + struct list_head in_use_list; /* create --> IDLE --> OPER --> ... --> IDLE --> destroy * usually: OPER --> UNLINK --> (IDLE | OPER) --> ... @@ -66,6 +67,13 @@ struct ed { /* HC may see EDs on rm_list until next frame (frame_no == tick) */ u16 tick; + + /* Detect TDs not added to the done queue */ + unsigned takeback_wdh_cnt; + struct td *pending_td; +#define OKAY_TO_TAKEBACK(ohci, ed) \ + ((int) (ohci->wdh_cnt - ed->takeback_wdh_cnt) >= 0) + } __attribute__ ((aligned(16))); #define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */ @@ -380,7 +388,9 @@ struct ohci_hcd { struct dma_pool *td_cache; struct dma_pool *ed_cache; struct td *td_hash [TD_HASH_SIZE]; + struct td *dl_start, *dl_end; /* the done list */ struct list_head pending; + struct list_head eds_in_use; /* all EDs with at least 1 TD */ /* * driver state @@ -392,6 +402,8 @@ struct ohci_hcd { unsigned long next_statechange; /* suspend/resume */ u32 fminterval; /* saved register */ unsigned autostop:1; /* rh auto stopping/stopped */ + unsigned working:1; + unsigned restart_work:1; unsigned long flags; /* for HC bugs */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ @@ -409,13 +421,12 @@ struct ohci_hcd { // there are also chip quirks/bugs in init logic - struct work_struct nec_work; /* Worker for NEC quirk */ + unsigned prev_frame_no; + unsigned wdh_cnt, prev_wdh_cnt; + u32 prev_donehead; + struct timer_list io_watchdog; - /* Needed for ZF Micro quirk */ - struct timer_list unlink_watchdog; - unsigned eds_scheduled; - struct ed *ed_to_check; - unsigned zf_delay; + struct work_struct nec_work; /* Worker for NEC quirk */ struct dentry *debug_dir; struct dentry *debug_async; diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index e07248b6ab67..4fe79a2d71a9 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -2046,7 +2046,7 @@ static void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh) /* simple/paranoid: always delay, expecting the HC needs to read * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and - * expect khubd to clean up after any CSPLITs we won't issue. + * expect hub_wq to clean up after any CSPLITs we won't issue. * active high speed queues may need bigger delays... */ if (list_empty(&qh->qtd_list) @@ -2501,7 +2501,7 @@ static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) continue; /* start 20 msec resume signaling from this port, - * and make khubd collect PORT_STAT_C_SUSPEND to + * and make hub_wq collect PORT_STAT_C_SUSPEND to * stop that signaling. */ oxu->reset_done[i] = jiffies + msecs_to_jiffies(20); @@ -3119,7 +3119,7 @@ static int oxu_hub_status_data(struct usb_hcd *hcd, char *buf) /* * Return status information even for ports with OWNER set. - * Otherwise khubd wouldn't see the disconnect event when a + * Otherwise hub_wq wouldn't see the disconnect event when a * high-speed device is switched over to the companion * controller by the user. */ @@ -3194,7 +3194,7 @@ static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq, /* * Even if OWNER is set, so the port is owned by the - * companion controller, khubd needs to be able to clear + * companion controller, hub_wq needs to be able to clear * the port-change status bits (especially * USB_PORT_STAT_C_CONNECTION). */ @@ -3336,7 +3336,7 @@ static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq, } /* - * Even if OWNER is set, there's no harm letting khubd + * Even if OWNER is set, there's no harm letting hub_wq * see the wPortStatus values (they should all be 0 except * for PORT_POWER anyway). */ @@ -3826,49 +3826,36 @@ static int oxu_drv_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "IRQ resource %d\n", irq); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no registers address! Check %s setup!\n", - dev_name(&pdev->dev)); - return -ENODEV; + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto error; } memstart = res->start; memlen = resource_size(res); - dev_dbg(&pdev->dev, "MEM resource %lx-%lx\n", memstart, memlen); - if (!request_mem_region(memstart, memlen, - oxu_hc_driver.description)) { - dev_dbg(&pdev->dev, "memory area already in use\n"); - return -EBUSY; - } ret = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING); if (ret) { dev_err(&pdev->dev, "error setting irq type\n"); ret = -EFAULT; - goto error_set_irq_type; - } - - base = ioremap(memstart, memlen); - if (!base) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - ret = -EFAULT; - goto error_ioremap; + goto error; } /* Allocate a driver data struct to hold useful info for both * SPH & OTG devices */ - info = kzalloc(sizeof(struct oxu_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(struct oxu_info), GFP_KERNEL); if (!info) { dev_dbg(&pdev->dev, "error allocating memory\n"); ret = -EFAULT; - goto error_alloc; + goto error; } platform_set_drvdata(pdev, info); ret = oxu_init(pdev, memstart, memlen, base, irq); if (ret < 0) { dev_dbg(&pdev->dev, "cannot init USB devices\n"); - goto error_init; + goto error; } dev_info(&pdev->dev, "devices enabled and running\n"); @@ -3876,16 +3863,7 @@ static int oxu_drv_probe(struct platform_device *pdev) return 0; -error_init: - kfree(info); - -error_alloc: - iounmap(base); - -error_set_irq_type: -error_ioremap: - release_mem_region(memstart, memlen); - +error: dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret); return ret; } @@ -3899,18 +3877,10 @@ static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd) static int oxu_drv_remove(struct platform_device *pdev) { struct oxu_info *info = platform_get_drvdata(pdev); - unsigned long memstart = info->hcd[0]->rsrc_start, - memlen = info->hcd[0]->rsrc_len; - void *base = info->hcd[0]->regs; oxu_remove(pdev, info->hcd[0]); oxu_remove(pdev, info->hcd[1]); - iounmap(base); - release_mem_region(memstart, memlen); - - kfree(info); - return 0; } diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index a517151867af..ad0c348e68e9 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -674,7 +674,7 @@ retry: sl811->next_periodic = sl811->periodic[index]; } - /* khubd manages debouncing and wakeup */ + /* hub_wq manages debouncing and wakeup */ if (irqstat & SL11H_INTMASK_INSRMV) { sl811->stat_insrmv++; @@ -714,7 +714,7 @@ retry: #endif /* port status seems weird until after reset, so - * force the reset and make khubd clean up later. + * force the reset and make hub_wq clean up later. */ if (irqstat & SL11H_INTMASK_RD) sl811->port1 &= ~USB_PORT_STAT_CONNECTION; @@ -1079,7 +1079,7 @@ sl811h_hub_status_data(struct usb_hcd *hcd, char *buf) if (!(sl811->port1 & (0xffff << 16))) return 0; - /* tell khubd port 1 changed */ + /* tell hub_wq port 1 changed */ *buf = (1 << 1); return 1; } @@ -1196,7 +1196,7 @@ sl811h_timer(unsigned long _sl811) sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), SL11H_HCTLMASK_ARM); - /* khubd provides debounce delay */ + /* hub_wq provides debounce delay */ } else { sl811->ctrl1 = 0; } diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index ab25dc397e8b..05f57ffdf9ab 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -17,6 +17,7 @@ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu */ +#include <linux/device.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -113,24 +114,17 @@ static int uhci_hcd_grlib_probe(struct platform_device *op) hcd->rsrc_start = res.start; hcd->rsrc_len = resource_size(&res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); - rv = -EBUSY; - goto err_rmr; - } - irq = irq_of_parse_and_map(dn, 0); if (irq == NO_IRQ) { printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); rv = -EBUSY; - goto err_irq; + goto err_usb; } - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - printk(KERN_ERR "%s: ioremap failed\n", __FILE__); - rv = -ENOMEM; - goto err_ioremap; + hcd->regs = devm_ioremap_resource(&op->dev, &res); + if (IS_ERR(hcd->regs)) { + rv = PTR_ERR(hcd->regs); + goto err_irq; } uhci = hcd_to_uhci(hcd); @@ -139,18 +133,14 @@ static int uhci_hcd_grlib_probe(struct platform_device *op) rv = usb_add_hcd(hcd, irq, 0); if (rv) - goto err_uhci; + goto err_irq; device_wakeup_enable(hcd->self.controller); return 0; -err_uhci: - iounmap(hcd->regs); -err_ioremap: - irq_dispose_mapping(irq); err_irq: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -err_rmr: + irq_dispose_mapping(irq); +err_usb: usb_put_hcd(hcd); return rv; @@ -164,10 +154,7 @@ static int uhci_hcd_grlib_remove(struct platform_device *op) usb_remove_hcd(hcd); - iounmap(hcd->regs); irq_dispose_mapping(hcd->irq); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 27f35e8f161b..a7de8e8bb458 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -591,7 +591,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->frame = dma_alloc_coherent(uhci_dev(uhci), UHCI_NUMFRAMES * sizeof(*uhci->frame), - &uhci->frame_dma_handle, 0); + &uhci->frame_dma_handle, GFP_KERNEL); if (!uhci->frame) { dev_err(uhci_dev(uhci), "unable to allocate consistent memory for frame list\n"); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 01833ab2b5c3..b987f1d10058 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -8,6 +8,7 @@ */ #include <linux/of.h> +#include <linux/device.h> #include <linux/platform_device.h> static int uhci_platform_init(struct usb_hcd *hcd) @@ -88,33 +89,22 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - pr_err("%s: request_mem_region failed\n", __func__); - ret = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); goto err_rmr; } - - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - pr_err("%s: ioremap failed\n", __func__); - ret = -ENOMEM; - goto err_irq; - } uhci = hcd_to_uhci(hcd); uhci->regs = hcd->regs; ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret) - goto err_uhci; + goto err_rmr; device_wakeup_enable(hcd->self.controller); return 0; -err_uhci: - iounmap(hcd->regs); -err_irq: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err_rmr: usb_put_hcd(hcd); @@ -126,8 +116,6 @@ static int uhci_hcd_platform_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index eb009a457fb5..bb89175ca6e5 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -594,3 +594,4 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), trace(&vaf); va_end(args); } +EXPORT_SYMBOL_GPL(xhci_dbg_trace); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index aa79e8749040..696160d48ae8 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -319,12 +319,19 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) */ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) { - int i; + int i, s; + struct xhci_virt_ep *ep; + + for (i = 0; i < LAST_EP_INDEX + 1; i++) { + ep = &xhci->devs[slot_id]->eps[i]; - for (i = 0; i < LAST_EP_INDEX + 1; i++) - if (xhci->devs[slot_id]->eps[i].ring && - xhci->devs[slot_id]->eps[i].ring->dequeue) + if (ep->ep_state & EP_HAS_STREAMS) { + for (s = 1; s < ep->stream_info->num_streams; s++) + xhci_ring_ep_doorbell(xhci, slot_id, i, s); + } else if (ep->ring && ep->ring->dequeue) { xhci_ring_ep_doorbell(xhci, slot_id, i, 0); + } + } return; } @@ -468,7 +475,8 @@ static void xhci_hub_report_usb2_link_state(u32 *status, u32 status_reg) } /* Updates Link Status for super Speed port */ -static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg) +static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, + u32 *status, u32 status_reg) { u32 pls = status_reg & PORT_PLS_MASK; @@ -507,7 +515,8 @@ static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg) * in which sometimes the port enters compliance mode * caused by a delay on the host-device negotiation. */ - if (pls == USB_SS_PORT_LS_COMP_MOD) + if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && + (pls == USB_SS_PORT_LS_COMP_MOD)) pls |= USB_PORT_STAT_CONNECTION; } @@ -666,7 +675,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, } /* Update Port Link State */ if (hcd->speed == HCD_USB3) { - xhci_hub_report_usb3_link_state(&status, raw_port_status); + xhci_hub_report_usb3_link_state(xhci, &status, raw_port_status); /* * Verify if all USB3 Ports Have entered U0 already. * Delete Compliance Mode Timer if so. @@ -890,7 +899,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* * Turn on ports, even if there isn't per-port switching. * HC will report connect events even before this is set. - * However, khubd will ignore the roothub events until + * However, hub_wq will ignore the roothub events until * the roothub is registered. */ writel(temp | PORT_POWER, port_array[wIndex]); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 8056d90690ee..5cb3d7a10017 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1812,6 +1812,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); + xhci->lpm_command = NULL; if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); xhci->cmd_ring = NULL; @@ -1819,7 +1820,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_cleanup_command_queue(xhci); num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - for (i = 0; i < num_ports; i++) { + for (i = 0; i < num_ports && xhci->rh_bw; i++) { struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table; for (j = 0; j < XHCI_MAX_INTERVAL; j++) { struct list_head *ep = &bwt->interval_bw[j].endpoints; @@ -1903,7 +1904,7 @@ static int xhci_test_trb_in_td(struct xhci_hcd *xhci, start_dma = xhci_trb_virt_to_dma(input_seg, start_trb); end_dma = xhci_trb_virt_to_dma(input_seg, end_trb); - seg = trb_in_td(input_seg, start_trb, end_trb, input_dma); + seg = trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, false); if (seg != result_seg) { xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n", test_name, test_number); @@ -1917,6 +1918,8 @@ static int xhci_test_trb_in_td(struct xhci_hcd *xhci, end_trb, end_dma); xhci_warn(xhci, "Expected seg %p, got seg %p\n", result_seg, seg); + trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, + true); return -1; } return 0; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index e20520f42753..280dde93abe5 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -33,13 +33,15 @@ #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400 #define PCI_VENDOR_ID_ETRON 0x1b6f -#define PCI_DEVICE_ID_ASROCK_P67 0x7023 +#define PCI_DEVICE_ID_EJ168 0x7023 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 static const char hcd_name[] = "xhci_hcd"; +static struct hc_driver __read_mostly xhci_pci_hc_driver; + /* called after powerup, by probe or system-pm "wakeup" */ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) { @@ -101,6 +103,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) /* AMD PLL quirk */ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info()) xhci->quirks |= XHCI_AMD_PLL_FIX; + + if (pdev->vendor == PCI_VENDOR_ID_AMD) + xhci->quirks |= XHCI_TRUST_TX_LENGTH; + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_INTEL_HOST; @@ -140,9 +146,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_SPURIOUS_REBOOT; } if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_ASROCK_P67) { + pdev->device == PCI_DEVICE_ID_EJ168) { xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_TRUST_TX_LENGTH; + xhci->quirks |= XHCI_BROKEN_STREAMS; } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && pdev->device == 0x0015) @@ -150,6 +157,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_VIA) xhci->quirks |= XHCI_RESET_ON_RESUME; + /* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */ + if (pdev->vendor == PCI_VENDOR_ID_VIA && + pdev->device == 0x3432) + xhci->quirks |= XHCI_BROKEN_STREAMS; + if (xhci->quirks & XHCI_RESET_ON_RESUME) xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "QUIRK: Resetting on resume"); @@ -230,7 +242,8 @@ 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 */ - if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + if (!(xhci->quirks & XHCI_BROKEN_STREAMS) && + 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 */ @@ -275,7 +288,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) * Systems with the TI redriver that loses port status change events * need to have the registers polled during D3, so avoid D3cold. */ - if (xhci_compliance_mode_recovery_timer_quirk_check()) + if (xhci->quirks & XHCI_COMP_MODE_QUIRK) pdev->no_d3cold = true; return xhci_suspend(xhci); @@ -313,68 +326,6 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) } #endif /* CONFIG_PM */ -static const struct hc_driver xhci_pci_hc_driver = { - .description = hcd_name, - .product_desc = "xHCI Host Controller", - .hcd_priv_size = sizeof(struct xhci_hcd *), - - /* - * generic hardware linkage - */ - .irq = xhci_irq, - .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED, - - /* - * basic lifecycle operations - */ - .reset = xhci_pci_setup, - .start = xhci_run, -#ifdef CONFIG_PM - .pci_suspend = xhci_pci_suspend, - .pci_resume = xhci_pci_resume, -#endif - .stop = xhci_stop, - .shutdown = xhci_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = xhci_urb_enqueue, - .urb_dequeue = xhci_urb_dequeue, - .alloc_dev = xhci_alloc_dev, - .free_dev = xhci_free_dev, - .alloc_streams = xhci_alloc_streams, - .free_streams = xhci_free_streams, - .add_endpoint = xhci_add_endpoint, - .drop_endpoint = xhci_drop_endpoint, - .endpoint_reset = xhci_endpoint_reset, - .check_bandwidth = xhci_check_bandwidth, - .reset_bandwidth = xhci_reset_bandwidth, - .address_device = xhci_address_device, - .enable_device = xhci_enable_device, - .update_hub_device = xhci_update_hub_device, - .reset_device = xhci_discover_or_reset_device, - - /* - * scheduling support - */ - .get_frame_number = xhci_get_frame, - - /* Root hub support */ - .hub_control = xhci_hub_control, - .hub_status_data = xhci_hub_status_data, - .bus_suspend = xhci_bus_suspend, - .bus_resume = xhci_bus_resume, - /* - * call back when device connected and addressed - */ - .update_device = xhci_update_device, - .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm, - .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout, - .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout, - .find_raw_port_number = xhci_find_raw_port_number, -}; - /*-------------------------------------------------------------------------*/ /* PCI driver selection metadata; PCI hotplugging uses this */ @@ -404,12 +355,22 @@ static struct pci_driver xhci_pci_driver = { #endif }; -int __init xhci_register_pci(void) +static int __init xhci_pci_init(void) { + xhci_init_driver(&xhci_pci_hc_driver, xhci_pci_setup); +#ifdef CONFIG_PM + xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend; + xhci_pci_hc_driver.pci_resume = xhci_pci_resume; +#endif return pci_register_driver(&xhci_pci_driver); } +module_init(xhci_pci_init); -void xhci_unregister_pci(void) +static void __exit xhci_pci_exit(void) { pci_unregister_driver(&xhci_pci_driver); } +module_exit(xhci_pci_exit); + +MODULE_DESCRIPTION("xHCI PCI Host Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 29d8adb5c8d1..3d78b0cd674b 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -17,9 +17,13 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/usb/xhci_pdriver.h> #include "xhci.h" #include "xhci-mvebu.h" +#include "xhci-rcar.h" + +static struct hc_driver __read_mostly xhci_plat_hc_driver; static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { @@ -34,66 +38,34 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) /* called during probe() after chip reset completes */ static int xhci_plat_setup(struct usb_hcd *hcd) { + struct device_node *of_node = hcd->self.controller->of_node; + int ret; + + if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") || + of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) { + ret = xhci_rcar_init_quirk(hcd); + if (ret) + return ret; + } + return xhci_gen_setup(hcd, xhci_plat_quirks); } static int xhci_plat_start(struct usb_hcd *hcd) { - return xhci_run(hcd); -} - -static const struct hc_driver xhci_plat_xhci_driver = { - .description = "xhci-hcd", - .product_desc = "xHCI Host Controller", - .hcd_priv_size = sizeof(struct xhci_hcd *), - - /* - * generic hardware linkage - */ - .irq = xhci_irq, - .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED, - - /* - * basic lifecycle operations - */ - .reset = xhci_plat_setup, - .start = xhci_plat_start, - .stop = xhci_stop, - .shutdown = xhci_shutdown, + struct device_node *of_node = hcd->self.controller->of_node; - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = xhci_urb_enqueue, - .urb_dequeue = xhci_urb_dequeue, - .alloc_dev = xhci_alloc_dev, - .free_dev = xhci_free_dev, - .alloc_streams = xhci_alloc_streams, - .free_streams = xhci_free_streams, - .add_endpoint = xhci_add_endpoint, - .drop_endpoint = xhci_drop_endpoint, - .endpoint_reset = xhci_endpoint_reset, - .check_bandwidth = xhci_check_bandwidth, - .reset_bandwidth = xhci_reset_bandwidth, - .address_device = xhci_address_device, - .enable_device = xhci_enable_device, - .update_hub_device = xhci_update_hub_device, - .reset_device = xhci_discover_or_reset_device, + if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") || + of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) + xhci_rcar_start(hcd); - /* - * scheduling support - */ - .get_frame_number = xhci_get_frame, - - /* Root hub support */ - .hub_control = xhci_hub_control, - .hub_status_data = xhci_hub_status_data, - .bus_suspend = xhci_bus_suspend, - .bus_resume = xhci_bus_resume, -}; + return xhci_run(hcd); +} static int xhci_plat_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; + struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev); const struct hc_driver *driver; struct xhci_hcd *xhci; struct resource *res; @@ -105,7 +77,7 @@ static int xhci_plat_probe(struct platform_device *pdev) if (usb_disabled()) return -ENODEV; - driver = &xhci_plat_xhci_driver; + driver = &xhci_plat_hc_driver; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -140,20 +112,12 @@ static int xhci_plat_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - driver->description)) { - dev_dbg(&pdev->dev, "controller already in use\n"); - ret = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); goto put_hcd; } - hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - ret = -EFAULT; - goto release_mem_region; - } - /* * Not all platforms have a clk so it is not an error if the * clock does not exists. @@ -162,7 +126,7 @@ static int xhci_plat_probe(struct platform_device *pdev) if (!IS_ERR(clk)) { ret = clk_prepare_enable(clk); if (ret) - goto unmap_registers; + goto put_hcd; } ret = usb_add_hcd(hcd, irq, IRQF_SHARED); @@ -182,6 +146,9 @@ static int xhci_plat_probe(struct platform_device *pdev) goto dealloc_usb2_hcd; } + if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || + (pdata && pdata->usb3_lpm_capable)) + xhci->quirks |= XHCI_LPM_SUPPORT; /* * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset) * is called by usb_add_hcd(). @@ -207,12 +174,6 @@ disable_clk: if (!IS_ERR(clk)) clk_disable_unprepare(clk); -unmap_registers: - iounmap(hcd->regs); - -release_mem_region: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - put_hcd: usb_put_hcd(hcd); @@ -231,8 +192,6 @@ static int xhci_plat_remove(struct platform_device *dev) usb_remove_hcd(hcd); if (!IS_ERR(clk)) clk_disable_unprepare(clk); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); kfree(xhci); @@ -270,6 +229,8 @@ static const struct of_device_id usb_xhci_of_match[] = { { .compatible = "xhci-platform" }, { .compatible = "marvell,armada-375-xhci"}, { .compatible = "marvell,armada-380-xhci"}, + { .compatible = "renesas,xhci-r8a7790"}, + { .compatible = "renesas,xhci-r8a7791"}, { }, }; MODULE_DEVICE_TABLE(of, usb_xhci_of_match); @@ -286,12 +247,19 @@ static struct platform_driver usb_xhci_driver = { }; MODULE_ALIAS("platform:xhci-hcd"); -int xhci_register_plat(void) +static int __init xhci_plat_init(void) { + xhci_init_driver(&xhci_plat_hc_driver, xhci_plat_setup); + xhci_plat_hc_driver.start = xhci_plat_start; return platform_driver_register(&usb_xhci_driver); } +module_init(xhci_plat_init); -void xhci_unregister_plat(void) +static void __exit xhci_plat_exit(void) { platform_driver_unregister(&usb_xhci_driver); } +module_exit(xhci_plat_exit); + +MODULE_DESCRIPTION("xHCI Platform Host Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c new file mode 100644 index 000000000000..ff0d1b44ea58 --- /dev/null +++ b/drivers/usb/host/xhci-rcar.c @@ -0,0 +1,148 @@ +/* + * xHCI host controller driver for R-Car SoCs + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/usb/phy.h> + +#include "xhci.h" +#include "xhci-rcar.h" + +#define FIRMWARE_NAME "r8a779x_usb3_v1.dlmem" +MODULE_FIRMWARE(FIRMWARE_NAME); + +/*** Register Offset ***/ +#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */ +#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ +#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ + +#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */ +#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */ +#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */ +#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */ +#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */ +#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */ + +/*** Register Settings ***/ +/* Interrupt Enable */ +#define RCAR_USB3_INT_XHC_ENA 0x00000001 +#define RCAR_USB3_INT_PME_ENA 0x00000002 +#define RCAR_USB3_INT_HSE_ENA 0x00000004 +#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \ + RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA) + +/* FW Download Control & Status */ +#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001 +#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010 +#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100 + +/* LCLK Select */ +#define RCAR_USB3_LCLK_ENA_VAL 0x01030001 + +/* USB3.0 Configuration */ +#define RCAR_USB3_CONF1_VAL 0x00030204 +#define RCAR_USB3_CONF2_VAL 0x00030300 +#define RCAR_USB3_CONF3_VAL 0x13802007 + +/* USB3.0 Polarity */ +#define RCAR_USB3_RX_POL_VAL BIT(21) +#define RCAR_USB3_TX_POL_VAL BIT(4) + +void xhci_rcar_start(struct usb_hcd *hcd) +{ + u32 temp; + + if (hcd->regs != NULL) { + /* Interrupt Enable */ + temp = readl(hcd->regs + RCAR_USB3_INT_ENA); + temp |= RCAR_USB3_INT_ENA_VAL; + writel(temp, hcd->regs + RCAR_USB3_INT_ENA); + /* LCLK Select */ + writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK); + /* USB3.0 Configuration */ + writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1); + writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2); + writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3); + /* USB3.0 Polarity */ + writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL); + writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL); + } +} + +static int xhci_rcar_download_firmware(struct device *dev, void __iomem *regs) +{ + const struct firmware *fw; + int retval, index, j, time; + int timeout = 10000; + u32 data, val, temp; + + /* request R-Car USB3.0 firmware */ + retval = request_firmware(&fw, FIRMWARE_NAME, dev); + if (retval) + return retval; + + /* download R-Car USB3.0 firmware */ + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp |= RCAR_USB3_DL_CTRL_ENABLE; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (index = 0; index < fw->size; index += 4) { + /* to avoid reading beyond the end of the buffer */ + for (data = 0, j = 3; j >= 0; j--) { + if ((j + index) < fw->size) + data |= fw->data[index + j] << (8 * j); + } + writel(data, regs + RCAR_USB3_FW_DATA0); + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp |= RCAR_USB3_DL_CTRL_FW_SET_DATA0; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (time = 0; time < timeout; time++) { + val = readl(regs + RCAR_USB3_DL_CTRL); + if ((val & RCAR_USB3_DL_CTRL_FW_SET_DATA0) == 0) + break; + udelay(1); + } + if (time == timeout) { + retval = -ETIMEDOUT; + break; + } + } + + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp &= ~RCAR_USB3_DL_CTRL_ENABLE; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (time = 0; time < timeout; time++) { + val = readl(regs + RCAR_USB3_DL_CTRL); + if (val & RCAR_USB3_DL_CTRL_FW_SUCCESS) { + retval = 0; + break; + } + udelay(1); + } + if (time == timeout) + retval = -ETIMEDOUT; + + release_firmware(fw); + + return retval; +} + +/* This function needs to initialize a "phy" of usb before */ +int xhci_rcar_init_quirk(struct usb_hcd *hcd) +{ + /* If hcd->regs is NULL, we don't just call the following function */ + if (!hcd->regs) + return 0; + + return xhci_rcar_download_firmware(hcd->self.controller, hcd->regs); +} diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h new file mode 100644 index 000000000000..58501256715d --- /dev/null +++ b/drivers/usb/host/xhci-rcar.h @@ -0,0 +1,27 @@ +/* + * drivers/usb/host/xhci-rcar.h + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * 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. + */ + +#ifndef _XHCI_RCAR_H +#define _XHCI_RCAR_H + +#if IS_ENABLED(CONFIG_USB_XHCI_RCAR) +void xhci_rcar_start(struct usb_hcd *hcd); +int xhci_rcar_init_quirk(struct usb_hcd *hcd); +#else +static inline void xhci_rcar_start(struct usb_hcd *hcd) +{ +} + +static inline int xhci_rcar_init_quirk(struct usb_hcd *hcd) +{ + return 0; +} +#endif +#endif /* _XHCI_RCAR_H */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 749fc68eb5c1..bc6fcbc16f61 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -327,7 +327,6 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, * We don't want to restart any stream rings if there's a set dequeue * pointer command pending because the device can choose to start any * stream once the endpoint is on the HW schedule. - * FIXME - check all the stream rings for pending cancellations. */ if ((ep_state & EP_HALT_PENDING) || (ep_state & SET_DEQ_PENDING) || (ep_state & EP_HALTED)) @@ -364,32 +363,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, } } -/* - * Find the segment that trb is in. Start searching in start_seg. - * If we must move past a segment that has a link TRB with a toggle cycle state - * bit set, then we will toggle the value pointed at by cycle_state. - */ -static struct xhci_segment *find_trb_seg( - struct xhci_segment *start_seg, - union xhci_trb *trb, int *cycle_state) -{ - struct xhci_segment *cur_seg = start_seg; - struct xhci_generic_trb *generic_trb; - - while (cur_seg->trbs > trb || - &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) { - generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic; - if (generic_trb->field[3] & cpu_to_le32(LINK_TOGGLE)) - *cycle_state ^= 0x1; - cur_seg = cur_seg->next; - if (cur_seg == start_seg) - /* Looped over the entire list. Oops! */ - return NULL; - } - return cur_seg; -} - - static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id) @@ -459,9 +432,12 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, 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_segment *new_seg; + union xhci_trb *new_deq; dma_addr_t addr; u64 hw_dequeue; + bool cycle_found = false; + bool td_last_trb_found = false; ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, ep_index, stream_id); @@ -486,45 +462,45 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, hw_dequeue = le64_to_cpu(ep_ctx->deq); } - /* Find virtual address and segment of hardware dequeue pointer */ - state->new_deq_seg = ep_ring->deq_seg; - state->new_deq_ptr = ep_ring->dequeue; - while (xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr) - != (dma_addr_t)(hw_dequeue & ~0xf)) { - next_trb(xhci, ep_ring, &state->new_deq_seg, - &state->new_deq_ptr); - if (state->new_deq_ptr == ep_ring->dequeue) { - WARN_ON(1); - return; - } - } + new_seg = ep_ring->deq_seg; + new_deq = ep_ring->dequeue; + state->new_cycle_state = hw_dequeue & 0x1; + /* - * Find cycle state for last_trb, starting at old cycle state of - * hw_dequeue. If there is only one segment ring, find_trb_seg() will - * return immediately and cannot toggle the cycle state if this search - * wraps around, so add one more toggle manually in that case. + * We want to find the pointer, segment and cycle state of the new trb + * (the one after current TD's last_trb). We know the cycle state at + * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are + * found. */ - state->new_cycle_state = hw_dequeue & 0x1; - if (ep_ring->first_seg == ep_ring->first_seg->next && - cur_td->last_trb < state->new_deq_ptr) - state->new_cycle_state ^= 0x1; + do { + if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq) + == (dma_addr_t)(hw_dequeue & ~0xf)) { + cycle_found = true; + if (td_last_trb_found) + break; + } + if (new_deq == cur_td->last_trb) + td_last_trb_found = true; - state->new_deq_ptr = cur_td->last_trb; - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Finding segment containing last TRB in TD."); - state->new_deq_seg = find_trb_seg(state->new_deq_seg, - state->new_deq_ptr, &state->new_cycle_state); - if (!state->new_deq_seg) { - WARN_ON(1); - return; - } + if (cycle_found && + TRB_TYPE_LINK_LE32(new_deq->generic.field[3]) && + new_deq->generic.field[3] & cpu_to_le32(LINK_TOGGLE)) + state->new_cycle_state ^= 0x1; + + next_trb(xhci, ep_ring, &new_seg, &new_deq); + + /* Search wrapped around, bail out */ + if (new_deq == ep->ring->dequeue) { + xhci_err(xhci, "Error: Failed finding new dequeue state\n"); + state->new_deq_seg = NULL; + state->new_deq_ptr = NULL; + return; + } + + } while (!cycle_found || !td_last_trb_found); - /* Increment to find next TRB after last_trb. Cycle if appropriate. */ - trb = &state->new_deq_ptr->generic; - if (TRB_TYPE_LINK_LE32(trb->field[3]) && - (trb->field[3] & cpu_to_le32(LINK_TOGGLE))) - state->new_cycle_state ^= 0x1; - next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); + state->new_deq_seg = new_seg; + state->new_deq_ptr = new_deq; /* Don't update the ring cycle state for the producer (us). */ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, @@ -595,40 +571,6 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, } } -static int queue_set_tr_deq(struct xhci_hcd *xhci, - struct xhci_command *cmd, int slot_id, - unsigned int ep_index, unsigned int stream_id, - struct xhci_segment *deq_seg, - union xhci_trb *deq_ptr, u32 cycle_state); - -void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, - struct xhci_command *cmd, - unsigned int slot_id, unsigned int ep_index, - unsigned int stream_id, - struct xhci_dequeue_state *deq_state) -{ - struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; - - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), " - "new deq ptr = %p (0x%llx dma), new cycle = %u", - deq_state->new_deq_seg, - (unsigned long long)deq_state->new_deq_seg->dma, - deq_state->new_deq_ptr, - (unsigned long long)xhci_trb_virt_to_dma(deq_state->new_deq_seg, deq_state->new_deq_ptr), - deq_state->new_cycle_state); - queue_set_tr_deq(xhci, cmd, slot_id, ep_index, stream_id, - deq_state->new_deq_seg, - deq_state->new_deq_ptr, - (u32) deq_state->new_cycle_state); - /* Stop the TD queueing code from ringing the doorbell until - * this command completes. The HC won't set the dequeue pointer - * if the ring is running, and ringing the doorbell starts the - * ring running. - */ - ep->ep_state |= SET_DEQ_PENDING; -} - static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) { @@ -766,12 +708,8 @@ remove_finished_td: /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */ if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { - struct xhci_command *command; - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); - xhci_queue_new_dequeue_state(xhci, command, - slot_id, ep_index, - ep->stopped_td->urb->stream_id, - &deq_state); + xhci_queue_new_dequeue_state(xhci, slot_id, ep_index, + ep->stopped_td->urb->stream_id, &deq_state); xhci_ring_cmd_db(xhci); } else { /* Otherwise ring the doorbell(s) to restart queued transfers */ @@ -1026,8 +964,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, 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; - return; + goto cleanup; } ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); @@ -1092,6 +1029,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, } } +cleanup: dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; dev->eps[ep_index].queued_deq_seg = NULL; dev->eps[ep_index].queued_deq_ptr = NULL; @@ -1118,6 +1056,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, if (xhci->quirks & XHCI_RESET_EP_QUIRK) { struct xhci_command *command; command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + if (!command) { + xhci_warn(xhci, "WARN Cannot submit cfg ep: ENOMEM\n"); + return; + } xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "Queueing configure endpoint command"); xhci_queue_configure_endpoint(xhci, command, @@ -1718,10 +1660,12 @@ cleanup: * TRB in this TD, this function returns that TRB's segment. Otherwise it * returns 0. */ -struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, +struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, + struct xhci_segment *start_seg, union xhci_trb *start_trb, union xhci_trb *end_trb, - dma_addr_t suspect_dma) + dma_addr_t suspect_dma, + bool debug) { dma_addr_t start_dma; dma_addr_t end_seg_dma; @@ -1740,6 +1684,15 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, /* If the end TRB isn't in this segment, this is set to 0 */ end_trb_dma = xhci_trb_virt_to_dma(cur_seg, end_trb); + if (debug) + xhci_warn(xhci, + "Looking for event-dma %016llx trb-start %016llx trb-end %016llx seg-start %016llx seg-end %016llx\n", + (unsigned long long)suspect_dma, + (unsigned long long)start_dma, + (unsigned long long)end_trb_dma, + (unsigned long long)cur_seg->dma, + (unsigned long long)end_seg_dma); + if (end_trb_dma > 0) { /* The end TRB is in this segment, so suspect should be here */ if (start_dma <= end_trb_dma) { @@ -2472,8 +2425,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, td_num--; /* Is this a TRB in the currently executing TD? */ - event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, - td->last_trb, event_dma); + event_seg = trb_in_td(xhci, ep_ring->deq_seg, ep_ring->dequeue, + td->last_trb, event_dma, false); /* * Skip the Force Stopped Event. The event_trb(event_dma) of FSE @@ -2483,7 +2436,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, * last TRB of the previous TD. The command completion handle * will take care the rest. */ - if (!event_seg && trb_comp_code == COMP_STOP_INVAL) { + if (!event_seg && (trb_comp_code == COMP_STOP || + trb_comp_code == COMP_STOP_INVAL)) { ret = 0; goto cleanup; } @@ -2504,7 +2458,12 @@ static int handle_tx_event(struct xhci_hcd *xhci, /* HC is busted, give up! */ xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not " - "part of current TD\n"); + "part of current TD ep_index %d " + "comp_code %u\n", ep_index, + trb_comp_code); + trb_in_td(xhci, ep_ring->deq_seg, + ep_ring->dequeue, td->last_trb, + event_dma, true); return -ESHUTDOWN; } @@ -3944,14 +3903,11 @@ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd, trb_slot_id | trb_ep_index | type | trb_suspend, false); } -/* Set Transfer Ring Dequeue Pointer command. - * This should not be used for endpoints that have streams enabled. - */ -static int queue_set_tr_deq(struct xhci_hcd *xhci, struct xhci_command *cmd, - int slot_id, - unsigned int ep_index, unsigned int stream_id, - struct xhci_segment *deq_seg, - union xhci_trb *deq_ptr, u32 cycle_state) +/* Set Transfer Ring Dequeue Pointer command */ +void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, + unsigned int stream_id, + struct xhci_dequeue_state *deq_state) { dma_addr_t addr; u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); @@ -3960,28 +3916,59 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, struct xhci_command *cmd, u32 trb_sct = 0; u32 type = TRB_TYPE(TRB_SET_DEQ); struct xhci_virt_ep *ep; + struct xhci_command *cmd; + int ret; - addr = xhci_trb_virt_to_dma(deq_seg, deq_ptr); + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), new deq ptr = %p (0x%llx dma), new cycle = %u", + deq_state->new_deq_seg, + (unsigned long long)deq_state->new_deq_seg->dma, + deq_state->new_deq_ptr, + (unsigned long long)xhci_trb_virt_to_dma( + deq_state->new_deq_seg, deq_state->new_deq_ptr), + deq_state->new_cycle_state); + + addr = xhci_trb_virt_to_dma(deq_state->new_deq_seg, + deq_state->new_deq_ptr); if (addr == 0) { xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n"); xhci_warn(xhci, "WARN deq seg = %p, deq pt = %p\n", - deq_seg, deq_ptr); - return 0; + deq_state->new_deq_seg, deq_state->new_deq_ptr); + return; } ep = &xhci->devs[slot_id]->eps[ep_index]; if ((ep->ep_state & SET_DEQ_PENDING)) { xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n"); xhci_warn(xhci, "A Set TR Deq Ptr command is pending.\n"); - return 0; + return; } - ep->queued_deq_seg = deq_seg; - ep->queued_deq_ptr = deq_ptr; + + /* This function gets called from contexts where it cannot sleep */ + cmd = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + if (!cmd) { + xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr: ENOMEM\n"); + return; + } + + ep->queued_deq_seg = deq_state->new_deq_seg; + ep->queued_deq_ptr = deq_state->new_deq_ptr; if (stream_id) trb_sct = SCT_FOR_TRB(SCT_PRI_TR); - return queue_command(xhci, cmd, - lower_32_bits(addr) | trb_sct | cycle_state, - upper_32_bits(addr), trb_stream_id, - trb_slot_id | trb_ep_index | type, false); + ret = queue_command(xhci, cmd, + lower_32_bits(addr) | trb_sct | deq_state->new_cycle_state, + upper_32_bits(addr), trb_stream_id, + trb_slot_id | trb_ep_index | type, false); + if (ret < 0) { + xhci_free_command(xhci, cmd); + return; + } + + /* Stop the TD queueing code from ringing the doorbell until + * this command completes. The HC won't set the dequeue pointer + * if the ring is running, and ringing the doorbell starts the + * ring running. + */ + ep->ep_state |= SET_DEQ_PENDING; } int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd, diff --git a/drivers/usb/host/xhci-trace.c b/drivers/usb/host/xhci-trace.c index 7cf30c83dcf3..367b630bdb3c 100644 --- a/drivers/usb/host/xhci-trace.c +++ b/drivers/usb/host/xhci-trace.c @@ -13,3 +13,5 @@ #define CREATE_TRACE_POINTS #include "xhci-trace.h" + +EXPORT_TRACEPOINT_SYMBOL_GPL(xhci_dbg_quirks); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7436d5f5e67a..2a5d45b4cb15 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -491,7 +491,7 @@ static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci) * Systems: * Vendor: Hewlett-Packard -> System Models: Z420, Z620 and Z820 */ -bool xhci_compliance_mode_recovery_timer_quirk_check(void) +static bool xhci_compliance_mode_recovery_timer_quirk_check(void) { const char *dmi_product_name, *dmi_sys_vendor; @@ -653,6 +653,7 @@ int xhci_run(struct usb_hcd *hcd) "Finished xhci_run for USB2 roothub"); return 0; } +EXPORT_SYMBOL_GPL(xhci_run); static void xhci_only_stop_hcd(struct usb_hcd *hcd) { @@ -871,6 +872,8 @@ int xhci_suspend(struct xhci_hcd *xhci) xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); + clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); + del_timer_sync(&xhci->shared_hcd->rh_timer); spin_lock_irq(&xhci->lock); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); @@ -927,6 +930,7 @@ int xhci_suspend(struct xhci_hcd *xhci) return rc; } +EXPORT_SYMBOL_GPL(xhci_suspend); /* * start xHC (not bus-specific) @@ -1075,9 +1079,12 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "%s: starting port polling.\n", __func__); set_bit(HCD_FLAG_POLL_RH, &hcd->flags); usb_hcd_poll_rh_status(hcd); + set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); + usb_hcd_poll_rh_status(xhci->shared_hcd); return retval; } +EXPORT_SYMBOL_GPL(xhci_resume); #endif /* CONFIG_PM */ /*-------------------------------------------------------------------------*/ @@ -1553,6 +1560,10 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) */ if (!(ep->ep_state & EP_HALT_PENDING)) { command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + if (!command) { + ret = -ENOMEM; + goto done; + } ep->ep_state |= EP_HALT_PENDING; ep->stop_cmds_pending++; ep->stop_cmd_timer.expires = jiffies + @@ -1586,12 +1597,10 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx, *out_ctx; struct xhci_input_control_ctx *ctrl_ctx; - struct xhci_slot_ctx *slot_ctx; - unsigned int last_ctx; unsigned int ep_index; struct xhci_ep_ctx *ep_ctx; u32 drop_flag; - u32 new_add_flags, new_drop_flags, new_slot_info; + u32 new_add_flags, new_drop_flags; int ret; ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); @@ -1638,24 +1647,13 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag); new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); - last_ctx = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)); - slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); - /* Update the last valid endpoint context, if we deleted the last one */ - if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) > - LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - } - new_slot_info = le32_to_cpu(slot_ctx->dev_info); - xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); - xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n", + xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, (unsigned int) new_drop_flags, - (unsigned int) new_add_flags, - (unsigned int) new_slot_info); + (unsigned int) new_add_flags); return 0; } @@ -1678,11 +1676,9 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx, *out_ctx; unsigned int ep_index; - struct xhci_slot_ctx *slot_ctx; struct xhci_input_control_ctx *ctrl_ctx; u32 added_ctxs; - unsigned int last_ctx; - u32 new_add_flags, new_drop_flags, new_slot_info; + u32 new_add_flags, new_drop_flags; struct xhci_virt_device *virt_dev; int ret = 0; @@ -1697,7 +1693,6 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return -ENODEV; added_ctxs = xhci_get_endpoint_flag(&ep->desc); - last_ctx = xhci_last_valid_endpoint(added_ctxs); if (added_ctxs == SLOT_FLAG || added_ctxs == EP0_FLAG) { /* FIXME when we have to issue an evaluate endpoint command to * deal with ep0 max packet size changing once we get the @@ -1763,24 +1758,14 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, */ new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); - slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); - /* Update the last valid endpoint context, if we just added one past */ - if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) < - LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - } - new_slot_info = le32_to_cpu(slot_ctx->dev_info); - /* Store the usb_device pointer for later use */ ep->hcpriv = udev; - xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n", + xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, (unsigned int) new_drop_flags, - (unsigned int) new_add_flags, - (unsigned int) new_slot_info); + (unsigned int) new_add_flags); return 0; } @@ -1830,15 +1815,15 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = -ETIME; break; case COMP_ENOMEM: - dev_warn(&udev->dev, "Not enough host controller resources " - "for new device state.\n"); + dev_warn(&udev->dev, + "Not enough host controller resources for new device state.\n"); ret = -ENOMEM; /* FIXME: can we allocate more resources for the HC? */ break; case COMP_BW_ERR: case COMP_2ND_BW_ERR: - dev_warn(&udev->dev, "Not enough bandwidth " - "for new device state.\n"); + dev_warn(&udev->dev, + "Not enough bandwidth for new device state.\n"); ret = -ENOSPC; /* FIXME: can we go back to the old state? */ break; @@ -1850,8 +1835,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = -EINVAL; break; case COMP_DEV_ERR: - dev_warn(&udev->dev, "ERROR: Incompatible device for endpoint " - "configure command.\n"); + dev_warn(&udev->dev, + "ERROR: Incompatible device for endpoint configure command.\n"); ret = -ENODEV; break; case COMP_SUCCESS: @@ -1860,8 +1845,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = 0; break; default: - xhci_err(xhci, "ERROR: unexpected command completion " - "code 0x%x.\n", *cmd_status); + xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n", + *cmd_status); ret = -EINVAL; break; } @@ -1881,24 +1866,24 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, ret = -ETIME; break; case COMP_EINVAL: - dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate " - "context command.\n"); + dev_warn(&udev->dev, + "WARN: xHCI driver setup invalid evaluate context command.\n"); ret = -EINVAL; break; case COMP_EBADSLT: - dev_warn(&udev->dev, "WARN: slot not enabled for" - "evaluate context command.\n"); + dev_warn(&udev->dev, + "WARN: slot not enabled for evaluate context command.\n"); ret = -EINVAL; break; case COMP_CTX_STATE: - dev_warn(&udev->dev, "WARN: invalid context state for " - "evaluate context command.\n"); + dev_warn(&udev->dev, + "WARN: invalid context state for evaluate context command.\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1); ret = -EINVAL; break; case COMP_DEV_ERR: - dev_warn(&udev->dev, "ERROR: Incompatible device for evaluate " - "context command.\n"); + dev_warn(&udev->dev, + "ERROR: Incompatible device for evaluate context command.\n"); ret = -ENODEV; break; case COMP_MEL_ERR: @@ -1912,8 +1897,8 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, ret = 0; break; default: - xhci_err(xhci, "ERROR: unexpected command completion " - "code 0x%x.\n", *cmd_status); + xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n", + *cmd_status); ret = -EINVAL; break; } @@ -2750,8 +2735,19 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) ret = 0; goto command_cleanup; } - xhci_dbg(xhci, "New Input Control Context:\n"); + /* Fix up Context Entries field. Minimum value is EP0 == BIT(1). */ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); + for (i = 31; i >= 1; i--) { + __le32 le32 = cpu_to_le32(BIT(i)); + + if ((virt_dev->eps[i-1].ring && !(ctrl_ctx->drop_flags & le32)) + || (ctrl_ctx->add_flags & le32) || i == 1) { + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(i)); + break; + } + } + xhci_dbg(xhci, "New Input Control Context:\n"); xhci_dbg_ctx(xhci, virt_dev->in_ctx, LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); @@ -2891,18 +2887,16 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, ep_index, ep->stopped_stream, ep->stopped_td, &deq_state); + if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg) + return; + /* HW with the reset endpoint quirk will use the saved dequeue state to * issue a configure endpoint command later. */ if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) { - struct xhci_command *command; - /* Can't sleep if we're called from cleanup_halted_endpoint() */ - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); - if (!command) - return; xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, "Queueing new dequeue state"); - xhci_queue_new_dequeue_state(xhci, command, udev->slot_id, + xhci_queue_new_dequeue_state(xhci, udev->slot_id, ep_index, ep->stopped_stream, &deq_state); } else { /* Better hope no one uses the input context between now and the @@ -3163,7 +3157,8 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, num_streams); /* MaxPSASize value 0 (2 streams) means streams are not supported */ - if (HCC_MAX_PSA(xhci->hcc_params) < 4) { + if ((xhci->quirks & XHCI_BROKEN_STREAMS) || + HCC_MAX_PSA(xhci->hcc_params) < 4) { xhci_dbg(xhci, "xHCI controller does not support streams.\n"); return -ENOSYS; } @@ -3768,8 +3763,8 @@ disable_slot: /* * Issue an Address Device command and optionally send a corresponding * SetAddress request to the device. - * We should be protected by the usb_address0_mutex in khubd's hub_port_init, so - * we should only issue and wait on one address command at the same time. + * We should be protected by the usb_address0_mutex in hub_wq's hub_port_init, + * so we should only issue and wait on one address command at the same time. */ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, enum xhci_setup_dev setup) @@ -3978,13 +3973,21 @@ static int __maybe_unused xhci_change_max_exit_latency(struct xhci_hcd *xhci, int ret; spin_lock_irqsave(&xhci->lock, flags); - if (max_exit_latency == xhci->devs[udev->slot_id]->current_mel) { + + virt_dev = xhci->devs[udev->slot_id]; + + /* + * virt_dev might not exists yet if xHC resumed from hibernate (S4) and + * xHC was re-initialized. Exit latency will be set later after + * hub_port_finish_reset() is done and xhci->devs[] are re-allocated + */ + + if (!virt_dev || max_exit_latency == virt_dev->current_mel) { spin_unlock_irqrestore(&xhci->lock, flags); return 0; } /* Attempt to issue an Evaluate Context command to change the MEL. */ - virt_dev = xhci->devs[udev->slot_id]; command = xhci->lpm_command; ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx); if (!ctrl_ctx) { @@ -4303,8 +4306,7 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev, return USB3_LPM_DISABLED; } -/* Returns the hub-encoded U1 timeout value. - * The U1 timeout should be the maximum of the following values: +/* The U1 timeout should be the maximum of the following values: * - For control endpoints, U1 system exit latency (SEL) * 3 * - For bulk endpoints, U1 SEL * 5 * - For interrupt endpoints: @@ -4312,7 +4314,8 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev, * - Periodic EPs, max(105% of bInterval, U1 SEL * 2) * - For isochronous endpoints, max(105% of bInterval, U1 SEL * 2) */ -static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, +static unsigned long long xhci_calculate_intel_u1_timeout( + struct usb_device *udev, struct usb_endpoint_descriptor *desc) { unsigned long long timeout_ns; @@ -4344,11 +4347,28 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, return 0; } - /* The U1 timeout is encoded in 1us intervals. */ - timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000); - /* Don't return a timeout of zero, because that's USB3_LPM_DISABLED. */ + return timeout_ns; +} + +/* Returns the hub-encoded U1 timeout value. */ +static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_endpoint_descriptor *desc) +{ + unsigned long long timeout_ns; + + if (xhci->quirks & XHCI_INTEL_HOST) + timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc); + else + timeout_ns = udev->u1_params.sel; + + /* The U1 timeout is encoded in 1us intervals. + * Don't return a timeout of zero, because that's USB3_LPM_DISABLED. + */ if (timeout_ns == USB3_LPM_DISABLED) - timeout_ns++; + timeout_ns = 1; + else + timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000); /* If the necessary timeout value is bigger than what we can set in the * USB 3.0 hub, we have to disable hub-initiated U1. @@ -4360,14 +4380,14 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1); } -/* Returns the hub-encoded U2 timeout value. - * The U2 timeout should be the maximum of: +/* The U2 timeout should be the maximum of: * - 10 ms (to avoid the bandwidth impact on the scheduler) * - largest bInterval of any active periodic endpoint (to avoid going * into lower power link states between intervals). * - the U2 Exit Latency of the device */ -static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev, +static unsigned long long xhci_calculate_intel_u2_timeout( + struct usb_device *udev, struct usb_endpoint_descriptor *desc) { unsigned long long timeout_ns; @@ -4383,6 +4403,21 @@ static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev, if (u2_del_ns > timeout_ns) timeout_ns = u2_del_ns; + return timeout_ns; +} + +/* Returns the hub-encoded U2 timeout value. */ +static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_endpoint_descriptor *desc) +{ + unsigned long long timeout_ns; + + if (xhci->quirks & XHCI_INTEL_HOST) + timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc); + else + timeout_ns = udev->u2_params.sel; + /* The U2 timeout is encoded in 256us intervals */ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000); /* If the necessary timeout value is bigger than what we can set in the @@ -4401,13 +4436,10 @@ static u16 xhci_call_host_update_timeout_for_endpoint(struct xhci_hcd *xhci, enum usb3_link_state state, u16 *timeout) { - if (state == USB3_LPM_U1) { - if (xhci->quirks & XHCI_INTEL_HOST) - return xhci_calculate_intel_u1_timeout(udev, desc); - } else { - if (xhci->quirks & XHCI_INTEL_HOST) - return xhci_calculate_intel_u2_timeout(udev, desc); - } + if (state == USB3_LPM_U1) + return xhci_calculate_u1_timeout(xhci, udev, desc); + else if (state == USB3_LPM_U2) + return xhci_calculate_u2_timeout(xhci, udev, desc); return USB3_LPM_DISABLED; } @@ -4484,7 +4516,8 @@ static int xhci_check_tier_policy(struct xhci_hcd *xhci, { if (xhci->quirks & XHCI_INTEL_HOST) return xhci_check_intel_tier_policy(udev, state); - return -EINVAL; + else + return 0; } /* Returns the U1 or U2 timeout that should be enabled. @@ -4872,6 +4905,76 @@ error: kfree(xhci); return retval; } +EXPORT_SYMBOL_GPL(xhci_gen_setup); + +static const struct hc_driver xhci_hc_driver = { + .description = "xhci-hcd", + .product_desc = "xHCI Host Controller", + .hcd_priv_size = sizeof(struct xhci_hcd *), + + /* + * generic hardware linkage + */ + .irq = xhci_irq, + .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED, + + /* + * basic lifecycle operations + */ + .reset = NULL, /* set in xhci_init_driver() */ + .start = xhci_run, + .stop = xhci_stop, + .shutdown = xhci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = xhci_urb_enqueue, + .urb_dequeue = xhci_urb_dequeue, + .alloc_dev = xhci_alloc_dev, + .free_dev = xhci_free_dev, + .alloc_streams = xhci_alloc_streams, + .free_streams = xhci_free_streams, + .add_endpoint = xhci_add_endpoint, + .drop_endpoint = xhci_drop_endpoint, + .endpoint_reset = xhci_endpoint_reset, + .check_bandwidth = xhci_check_bandwidth, + .reset_bandwidth = xhci_reset_bandwidth, + .address_device = xhci_address_device, + .enable_device = xhci_enable_device, + .update_hub_device = xhci_update_hub_device, + .reset_device = xhci_discover_or_reset_device, + + /* + * scheduling support + */ + .get_frame_number = xhci_get_frame, + + /* + * root hub support + */ + .hub_control = xhci_hub_control, + .hub_status_data = xhci_hub_status_data, + .bus_suspend = xhci_bus_suspend, + .bus_resume = xhci_bus_resume, + + /* + * call back when device connected and addressed + */ + .update_device = xhci_update_device, + .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm, + .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout, + .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout, + .find_raw_port_number = xhci_find_raw_port_number, +}; + +void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)) +{ + BUG_ON(!setup_fn); + *drv = xhci_hc_driver; + drv->reset = setup_fn; +} +EXPORT_SYMBOL_GPL(xhci_init_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); @@ -4879,18 +4982,6 @@ MODULE_LICENSE("GPL"); static int __init xhci_hcd_init(void) { - int retval; - - retval = xhci_register_pci(); - if (retval < 0) { - pr_debug("Problem registering PCI driver.\n"); - return retval; - } - retval = xhci_register_plat(); - if (retval < 0) { - pr_debug("Problem registering platform driver.\n"); - goto unreg_pci; - } /* * Check the compiler generated sizes of structures that must be laid * out in specific ways for hardware access. @@ -4909,15 +5000,5 @@ static int __init xhci_hcd_init(void) /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */ BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8); return 0; -unreg_pci: - xhci_unregister_pci(); - return retval; } module_init(xhci_hcd_init); - -static void __exit xhci_hcd_cleanup(void) -{ - xhci_unregister_pci(); - xhci_unregister_plat(); -} -module_exit(xhci_hcd_cleanup); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9ffecd56600d..df76d642e719 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1558,6 +1558,8 @@ struct xhci_hcd { #define XHCI_PLAT (1 << 16) #define XHCI_SLOW_SUSPEND (1 << 17) #define XHCI_SPURIOUS_WAKEUP (1 << 18) +/* For controllers with a broken beyond repair streams implementation */ +#define XHCI_BROKEN_STREAMS (1 << 19) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ @@ -1729,25 +1731,6 @@ void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv); void xhci_free_command(struct xhci_hcd *xhci, struct xhci_command *command); -#ifdef CONFIG_PCI -/* xHCI PCI glue */ -int xhci_register_pci(void); -void xhci_unregister_pci(void); -#else -static inline int xhci_register_pci(void) { return 0; } -static inline void xhci_unregister_pci(void) {} -#endif - -#if IS_ENABLED(CONFIG_USB_XHCI_PLATFORM) -int xhci_register_plat(void); -void xhci_unregister_plat(void); -#else -static inline int xhci_register_plat(void) -{ return 0; } -static inline void xhci_unregister_plat(void) -{ } -#endif - /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); int xhci_handshake(struct xhci_hcd *xhci, void __iomem *ptr, @@ -1760,6 +1743,7 @@ int xhci_run(struct usb_hcd *hcd); void xhci_stop(struct usb_hcd *hcd); void xhci_shutdown(struct usb_hcd *hcd); int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); +void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)); #ifdef CONFIG_PM int xhci_suspend(struct xhci_hcd *xhci); @@ -1802,9 +1786,9 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); /* xHCI ring, segment, TRB, and TD functions */ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); -struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, - union xhci_trb *start_trb, union xhci_trb *end_trb, - dma_addr_t suspect_dma); +struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, + struct xhci_segment *start_seg, union xhci_trb *start_trb, + union xhci_trb *end_trb, dma_addr_t suspect_dma, bool debug); int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code); void xhci_ring_cmd_db(struct xhci_hcd *xhci); int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd, @@ -1837,7 +1821,6 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, unsigned int stream_id, struct xhci_td *cur_td, struct xhci_dequeue_state *state); void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, - struct xhci_command *cmd, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id, struct xhci_dequeue_state *deq_state); @@ -1885,7 +1868,4 @@ struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_hcd *xhci, struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_index); -/* xHCI quirks */ -bool xhci_compliance_mode_recovery_timer_quirk_check(void); - #endif /* __LINUX_XHCI_HCD_H */ diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 1bca274dc3b5..76d77206e011 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -248,3 +248,10 @@ config USB_HSIC_USB3503 select REGMAP_I2C help This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. + +config USB_LINK_LAYER_TEST + tristate "USB Link Layer Test driver" + help + This driver is for generating specific traffic for Super Speed Link + Layer Test Device. Say Y only when you want to conduct USB Super Speed + Link Layer Test for host controllers. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index e748fd5dbe94..65b0402c1ca1 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_USB_YUREX) += yurex.o obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ +obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c new file mode 100644 index 000000000000..62cb8cd08403 --- /dev/null +++ b/drivers/usb/misc/lvstest.c @@ -0,0 +1,460 @@ +/* + * drivers/usb/misc/lvstest.c + * + * Test pattern generation for Link Layer Validation System Tests + * + * Copyright (C) 2014 ST Microelectronics + * Pratyush Anand <pratyush.anand@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/ch11.h> +#include <linux/usb/hcd.h> +#include <linux/usb/phy.h> + +struct lvs_rh { + /* root hub interface */ + struct usb_interface *intf; + /* if lvs device connected */ + bool present; + /* port no at which lvs device is present */ + int portnum; + /* urb buffer */ + u8 buffer[8]; + /* class descriptor */ + struct usb_hub_descriptor descriptor; + /* urb for polling interrupt pipe */ + struct urb *urb; + /* LVS RH work queue */ + struct workqueue_struct *rh_queue; + /* LVH RH work */ + struct work_struct rh_work; + /* RH port status */ + struct usb_port_status port_status; +}; + +static struct usb_device *create_lvs_device(struct usb_interface *intf) +{ + struct usb_device *udev, *hdev; + struct usb_hcd *hcd; + struct lvs_rh *lvs = usb_get_intfdata(intf); + + if (!lvs->present) { + dev_err(&intf->dev, "No LVS device is present\n"); + return NULL; + } + + hdev = interface_to_usbdev(intf); + hcd = bus_to_hcd(hdev->bus); + + udev = usb_alloc_dev(hdev, hdev->bus, lvs->portnum); + if (!udev) { + dev_err(&intf->dev, "Could not allocate lvs udev\n"); + return NULL; + } + udev->speed = USB_SPEED_SUPER; + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); + usb_set_device_state(udev, USB_STATE_DEFAULT); + + if (hcd->driver->enable_device) { + if (hcd->driver->enable_device(hcd, udev) < 0) { + dev_err(&intf->dev, "Failed to enable\n"); + usb_put_dev(udev); + return NULL; + } + } + + return udev; +} + +static void destroy_lvs_device(struct usb_device *udev) +{ + struct usb_device *hdev = udev->parent; + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); + + if (hcd->driver->free_dev) + hcd->driver->free_dev(hcd, udev); + + usb_put_dev(udev); +} + +static int lvs_rh_clear_port_feature(struct usb_device *hdev, + int port1, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1, + NULL, 0, 1000); +} + +static int lvs_rh_set_port_feature(struct usb_device *hdev, + int port1, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1, + NULL, 0, 1000); +} + +static ssize_t u3_entry_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + struct usb_device *udev; + int ret; + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + return -ENOMEM; + } + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_SUSPEND); + if (ret < 0) + dev_err(dev, "can't issue U3 entry %d\n", ret); + + destroy_lvs_device(udev); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(u3_entry); + +static ssize_t u3_exit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + struct usb_device *udev; + int ret; + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + return -ENOMEM; + } + + ret = lvs_rh_clear_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_SUSPEND); + if (ret < 0) + dev_err(dev, "can't issue U3 exit %d\n", ret); + + destroy_lvs_device(udev); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(u3_exit); + +static ssize_t hot_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + int ret; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_RESET); + if (ret < 0) { + dev_err(dev, "can't issue hot reset %d\n", ret); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(hot_reset); + +static ssize_t u2_timeout_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "couldn't parse string %d\n", ret); + return ret; + } + + if (val < 0 || val > 127) + return -EINVAL; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8), + USB_PORT_FEAT_U2_TIMEOUT); + if (ret < 0) { + dev_err(dev, "Error %d while setting U2 timeout %ld\n", ret, val); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(u2_timeout); + +static ssize_t u1_timeout_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "couldn't parse string %d\n", ret); + return ret; + } + + if (val < 0 || val > 127) + return -EINVAL; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8), + USB_PORT_FEAT_U1_TIMEOUT); + if (ret < 0) { + dev_err(dev, "Error %d while setting U1 timeout %ld\n", ret, val); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(u1_timeout); + +static ssize_t get_dev_desc_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev; + struct usb_device_descriptor *descriptor; + int ret; + + descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL); + if (!descriptor) { + dev_err(dev, "failed to allocate descriptor memory\n"); + return -ENOMEM; + } + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + ret = -ENOMEM; + goto free_desc; + } + + ret = usb_control_msg(udev, (PIPE_CONTROL << 30) | USB_DIR_IN, + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, + 0, descriptor, sizeof(*descriptor), + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + dev_err(dev, "can't read device descriptor %d\n", ret); + + destroy_lvs_device(udev); + +free_desc: + kfree(descriptor); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(get_dev_desc); + +static struct attribute *lvs_attributes[] = { + &dev_attr_get_dev_desc.attr, + &dev_attr_u1_timeout.attr, + &dev_attr_u2_timeout.attr, + &dev_attr_hot_reset.attr, + &dev_attr_u3_entry.attr, + &dev_attr_u3_exit.attr, + NULL +}; + +static const struct attribute_group lvs_attr_group = { + .attrs = lvs_attributes, +}; + +static void lvs_rh_work(struct work_struct *work) +{ + struct lvs_rh *lvs = container_of(work, struct lvs_rh, rh_work); + struct usb_interface *intf = lvs->intf; + struct usb_device *hdev = interface_to_usbdev(intf); + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); + struct usb_hub_descriptor *descriptor = &lvs->descriptor; + struct usb_port_status *port_status = &lvs->port_status; + int i, ret = 0; + u16 portchange; + + /* Examine each root port */ + for (i = 1; i <= descriptor->bNbrPorts; i++) { + ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, i, + port_status, sizeof(*port_status), 1000); + if (ret < 4) + continue; + + portchange = le16_to_cpu(port_status->wPortChange); + + if (portchange & USB_PORT_STAT_C_LINK_STATE) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_PORT_LINK_STATE); + if (portchange & USB_PORT_STAT_C_ENABLE) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_ENABLE); + if (portchange & USB_PORT_STAT_C_RESET) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_RESET); + if (portchange & USB_PORT_STAT_C_BH_RESET) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_BH_PORT_RESET); + if (portchange & USB_PORT_STAT_C_CONNECTION) { + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_CONNECTION); + + if (le16_to_cpu(port_status->wPortStatus) & + USB_PORT_STAT_CONNECTION) { + lvs->present = true; + lvs->portnum = i; + if (hcd->usb_phy) + usb_phy_notify_connect(hcd->usb_phy, + USB_SPEED_SUPER); + } else { + lvs->present = false; + if (hcd->usb_phy) + usb_phy_notify_disconnect(hcd->usb_phy, + USB_SPEED_SUPER); + } + break; + } + } + + ret = usb_submit_urb(lvs->urb, GFP_KERNEL); + if (ret != 0 && ret != -ENODEV && ret != -EPERM) + dev_err(&intf->dev, "urb resubmit error %d\n", ret); +} + +static void lvs_rh_irq(struct urb *urb) +{ + struct lvs_rh *lvs = urb->context; + + queue_work(lvs->rh_queue, &lvs->rh_work); +} + +static int lvs_rh_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *hdev; + struct usb_host_interface *desc; + struct usb_endpoint_descriptor *endpoint; + struct lvs_rh *lvs; + unsigned int pipe; + int ret, maxp; + + hdev = interface_to_usbdev(intf); + desc = intf->cur_altsetting; + endpoint = &desc->endpoint[0].desc; + + /* valid only for SS root hub */ + if (hdev->descriptor.bDeviceProtocol != USB_HUB_PR_SS || hdev->parent) { + dev_err(&intf->dev, "Bind LVS driver with SS root Hub only\n"); + return -EINVAL; + } + + lvs = devm_kzalloc(&intf->dev, sizeof(*lvs), GFP_KERNEL); + if (!lvs) + return -ENOMEM; + + lvs->intf = intf; + usb_set_intfdata(intf, lvs); + + /* how many number of ports this root hub has */ + ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_SS_HUB << 8, 0, &lvs->descriptor, + USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT); + if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) { + dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret); + return ret; + } + + /* submit urb to poll interrupt endpoint */ + lvs->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!lvs->urb) { + dev_err(&intf->dev, "couldn't allocate lvs urb\n"); + return -ENOMEM; + } + + lvs->rh_queue = create_singlethread_workqueue("lvs_rh_queue"); + if (!lvs->rh_queue) { + dev_err(&intf->dev, "couldn't create workqueue\n"); + ret = -ENOMEM; + goto free_urb; + } + + INIT_WORK(&lvs->rh_work, lvs_rh_work); + + ret = sysfs_create_group(&intf->dev.kobj, &lvs_attr_group); + if (ret < 0) { + dev_err(&intf->dev, "Failed to create sysfs node %d\n", ret); + goto destroy_queue; + } + + pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); + usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp, + lvs_rh_irq, lvs, endpoint->bInterval); + + ret = usb_submit_urb(lvs->urb, GFP_KERNEL); + if (ret < 0) { + dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret); + goto sysfs_remove; + } + + return ret; + +sysfs_remove: + sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group); +destroy_queue: + destroy_workqueue(lvs->rh_queue); +free_urb: + usb_free_urb(lvs->urb); + return ret; +} + +static void lvs_rh_disconnect(struct usb_interface *intf) +{ + struct lvs_rh *lvs = usb_get_intfdata(intf); + + sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group); + destroy_workqueue(lvs->rh_queue); + usb_free_urb(lvs->urb); +} + +static struct usb_driver lvs_driver = { + .name = "lvs", + .probe = lvs_rh_probe, + .disconnect = lvs_rh_disconnect, +}; + +module_usb_driver(lvs_driver); + +MODULE_DESCRIPTION("Link Layer Validation System Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 06b5d77cd9ad..633caf643122 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3250,6 +3250,7 @@ static const struct usb_device_id sisusb_table[] = { { USB_DEVICE(0x0711, 0x0918) }, { USB_DEVICE(0x0711, 0x0920) }, { USB_DEVICE(0x0711, 0x0950) }, + { USB_DEVICE(0x0711, 0x5200) }, { USB_DEVICE(0x182d, 0x021c) }, { USB_DEVICE(0x182d, 0x0269) }, { } diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index f43c61989cef..ae7e1206ca54 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -98,7 +98,7 @@ static int usb3503_connect(struct usb3503 *hub) return err; } - /* PDS : Disable For Self Powered Operation */ + /* PDS : Set the ports which are disabled in self-powered mode. */ if (hub->port_off_mask) { err = regmap_update_bits(hub->regmap, USB3503_PDS, hub->port_off_mask, @@ -109,7 +109,7 @@ static int usb3503_connect(struct usb3503 *hub) } } - /* CFG1 : SELF_BUS_PWR -> Self-Powerd operation */ + /* CFG1 : Set SELF_BUS_PWR, this enables self-powered operation. */ err = regmap_update_bits(hub->regmap, USB3503_CFG1, USB3503_SELF_BUS_PWR, USB3503_SELF_BUS_PWR); @@ -149,8 +149,6 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) case USB3503_MODE_STANDBY: usb3503_reset(hub, 0); - - hub->mode = mode; dev_info(dev, "switched to STANDBY mode\n"); break; @@ -192,7 +190,8 @@ static int usb3503_probe(struct usb3503 *hub) clk = devm_clk_get(dev, "refclk"); if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT) { - dev_err(dev, "unable to request refclk (%d)\n", err); + dev_err(dev, "unable to request refclk (%ld)\n", + PTR_ERR(clk)); return PTR_ERR(clk); } @@ -272,7 +271,7 @@ static int usb3503_probe(struct usb3503 *hub) "usb3503 intn"); if (err) { dev_err(dev, - "unable to request GPIO %d as connect pin (%d)\n", + "unable to request GPIO %d as interrupt pin (%d)\n", hub->gpio_intn, err); return err; } @@ -346,6 +345,37 @@ static int usb3503_platform_probe(struct platform_device *pdev) return usb3503_probe(hub); } +#ifdef CONFIG_PM_SLEEP +static int usb3503_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb3503 *hub = i2c_get_clientdata(client); + + usb3503_switch_mode(hub, USB3503_MODE_STANDBY); + + if (hub->clk) + clk_disable_unprepare(hub->clk); + + return 0; +} + +static int usb3503_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb3503 *hub = i2c_get_clientdata(client); + + if (hub->clk) + clk_prepare_enable(hub->clk); + + usb3503_switch_mode(hub, hub->mode); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(usb3503_i2c_pm_ops, usb3503_i2c_suspend, + usb3503_i2c_resume); + static const struct i2c_device_id usb3503_id[] = { { USB3503_I2C_NAME, 0 }, { } @@ -364,6 +394,7 @@ MODULE_DEVICE_TABLE(of, usb3503_of_match); static struct i2c_driver usb3503_i2c_driver = { .driver = { .name = USB3503_I2C_NAME, + .pm = &usb3503_i2c_pm_ops, .of_match_table = of_match_ptr(usb3503_of_match), }, .probe = usb3503_i2c_probe, diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 829f446064ea..0bbafe795a72 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -54,6 +54,7 @@ struct usbtest_info { unsigned autoconf:1; unsigned ctrl_out:1; unsigned iso:1; /* try iso in/out */ + unsigned intr:1; /* try interrupt in/out */ int alt; }; @@ -70,7 +71,10 @@ struct usbtest_dev { int out_pipe; int in_iso_pipe; int out_iso_pipe; + int in_int_pipe; + int out_int_pipe; struct usb_endpoint_descriptor *iso_in, *iso_out; + struct usb_endpoint_descriptor *int_in, *int_out; struct mutex lock; #define TBUF_SIZE 256 @@ -101,6 +105,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) struct usb_host_interface *alt; struct usb_host_endpoint *in, *out; struct usb_host_endpoint *iso_in, *iso_out; + struct usb_host_endpoint *int_in, *int_out; struct usb_device *udev; for (tmp = 0; tmp < intf->num_altsetting; tmp++) { @@ -108,6 +113,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) in = out = NULL; iso_in = iso_out = NULL; + int_in = int_out = NULL; alt = intf->altsetting + tmp; if (override_alt >= 0 && @@ -124,6 +130,9 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) switch (usb_endpoint_type(&e->desc)) { case USB_ENDPOINT_XFER_BULK: break; + case USB_ENDPOINT_XFER_INT: + if (dev->info->intr) + goto try_intr; case USB_ENDPOINT_XFER_ISOC: if (dev->info->iso) goto try_iso; @@ -139,6 +148,15 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) out = e; } continue; +try_intr: + if (usb_endpoint_dir_in(&e->desc)) { + if (!int_in) + int_in = e; + } else { + if (!int_out) + int_out = e; + } + continue; try_iso: if (usb_endpoint_dir_in(&e->desc)) { if (!iso_in) @@ -148,7 +166,7 @@ try_iso: iso_out = e; } } - if ((in && out) || iso_in || iso_out) + if ((in && out) || iso_in || iso_out || int_in || int_out) goto found; } return -EINVAL; @@ -183,6 +201,20 @@ found: iso_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); } + + if (int_in) { + dev->int_in = &int_in->desc; + dev->in_int_pipe = usb_rcvintpipe(udev, + int_in->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + } + + if (int_out) { + dev->int_out = &int_out->desc; + dev->out_int_pipe = usb_sndintpipe(udev, + int_out->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + } return 0; } @@ -205,14 +237,22 @@ static struct urb *usbtest_alloc_urb( int pipe, unsigned long bytes, unsigned transfer_flags, - unsigned offset) + unsigned offset, + u8 bInterval) { struct urb *urb; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return urb; - usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback, NULL); + + if (bInterval) + usb_fill_int_urb(urb, udev, pipe, NULL, bytes, simple_callback, + NULL, bInterval); + else + usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback, + NULL); + urb->interval = (udev->speed == USB_SPEED_HIGH) ? (INTERRUPT_RATE << 3) : INTERRUPT_RATE; @@ -251,9 +291,11 @@ static struct urb *usbtest_alloc_urb( static struct urb *simple_alloc_urb( struct usb_device *udev, int pipe, - unsigned long bytes) + unsigned long bytes, + u8 bInterval) { - return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0); + return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0, + bInterval); } static unsigned pattern; @@ -1255,7 +1297,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param) goto cleanup; } req.wLength = cpu_to_le16(len); - urb[i] = u = simple_alloc_urb(udev, pipe, len); + urb[i] = u = simple_alloc_urb(udev, pipe, len, 0); if (!u) goto cleanup; @@ -1328,7 +1370,7 @@ static int unlink1(struct usbtest_dev *dev, int pipe, int size, int async) int retval = 0; init_completion(&completion); - urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size); + urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size, 0); if (!urb) return -ENOMEM; urb->context = &completion; @@ -1616,9 +1658,9 @@ static int halt_simple(struct usbtest_dev *dev) struct usb_device *udev = testdev_to_usbdev(dev); if (udev->speed == USB_SPEED_SUPER) - urb = simple_alloc_urb(udev, 0, 1024); + urb = simple_alloc_urb(udev, 0, 1024, 0); else - urb = simple_alloc_urb(udev, 0, 512); + urb = simple_alloc_urb(udev, 0, 512, 0); if (urb == NULL) return -ENOMEM; @@ -1962,7 +2004,7 @@ static int test_unaligned_bulk( { int retval; struct urb *urb = usbtest_alloc_urb( - testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1); + testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1, 0); if (!urb) return -ENOMEM; @@ -1989,7 +2031,7 @@ static int test_unaligned_bulk( * * WARNING: Because usbfs grabs udev->dev.sem before calling this ioctl(), * it locks out usbcore in certain code paths. Notably, if you disconnect - * the device-under-test, khubd will wait block forever waiting for the + * the device-under-test, hub_wq will wait block forever waiting for the * ioctl to complete ... so that usb_disconnect() can abort the pending * urbs and then call usbtest_disconnect(). To abort a test, you're best * off just killing the userspace task and waiting for it to exit. @@ -2068,7 +2110,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev_info(&intf->dev, "TEST 1: write %d bytes %u times\n", param->length, param->iterations); - urb = simple_alloc_urb(udev, dev->out_pipe, param->length); + urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0); if (!urb) { retval = -ENOMEM; break; @@ -2083,7 +2125,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev_info(&intf->dev, "TEST 2: read %d bytes %u times\n", param->length, param->iterations); - urb = simple_alloc_urb(udev, dev->in_pipe, param->length); + urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0); if (!urb) { retval = -ENOMEM; break; @@ -2098,7 +2140,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev_info(&intf->dev, "TEST 3: write/%d 0..%d bytes %u times\n", param->vary, param->length, param->iterations); - urb = simple_alloc_urb(udev, dev->out_pipe, param->length); + urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0); if (!urb) { retval = -ENOMEM; break; @@ -2114,7 +2156,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev_info(&intf->dev, "TEST 4: read/%d 0..%d bytes %u times\n", param->vary, param->length, param->iterations); - urb = simple_alloc_urb(udev, dev->in_pipe, param->length); + urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0); if (!urb) { retval = -ENOMEM; break; @@ -2411,6 +2453,39 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) } break; + /* Simple non-queued interrupt I/O tests */ + case 25: + if (dev->out_int_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 25: write %d bytes %u times\n", + param->length, param->iterations); + urb = simple_alloc_urb(udev, dev->out_int_pipe, param->length, + dev->int_out->bInterval); + if (!urb) { + retval = -ENOMEM; + break; + } + /* FIRMWARE: interrupt sink (maybe accepts short writes) */ + retval = simple_io(dev, urb, param->iterations, 0, 0, "test25"); + simple_free_urb(urb); + break; + case 26: + if (dev->in_int_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 26: read %d bytes %u times\n", + param->length, param->iterations); + urb = simple_alloc_urb(udev, dev->in_int_pipe, param->length, + dev->int_in->bInterval); + if (!urb) { + retval = -ENOMEM; + break; + } + /* FIRMWARE: interrupt source (maybe generates short writes) */ + retval = simple_io(dev, urb, param->iterations, 0, 0, "test26"); + simple_free_urb(urb); + break; } do_gettimeofday(¶m->duration); param->duration.tv_sec -= start.tv_sec; @@ -2447,6 +2522,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usbtest_info *info; char *rtest, *wtest; char *irtest, *iwtest; + char *intrtest, *intwtest; udev = interface_to_usbdev(intf); @@ -2487,6 +2563,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) */ rtest = wtest = ""; irtest = iwtest = ""; + intrtest = intwtest = ""; if (force_interrupt || udev->speed == USB_SPEED_LOW) { if (info->ep_in) { dev->in_pipe = usb_rcvintpipe(udev, info->ep_in); @@ -2525,15 +2602,20 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) irtest = " iso-in"; if (dev->out_iso_pipe) iwtest = " iso-out"; + if (dev->in_int_pipe) + intrtest = " int-in"; + if (dev->out_int_pipe) + intwtest = " int-out"; } usb_set_intfdata(intf, dev); dev_info(&intf->dev, "%s\n", info->name); - dev_info(&intf->dev, "%s {control%s%s%s%s%s} tests%s\n", + dev_info(&intf->dev, "%s {control%s%s%s%s%s%s%s} tests%s\n", usb_speed_string(udev->speed), info->ctrl_out ? " in/out" : "", rtest, wtest, irtest, iwtest, + intrtest, intwtest, info->alt >= 0 ? " (+alt)" : ""); return 0; } @@ -2607,6 +2689,7 @@ static struct usbtest_info gz_info = { .autoconf = 1, .ctrl_out = 1, .iso = 1, + .intr = 1, .alt = 0, }; diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 1472805083de..c3a45da11610 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -358,7 +358,7 @@ static int yurex_fasync(int fd, struct file *file, int on) { struct usb_yurex *dev; - dev = (struct usb_yurex *)file->private_data; + dev = file->private_data; return fasync_helper(fd, file, on, &dev->async_queue); } @@ -401,7 +401,7 @@ static int yurex_release(struct inode *inode, struct file *file) { struct usb_yurex *dev; - dev = (struct usb_yurex *)file->private_data; + dev = file->private_data; if (dev == NULL) return -ENODEV; @@ -418,7 +418,7 @@ static ssize_t yurex_read(struct file *file, char *buffer, size_t count, loff_t char in_buffer[20]; unsigned long flags; - dev = (struct usb_yurex *)file->private_data; + dev = file->private_data; mutex_lock(&dev->io_mutex); if (!dev->interface) { /* already disconnected */ @@ -455,7 +455,7 @@ static ssize_t yurex_write(struct file *file, const char *user_buffer, size_t co DEFINE_WAIT(wait); count = min(sizeof(buffer), count); - dev = (struct usb_yurex *)file->private_data; + dev = file->private_data; /* verify that we actually have some data to write */ if (count == 0) diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 0a34dd859555..a2735df24cc6 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -1,3 +1,4 @@ + /* * Texas Instruments AM35x "glue layer" * diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index d40d5f0b5528..ac4422b33dcd 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -455,7 +455,7 @@ static int bfin_probe(struct platform_device *pdev) int ret = -ENOMEM; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; @@ -464,7 +464,7 @@ static int bfin_probe(struct platform_device *pdev) musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err1; + goto err0; } musb->dev.parent = &pdev->dev; @@ -478,7 +478,7 @@ static int bfin_probe(struct platform_device *pdev) glue->phy = usb_phy_generic_register(); if (IS_ERR(glue->phy)) - goto err2; + goto err1; platform_set_drvdata(pdev, glue); memset(musb_resources, 0x00, sizeof(*musb_resources) * @@ -498,31 +498,28 @@ static int bfin_probe(struct platform_device *pdev) ARRAY_SIZE(musb_resources)); if (ret) { dev_err(&pdev->dev, "failed to add resources\n"); - goto err3; + goto err2; } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err3; + goto err2; } ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err3; + goto err2; } return 0; -err3: - usb_phy_generic_unregister(glue->phy); - err2: - platform_device_put(musb); + usb_phy_generic_unregister(glue->phy); err1: - kfree(glue); + platform_device_put(musb); err0: return ret; @@ -534,7 +531,6 @@ static int bfin_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(glue->phy); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index de8492b06e46..110b78415bf0 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -519,23 +519,23 @@ static int davinci_probe(struct platform_device *pdev) int ret = -ENOMEM; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; } - clk = clk_get(&pdev->dev, "usb"); + clk = devm_clk_get(&pdev->dev, "usb"); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); ret = PTR_ERR(clk); - goto err3; + goto err0; } ret = clk_enable(clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock\n"); - goto err4; + goto err0; } glue->dev = &pdev->dev; @@ -579,20 +579,14 @@ static int davinci_probe(struct platform_device *pdev) if (IS_ERR(musb)) { ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err5; + goto err1; } return 0; -err5: +err1: clk_disable(clk); -err4: - clk_put(clk); - -err3: - kfree(glue); - err0: return ret; } @@ -604,8 +598,6 @@ static int davinci_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(); clk_disable(glue->clk); - clk_put(glue->clk); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 5f30537f1927..d1187290d4e3 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/usb/usb_phy_generic.h> #include "musb_core.h" @@ -80,6 +81,7 @@ static struct musb_hdrc_platform_data jz4740_musb_platform_data = { static int jz4740_musb_init(struct musb *musb) { + usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS UDC: no transceiver configured\n"); @@ -182,6 +184,7 @@ static int jz4740_remove(struct platform_device *pdev) struct jz4740_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); + usb_phy_generic_unregister(pdev); clk_disable_unprepare(glue->clk); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index eff3c5cf84f4..b841ee0bff06 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -850,7 +850,8 @@ b_host: /* handle babble condition */ if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) - schedule_work(&musb->recover_work); + schedule_delayed_work(&musb->recover_work, + msecs_to_jiffies(100)); #if 0 /* REVISIT ... this would be for multiplexing periodic endpoints, or @@ -1517,7 +1518,7 @@ irqreturn_t musb_interrupt(struct musb *musb) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n", - (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral", + is_host_active(musb) ? "host" : "peripheral", musb->int_usb, musb->int_tx, musb->int_rx); /* the core can interrupt us for multiple reasons; docs have @@ -1531,7 +1532,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* handle endpoint 0 first */ if (musb->int_tx & 1) { - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) retval |= musb_h_ep0_irq(musb); else retval |= musb_g_ep0_irq(musb); @@ -1545,7 +1546,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* musb_ep_select(musb->mregs, ep_num); */ /* REVISIT just retval = ep->rx_irq(...) */ retval = IRQ_HANDLED; - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_rx(musb, ep_num); else musb_g_rx(musb, ep_num); @@ -1563,7 +1564,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* musb_ep_select(musb->mregs, ep_num); */ /* REVISIT just retval |= ep->tx_irq(...) */ retval = IRQ_HANDLED; - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_tx(musb, ep_num); else musb_g_tx(musb, ep_num); @@ -1585,15 +1586,13 @@ MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) { - u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - /* called with controller lock already held */ if (!epnum) { #ifndef CONFIG_USB_TUSB_OMAP_DMA if (!is_cppi_enabled()) { /* endpoint 0 */ - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_h_ep0_irq(musb); else musb_g_ep0_irq(musb); @@ -1602,13 +1601,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) } else { /* endpoints 1..15 */ if (transmit) { - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_tx(musb, epnum); else musb_g_tx(musb, epnum); } else { /* receive */ - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_rx(musb, epnum); else musb_g_rx(musb, epnum); @@ -1753,20 +1752,22 @@ static void musb_irq_work(struct work_struct *data) /* Recover from babble interrupt conditions */ static void musb_recover_work(struct work_struct *data) { - struct musb *musb = container_of(data, struct musb, recover_work); - int status; + struct musb *musb = container_of(data, struct musb, recover_work.work); + int status, ret; - musb_platform_reset(musb); + ret = musb_platform_reset(musb); + if (ret) + return; usb_phy_vbus_off(musb->xceiv); - udelay(100); + usleep_range(100, 200); usb_phy_vbus_on(musb->xceiv); - udelay(100); + usleep_range(100, 200); /* - * When a babble condition occurs, the musb controller removes the - * session bit and the endpoint config is lost. + * When a babble condition occurs, the musb controller + * removes the session bit and the endpoint config is lost. */ if (musb->dyn_fifo) status = ep_config_from_table(musb); @@ -1945,7 +1946,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); - INIT_WORK(&musb->recover_work, musb_recover_work); + INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work); INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); @@ -2041,7 +2042,7 @@ fail4: fail3: cancel_work_sync(&musb->irq_work); - cancel_work_sync(&musb->recover_work); + cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) @@ -2107,7 +2108,7 @@ static int musb_remove(struct platform_device *pdev) dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); - cancel_work_sync(&musb->recover_work); + cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); musb_free(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index d155a156f240..414e57a984bb 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -192,7 +192,7 @@ struct musb_platform_ops { int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); - void (*reset)(struct musb *musb); + int (*reset)(struct musb *musb); int (*vbus_status)(struct musb *musb); void (*set_vbus)(struct musb *musb, int on); @@ -297,7 +297,7 @@ struct musb { irqreturn_t (*isr)(int, void *); struct work_struct irq_work; - struct work_struct recover_work; + struct delayed_work recover_work; struct delayed_work deassert_reset_work; struct delayed_work finish_resume_work; u16 hwvers; @@ -555,10 +555,12 @@ static inline void musb_platform_try_idle(struct musb *musb, musb->ops->try_idle(musb, timeout); } -static inline void musb_platform_reset(struct musb *musb) +static inline int musb_platform_reset(struct musb *musb) { - if (musb->ops->reset) - musb->ops->reset(musb); + if (!musb->ops->reset) + return -EINVAL; + + return musb->ops->reset(musb); } static inline int musb_platform_get_vbus_status(struct musb *musb) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 5341bb223b7c..acdfb3e68a90 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -39,7 +39,7 @@ struct cppi41_dma_channel { u32 transferred; u32 packet_sz; struct list_head tx_check; - struct work_struct dma_completion; + int tx_zlp; }; #define MUSB_DMA_NUM_CHANNELS 15 @@ -74,15 +74,18 @@ static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel) static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel) { + struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + struct musb *musb = hw_ep->musb; u16 csr; u8 toggle; if (cppi41_channel->is_tx) return; - if (!is_host_active(cppi41_channel->controller->musb)) + if (!is_host_active(musb)) return; - csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR); + musb_ep_select(musb->mregs, hw_ep->epnum); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0; /* @@ -107,30 +110,21 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep) void __iomem *epio = musb->endpoints[epnum].regs; u16 csr; + musb_ep_select(musb->mregs, hw_ep->epnum); csr = musb_readw(epio, MUSB_TXCSR); if (csr & MUSB_TXCSR_TXPKTRDY) return false; 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) { struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; struct musb *musb = hw_ep->musb; + void __iomem *epio = hw_ep->regs; + u16 csr; if (!cppi41_channel->prog_len || (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)) { @@ -139,15 +133,25 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) cppi41_channel->channel.actual_len = cppi41_channel->transferred; cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE; + cppi41_channel->channel.rx_packet_done = true; + + /* + * transmit ZLP using PIO mode for transfers which size is + * multiple of EP packet size. + */ + if (cppi41_channel->tx_zlp && (cppi41_channel->transferred % + cppi41_channel->packet_sz) == 0) { + musb_ep_select(musb->mregs, hw_ep->epnum); + csr = MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY; + musb_writew(epio, MUSB_TXCSR, csr); + } musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx); } else { /* next iteration, reload */ struct dma_chan *dc = cppi41_channel->dc; struct dma_async_tx_descriptor *dma_desc; enum dma_transfer_direction direction; - u16 csr; u32 remain_bytes; - void __iomem *epio = cppi41_channel->hw_ep->regs; cppi41_channel->buf_addr += cppi41_channel->packet_sz; @@ -172,6 +176,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) dma_async_issue_pending(dc); if (!cppi41_channel->is_tx) { + musb_ep_select(musb->mregs, hw_ep->epnum); csr = musb_readw(epio, MUSB_RXCSR); csr |= MUSB_RXCSR_H_REQPKT; musb_writew(epio, MUSB_RXCSR, csr); @@ -179,32 +184,6 @@ 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; @@ -233,7 +212,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) if (!list_empty(&controller->early_tx_list)) { ret = HRTIMER_RESTART; hrtimer_forward_now(&controller->early_tx, - ktime_set(0, 150 * NSEC_PER_USEC)); + ktime_set(0, 20 * NSEC_PER_USEC)); } spin_unlock_irqrestore(&musb->lock, flags); @@ -268,14 +247,6 @@ 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); @@ -312,16 +283,14 @@ 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_is_queued(&controller->early_tx)) { + unsigned long usecs = cppi41_channel->total_len / 10; + hrtimer_start_range_ns(&controller->early_tx, - ktime_set(0, 140 * NSEC_PER_USEC), - 40 * NSEC_PER_USEC, + ktime_set(0, usecs * NSEC_PER_USEC), + 20 * NSEC_PER_USEC, HRTIMER_MODE_REL); } } @@ -406,6 +375,7 @@ static bool cppi41_configure_channel(struct dma_channel *channel, cppi41_channel->total_len = len; cppi41_channel->transferred = 0; cppi41_channel->packet_sz = packet_sz; + cppi41_channel->tx_zlp = (cppi41_channel->is_tx && mode) ? 1 : 0; /* * Due to AM335x' Advisory 1.0.13 we are not allowed to transfer more @@ -450,6 +420,7 @@ static bool cppi41_configure_channel(struct dma_channel *channel, dma_desc->callback = cppi41_dma_callback; dma_desc->callback_param = channel; cppi41_channel->cookie = dma_desc->tx_submit(dma_desc); + cppi41_channel->channel.rx_packet_done = false; save_rx_toggle(cppi41_channel); dma_async_issue_pending(dc); @@ -672,8 +643,6 @@ 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_dma.h b/drivers/usb/musb/musb_dma.h index 1345a4ff041a..1d44faa86252 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -129,6 +129,7 @@ struct dma_channel { size_t actual_len; enum dma_channel_status status; bool desired_mode; + bool rx_packet_done; }; /* diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 09529f94e72d..154bcf1b5dfa 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -56,16 +56,24 @@ static const struct of_device_id musb_dsps_of_match[]; * dependent on musb core layer symbols. */ static inline u8 dsps_readb(const void __iomem *addr, unsigned offset) - { return __raw_readb(addr + offset); } +{ + return __raw_readb(addr + offset); +} static inline u32 dsps_readl(const void __iomem *addr, unsigned offset) - { return __raw_readl(addr + offset); } +{ + return __raw_readl(addr + offset); +} static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data) - { __raw_writeb(data, addr + offset); } +{ + __raw_writeb(data, addr + offset); +} static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data) - { __raw_writel(data, addr + offset); } +{ + __raw_writel(data, addr + offset); +} /** * DSPS musb wrapper register offset. @@ -136,6 +144,7 @@ struct dsps_glue { const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ struct timer_list timer; /* otg_workaround timer */ unsigned long last_timer; /* last timer data for each instance */ + bool sw_babble_enabled; struct dsps_context context; struct debugfs_regset32 regset; @@ -469,6 +478,19 @@ static int dsps_musb_init(struct musb *musb) val &= ~(1 << wrp->otg_disable); dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); + /* + * Check whether the dsps version has babble control enabled. + * In latest silicon revision the babble control logic is enabled. + * If MUSB_BABBLE_CTL returns 0x4 then we have the babble control + * logic enabled. + */ + val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + if (val == MUSB_BABBLE_RCV_DISABLE) { + glue->sw_babble_enabled = true; + val |= MUSB_BABBLE_SW_SESSION_CTRL; + dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val); + } + ret = dsps_musb_dbg_init(musb, glue); if (ret) return ret; @@ -535,14 +557,82 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode) return 0; } -static void dsps_musb_reset(struct musb *musb) +static bool sw_babble_control(struct musb *musb) +{ + u8 babble_ctl; + bool session_restart = false; + + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + dev_dbg(musb->controller, "babble: MUSB_BABBLE_CTL value %x\n", + babble_ctl); + /* + * check line monitor flag to check whether babble is + * due to noise + */ + dev_dbg(musb->controller, "STUCK_J is %s\n", + babble_ctl & MUSB_BABBLE_STUCK_J ? "set" : "reset"); + + if (babble_ctl & MUSB_BABBLE_STUCK_J) { + int timeout = 10; + + /* + * babble is due to noise, then set transmit idle (d7 bit) + * to resume normal operation + */ + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + babble_ctl |= MUSB_BABBLE_FORCE_TXIDLE; + dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl); + + /* wait till line monitor flag cleared */ + dev_dbg(musb->controller, "Set TXIDLE, wait J to clear\n"); + do { + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + udelay(1); + } while ((babble_ctl & MUSB_BABBLE_STUCK_J) && timeout--); + + /* check whether stuck_at_j bit cleared */ + if (babble_ctl & MUSB_BABBLE_STUCK_J) { + /* + * real babble condition has occurred + * restart the controller to start the + * session again + */ + dev_dbg(musb->controller, "J not cleared, misc (%x)\n", + babble_ctl); + session_restart = true; + } + } else { + session_restart = true; + } + + return session_restart; +} + +static int dsps_musb_reset(struct musb *musb) { struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; + int session_restart = 0; + + if (glue->sw_babble_enabled) + session_restart = sw_babble_control(musb); + /* + * In case of new silicon version babble condition can be recovered + * without resetting the MUSB. But for older silicon versions, MUSB + * reset is needed + */ + if (session_restart || !glue->sw_babble_enabled) { + dev_info(musb->controller, "Restarting MUSB to recover from Babble\n"); + dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); + usleep_range(100, 200); + usb_phy_shutdown(musb->xceiv); + usleep_range(100, 200); + usb_phy_init(musb->xceiv); + session_restart = 1; + } - dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); - udelay(100); + return !session_restart; } static struct musb_platform_ops dsps_ops = { @@ -780,6 +870,7 @@ static int dsps_suspend(struct device *dev) struct musb *musb = platform_get_drvdata(glue->musb); void __iomem *mbase = musb->ctrl_base; + del_timer_sync(&glue->timer); glue->context.control = dsps_readl(mbase, wrp->control); glue->context.epintr = dsps_readl(mbase, wrp->epintr_set); glue->context.coreintr = dsps_readl(mbase, wrp->coreintr_set); @@ -805,6 +896,7 @@ static int dsps_resume(struct device *dev) dsps_writel(mbase, wrp->mode, glue->context.mode); dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode); dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode); + setup_timer(&glue->timer, otg_timer, (unsigned long) musb); return 0; } diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index d4aa779339f1..24c8c0219790 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -176,7 +176,7 @@ __acquires(ep->musb->lock) ep->end_point.name, request, req->request.actual, req->request.length, request->status); - req->request.complete(&req->ep->end_point, &req->request); + usb_gadget_giveback_request(&req->ep->end_point, &req->request); spin_lock(&musb->lock); ep->busy = busy; } diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index eb06291a40c8..855793d701bb 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -120,7 +120,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) if (csr != lastcsr) dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr); lastcsr = csr; - csr |= MUSB_TXCSR_FLUSHFIFO; + csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_TXPKTRDY; musb_writew(epio, MUSB_TXCSR, csr); csr = musb_readw(epio, MUSB_TXCSR); if (WARN(retries-- < 1, @@ -1295,7 +1295,7 @@ done: if (status) { if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); } /* do the proper sequence to abort the transfer in the @@ -1640,7 +1640,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) /* clean up dma and collect transfer count */ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); xfer_len = dma->actual_len; } musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); @@ -1671,7 +1671,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); xfer_len = dma->actual_len; done = true; } @@ -1734,10 +1734,11 @@ void musb_host_rx(struct musb *musb, u8 epnum) } } else { - /* done if urb buffer is full or short packet is recd */ - done = (urb->actual_length + xfer_len >= - urb->transfer_buffer_length - || dma->actual_len < qh->maxpacket); + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + xfer_len >= + urb->transfer_buffer_length + || dma->actual_len < qh->maxpacket + || dma->rx_packet_done); } /* send IN token for next packet, without AUTOREQ */ @@ -1957,7 +1958,7 @@ static int musb_schedule( struct musb_qh *qh, int is_in) { - int idle; + int idle = 0; int best_diff; int best_end, epnum; struct musb_hw_ep *hw_ep = NULL; diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 03f2655af290..37122a480bc1 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -72,6 +72,12 @@ #define MUSB_DEVCTL_HR 0x02 #define MUSB_DEVCTL_SESSION 0x01 +/* BABBLE_CTL */ +#define MUSB_BABBLE_FORCE_TXIDLE 0x80 +#define MUSB_BABBLE_SW_SESSION_CTRL 0x40 +#define MUSB_BABBLE_STUCK_J 0x20 +#define MUSB_BABBLE_RCV_DISABLE 0x04 + /* MUSB ULPI VBUSCONTROL */ #define MUSB_ULPI_USE_EXTVBUS 0x01 #define MUSB_ULPI_USE_EXTVBUSIND 0x02 @@ -246,6 +252,7 @@ */ #define MUSB_DEVCTL 0x60 /* 8 bit */ +#define MUSB_BABBLE_CTL 0x61 /* 8 bit */ /* These are always controlled through the INDEX register */ #define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */ @@ -570,7 +577,7 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) { /* * This register is invisible on Blackfin, actually the MUSB - * RTL version of Blackfin is 1.9, so just harcode its value. + * RTL version of Blackfin is 1.9, so just hardcode its value. */ return MUSB_HWVERS_1900; } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 159ef4be1ef2..2daa779f1382 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -22,6 +22,7 @@ #include <linux/usb.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/device.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/usb/usb_phy_generic.h> @@ -432,7 +433,7 @@ static void musb_do_idle(unsigned long _musb) if (!musb->is_active) { u32 wakeups; - /* wait until khubd handles port change status */ + /* wait until hub_wq handles port change status */ if (is_host_active(musb) && (musb->port1_status >> 16)) goto done; @@ -1160,12 +1161,12 @@ static int tusb_probe(struct platform_device *pdev) struct platform_device *musb; struct tusb6010_glue *glue; struct platform_device_info pinfo; - int ret = -ENOMEM; + int ret; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); - goto err0; + return -ENOMEM; } glue->dev = &pdev->dev; @@ -1204,16 +1205,10 @@ static int tusb_probe(struct platform_device *pdev) if (IS_ERR(musb)) { ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err3; + return ret; } return 0; - -err3: - kfree(glue); - -err0: - return ret; } static int tusb_remove(struct platform_device *pdev) @@ -1222,7 +1217,6 @@ static int tusb_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(glue->phy); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index f202e5088461..dc666e96f45f 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -246,7 +246,7 @@ static int ux500_probe(struct platform_device *pdev) } } - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; @@ -255,20 +255,20 @@ static int ux500_probe(struct platform_device *pdev) musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err1; + goto err0; } - clk = clk_get(&pdev->dev, NULL); + clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); ret = PTR_ERR(clk); - goto err3; + goto err1; } ret = clk_prepare_enable(clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock\n"); - goto err4; + goto err1; } musb->dev.parent = &pdev->dev; @@ -301,34 +301,28 @@ static int ux500_probe(struct platform_device *pdev) ARRAY_SIZE(musb_resources)); if (ret) { dev_err(&pdev->dev, "failed to add resources\n"); - goto err5; + goto err2; } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err5; + goto err2; } ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err5; + goto err2; } return 0; -err5: +err2: clk_disable_unprepare(clk); -err4: - clk_put(clk); - -err3: - platform_device_put(musb); - err1: - kfree(glue); + platform_device_put(musb); err0: return ret; @@ -340,8 +334,6 @@ static int ux500_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); clk_disable_unprepare(glue->clk); - clk_put(glue->clk); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 9aad00f11bd5..221faed9f074 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -96,7 +96,7 @@ static bool ux500_configure_channel(struct dma_channel *channel, struct musb *musb = ux500_channel->controller->private_data; dev_dbg(musb->controller, - "packet_sz=%d, mode=%d, dma_addr=0x%llu, len=%d is_tx=%d\n", + "packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n", packet_sz, mode, (unsigned long long) dma_addr, len, ux500_channel->is_tx); diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index e253fa05be68..0cd1f44f0ee8 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -78,22 +78,6 @@ config SAMSUNG_USBPHY This driver provides common interface to interact, for Samsung USB 2.0 PHY driver and later for Samsung USB 3.0 PHY driver. -config SAMSUNG_USB2PHY - tristate "Samsung USB 2.0 PHY controller Driver" - select SAMSUNG_USBPHY - select USB_PHY - help - Enable this to support Samsung USB 2.0 (High Speed) PHY controller - driver for Samsung SoCs. - -config SAMSUNG_USB3PHY - tristate "Samsung USB 3.0 PHY controller Driver" - select SAMSUNG_USBPHY - select USB_PHY - help - Enable this to support Samsung USB 3.0 (Super Speed) phy controller - for samsung SoCs. - config TWL6030_USB tristate "TWL6030 USB Transceiver Driver" depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 24a91332d4ad..75f2bba58c84 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -15,8 +15,6 @@ 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_SAMSUNG_USBPHY) += phy-samsung-usb.o -obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o -obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 585e50cb1980..b70e05537180 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -122,16 +122,10 @@ static int am335x_phy_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops am335x_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume) -}; - -#define DEV_PM_OPS (&am335x_pm_ops) -#else -#define DEV_PM_OPS NULL #endif +static SIMPLE_DEV_PM_OPS(am335x_pm_ops, am335x_phy_suspend, am335x_phy_resume); + static const struct of_device_id am335x_phy_ids[] = { { .compatible = "ti,am335x-usb-phy" }, { } @@ -144,7 +138,7 @@ static struct platform_driver am335x_phy_driver = { .driver = { .name = "am335x-phy-driver", .owner = THIS_MODULE, - .pm = DEV_PM_OPS, + .pm = &am335x_pm_ops, .of_match_table = am335x_phy_ids, }, }; diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index 2b0f968d9325..f1ea5990a50a 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -609,7 +609,7 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host) otg->host->otg_port = fsl_otg_initdata.otg_port; otg->host->is_b_host = otg_dev->fsm.id; /* - * must leave time for khubd to finish its thing + * must leave time for hub_wq to finish its thing * before yanking the host driver out from under it, * so suspend the host after a short delay. */ diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 69462e09d014..f4b14bd97e14 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -253,15 +253,15 @@ static int gpio_vbus_probe(struct platform_device *pdev) return -EINVAL; gpio = pdata->gpio_vbus; - gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); + gpio_vbus = devm_kzalloc(&pdev->dev, sizeof(struct gpio_vbus_data), + GFP_KERNEL); if (!gpio_vbus) return -ENOMEM; - gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); - if (!gpio_vbus->phy.otg) { - kfree(gpio_vbus); + gpio_vbus->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), + GFP_KERNEL); + if (!gpio_vbus->phy.otg) return -ENOMEM; - } platform_set_drvdata(pdev, gpio_vbus); gpio_vbus->dev = &pdev->dev; @@ -274,11 +274,11 @@ static int gpio_vbus_probe(struct platform_device *pdev) gpio_vbus->phy.otg->phy = &gpio_vbus->phy; gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral; - err = gpio_request(gpio, "vbus_detect"); + err = devm_gpio_request(&pdev->dev, gpio, "vbus_detect"); if (err) { dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", gpio, err); - goto err_gpio; + return err; } gpio_direction_input(gpio); @@ -296,27 +296,27 @@ static int gpio_vbus_probe(struct platform_device *pdev) /* if data line pullup is in use, initialize it to "not pulling up" */ gpio = pdata->gpio_pullup; if (gpio_is_valid(gpio)) { - err = gpio_request(gpio, "udc_pullup"); + err = devm_gpio_request(&pdev->dev, gpio, "udc_pullup"); if (err) { dev_err(&pdev->dev, "can't request pullup gpio %d, err: %d\n", gpio, err); - gpio_free(pdata->gpio_vbus); - goto err_gpio; + return err; } gpio_direction_output(gpio, pdata->gpio_pullup_inverted); } - err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev); + err = devm_request_irq(&pdev->dev, irq, gpio_vbus_irq, irqflags, + "vbus_detect", pdev); if (err) { dev_err(&pdev->dev, "can't request irq %i, err: %d\n", irq, err); - goto err_irq; + return err; } INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); - gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + gpio_vbus->vbus_draw = devm_regulator_get(&pdev->dev, "vbus_draw"); if (IS_ERR(gpio_vbus->vbus_draw)) { dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", PTR_ERR(gpio_vbus->vbus_draw)); @@ -328,44 +328,23 @@ static int gpio_vbus_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); - goto err_otg; + return err; } device_init_wakeup(&pdev->dev, pdata->wakeup); return 0; -err_otg: - regulator_put(gpio_vbus->vbus_draw); - free_irq(irq, pdev); -err_irq: - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(pdata->gpio_vbus); -err_gpio: - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - return err; } static int gpio_vbus_remove(struct platform_device *pdev) { struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); - struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev); - int gpio = pdata->gpio_vbus; device_init_wakeup(&pdev->dev, 0); cancel_delayed_work_sync(&gpio_vbus->work); - regulator_put(gpio_vbus->vbus_draw); usb_remove_phy(&gpio_vbus->phy); - free_irq(gpio_vbus->irq, pdev); - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(gpio); - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - return 0; } diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index 69e49be8866b..8eea56d3ded6 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -1011,7 +1011,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat) break; case OTG_STATE_A_WAIT_VFALL: state = OTG_STATE_A_IDLE; - /* khubd may take a while to notice and + /* hub_wq may take a while to notice and * handle this disconnect, so don't go * to B_IDLE quite yet. */ diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c929370cdaa6..7843ef7dd0ff 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -279,11 +279,11 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) static int msm_otg_phy_clk_reset(struct msm_otg *motg) { - int ret; + int ret = 0; if (motg->pdata->phy_clk_reset) ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk); - else + else if (motg->phy_rst) ret = reset_control_reset(motg->phy_rst); if (ret) @@ -1394,7 +1394,7 @@ out: return status; } -const struct file_operations msm_otg_mode_fops = { +static const struct file_operations msm_otg_mode_fops = { .open = msm_otg_mode_open, .read = seq_read, .write = msm_otg_mode_write, @@ -1429,7 +1429,7 @@ static void msm_otg_debugfs_cleanup(void) debugfs_remove(msm_otg_dbg_root); } -static struct of_device_id msm_otg_dt_match[] = { +static const struct of_device_id msm_otg_dt_match[] = { { .compatible = "qcom,usb-otg-ci", .data = (void *) CI_45NM_INTEGRATED_PHY @@ -1466,7 +1466,7 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy"); if (IS_ERR(motg->phy_rst)) - return PTR_ERR(motg->phy_rst); + motg->phy_rst = NULL; pdata->mode = of_usb_get_dr_mode(node); if (pdata->mode == USB_DR_MODE_UNKNOWN) @@ -1554,11 +1554,14 @@ static int msm_otg_probe(struct platform_device *pdev) phy = &motg->phy; phy->dev = &pdev->dev; - motg->phy_reset_clk = devm_clk_get(&pdev->dev, + if (motg->pdata->phy_clk_reset) { + motg->phy_reset_clk = devm_clk_get(&pdev->dev, np ? "phy" : "usb_phy_clk"); - if (IS_ERR(motg->phy_reset_clk)) { - dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); - return PTR_ERR(motg->phy_reset_clk); + + if (IS_ERR(motg->phy_reset_clk)) { + dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); + return PTR_ERR(motg->phy_reset_clk); + } } motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk"); @@ -1601,8 +1604,8 @@ static int msm_otg_probe(struct platform_device *pdev) */ if (motg->phy_number) { phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4); - if (IS_ERR(phy_select)) - return PTR_ERR(phy_select); + if (!phy_select) + return -ENOMEM; /* Enable second PHY with the OTG port */ writel(0x1, phy_select); } @@ -1838,7 +1841,6 @@ static struct platform_driver msm_otg_driver = { .remove = msm_otg_remove, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .pm = &msm_otg_dev_pm_ops, .of_match_table = msm_otg_dt_match, }, diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index c42bdf0c4a1f..0e0c41587a08 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2012-2014 Freescale Semiconductor, Inc. * Copyright (C) 2012 Marek Vasut <marex@denx.de> * on behalf of DENX Software Engineering GmbH * @@ -125,10 +125,22 @@ static const struct mxs_phy_data imx6sl_phy_data = { MXS_PHY_NEED_IP_FIX, }; +static const struct mxs_phy_data vf610_phy_data = { + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_NEED_IP_FIX, +}; + +static const struct mxs_phy_data imx6sx_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,imx6sx-usbphy", .data = &imx6sx_phy_data, }, { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, }, { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, }, { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, }, + { .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c deleted file mode 100644 index ac025ca08425..000000000000 --- a/drivers/usb/phy/phy-samsung-usb.c +++ /dev/null @@ -1,241 +0,0 @@ -/* linux/drivers/usb/phy/phy-samsung-usb.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Praveen Paneri <p.paneri@samsung.com> - * - * Samsung USB-PHY helper driver with common function calls; - * interacts with Samsung USB 2.0 PHY controller driver and later - * with Samsung USB 3.0 PHY driver. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/usb/samsung_usb_phy.h> - -#include "phy-samsung-usb.h" - -int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) -{ - struct device_node *usbphy_sys; - - /* Getting node for system controller interface for usb-phy */ - usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys"); - if (!usbphy_sys) { - dev_err(sphy->dev, "No sys-controller interface for usb-phy\n"); - return -ENODEV; - } - - sphy->pmuregs = of_iomap(usbphy_sys, 0); - - if (sphy->pmuregs == NULL) { - dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); - goto err0; - } - - sphy->sysreg = of_iomap(usbphy_sys, 1); - - /* - * Not returning error code here, since this situation is not fatal. - * Few SoCs may not have this switch available - */ - if (sphy->sysreg == NULL) - dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); - - of_node_put(usbphy_sys); - - return 0; - -err0: - of_node_put(usbphy_sys); - return -ENXIO; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt); - -/* - * Set isolation here for phy. - * Here 'on = true' would mean USB PHY block is isolated, hence - * de-activated and vice-versa. - */ -void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, bool on) -{ - void __iomem *reg = NULL; - u32 reg_val; - u32 en_mask = 0; - - if (!sphy->pmuregs) { - dev_warn(sphy->dev, "Can't set pmu isolation\n"); - return; - } - - if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { - reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; - en_mask = sphy->drv_data->devphy_en_mask; - } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { - reg = sphy->pmuregs + sphy->drv_data->hostphy_reg_offset; - en_mask = sphy->drv_data->hostphy_en_mask; - } - - reg_val = readl(reg); - - if (on) - reg_val &= ~en_mask; - else - reg_val |= en_mask; - - writel(reg_val, reg); - - if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) { - writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL0); - writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL1); - } -} -EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation_4210); - -/* - * Configure the mode of working of usb-phy here: HOST/DEVICE. - */ -void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) -{ - u32 reg; - - if (!sphy->sysreg) { - dev_warn(sphy->dev, "Can't configure specified phy mode\n"); - return; - } - - reg = readl(sphy->sysreg); - - if (sphy->phy_type == USB_PHY_TYPE_DEVICE) - reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK; - else if (sphy->phy_type == USB_PHY_TYPE_HOST) - reg |= EXYNOS_USB20PHY_CFG_HOST_LINK; - - writel(reg, sphy->sysreg); -} -EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel); - -/* - * PHYs are different for USB Device and USB Host. - * This make sure that correct PHY type is selected before - * any operation on PHY. - */ -int samsung_usbphy_set_type(struct usb_phy *phy, - enum samsung_usb_phy_type phy_type) -{ - struct samsung_usbphy *sphy = phy_to_sphy(phy); - - sphy->phy_type = phy_type; - - return 0; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_set_type); - -int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy, - unsigned long rate) -{ - unsigned int clksel; - - switch (rate) { - case 12 * MHZ: - clksel = PHYCLK_CLKSEL_12M; - break; - case 24 * MHZ: - clksel = PHYCLK_CLKSEL_24M; - break; - case 48 * MHZ: - clksel = PHYCLK_CLKSEL_48M; - break; - default: - dev_err(sphy->dev, - "Invalid reference clock frequency: %lu\n", rate); - return -EINVAL; - } - - return clksel; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_64xx); - -int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy, - unsigned long rate) -{ - unsigned int clksel; - - switch (rate) { - case 9600 * KHZ: - clksel = FSEL_CLKSEL_9600K; - break; - case 10 * MHZ: - clksel = FSEL_CLKSEL_10M; - break; - case 12 * MHZ: - clksel = FSEL_CLKSEL_12M; - break; - case 19200 * KHZ: - clksel = FSEL_CLKSEL_19200K; - break; - case 20 * MHZ: - clksel = FSEL_CLKSEL_20M; - break; - case 24 * MHZ: - clksel = FSEL_CLKSEL_24M; - break; - case 50 * MHZ: - clksel = FSEL_CLKSEL_50M; - break; - default: - dev_err(sphy->dev, - "Invalid reference clock frequency: %lu\n", rate); - return -EINVAL; - } - - return clksel; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_4x12); - -/* - * Returns reference clock frequency selection value - */ -int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) -{ - struct clk *ref_clk; - unsigned long rate; - int refclk_freq; - - /* - * In exynos5250 USB host and device PHY use - * external crystal clock XXTI - */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - ref_clk = clk_get(sphy->dev, "ext_xtal"); - else - ref_clk = clk_get(sphy->dev, "xusbxti"); - if (IS_ERR(ref_clk)) { - dev_err(sphy->dev, "Failed to get reference clock\n"); - return PTR_ERR(ref_clk); - } - - rate = clk_get_rate(ref_clk); - refclk_freq = sphy->drv_data->rate_to_clksel(sphy, rate); - - clk_put(ref_clk); - - return refclk_freq; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq); diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h deleted file mode 100644 index 68771bfd1825..000000000000 --- a/drivers/usb/phy/phy-samsung-usb.h +++ /dev/null @@ -1,349 +0,0 @@ -/* linux/drivers/usb/phy/phy-samsung-usb.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Samsung USB-PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and - * OHCI-EXYNOS controllers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/usb/phy.h> - -/* Register definitions */ - -#define SAMSUNG_PHYPWR (0x00) - -#define PHYPWR_NORMAL_MASK (0x19 << 0) -#define PHYPWR_OTG_DISABLE (0x1 << 4) -#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) -#define PHYPWR_FORCE_SUSPEND (0x1 << 1) -/* For Exynos4 */ -#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) -#define PHYPWR_SLEEP_PHY0 (0x1 << 5) - -#define SAMSUNG_PHYCLK (0x04) - -#define PHYCLK_MODE_USB11 (0x1 << 6) -#define PHYCLK_EXT_OSC (0x1 << 5) -#define PHYCLK_COMMON_ON_N (0x1 << 4) -#define PHYCLK_ID_PULL (0x1 << 2) -#define PHYCLK_CLKSEL_MASK (0x3 << 0) -#define PHYCLK_CLKSEL_48M (0x0 << 0) -#define PHYCLK_CLKSEL_12M (0x2 << 0) -#define PHYCLK_CLKSEL_24M (0x3 << 0) - -#define SAMSUNG_RSTCON (0x08) - -#define RSTCON_PHYLINK_SWRST (0x1 << 2) -#define RSTCON_HLINK_SWRST (0x1 << 1) -#define RSTCON_SWRST (0x1 << 0) - -/* EXYNOS4X12 */ -#define EXYNOS4X12_PHY_HSIC_CTRL0 (0x04) -#define EXYNOS4X12_PHY_HSIC_CTRL1 (0x08) - -#define PHYPWR_NORMAL_MASK_HSIC1 (0x7 << 12) -#define PHYPWR_NORMAL_MASK_HSIC0 (0x7 << 9) -#define PHYPWR_NORMAL_MASK_PHY1 (0x7 << 6) - -#define RSTCON_HOSTPHY_SWRST (0xf << 3) - -/* EXYNOS5 */ -#define EXYNOS5_PHY_HOST_CTRL0 (0x00) - -#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) - -#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) -#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) -#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) -#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) - -#define HOST_CTRL0_FSEL_MASK (0x7 << 16) -#define HOST_CTRL0_FSEL(_x) ((_x) << 16) - -#define FSEL_CLKSEL_50M (0x7) -#define FSEL_CLKSEL_24M (0x5) -#define FSEL_CLKSEL_20M (0x4) -#define FSEL_CLKSEL_19200K (0x3) -#define FSEL_CLKSEL_12M (0x2) -#define FSEL_CLKSEL_10M (0x1) -#define FSEL_CLKSEL_9600K (0x0) - -#define HOST_CTRL0_TESTBURNIN (0x1 << 11) -#define HOST_CTRL0_RETENABLE (0x1 << 10) -#define HOST_CTRL0_COMMONON_N (0x1 << 9) -#define HOST_CTRL0_SIDDQ (0x1 << 6) -#define HOST_CTRL0_FORCESLEEP (0x1 << 5) -#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) -#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) -#define HOST_CTRL0_UTMISWRST (0x1 << 2) -#define HOST_CTRL0_LINKSWRST (0x1 << 1) -#define HOST_CTRL0_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_TUNE0 (0x04) - -#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) - -#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) - -#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) - -#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) - -#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) -#define HSIC_CTRL_REFCLKSEL (0x2 << 23) - -#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) -#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) -#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) -#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) -#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) -#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) -#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) - -#define HSIC_CTRL_SIDDQ (0x1 << 6) -#define HSIC_CTRL_FORCESLEEP (0x1 << 5) -#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) -#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) -#define HSIC_CTRL_UTMISWRST (0x1 << 2) -#define HSIC_CTRL_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) - -#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) -#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) -#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) -#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) - -#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) - -#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) -#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) -#define HOST_OHCICTRL_CNTSEL (0x1 << 1) -#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) - -#define EXYNOS5_PHY_OTG_SYS (0x38) - -#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) -#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) -#define OTG_SYS_PHY0_SWRST (0x1 << 12) - -#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) -#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) -#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) -#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) - -#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) -#define OTG_SYS_COMMON_ON (0x1 << 7) - -#define OTG_SYS_FSEL_MASK (0x7 << 4) -#define OTG_SYS_FSEL(_x) ((_x) << 4) - -#define OTG_SYS_FORCESLEEP (0x1 << 3) -#define OTG_SYS_OTGDISABLE (0x1 << 2) -#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) -#define OTG_SYS_FORCESUSPEND (0x1 << 0) - -#define EXYNOS5_PHY_OTG_TUNE (0x40) - -/* EXYNOS5: USB 3.0 DRD */ -#define EXYNOS5_DRD_LINKSYSTEM (0x04) - -#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) -#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) -#define LINKSYSTEM_XHCI_VERSION_CONTROL (0x1 << 27) - -#define EXYNOS5_DRD_PHYUTMI (0x08) - -#define PHYUTMI_OTGDISABLE (0x1 << 6) -#define PHYUTMI_FORCESUSPEND (0x1 << 1) -#define PHYUTMI_FORCESLEEP (0x1 << 0) - -#define EXYNOS5_DRD_PHYPIPE (0x0c) - -#define EXYNOS5_DRD_PHYCLKRST (0x10) - -#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) -#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) - -#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) -#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) - -#define PHYCLKRST_SSC_EN (0x1 << 20) -#define PHYCLKRST_REF_SSP_EN (0x1 << 19) -#define PHYCLKRST_REF_CLKDIV2 (0x1 << 18) - -#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x02 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) - -#define PHYCLKRST_FSEL_MASK (0x3f << 5) -#define PHYCLKRST_FSEL(_x) ((_x) << 5) -#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) -#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) -#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) -#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) - -#define PHYCLKRST_RETENABLEN (0x1 << 4) - -#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) -#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) -#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) - -#define PHYCLKRST_PORTRESET (0x1 << 1) -#define PHYCLKRST_COMMONONN (0x1 << 0) - -#define EXYNOS5_DRD_PHYREG0 (0x14) -#define EXYNOS5_DRD_PHYREG1 (0x18) - -#define EXYNOS5_DRD_PHYPARAM0 (0x1c) - -#define PHYPARAM0_REF_USE_PAD (0x1 << 31) -#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) -#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) - -#define EXYNOS5_DRD_PHYPARAM1 (0x20) - -#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) -#define PHYPARAM1_PCS_TXDEEMPH (0x1c) - -#define EXYNOS5_DRD_PHYTERM (0x24) - -#define EXYNOS5_DRD_PHYTEST (0x28) - -#define PHYTEST_POWERDOWN_SSP (0x1 << 3) -#define PHYTEST_POWERDOWN_HSP (0x1 << 2) - -#define EXYNOS5_DRD_PHYADP (0x2c) - -#define EXYNOS5_DRD_PHYBATCHG (0x30) - -#define PHYBATCHG_UTMI_CLKSEL (0x1 << 2) - -#define EXYNOS5_DRD_PHYRESUME (0x34) -#define EXYNOS5_DRD_LINKPORT (0x44) - -#ifndef MHZ -#define MHZ (1000*1000) -#endif - -#ifndef KHZ -#define KHZ (1000) -#endif - -#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) -#define S3C64XX_USBPHY_ENABLE (0x1 << 16) -#define EXYNOS_USBPHY_ENABLE (0x1 << 0) -#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) - -enum samsung_cpu_type { - TYPE_S3C64XX, - TYPE_EXYNOS4210, - TYPE_EXYNOS4X12, - TYPE_EXYNOS5250, -}; - -struct samsung_usbphy; - -/* - * struct samsung_usbphy_drvdata - driver data for various SoC variants - * @cpu_type: machine identifier - * @devphy_en_mask: device phy enable mask for PHY CONTROL register - * @hostphy_en_mask: host phy enable mask for PHY CONTROL register - * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from - * mapped address of system controller. - * @hostphy_reg_offset: offset to HOST PHY CONTROL register from - * mapped address of system controller. - * - * Here we have a separate mask for device type phy. - * Having different masks for host and device type phy helps - * in setting independent masks in case of SoCs like S5PV210, - * in which PHY0 and PHY1 enable bits belong to same register - * placed at position 0 and 1 respectively. - * Although for newer SoCs like exynos these bits belong to - * different registers altogether placed at position 0. - */ -struct samsung_usbphy_drvdata { - int cpu_type; - int devphy_en_mask; - int hostphy_en_mask; - u32 devphy_reg_offset; - u32 hostphy_reg_offset; - int (*rate_to_clksel)(struct samsung_usbphy *, unsigned long); - void (*set_isolation)(struct samsung_usbphy *, bool); - void (*phy_enable)(struct samsung_usbphy *); - void (*phy_disable)(struct samsung_usbphy *); -}; - -/* - * struct samsung_usbphy - transceiver driver state - * @phy: transceiver structure - * @plat: platform data - * @dev: The parent device supplied to the probe function - * @clk: usb phy clock - * @regs: usb phy controller registers memory base - * @pmuregs: USB device PHY_CONTROL register memory base - * @sysreg: USB2.0 PHY_CFG register memory base - * @ref_clk_freq: reference clock frequency selection - * @drv_data: driver data available for different SoCs - * @phy_type: Samsung SoCs specific phy types: #HOST - * #DEVICE - * @phy_usage: usage count for phy - * @lock: lock for phy operations - */ -struct samsung_usbphy { - struct usb_phy phy; - struct samsung_usbphy_data *plat; - struct device *dev; - struct clk *clk; - void __iomem *regs; - void __iomem *pmuregs; - void __iomem *sysreg; - int ref_clk_freq; - const struct samsung_usbphy_drvdata *drv_data; - enum samsung_usb_phy_type phy_type; - atomic_t phy_usage; - spinlock_t lock; -}; - -#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) - -static const struct of_device_id samsung_usbphy_dt_match[]; - -static inline const struct samsung_usbphy_drvdata -*samsung_usbphy_get_driver_data(struct platform_device *pdev) -{ - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(samsung_usbphy_dt_match, - pdev->dev.of_node); - return match->data; - } - - return (struct samsung_usbphy_drvdata *) - platform_get_device_id(pdev)->driver_data; -} - -extern int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy); -extern void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, - bool on); -extern void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy); -extern int samsung_usbphy_set_type(struct usb_phy *phy, - enum samsung_usb_phy_type phy_type); -extern int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy); -extern int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy, - unsigned long rate); -extern int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy, - unsigned long rate); diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c deleted file mode 100644 index b3ba86627b72..000000000000 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ /dev/null @@ -1,541 +0,0 @@ -/* linux/drivers/usb/phy/phy-samsung-usb2.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Praveen Paneri <p.paneri@samsung.com> - * - * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and - * OHCI-EXYNOS controllers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/usb/otg.h> -#include <linux/usb/samsung_usb_phy.h> -#include <linux/platform_data/samsung-usbphy.h> - -#include "phy-samsung-usb.h" - -static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!otg->host) - otg->host = host; - - return 0; -} - -static bool exynos5_phyhost_is_on(void __iomem *regs) -{ - u32 reg; - - reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - return !(reg & HOST_CTRL0_SIDDQ); -} - -static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyclk = sphy->ref_clk_freq; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - u32 ehcictrl; - u32 ohcictrl; - - /* - * phy_usage helps in keeping usage count for phy - * so that the first consumer enabling the phy is also - * the last consumer to disable it. - */ - - atomic_inc(&sphy->phy_usage); - - if (exynos5_phyhost_is_on(regs)) { - dev_info(sphy->dev, "Already power on PHY\n"); - return; - } - - /* Host configuration */ - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - /* phy reference clock configuration */ - phyhost &= ~HOST_CTRL0_FSEL_MASK; - phyhost |= HOST_CTRL0_FSEL(phyclk); - - /* host phy reset */ - phyhost &= ~(HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL | - HOST_CTRL0_SIDDQ | - /* Enable normal mode of operation */ - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP); - - /* Link reset */ - phyhost |= (HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST | - /* COMMON Block configuration during suspend */ - HOST_CTRL0_COMMONON_N); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - udelay(10); - phyhost &= ~(HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - /* OTG configuration */ - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - - /* phy reference clock configuration */ - phyotg &= ~OTG_SYS_FSEL_MASK; - phyotg |= OTG_SYS_FSEL(phyclk); - - /* Enable normal mode of operation */ - phyotg &= ~(OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP | - OTG_SYS_REFCLKSEL_MASK | - /* COMMON Block configuration during suspend */ - OTG_SYS_COMMON_ON); - - /* OTG phy & link reset */ - phyotg |= (OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET | - OTG_SYS_OTGDISABLE | - /* Set phy refclk */ - OTG_SYS_REFCLKSEL_CLKCORE); - - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - udelay(10); - phyotg &= ~(OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - - /* HSIC phy configuration */ - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_PHYSWRST); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - udelay(10); - phyhsic &= ~HSIC_CTRL_PHYSWRST; - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - udelay(80); - - /* enable EHCI DMA burst */ - ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); - ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | - HOST_EHCICTRL_ENAINCR4 | - HOST_EHCICTRL_ENAINCR8 | - HOST_EHCICTRL_ENAINCR16); - writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); - - /* set ohci_suspend_on_n */ - ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); - ohcictrl |= HOST_OHCICTRL_SUSPLGCY; - writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); -} - -static void samsung_usb2phy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - u32 phyclk; - u32 rstcon; - - /* set clock frequency for PLL */ - phyclk = sphy->ref_clk_freq; - phypwr = readl(regs + SAMSUNG_PHYPWR); - rstcon = readl(regs + SAMSUNG_RSTCON); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phyclk &= ~PHYCLK_COMMON_ON_N; - phypwr &= ~PHYPWR_NORMAL_MASK; - rstcon |= RSTCON_SWRST; - break; - case TYPE_EXYNOS4X12: - phypwr &= ~(PHYPWR_NORMAL_MASK_HSIC0 | - PHYPWR_NORMAL_MASK_HSIC1 | - PHYPWR_NORMAL_MASK_PHY1); - rstcon |= RSTCON_HOSTPHY_SWRST; - case TYPE_EXYNOS4210: - phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; - rstcon |= RSTCON_SWRST; - default: - break; - } - - writel(phyclk, regs + SAMSUNG_PHYCLK); - /* Configure PHY0 for normal operation*/ - writel(phypwr, regs + SAMSUNG_PHYPWR); - /* reset all ports of PHY and Link */ - writel(rstcon, regs + SAMSUNG_RSTCON); - udelay(10); - if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) - rstcon &= ~RSTCON_HOSTPHY_SWRST; - rstcon &= ~RSTCON_SWRST; - writel(rstcon, regs + SAMSUNG_RSTCON); -} - -static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - - if (atomic_dec_return(&sphy->phy_usage) > 0) { - dev_info(sphy->dev, "still being used\n"); - return; - } - - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_SIDDQ | - HSIC_CTRL_FORCESLEEP | - HSIC_CTRL_FORCESUSPEND); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - phyhost |= (HOST_CTRL0_SIDDQ | - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP | - HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - phyotg |= (OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); -} - -static void samsung_usb2phy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - - phypwr = readl(regs + SAMSUNG_PHYPWR); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phypwr |= PHYPWR_NORMAL_MASK; - break; - case TYPE_EXYNOS4X12: - phypwr |= (PHYPWR_NORMAL_MASK_HSIC0 | - PHYPWR_NORMAL_MASK_HSIC1 | - PHYPWR_NORMAL_MASK_PHY1); - case TYPE_EXYNOS4210: - phypwr |= PHYPWR_NORMAL_MASK_PHY0; - default: - break; - } - - /* Disable analog and otg block power */ - writel(phypwr, regs + SAMSUNG_PHYPWR); -} - -/* - * The function passed to the usb driver for phy initialization - */ -static int samsung_usb2phy_init(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - int ret = 0; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - /* Enable the phy clock */ - ret = clk_prepare_enable(sphy->clk); - if (ret) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return ret; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* Disable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(false); - else if (sphy->drv_data->set_isolation) - sphy->drv_data->set_isolation(sphy, false); - - /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ - samsung_usbphy_cfg_sel(sphy); - - /* Initialize usb phy registers */ - sphy->drv_data->phy_enable(sphy); - - spin_unlock_irqrestore(&sphy->lock, flags); - - /* Disable the phy clock */ - clk_disable_unprepare(sphy->clk); - - return ret; -} - -/* - * The function passed to the usb driver for phy shutdown - */ -static void samsung_usb2phy_shutdown(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - if (clk_prepare_enable(sphy->clk)) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* De-initialize usb phy registers */ - sphy->drv_data->phy_disable(sphy); - - /* Enable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(true); - else if (sphy->drv_data->set_isolation) - sphy->drv_data->set_isolation(sphy, true); - - spin_unlock_irqrestore(&sphy->lock, flags); - - clk_disable_unprepare(sphy->clk); -} - -static int samsung_usb2phy_probe(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy; - struct usb_otg *otg; - struct samsung_usbphy_data *pdata = dev_get_platdata(&pdev->dev); - const struct samsung_usbphy_drvdata *drv_data; - struct device *dev = &pdev->dev; - struct resource *phy_mem; - void __iomem *phy_base; - struct clk *clk; - int ret; - - phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy_base = devm_ioremap_resource(dev, phy_mem); - if (IS_ERR(phy_base)) - return PTR_ERR(phy_base); - - sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); - if (!sphy) - return -ENOMEM; - - otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - drv_data = samsung_usbphy_get_driver_data(pdev); - - if (drv_data->cpu_type == TYPE_EXYNOS5250) - clk = devm_clk_get(dev, "usbhost"); - else - clk = devm_clk_get(dev, "otg"); - - if (IS_ERR(clk)) { - dev_err(dev, "Failed to get usbhost/otg clock\n"); - return PTR_ERR(clk); - } - - sphy->dev = dev; - - if (dev->of_node) { - ret = samsung_usbphy_parse_dt(sphy); - if (ret < 0) - return ret; - } else { - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - } - - sphy->plat = pdata; - sphy->regs = phy_base; - sphy->clk = clk; - sphy->drv_data = drv_data; - sphy->phy.dev = sphy->dev; - sphy->phy.label = "samsung-usb2phy"; - sphy->phy.type = USB_PHY_TYPE_USB2; - sphy->phy.init = samsung_usb2phy_init; - sphy->phy.shutdown = samsung_usb2phy_shutdown; - - sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); - if (sphy->ref_clk_freq < 0) - return -EINVAL; - - sphy->phy.otg = otg; - sphy->phy.otg->phy = &sphy->phy; - sphy->phy.otg->set_host = samsung_usbphy_set_host; - - spin_lock_init(&sphy->lock); - - platform_set_drvdata(pdev, sphy); - - return usb_add_phy_dev(&sphy->phy); -} - -static int samsung_usb2phy_remove(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy = platform_get_drvdata(pdev); - - usb_remove_phy(&sphy->phy); - - if (sphy->pmuregs) - iounmap(sphy->pmuregs); - if (sphy->sysreg) - iounmap(sphy->sysreg); - - return 0; -} - -static const struct samsung_usbphy_drvdata usb2phy_s3c64xx = { - .cpu_type = TYPE_S3C64XX, - .devphy_en_mask = S3C64XX_USBPHY_ENABLE, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_64xx, - .set_isolation = NULL, /* TODO */ - .phy_enable = samsung_usb2phy_enable, - .phy_disable = samsung_usb2phy_disable, -}; - -static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { - .cpu_type = TYPE_EXYNOS4210, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_64xx, - .set_isolation = samsung_usbphy_set_isolation_4210, - .phy_enable = samsung_usb2phy_enable, - .phy_disable = samsung_usb2phy_disable, -}; - -static const struct samsung_usbphy_drvdata usb2phy_exynos4x12 = { - .cpu_type = TYPE_EXYNOS4X12, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12, - .set_isolation = samsung_usbphy_set_isolation_4210, - .phy_enable = samsung_usb2phy_enable, - .phy_disable = samsung_usb2phy_disable, -}; - -static struct samsung_usbphy_drvdata usb2phy_exynos5 = { - .cpu_type = TYPE_EXYNOS5250, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12, - .set_isolation = samsung_usbphy_set_isolation_4210, - .phy_enable = samsung_exynos5_usb2phy_enable, - .phy_disable = samsung_exynos5_usb2phy_disable, -}; - -#ifdef CONFIG_OF -static const struct of_device_id samsung_usbphy_dt_match[] = { - { - .compatible = "samsung,s3c64xx-usb2phy", - .data = &usb2phy_s3c64xx, - }, { - .compatible = "samsung,exynos4210-usb2phy", - .data = &usb2phy_exynos4, - }, { - .compatible = "samsung,exynos4x12-usb2phy", - .data = &usb2phy_exynos4x12, - }, { - .compatible = "samsung,exynos5250-usb2phy", - .data = &usb2phy_exynos5 - }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); -#endif - -static struct platform_device_id samsung_usbphy_driver_ids[] = { - { - .name = "s3c64xx-usb2phy", - .driver_data = (unsigned long)&usb2phy_s3c64xx, - }, { - .name = "exynos4210-usb2phy", - .driver_data = (unsigned long)&usb2phy_exynos4, - }, { - .name = "exynos4x12-usb2phy", - .driver_data = (unsigned long)&usb2phy_exynos4x12, - }, { - .name = "exynos5250-usb2phy", - .driver_data = (unsigned long)&usb2phy_exynos5, - }, - {}, -}; - -MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); - -static struct platform_driver samsung_usb2phy_driver = { - .probe = samsung_usb2phy_probe, - .remove = samsung_usb2phy_remove, - .id_table = samsung_usbphy_driver_ids, - .driver = { - .name = "samsung-usb2phy", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(samsung_usbphy_dt_match), - }, -}; - -module_platform_driver(samsung_usb2phy_driver); - -MODULE_DESCRIPTION("Samsung USB 2.0 phy controller"); -MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-usb2phy"); diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c deleted file mode 100644 index cc0819248acf..000000000000 --- a/drivers/usb/phy/phy-samsung-usb3.c +++ /dev/null @@ -1,350 +0,0 @@ -/* linux/drivers/usb/phy/phy-samsung-usb3.c - * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Vivek Gautam <gautam.vivek@samsung.com> - * - * Samsung USB 3.0 PHY transceiver; talks to DWC3 controller. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/usb/samsung_usb_phy.h> -#include <linux/platform_data/samsung-usbphy.h> - -#include "phy-samsung-usb.h" - -/* - * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core. - */ -static u32 samsung_usb3phy_set_refclk(struct samsung_usbphy *sphy) -{ - u32 reg; - u32 refclk; - - refclk = sphy->ref_clk_freq; - - reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | - PHYCLKRST_FSEL(refclk); - - switch (refclk) { - case FSEL_CLKSEL_50M: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | - PHYCLKRST_SSC_REFCLKSEL(0x00)); - break; - case FSEL_CLKSEL_20M: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x00)); - break; - case FSEL_CLKSEL_19200K: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x88)); - break; - case FSEL_CLKSEL_24M: - default: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x88)); - break; - } - - return reg; -} - -static void samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyparam0; - u32 phyparam1; - u32 linksystem; - u32 phybatchg; - u32 phytest; - u32 phyclkrst; - - /* Reset USB 3.0 PHY */ - writel(0x0, regs + EXYNOS5_DRD_PHYREG0); - - phyparam0 = readl(regs + EXYNOS5_DRD_PHYPARAM0); - /* Select PHY CLK source */ - phyparam0 &= ~PHYPARAM0_REF_USE_PAD; - /* Set Loss-of-Signal Detector sensitivity */ - phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK; - phyparam0 |= PHYPARAM0_REF_LOSLEVEL; - writel(phyparam0, regs + EXYNOS5_DRD_PHYPARAM0); - - writel(0x0, regs + EXYNOS5_DRD_PHYRESUME); - - /* - * Setting the Frame length Adj value[6:1] to default 0x20 - * See xHCI 1.0 spec, 5.2.4 - */ - linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL | - LINKSYSTEM_FLADJ(0x20); - writel(linksystem, regs + EXYNOS5_DRD_LINKSYSTEM); - - phyparam1 = readl(regs + EXYNOS5_DRD_PHYPARAM1); - /* Set Tx De-Emphasis level */ - phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; - phyparam1 |= PHYPARAM1_PCS_TXDEEMPH; - writel(phyparam1, regs + EXYNOS5_DRD_PHYPARAM1); - - phybatchg = readl(regs + EXYNOS5_DRD_PHYBATCHG); - phybatchg |= PHYBATCHG_UTMI_CLKSEL; - writel(phybatchg, regs + EXYNOS5_DRD_PHYBATCHG); - - /* PHYTEST POWERDOWN Control */ - phytest = readl(regs + EXYNOS5_DRD_PHYTEST); - phytest &= ~(PHYTEST_POWERDOWN_SSP | - PHYTEST_POWERDOWN_HSP); - writel(phytest, regs + EXYNOS5_DRD_PHYTEST); - - /* UTMI Power Control */ - writel(PHYUTMI_OTGDISABLE, regs + EXYNOS5_DRD_PHYUTMI); - - phyclkrst = samsung_usb3phy_set_refclk(sphy); - - phyclkrst |= PHYCLKRST_PORTRESET | - /* Digital power supply in normal operating mode */ - PHYCLKRST_RETENABLEN | - /* Enable ref clock for SS function */ - PHYCLKRST_REF_SSP_EN | - /* Enable spread spectrum */ - PHYCLKRST_SSC_EN | - /* Power down HS Bias and PLL blocks in suspend mode */ - PHYCLKRST_COMMONONN; - - writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); - - udelay(10); - - phyclkrst &= ~(PHYCLKRST_PORTRESET); - writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); -} - -static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy) -{ - u32 phyutmi; - u32 phyclkrst; - u32 phytest; - void __iomem *regs = sphy->regs; - - phyutmi = PHYUTMI_OTGDISABLE | - PHYUTMI_FORCESUSPEND | - PHYUTMI_FORCESLEEP; - writel(phyutmi, regs + EXYNOS5_DRD_PHYUTMI); - - /* Resetting the PHYCLKRST enable bits to reduce leakage current */ - phyclkrst = readl(regs + EXYNOS5_DRD_PHYCLKRST); - phyclkrst &= ~(PHYCLKRST_REF_SSP_EN | - PHYCLKRST_SSC_EN | - PHYCLKRST_COMMONONN); - writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); - - /* Control PHYTEST to remove leakage current */ - phytest = readl(regs + EXYNOS5_DRD_PHYTEST); - phytest |= (PHYTEST_POWERDOWN_SSP | - PHYTEST_POWERDOWN_HSP); - writel(phytest, regs + EXYNOS5_DRD_PHYTEST); -} - -static int samsung_usb3phy_init(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - unsigned long flags; - int ret = 0; - - sphy = phy_to_sphy(phy); - - /* Enable the phy clock */ - ret = clk_prepare_enable(sphy->clk); - if (ret) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return ret; - } - - spin_lock_irqsave(&sphy->lock, flags); - - /* setting default phy-type for USB 3.0 */ - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - - /* Disable phy isolation */ - if (sphy->drv_data->set_isolation) - sphy->drv_data->set_isolation(sphy, false); - - /* Initialize usb phy registers */ - sphy->drv_data->phy_enable(sphy); - - spin_unlock_irqrestore(&sphy->lock, flags); - - /* Disable the phy clock */ - clk_disable_unprepare(sphy->clk); - - return ret; -} - -/* - * The function passed to the usb driver for phy shutdown - */ -static void samsung_usb3phy_shutdown(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - unsigned long flags; - - sphy = phy_to_sphy(phy); - - if (clk_prepare_enable(sphy->clk)) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return; - } - - spin_lock_irqsave(&sphy->lock, flags); - - /* setting default phy-type for USB 3.0 */ - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - - /* De-initialize usb phy registers */ - sphy->drv_data->phy_disable(sphy); - - /* Enable phy isolation */ - if (sphy->drv_data->set_isolation) - sphy->drv_data->set_isolation(sphy, true); - - spin_unlock_irqrestore(&sphy->lock, flags); - - clk_disable_unprepare(sphy->clk); -} - -static int samsung_usb3phy_probe(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy; - struct samsung_usbphy_data *pdata = dev_get_platdata(&pdev->dev); - struct device *dev = &pdev->dev; - struct resource *phy_mem; - void __iomem *phy_base; - struct clk *clk; - int ret; - - phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy_base = devm_ioremap_resource(dev, phy_mem); - if (IS_ERR(phy_base)) - return PTR_ERR(phy_base); - - sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); - if (!sphy) - return -ENOMEM; - - clk = devm_clk_get(dev, "usbdrd30"); - if (IS_ERR(clk)) { - dev_err(dev, "Failed to get device clock\n"); - return PTR_ERR(clk); - } - - sphy->dev = dev; - - if (dev->of_node) { - ret = samsung_usbphy_parse_dt(sphy); - if (ret < 0) - return ret; - } else { - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - } - - sphy->plat = pdata; - sphy->regs = phy_base; - sphy->clk = clk; - sphy->phy.dev = sphy->dev; - sphy->phy.label = "samsung-usb3phy"; - sphy->phy.type = USB_PHY_TYPE_USB3; - sphy->phy.init = samsung_usb3phy_init; - sphy->phy.shutdown = samsung_usb3phy_shutdown; - sphy->drv_data = samsung_usbphy_get_driver_data(pdev); - - sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); - if (sphy->ref_clk_freq < 0) - return -EINVAL; - - spin_lock_init(&sphy->lock); - - platform_set_drvdata(pdev, sphy); - - return usb_add_phy_dev(&sphy->phy); -} - -static int samsung_usb3phy_remove(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy = platform_get_drvdata(pdev); - - usb_remove_phy(&sphy->phy); - - if (sphy->pmuregs) - iounmap(sphy->pmuregs); - if (sphy->sysreg) - iounmap(sphy->sysreg); - - return 0; -} - -static struct samsung_usbphy_drvdata usb3phy_exynos5 = { - .cpu_type = TYPE_EXYNOS5250, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12, - .set_isolation = samsung_usbphy_set_isolation_4210, - .phy_enable = samsung_exynos5_usb3phy_enable, - .phy_disable = samsung_exynos5_usb3phy_disable, -}; - -#ifdef CONFIG_OF -static const struct of_device_id samsung_usbphy_dt_match[] = { - { - .compatible = "samsung,exynos5250-usb3phy", - .data = &usb3phy_exynos5 - }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); -#endif - -static struct platform_device_id samsung_usbphy_driver_ids[] = { - { - .name = "exynos5250-usb3phy", - .driver_data = (unsigned long)&usb3phy_exynos5, - }, - {}, -}; - -MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); - -static struct platform_driver samsung_usb3phy_driver = { - .probe = samsung_usb3phy_probe, - .remove = samsung_usb3phy_remove, - .id_table = samsung_usbphy_driver_ids, - .driver = { - .name = "samsung-usb3phy", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(samsung_usbphy_dt_match), - }, -}; - -module_platform_driver(samsung_usb3phy_driver); - -MODULE_DESCRIPTION("Samsung USB 3.0 phy controller"); -MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-usb3phy"); diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index bbe4f8e6e8d7..886f1807a67b 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -33,7 +33,6 @@ #include <linux/usb/otg.h> #include <linux/usb/ulpi.h> #include <linux/usb/of.h> -#include <asm/mach-types.h> #include <linux/usb/ehci_def.h> #include <linux/usb/tegra_usb_phy.h> #include <linux/regulator/consumer.h> @@ -686,10 +685,8 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) return gpio_direction_output(phy->reset_gpio, 0); } -static void tegra_usb_phy_close(struct usb_phy *x) +static void tegra_usb_phy_close(struct tegra_usb_phy *phy) { - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - if (!IS_ERR(phy->vbus)) regulator_disable(phy->vbus); @@ -881,8 +878,8 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, return -ENOMEM; } - tegra_phy->config = devm_kzalloc(&pdev->dev, - sizeof(*tegra_phy->config), GFP_KERNEL); + tegra_phy->config = devm_kzalloc(&pdev->dev, sizeof(*config), + GFP_KERNEL); if (!tegra_phy->config) { dev_err(&pdev->dev, "unable to allocate memory for USB UTMIP config\n"); @@ -965,7 +962,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = { .requires_extra_tuning_parameters = true, }; -static struct of_device_id tegra_usb_phy_id_table[] = { +static const struct of_device_id tegra_usb_phy_id_table[] = { { .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config }, { .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config }, { }, @@ -1061,14 +1058,13 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) if (err < 0) return err; - tegra_phy->u_phy.shutdown = tegra_usb_phy_close; tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend; platform_set_drvdata(pdev, tegra_phy); err = usb_add_phy_dev(&tegra_phy->u_phy); if (err < 0) { - tegra_usb_phy_close(&tegra_phy->u_phy); + tegra_usb_phy_close(tegra_phy); return err; } @@ -1080,6 +1076,7 @@ static int tegra_usb_phy_remove(struct platform_device *pdev) struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev); usb_remove_phy(&tegra_phy->u_phy); + tegra_usb_phy_close(tegra_phy); return 0; } diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 04778cf80d60..44ea082e40dc 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -104,7 +104,6 @@ struct twl6030_usb { int irq2; enum omap_musb_vbus_id_status linkstat; u8 asleep; - bool irq_enabled; bool vbus_enable; const char *regulator; }; @@ -373,7 +372,6 @@ static int twl6030_usb_probe(struct platform_device *pdev) INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); - twl->irq_enabled = true; status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl6030_usb", twl); diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 36b6bce33b20..045cd309367a 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -147,7 +147,7 @@ err0: } EXPORT_SYMBOL_GPL(usb_get_phy); - /** +/** * devm_usb_get_phy_by_phandle - find the USB PHY by phandle * @dev - device that requests this phy * @phandle - name of the property holding the phy phandle value @@ -232,6 +232,9 @@ struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) phy = __usb_find_phy_dev(dev, &phy_bind_list, index); if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { dev_dbg(dev, "unable to find transceiver\n"); + if (!IS_ERR(phy)) + phy = ERR_PTR(-ENODEV); + goto err0; } diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index 1c4195abc108..de83b9d0cd5c 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -5,6 +5,7 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' depends on USB_GADGET + depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST default n help Renesas USBHS is a discrete USB host and peripheral controller chip diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index bc8aef4311a1..9e47f477b6d2 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o -renesas_usbhs-y := common.o mod.o pipe.o fifo.o +renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) renesas_usbhs-y += mod_host.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 17267b0a2e95..b3b6813ab270 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -15,12 +15,16 @@ * */ #include <linux/err.h> +#include <linux/gpio.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/sysfs.h> #include "common.h" +#include "rcar2.h" /* * image of renesas_usbhs @@ -284,6 +288,8 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv) /* * platform default param */ + +/* commonly used on old SH-Mobile SoCs */ static u32 usbhsc_default_pipe_type[] = { USB_ENDPOINT_XFER_CONTROL, USB_ENDPOINT_XFER_ISOC, @@ -297,6 +303,26 @@ static u32 usbhsc_default_pipe_type[] = { USB_ENDPOINT_XFER_INT, }; +/* commonly used on newer SH-Mobile and R-Car SoCs */ +static u32 usbhsc_new_pipe_type[] = { + USB_ENDPOINT_XFER_CONTROL, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, +}; + /* * power control */ @@ -414,6 +440,43 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) /* * platform functions */ +static const struct of_device_id usbhs_of_match[] = { + { + .compatible = "renesas,usbhs-r8a7790", + .data = (void *)USBHS_TYPE_R8A7790, + }, + { + .compatible = "renesas,usbhs-r8a7791", + .data = (void *)USBHS_TYPE_R8A7791, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, usbhs_of_match); + +static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) +{ + struct renesas_usbhs_platform_info *info; + struct renesas_usbhs_driver_param *dparam; + const struct of_device_id *of_id = of_match_device(usbhs_of_match, dev); + u32 tmp; + int gpio; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; + + dparam = &info->driver_param; + dparam->type = of_id ? (u32)of_id->data : 0; + if (!of_property_read_u32(dev->of_node, "renesas,buswait", &tmp)) + dparam->buswait_bwait = tmp; + gpio = of_get_named_gpio_flags(dev->of_node, "renesas,enable-gpio", 0, + NULL); + if (gpio > 0) + dparam->enable_gpio = gpio; + + return info; +} + static int usbhs_probe(struct platform_device *pdev) { struct renesas_usbhs_platform_info *info = dev_get_platdata(&pdev->dev); @@ -422,9 +485,12 @@ static int usbhs_probe(struct platform_device *pdev) struct resource *res, *irq_res; int ret; + /* check device node */ + if (pdev->dev.of_node) + info = pdev->dev.platform_data = usbhs_parse_dt(&pdev->dev); + /* check platform information */ - if (!info || - !info->platform_callback.get_id) { + if (!info) { dev_err(&pdev->dev, "no platform information\n"); return -EINVAL; } @@ -451,13 +517,32 @@ static int usbhs_probe(struct platform_device *pdev) /* * care platform info */ - memcpy(&priv->pfunc, - &info->platform_callback, - sizeof(struct renesas_usbhs_platform_callback)); + memcpy(&priv->dparam, &info->driver_param, sizeof(struct renesas_usbhs_driver_param)); + switch (priv->dparam.type) { + case USBHS_TYPE_R8A7790: + case USBHS_TYPE_R8A7791: + priv->pfunc = usbhs_rcar2_ops; + if (!priv->dparam.pipe_type) { + priv->dparam.pipe_type = usbhsc_new_pipe_type; + priv->dparam.pipe_size = + ARRAY_SIZE(usbhsc_new_pipe_type); + } + break; + default: + if (!info->platform_callback.get_id) { + dev_err(&pdev->dev, "no platform callbacks"); + return -EINVAL; + } + memcpy(&priv->pfunc, + &info->platform_callback, + sizeof(struct renesas_usbhs_platform_callback)); + break; + } + /* set driver callback functions for platform */ dfunc = &info->driver_callback; dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; @@ -507,6 +592,20 @@ static int usbhs_probe(struct platform_device *pdev) */ usbhs_sys_clock_ctrl(priv, 0); + /* check GPIO determining if USB function should be enabled */ + if (priv->dparam.enable_gpio) { + gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL); + ret = !gpio_get_value(priv->dparam.enable_gpio); + gpio_free(priv->dparam.enable_gpio); + if (ret) { + dev_warn(&pdev->dev, + "USB function not selected (GPIO %d)\n", + priv->dparam.enable_gpio); + ret = -ENOTSUPP; + goto probe_end_mod_exit; + } + } + /* * platform call * @@ -633,6 +732,7 @@ static struct platform_driver renesas_usbhs_driver = { .driver = { .name = "renesas_usbhs", .pm = &usbhsc_pm_ops, + .of_match_table = of_match_ptr(usbhs_of_match), }, .probe = usbhs_probe, .remove = usbhs_remove, diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index c69dd2fba360..a7996da6a1bd 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -268,6 +268,8 @@ struct usbhs_priv { * fifo control */ struct usbhs_fifo_info fifo_info; + + struct usb_phy *phy; }; /* diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 4fd36530bfa3..b0c97a3f1bfe 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -108,19 +108,45 @@ static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe) return list_first_entry(&pipe->list, struct usbhs_pkt, node); } +static void usbhsf_fifo_clear(struct usbhs_pipe *pipe, + struct usbhs_fifo *fifo); +static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe, + struct usbhs_fifo *fifo); +static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo, + struct usbhs_pkt *pkt); +#define usbhsf_dma_map(p) __usbhsf_dma_map_ctrl(p, 1) +#define usbhsf_dma_unmap(p) __usbhsf_dma_map_ctrl(p, 0) +static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); unsigned long flags; /******************** spin lock ********************/ usbhs_lock(priv, flags); + usbhs_pipe_disable(pipe); + if (!pkt) pkt = __usbhsf_pkt_get(pipe); - if (pkt) + if (pkt) { + struct dma_chan *chan = NULL; + + if (fifo) + chan = usbhsf_dma_chan_get(fifo, pkt); + if (chan) { + dmaengine_terminate_all(chan); + usbhsf_fifo_clear(pipe, fifo); + usbhsf_dma_unmap(pkt); + } + __usbhsf_pkt_del(pkt); + } + + if (fifo) + usbhsf_fifo_unselect(pipe, fifo); usbhs_unlock(priv, flags); /******************** spin unlock ******************/ @@ -544,6 +570,7 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done) usbhsf_send_terminator(pipe, fifo); usbhsf_tx_irq_ctrl(pipe, !*is_done); + usbhs_pipe_running(pipe, !*is_done); usbhs_pipe_enable(pipe); dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", @@ -570,12 +597,21 @@ usbhs_fifo_write_busy: * retry in interrupt */ usbhsf_tx_irq_ctrl(pipe, 1); + usbhs_pipe_running(pipe, 1); return ret; } +static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done) +{ + if (usbhs_pipe_is_running(pkt->pipe)) + return 0; + + return usbhsf_pio_try_push(pkt, is_done); +} + struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = { - .prepare = usbhsf_pio_try_push, + .prepare = usbhsf_pio_prepare_push, .try_run = usbhsf_pio_try_push, }; @@ -589,6 +625,9 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) if (usbhs_pipe_is_busy(pipe)) return 0; + if (usbhs_pipe_is_running(pipe)) + return 0; + /* * pipe enable to prepare packet receive */ @@ -597,6 +636,7 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length); usbhs_pipe_enable(pipe); + usbhs_pipe_running(pipe, 1); usbhsf_rx_irq_ctrl(pipe, 1); return 0; @@ -642,6 +682,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) (total_len < maxp)) { /* short packet */ *is_done = 1; usbhsf_rx_irq_ctrl(pipe, 0); + usbhs_pipe_running(pipe, 0); usbhs_pipe_disable(pipe); /* disable pipe first */ } @@ -763,8 +804,6 @@ static void __usbhsf_dma_ctrl(struct usbhs_pipe *pipe, usbhs_bset(priv, fifo->sel, DREQE, dreqe); } -#define usbhsf_dma_map(p) __usbhsf_dma_map_ctrl(p, 1) -#define usbhsf_dma_unmap(p) __usbhsf_dma_map_ctrl(p, 0) static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map) { struct usbhs_pipe *pipe = pkt->pipe; @@ -805,6 +844,7 @@ static void xfer_work(struct work_struct *work) dev_dbg(dev, " %s %d (%d/ %d)\n", fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); + usbhs_pipe_running(pipe, 1); usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); usbhs_pipe_enable(pipe); usbhsf_dma_start(pipe, fifo); @@ -836,6 +876,10 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ goto usbhsf_pio_prepare_push; + /* return at this time if the pipe is running */ + if (usbhs_pipe_is_running(pipe)) + return 0; + /* get enable DMA fifo */ fifo = usbhsf_get_dma_fifo(priv, pkt); if (!fifo) @@ -869,15 +913,29 @@ usbhsf_pio_prepare_push: static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done) { struct usbhs_pipe *pipe = pkt->pipe; + int is_short = pkt->trans % usbhs_pipe_get_maxpacket(pipe); + + pkt->actual += pkt->trans; - pkt->actual = pkt->trans; + if (pkt->actual < pkt->length) + *is_done = 0; /* there are remainder data */ + else if (is_short) + *is_done = 1; /* short packet */ + else + *is_done = !pkt->zero; /* send zero packet? */ - *is_done = !pkt->zero; /* send zero packet ? */ + usbhs_pipe_running(pipe, !*is_done); usbhsf_dma_stop(pipe, pipe->fifo); usbhsf_dma_unmap(pkt); usbhsf_fifo_unselect(pipe, pipe->fifo); + if (!*is_done) { + /* change handler to PIO */ + pkt->handler = &usbhs_fifo_pio_push_handler; + return pkt->handler->try_run(pkt, is_done); + } + return 0; } @@ -972,8 +1030,10 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) if ((pkt->actual == pkt->length) || /* receive all data */ (pkt->trans < maxp)) { /* short packet */ *is_done = 1; + usbhs_pipe_running(pipe, 0); } else { /* re-enable */ + usbhs_pipe_running(pipe, 0); usbhsf_prepare_pop(pkt, is_done); } diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 6a030b931a3b..9a705b15b3a1 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -213,7 +213,10 @@ static int usbhs_status_get_each_irq(struct usbhs_priv *priv, { struct usbhs_mod *mod = usbhs_mod_get_current(priv); u16 intenb0, intenb1; + unsigned long flags; + /******************** spin lock ********************/ + usbhs_lock(priv, flags); state->intsts0 = usbhs_read(priv, INTSTS0); state->intsts1 = usbhs_read(priv, INTSTS1); @@ -229,6 +232,8 @@ static int usbhs_status_get_each_irq(struct usbhs_priv *priv, state->bempsts &= mod->irq_bempsts; state->brdysts &= mod->irq_brdysts; } + usbhs_unlock(priv, flags); + /******************** spin unlock ******************/ /* * Check whether the irq enable registers and the irq status are set diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 458f3766bef1..2d17c10a0428 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -129,7 +129,7 @@ static void usbhsg_queue_pop(struct usbhsg_uep *uep, dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe)); ureq->req.status = status; - ureq->req.complete(&uep->ep, &ureq->req); + usb_gadget_giveback_request(&uep->ep, &ureq->req); } static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) @@ -600,8 +600,10 @@ static int usbhsg_ep_enable(struct usb_ep *ep, static int usbhsg_ep_disable(struct usb_ep *ep) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); usbhsg_pipe_disable(uep); + usbhs_pipe_free(pipe); uep->pipe->mod_private = NULL; uep->pipe = NULL; diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 7926e1c700f1..040bcefcb040 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -578,6 +578,19 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe) return usbhsp_flags_has(pipe, IS_DIR_HOST); } +int usbhs_pipe_is_running(struct usbhs_pipe *pipe) +{ + return usbhsp_flags_has(pipe, IS_RUNNING); +} + +void usbhs_pipe_running(struct usbhs_pipe *pipe, int running) +{ + if (running) + usbhsp_flags_set(pipe, IS_RUNNING); + else + usbhsp_flags_clr(pipe, IS_RUNNING); +} + void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) { u16 mask = (SQCLR | SQSET); @@ -640,6 +653,11 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) return pipe; } +static void usbhsp_put_pipe(struct usbhs_pipe *pipe) +{ + usbhsp_flags_init(pipe); +} + void usbhs_pipe_init(struct usbhs_priv *priv, int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) { @@ -710,6 +728,7 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, usbhsp_pipe_select(pipe); usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); + usbhs_pipe_clear(pipe); usbhs_pipe_sequence_data0(pipe); @@ -726,6 +745,11 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, return pipe; } +void usbhs_pipe_free(struct usbhs_pipe *pipe) +{ + usbhsp_put_pipe(pipe); +} + void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo) { if (pipe->fifo) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 3e5349879838..d24a05972370 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -36,6 +36,7 @@ struct usbhs_pipe { #define USBHS_PIPE_FLAGS_IS_USED (1 << 0) #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) +#define USBHS_PIPE_FLAGS_IS_RUNNING (1 << 3) struct usbhs_pkt_handle *handler; @@ -75,10 +76,14 @@ struct usbhs_pipe_info { char *usbhs_pipe_name(struct usbhs_pipe *pipe); struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in); +void usbhs_pipe_free(struct usbhs_pipe *pipe); int usbhs_pipe_probe(struct usbhs_priv *priv); void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); +int usbhs_pipe_is_running(struct usbhs_pipe *pipe); +void usbhs_pipe_running(struct usbhs_pipe *pipe, int running); + void usbhs_pipe_init(struct usbhs_priv *priv, int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c new file mode 100644 index 000000000000..e6b9dcc1c289 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar2.c @@ -0,0 +1,77 @@ +/* + * Renesas USB driver R-Car Gen. 2 initialization and power control + * + * Copyright (C) 2014 Ulrich Hecht + * + * 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/gpio.h> +#include <linux/of_gpio.h> +#include <linux/platform_data/gpio-rcar.h> +#include <linux/usb/phy.h> +#include "common.h" +#include "rcar2.h" + +static int usbhs_rcar2_hardware_init(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + struct usb_phy *phy; + + phy = usb_get_phy_dev(&pdev->dev, 0); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + priv->phy = phy; + return 0; +} + +static int usbhs_rcar2_hardware_exit(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + if (!priv->phy) + return 0; + + usb_put_phy(priv->phy); + priv->phy = NULL; + + return 0; +} + +static int usbhs_rcar2_power_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + if (!priv->phy) + return -ENODEV; + + if (enable) { + int retval = usb_phy_init(priv->phy); + + if (!retval) + retval = usb_phy_set_suspend(priv->phy, 0); + return retval; + } + + usb_phy_set_suspend(priv->phy, 1); + usb_phy_shutdown(priv->phy); + return 0; +} + +static int usbhs_rcar2_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +const struct renesas_usbhs_platform_callback usbhs_rcar2_ops = { + .hardware_init = usbhs_rcar2_hardware_init, + .hardware_exit = usbhs_rcar2_hardware_exit, + .power_ctrl = usbhs_rcar2_power_ctrl, + .get_id = usbhs_rcar2_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h new file mode 100644 index 000000000000..f07f10d9b3b2 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar2.h @@ -0,0 +1,4 @@ +#include "common.h" + +extern const struct renesas_usbhs_platform_callback + usbhs_rcar2_ops; diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 3ce5c74b29e4..a69f7cd9d0bf 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -58,9 +58,11 @@ config USB_SERIAL_SIMPLE handles a wide range of very simple devices, all in one driver. Specifically, it supports: - Suunto ANT+ USB device. + - Medtronic CareLink USB device - Fundamental Software dongle. - HP4x calculators - a number of Motorola phones + - Novatel Wireless GPS receivers - Siemens USB/MPI adapter. - ViVOtech ViVOpay USB device. - Infineon Modem Flashloader USB interface @@ -682,14 +684,6 @@ config USB_SERIAL_WISHBONE To compile this driver as a module, choose M here: the module will be called wishbone-serial. -config USB_SERIAL_ZTE - tristate "ZTE USB serial driver" - help - Say Y here if you want to use a ZTE USB to serial device. - - To compile this driver as a module, choose M here: the - module will be called zte. - config USB_SERIAL_SSU100 tristate "USB Quatech SSU-100 Single Port Serial Driver" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index bfdafd349441..349d9df0895f 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -60,4 +60,3 @@ obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_XSENS_MT) += xsens_mt.o -obj-$(CONFIG_USB_SERIAL_ZTE) += zte_ev.o diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 330df5ce435b..eca1747ca8c7 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -122,6 +122,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */ + { USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ @@ -155,6 +156,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ + { USB_DEVICE(0x1D6F, 0x0010) }, /* Seluxit ApS RF Dongle */ { USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */ { USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */ { USB_DEVICE(0x1FB9, 0x0100) }, /* Lake Shore Model 121 Current Source */ @@ -856,9 +858,6 @@ static int cp210x_startup(struct usb_serial *serial) struct usb_host_interface *cur_altsetting; struct cp210x_serial_private *spriv; - /* cp210x buffers behave strangely unless device is reset */ - usb_reset_device(serial->dev); - spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) return -ENOMEM; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 8a23c53b946e..12b0e67473ba 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -834,7 +834,6 @@ static void digi_set_termios(struct tty_struct *tty, arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS; } else { arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; - tty->hw_stopped = 0; } buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; @@ -1500,15 +1499,11 @@ static int digi_read_oob_callback(struct urb *urb) if (val & DIGI_READ_INPUT_SIGNALS_CTS) { priv->dp_modem_signals |= TIOCM_CTS; /* port must be open to use tty struct */ - if (rts) { - tty->hw_stopped = 0; + if (rts) tty_port_tty_wakeup(&port->port); - } } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ - if (rts) - tty->hw_stopped = 1; } if (val & DIGI_READ_INPUT_SIGNALS_DSR) priv->dp_modem_signals |= TIOCM_DSR; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8a3813be1b28..dc72b924c399 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -87,7 +87,6 @@ struct ftdi_sio_quirk { }; static int ftdi_jtag_probe(struct usb_serial *serial); -static int ftdi_mtxorb_hack_setup(struct usb_serial *serial); static int ftdi_NDI_device_setup(struct usb_serial *serial); static int ftdi_stmclite_probe(struct usb_serial *serial); static int ftdi_8u2232c_probe(struct usb_serial *serial); @@ -98,10 +97,6 @@ static struct ftdi_sio_quirk ftdi_jtag_quirk = { .probe = ftdi_jtag_probe, }; -static struct ftdi_sio_quirk ftdi_mtxorb_hack_quirk = { - .probe = ftdi_mtxorb_hack_setup, -}; - static struct ftdi_sio_quirk ftdi_NDI_device_quirk = { .probe = ftdi_NDI_device_setup, }; @@ -151,6 +146,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_BM_ATOM_NANO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_EV3CON_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) }, @@ -256,14 +252,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) }, @@ -302,18 +296,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) }, @@ -673,6 +661,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) }, + { USB_DEVICE(XSENS_VID, XSENS_CONVERTER_PID) }, + { USB_DEVICE(XSENS_VID, XSENS_MTW_PID) }, { USB_DEVICE(FTDI_VID, FTDI_OMNI1509) }, { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, @@ -738,6 +728,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID), .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, + { USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_S03_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_59_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_57A_PID) }, @@ -945,8 +936,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_2_PID) }, { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_3_PID) }, { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_4_PID) }, + /* ekey Devices */ + { USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) }, /* Infineon Devices */ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) }, + /* GE Healthcare devices */ + { USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) }, { } /* Terminating entry */ }; @@ -1559,45 +1554,40 @@ static void ftdi_determine_type(struct usb_serial_port *port) } -/* Determine the maximum packet size for the device. This depends on the chip - * type and the USB host capabilities. The value should be obtained from the - * device descriptor as the chip will use the appropriate values for the host.*/ +/* + * Determine the maximum packet size for the device. This depends on the chip + * type and the USB host capabilities. The value should be obtained from the + * device descriptor as the chip will use the appropriate values for the host. + */ static void ftdi_set_max_packet_size(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; - struct usb_device *udev = serial->dev; - - struct usb_interface *interface = serial->interface; + struct usb_interface *interface = port->serial->interface; struct usb_endpoint_descriptor *ep_desc; - unsigned num_endpoints; unsigned i; num_endpoints = interface->cur_altsetting->desc.bNumEndpoints; - dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints); - if (!num_endpoints) return; - /* NOTE: some customers have programmed FT232R/FT245R devices - * with an endpoint size of 0 - not good. In this case, we + /* + * NOTE: Some customers have programmed FT232R/FT245R devices + * with an endpoint size of 0 - not good. In this case, we * want to override the endpoint descriptor setting and use a - * value of 64 for wMaxPacketSize */ + * value of 64 for wMaxPacketSize. + */ for (i = 0; i < num_endpoints; i++) { - dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1, - interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize); ep_desc = &interface->cur_altsetting->endpoint[i].desc; - if (ep_desc->wMaxPacketSize == 0) { + if (!ep_desc->wMaxPacketSize) { ep_desc->wMaxPacketSize = cpu_to_le16(0x40); - dev_info(&udev->dev, "Overriding wMaxPacketSize on endpoint %d\n", i); + dev_warn(&port->dev, "Overriding wMaxPacketSize on endpoint %d\n", + usb_endpoint_num(ep_desc)); } } - /* set max packet size based on descriptor */ + /* Set max packet size based on last descriptor. */ priv->max_packet_size = usb_endpoint_maxp(ep_desc); - - dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); } @@ -1866,24 +1856,6 @@ static int ftdi_stmclite_probe(struct usb_serial *serial) return 0; } -/* - * The Matrix Orbital VK204-25-USB has an invalid IN endpoint. - * We have to correct it if we want to read from it. - */ -static int ftdi_mtxorb_hack_setup(struct usb_serial *serial) -{ - struct usb_host_endpoint *ep = serial->dev->ep_in[1]; - struct usb_endpoint_descriptor *ep_desc = &ep->desc; - - if (ep->enabled && ep_desc->wMaxPacketSize == 0) { - ep_desc->wMaxPacketSize = cpu_to_le16(0x40); - dev_info(&serial->dev->dev, - "Fixing invalid wMaxPacketSize on read pipe\n"); - } - - return 0; -} - static int ftdi_sio_port_remove(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index c4777bc6aee0..5937b2d242f2 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -42,6 +42,8 @@ /* www.candapter.com Ewert Energy Systems CANdapter device */ #define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ +#define FTDI_BM_ATOM_NANO_PID 0xa559 /* Basic Micro ATOM Nano USB2Serial */ + /* * Texas Instruments XDS100v2 JTAG / BeagleBone A3 * http://processors.wiki.ti.com/index.php/XDS100 @@ -140,12 +142,15 @@ /* * Xsens Technologies BV products (http://www.xsens.com). */ -#define XSENS_CONVERTER_0_PID 0xD388 -#define XSENS_CONVERTER_1_PID 0xD389 +#define XSENS_VID 0x2639 +#define XSENS_CONVERTER_PID 0xD00D /* Xsens USB-serial converter */ +#define XSENS_MTW_PID 0x0200 /* Xsens MTw */ +#define XSENS_CONVERTER_0_PID 0xD388 /* Xsens USB converter */ +#define XSENS_CONVERTER_1_PID 0xD389 /* Xsens Wireless Receiver */ #define XSENS_CONVERTER_2_PID 0xD38A -#define XSENS_CONVERTER_3_PID 0xD38B -#define XSENS_CONVERTER_4_PID 0xD38C -#define XSENS_CONVERTER_5_PID 0xD38D +#define XSENS_CONVERTER_3_PID 0xD38B /* Xsens USB-serial converter */ +#define XSENS_CONVERTER_4_PID 0xD38C /* Xsens Wireless Receiver */ +#define XSENS_CONVERTER_5_PID 0xD38D /* Xsens Awinda Station */ #define XSENS_CONVERTER_6_PID 0xD38E #define XSENS_CONVERTER_7_PID 0xD38F @@ -832,6 +837,12 @@ #define TELLDUS_TELLSTICK_PID 0x0C30 /* RF control dongle 433 MHz using FT232RL */ /* + * NOVITUS printers + */ +#define NOVITUS_VID 0x1a28 +#define NOVITUS_BONO_E_PID 0x6010 + +/* * RT Systems programming cables for various ham radios */ #define RTSYSTEMS_VID 0x2100 /* Vendor ID */ @@ -1375,3 +1386,14 @@ #define BRAINBOXES_US_160_6_PID 0x9006 /* US-160 16xRS232 1Mbaud Port 11 and 12 */ #define BRAINBOXES_US_160_7_PID 0x9007 /* US-160 16xRS232 1Mbaud Port 13 and 14 */ #define BRAINBOXES_US_160_8_PID 0x9008 /* US-160 16xRS232 1Mbaud Port 15 and 16 */ + +/* + * ekey biometric systems GmbH (http://ekey.net/) + */ +#define FTDI_EKEY_CONV_USB_PID 0xCB08 /* Converter USB */ + +/* + * GE Healthcare devices + */ +#define GE_HEALTHCARE_VID 0x1901 +#define GE_HEALTHCARE_NEMO_TRACKER_PID 0x0015 diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index c0a42e9e6777..ddbb8fe1046d 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1456,12 +1456,8 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) tty = tty_port_tty_get(&edge_port->port->port); /* handle CTS flow control */ if (tty && C_CRTSCTS(tty)) { - if (msr & EDGEPORT_MSR_CTS) { - tty->hw_stopped = 0; + if (msr & EDGEPORT_MSR_CTS) tty_wakeup(tty); - } else { - tty->hw_stopped = 1; - } } tty_kref_put(tty); } @@ -2177,7 +2173,6 @@ static void change_port_settings(struct tty_struct *tty, dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__); } else { dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__); - tty->hw_stopped = 0; restart_read(edge_port); } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index d7440b7557af..e020ad28a00c 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -62,8 +62,6 @@ static void klsi_105_close(struct usb_serial_port *port); static void klsi_105_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int klsi_105_tiocmget(struct tty_struct *tty); -static int klsi_105_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); static void klsi_105_process_read_urb(struct urb *urb); static int klsi_105_prepare_write_buffer(struct usb_serial_port *port, void *dest, size_t size); @@ -93,7 +91,6 @@ static struct usb_serial_driver kl5kusb105d_device = { .set_termios = klsi_105_set_termios, /*.break_ctl = klsi_105_break_ctl,*/ .tiocmget = klsi_105_tiocmget, - .tiocmset = klsi_105_tiocmset, .port_probe = klsi_105_port_probe, .port_remove = klsi_105_port_remove, .throttle = usb_serial_generic_throttle, @@ -602,33 +599,6 @@ static int klsi_105_tiocmget(struct tty_struct *tty) return (int)line_state; } -static int klsi_105_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - int retval = -EINVAL; - -/* if this ever gets implemented, it should be done something like this: - struct usb_serial *serial = port->serial; - struct klsi_105_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - int control; - - spin_lock_irqsave (&priv->lock, flags); - if (set & TIOCM_RTS) - priv->control_state |= TIOCM_RTS; - if (set & TIOCM_DTR) - priv->control_state |= TIOCM_DTR; - if (clear & TIOCM_RTS) - priv->control_state &= ~TIOCM_RTS; - if (clear & TIOCM_DTR) - priv->control_state &= ~TIOCM_DTR; - control = priv->control_state; - spin_unlock_irqrestore (&priv->lock, flags); - retval = mct_u232_set_modem_ctrl(serial, control); -*/ - return retval; -} - module_usb_serial_driver(serial_drivers, id_table); MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 393be562d875..3d88eefdf1d1 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1181,10 +1181,7 @@ static void mos7840_close(struct usb_serial_port *port) /* Freeing Write URBs */ for (j = 0; j < NUM_URBS; ++j) { if (mos7840_port->write_urb_pool[j]) { - if (mos7840_port->write_urb_pool[j]->transfer_buffer) - kfree(mos7840_port->write_urb_pool[j]-> - transfer_buffer); - + kfree(mos7840_port->write_urb_pool[j]->transfer_buffer); usb_free_urb(mos7840_port->write_urb_pool[j]); } } diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index a9688940543d..d1a3f6044c8a 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -275,8 +275,13 @@ static void option_instat_callback(struct urb *urb); #define ZTE_PRODUCT_MF622 0x0001 #define ZTE_PRODUCT_MF628 0x0015 #define ZTE_PRODUCT_MF626 0x0031 -#define ZTE_PRODUCT_MC2718 0xffe8 #define ZTE_PRODUCT_AC2726 0xfff1 +#define ZTE_PRODUCT_MG880 0xfffd +#define ZTE_PRODUCT_CDMA_TECH 0xfffe +#define ZTE_PRODUCT_AC8710T 0xffff +#define ZTE_PRODUCT_MC2718 0xffe8 +#define ZTE_PRODUCT_AD3812 0xffeb +#define ZTE_PRODUCT_MC2716 0xffed #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 @@ -494,6 +499,10 @@ static void option_instat_callback(struct urb *urb); #define INOVIA_VENDOR_ID 0x20a6 #define INOVIA_SEW858 0x1105 +/* VIA Telecom */ +#define VIATELECOM_VENDOR_ID 0x15eb +#define VIATELECOM_PRODUCT_CDS7 0x0001 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -527,10 +536,18 @@ static const struct option_blacklist_info zte_k3765_z_blacklist = { .reserved = BIT(4), }; +static const struct option_blacklist_info zte_ad3812_z_blacklist = { + .sendsetup = BIT(0) | BIT(1) | BIT(2), +}; + static const struct option_blacklist_info zte_mc2718_z_blacklist = { .sendsetup = BIT(1) | BIT(2) | BIT(3) | BIT(4), }; +static const struct option_blacklist_info zte_mc2716_z_blacklist = { + .sendsetup = BIT(1) | BIT(2) | BIT(3), +}; + static const struct option_blacklist_info huawei_cdc12_blacklist = { .reserved = BIT(1) | BIT(2), }; @@ -1070,6 +1087,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1012, 0xff) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC650) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) }, + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */ @@ -1543,14 +1561,27 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff92, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) }, - - /* NOTE: most ZTE CDMA devices should be driven by zte_ev, not option */ + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffec, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffee, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff6, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff7, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff8, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff9, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfffb, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfffc, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MG880, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mc2718_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, @@ -1724,6 +1755,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */ { USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) }, + { USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -1916,6 +1948,8 @@ static void option_instat_callback(struct urb *urb) dev_dbg(dev, "%s: type %x req %x\n", __func__, req_pkt->bRequestType, req_pkt->bRequest); } + } else if (status == -ENOENT || status == -ESHUTDOWN) { + dev_dbg(dev, "%s: urb stopped: %d\n", __func__, status); } else dev_err(dev, "%s: error %d\n", __func__, status); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index b3d5a35c0d4b..0f872e6b2c87 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -45,6 +45,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, @@ -161,6 +162,9 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { .max_baud_rate = 1228800, .quirks = PL2303_QUIRK_LEGACY, }, + [TYPE_HX] = { + .max_baud_rate = 12000000, + }, }; static int pl2303_vendor_read(struct usb_serial *serial, u16 value, @@ -394,16 +398,14 @@ static void pl2303_encode_baud_rate(struct tty_struct *tty, if (spriv->type->max_baud_rate) baud = min_t(speed_t, baud, spriv->type->max_baud_rate); /* - * Set baud rate to nearest supported value. - * - * NOTE: Baud rate 500k can only be set using divisors. + * Use direct method for supported baud rates, otherwise use divisors. */ baud_sup = pl2303_get_supported_baud_rate(baud); - if (baud == 500000) - baud = pl2303_encode_baud_rate_divisor(buf, baud); + if (baud == baud_sup) + baud = pl2303_encode_baud_rate_direct(buf, baud); else - baud = pl2303_encode_baud_rate_direct(buf, baud_sup); + baud = pl2303_encode_baud_rate_divisor(buf, baud); /* Save resulting baud rate */ tty_encode_baud_rate(tty, baud, baud); diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 42bc082896ac..71fd9da1d6e7 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -22,6 +22,7 @@ #define PL2303_PRODUCT_ID_GPRS 0x0609 #define PL2303_PRODUCT_ID_HCR331 0x331a #define PL2303_PRODUCT_ID_MOTOROLA 0x0307 +#define PL2303_PRODUCT_ID_ZTEK 0xe1f1 #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 6f7f01eb556a..46179a0828eb 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -282,14 +282,19 @@ static const struct usb_device_id id_table[] = { /* Sierra Wireless HSPA Non-Composite Device */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)}, { USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */ - { USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */ + /* Sierra Wireless Direct IP modems */ + { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68A3, 0xFF, 0xFF, 0xFF), + .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist + }, + { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68AA, 0xFF, 0xFF, 0xFF), .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist }, /* AT&T Direct IP LTE modems */ { USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68AA, 0xFF, 0xFF, 0xFF), .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist }, - { USB_DEVICE(0x0f3d, 0x68A3), /* Airprime/Sierra Wireless Direct IP modems */ + /* Airprime/Sierra Wireless Direct IP modems */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68A3, 0xFF, 0xFF, 0xFF), .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist }, diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 3dd3ff8c50d3..e9da41d9fe7f 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -773,7 +773,6 @@ static void ti_set_termios(struct tty_struct *tty, config->wFlags |= TI_UART_ENABLE_RTS_IN; config->wFlags |= TI_UART_ENABLE_CTS_OUT; } else { - tty->hw_stopped = 0; ti_restart_read(tport, tty); } @@ -1291,12 +1290,8 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) /* handle CTS flow control */ tty = tty_port_tty_get(&tport->tp_port->port); if (tty && C_CRTSCTS(tty)) { - if (msr & TI_MSR_CTS) { - tty->hw_stopped = 0; + if (msr & TI_MSR_CTS) tty_wakeup(tty); - } else { - tty->hw_stopped = 1; - } } tty_kref_put(tty); } diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index fb79775447b0..7064eb8d6142 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -20,7 +20,7 @@ #include <linux/usb.h> #include <linux/usb/serial.h> -#define DEVICE(vendor, IDS) \ +#define DEVICE_N(vendor, IDS, nport) \ static const struct usb_device_id vendor##_id_table[] = { \ IDS(), \ { }, \ @@ -31,9 +31,15 @@ static struct usb_serial_driver vendor##_device = { \ .name = #vendor, \ }, \ .id_table = vendor##_id_table, \ - .num_ports = 1, \ + .num_ports = nport, \ }; +#define DEVICE(vendor, IDS) DEVICE_N(vendor, IDS, 1) + +/* Medtronic CareLink USB driver */ +#define CARELINK_IDS() \ + { USB_DEVICE(0x0a21, 0x8001) } /* MMT-7305WW */ +DEVICE(carelink, CARELINK_IDS); /* ZIO Motherboard USB driver */ #define ZIO_IDS() \ @@ -64,6 +70,11 @@ DEVICE(vivopay, VIVOPAY_IDS); { USB_DEVICE(0x22b8, 0x2c64) } /* Motorola V950 phone */ DEVICE(moto_modem, MOTO_IDS); +/* Novatel Wireless GPS driver */ +#define NOVATEL_IDS() \ + { USB_DEVICE(0x09d7, 0x0100) } /* NovAtel FlexPack GPS */ +DEVICE_N(novatel_gps, NOVATEL_IDS, 3); + /* HP4x (48/49) Generic Serial driver */ #define HP4X_IDS() \ { USB_DEVICE(0x03f0, 0x0121) } @@ -82,11 +93,13 @@ DEVICE(siemens_mpi, SIEMENS_IDS); /* All of the above structures mushed into two lists */ static struct usb_serial_driver * const serial_drivers[] = { + &carelink_device, &zio_device, &funsoft_device, &flashloader_device, &vivopay_device, &moto_modem_device, + &novatel_gps_device, &hp4x_device, &suunto_device, &siemens_mpi_device, @@ -94,11 +107,13 @@ static struct usb_serial_driver * const serial_drivers[] = { }; static const struct usb_device_id id_table[] = { + CARELINK_IDS(), ZIO_IDS(), FUNSOFT_IDS(), FLASHLOADER_IDS(), VIVOPAY_IDS(), MOTO_IDS(), + NOVATEL_IDS(), HP4X_IDS(), SUUNTO_IDS(), SIEMENS_IDS(), diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 02de3110fe94..475723c006f9 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -764,29 +764,39 @@ static int usb_serial_probe(struct usb_interface *interface, if (usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ dev_dbg(ddev, "found bulk in on endpoint %d\n", i); - bulk_in_endpoint[num_bulk_in] = endpoint; - ++num_bulk_in; + if (num_bulk_in < MAX_NUM_PORTS) { + bulk_in_endpoint[num_bulk_in] = endpoint; + ++num_bulk_in; + } } if (usb_endpoint_is_bulk_out(endpoint)) { /* we found a bulk out endpoint */ dev_dbg(ddev, "found bulk out on endpoint %d\n", i); - bulk_out_endpoint[num_bulk_out] = endpoint; - ++num_bulk_out; + if (num_bulk_out < MAX_NUM_PORTS) { + bulk_out_endpoint[num_bulk_out] = endpoint; + ++num_bulk_out; + } } if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dev_dbg(ddev, "found interrupt in on endpoint %d\n", i); - interrupt_in_endpoint[num_interrupt_in] = endpoint; - ++num_interrupt_in; + if (num_interrupt_in < MAX_NUM_PORTS) { + interrupt_in_endpoint[num_interrupt_in] = + endpoint; + ++num_interrupt_in; + } } if (usb_endpoint_is_int_out(endpoint)) { /* we found an interrupt out endpoint */ dev_dbg(ddev, "found interrupt out on endpoint %d\n", i); - interrupt_out_endpoint[num_interrupt_out] = endpoint; - ++num_interrupt_out; + if (num_interrupt_out < MAX_NUM_PORTS) { + interrupt_out_endpoint[num_interrupt_out] = + endpoint; + ++num_interrupt_out; + } } } @@ -809,8 +819,10 @@ static int usb_serial_probe(struct usb_interface *interface, if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dev_dbg(ddev, "found interrupt in for Prolific device on separate interface\n"); - interrupt_in_endpoint[num_interrupt_in] = endpoint; - ++num_interrupt_in; + if (num_interrupt_in < MAX_NUM_PORTS) { + interrupt_in_endpoint[num_interrupt_in] = endpoint; + ++num_interrupt_in; + } } } } @@ -850,6 +862,11 @@ static int usb_serial_probe(struct usb_interface *interface, num_ports = type->num_ports; } + if (num_ports > MAX_NUM_PORTS) { + dev_warn(ddev, "too many ports requested: %d\n", num_ports); + num_ports = MAX_NUM_PORTS; + } + serial->num_ports = num_ports; serial->num_bulk_in = num_bulk_in; serial->num_bulk_out = num_bulk_out; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index e62f2dff8b7d..6c3734d2b45a 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -514,6 +514,10 @@ static void command_port_read_callback(struct urb *urb) dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__); return; } + if (!urb->actual_length) { + dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__); + return; + } if (status) { dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status); if (status != -ENOENT) @@ -534,7 +538,8 @@ static void command_port_read_callback(struct urb *urb) /* These are unsolicited reports from the firmware, hence no waiting command to wakeup */ dev_dbg(&urb->dev->dev, "%s - event received\n", __func__); - } else if (data[0] == WHITEHEAT_GET_DTR_RTS) { + } else if ((data[0] == WHITEHEAT_GET_DTR_RTS) && + (urb->actual_length - 1 <= sizeof(command_info->result_buffer))) { memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1); command_info->command_finished = WHITEHEAT_CMD_COMPLETE; diff --git a/drivers/usb/serial/xsens_mt.c b/drivers/usb/serial/xsens_mt.c index 4841fb57400c..3837d5113bb2 100644 --- a/drivers/usb/serial/xsens_mt.c +++ b/drivers/usb/serial/xsens_mt.c @@ -41,28 +41,13 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); -static int has_required_endpoints(const struct usb_host_interface *interface) -{ - __u8 i; - int has_bulk_in = 0; - int has_bulk_out = 0; - - for (i = 0; i < interface->desc.bNumEndpoints; ++i) { - if (usb_endpoint_is_bulk_in(&interface->endpoint[i].desc)) - has_bulk_in = 1; - else if (usb_endpoint_is_bulk_out(&interface->endpoint[i].desc)) - has_bulk_out = 1; - } - - return has_bulk_in && has_bulk_out; -} - static int xsens_mt_probe(struct usb_serial *serial, const struct usb_device_id *id) { - if (!has_required_endpoints(serial->interface->cur_altsetting)) - return -ENODEV; - return 0; + if (serial->interface->cur_altsetting->desc.bInterfaceNumber == 1) + return 0; + + return -ENODEV; } static struct usb_serial_driver xsens_mt_device = { @@ -82,4 +67,6 @@ static struct usb_serial_driver * const serial_drivers[] = { module_usb_serial_driver(serial_drivers, id_table); +MODULE_AUTHOR("Frans Klaver <frans.klaver@xsens.com>"); +MODULE_DESCRIPTION("USB-serial driver for Xsens motion trackers"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/zte_ev.c b/drivers/usb/serial/zte_ev.c deleted file mode 100644 index e40ab739c4a6..000000000000 --- a/drivers/usb/serial/zte_ev.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * ZTE_EV USB serial driver - * - * Copyright (C) 2012 Greg Kroah-Hartman <gregkh@linuxfoundation.org> - * Copyright (C) 2012 Linux Foundation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This driver is based on code found in a ZTE_ENV patch that modified - * the usb-serial generic driver. Comments were left in that I think - * show the commands used to talk to the device, but I am not sure. - */ -#include <linux/kernel.h> -#include <linux/tty.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/usb.h> -#include <linux/usb/serial.h> -#include <linux/uaccess.h> - -#define MAX_SETUP_DATA_SIZE 32 - -static void debug_data(struct device *dev, const char *function, int len, - const unsigned char *data, int result) -{ - dev_dbg(dev, "result = %d\n", result); - if (result == len) - dev_dbg(dev, "%s - length = %d, data = %*ph\n", function, - len, len, data); -} - -static int zte_ev_usb_serial_open(struct tty_struct *tty, - struct usb_serial_port *port) -{ - struct usb_device *udev = port->serial->dev; - struct device *dev = &port->dev; - int result = 0; - int len; - unsigned char *buf; - - buf = kmalloc(MAX_SETUP_DATA_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* send 1st ctl cmd(CTL 21 22 01 00 00 00 00 00) */ - len = 0; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x22, 0x21, - 0x0001, 0x0000, NULL, len, - USB_CTRL_GET_TIMEOUT); - dev_dbg(dev, "result = %d\n", result); - - /* send 2st cmd and receive data */ - /* - * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 25.1.0(5) - * 16.0 DI 00 96 00 00 00 00 08 - */ - len = 0x0007; - result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x21, 0xa1, - 0x0000, 0x0000, buf, len, - USB_CTRL_GET_TIMEOUT); - debug_data(dev, __func__, len, buf, result); - - /* send 3rd cmd */ - /* - * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 30.1.0 - * 16.0 DO 80 25 00 00 00 00 08 .%..... 30.2.0 - */ - len = 0x0007; - buf[0] = 0x80; - buf[1] = 0x25; - buf[2] = 0x00; - buf[3] = 0x00; - buf[4] = 0x00; - buf[5] = 0x00; - buf[6] = 0x08; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x20, 0x21, - 0x0000, 0x0000, buf, len, - USB_CTRL_GET_TIMEOUT); - debug_data(dev, __func__, len, buf, result); - - /* send 4th cmd */ - /* - * 16.0 CTL 21 22 03 00 00 00 00 00 - */ - len = 0; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x22, 0x21, - 0x0003, 0x0000, NULL, len, - USB_CTRL_GET_TIMEOUT); - dev_dbg(dev, "result = %d\n", result); - - /* send 5th cmd */ - /* - * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 33.1.0 - * 16.0 DI 80 25 00 00 00 00 08 - */ - len = 0x0007; - result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x21, 0xa1, - 0x0000, 0x0000, buf, len, - USB_CTRL_GET_TIMEOUT); - debug_data(dev, __func__, len, buf, result); - - /* send 6th cmd */ - /* - * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 34.1.0 - * 16.0 DO 80 25 00 00 00 00 08 - */ - len = 0x0007; - buf[0] = 0x80; - buf[1] = 0x25; - buf[2] = 0x00; - buf[3] = 0x00; - buf[4] = 0x00; - buf[5] = 0x00; - buf[6] = 0x08; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x20, 0x21, - 0x0000, 0x0000, buf, len, - USB_CTRL_GET_TIMEOUT); - debug_data(dev, __func__, len, buf, result); - kfree(buf); - - return usb_serial_generic_open(tty, port); -} - -/* - * CTL 21 22 02 00 00 00 00 00 CLASS 338.1.0 - * - * 16.1 DI a1 20 00 00 00 00 02 00 02 00 . ........ 340.1.0 - * 16.0 CTL 21 22 03 00 00 00 00 00 CLASS 341.1.0 - * - * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 346.1.0(3) - * 16.0 DI 00 08 07 00 00 00 08 ....... 346.2.0 - * - * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 349.1.0 - * 16.0 DO 00 c2 01 00 00 00 08 ....... 349.2.0 - * - * 16.0 CTL 21 22 03 00 00 00 00 00 CLASS 350.1.0(2) - * - * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 352.1.0 - * 16.0 DI 00 c2 01 00 00 00 08 ....... 352.2.0 - * - * 16.1 DI a1 20 00 00 00 00 02 00 02 00 . ........ 353.1.0 - * - * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 354.1.0 - * 16.0 DO 00 c2 01 00 00 00 08 ....... 354.2.0 - * - * 16.0 CTL 21 22 03 00 00 00 00 00 -*/ - -static void zte_ev_usb_serial_close(struct usb_serial_port *port) -{ - struct usb_device *udev = port->serial->dev; - struct device *dev = &port->dev; - int result = 0; - int len; - unsigned char *buf; - - buf = kmalloc(MAX_SETUP_DATA_SIZE, GFP_KERNEL); - if (!buf) - return; - - /* send 1st ctl cmd(CTL 21 22 02 00 00 00 00 00) */ - len = 0; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x22, 0x21, - 0x0002, 0x0000, NULL, len, - USB_CTRL_GET_TIMEOUT); - dev_dbg(dev, "result = %d\n", result); - - /* send 2st ctl cmd(CTL 21 22 03 00 00 00 00 00 ) */ - len = 0; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x22, 0x21, - 0x0003, 0x0000, NULL, len, - USB_CTRL_GET_TIMEOUT); - dev_dbg(dev, "result = %d\n", result); - - /* send 3st cmd and recieve data */ - /* - * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 25.1.0(5) - * 16.0 DI 00 08 07 00 00 00 08 - */ - len = 0x0007; - result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x21, 0xa1, - 0x0000, 0x0000, buf, len, - USB_CTRL_GET_TIMEOUT); - debug_data(dev, __func__, len, buf, result); - - /* send 4th cmd */ - /* - * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 30.1.0 - * 16.0 DO 00 c2 01 00 00 00 08 .%..... 30.2.0 - */ - len = 0x0007; - buf[0] = 0x00; - buf[1] = 0xc2; - buf[2] = 0x01; - buf[3] = 0x00; - buf[4] = 0x00; - buf[5] = 0x00; - buf[6] = 0x08; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x20, 0x21, - 0x0000, 0x0000, buf, len, - USB_CTRL_GET_TIMEOUT); - debug_data(dev, __func__, len, buf, result); - - /* send 5th cmd */ - /* - * 16.0 CTL 21 22 03 00 00 00 00 00 - */ - len = 0; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x22, 0x21, - 0x0003, 0x0000, NULL, len, - USB_CTRL_GET_TIMEOUT); - dev_dbg(dev, "result = %d\n", result); - - /* send 6th cmd */ - /* - * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 33.1.0 - * 16.0 DI 00 c2 01 00 00 00 08 - */ - len = 0x0007; - result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x21, 0xa1, - 0x0000, 0x0000, buf, len, - USB_CTRL_GET_TIMEOUT); - debug_data(dev, __func__, len, buf, result); - - /* send 7th cmd */ - /* - * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 354.1.0 - * 16.0 DO 00 c2 01 00 00 00 08 ....... 354.2.0 - */ - len = 0x0007; - buf[0] = 0x00; - buf[1] = 0xc2; - buf[2] = 0x01; - buf[3] = 0x00; - buf[4] = 0x00; - buf[5] = 0x00; - buf[6] = 0x08; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x20, 0x21, - 0x0000, 0x0000, buf, len, - USB_CTRL_GET_TIMEOUT); - debug_data(dev, __func__, len, buf, result); - - /* send 8th cmd */ - /* - * 16.0 CTL 21 22 03 00 00 00 00 00 - */ - len = 0; - result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x22, 0x21, - 0x0003, 0x0000, NULL, len, - USB_CTRL_GET_TIMEOUT); - dev_dbg(dev, "result = %d\n", result); - - kfree(buf); - - usb_serial_generic_close(port); -} - -static const struct usb_device_id id_table[] = { - /* AC8710, AC8710T */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffff, 0xff, 0xff, 0xff) }, - /* AC8700 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xfffe, 0xff, 0xff, 0xff) }, - /* MG880 */ - { USB_DEVICE(0x19d2, 0xfffd) }, - { USB_DEVICE(0x19d2, 0xfffc) }, - { USB_DEVICE(0x19d2, 0xfffb) }, - /* AC8710_V3 */ - { USB_DEVICE(0x19d2, 0xfff6) }, - { USB_DEVICE(0x19d2, 0xfff7) }, - { USB_DEVICE(0x19d2, 0xfff8) }, - { USB_DEVICE(0x19d2, 0xfff9) }, - { USB_DEVICE(0x19d2, 0xffee) }, - /* AC2716, MC2716 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffed, 0xff, 0xff, 0xff) }, - /* AD3812 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffeb, 0xff, 0xff, 0xff) }, - { USB_DEVICE(0x19d2, 0xffec) }, - { USB_DEVICE(0x05C6, 0x3197) }, - { USB_DEVICE(0x05C6, 0x6000) }, - { USB_DEVICE(0x05C6, 0x9008) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -static struct usb_serial_driver zio_device = { - .driver = { - .owner = THIS_MODULE, - .name = "zte_ev", - }, - .id_table = id_table, - .num_ports = 1, - .open = zte_ev_usb_serial_open, - .close = zte_ev_usb_serial_close, -}; - -static struct usb_serial_driver * const serial_drivers[] = { - &zio_device, NULL -}; - -module_usb_serial_driver(serial_drivers, id_table); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 13b5bfbaf951..715f299af6ea 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -18,9 +18,7 @@ config USB_STORAGE This option depends on 'SCSI' support being enabled, but you probably also need 'SCSI device support: SCSI disk support' - (BLK_DEV_SD) for most USB storage devices. Some devices also - will require 'Probe all LUNs on each SCSI device' - (SCSI_MULTI_LUN). + (BLK_DEV_SD) for most USB storage devices. To compile this driver as a module, choose M here: the module will be called usb-storage. @@ -193,7 +191,7 @@ config USB_STORAGE_ENE_UB6250 depends on USB_STORAGE ---help--- Say Y here if you wish to control a ENE SD/MS Card reader. - To use SM card, please build driver/staging/keucr/keucr.ko + Note that this driver does not support SM cards. This option depends on 'SCSI' support being enabled, but you probably also need 'SCSI device support: SCSI disk support' diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 6636a583da12..62c2d9daa7d6 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -415,14 +415,11 @@ static int alauda_init_media(struct us_data *us) if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - usb_stor_dbg(us, "Media signature: %02X %02X %02X %02X\n", - data[0], data[1], data[2], data[3]); + usb_stor_dbg(us, "Media signature: %4ph\n", data); media_info = alauda_card_find_id(data[1]); if (media_info == NULL) { - printk(KERN_WARNING - "alauda_init_media: Unrecognised media signature: " - "%02X %02X %02X %02X\n", - data[0], data[1], data[2], data[3]); + pr_warn("alauda_init_media: Unrecognised media signature: %4ph\n", + data); return USB_STOR_TRANSPORT_ERROR; } @@ -513,7 +510,7 @@ static int alauda_check_status2(struct us_data *us) if (rc != USB_STOR_XFER_GOOD) return rc; - usb_stor_dbg(us, "%02X %02X %02X\n", data[0], data[1], data[2]); + usb_stor_dbg(us, "%3ph\n", data); if (data[0] & ALAUDA_STATUS_ERROR) return USB_STOR_XFER_ERROR; diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index ef6efb55dc31..56f782bef36b 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2344,8 +2344,8 @@ static int ene_ub6250_probe(struct usb_interface *intf, } if (!(misc_reg03 & 0x01)) { - pr_info("ums_eneub6250: The driver only supports SD/MS card. " - "To use SM card, please build driver/staging/keucr\n"); + pr_info("ums_eneub6250: This driver only supports SD/MS cards. " + "It does not support SM cards.\n"); } return result; diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 5a8b5ff1e45b..4bc2fc98636e 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -52,7 +52,7 @@ int usb_stor_euscsi_init(struct us_data *us) us->iobuf[0] = 0x1; result = usb_stor_control_msg(us, us->send_ctrl_pipe, 0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR, - 0x01, 0x0, us->iobuf, 0x1, 5000); + 0x01, 0x0, us->iobuf, 0x1, USB_CTRL_SET_TIMEOUT); usb_stor_dbg(us, "-- result is %d\n", result); return 0; diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 281be56d5648..8591d89a38e6 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -115,7 +115,7 @@ struct rts51x_chip { enum RTS51X_STAT state; int support_auto_delink; #endif - /* used to back up the protocal choosen in probe1 phase */ + /* used to back up the protocol chosen in probe1 phase */ proto_cmnd proto_handler_backup; }; @@ -925,7 +925,7 @@ static int realtek_cr_autosuspend_setup(struct us_data *us) (unsigned long)chip); fw5895_init(us); - /* enable autosuspend funciton of the usb device */ + /* enable autosuspend function of the usb device */ usb_enable_autosuspend(us->pusb_dev); return 0; diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 866b5df36ed1..0e400f382f3a 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -241,7 +241,7 @@ static int slave_configure(struct scsi_device *sdev) /* Some USB cardreaders have trouble reading an sdcard's last * sector in a larger then 1 sector read, since the performance - * impact is negible we set this flag for all USB disks */ + * impact is negligible we set this flag for all USB disks */ sdev->last_sector_bug = 1; /* Enable last-sector hacks for single-target devices using diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 073a2c32ccc4..3847053d732c 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1155,8 +1155,7 @@ sddr09_get_cardinfo(struct us_data *us, unsigned char flags) { return NULL; } - sprintf(blurbtxt, "sddr09: Found Flash card, ID = %02X %02X %02X %02X", - deviceID[0], deviceID[1], deviceID[2], deviceID[3]); + sprintf(blurbtxt, "sddr09: Found Flash card, ID = %4ph", deviceID); /* Byte 0 is the manufacturer */ sprintf(blurbtxt + strlen(blurbtxt), @@ -1498,7 +1497,7 @@ static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) { int ret; - usb_stor_dbg(us, "LUN=%d\n", srb->device->lun); + usb_stor_dbg(us, "LUN=%d\n", (u8)srb->device->lun); switch (srb->device->lun) { case 0: @@ -1524,7 +1523,7 @@ static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) break; default: - usb_stor_dbg(us, "Invalid LUN %d\n", srb->device->lun); + usb_stor_dbg(us, "Invalid LUN %d\n", (u8)srb->device->lun); ret = USB_STOR_TRANSPORT_ERROR; break; } diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index bb05b984d5f6..8a6f371ed6e7 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -9,32 +9,15 @@ static int uas_is_interface(struct usb_host_interface *intf) 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); + if (uas_is_interface(alt)) return alt->desc.bAlternateSetting; - } } return -ENODEV; @@ -76,13 +59,6 @@ static int uas_use_uas_driver(struct usb_interface *intf, 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) @@ -92,5 +68,46 @@ static int uas_use_uas_driver(struct usb_interface *intf, if (r < 0) return 0; + /* + * ASM1051 and older ASM1053 devices have the same usb-id, and UAS is + * broken on the ASM1051, use the number of streams to differentiate. + * New ASM1053-s also support 32 streams, but have a different prod-id. + */ + if (le16_to_cpu(udev->descriptor.idVendor) == 0x174c && + le16_to_cpu(udev->descriptor.idProduct) == 0x55aa) { + if (udev->speed < USB_SPEED_SUPER) { + /* No streams info, assume ASM1051 */ + flags |= US_FL_IGNORE_UAS; + } else if (usb_ss_max_streams(&eps[1]->ss_ep_comp) == 32) { + flags |= US_FL_IGNORE_UAS; + } + } + + usb_stor_adjust_quirks(udev, &flags); + + if (flags & US_FL_IGNORE_UAS) { + dev_warn(&udev->dev, + "UAS is blacklisted for this device, using usb-storage instead\n"); + return 0; + } + + if (udev->bus->sg_tablesize == 0) { + 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 other USB controller if you wish to use UAS.\n"); + return 0; + } + + if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) { + dev_warn(&udev->dev, + "USB controller %s does not support streams, which are required by the UAS driver.\n", + hcd_to_bus(hcd)->bus_name); + dev_warn(&udev->dev, + "Please try an other USB controller if you wish to use UAS.\n"); + return 0; + } + return 1; } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 511b22953167..89b24349269e 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -2,7 +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 Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2014 * Copyright Matthew Wilcox for Intel Corp, 2010 * Copyright Sarah Sharp for Intel Corp, 2010 * @@ -28,20 +28,9 @@ #include <scsi/scsi_tcq.h> #include "uas-detect.h" +#include "scsiglue.h" -/* - * The r00-r01c specs define this version of the SENSE IU data structure. - * It's still in use by several different firmware releases. - */ -struct sense_iu_old { - __u8 iu_id; - __u8 rsvd1; - __be16 tag; - __be16 len; - __u8 status; - __u8 service_response; - __u8 sense[SCSI_SENSE_BUFFERSIZE]; -}; +#define MAX_CMNDS 256 struct uas_dev_info { struct usb_interface *intf; @@ -49,18 +38,14 @@ struct uas_dev_info { struct usb_anchor cmd_urbs; struct usb_anchor sense_urbs; struct usb_anchor data_urbs; + unsigned long flags; int qdepth, resetting; - 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; + struct scsi_cmnd *cmnd[MAX_CMNDS]; spinlock_t lock; struct work_struct work; - struct list_head inflight_list; - struct list_head dead_list; }; enum { @@ -74,10 +59,8 @@ enum { COMMAND_INFLIGHT = (1 << 8), DATA_IN_URB_INFLIGHT = (1 << 9), DATA_OUT_URB_INFLIGHT = (1 << 10), - COMMAND_COMPLETED = (1 << 11), - COMMAND_ABORTED = (1 << 12), - UNLINK_DATA_URBS = (1 << 13), - IS_IN_WORK_LIST = (1 << 14), + COMMAND_ABORTED = (1 << 11), + IS_IN_WORK_LIST = (1 << 12), }; /* Overrides scsi_pointer */ @@ -87,7 +70,6 @@ struct uas_cmd_info { struct urb *cmd_urb; struct urb *data_in_urb; struct urb *data_out_urb; - struct list_head list; }; /* I hate forward declarations, but I actually have a loop */ @@ -96,43 +78,29 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, 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); - -/* 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, - unsigned long *lock_flags) -{ - /* - * The UNLINK_DATA_URBS flag makes sure uas_try_complete - * (called by urb completion) doesn't release cmdinfo - * underneath us. - */ - cmdinfo->state |= UNLINK_DATA_URBS; - 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, *lock_flags); - cmdinfo->state &= ~UNLINK_DATA_URBS; -} +static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix, + int status); 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 scsi_cmnd *cmnd; unsigned long flags; - int err; + int i, err; 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); + + if (devinfo->resetting) + goto out; + + for (i = 0; i < devinfo->qdepth; i++) { + if (!devinfo->cmnd[i]) + continue; + + cmnd = devinfo->cmnd[i]; + cmdinfo = (void *)&cmnd->SCp; if (!(cmdinfo->state & IS_IN_WORK_LIST)) continue; @@ -143,35 +111,7 @@ static void uas_do_work(struct work_struct *work) else schedule_work(&devinfo->work); } - spin_unlock_irqrestore(&devinfo->lock, flags); -} - -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; - unsigned long flags; - - 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); +out: spin_unlock_irqrestore(&devinfo->lock, flags); } @@ -181,31 +121,32 @@ static void uas_add_work(struct uas_cmd_info *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)); + lockdep_assert_held(&devinfo->lock); cmdinfo->state |= IS_IN_WORK_LIST; schedule_work(&devinfo->work); } -static void uas_zap_dead(struct uas_dev_info *devinfo) +static void uas_zap_pending(struct uas_dev_info *devinfo, int result) { struct uas_cmd_info *cmdinfo; - struct uas_cmd_info *temp; + struct scsi_cmnd *cmnd; unsigned long flags; + int i, err; spin_lock_irqsave(&devinfo->lock, flags); - 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); - 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__); + for (i = 0; i < devinfo->qdepth; i++) { + if (!devinfo->cmnd[i]) + continue; + + cmnd = devinfo->cmnd[i]; + cmdinfo = (void *)&cmnd->SCp; + uas_log_cmd_state(cmnd, __func__, 0); + /* Sense urbs were killed, clear COMMAND_INFLIGHT manually */ + cmdinfo->state &= ~COMMAND_INFLIGHT; + cmnd->result = result << 16; + err = uas_try_complete(cmnd, __func__); + WARN_ON(err != 0); } - devinfo->running_task = 0; spin_unlock_irqrestore(&devinfo->lock, flags); } @@ -232,36 +173,30 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) cmnd->result = sense_iu->status; } -static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) +/* + * scsi-tags go from 0 - (nr_tags - 1), uas tags need to match stream-ids, + * which go from 1 - nr_streams. And we use 1 for untagged commands. + */ +static int uas_get_tag(struct scsi_cmnd *cmnd) { - struct sense_iu_old *sense_iu = urb->transfer_buffer; - struct scsi_device *sdev = cmnd->device; + int tag; - if (urb->actual_length > 8) { - unsigned len = be16_to_cpup(&sense_iu->len) - 2; - if (len + 8 != urb->actual_length) { - int newlen = min(len + 8, urb->actual_length) - 8; - if (newlen < 0) - newlen = 0; - sdev_printk(KERN_INFO, sdev, "%s: urb length %d " - "disagrees with IU sense data length %d, " - "using %d bytes of sense data\n", __func__, - urb->actual_length, len, newlen); - len = newlen; - } - memcpy(cmnd->sense_buffer, sense_iu->sense, len); - } + if (blk_rq_tagged(cmnd->request)) + tag = cmnd->request->tag + 2; + else + tag = 1; - cmnd->result = sense_iu->status; + return tag; } -static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller) +static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix, + int status) { struct uas_cmd_info *ci = (void *)&cmnd->SCp; - scmd_printk(KERN_INFO, cmnd, "%s %p tag %d, inflight:" - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - caller, cmnd, cmnd->request->tag, + scmd_printk(KERN_INFO, cmnd, + "%s %d tag %d inflight:%s%s%s%s%s%s%s%s%s%s%s%s ", + prefix, status, uas_get_tag(cmnd), (ci->state & SUBMIT_STATUS_URB) ? " s-st" : "", (ci->state & ALLOC_DATA_IN_URB) ? " a-in" : "", (ci->state & SUBMIT_DATA_IN_URB) ? " s-in" : "", @@ -272,10 +207,28 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller) (ci->state & COMMAND_INFLIGHT) ? " CMD" : "", (ci->state & DATA_IN_URB_INFLIGHT) ? " IN" : "", (ci->state & DATA_OUT_URB_INFLIGHT) ? " OUT" : "", - (ci->state & COMMAND_COMPLETED) ? " done" : "", (ci->state & COMMAND_ABORTED) ? " abort" : "", - (ci->state & UNLINK_DATA_URBS) ? " unlink": "", (ci->state & IS_IN_WORK_LIST) ? " work" : ""); + scsi_print_command(cmnd); +} + +static void uas_free_unsubmitted_urbs(struct scsi_cmnd *cmnd) +{ + struct uas_cmd_info *cmdinfo; + + if (!cmnd) + return; + + cmdinfo = (void *)&cmnd->SCp; + + if (cmdinfo->state & SUBMIT_CMD_URB) + usb_free_urb(cmdinfo->cmd_urb); + + /* data urbs may have never gotten their submit flag set */ + if (!(cmdinfo->state & DATA_IN_URB_INFLIGHT)) + usb_free_urb(cmdinfo->data_in_urb); + if (!(cmdinfo->state & DATA_OUT_URB_INFLIGHT)) + usb_free_urb(cmdinfo->data_out_urb); } static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) @@ -283,19 +236,14 @@ 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_ONCE(!spin_is_locked(&devinfo->lock)); + lockdep_assert_held(&devinfo->lock); if (cmdinfo->state & (COMMAND_INFLIGHT | DATA_IN_URB_INFLIGHT | DATA_OUT_URB_INFLIGHT | - UNLINK_DATA_URBS)) + COMMAND_ABORTED)) return -EBUSY; - 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) - scmd_printk(KERN_INFO, cmnd, "abort completed\n"); - list_del(&cmdinfo->list); + devinfo->cmnd[uas_get_tag(cmnd) - 1] = NULL; + uas_free_unsubmitted_urbs(cmnd); cmnd->scsi_done(cmnd); return 0; } @@ -318,63 +266,48 @@ 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 = (struct uas_dev_info *)shost->hostdata; + struct urb *data_in_urb = NULL; + struct urb *data_out_urb = NULL; struct scsi_cmnd *cmnd; struct uas_cmd_info *cmdinfo; unsigned long flags; - u16 tag; + unsigned int idx; + + spin_lock_irqsave(&devinfo->lock, flags); + + if (devinfo->resetting) + goto out; if (urb->status) { - if (urb->status == -ENOENT) { - dev_err(&urb->dev->dev, "stat urb: killed, stream %d\n", - urb->stream_id); - } else { + if (urb->status != -ENOENT && urb->status != -ECONNRESET) { dev_err(&urb->dev->dev, "stat urb: status %d\n", urb->status); } - usb_free_urb(urb); - return; + goto out; } - if (devinfo->resetting) { - usb_free_urb(urb); - return; + idx = be16_to_cpup(&iu->tag) - 1; + if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) { + dev_err(&urb->dev->dev, + "stat urb: no pending cmd for tag %d\n", idx + 1); + goto out; } - spin_lock_irqsave(&devinfo->lock, flags); - tag = be16_to_cpup(&iu->tag) - 1; - if (tag == 0) - cmnd = devinfo->cmnd; - else - cmnd = scsi_host_find_tag(shost, tag - 1); - - 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)); - } - usb_free_urb(urb); - spin_unlock_irqrestore(&devinfo->lock, flags); - return; + cmnd = devinfo->cmnd[idx]; + cmdinfo = (void *)&cmnd->SCp; + + if (!(cmdinfo->state & COMMAND_INFLIGHT)) { + uas_log_cmd_state(cmnd, "unexpected status cmplt", 0); + goto out; } - cmdinfo = (void *)&cmnd->SCp; switch (iu->iu_id) { case IU_ID_STATUS: - if (devinfo->cmnd == cmnd) - devinfo->cmnd = NULL; - - if (urb->actual_length < 16) - devinfo->uas_sense_old = 1; - if (devinfo->uas_sense_old) - uas_sense_old(urb, cmnd); - else - uas_sense(urb, cmnd); + uas_sense(urb, cmnd); if (cmnd->result != 0) { /* cancel data transfers on error */ - uas_unlink_data_urbs(devinfo, cmdinfo, &flags); + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); } cmdinfo->state &= ~COMMAND_INFLIGHT; uas_try_complete(cmnd, __func__); @@ -382,7 +315,7 @@ static void uas_stat_cmplt(struct urb *urb) 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"); + uas_log_cmd_state(cmnd, "unexpected read rdy", 0); break; } uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB); @@ -390,17 +323,37 @@ static void uas_stat_cmplt(struct urb *urb) 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"); + uas_log_cmd_state(cmnd, "unexpected write rdy", 0); break; } uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); break; + case IU_ID_RESPONSE: + uas_log_cmd_state(cmnd, "unexpected response iu", + ((struct response_iu *)iu)->response_code); + /* Error, cancel data transfers */ + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); + cmdinfo->state &= ~COMMAND_INFLIGHT; + cmnd->result = DID_ERROR << 16; + uas_try_complete(cmnd, __func__); + break; default: - scmd_printk(KERN_ERR, cmnd, - "Bogus IU (%d) received on status pipe\n", iu->iu_id); + uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id); } +out: usb_free_urb(urb); spin_unlock_irqrestore(&devinfo->lock, flags); + + /* Unlinking of data urbs must be done without holding the lock */ + if (data_in_urb) { + usb_unlink_urb(data_in_urb); + usb_put_urb(data_in_urb); + } + if (data_out_urb) { + usb_unlink_urb(data_out_urb); + usb_put_urb(data_out_urb); + } } static void uas_data_cmplt(struct urb *urb) @@ -412,57 +365,69 @@ static void uas_data_cmplt(struct urb *urb) unsigned long flags; spin_lock_irqsave(&devinfo->lock, flags); + if (cmdinfo->data_in_urb == urb) { sdb = scsi_in(cmnd); cmdinfo->state &= ~DATA_IN_URB_INFLIGHT; + cmdinfo->data_in_urb = NULL; } else if (cmdinfo->data_out_urb == urb) { sdb = scsi_out(cmnd); cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT; + cmdinfo->data_out_urb = NULL; } 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); - } + goto out; + } + + if (devinfo->resetting) + goto out; + + /* Data urbs should not complete before the cmd urb is submitted */ + if (cmdinfo->state & SUBMIT_CMD_URB) { + uas_log_cmd_state(cmnd, "unexpected data cmplt", 0); + goto out; + } + + if (urb->status) { + if (urb->status != -ENOENT && urb->status != -ECONNRESET) + uas_log_cmd_state(cmnd, "data cmplt err", urb->status); /* error: no data transfered */ sdb->resid = sdb->length; } else { sdb->resid = sdb->length - urb->actual_length; } uas_try_complete(cmnd, __func__); +out: + usb_free_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) + dev_err(&urb->dev->dev, "cmd cmplt err %d\n", urb->status); - 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, enum dma_data_direction dir) { struct usb_device *udev = devinfo->udev; + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct urb *urb = usb_alloc_urb(0, gfp); struct scsi_data_buffer *sdb = (dir == DMA_FROM_DEVICE) ? scsi_in(cmnd) : scsi_out(cmnd); + unsigned int pipe = (dir == DMA_FROM_DEVICE) + ? devinfo->data_in_pipe : devinfo->data_out_pipe; if (!urb) goto out; usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt, cmnd); - urb->stream_id = stream_id; + urb->stream_id = cmdinfo->stream; urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; urb->sg = sdb->table.sgl; out: @@ -470,9 +435,10 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, } static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, - struct Scsi_Host *shost, u16 stream_id) + struct scsi_cmnd *cmnd) { struct usb_device *udev = devinfo->udev; + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct urb *urb = usb_alloc_urb(0, gfp); struct sense_iu *iu; @@ -484,8 +450,8 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto free; usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu), - uas_stat_cmplt, shost); - urb->stream_id = stream_id; + uas_stat_cmplt, cmnd->device->host); + urb->stream_id = cmdinfo->stream; urb->transfer_flags |= URB_FREE_BUFFER; out: return urb; @@ -515,17 +481,14 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto free; iu->iu_id = IU_ID_COMMAND; - if (blk_rq_tagged(cmnd->request)) - iu->tag = cpu_to_be16(cmnd->request->tag + 2); - else - iu->tag = cpu_to_be16(1); + iu->tag = cpu_to_be16(uas_get_tag(cmnd)); iu->prio_attr = UAS_SIMPLE_TAG; iu->len = len; int_to_scsilun(sdev->lun, &iu->lun); memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len); usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu) + len, - uas_cmd_cmplt, cmnd); + uas_cmd_cmplt, NULL); urb->transfer_flags |= URB_FREE_BUFFER; out: return urb; @@ -534,81 +497,26 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, return NULL; } -static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp, - u8 function, u16 stream_id) -{ - struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; - struct usb_device *udev = devinfo->udev; - struct urb *urb = usb_alloc_urb(0, gfp); - struct task_mgmt_iu *iu; - int err = -ENOMEM; - - if (!urb) - goto err; - - iu = kzalloc(sizeof(*iu), gfp); - if (!iu) - goto err; - - iu->iu_id = IU_ID_TASK_MGMT; - iu->tag = cpu_to_be16(stream_id); - int_to_scsilun(cmnd->device->lun, &iu->lun); - - iu->function = function; - switch (function) { - case TMF_ABORT_TASK: - if (blk_rq_tagged(cmnd->request)) - iu->task_tag = cpu_to_be16(cmnd->request->tag + 2); - else - iu->task_tag = cpu_to_be16(1); - break; - } - - usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu), - 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) { - usb_unanchor_urb(urb); - uas_log_cmd_state(cmnd, __func__); - scmd_printk(KERN_ERR, cmnd, "task submission err %d\n", err); - goto err; - } - - return 0; - -err: - usb_free_urb(urb); - return err; -} - /* * Why should I request the Status IU before sending the Command IU? Spec * says to, but also says the device may receive them in any order. Seems * daft to me. */ -static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, - gfp_t gfp, unsigned int stream) +static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp) { - struct Scsi_Host *shost = cmnd->device->host; - struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + struct uas_dev_info *devinfo = cmnd->device->hostdata; struct urb *urb; int err; - urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream); + urb = uas_alloc_sense_urb(devinfo, gfp, cmnd); if (!urb) 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 error %d stream %d\n", - err, stream); + uas_log_cmd_state(cmnd, "sense submit err", err); usb_free_urb(urb); return NULL; } @@ -622,9 +530,9 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct urb *urb; int err; - WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); + lockdep_assert_held(&devinfo->lock); if (cmdinfo->state & SUBMIT_STATUS_URB) { - urb = uas_submit_sense_urb(cmnd, gfp, cmdinfo->stream); + urb = uas_submit_sense_urb(cmnd, gfp); if (!urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~SUBMIT_STATUS_URB; @@ -632,8 +540,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & ALLOC_DATA_IN_URB) { cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp, - devinfo->data_in_pipe, cmdinfo->stream, - cmnd, DMA_FROM_DEVICE); + cmnd, DMA_FROM_DEVICE); if (!cmdinfo->data_in_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_DATA_IN_URB; @@ -644,10 +551,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, 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 error %d stream %d\n", - err, cmdinfo->data_in_urb->stream_id); + uas_log_cmd_state(cmnd, "data in submit err", err); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_IN_URB; @@ -656,8 +560,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & ALLOC_DATA_OUT_URB) { cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp, - devinfo->data_out_pipe, cmdinfo->stream, - cmnd, DMA_TO_DEVICE); + cmnd, DMA_TO_DEVICE); if (!cmdinfo->data_out_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_DATA_OUT_URB; @@ -668,10 +571,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, 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 error %d stream %d\n", - err, cmdinfo->data_out_urb->stream_id); + uas_log_cmd_state(cmnd, "data out submit err", err); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_OUT_URB; @@ -690,9 +590,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, 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 error %d\n", err); + uas_log_cmd_state(cmnd, "cmd submit err", err); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->cmd_urb = NULL; @@ -710,10 +608,24 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo = sdev->hostdata; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; unsigned long flags; + unsigned int stream; int err; BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); + /* Re-check scsi_block_requests now that we've the host-lock */ + if (cmnd->device->host->host_self_blocked) + return SCSI_MLQUEUE_DEVICE_BUSY; + + if ((devinfo->flags & US_FL_NO_ATA_1X) && + (cmnd->cmnd[0] == ATA_12 || cmnd->cmnd[0] == ATA_16)) { + memcpy(cmnd->sense_buffer, usb_stor_sense_invalidCDB, + sizeof(usb_stor_sense_invalidCDB)); + cmnd->result = SAM_STAT_CHECK_CONDITION; + cmnd->scsi_done(cmnd); + return 0; + } + spin_lock_irqsave(&devinfo->lock, flags); if (devinfo->resetting) { @@ -723,24 +635,17 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, return 0; } - if (devinfo->cmnd) { + stream = uas_get_tag(cmnd); + if (devinfo->cmnd[stream - 1]) { 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 { - devinfo->cmnd = cmnd; - cmdinfo->stream = 1; - } - cmnd->scsi_done = done; - cmdinfo->state = SUBMIT_STATUS_URB | - ALLOC_CMD_URB | SUBMIT_CMD_URB; + memset(cmdinfo, 0, sizeof(*cmdinfo)); + cmdinfo->stream = stream; + cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; switch (cmnd->sc_data_direction) { case DMA_FROM_DEVICE: @@ -769,123 +674,54 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, uas_add_work(cmdinfo); } - list_add_tail(&cmdinfo->list, &devinfo->inflight_list); + devinfo->cmnd[stream - 1] = cmnd; spin_unlock_irqrestore(&devinfo->lock, flags); return 0; } static DEF_SCSI_QCMD(uas_queuecommand) -static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, - const char *fname, u8 function) +/* + * For now we do not support actually sending an abort to the device, so + * this eh always fails. Still we must define it to make sure that we've + * dropped all references to the cmnd in question once this function exits. + */ +static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) { - struct Scsi_Host *shost = cmnd->device->host; - struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; - u16 tag = devinfo->qdepth; + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; + struct urb *data_in_urb = NULL; + struct urb *data_out_urb = NULL; 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; - } + uas_log_cmd_state(cmnd, __func__, 0); - 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; - } + /* Ensure that try_complete does not call scsi_done */ + cmdinfo->state |= COMMAND_ABORTED; - devinfo->running_task = 1; - memset(&devinfo->response, 0, sizeof(devinfo->response)); - sense_urb = uas_submit_sense_urb(cmnd, GFP_ATOMIC, - 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)) { - 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); + /* Drop all refs to this cmnd, kill data urbs to break their ref */ + devinfo->cmnd[uas_get_tag(cmnd) - 1] = NULL; + if (cmdinfo->state & DATA_IN_URB_INFLIGHT) + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); - 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; - } + uas_free_unsubmitted_urbs(cmnd); - 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); - 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); - result = FAILED; - } spin_unlock_irqrestore(&devinfo->lock, flags); - return result; -} - -static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) -{ - struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; - struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; - unsigned long flags; - int ret; - - spin_lock_irqsave(&devinfo->lock, flags); - - if (devinfo->resetting) { - spin_unlock_irqrestore(&devinfo->lock, flags); - return FAILED; + if (data_in_urb) { + usb_kill_urb(data_in_urb); + usb_put_urb(data_in_urb); } - - 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 { - uas_unlink_data_urbs(devinfo, cmdinfo, &flags); - uas_try_complete(cmnd, __func__); - spin_unlock_irqrestore(&devinfo->lock, flags); - ret = SUCCESS; + if (data_out_urb) { + usb_kill_urb(data_out_urb); + usb_put_urb(data_out_urb); } - return ret; -} -static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) -{ - sdev_printk(KERN_INFO, cmnd->device, "%s\n", __func__); - return uas_eh_task_mgmt(cmnd, "LOGICAL UNIT RESET", - TMF_LOGICAL_UNIT_RESET); + return FAILED; } static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) @@ -893,6 +729,7 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) struct scsi_device *sdev = cmnd->device; struct uas_dev_info *devinfo = sdev->hostdata; struct usb_device *udev = devinfo->udev; + unsigned long flags; int err; err = usb_lock_device_for_reset(udev, devinfo->intf); @@ -903,19 +740,27 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) } shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__); + + spin_lock_irqsave(&devinfo->lock, flags); devinfo->resetting = 1; - uas_abort_inflight(devinfo, DID_RESET, __func__); + spin_unlock_irqrestore(&devinfo->lock, flags); + 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); + uas_zap_pending(devinfo, DID_RESET); + err = usb_reset_device(udev); + + spin_lock_irqsave(&devinfo->lock, flags); devinfo->resetting = 0; + spin_unlock_irqrestore(&devinfo->lock, flags); usb_unlock_device(udev); if (err) { - shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__); + shost_printk(KERN_INFO, sdev->host, "%s FAILED err %d\n", + __func__, err); return FAILED; } @@ -950,6 +795,10 @@ static int uas_slave_alloc(struct scsi_device *sdev) static int uas_slave_configure(struct scsi_device *sdev) { struct uas_dev_info *devinfo = sdev->hostdata; + + if (devinfo->flags & US_FL_NO_REPORT_OPCODES) + sdev->no_report_opcodes = 1; + scsi_set_tag_type(sdev, MSG_ORDERED_TAG); scsi_activate_tcq(sdev, devinfo->qdepth - 2); return 0; @@ -962,7 +811,6 @@ static struct scsi_host_template uas_host_template = { .slave_alloc = uas_slave_alloc, .slave_configure = uas_slave_configure, .eh_abort_handler = uas_eh_abort_handler, - .eh_device_reset_handler = uas_eh_device_reset_handler, .eh_bus_reset_handler = uas_eh_bus_reset_handler, .can_queue = 65536, /* Is there a limit on the _host_ ? */ .this_id = -1, @@ -970,6 +818,13 @@ static struct scsi_host_template uas_host_template = { .cmd_per_lun = 1, /* until we override it */ .skip_settle_delay = 1, .ordered_tag = 1, + + /* + * The uas drivers expects tags not to be bigger than the maximum + * per-device queue depth, which is not true with the blk-mq tag + * allocator. + */ + .disable_blk_mq = true, }; #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ @@ -982,8 +837,6 @@ 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 */ - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, 0xaa) }, { } }; MODULE_DEVICE_TABLE(usb, uas_usb_ids); @@ -1009,9 +862,6 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo) struct usb_device *udev = devinfo->udev; int r; - devinfo->uas_sense_old = 0; - devinfo->cmnd = NULL; - r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps); if (r) return r; @@ -1025,12 +875,12 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo) devinfo->data_out_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(&eps[3]->desc)); - if (udev->speed != USB_SPEED_SUPER) { - devinfo->qdepth = 256; + if (udev->speed < USB_SPEED_SUPER) { + devinfo->qdepth = 32; devinfo->use_streams = 0; } else { devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, - 3, 256, GFP_NOIO); + 3, MAX_CMNDS, GFP_NOIO); if (devinfo->qdepth < 0) return devinfo->qdepth; devinfo->use_streams = 1; @@ -1078,15 +928,14 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->intf = intf; devinfo->udev = udev; devinfo->resetting = 0; - devinfo->running_task = 0; devinfo->shutdown = 0; + devinfo->flags = id->driver_info; + usb_stor_adjust_quirks(udev, &devinfo->flags); init_usb_anchor(&devinfo->cmd_urbs); init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->data_urbs); spin_lock_init(&devinfo->lock); INIT_WORK(&devinfo->work, uas_do_work); - INIT_LIST_HEAD(&devinfo->inflight_list); - INIT_LIST_HEAD(&devinfo->dead_list); result = uas_configure_endpoints(devinfo); if (result) @@ -1114,6 +963,54 @@ set_alt0: return result; } +static int uas_cmnd_list_empty(struct uas_dev_info *devinfo) +{ + unsigned long flags; + int i, r = 1; + + spin_lock_irqsave(&devinfo->lock, flags); + + for (i = 0; i < devinfo->qdepth; i++) { + if (devinfo->cmnd[i]) { + r = 0; /* Not empty */ + break; + } + } + + spin_unlock_irqrestore(&devinfo->lock, flags); + + return r; +} + +/* + * Wait for any pending cmnds to complete, on usb-2 sense_urbs may temporarily + * get empty while there still is more work to do due to sense-urbs completing + * with a READ/WRITE_READY iu code, so keep waiting until the list gets empty. + */ +static int uas_wait_for_pending_cmnds(struct uas_dev_info *devinfo) +{ + unsigned long start_time; + int r; + + start_time = jiffies; + do { + flush_work(&devinfo->work); + + r = usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000); + if (r == 0) + return -ETIME; + + r = usb_wait_anchor_empty_timeout(&devinfo->data_urbs, 500); + if (r == 0) + return -ETIME; + + if (time_after(jiffies, start_time + 5 * HZ)) + return -ETIME; + } while (!uas_cmnd_list_empty(devinfo)); + + return 0; +} + static int uas_pre_reset(struct usb_interface *intf) { struct Scsi_Host *shost = usb_get_intfdata(intf); @@ -1128,10 +1025,9 @@ static int uas_pre_reset(struct usb_interface *intf) 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) { + if (uas_wait_for_pending_cmnds(devinfo) != 0) { shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); + scsi_unblock_requests(shost); return 1; } @@ -1145,13 +1041,16 @@ static int uas_post_reset(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; + int err; if (devinfo->shutdown) return 0; - if (uas_configure_endpoints(devinfo) != 0) { + err = uas_configure_endpoints(devinfo); + if (err) { shost_printk(KERN_ERR, shost, - "%s: alloc streams error after reset", __func__); + "%s: alloc streams error %d after reset", + __func__, err); return 1; } @@ -1169,9 +1068,7 @@ 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) { + if (uas_wait_for_pending_cmnds(devinfo) != 0) { shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); return -ETIME; } @@ -1189,10 +1086,13 @@ 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; + int err; - if (uas_configure_endpoints(devinfo) != 0) { + err = uas_configure_endpoints(devinfo); + if (err) { shost_printk(KERN_ERR, shost, - "%s: alloc streams error after reset", __func__); + "%s: alloc streams error %d after reset", + __func__, err); return -EIO; } @@ -1207,14 +1107,18 @@ static void uas_disconnect(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; + spin_lock_irqsave(&devinfo->lock, flags); devinfo->resetting = 1; + spin_unlock_irqrestore(&devinfo->lock, flags); + 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); + uas_zap_pending(devinfo, DID_NO_CONNECT); + scsi_remove_host(shost); uas_free_streams(devinfo); scsi_host_put(shost); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 80a5b366255f..11c7a9676441 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -101,6 +101,12 @@ UNUSUAL_DEV( 0x03f0, 0x4002, 0x0001, 0x0001, "PhotoSmart R707", USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +UNUSUAL_DEV( 0x03f3, 0x0001, 0x0000, 0x9999, + "Adaptec", + "USBConnect 2000", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + /* Reported by Sebastian Kapfer <sebastian_kapfer@gmx.net> * and Olaf Hering <olh@suse.de> (different bcd's, same vendor/product) * for USB floppies that need the SINGLE_LUN enforcement. @@ -486,18 +492,24 @@ UNUSUAL_DEV( 0x04e6, 0x000a, 0x0200, 0x0200, "eUSB CompactFlash Adapter", USB_SC_8020, USB_PR_CB, NULL, 0), -UNUSUAL_DEV( 0x04e6, 0x000B, 0x0100, 0x0100, +UNUSUAL_DEV( 0x04e6, 0x000b, 0x0100, 0x0100, "Shuttle", "eUSCSI Bridge", USB_SC_SCSI, USB_PR_BULK, usb_stor_euscsi_init, US_FL_SCM_MULT_TARG ), -UNUSUAL_DEV( 0x04e6, 0x000C, 0x0100, 0x0100, +UNUSUAL_DEV( 0x04e6, 0x000c, 0x0100, 0x0100, "Shuttle", "eUSCSI Bridge", USB_SC_SCSI, USB_PR_BULK, usb_stor_euscsi_init, US_FL_SCM_MULT_TARG ), +UNUSUAL_DEV( 0x04e6, 0x000f, 0x0000, 0x9999, + "SCM Microsystems", + "eUSB SCSI Adapter (Bus Powered)", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + UNUSUAL_DEV( 0x04e6, 0x0101, 0x0200, 0x0200, "Shuttle", "CD-RW Device", @@ -741,6 +753,12 @@ UNUSUAL_DEV( 0x059b, 0x0001, 0x0100, 0x0100, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), +UNUSUAL_DEV( 0x059b, 0x0040, 0x0100, 0x0100, + "Iomega", + "Jaz USB Adapter", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + /* Reported by <Hendryk.Pfeiffer@gmx.de> */ UNUSUAL_DEV( 0x059f, 0x0643, 0x0000, 0x0000, "LaCie", @@ -922,6 +940,12 @@ UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +UNUSUAL_DEV( 0x06ca, 0x2003, 0x0100, 0x0100, + "Newer Technology", + "uSCSI", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + /* Reported by Adrian Pilchowiec <adi1981@epf.pl> */ UNUSUAL_DEV( 0x071b, 0x3203, 0x0000, 0x0000, "RockChip", @@ -1081,6 +1105,13 @@ UNUSUAL_DEV( 0x0840, 0x0085, 0x0001, 0x0001, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Supplied with some Castlewood ORB removable drives */ +UNUSUAL_DEV( 0x084b, 0xa001, 0x0000, 0x9999, + "Castlewood Systems", + "USB to SCSI cable", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + /* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>. * Flag will support Bulk devices which use a standards-violating 32-byte * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with @@ -1113,6 +1144,18 @@ UNUSUAL_DEV( 0x0851, 0x1543, 0x0200, 0x0200, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE), +UNUSUAL_DEV( 0x085a, 0x0026, 0x0100, 0x0133, + "Xircom", + "PortGear USB-SCSI (Mac USB Dock)", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +UNUSUAL_DEV( 0x085a, 0x0028, 0x0100, 0x0133, + "Xircom", + "PortGear USB to SCSI Converter", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + /* Submitted by Jan De Luyck <lkml@kcore.org> */ UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000, "CITIZEN", @@ -1952,6 +1995,14 @@ UNUSUAL_DEV( 0x152d, 0x2329, 0x0100, 0x0100, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE | US_FL_SANE_SENSE ), +/* Entrega Technologies U1-SC25 (later Xircom PortGear PGSCSI) + * and Mac USB Dock USB-SCSI */ +UNUSUAL_DEV( 0x1645, 0x0007, 0x0100, 0x0133, + "Entrega Technologies", + "USB to SCSI Converter", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + /* Reported by Robert Schedel <r.schedel@yahoo.de> * Note: this is a 'super top' device like the above 14cd/6600 device */ UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201, @@ -1974,6 +2025,12 @@ UNUSUAL_DEV( 0x177f, 0x0400, 0x0000, 0x0000, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_BULK_IGNORE_TAG | US_FL_MAX_SECTORS_64 ), +UNUSUAL_DEV( 0x1822, 0x0001, 0x0000, 0x9999, + "Ariston Technologies", + "iConnect USB to SCSI adapter", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + /* Reported by Hans de Goede <hdegoede@redhat.com> * These Appotech controllers are found in Picture Frames, they provide a * (buggy) emulation of a cdrom drive which contains the windows software @@ -2019,6 +2076,13 @@ UNUSUAL_DEV( 0x1e74, 0x4621, 0x0000, 0x0000, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_BULK_IGNORE_TAG | US_FL_MAX_SECTORS_64 ), +/* Supplied with some Castlewood ORB removable drives */ +UNUSUAL_DEV( 0x2027, 0xa001, 0x0000, 0x9999, + "Double-H Technology", + "USB to SCSI Intelligent Cable", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001, "ST", "2A", diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 7244444df8ee..8511b54a65d9 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -40,13 +40,38 @@ * 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), - */ +/* https://bugzilla.kernel.org/show_bug.cgi?id=79511 */ +UNUSUAL_DEV(0x0bc2, 0x2312, 0x0000, 0x9999, + "Seagate", + "Expansion Desk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* https://bbs.archlinux.org/viewtopic.php?id=183190 */ +UNUSUAL_DEV(0x0bc2, 0x3312, 0x0000, 0x9999, + "Seagate", + "Expansion Desk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* https://bbs.archlinux.org/viewtopic.php?id=183190 */ +UNUSUAL_DEV(0x0bc2, 0xab20, 0x0000, 0x9999, + "Seagate", + "Backup+ BK", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* Reported-by: Claudio Bizzarri <claudio.bizzarri@gmail.com> */ +UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999, + "JMicron", + "JMS567", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + +/* Most ASM1051 based devices have issues with uas, blacklist them all */ +/* Reported-by: Hans de Goede <hdegoede@redhat.com> */ +UNUSUAL_DEV(0x174c, 0x5106, 0x0000, 0x9999, + "ASMedia", + "ASM1051", + 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 f1c96261a501..9d66ce62542e 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -98,7 +98,7 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); /* The vendor name should be kept at eight characters or less, and * the product name should be kept at 16 characters or less. If a device * has the US_FL_FIX_INQUIRY flag, then the vendor and product names - * normally generated by a device thorugh the INQUIRY response will be + * normally generated by a device through the INQUIRY response will be * taken from this list, and this is the reason for the above size * restriction. However, if the flag is not present, then you * are free to use as many characters as you like. @@ -347,14 +347,16 @@ static int usb_stor_control_thread(void * __us) */ else if (us->srb->device->id && !(us->fflags & US_FL_SCM_MULT_TARG)) { - usb_stor_dbg(us, "Bad target number (%d:%d)\n", - us->srb->device->id, us->srb->device->lun); + usb_stor_dbg(us, "Bad target number (%d:%llu)\n", + us->srb->device->id, + us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } else if (us->srb->device->lun > us->max_lun) { - usb_stor_dbg(us, "Bad LUN (%d:%d)\n", - us->srb->device->id, us->srb->device->lun); + usb_stor_dbg(us, "Bad LUN (%d:%llu)\n", + us->srb->device->id, + us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } @@ -476,7 +478,8 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT | US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 | - US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE); + US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE | + US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES); p = quirks; while (*p) { @@ -514,6 +517,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) case 'e': f |= US_FL_NO_READ_CAPACITY_16; break; + case 'f': + f |= US_FL_NO_REPORT_OPCODES; + break; case 'h': f |= US_FL_CAPACITY_HEURISTICS; break; @@ -541,6 +547,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) case 's': f |= US_FL_SINGLE_LUN; break; + case 't': + f |= US_FL_NO_ATA_1X; + break; case 'u': f |= US_FL_IGNORE_UAS; break; @@ -981,6 +990,14 @@ int usb_stor_probe2(struct us_data *us) if (!(us->fflags & US_FL_SCM_MULT_TARG)) us_to_host(us)->max_id = 1; + /* + * Like Windows, we won't store the LUN bits in CDB[1] for SCSI-2 + * devices using the Bulk-Only transport (even though this violates + * the SCSI spec). + */ + if (us->transport == usb_stor_Bulk_transport) + us_to_host(us)->no_scsi2_lun_in_cdb = 1; + /* Find the endpoints and calculate pipe values */ result = get_pipes(us); if (result) diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig new file mode 100644 index 000000000000..bd99e9e47e50 --- /dev/null +++ b/drivers/usb/usbip/Kconfig @@ -0,0 +1,41 @@ +config USBIP_CORE + tristate "USB/IP support" + depends on USB && NET + ---help--- + This enables pushing USB packets over IP to allow remote + machines direct access to USB devices. It provides the + USB/IP core that is required by both drivers. + + For more details, and to get the userspace utility + programs, please see <http://usbip.sourceforge.net/>. + + To compile this as a module, choose M here: the module will + be called usbip-core. + + If unsure, say N. + +config USBIP_VHCI_HCD + tristate "VHCI hcd" + depends on USBIP_CORE + ---help--- + This enables the USB/IP virtual host controller driver, + which is run on the remote machine. + + To compile this driver as a module, choose M here: the + module will be called vhci-hcd. + +config USBIP_HOST + tristate "Host driver" + depends on USBIP_CORE + ---help--- + This enables the USB/IP host driver, which is run on the + machine that is sharing the USB devices. + + To compile this driver as a module, choose M here: the + module will be called usbip-host. + +config USBIP_DEBUG + bool "Debug messages for USB/IP" + depends on USBIP_CORE + ---help--- + This enables the debug messages from the USB/IP drivers. diff --git a/drivers/usb/usbip/Makefile b/drivers/usb/usbip/Makefile new file mode 100644 index 000000000000..9ecd61545be1 --- /dev/null +++ b/drivers/usb/usbip/Makefile @@ -0,0 +1,10 @@ +ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG + +obj-$(CONFIG_USBIP_CORE) += usbip-core.o +usbip-core-y := usbip_common.o usbip_event.o + +obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o +vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o + +obj-$(CONFIG_USBIP_HOST) += usbip-host.o +usbip-host-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o diff --git a/drivers/usb/usbip/README b/drivers/usb/usbip/README new file mode 100644 index 000000000000..41a2cf2e77a6 --- /dev/null +++ b/drivers/usb/usbip/README @@ -0,0 +1,7 @@ +TODO: + - more discussion about the protocol + - testing + - review of the userspace interface + - document the protocol + +Please send patches for this code to Greg Kroah-Hartman <greg@kroah.com> diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h new file mode 100644 index 000000000000..266e2b0ce9a8 --- /dev/null +++ b/drivers/usb/usbip/stub.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __USBIP_STUB_H +#define __USBIP_STUB_H + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/wait.h> + +#define STUB_BUSID_OTHER 0 +#define STUB_BUSID_REMOV 1 +#define STUB_BUSID_ADDED 2 +#define STUB_BUSID_ALLOC 3 + +struct stub_device { + struct usb_interface *interface; + struct usb_device *udev; + + struct usbip_device ud; + __u32 devid; + + /* + * stub_priv preserves private data of each urb. + * It is allocated as stub_priv_cache and assigned to urb->context. + * + * stub_priv is always linked to any one of 3 lists; + * priv_init: linked to this until the comletion of a urb. + * priv_tx : linked to this after the completion of a urb. + * priv_free: linked to this after the sending of the result. + * + * Any of these list operations should be locked by priv_lock. + */ + spinlock_t priv_lock; + struct list_head priv_init; + struct list_head priv_tx; + struct list_head priv_free; + + /* see comments for unlinking in stub_rx.c */ + struct list_head unlink_tx; + struct list_head unlink_free; + + wait_queue_head_t tx_waitq; +}; + +/* private data into urb->priv */ +struct stub_priv { + unsigned long seqnum; + struct list_head list; + struct stub_device *sdev; + struct urb *urb; + + int unlinking; +}; + +struct stub_unlink { + unsigned long seqnum; + struct list_head list; + __u32 status; +}; + +/* same as SYSFS_BUS_ID_SIZE */ +#define BUSID_SIZE 32 + +struct bus_id_priv { + char name[BUSID_SIZE]; + char status; + int interf_count; + struct stub_device *sdev; + struct usb_device *udev; + char shutdown_busid; +}; + +/* stub_priv is allocated from stub_priv_cache */ +extern struct kmem_cache *stub_priv_cache; + +/* stub_dev.c */ +extern struct usb_device_driver stub_driver; + +/* stub_main.c */ +struct bus_id_priv *get_busid_priv(const char *busid); +int del_match_busid(char *busid); +void stub_device_cleanup_urbs(struct stub_device *sdev); + +/* stub_rx.c */ +int stub_rx_loop(void *data); + +/* stub_tx.c */ +void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status); +void stub_complete(struct urb *urb); +int stub_tx_loop(void *data); + +#endif /* __USBIP_STUB_H */ diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c new file mode 100644 index 000000000000..fac20e0434c0 --- /dev/null +++ b/drivers/usb/usbip/stub_dev.c @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/device.h> +#include <linux/file.h> +#include <linux/kthread.h> +#include <linux/module.h> + +#include "usbip_common.h" +#include "stub.h" + +/* + * usbip_status shows the status of usbip-host as long as this driver is bound + * to the target device. + */ +static ssize_t usbip_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stub_device *sdev = dev_get_drvdata(dev); + int status; + + if (!sdev) { + dev_err(dev, "sdev is null\n"); + return -ENODEV; + } + + spin_lock_irq(&sdev->ud.lock); + status = sdev->ud.status; + spin_unlock_irq(&sdev->ud.lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} +static DEVICE_ATTR_RO(usbip_status); + +/* + * usbip_sockfd gets a socket descriptor of an established TCP connection that + * is used to transfer usbip requests by kernel threads. -1 is a magic number + * by which usbip connection is finished. + */ +static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stub_device *sdev = dev_get_drvdata(dev); + int sockfd = 0; + struct socket *socket; + int rv; + + if (!sdev) { + dev_err(dev, "sdev is null\n"); + return -ENODEV; + } + + rv = sscanf(buf, "%d", &sockfd); + if (rv != 1) + return -EINVAL; + + if (sockfd != -1) { + int err; + + dev_info(dev, "stub up\n"); + + spin_lock_irq(&sdev->ud.lock); + + if (sdev->ud.status != SDEV_ST_AVAILABLE) { + dev_err(dev, "not ready\n"); + goto err; + } + + socket = sockfd_lookup(sockfd, &err); + if (!socket) + goto err; + + sdev->ud.tcp_socket = socket; + + spin_unlock_irq(&sdev->ud.lock); + + sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud, + "stub_rx"); + sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud, + "stub_tx"); + + spin_lock_irq(&sdev->ud.lock); + sdev->ud.status = SDEV_ST_USED; + spin_unlock_irq(&sdev->ud.lock); + + } else { + dev_info(dev, "stub down\n"); + + spin_lock_irq(&sdev->ud.lock); + if (sdev->ud.status != SDEV_ST_USED) + goto err; + + spin_unlock_irq(&sdev->ud.lock); + + usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN); + } + + return count; + +err: + spin_unlock_irq(&sdev->ud.lock); + return -EINVAL; +} +static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd); + +static int stub_add_files(struct device *dev) +{ + int err = 0; + + err = device_create_file(dev, &dev_attr_usbip_status); + if (err) + goto err_status; + + err = device_create_file(dev, &dev_attr_usbip_sockfd); + if (err) + goto err_sockfd; + + err = device_create_file(dev, &dev_attr_usbip_debug); + if (err) + goto err_debug; + + return 0; + +err_debug: + device_remove_file(dev, &dev_attr_usbip_sockfd); +err_sockfd: + device_remove_file(dev, &dev_attr_usbip_status); +err_status: + return err; +} + +static void stub_remove_files(struct device *dev) +{ + device_remove_file(dev, &dev_attr_usbip_status); + device_remove_file(dev, &dev_attr_usbip_sockfd); + device_remove_file(dev, &dev_attr_usbip_debug); +} + +static void stub_shutdown_connection(struct usbip_device *ud) +{ + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + + /* + * When removing an exported device, kernel panic sometimes occurred + * and then EIP was sk_wait_data of stub_rx thread. Is this because + * sk_wait_data returned though stub_rx thread was already finished by + * step 1? + */ + if (ud->tcp_socket) { + dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n", + ud->tcp_socket); + kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); + } + + /* 1. stop threads */ + if (ud->tcp_rx) { + kthread_stop_put(ud->tcp_rx); + ud->tcp_rx = NULL; + } + if (ud->tcp_tx) { + kthread_stop_put(ud->tcp_tx); + ud->tcp_tx = NULL; + } + + /* + * 2. close the socket + * + * tcp_socket is freed after threads are killed so that usbip_xmit does + * not touch NULL socket. + */ + if (ud->tcp_socket) { + sockfd_put(ud->tcp_socket); + ud->tcp_socket = NULL; + } + + /* 3. free used data */ + stub_device_cleanup_urbs(sdev); + + /* 4. free stub_unlink */ + { + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { + list_del(&unlink->list); + kfree(unlink); + } + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, + list) { + list_del(&unlink->list); + kfree(unlink); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + } +} + +static void stub_device_reset(struct usbip_device *ud) +{ + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + struct usb_device *udev = sdev->udev; + int ret; + + dev_dbg(&udev->dev, "device reset"); + + ret = usb_lock_device_for_reset(udev, sdev->interface); + if (ret < 0) { + dev_err(&udev->dev, "lock for reset\n"); + spin_lock_irq(&ud->lock); + ud->status = SDEV_ST_ERROR; + spin_unlock_irq(&ud->lock); + return; + } + + /* try to reset the device */ + ret = usb_reset_device(udev); + usb_unlock_device(udev); + + spin_lock_irq(&ud->lock); + if (ret) { + dev_err(&udev->dev, "device reset\n"); + ud->status = SDEV_ST_ERROR; + } else { + dev_info(&udev->dev, "device reset\n"); + ud->status = SDEV_ST_AVAILABLE; + } + spin_unlock_irq(&ud->lock); +} + +static void stub_device_unusable(struct usbip_device *ud) +{ + spin_lock_irq(&ud->lock); + ud->status = SDEV_ST_ERROR; + spin_unlock_irq(&ud->lock); +} + +/** + * stub_device_alloc - allocate a new stub_device struct + * @interface: usb_interface of a new device + * + * Allocates and initializes a new stub_device struct. + */ +static struct stub_device *stub_device_alloc(struct usb_device *udev) +{ + struct stub_device *sdev; + int busnum = udev->bus->busnum; + int devnum = udev->devnum; + + dev_dbg(&udev->dev, "allocating stub device"); + + /* yes, it's a new device */ + sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL); + if (!sdev) + return NULL; + + sdev->udev = usb_get_dev(udev); + + /* + * devid is defined with devnum when this driver is first allocated. + * devnum may change later if a device is reset. However, devid never + * changes during a usbip connection. + */ + sdev->devid = (busnum << 16) | devnum; + sdev->ud.side = USBIP_STUB; + sdev->ud.status = SDEV_ST_AVAILABLE; + spin_lock_init(&sdev->ud.lock); + sdev->ud.tcp_socket = NULL; + + INIT_LIST_HEAD(&sdev->priv_init); + INIT_LIST_HEAD(&sdev->priv_tx); + INIT_LIST_HEAD(&sdev->priv_free); + INIT_LIST_HEAD(&sdev->unlink_free); + INIT_LIST_HEAD(&sdev->unlink_tx); + spin_lock_init(&sdev->priv_lock); + + init_waitqueue_head(&sdev->tx_waitq); + + sdev->ud.eh_ops.shutdown = stub_shutdown_connection; + sdev->ud.eh_ops.reset = stub_device_reset; + sdev->ud.eh_ops.unusable = stub_device_unusable; + + usbip_start_eh(&sdev->ud); + + dev_dbg(&udev->dev, "register new device\n"); + + return sdev; +} + +static void stub_device_free(struct stub_device *sdev) +{ + kfree(sdev); +} + +static int stub_probe(struct usb_device *udev) +{ + struct stub_device *sdev = NULL; + const char *udev_busid = dev_name(&udev->dev); + int err = 0; + struct bus_id_priv *busid_priv; + int rc; + + dev_dbg(&udev->dev, "Enter\n"); + + /* check we should claim or not by busid_table */ + busid_priv = get_busid_priv(udev_busid); + if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) || + (busid_priv->status == STUB_BUSID_OTHER)) { + dev_info(&udev->dev, + "%s is not in match_busid table... skip!\n", + udev_busid); + + /* + * Return value should be ENODEV or ENOXIO to continue trying + * other matched drivers by the driver core. + * See driver_probe_device() in driver/base/dd.c + */ + return -ENODEV; + } + + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) { + dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n", + udev_busid); + return -ENODEV; + } + + if (!strcmp(udev->bus->bus_name, "vhci_hcd")) { + dev_dbg(&udev->dev, + "%s is attached on vhci_hcd... skip!\n", + udev_busid); + + return -ENODEV; + } + + /* ok, this is my device */ + sdev = stub_device_alloc(udev); + if (!sdev) + return -ENOMEM; + + dev_info(&udev->dev, + "usbip-host: register new device (bus %u dev %u)\n", + udev->bus->busnum, udev->devnum); + + busid_priv->shutdown_busid = 0; + + /* set private data to usb_device */ + dev_set_drvdata(&udev->dev, sdev); + busid_priv->sdev = sdev; + busid_priv->udev = udev; + + /* + * Claim this hub port. + * It doesn't matter what value we pass as owner + * (struct dev_state) as long as it is unique. + */ + rc = usb_hub_claim_port(udev->parent, udev->portnum, + (struct usb_dev_state *) udev); + if (rc) { + dev_dbg(&udev->dev, "unable to claim port\n"); + return rc; + } + + err = stub_add_files(&udev->dev); + if (err) { + dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid); + dev_set_drvdata(&udev->dev, NULL); + usb_put_dev(udev); + kthread_stop_put(sdev->ud.eh); + + busid_priv->sdev = NULL; + stub_device_free(sdev); + return err; + } + busid_priv->status = STUB_BUSID_ALLOC; + + return 0; +} + +static void shutdown_busid(struct bus_id_priv *busid_priv) +{ + if (busid_priv->sdev && !busid_priv->shutdown_busid) { + busid_priv->shutdown_busid = 1; + usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED); + + /* wait for the stop of the event handler */ + usbip_stop_eh(&busid_priv->sdev->ud); + } +} + +/* + * called in usb_disconnect() or usb_deregister() + * but only if actconfig(active configuration) exists + */ +static void stub_disconnect(struct usb_device *udev) +{ + struct stub_device *sdev; + const char *udev_busid = dev_name(&udev->dev); + struct bus_id_priv *busid_priv; + int rc; + + dev_dbg(&udev->dev, "Enter\n"); + + busid_priv = get_busid_priv(udev_busid); + if (!busid_priv) { + BUG(); + return; + } + + sdev = dev_get_drvdata(&udev->dev); + + /* get stub_device */ + if (!sdev) { + dev_err(&udev->dev, "could not get device"); + return; + } + + dev_set_drvdata(&udev->dev, NULL); + + /* + * NOTE: rx/tx threads are invoked for each usb_device. + */ + stub_remove_files(&udev->dev); + + /* release port */ + rc = usb_hub_release_port(udev->parent, udev->portnum, + (struct usb_dev_state *) udev); + if (rc) { + dev_dbg(&udev->dev, "unable to release port\n"); + return; + } + + /* If usb reset is called from event handler */ + if (busid_priv->sdev->ud.eh == current) + return; + + /* shutdown the current connection */ + shutdown_busid(busid_priv); + + usb_put_dev(sdev->udev); + + /* free sdev */ + busid_priv->sdev = NULL; + stub_device_free(sdev); + + if (busid_priv->status == STUB_BUSID_ALLOC) { + busid_priv->status = STUB_BUSID_ADDED; + } else { + busid_priv->status = STUB_BUSID_OTHER; + del_match_busid((char *)udev_busid); + } +} + +#ifdef CONFIG_PM + +/* These functions need usb_port_suspend and usb_port_resume, + * which reside in drivers/usb/core/usb.h. Skip for now. */ + +static int stub_suspend(struct usb_device *udev, pm_message_t message) +{ + dev_dbg(&udev->dev, "stub_suspend\n"); + + return 0; +} + +static int stub_resume(struct usb_device *udev, pm_message_t message) +{ + dev_dbg(&udev->dev, "stub_resume\n"); + + return 0; +} + +#endif /* CONFIG_PM */ + +struct usb_device_driver stub_driver = { + .name = "usbip-host", + .probe = stub_probe, + .disconnect = stub_disconnect, +#ifdef CONFIG_PM + .suspend = stub_suspend, + .resume = stub_resume, +#endif + .supports_autosuspend = 0, +}; diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c new file mode 100644 index 000000000000..44ab43fc4fcc --- /dev/null +++ b/drivers/usb/usbip/stub_main.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/string.h> +#include <linux/module.h> +#include <linux/device.h> + +#include "usbip_common.h" +#include "stub.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi" +#define DRIVER_DESC "USB/IP Host Driver" + +struct kmem_cache *stub_priv_cache; +/* + * busid_tables defines matching busids that usbip can grab. A user can change + * dynamically what device is locally used and what device is exported to a + * remote host. + */ +#define MAX_BUSID 16 +static struct bus_id_priv busid_table[MAX_BUSID]; +static spinlock_t busid_table_lock; + +static void init_busid_table(void) +{ + /* + * This also sets the bus_table[i].status to + * STUB_BUSID_OTHER, which is 0. + */ + memset(busid_table, 0, sizeof(busid_table)); + + spin_lock_init(&busid_table_lock); +} + +/* + * Find the index of the busid by name. + * Must be called with busid_table_lock held. + */ +static int get_busid_idx(const char *busid) +{ + int i; + int idx = -1; + + for (i = 0; i < MAX_BUSID; i++) + if (busid_table[i].name[0]) + if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { + idx = i; + break; + } + return idx; +} + +struct bus_id_priv *get_busid_priv(const char *busid) +{ + int idx; + struct bus_id_priv *bid = NULL; + + spin_lock(&busid_table_lock); + idx = get_busid_idx(busid); + if (idx >= 0) + bid = &(busid_table[idx]); + spin_unlock(&busid_table_lock); + + return bid; +} + +static int add_match_busid(char *busid) +{ + int i; + int ret = -1; + + spin_lock(&busid_table_lock); + /* already registered? */ + if (get_busid_idx(busid) >= 0) { + ret = 0; + goto out; + } + + for (i = 0; i < MAX_BUSID; i++) + if (!busid_table[i].name[0]) { + strlcpy(busid_table[i].name, busid, BUSID_SIZE); + if ((busid_table[i].status != STUB_BUSID_ALLOC) && + (busid_table[i].status != STUB_BUSID_REMOV)) + busid_table[i].status = STUB_BUSID_ADDED; + ret = 0; + break; + } + +out: + spin_unlock(&busid_table_lock); + + return ret; +} + +int del_match_busid(char *busid) +{ + int idx; + int ret = -1; + + spin_lock(&busid_table_lock); + idx = get_busid_idx(busid); + if (idx < 0) + goto out; + + /* found */ + ret = 0; + + if (busid_table[idx].status == STUB_BUSID_OTHER) + memset(busid_table[idx].name, 0, BUSID_SIZE); + + if ((busid_table[idx].status != STUB_BUSID_OTHER) && + (busid_table[idx].status != STUB_BUSID_ADDED)) + busid_table[idx].status = STUB_BUSID_REMOV; + +out: + spin_unlock(&busid_table_lock); + + return ret; +} + +static ssize_t show_match_busid(struct device_driver *drv, char *buf) +{ + int i; + char *out = buf; + + spin_lock(&busid_table_lock); + for (i = 0; i < MAX_BUSID; i++) + if (busid_table[i].name[0]) + out += sprintf(out, "%s ", busid_table[i].name); + spin_unlock(&busid_table_lock); + out += sprintf(out, "\n"); + + return out - buf; +} + +static ssize_t store_match_busid(struct device_driver *dev, const char *buf, + size_t count) +{ + int len; + char busid[BUSID_SIZE]; + + if (count < 5) + return -EINVAL; + + /* busid needs to include \0 termination */ + len = strlcpy(busid, buf + 4, BUSID_SIZE); + if (sizeof(busid) <= len) + return -EINVAL; + + if (!strncmp(buf, "add ", 4)) { + if (add_match_busid(busid) < 0) + return -ENOMEM; + + pr_debug("add busid %s\n", busid); + return count; + } + + if (!strncmp(buf, "del ", 4)) { + if (del_match_busid(busid) < 0) + return -ENODEV; + + pr_debug("del busid %s\n", busid); + return count; + } + + return -EINVAL; +} +static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid, + store_match_busid); + +static ssize_t rebind_store(struct device_driver *dev, const char *buf, + size_t count) +{ + int ret; + int len; + struct bus_id_priv *bid; + + /* buf length should be less that BUSID_SIZE */ + len = strnlen(buf, BUSID_SIZE); + + if (!(len < BUSID_SIZE)) + return -EINVAL; + + bid = get_busid_priv(buf); + if (!bid) + return -ENODEV; + + ret = device_attach(&bid->udev->dev); + if (ret < 0) { + dev_err(&bid->udev->dev, "rebind failed\n"); + return ret; + } + + return count; +} + +static DRIVER_ATTR_WO(rebind); + +static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) +{ + struct stub_priv *priv, *tmp; + + list_for_each_entry_safe(priv, tmp, listhead, list) { + list_del(&priv->list); + return priv; + } + + return NULL; +} + +static struct stub_priv *stub_priv_pop(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + priv = stub_priv_pop_from_listhead(&sdev->priv_init); + if (priv) + goto done; + + priv = stub_priv_pop_from_listhead(&sdev->priv_tx); + if (priv) + goto done; + + priv = stub_priv_pop_from_listhead(&sdev->priv_free); + +done: + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return priv; +} + +void stub_device_cleanup_urbs(struct stub_device *sdev) +{ + struct stub_priv *priv; + struct urb *urb; + + dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev); + + while ((priv = stub_priv_pop(sdev))) { + urb = priv->urb; + dev_dbg(&sdev->udev->dev, "free urb %p\n", urb); + usb_kill_urb(urb); + + kmem_cache_free(stub_priv_cache, priv); + + kfree(urb->transfer_buffer); + kfree(urb->setup_packet); + usb_free_urb(urb); + } +} + +static int __init usbip_host_init(void) +{ + int ret; + + init_busid_table(); + + stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); + if (!stub_priv_cache) { + pr_err("kmem_cache_create failed\n"); + return -ENOMEM; + } + + ret = usb_register_device_driver(&stub_driver, THIS_MODULE); + if (ret) { + pr_err("usb_register failed %d\n", ret); + goto err_usb_register; + } + + ret = driver_create_file(&stub_driver.drvwrap.driver, + &driver_attr_match_busid); + if (ret) { + pr_err("driver_create_file failed\n"); + goto err_create_file; + } + + ret = driver_create_file(&stub_driver.drvwrap.driver, + &driver_attr_rebind); + if (ret) { + pr_err("driver_create_file failed\n"); + goto err_create_file; + } + + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return ret; + +err_create_file: + usb_deregister_device_driver(&stub_driver); +err_usb_register: + kmem_cache_destroy(stub_priv_cache); + return ret; +} + +static void __exit usbip_host_exit(void) +{ + driver_remove_file(&stub_driver.drvwrap.driver, + &driver_attr_match_busid); + + driver_remove_file(&stub_driver.drvwrap.driver, + &driver_attr_rebind); + + /* + * deregister() calls stub_disconnect() for all devices. Device + * specific data is cleared in stub_disconnect(). + */ + usb_deregister_device_driver(&stub_driver); + + kmem_cache_destroy(stub_priv_cache); +} + +module_init(usbip_host_init); +module_exit(usbip_host_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c new file mode 100644 index 000000000000..00e475c51a12 --- /dev/null +++ b/drivers/usb/usbip/stub_rx.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <asm/byteorder.h> +#include <linux/kthread.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "usbip_common.h" +#include "stub.h" + +static int is_clear_halt_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_CLEAR_FEATURE) && + (req->bRequestType == USB_RECIP_ENDPOINT) && + (req->wValue == USB_ENDPOINT_HALT); +} + +static int is_set_interface_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_SET_INTERFACE) && + (req->bRequestType == USB_RECIP_INTERFACE); +} + +static int is_set_configuration_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_SET_CONFIGURATION) && + (req->bRequestType == USB_RECIP_DEVICE); +} + +static int is_reset_device_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + __u16 value; + __u16 index; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + value = le16_to_cpu(req->wValue); + index = le16_to_cpu(req->wIndex); + + if ((req->bRequest == USB_REQ_SET_FEATURE) && + (req->bRequestType == USB_RT_PORT) && + (value == USB_PORT_FEAT_RESET)) { + usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index); + return 1; + } else + return 0; +} + +static int tweak_clear_halt_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + int target_endp; + int target_dir; + int target_pipe; + int ret; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + /* + * The stalled endpoint is specified in the wIndex value. The endpoint + * of the urb is the target of this clear_halt request (i.e., control + * endpoint). + */ + target_endp = le16_to_cpu(req->wIndex) & 0x000f; + + /* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80. */ + target_dir = le16_to_cpu(req->wIndex) & 0x0080; + + if (target_dir) + target_pipe = usb_rcvctrlpipe(urb->dev, target_endp); + else + target_pipe = usb_sndctrlpipe(urb->dev, target_endp); + + ret = usb_clear_halt(urb->dev, target_pipe); + if (ret < 0) + dev_err(&urb->dev->dev, + "usb_clear_halt error: devnum %d endp %d ret %d\n", + urb->dev->devnum, target_endp, ret); + else + dev_info(&urb->dev->dev, + "usb_clear_halt done: devnum %d endp %d\n", + urb->dev->devnum, target_endp); + + return ret; +} + +static int tweak_set_interface_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + __u16 alternate; + __u16 interface; + int ret; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + alternate = le16_to_cpu(req->wValue); + interface = le16_to_cpu(req->wIndex); + + usbip_dbg_stub_rx("set_interface: inf %u alt %u\n", + interface, alternate); + + ret = usb_set_interface(urb->dev, interface, alternate); + if (ret < 0) + dev_err(&urb->dev->dev, + "usb_set_interface error: inf %u alt %u ret %d\n", + interface, alternate, ret); + else + dev_info(&urb->dev->dev, + "usb_set_interface done: inf %u alt %u\n", + interface, alternate); + + return ret; +} + +static int tweak_set_configuration_cmd(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + struct usb_ctrlrequest *req; + __u16 config; + int err; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + config = le16_to_cpu(req->wValue); + + err = usb_set_configuration(sdev->udev, config); + if (err && err != -ENODEV) + dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n", + config, err); + return 0; +} + +static int tweak_reset_device_cmd(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + + dev_info(&urb->dev->dev, "usb_queue_reset_device\n"); + + /* + * With the implementation of pre_reset and post_reset the driver no + * longer unbinds. This allows the use of synchronous reset. + */ + + if (usb_lock_device_for_reset(sdev->udev, sdev->interface) < 0) { + dev_err(&urb->dev->dev, "could not obtain lock to reset device\n"); + return 0; + } + usb_reset_device(sdev->udev); + usb_unlock_device(sdev->udev); + + return 0; +} + +/* + * clear_halt, set_interface, and set_configuration require special tricks. + */ +static void tweak_special_requests(struct urb *urb) +{ + if (!urb || !urb->setup_packet) + return; + + if (usb_pipetype(urb->pipe) != PIPE_CONTROL) + return; + + if (is_clear_halt_cmd(urb)) + /* tweak clear_halt */ + tweak_clear_halt_cmd(urb); + + else if (is_set_interface_cmd(urb)) + /* tweak set_interface */ + tweak_set_interface_cmd(urb); + + else if (is_set_configuration_cmd(urb)) + /* tweak set_configuration */ + tweak_set_configuration_cmd(urb); + + else if (is_reset_device_cmd(urb)) + tweak_reset_device_cmd(urb); + else + usbip_dbg_stub_rx("no need to tweak\n"); +} + +/* + * stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb(). + * By unlinking the urb asynchronously, stub_rx can continuously + * process coming urbs. Even if the urb is unlinked, its completion + * handler will be called and stub_tx will send a return pdu. + * + * See also comments about unlinking strategy in vhci_hcd.c. + */ +static int stub_recv_cmd_unlink(struct stub_device *sdev, + struct usbip_header *pdu) +{ + int ret; + unsigned long flags; + struct stub_priv *priv; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry(priv, &sdev->priv_init, list) { + if (priv->seqnum != pdu->u.cmd_unlink.seqnum) + continue; + + dev_info(&priv->urb->dev->dev, "unlink urb %p\n", + priv->urb); + + /* + * This matched urb is not completed yet (i.e., be in + * flight in usb hcd hardware/driver). Now we are + * cancelling it. The unlinking flag means that we are + * now not going to return the normal result pdu of a + * submission request, but going to return a result pdu + * of the unlink request. + */ + priv->unlinking = 1; + + /* + * In the case that unlinking flag is on, prev->seqnum + * is changed from the seqnum of the cancelling urb to + * the seqnum of the unlink request. This will be used + * to make the result pdu of the unlink request. + */ + priv->seqnum = pdu->base.seqnum; + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + /* + * usb_unlink_urb() is now out of spinlocking to avoid + * spinlock recursion since stub_complete() is + * sometimes called in this context but not in the + * interrupt context. If stub_complete() is executed + * before we call usb_unlink_urb(), usb_unlink_urb() + * will return an error value. In this case, stub_tx + * will return the result pdu of this unlink request + * though submission is completed and actual unlinking + * is not executed. OK? + */ + /* In the above case, urb->status is not -ECONNRESET, + * so a driver in a client host will know the failure + * of the unlink request ? + */ + ret = usb_unlink_urb(priv->urb); + if (ret != -EINPROGRESS) + dev_err(&priv->urb->dev->dev, + "failed to unlink a urb %p, ret %d\n", + priv->urb, ret); + + return 0; + } + + usbip_dbg_stub_rx("seqnum %d is not pending\n", + pdu->u.cmd_unlink.seqnum); + + /* + * The urb of the unlink target is not found in priv_init queue. It was + * already completed and its results is/was going to be sent by a + * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only + * return the completeness of this unlink request to vhci_hcd. + */ + stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0); + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return 0; +} + +static int valid_request(struct stub_device *sdev, struct usbip_header *pdu) +{ + struct usbip_device *ud = &sdev->ud; + int valid = 0; + + if (pdu->base.devid == sdev->devid) { + spin_lock_irq(&ud->lock); + if (ud->status == SDEV_ST_USED) { + /* A request is valid. */ + valid = 1; + } + spin_unlock_irq(&ud->lock); + } + + return valid; +} + +static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, + struct usbip_header *pdu) +{ + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + unsigned long flags; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + priv = kmem_cache_zalloc(stub_priv_cache, GFP_ATOMIC); + if (!priv) { + dev_err(&sdev->interface->dev, "alloc stub_priv\n"); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return NULL; + } + + priv->seqnum = pdu->base.seqnum; + priv->sdev = sdev; + + /* + * After a stub_priv is linked to a list_head, + * our error handler can free allocated data. + */ + list_add_tail(&priv->list, &sdev->priv_init); + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return priv; +} + +static int get_pipe(struct stub_device *sdev, int epnum, int dir) +{ + struct usb_device *udev = sdev->udev; + struct usb_host_endpoint *ep; + struct usb_endpoint_descriptor *epd = NULL; + + if (dir == USBIP_DIR_IN) + ep = udev->ep_in[epnum & 0x7f]; + else + ep = udev->ep_out[epnum & 0x7f]; + if (!ep) { + dev_err(&sdev->interface->dev, "no such endpoint?, %d\n", + epnum); + BUG(); + } + + epd = &ep->desc; + if (usb_endpoint_xfer_control(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndctrlpipe(udev, epnum); + else + return usb_rcvctrlpipe(udev, epnum); + } + + if (usb_endpoint_xfer_bulk(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndbulkpipe(udev, epnum); + else + return usb_rcvbulkpipe(udev, epnum); + } + + if (usb_endpoint_xfer_int(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndintpipe(udev, epnum); + else + return usb_rcvintpipe(udev, epnum); + } + + if (usb_endpoint_xfer_isoc(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndisocpipe(udev, epnum); + else + return usb_rcvisocpipe(udev, epnum); + } + + /* NOT REACHED */ + dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); + return 0; +} + +static void masking_bogus_flags(struct urb *urb) +{ + int xfertype; + struct usb_device *dev; + struct usb_host_endpoint *ep; + int is_out; + unsigned int allowed; + + if (!urb || urb->hcpriv || !urb->complete) + return; + dev = urb->dev; + if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED)) + return; + + ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!ep) + return; + + xfertype = usb_endpoint_type(&ep->desc); + if (xfertype == USB_ENDPOINT_XFER_CONTROL) { + struct usb_ctrlrequest *setup = + (struct usb_ctrlrequest *) urb->setup_packet; + + if (!setup) + return; + is_out = !(setup->bRequestType & USB_DIR_IN) || + !setup->wLength; + } else { + is_out = usb_endpoint_dir_out(&ep->desc); + } + + /* enforce simple/standard policy */ + allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | + URB_DIR_MASK | URB_FREE_BUFFER); + switch (xfertype) { + case USB_ENDPOINT_XFER_BULK: + if (is_out) + allowed |= URB_ZERO_PACKET; + /* FALLTHROUGH */ + case USB_ENDPOINT_XFER_CONTROL: + allowed |= URB_NO_FSBR; /* only affects UHCI */ + /* FALLTHROUGH */ + default: /* all non-iso endpoints */ + if (!is_out) + allowed |= URB_SHORT_NOT_OK; + break; + case USB_ENDPOINT_XFER_ISOC: + allowed |= URB_ISO_ASAP; + break; + } + urb->transfer_flags &= allowed; +} + +static void stub_recv_cmd_submit(struct stub_device *sdev, + struct usbip_header *pdu) +{ + int ret; + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + struct usb_device *udev = sdev->udev; + int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); + + priv = stub_priv_alloc(sdev, pdu); + if (!priv) + return; + + /* setup a urb */ + if (usb_pipeisoc(pipe)) + priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets, + GFP_KERNEL); + else + priv->urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!priv->urb) { + dev_err(&sdev->interface->dev, "malloc urb\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + /* allocate urb transfer buffer, if needed */ + if (pdu->u.cmd_submit.transfer_buffer_length > 0) { + priv->urb->transfer_buffer = + kzalloc(pdu->u.cmd_submit.transfer_buffer_length, + GFP_KERNEL); + if (!priv->urb->transfer_buffer) { + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + } + + /* copy urb setup packet */ + priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8, + GFP_KERNEL); + if (!priv->urb->setup_packet) { + dev_err(&sdev->interface->dev, "allocate setup_packet\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + /* set other members from the base header of pdu */ + priv->urb->context = (void *) priv; + priv->urb->dev = udev; + priv->urb->pipe = pipe; + priv->urb->complete = stub_complete; + + usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0); + + + if (usbip_recv_xbuff(ud, priv->urb) < 0) + return; + + if (usbip_recv_iso(ud, priv->urb) < 0) + return; + + /* no need to submit an intercepted request, but harmless? */ + tweak_special_requests(priv->urb); + + masking_bogus_flags(priv->urb); + /* urb is now ready to submit */ + ret = usb_submit_urb(priv->urb, GFP_KERNEL); + + if (ret == 0) + usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", + pdu->base.seqnum); + else { + dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret); + usbip_dump_header(pdu); + usbip_dump_urb(priv->urb); + + /* + * Pessimistic. + * This connection will be discarded. + */ + usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); + } + + usbip_dbg_stub_rx("Leave\n"); +} + +/* recv a pdu */ +static void stub_rx_pdu(struct usbip_device *ud) +{ + int ret; + struct usbip_header pdu; + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + struct device *dev = &sdev->udev->dev; + + usbip_dbg_stub_rx("Enter\n"); + + memset(&pdu, 0, sizeof(pdu)); + + /* receive a pdu header */ + ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); + if (ret != sizeof(pdu)) { + dev_err(dev, "recv a header, %d\n", ret); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + return; + } + + usbip_header_correct_endian(&pdu, 0); + + if (usbip_dbg_flag_stub_rx) + usbip_dump_header(&pdu); + + if (!valid_request(sdev, &pdu)) { + dev_err(dev, "recv invalid request\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + return; + } + + switch (pdu.base.command) { + case USBIP_CMD_UNLINK: + stub_recv_cmd_unlink(sdev, &pdu); + break; + + case USBIP_CMD_SUBMIT: + stub_recv_cmd_submit(sdev, &pdu); + break; + + default: + /* NOTREACHED */ + dev_err(dev, "unknown pdu\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + break; + } +} + +int stub_rx_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + stub_rx_pdu(ud); + } + + return 0; +} diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c new file mode 100644 index 000000000000..dbcabc9dbe0d --- /dev/null +++ b/drivers/usb/usbip/stub_tx.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/socket.h> + +#include "usbip_common.h" +#include "stub.h" + +static void stub_free_priv_and_urb(struct stub_priv *priv) +{ + struct urb *urb = priv->urb; + + kfree(urb->setup_packet); + kfree(urb->transfer_buffer); + list_del(&priv->list); + kmem_cache_free(stub_priv_cache, priv); + usb_free_urb(urb); +} + +/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */ +void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status) +{ + struct stub_unlink *unlink; + + unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC); + if (!unlink) { + usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC); + return; + } + + unlink->seqnum = seqnum; + unlink->status = status; + + list_add_tail(&unlink->list, &sdev->unlink_tx); +} + +/** + * stub_complete - completion handler of a usbip urb + * @urb: pointer to the urb completed + * + * When a urb has completed, the USB core driver calls this function mostly in + * the interrupt context. To return the result of a urb, the completed urb is + * linked to the pending list of returning. + * + */ +void stub_complete(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + unsigned long flags; + + usbip_dbg_stub_tx("complete! status %d\n", urb->status); + + switch (urb->status) { + case 0: + /* OK */ + break; + case -ENOENT: + dev_info(&urb->dev->dev, + "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n"); + return; + case -ECONNRESET: + dev_info(&urb->dev->dev, + "unlinked by a call to usb_unlink_urb()\n"); + break; + case -EPIPE: + dev_info(&urb->dev->dev, "endpoint %d is stalled\n", + usb_pipeendpoint(urb->pipe)); + break; + case -ESHUTDOWN: + dev_info(&urb->dev->dev, "device removed?\n"); + break; + default: + dev_info(&urb->dev->dev, + "urb completion with non-zero status %d\n", + urb->status); + break; + } + + /* link a urb to the queue of tx. */ + spin_lock_irqsave(&sdev->priv_lock, flags); + if (priv->unlinking) { + stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status); + stub_free_priv_and_urb(priv); + } else { + list_move_tail(&priv->list, &sdev->priv_tx); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + /* wake up tx_thread */ + wake_up(&sdev->tx_waitq); +} + +static inline void setup_base_pdu(struct usbip_header_basic *base, + __u32 command, __u32 seqnum) +{ + base->command = command; + base->seqnum = seqnum; + base->devid = 0; + base->ep = 0; + base->direction = 0; +} + +static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + + setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum); + usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1); +} + +static void setup_ret_unlink_pdu(struct usbip_header *rpdu, + struct stub_unlink *unlink) +{ + setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum); + rpdu->u.ret_unlink.status = unlink->status; +} + +static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) { + list_move_tail(&priv->list, &sdev->priv_free); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + return priv; + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return NULL; +} + +static int stub_send_ret_submit(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv, *tmp; + + struct msghdr msg; + size_t txsize; + + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(sdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + struct kvec *iov = NULL; + int iovnum = 0; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + iovnum = 2 + urb->number_of_packets; + else + iovnum = 2; + + iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL); + + if (!iov) { + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iovnum = 0; + + /* 1. setup usbip_header */ + setup_ret_submit_pdu(&pdu_header, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", + pdu_header.base.seqnum, urb); + usbip_header_correct_endian(&pdu_header, 1); + + iov[iovnum].iov_base = &pdu_header; + iov[iovnum].iov_len = sizeof(pdu_header); + iovnum++; + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ + if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && + urb->actual_length > 0) { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = urb->actual_length; + iovnum++; + txsize += urb->actual_length; + } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + /* + * For isochronous packets: actual length is the sum of + * the actual length of the individual, packets, but as + * the packet offsets are not changed there will be + * padding between the packets. To optimally use the + * bandwidth the padding is not transmitted. + */ + + int i; + + for (i = 0; i < urb->number_of_packets; i++) { + iov[iovnum].iov_base = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + iov[iovnum].iov_len = + urb->iso_frame_desc[i].actual_length; + iovnum++; + txsize += urb->iso_frame_desc[i].actual_length; + } + + if (txsize != sizeof(pdu_header) + urb->actual_length) { + dev_err(&sdev->interface->dev, + "actual length of urb %d does not match iso packet sizes %zu\n", + urb->actual_length, + txsize-sizeof(pdu_header)); + kfree(iov); + usbip_event_add(&sdev->ud, + SDEV_EVENT_ERROR_TCP); + return -1; + } + } + + /* 3. setup iso_packet_descriptor */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + ssize_t len = 0; + + iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); + if (!iso_buffer) { + usbip_event_add(&sdev->ud, + SDEV_EVENT_ERROR_MALLOC); + kfree(iov); + return -1; + } + + iov[iovnum].iov_base = iso_buffer; + iov[iovnum].iov_len = len; + txsize += len; + iovnum++; + } + + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, + iov, iovnum, txsize); + if (ret != txsize) { + dev_err(&sdev->interface->dev, + "sendmsg failed!, retval %d for %zd\n", + ret, txsize); + kfree(iov); + kfree(iso_buffer); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } + + kfree(iov); + kfree(iso_buffer); + + total_size += txsize; + } + + spin_lock_irqsave(&sdev->priv_lock, flags); + list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { + stub_free_priv_and_urb(priv); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return total_size; +} + +static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { + list_move_tail(&unlink->list, &sdev->unlink_free); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + return unlink; + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return NULL; +} + +static int stub_send_ret_unlink(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + struct msghdr msg; + struct kvec iov[1]; + size_t txsize; + + size_t total_size = 0; + + while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) { + int ret; + struct usbip_header pdu_header; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum); + + /* 1. setup usbip_header */ + setup_ret_unlink_pdu(&pdu_header, unlink); + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, + 1, txsize); + if (ret != txsize) { + dev_err(&sdev->interface->dev, + "sendmsg failed!, retval %d for %zd\n", + ret, txsize); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } + + usbip_dbg_stub_tx("send txdata\n"); + total_size += txsize; + } + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) { + list_del(&unlink->list); + kfree(unlink); + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return total_size; +} + +int stub_tx_loop(void *data) +{ + struct usbip_device *ud = data; + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + /* + * send_ret_submit comes earlier than send_ret_unlink. stub_rx + * looks at only priv_init queue. If the completion of a URB is + * earlier than the receive of CMD_UNLINK, priv is moved to + * priv_tx queue and stub_rx does not find the target priv. In + * this case, vhci_rx receives the result of the submit request + * and then receives the result of the unlink request. The + * result of the submit is given back to the usbcore as the + * completion of the unlink request. The request of the + * unlink is ignored. This is ok because a driver who calls + * usb_unlink_urb() understands the unlink was too late by + * getting the status of the given-backed URB which has the + * status of usb_submit_urb(). + */ + if (stub_send_ret_submit(sdev) < 0) + break; + + if (stub_send_ret_unlink(sdev) < 0) + break; + + wait_event_interruptible(sdev->tx_waitq, + (!list_empty(&sdev->priv_tx) || + !list_empty(&sdev->unlink_tx) || + kthread_should_stop())); + } + + return 0; +} diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c new file mode 100644 index 000000000000..facaaf003f19 --- /dev/null +++ b/drivers/usb/usbip/usbip_common.c @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <asm/byteorder.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <net/sock.h> + +#include "usbip_common.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>" +#define DRIVER_DESC "USB/IP Core" + +#ifdef CONFIG_USBIP_DEBUG +unsigned long usbip_debug_flag = 0xffffffff; +#else +unsigned long usbip_debug_flag; +#endif +EXPORT_SYMBOL_GPL(usbip_debug_flag); +module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)"); + +/* FIXME */ +struct device_attribute dev_attr_usbip_debug; +EXPORT_SYMBOL_GPL(dev_attr_usbip_debug); + +static ssize_t usbip_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%lx\n", usbip_debug_flag); +} + +static ssize_t usbip_debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + if (sscanf(buf, "%lx", &usbip_debug_flag) != 1) + return -EINVAL; + return count; +} +DEVICE_ATTR_RW(usbip_debug); + +static void usbip_dump_buffer(char *buff, int bufflen) +{ + print_hex_dump(KERN_DEBUG, "usbip-core", DUMP_PREFIX_OFFSET, 16, 4, + buff, bufflen, false); +} + +static void usbip_dump_pipe(unsigned int p) +{ + unsigned char type = usb_pipetype(p); + unsigned char ep = usb_pipeendpoint(p); + unsigned char dev = usb_pipedevice(p); + unsigned char dir = usb_pipein(p); + + pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT"); + + switch (type) { + case PIPE_ISOCHRONOUS: + pr_debug("ISO\n"); + break; + case PIPE_INTERRUPT: + pr_debug("INT\n"); + break; + case PIPE_CONTROL: + pr_debug("CTRL\n"); + break; + case PIPE_BULK: + pr_debug("BULK\n"); + break; + default: + pr_debug("ERR\n"); + break; + } +} + +static void usbip_dump_usb_device(struct usb_device *udev) +{ + struct device *dev = &udev->dev; + int i; + + dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)", + udev->devnum, udev->devpath, usb_speed_string(udev->speed)); + + pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport); + + dev_dbg(dev, " "); + for (i = 0; i < 16; i++) + pr_debug(" %2u", i); + pr_debug("\n"); + + dev_dbg(dev, " toggle0(IN) :"); + for (i = 0; i < 16; i++) + pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0); + pr_debug("\n"); + + dev_dbg(dev, " toggle1(OUT):"); + for (i = 0; i < 16; i++) + pr_debug(" %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0); + pr_debug("\n"); + + dev_dbg(dev, " epmaxp_in :"); + for (i = 0; i < 16; i++) { + if (udev->ep_in[i]) + pr_debug(" %2u", + le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize)); + } + pr_debug("\n"); + + dev_dbg(dev, " epmaxp_out :"); + for (i = 0; i < 16; i++) { + if (udev->ep_out[i]) + pr_debug(" %2u", + le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize)); + } + pr_debug("\n"); + + dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus); + + dev_dbg(dev, + "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n", + &udev->descriptor, udev->config, + udev->actconfig, udev->rawdescriptors); + + dev_dbg(dev, "have_langid %d, string_langid %d\n", + udev->have_langid, udev->string_langid); + + dev_dbg(dev, "maxchild %d\n", udev->maxchild); +} + +static void usbip_dump_request_type(__u8 rt) +{ + switch (rt & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + pr_debug("DEVICE"); + break; + case USB_RECIP_INTERFACE: + pr_debug("INTERF"); + break; + case USB_RECIP_ENDPOINT: + pr_debug("ENDPOI"); + break; + case USB_RECIP_OTHER: + pr_debug("OTHER "); + break; + default: + pr_debug("------"); + break; + } +} + +static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd) +{ + if (!cmd) { + pr_debug(" : null pointer\n"); + return; + } + + pr_debug(" "); + pr_debug("bRequestType(%02X) bRequest(%02X) wValue(%04X) wIndex(%04X) wLength(%04X) ", + cmd->bRequestType, cmd->bRequest, + cmd->wValue, cmd->wIndex, cmd->wLength); + pr_debug("\n "); + + if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + pr_debug("STANDARD "); + switch (cmd->bRequest) { + case USB_REQ_GET_STATUS: + pr_debug("GET_STATUS\n"); + break; + case USB_REQ_CLEAR_FEATURE: + pr_debug("CLEAR_FEAT\n"); + break; + case USB_REQ_SET_FEATURE: + pr_debug("SET_FEAT\n"); + break; + case USB_REQ_SET_ADDRESS: + pr_debug("SET_ADDRRS\n"); + break; + case USB_REQ_GET_DESCRIPTOR: + pr_debug("GET_DESCRI\n"); + break; + case USB_REQ_SET_DESCRIPTOR: + pr_debug("SET_DESCRI\n"); + break; + case USB_REQ_GET_CONFIGURATION: + pr_debug("GET_CONFIG\n"); + break; + case USB_REQ_SET_CONFIGURATION: + pr_debug("SET_CONFIG\n"); + break; + case USB_REQ_GET_INTERFACE: + pr_debug("GET_INTERF\n"); + break; + case USB_REQ_SET_INTERFACE: + pr_debug("SET_INTERF\n"); + break; + case USB_REQ_SYNCH_FRAME: + pr_debug("SYNC_FRAME\n"); + break; + default: + pr_debug("REQ(%02X)\n", cmd->bRequest); + break; + } + usbip_dump_request_type(cmd->bRequestType); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + pr_debug("CLASS\n"); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + pr_debug("VENDOR\n"); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED) { + pr_debug("RESERVED\n"); + } +} + +void usbip_dump_urb(struct urb *urb) +{ + struct device *dev; + + if (!urb) { + pr_debug("urb: null pointer!!\n"); + return; + } + + if (!urb->dev) { + pr_debug("urb->dev: null pointer!!\n"); + return; + } + + dev = &urb->dev->dev; + + dev_dbg(dev, " urb :%p\n", urb); + dev_dbg(dev, " dev :%p\n", urb->dev); + + usbip_dump_usb_device(urb->dev); + + dev_dbg(dev, " pipe :%08x ", urb->pipe); + + usbip_dump_pipe(urb->pipe); + + dev_dbg(dev, " status :%d\n", urb->status); + dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags); + dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer); + dev_dbg(dev, " transfer_buffer_length:%d\n", + urb->transfer_buffer_length); + dev_dbg(dev, " actual_length :%d\n", urb->actual_length); + dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet); + + if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL) + usbip_dump_usb_ctrlrequest( + (struct usb_ctrlrequest *)urb->setup_packet); + + dev_dbg(dev, " start_frame :%d\n", urb->start_frame); + dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets); + dev_dbg(dev, " interval :%d\n", urb->interval); + dev_dbg(dev, " error_count :%d\n", urb->error_count); + dev_dbg(dev, " context :%p\n", urb->context); + dev_dbg(dev, " complete :%p\n", urb->complete); +} +EXPORT_SYMBOL_GPL(usbip_dump_urb); + +void usbip_dump_header(struct usbip_header *pdu) +{ + pr_debug("BASE: cmd %u seq %u devid %u dir %u ep %u\n", + pdu->base.command, + pdu->base.seqnum, + pdu->base.devid, + pdu->base.direction, + pdu->base.ep); + + switch (pdu->base.command) { + case USBIP_CMD_SUBMIT: + pr_debug("USBIP_CMD_SUBMIT: x_flags %u x_len %u sf %u #p %d iv %d\n", + pdu->u.cmd_submit.transfer_flags, + pdu->u.cmd_submit.transfer_buffer_length, + pdu->u.cmd_submit.start_frame, + pdu->u.cmd_submit.number_of_packets, + pdu->u.cmd_submit.interval); + break; + case USBIP_CMD_UNLINK: + pr_debug("USBIP_CMD_UNLINK: seq %u\n", + pdu->u.cmd_unlink.seqnum); + break; + case USBIP_RET_SUBMIT: + pr_debug("USBIP_RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n", + pdu->u.ret_submit.status, + pdu->u.ret_submit.actual_length, + pdu->u.ret_submit.start_frame, + pdu->u.ret_submit.number_of_packets, + pdu->u.ret_submit.error_count); + break; + case USBIP_RET_UNLINK: + pr_debug("USBIP_RET_UNLINK: status %d\n", + pdu->u.ret_unlink.status); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_dump_header); + +/* Receive data over TCP/IP. */ +int usbip_recv(struct socket *sock, void *buf, int size) +{ + int result; + struct msghdr msg; + struct kvec iov; + int total = 0; + + /* for blocks of if (usbip_dbg_flag_xmit) */ + char *bp = buf; + int osize = size; + + usbip_dbg_xmit("enter\n"); + + if (!sock || !buf || !size) { + pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf, + size); + return -EINVAL; + } + + do { + sock->sk->sk_allocation = GFP_NOIO; + iov.iov_base = buf; + iov.iov_len = size; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_NOSIGNAL; + + result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL); + if (result <= 0) { + pr_debug("receive sock %p buf %p size %u ret %d total %d\n", + sock, buf, size, result, total); + goto err; + } + + size -= result; + buf += result; + total += result; + } while (size > 0); + + if (usbip_dbg_flag_xmit) { + if (!in_interrupt()) + pr_debug("%-10s:", current->comm); + else + pr_debug("interrupt :"); + + pr_debug("receiving....\n"); + usbip_dump_buffer(bp, osize); + pr_debug("received, osize %d ret %d size %d total %d\n", + osize, result, size, total); + } + + return total; + +err: + return result; +} +EXPORT_SYMBOL_GPL(usbip_recv); + +/* there may be more cases to tweak the flags. */ +static unsigned int tweak_transfer_flags(unsigned int flags) +{ + flags &= ~URB_NO_TRANSFER_DMA_MAP; + return flags; +} + +static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb, + int pack) +{ + struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit; + + /* + * Some members are not still implemented in usbip. I hope this issue + * will be discussed when usbip is ported to other operating systems. + */ + if (pack) { + spdu->transfer_flags = + tweak_transfer_flags(urb->transfer_flags); + spdu->transfer_buffer_length = urb->transfer_buffer_length; + spdu->start_frame = urb->start_frame; + spdu->number_of_packets = urb->number_of_packets; + spdu->interval = urb->interval; + } else { + urb->transfer_flags = spdu->transfer_flags; + urb->transfer_buffer_length = spdu->transfer_buffer_length; + urb->start_frame = spdu->start_frame; + urb->number_of_packets = spdu->number_of_packets; + urb->interval = spdu->interval; + } +} + +static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, + int pack) +{ + struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit; + + if (pack) { + rpdu->status = urb->status; + rpdu->actual_length = urb->actual_length; + rpdu->start_frame = urb->start_frame; + rpdu->number_of_packets = urb->number_of_packets; + rpdu->error_count = urb->error_count; + } else { + urb->status = rpdu->status; + urb->actual_length = rpdu->actual_length; + urb->start_frame = rpdu->start_frame; + urb->number_of_packets = rpdu->number_of_packets; + urb->error_count = rpdu->error_count; + } +} + +void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, + int pack) +{ + switch (cmd) { + case USBIP_CMD_SUBMIT: + usbip_pack_cmd_submit(pdu, urb, pack); + break; + case USBIP_RET_SUBMIT: + usbip_pack_ret_submit(pdu, urb, pack); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_pack_pdu); + +static void correct_endian_basic(struct usbip_header_basic *base, int send) +{ + if (send) { + base->command = cpu_to_be32(base->command); + base->seqnum = cpu_to_be32(base->seqnum); + base->devid = cpu_to_be32(base->devid); + base->direction = cpu_to_be32(base->direction); + base->ep = cpu_to_be32(base->ep); + } else { + base->command = be32_to_cpu(base->command); + base->seqnum = be32_to_cpu(base->seqnum); + base->devid = be32_to_cpu(base->devid); + base->direction = be32_to_cpu(base->direction); + base->ep = be32_to_cpu(base->ep); + } +} + +static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu, + int send) +{ + if (send) { + pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags); + + cpu_to_be32s(&pdu->transfer_buffer_length); + cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); + cpu_to_be32s(&pdu->interval); + } else { + pdu->transfer_flags = be32_to_cpu(pdu->transfer_flags); + + be32_to_cpus(&pdu->transfer_buffer_length); + be32_to_cpus(&pdu->start_frame); + be32_to_cpus(&pdu->number_of_packets); + be32_to_cpus(&pdu->interval); + } +} + +static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, + int send) +{ + if (send) { + cpu_to_be32s(&pdu->status); + cpu_to_be32s(&pdu->actual_length); + cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); + cpu_to_be32s(&pdu->error_count); + } else { + be32_to_cpus(&pdu->status); + be32_to_cpus(&pdu->actual_length); + be32_to_cpus(&pdu->start_frame); + be32_to_cpus(&pdu->number_of_packets); + be32_to_cpus(&pdu->error_count); + } +} + +static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu, + int send) +{ + if (send) + pdu->seqnum = cpu_to_be32(pdu->seqnum); + else + pdu->seqnum = be32_to_cpu(pdu->seqnum); +} + +static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu, + int send) +{ + if (send) + cpu_to_be32s(&pdu->status); + else + be32_to_cpus(&pdu->status); +} + +void usbip_header_correct_endian(struct usbip_header *pdu, int send) +{ + __u32 cmd = 0; + + if (send) + cmd = pdu->base.command; + + correct_endian_basic(&pdu->base, send); + + if (!send) + cmd = pdu->base.command; + + switch (cmd) { + case USBIP_CMD_SUBMIT: + correct_endian_cmd_submit(&pdu->u.cmd_submit, send); + break; + case USBIP_RET_SUBMIT: + correct_endian_ret_submit(&pdu->u.ret_submit, send); + break; + case USBIP_CMD_UNLINK: + correct_endian_cmd_unlink(&pdu->u.cmd_unlink, send); + break; + case USBIP_RET_UNLINK: + correct_endian_ret_unlink(&pdu->u.ret_unlink, send); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_header_correct_endian); + +static void usbip_iso_packet_correct_endian( + struct usbip_iso_packet_descriptor *iso, int send) +{ + /* does not need all members. but copy all simply. */ + if (send) { + iso->offset = cpu_to_be32(iso->offset); + iso->length = cpu_to_be32(iso->length); + iso->status = cpu_to_be32(iso->status); + iso->actual_length = cpu_to_be32(iso->actual_length); + } else { + iso->offset = be32_to_cpu(iso->offset); + iso->length = be32_to_cpu(iso->length); + iso->status = be32_to_cpu(iso->status); + iso->actual_length = be32_to_cpu(iso->actual_length); + } +} + +static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso, + struct usb_iso_packet_descriptor *uiso, int pack) +{ + if (pack) { + iso->offset = uiso->offset; + iso->length = uiso->length; + iso->status = uiso->status; + iso->actual_length = uiso->actual_length; + } else { + uiso->offset = iso->offset; + uiso->length = iso->length; + uiso->status = iso->status; + uiso->actual_length = iso->actual_length; + } +} + +/* must free buffer */ +struct usbip_iso_packet_descriptor* +usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen) +{ + struct usbip_iso_packet_descriptor *iso; + int np = urb->number_of_packets; + ssize_t size = np * sizeof(*iso); + int i; + + iso = kzalloc(size, GFP_KERNEL); + if (!iso) + return NULL; + + for (i = 0; i < np; i++) { + usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1); + usbip_iso_packet_correct_endian(&iso[i], 1); + } + + *bufflen = size; + + return iso; +} +EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu); + +/* some members of urb must be substituted before. */ +int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) +{ + void *buff; + struct usbip_iso_packet_descriptor *iso; + int np = urb->number_of_packets; + int size = np * sizeof(*iso); + int i; + int ret; + int total_length = 0; + + if (!usb_pipeisoc(urb->pipe)) + return 0; + + /* my Bluetooth dongle gets ISO URBs which are np = 0 */ + if (np == 0) + return 0; + + buff = kzalloc(size, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + ret = usbip_recv(ud->tcp_socket, buff, size); + if (ret != size) { + dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n", + ret); + kfree(buff); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + + iso = (struct usbip_iso_packet_descriptor *) buff; + for (i = 0; i < np; i++) { + usbip_iso_packet_correct_endian(&iso[i], 0); + usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0); + total_length += urb->iso_frame_desc[i].actual_length; + } + + kfree(buff); + + if (total_length != urb->actual_length) { + dev_err(&urb->dev->dev, + "total length of iso packets %d not equal to actual length of buffer %d\n", + total_length, urb->actual_length); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + + return ret; +} +EXPORT_SYMBOL_GPL(usbip_recv_iso); + +/* + * This functions restores the padding which was removed for optimizing + * the bandwidth during transfer over tcp/ip + * + * buffer and iso packets need to be stored and be in propeper endian in urb + * before calling this function + */ +void usbip_pad_iso(struct usbip_device *ud, struct urb *urb) +{ + int np = urb->number_of_packets; + int i; + int actualoffset = urb->actual_length; + + if (!usb_pipeisoc(urb->pipe)) + return; + + /* if no packets or length of data is 0, then nothing to unpack */ + if (np == 0 || urb->actual_length == 0) + return; + + /* + * if actual_length is transfer_buffer_length then no padding is + * present. + */ + if (urb->actual_length == urb->transfer_buffer_length) + return; + + /* + * loop over all packets from last to first (to prevent overwritting + * memory when padding) and move them into the proper place + */ + for (i = np-1; i > 0; i--) { + actualoffset -= urb->iso_frame_desc[i].actual_length; + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->transfer_buffer + actualoffset, + urb->iso_frame_desc[i].actual_length); + } +} +EXPORT_SYMBOL_GPL(usbip_pad_iso); + +/* some members of urb must be substituted before. */ +int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) +{ + int ret; + int size; + + if (ud->side == USBIP_STUB) { + /* the direction of urb must be OUT. */ + if (usb_pipein(urb->pipe)) + return 0; + + size = urb->transfer_buffer_length; + } else { + /* the direction of urb must be IN. */ + if (usb_pipeout(urb->pipe)) + return 0; + + size = urb->actual_length; + } + + /* no need to recv xbuff */ + if (!(size > 0)) + return 0; + + ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); + if (ret != size) { + dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); + if (ud->side == USBIP_STUB) { + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + } else { + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return -EPIPE; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(usbip_recv_xbuff); + +static int __init usbip_core_init(void) +{ + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return 0; +} + +static void __exit usbip_core_exit(void) +{ + return; +} + +module_init(usbip_core_init); +module_exit(usbip_core_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h new file mode 100644 index 000000000000..86b08475c254 --- /dev/null +++ b/drivers/usb/usbip/usbip_common.h @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __USBIP_COMMON_H +#define __USBIP_COMMON_H + +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/net.h> +#include <linux/printk.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/wait.h> +#include <uapi/linux/usbip.h> + +#define USBIP_VERSION "1.0.0" + +#undef pr_fmt + +#ifdef DEBUG +#define pr_fmt(fmt) KBUILD_MODNAME ": %s:%d: " fmt, __func__, __LINE__ +#else +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +enum { + usbip_debug_xmit = (1 << 0), + usbip_debug_sysfs = (1 << 1), + usbip_debug_urb = (1 << 2), + usbip_debug_eh = (1 << 3), + + usbip_debug_stub_cmp = (1 << 8), + usbip_debug_stub_dev = (1 << 9), + usbip_debug_stub_rx = (1 << 10), + usbip_debug_stub_tx = (1 << 11), + + usbip_debug_vhci_rh = (1 << 8), + usbip_debug_vhci_hc = (1 << 9), + usbip_debug_vhci_rx = (1 << 10), + usbip_debug_vhci_tx = (1 << 11), + usbip_debug_vhci_sysfs = (1 << 12) +}; + +#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit) +#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh) +#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc) +#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx) +#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx) +#define usbip_dbg_flag_stub_rx (usbip_debug_flag & usbip_debug_stub_rx) +#define usbip_dbg_flag_stub_tx (usbip_debug_flag & usbip_debug_stub_tx) +#define usbip_dbg_flag_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs) + +extern unsigned long usbip_debug_flag; +extern struct device_attribute dev_attr_usbip_debug; + +#define usbip_dbg_with_flag(flag, fmt, args...) \ + do { \ + if (flag & usbip_debug_flag) \ + pr_debug(fmt, ##args); \ + } while (0) + +#define usbip_dbg_sysfs(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args) +#define usbip_dbg_xmit(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args) +#define usbip_dbg_urb(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args) +#define usbip_dbg_eh(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args) + +#define usbip_dbg_vhci_rh(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args) +#define usbip_dbg_vhci_hc(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args) +#define usbip_dbg_vhci_rx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args) +#define usbip_dbg_vhci_tx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args) +#define usbip_dbg_vhci_sysfs(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args) + +#define usbip_dbg_stub_cmp(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args) +#define usbip_dbg_stub_rx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args) +#define usbip_dbg_stub_tx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args) + +/* + * USB/IP request headers + * + * Each request is transferred across the network to its counterpart, which + * facilitates the normal USB communication. The values contained in the headers + * are basically the same as in a URB. Currently, four request types are + * defined: + * + * - USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb() + * (client to server) + * + * - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT + * (server to client) + * + * - USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT, + * corresponds to usb_unlink_urb() + * (client to server) + * + * - USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK + * (server to client) + * + */ +#define USBIP_CMD_SUBMIT 0x0001 +#define USBIP_CMD_UNLINK 0x0002 +#define USBIP_RET_SUBMIT 0x0003 +#define USBIP_RET_UNLINK 0x0004 + +#define USBIP_DIR_OUT 0x00 +#define USBIP_DIR_IN 0x01 + +/** + * struct usbip_header_basic - data pertinent to every request + * @command: the usbip request type + * @seqnum: sequential number that identifies requests; incremented per + * connection + * @devid: specifies a remote USB device uniquely instead of busnum and devnum; + * in the stub driver, this value is ((busnum << 16) | devnum) + * @direction: direction of the transfer + * @ep: endpoint number + */ +struct usbip_header_basic { + __u32 command; + __u32 seqnum; + __u32 devid; + __u32 direction; + __u32 ep; +} __packed; + +/** + * struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header + * @transfer_flags: URB flags + * @transfer_buffer_length: the data size for (in) or (out) transfer + * @start_frame: initial frame for isochronous or interrupt transfers + * @number_of_packets: number of isochronous packets + * @interval: maximum time for the request on the server-side host controller + * @setup: setup data for a control request + */ +struct usbip_header_cmd_submit { + __u32 transfer_flags; + __s32 transfer_buffer_length; + + /* it is difficult for usbip to sync frames (reserved only?) */ + __s32 start_frame; + __s32 number_of_packets; + __s32 interval; + + unsigned char setup[8]; +} __packed; + +/** + * struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header + * @status: return status of a non-iso request + * @actual_length: number of bytes transferred + * @start_frame: initial frame for isochronous or interrupt transfers + * @number_of_packets: number of isochronous packets + * @error_count: number of errors for isochronous transfers + */ +struct usbip_header_ret_submit { + __s32 status; + __s32 actual_length; + __s32 start_frame; + __s32 number_of_packets; + __s32 error_count; +} __packed; + +/** + * struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header + * @seqnum: the URB seqnum to unlink + */ +struct usbip_header_cmd_unlink { + __u32 seqnum; +} __packed; + +/** + * struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header + * @status: return status of the request + */ +struct usbip_header_ret_unlink { + __s32 status; +} __packed; + +/** + * struct usbip_header - common header for all usbip packets + * @base: the basic header + * @u: packet type dependent header + */ +struct usbip_header { + struct usbip_header_basic base; + + union { + struct usbip_header_cmd_submit cmd_submit; + struct usbip_header_ret_submit ret_submit; + struct usbip_header_cmd_unlink cmd_unlink; + struct usbip_header_ret_unlink ret_unlink; + } u; +} __packed; + +/* + * This is the same as usb_iso_packet_descriptor but packed for pdu. + */ +struct usbip_iso_packet_descriptor { + __u32 offset; + __u32 length; /* expected length */ + __u32 actual_length; + __u32 status; +} __packed; + +enum usbip_side { + USBIP_VHCI, + USBIP_STUB, +}; + +/* event handler */ +#define USBIP_EH_SHUTDOWN (1 << 0) +#define USBIP_EH_BYE (1 << 1) +#define USBIP_EH_RESET (1 << 2) +#define USBIP_EH_UNUSABLE (1 << 3) + +#define SDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE) +#define SDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_SUBMIT (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) + +#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE) +#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) + +/* a common structure for stub_device and vhci_device */ +struct usbip_device { + enum usbip_side side; + enum usbip_device_status status; + + /* lock for status */ + spinlock_t lock; + + struct socket *tcp_socket; + + struct task_struct *tcp_rx; + struct task_struct *tcp_tx; + + unsigned long event; + struct task_struct *eh; + wait_queue_head_t eh_waitq; + + struct eh_ops { + void (*shutdown)(struct usbip_device *); + void (*reset)(struct usbip_device *); + void (*unusable)(struct usbip_device *); + } eh_ops; +}; + +#define kthread_get_run(threadfn, data, namefmt, ...) \ +({ \ + struct task_struct *__k \ + = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \ + if (!IS_ERR(__k)) { \ + get_task_struct(__k); \ + wake_up_process(__k); \ + } \ + __k; \ +}) + +#define kthread_stop_put(k) \ + do { \ + kthread_stop(k); \ + put_task_struct(k); \ + } while (0) + +/* usbip_common.c */ +void usbip_dump_urb(struct urb *purb); +void usbip_dump_header(struct usbip_header *pdu); + +int usbip_recv(struct socket *sock, void *buf, int size); + +void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, + int pack); +void usbip_header_correct_endian(struct usbip_header *pdu, int send); + +struct usbip_iso_packet_descriptor* +usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); + +/* some members of urb must be substituted before. */ +int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); +void usbip_pad_iso(struct usbip_device *ud, struct urb *urb); +int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); + +/* usbip_event.c */ +int usbip_start_eh(struct usbip_device *ud); +void usbip_stop_eh(struct usbip_device *ud); +void usbip_event_add(struct usbip_device *ud, unsigned long event); +int usbip_event_happened(struct usbip_device *ud); + +static inline int interface_to_busnum(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + return udev->bus->busnum; +} + +static inline int interface_to_devnum(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + return udev->devnum; +} + +#endif /* __USBIP_COMMON_H */ diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c new file mode 100644 index 000000000000..64933b993d7a --- /dev/null +++ b/drivers/usb/usbip/usbip_event.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/export.h> + +#include "usbip_common.h" + +static int event_handler(struct usbip_device *ud) +{ + usbip_dbg_eh("enter\n"); + + /* + * Events are handled by only this thread. + */ + while (usbip_event_happened(ud)) { + usbip_dbg_eh("pending event %lx\n", ud->event); + + /* + * NOTE: shutdown must come first. + * Shutdown the device. + */ + if (ud->event & USBIP_EH_SHUTDOWN) { + ud->eh_ops.shutdown(ud); + ud->event &= ~USBIP_EH_SHUTDOWN; + } + + /* Reset the device. */ + if (ud->event & USBIP_EH_RESET) { + ud->eh_ops.reset(ud); + ud->event &= ~USBIP_EH_RESET; + } + + /* Mark the device as unusable. */ + if (ud->event & USBIP_EH_UNUSABLE) { + ud->eh_ops.unusable(ud); + ud->event &= ~USBIP_EH_UNUSABLE; + } + + /* Stop the error handler. */ + if (ud->event & USBIP_EH_BYE) + return -1; + } + + return 0; +} + +static int event_handler_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + wait_event_interruptible(ud->eh_waitq, + usbip_event_happened(ud) || + kthread_should_stop()); + usbip_dbg_eh("wakeup\n"); + + if (event_handler(ud) < 0) + break; + } + + return 0; +} + +int usbip_start_eh(struct usbip_device *ud) +{ + init_waitqueue_head(&ud->eh_waitq); + ud->event = 0; + + ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh"); + if (IS_ERR(ud->eh)) { + pr_warn("Unable to start control thread\n"); + return PTR_ERR(ud->eh); + } + + return 0; +} +EXPORT_SYMBOL_GPL(usbip_start_eh); + +void usbip_stop_eh(struct usbip_device *ud) +{ + if (ud->eh == current) + return; /* do not wait for myself */ + + kthread_stop(ud->eh); + usbip_dbg_eh("usbip_eh has finished\n"); +} +EXPORT_SYMBOL_GPL(usbip_stop_eh); + +void usbip_event_add(struct usbip_device *ud, unsigned long event) +{ + unsigned long flags; + + spin_lock_irqsave(&ud->lock, flags); + ud->event |= event; + wake_up(&ud->eh_waitq); + spin_unlock_irqrestore(&ud->lock, flags); +} +EXPORT_SYMBOL_GPL(usbip_event_add); + +int usbip_event_happened(struct usbip_device *ud) +{ + int happened = 0; + + spin_lock(&ud->lock); + if (ud->event != 0) + happened = 1; + spin_unlock(&ud->lock); + + return happened; +} +EXPORT_SYMBOL_GPL(usbip_event_happened); diff --git a/drivers/usb/usbip/usbip_protocol.txt b/drivers/usb/usbip/usbip_protocol.txt new file mode 100644 index 000000000000..16b6fe27284c --- /dev/null +++ b/drivers/usb/usbip/usbip_protocol.txt @@ -0,0 +1,358 @@ +PRELIMINARY DRAFT, MAY CONTAIN MISTAKES! +28 Jun 2011 + +The USB/IP protocol follows a server/client architecture. The server exports the +USB devices and the clients imports them. The device driver for the exported +USB device runs on the client machine. + +The client may ask for the list of the exported USB devices. To get the list the +client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST +packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent +in one or more pieces at the low level transport layer). The server sends back +the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the +TCP/IP connection is closed. + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | OP_REQ_DEVLIST | + | ----------------------------------------------> | + | | + | OP_REP_DEVLIST | + | <---------------------------------------------- | + | | + +Once the client knows the list of exported USB devices it may decide to use one +of them. First the client opens a TCP/IP connection towards the server and +sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the +import was successful the TCP/IP connection remains open and will be used +to transfer the URB traffic between the client and the server. The client may +send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and +USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the +server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively. + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | OP_REQ_IMPORT | + | ----------------------------------------------> | + | | + | OP_REP_IMPORT | + | <---------------------------------------------- | + | | + | | + | USBIP_CMD_SUBMIT(seqnum = n) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = n) | + | <---------------------------------------------- | + | . | + | : | + | | + | USBIP_CMD_SUBMIT(seqnum = m) | + | ----------------------------------------------> | + | | + | USBIP_CMD_SUBMIT(seqnum = m+1) | + | ----------------------------------------------> | + | | + | USBIP_CMD_SUBMIT(seqnum = m+2) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m) | + | <---------------------------------------------- | + | | + | USBIP_CMD_SUBMIT(seqnum = m+3) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m+1) | + | <---------------------------------------------- | + | | + | USBIP_CMD_SUBMIT(seqnum = m+4) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m+2) | + | <---------------------------------------------- | + | . | + | : | + | | + | USBIP_CMD_UNLINK | + | ----------------------------------------------> | + | | + | USBIP_RET_UNLINK | + | <---------------------------------------------- | + | | + +The fields are in network (big endian) byte order meaning that the most significant +byte (MSB) is stored at the lowest address. + + +OP_REQ_DEVLIST: Retrieve the list of exported USB devices. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB + | | | devices. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 + +OP_REP_DEVLIST: Reply with the list of exported USB devices. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0. +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x0005 | Reply code: The list of exported USB devices. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: 0 for OK +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | n | Number of exported devices: 0 means no exported + | | | devices. +-----------+--------+------------+--------------------------------------------------- + 0x0C | | | From now on the exported n devices are described, + | | | if any. If no devices are exported the message + | | | ends with the previous "number of exported + | | | devices" field. +-----------+--------+------------+--------------------------------------------------- + | 256 | | path: Path of the device on the host exporting the + | | | USB device, string closed with zero byte, e.g. + | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" + | | | The unused bytes shall be filled with zero + | | | bytes. +-----------+--------+------------+--------------------------------------------------- + 0x10C | 32 | | busid: Bus ID of the exported device, string + | | | closed with zero byte, e.g. "3-2". The unused + | | | bytes shall be filled with zero bytes. +-----------+--------+------------+--------------------------------------------------- + 0x12C | 4 | | busnum +-----------+--------+------------+--------------------------------------------------- + 0x130 | 4 | | devnum +-----------+--------+------------+--------------------------------------------------- + 0x134 | 4 | | speed +-----------+--------+------------+--------------------------------------------------- + 0x138 | 2 | | idVendor +-----------+--------+------------+--------------------------------------------------- + 0x13A | 2 | | idProduct +-----------+--------+------------+--------------------------------------------------- + 0x13C | 2 | | bcdDevice +-----------+--------+------------+--------------------------------------------------- + 0x13E | 1 | | bDeviceClass +-----------+--------+------------+--------------------------------------------------- + 0x13F | 1 | | bDeviceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x140 | 1 | | bDeviceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x141 | 1 | | bConfigurationValue +-----------+--------+------------+--------------------------------------------------- + 0x142 | 1 | | bNumConfigurations +-----------+--------+------------+--------------------------------------------------- + 0x143 | 1 | | bNumInterfaces +-----------+--------+------------+--------------------------------------------------- + 0x144 | | m_0 | From now on each interface is described, all + | | | together bNumInterfaces times, with the + | | | the following 4 fields: +-----------+--------+------------+--------------------------------------------------- + | 1 | | bInterfaceClass +-----------+--------+------------+--------------------------------------------------- + 0x145 | 1 | | bInterfaceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x146 | 1 | | bInterfaceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x147 | 1 | | padding byte for alignment, shall be set to zero +-----------+--------+------------+--------------------------------------------------- + 0xC + | | | The second exported USB device starts at i=1 + i*0x138 + | | | with the busid field. + m_(i-1)*4 | | | + +OP_REQ_IMPORT: Request to import (attach) a remote USB device. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x8003 | Command code: import a remote USB device. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 +-----------+--------+------------+--------------------------------------------------- + 8 | 32 | | busid: the busid of the exported device on the + | | | remote host. The possible values are taken + | | | from the message field OP_REP_DEVLIST.busid. + | | | A string closed with zero, the unused bytes + | | | shall be filled with zeros. +-----------+--------+------------+--------------------------------------------------- + +OP_REP_IMPORT: Reply to import (attach) a remote USB device. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x0003 | Reply code: Reply to import. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: 0 for OK + | | | 1 for error +-----------+--------+------------+--------------------------------------------------- + 8 | | | From now on comes the details of the imported + | | | device, if the previous status field was OK (0), + | | | otherwise the reply ends with the status field. +-----------+--------+------------+--------------------------------------------------- + | 256 | | path: Path of the device on the host exporting the + | | | USB device, string closed with zero byte, e.g. + | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" + | | | The unused bytes shall be filled with zero + | | | bytes. +-----------+--------+------------+--------------------------------------------------- + 0x108 | 32 | | busid: Bus ID of the exported device, string + | | | closed with zero byte, e.g. "3-2". The unused + | | | bytes shall be filled with zero bytes. +-----------+--------+------------+--------------------------------------------------- + 0x128 | 4 | | busnum +-----------+--------+------------+--------------------------------------------------- + 0x12C | 4 | | devnum +-----------+--------+------------+--------------------------------------------------- + 0x130 | 4 | | speed +-----------+--------+------------+--------------------------------------------------- + 0x134 | 2 | | idVendor +-----------+--------+------------+--------------------------------------------------- + 0x136 | 2 | | idProduct +-----------+--------+------------+--------------------------------------------------- + 0x138 | 2 | | bcdDevice +-----------+--------+------------+--------------------------------------------------- + 0x139 | 1 | | bDeviceClass +-----------+--------+------------+--------------------------------------------------- + 0x13A | 1 | | bDeviceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x13B | 1 | | bDeviceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x13C | 1 | | bConfigurationValue +-----------+--------+------------+--------------------------------------------------- + 0x13D | 1 | | bNumConfigurations +-----------+--------+------------+--------------------------------------------------- + 0x13E | 1 | | bNumInterfaces + +USBIP_CMD_SUBMIT: Submit an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000001 | command: Submit an URB +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: the sequence number of the URB to submit +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number, possible values are: 0...15 +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | transfer_flags: possible values depend on the + | | | URB transfer type, see below +-----------+--------+------------+--------------------------------------------------- + 0x18 | 4 | | transfer_buffer_length +-----------+--------+------------+--------------------------------------------------- + 0x1C | 4 | | start_frame: specify the selected frame to + | | | transmit an ISO frame, ignored if URB_ISO_ASAP + | | | is specified at transfer_flags +-----------+--------+------------+--------------------------------------------------- + 0x20 | 4 | | number_of_packets: number of ISO packets +-----------+--------+------------+--------------------------------------------------- + 0x24 | 4 | | interval: maximum time for the request on the + | | | server-side host controller +-----------+--------+------------+--------------------------------------------------- + 0x28 | 8 | | setup: data bytes for USB setup, filled with + | | | zeros if not used +-----------+--------+------------+--------------------------------------------------- + 0x30 | | | URB data. For ISO transfers the padding between + | | | each ISO packets is not transmitted. + + + Allowed transfer_flags | value | control | interrupt | bulk | isochronous + -------------------------+------------+---------+-----------+----------+------------- + URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no + URB_ISO_ASAP | 0x00000002 | no | no | no | yes + URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes + URB_NO_FSBR | 0x00000020 | yes | no | no | no + URB_ZERO_PACKET | 0x00000040 | no | no | only out | no + URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes + URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes + URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes + + +USBIP_RET_SUBMIT: Reply for submitting an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000003 | command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: URB sequence number +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | status: zero for successful URB transaction, + | | | otherwise some kind of error happened. +-----------+--------+------------+--------------------------------------------------- + 0x18 | 4 | n | actual_length: number of URB data bytes +-----------+--------+------------+--------------------------------------------------- + 0x1C | 4 | | start_frame: for an ISO frame the actually + | | | selected frame for transmit. +-----------+--------+------------+--------------------------------------------------- + 0x20 | 4 | | number_of_packets +-----------+--------+------------+--------------------------------------------------- + 0x24 | 4 | | error_count +-----------+--------+------------+--------------------------------------------------- + 0x28 | 8 | | setup: data bytes for USB setup, filled with + | | | zeros if not used +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. + +USBIP_CMD_UNLINK: Unlink an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000002 | command: URB unlink command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so? +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number: zero +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | seqnum: the URB sequence number given previously + | | | at USBIP_CMD_SUBMIT.seqnum field +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. + +USBIP_RET_UNLINK: Reply for URB unlink + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000004 | command: reply for the URB unlink command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: the unlinked URB sequence number +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | status: This is the value contained in the + | | | urb->status in the URB completition handler. + | | | FIXME: a better explanation needed. +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h new file mode 100644 index 000000000000..a863a98a91ce --- /dev/null +++ b/drivers/usb/usbip/vhci.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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. + * + */ + +#ifndef __USBIP_VHCI_H +#define __USBIP_VHCI_H + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/wait.h> + +struct vhci_device { + struct usb_device *udev; + + /* + * devid specifies a remote usb device uniquely instead + * of combination of busnum and devnum. + */ + __u32 devid; + + /* speed of a remote device */ + enum usb_device_speed speed; + + /* vhci root-hub port to which this device is attached */ + __u32 rhport; + + struct usbip_device ud; + + /* lock for the below link lists */ + spinlock_t priv_lock; + + /* vhci_priv is linked to one of them. */ + struct list_head priv_tx; + struct list_head priv_rx; + + /* vhci_unlink is linked to one of them */ + struct list_head unlink_tx; + struct list_head unlink_rx; + + /* vhci_tx thread sleeps for this queue */ + wait_queue_head_t waitq_tx; +}; + +/* urb->hcpriv, use container_of() */ +struct vhci_priv { + unsigned long seqnum; + struct list_head list; + + struct vhci_device *vdev; + struct urb *urb; +}; + +struct vhci_unlink { + /* seqnum of this request */ + unsigned long seqnum; + + struct list_head list; + + /* seqnum of the unlink target */ + unsigned long unlink_seqnum; +}; + +/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */ +#define VHCI_NPORTS 8 + +/* for usb_bus.hcpriv */ +struct vhci_hcd { + spinlock_t lock; + + u32 port_status[VHCI_NPORTS]; + + unsigned resuming:1; + unsigned long re_timeout; + + atomic_t seqnum; + + /* + * NOTE: + * wIndex shows the port number and begins from 1. + * But, the index of this array begins from 0. + */ + struct vhci_device vdev[VHCI_NPORTS]; +}; + +extern struct vhci_hcd *the_controller; +extern const struct attribute_group dev_attr_group; + +/* vhci_hcd.c */ +void rh_port_connect(int rhport, enum usb_device_speed speed); + +/* vhci_rx.c */ +struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum); +int vhci_rx_loop(void *data); + +/* vhci_tx.c */ +int vhci_tx_loop(void *data); + +static inline struct vhci_device *port_to_vdev(__u32 port) +{ + return &the_controller->vdev[port]; +} + +static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd) +{ + return (struct vhci_hcd *) (hcd->hcd_priv); +} + +static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci) +{ + return container_of((void *) vhci, struct usb_hcd, hcd_priv); +} + +static inline struct device *vhci_dev(struct vhci_hcd *vhci) +{ + return vhci_to_hcd(vhci)->self.controller; +} + +#endif /* __USBIP_VHCI_H */ diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c new file mode 100644 index 000000000000..c02374b6049c --- /dev/null +++ b/drivers/usb/usbip/vhci_hcd.c @@ -0,0 +1,1171 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/init.h> +#include <linux/file.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi" +#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver" + +/* + * TODO + * - update root hub emulation + * - move the emulation code to userland ? + * porting to other operating systems + * minimize kernel code + * - add suspend/resume code + * - clean up everything + */ + +/* See usb gadget dummy hcd */ + +static int vhci_hub_status(struct usb_hcd *hcd, char *buff); +static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buff, u16 wLength); +static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); +static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); +static int vhci_start(struct usb_hcd *vhci_hcd); +static void vhci_stop(struct usb_hcd *hcd); +static int vhci_get_frame_number(struct usb_hcd *hcd); + +static const char driver_name[] = "vhci_hcd"; +static const char driver_desc[] = "USB/IP Virtual Host Controller"; + +struct vhci_hcd *the_controller; + +static const char * const bit_desc[] = { + "CONNECTION", /*0*/ + "ENABLE", /*1*/ + "SUSPEND", /*2*/ + "OVER_CURRENT", /*3*/ + "RESET", /*4*/ + "R5", /*5*/ + "R6", /*6*/ + "R7", /*7*/ + "POWER", /*8*/ + "LOWSPEED", /*9*/ + "HIGHSPEED", /*10*/ + "PORT_TEST", /*11*/ + "INDICATOR", /*12*/ + "R13", /*13*/ + "R14", /*14*/ + "R15", /*15*/ + "C_CONNECTION", /*16*/ + "C_ENABLE", /*17*/ + "C_SUSPEND", /*18*/ + "C_OVER_CURRENT", /*19*/ + "C_RESET", /*20*/ + "R21", /*21*/ + "R22", /*22*/ + "R23", /*23*/ + "R24", /*24*/ + "R25", /*25*/ + "R26", /*26*/ + "R27", /*27*/ + "R28", /*28*/ + "R29", /*29*/ + "R30", /*30*/ + "R31", /*31*/ +}; + +static void dump_port_status_diff(u32 prev_status, u32 new_status) +{ + int i = 0; + u32 bit = 1; + + pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status); + while (bit) { + u32 prev = prev_status & bit; + u32 new = new_status & bit; + char change; + + if (!prev && new) + change = '+'; + else if (prev && !new) + change = '-'; + else + change = ' '; + + if (prev || new) + pr_debug(" %c%s\n", change, bit_desc[i]); + bit <<= 1; + i++; + } + pr_debug("\n"); +} + +void rh_port_connect(int rhport, enum usb_device_speed speed) +{ + usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); + + spin_lock(&the_controller->lock); + + the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION + | (1 << USB_PORT_FEAT_C_CONNECTION); + + switch (speed) { + case USB_SPEED_HIGH: + the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; + break; + default: + break; + } + + spin_unlock(&the_controller->lock); + + usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); +} + +static void rh_port_disconnect(int rhport) +{ + usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); + + spin_lock(&the_controller->lock); + + the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; + the_controller->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_CONNECTION); + + spin_unlock(&the_controller->lock); + usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); +} + +#define PORT_C_MASK \ + ((USB_PORT_STAT_C_CONNECTION \ + | USB_PORT_STAT_C_ENABLE \ + | USB_PORT_STAT_C_SUSPEND \ + | USB_PORT_STAT_C_OVERCURRENT \ + | USB_PORT_STAT_C_RESET) << 16) + +/* + * Returns 0 if the status hasn't changed, or the number of bytes in buf. + * Ports are 0-indexed from the HCD point of view, + * and 1-indexed from the USB core pointer of view. + * + * @buf: a bitmap to show which port status has been changed. + * bit 0: reserved + * bit 1: the status of port 0 has been changed. + * bit 2: the status of port 1 has been changed. + * ... + */ +static int vhci_hub_status(struct usb_hcd *hcd, char *buf) +{ + struct vhci_hcd *vhci; + int retval; + int rhport; + int changed = 0; + + retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8); + memset(buf, 0, retval); + + vhci = hcd_to_vhci(hcd); + + spin_lock(&vhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd)) { + usbip_dbg_vhci_rh("hw accessible flag not on?\n"); + goto done; + } + + /* check pseudo status register for each port */ + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + if ((vhci->port_status[rhport] & PORT_C_MASK)) { + /* The status of a port has been changed, */ + usbip_dbg_vhci_rh("port %d status changed\n", rhport); + + buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8; + changed = 1; + } + } + + if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1)) + usb_hcd_resume_root_hub(hcd); + +done: + spin_unlock(&vhci->lock); + return changed ? retval : 0; +} + +static inline void hub_descriptor(struct usb_hub_descriptor *desc) +{ + memset(desc, 0, sizeof(*desc)); + desc->bDescriptorType = 0x29; + desc->bDescLength = 9; + desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001)); + desc->bNbrPorts = VHCI_NPORTS; + desc->u.hs.DeviceRemovable[0] = 0xff; + desc->u.hs.DeviceRemovable[1] = 0xff; +} + +static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct vhci_hcd *dum; + int retval = 0; + int rhport; + + u32 prev_port_status[VHCI_NPORTS]; + + if (!HCD_HW_ACCESSIBLE(hcd)) + return -ETIMEDOUT; + + /* + * NOTE: + * wIndex shows the port number and begins from 1. + */ + usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, + wIndex); + if (wIndex > VHCI_NPORTS) + pr_err("invalid port number %d\n", wIndex); + rhport = ((__u8)(wIndex & 0x00ff)) - 1; + + dum = hcd_to_vhci(hcd); + + spin_lock(&dum->lock); + + /* store old status and compare now and old later */ + if (usbip_dbg_flag_vhci_rh) { + memcpy(prev_port_status, dum->port_status, + sizeof(prev_port_status)); + } + + switch (typeReq) { + case ClearHubFeature: + usbip_dbg_vhci_rh(" ClearHubFeature\n"); + break; + case ClearPortFeature: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if (dum->port_status[rhport] & USB_PORT_STAT_SUSPEND) { + /* 20msec signaling */ + dum->resuming = 1; + dum->re_timeout = + jiffies + msecs_to_jiffies(20); + } + break; + case USB_PORT_FEAT_POWER: + usbip_dbg_vhci_rh( + " ClearPortFeature: USB_PORT_FEAT_POWER\n"); + dum->port_status[rhport] = 0; + dum->resuming = 0; + break; + case USB_PORT_FEAT_C_RESET: + usbip_dbg_vhci_rh( + " ClearPortFeature: USB_PORT_FEAT_C_RESET\n"); + switch (dum->vdev[rhport].speed) { + case USB_SPEED_HIGH: + dum->port_status[rhport] |= + USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + dum->port_status[rhport] |= + USB_PORT_STAT_LOW_SPEED; + break; + default: + break; + } + default: + usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n", + wValue); + dum->port_status[rhport] &= ~(1 << wValue); + break; + } + break; + case GetHubDescriptor: + usbip_dbg_vhci_rh(" GetHubDescriptor\n"); + hub_descriptor((struct usb_hub_descriptor *) buf); + break; + case GetHubStatus: + usbip_dbg_vhci_rh(" GetHubStatus\n"); + *(__le32 *) buf = cpu_to_le32(0); + break; + case GetPortStatus: + usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); + if (wIndex > VHCI_NPORTS || wIndex < 1) { + pr_err("invalid port number %d\n", wIndex); + retval = -EPIPE; + } + + /* we do not care about resume. */ + + /* whoever resets or resumes must GetPortStatus to + * complete it!! + */ + if (dum->resuming && time_after(jiffies, dum->re_timeout)) { + dum->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_SUSPEND); + dum->port_status[rhport] &= + ~(1 << USB_PORT_FEAT_SUSPEND); + dum->resuming = 0; + dum->re_timeout = 0; + } + + if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) != + 0 && time_after(jiffies, dum->re_timeout)) { + dum->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_RESET); + dum->port_status[rhport] &= + ~(1 << USB_PORT_FEAT_RESET); + dum->re_timeout = 0; + + if (dum->vdev[rhport].ud.status == + VDEV_ST_NOTASSIGNED) { + usbip_dbg_vhci_rh( + " enable rhport %d (status %u)\n", + rhport, + dum->vdev[rhport].ud.status); + dum->port_status[rhport] |= + USB_PORT_STAT_ENABLE; + } + } + ((__le16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]); + ((__le16 *) buf)[1] = + cpu_to_le16(dum->port_status[rhport] >> 16); + + usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0], + ((u16 *)buf)[1]); + break; + case SetHubFeature: + usbip_dbg_vhci_rh(" SetHubFeature\n"); + retval = -EPIPE; + break; + case SetPortFeature: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + usbip_dbg_vhci_rh( + " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); + break; + case USB_PORT_FEAT_RESET: + usbip_dbg_vhci_rh( + " SetPortFeature: USB_PORT_FEAT_RESET\n"); + /* if it's already running, disconnect first */ + if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) { + dum->port_status[rhport] &= + ~(USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED); + /* FIXME test that code path! */ + } + /* 50msec reset signaling */ + dum->re_timeout = jiffies + msecs_to_jiffies(50); + + /* FALLTHROUGH */ + default: + usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", + wValue); + dum->port_status[rhport] |= (1 << wValue); + break; + } + break; + + default: + pr_err("default: no such request\n"); + + /* "protocol stall" on error */ + retval = -EPIPE; + } + + if (usbip_dbg_flag_vhci_rh) { + pr_debug("port %d\n", rhport); + /* Only dump valid port status */ + if (rhport >= 0) { + dump_port_status_diff(prev_port_status[rhport], + dum->port_status[rhport]); + } + } + usbip_dbg_vhci_rh(" bye\n"); + + spin_unlock(&dum->lock); + + return retval; +} + +static struct vhci_device *get_vdev(struct usb_device *udev) +{ + int i; + + if (!udev) + return NULL; + + for (i = 0; i < VHCI_NPORTS; i++) + if (the_controller->vdev[i].udev == udev) + return port_to_vdev(i); + + return NULL; +} + +static void vhci_tx_urb(struct urb *urb) +{ + struct vhci_device *vdev = get_vdev(urb->dev); + struct vhci_priv *priv; + + if (!vdev) { + pr_err("could not get virtual device"); + return; + } + + priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); + if (!priv) { + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); + return; + } + + spin_lock(&vdev->priv_lock); + + priv->seqnum = atomic_inc_return(&the_controller->seqnum); + if (priv->seqnum == 0xffff) + dev_info(&urb->dev->dev, "seqnum max\n"); + + priv->vdev = vdev; + priv->urb = urb; + + urb->hcpriv = (void *) priv; + + list_add_tail(&priv->list, &vdev->priv_tx); + + wake_up(&vdev->waitq_tx); + spin_unlock(&vdev->priv_lock); +} + +static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct device *dev = &urb->dev->dev; + int ret = 0; + struct vhci_device *vdev; + + usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", + hcd, urb, mem_flags); + + /* patch to usb_sg_init() is in 2.5.60 */ + BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); + + spin_lock(&the_controller->lock); + + if (urb->status != -EINPROGRESS) { + dev_err(dev, "URB already unlinked!, status %d\n", urb->status); + spin_unlock(&the_controller->lock); + return urb->status; + } + + vdev = port_to_vdev(urb->dev->portnum-1); + + /* refuse enqueue for dead connection */ + spin_lock(&vdev->ud.lock); + if (vdev->ud.status == VDEV_ST_NULL || + vdev->ud.status == VDEV_ST_ERROR) { + dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport); + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + return -ENODEV; + } + spin_unlock(&vdev->ud.lock); + + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto no_need_unlink; + + /* + * The enumeration process is as follows; + * + * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0) + * to get max packet length of default pipe + * + * 2. Set_Address request to DevAddr(0) EndPoint(0) + * + */ + if (usb_pipedevice(urb->pipe) == 0) { + __u8 type = usb_pipetype(urb->pipe); + struct usb_ctrlrequest *ctrlreq = + (struct usb_ctrlrequest *) urb->setup_packet; + + if (type != PIPE_CONTROL || !ctrlreq) { + dev_err(dev, "invalid request to devnum 0\n"); + ret = -EINVAL; + goto no_need_xmit; + } + + switch (ctrlreq->bRequest) { + case USB_REQ_SET_ADDRESS: + /* set_address may come when a device is reset */ + dev_info(dev, "SetAddress Request (%d) to port %d\n", + ctrlreq->wValue, vdev->rhport); + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = usb_get_dev(urb->dev); + + spin_lock(&vdev->ud.lock); + vdev->ud.status = VDEV_ST_USED; + spin_unlock(&vdev->ud.lock); + + if (urb->status == -EINPROGRESS) { + /* This request is successfully completed. */ + /* If not -EINPROGRESS, possibly unlinked. */ + urb->status = 0; + } + + goto no_need_xmit; + + case USB_REQ_GET_DESCRIPTOR: + if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8)) + usbip_dbg_vhci_hc( + "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n"); + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = usb_get_dev(urb->dev); + goto out; + + default: + /* NOT REACHED */ + dev_err(dev, + "invalid request to devnum 0 bRequest %u, wValue %u\n", + ctrlreq->bRequest, + ctrlreq->wValue); + ret = -EINVAL; + goto no_need_xmit; + } + + } + +out: + vhci_tx_urb(urb); + spin_unlock(&the_controller->lock); + + return 0; + +no_need_xmit: + usb_hcd_unlink_urb_from_ep(hcd, urb); +no_need_unlink: + spin_unlock(&the_controller->lock); + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + return ret; +} + +/* + * vhci_rx gives back the urb after receiving the reply of the urb. If an + * unlink pdu is sent or not, vhci_rx receives a normal return pdu and gives + * back its urb. For the driver unlinking the urb, the content of the urb is + * not important, but the calling to its completion handler is important; the + * completion of unlinking is notified by the completion handler. + * + * + * CLIENT SIDE + * + * - When vhci_hcd receives RET_SUBMIT, + * + * - case 1a). the urb of the pdu is not unlinking. + * - normal case + * => just give back the urb + * + * - case 1b). the urb of the pdu is unlinking. + * - usbip.ko will return a reply of the unlinking request. + * => give back the urb now and go to case 2b). + * + * - When vhci_hcd receives RET_UNLINK, + * + * - case 2a). a submit request is still pending in vhci_hcd. + * - urb was really pending in usbip.ko and urb_unlink_urb() was + * completed there. + * => free a pending submit request + * => notify unlink completeness by giving back the urb + * + * - case 2b). a submit request is *not* pending in vhci_hcd. + * - urb was already given back to the core driver. + * => do not give back the urb + * + * + * SERVER SIDE + * + * - When usbip receives CMD_UNLINK, + * + * - case 3a). the urb of the unlink request is now in submission. + * => do usb_unlink_urb(). + * => after the unlink is completed, send RET_UNLINK. + * + * - case 3b). the urb of the unlink request is not in submission. + * - may be already completed or never be received + * => send RET_UNLINK + * + */ +static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct vhci_priv *priv; + struct vhci_device *vdev; + + pr_info("dequeue a urb %p\n", urb); + + spin_lock(&the_controller->lock); + + priv = urb->hcpriv; + if (!priv) { + /* URB was never linked! or will be soon given back by + * vhci_rx. */ + spin_unlock(&the_controller->lock); + return 0; + } + + { + int ret = 0; + + ret = usb_hcd_check_unlink_urb(hcd, urb, status); + if (ret) { + spin_unlock(&the_controller->lock); + return ret; + } + } + + /* send unlink request here? */ + vdev = priv->vdev; + + if (!vdev->ud.tcp_socket) { + /* tcp connection is closed */ + spin_lock(&vdev->priv_lock); + + pr_info("device %p seems to be disconnected\n", vdev); + list_del(&priv->list); + kfree(priv); + urb->hcpriv = NULL; + + spin_unlock(&vdev->priv_lock); + + /* + * If tcp connection is alive, we have sent CMD_UNLINK. + * vhci_rx will receive RET_UNLINK and give back the URB. + * Otherwise, we give back it here. + */ + pr_info("gives back urb %p\n", urb); + + usb_hcd_unlink_urb_from_ep(hcd, urb); + + spin_unlock(&the_controller->lock); + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + spin_lock(&the_controller->lock); + + } else { + /* tcp connection is alive */ + struct vhci_unlink *unlink; + + spin_lock(&vdev->priv_lock); + + /* setup CMD_UNLINK pdu */ + unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); + if (!unlink) { + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); + return -ENOMEM; + } + + unlink->seqnum = atomic_inc_return(&the_controller->seqnum); + if (unlink->seqnum == 0xffff) + pr_info("seqnum max\n"); + + unlink->unlink_seqnum = priv->seqnum; + + pr_info("device %p seems to be still connected\n", vdev); + + /* send cmd_unlink and try to cancel the pending URB in the + * peer */ + list_add_tail(&unlink->list, &vdev->unlink_tx); + wake_up(&vdev->waitq_tx); + + spin_unlock(&vdev->priv_lock); + } + + spin_unlock(&the_controller->lock); + + usbip_dbg_vhci_hc("leave\n"); + return 0; +} + +static void vhci_device_unlink_cleanup(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&the_controller->lock); + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { + pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum); + list_del(&unlink->list); + kfree(unlink); + } + + while (!list_empty(&vdev->unlink_rx)) { + struct urb *urb; + + unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink, + list); + + /* give back URB of unanswered unlink request */ + pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum); + + urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); + if (!urb) { + pr_info("the urb (seqnum %lu) was already given back\n", + unlink->unlink_seqnum); + list_del(&unlink->list); + kfree(unlink); + continue; + } + + urb->status = -ENODEV; + + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + + list_del(&unlink->list); + + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + + spin_lock(&the_controller->lock); + spin_lock(&vdev->priv_lock); + + kfree(unlink); + } + + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); +} + +/* + * The important thing is that only one context begins cleanup. + * This is why error handling and cleanup become simple. + * We do not want to consider race condition as possible. + */ +static void vhci_shutdown_connection(struct usbip_device *ud) +{ + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + /* need this? see stub_dev.c */ + if (ud->tcp_socket) { + pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket); + kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); + } + + /* kill threads related to this sdev */ + if (vdev->ud.tcp_rx) { + kthread_stop_put(vdev->ud.tcp_rx); + vdev->ud.tcp_rx = NULL; + } + if (vdev->ud.tcp_tx) { + kthread_stop_put(vdev->ud.tcp_tx); + vdev->ud.tcp_tx = NULL; + } + pr_info("stop threads\n"); + + /* active connection is closed */ + if (vdev->ud.tcp_socket) { + sockfd_put(vdev->ud.tcp_socket); + vdev->ud.tcp_socket = NULL; + } + pr_info("release socket\n"); + + vhci_device_unlink_cleanup(vdev); + + /* + * rh_port_disconnect() is a trigger of ... + * usb_disable_device(): + * disable all the endpoints for a USB device. + * usb_disable_endpoint(): + * disable endpoints. pending urbs are unlinked(dequeued). + * + * NOTE: After calling rh_port_disconnect(), the USB device drivers of a + * detached device should release used urbs in a cleanup function (i.e. + * xxx_disconnect()). Therefore, vhci_hcd does not need to release + * pushed urbs and their private data in this function. + * + * NOTE: vhci_dequeue() must be considered carefully. When shutting down + * a connection, vhci_shutdown_connection() expects vhci_dequeue() + * gives back pushed urbs and frees their private data by request of + * the cleanup function of a USB driver. When unlinking a urb with an + * active connection, vhci_dequeue() does not give back the urb which + * is actually given back by vhci_rx after receiving its return pdu. + * + */ + rh_port_disconnect(vdev->rhport); + + pr_info("disconnect device\n"); +} + + +static void vhci_device_reset(struct usbip_device *ud) +{ + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + spin_lock(&ud->lock); + + vdev->speed = 0; + vdev->devid = 0; + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = NULL; + + if (ud->tcp_socket) { + sockfd_put(ud->tcp_socket); + ud->tcp_socket = NULL; + } + ud->status = VDEV_ST_NULL; + + spin_unlock(&ud->lock); +} + +static void vhci_device_unusable(struct usbip_device *ud) +{ + spin_lock(&ud->lock); + ud->status = VDEV_ST_ERROR; + spin_unlock(&ud->lock); +} + +static void vhci_device_init(struct vhci_device *vdev) +{ + memset(vdev, 0, sizeof(*vdev)); + + vdev->ud.side = USBIP_VHCI; + vdev->ud.status = VDEV_ST_NULL; + spin_lock_init(&vdev->ud.lock); + + INIT_LIST_HEAD(&vdev->priv_rx); + INIT_LIST_HEAD(&vdev->priv_tx); + INIT_LIST_HEAD(&vdev->unlink_tx); + INIT_LIST_HEAD(&vdev->unlink_rx); + spin_lock_init(&vdev->priv_lock); + + init_waitqueue_head(&vdev->waitq_tx); + + vdev->ud.eh_ops.shutdown = vhci_shutdown_connection; + vdev->ud.eh_ops.reset = vhci_device_reset; + vdev->ud.eh_ops.unusable = vhci_device_unusable; + + usbip_start_eh(&vdev->ud); +} + +static int vhci_start(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rhport; + int err = 0; + + usbip_dbg_vhci_hc("enter vhci_start\n"); + + /* initialize private data of usb_hcd */ + + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + struct vhci_device *vdev = &vhci->vdev[rhport]; + + vhci_device_init(vdev); + vdev->rhport = rhport; + } + + atomic_set(&vhci->seqnum, 0); + spin_lock_init(&vhci->lock); + + hcd->power_budget = 0; /* no limit */ + hcd->uses_new_polling = 1; + + /* vhci_hcd is now ready to be controlled through sysfs */ + err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group); + if (err) { + pr_err("create sysfs files\n"); + return err; + } + + return 0; +} + +static void vhci_stop(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rhport = 0; + + usbip_dbg_vhci_hc("stop VHCI controller\n"); + + /* 1. remove the userland interface of vhci_hcd */ + sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group); + + /* 2. shutdown all the ports of vhci_hcd */ + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + struct vhci_device *vdev = &vhci->vdev[rhport]; + + usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED); + usbip_stop_eh(&vdev->ud); + } +} + +static int vhci_get_frame_number(struct usb_hcd *hcd) +{ + pr_err("Not yet implemented\n"); + return 0; +} + +#ifdef CONFIG_PM + +/* FIXME: suspend/resume */ +static int vhci_bus_suspend(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); + + spin_lock(&vhci->lock); + hcd->state = HC_STATE_SUSPENDED; + spin_unlock(&vhci->lock); + + return 0; +} + +static int vhci_bus_resume(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rc = 0; + + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); + + spin_lock(&vhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd)) + rc = -ESHUTDOWN; + else + hcd->state = HC_STATE_RUNNING; + spin_unlock(&vhci->lock); + + return rc; +} + +#else + +#define vhci_bus_suspend NULL +#define vhci_bus_resume NULL +#endif + +static struct hc_driver vhci_hc_driver = { + .description = driver_name, + .product_desc = driver_desc, + .hcd_priv_size = sizeof(struct vhci_hcd), + + .flags = HCD_USB2, + + .start = vhci_start, + .stop = vhci_stop, + + .urb_enqueue = vhci_urb_enqueue, + .urb_dequeue = vhci_urb_dequeue, + + .get_frame_number = vhci_get_frame_number, + + .hub_status_data = vhci_hub_status, + .hub_control = vhci_hub_control, + .bus_suspend = vhci_bus_suspend, + .bus_resume = vhci_bus_resume, +}; + +static int vhci_hcd_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + int ret; + + usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id); + + /* + * Allocate and initialize hcd. + * Our private data is also allocated automatically. + */ + hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + pr_err("create hcd failed\n"); + return -ENOMEM; + } + hcd->has_tt = 1; + + /* this is private data for vhci_hcd */ + the_controller = hcd_to_vhci(hcd); + + /* + * Finish generic HCD structure initialization and register. + * Call the driver's reset() and start() routines. + */ + ret = usb_add_hcd(hcd, 0, 0); + if (ret != 0) { + pr_err("usb_add_hcd failed %d\n", ret); + usb_put_hcd(hcd); + the_controller = NULL; + return ret; + } + + usbip_dbg_vhci_hc("bye\n"); + return 0; +} + +static int vhci_hcd_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + + hcd = platform_get_drvdata(pdev); + if (!hcd) + return 0; + + /* + * Disconnects the root hub, + * then reverses the effects of usb_add_hcd(), + * invoking the HCD's stop() methods. + */ + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + the_controller = NULL; + + return 0; +} + +#ifdef CONFIG_PM + +/* what should happen for USB/IP under suspend/resume? */ +static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd; + int rhport = 0; + int connected = 0; + int ret = 0; + + hcd = platform_get_drvdata(pdev); + + spin_lock(&the_controller->lock); + + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) + if (the_controller->port_status[rhport] & + USB_PORT_STAT_CONNECTION) + connected += 1; + + spin_unlock(&the_controller->lock); + + if (connected > 0) { + dev_info(&pdev->dev, + "We have %d active connection%s. Do not suspend.\n", + connected, (connected == 1 ? "" : "s")); + ret = -EBUSY; + } else { + dev_info(&pdev->dev, "suspend vhci_hcd"); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + } + + return ret; +} + +static int vhci_hcd_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + hcd = platform_get_drvdata(pdev); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_poll_rh_status(hcd); + + return 0; +} + +#else + +#define vhci_hcd_suspend NULL +#define vhci_hcd_resume NULL + +#endif + +static struct platform_driver vhci_driver = { + .probe = vhci_hcd_probe, + .remove = vhci_hcd_remove, + .suspend = vhci_hcd_suspend, + .resume = vhci_hcd_resume, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, +}; + +/* + * The VHCI 'device' is 'virtual'; not a real plug&play hardware. + * We need to add this virtual device as a platform device arbitrarily: + * 1. platform_device_register() + */ +static void the_pdev_release(struct device *dev) +{ +} + +static struct platform_device the_pdev = { + /* should be the same name as driver_name */ + .name = driver_name, + .id = -1, + .dev = { + .release = the_pdev_release, + }, +}; + +static int __init vhci_hcd_init(void) +{ + int ret; + + if (usb_disabled()) + return -ENODEV; + + ret = platform_driver_register(&vhci_driver); + if (ret) + goto err_driver_register; + + ret = platform_device_register(&the_pdev); + if (ret) + goto err_platform_device_register; + + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return ret; + +err_platform_device_register: + platform_driver_unregister(&vhci_driver); +err_driver_register: + return ret; +} + +static void __exit vhci_hcd_exit(void) +{ + platform_device_unregister(&the_pdev); + platform_driver_unregister(&vhci_driver); +} + +module_init(vhci_hcd_init); +module_exit(vhci_hcd_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c new file mode 100644 index 000000000000..00e4a54308e4 --- /dev/null +++ b/drivers/usb/usbip/vhci_rx.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */ +struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) +{ + struct vhci_priv *priv, *tmp; + struct urb *urb = NULL; + int status; + + list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) { + if (priv->seqnum != seqnum) + continue; + + urb = priv->urb; + status = urb->status; + + usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n", + urb, priv, seqnum); + + switch (status) { + case -ENOENT: + /* fall through */ + case -ECONNRESET: + dev_info(&urb->dev->dev, + "urb %p was unlinked %ssynchronuously.\n", urb, + status == -ENOENT ? "" : "a"); + break; + case -EINPROGRESS: + /* no info output */ + break; + default: + dev_info(&urb->dev->dev, + "urb %p may be in a error, status %d\n", urb, + status); + } + + list_del(&priv->list); + kfree(priv); + urb->hcpriv = NULL; + + break; + } + + return urb; +} + +static void vhci_recv_ret_submit(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct usbip_device *ud = &vdev->ud; + struct urb *urb; + + spin_lock(&vdev->priv_lock); + urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); + spin_unlock(&vdev->priv_lock); + + if (!urb) { + pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); + pr_info("max seqnum %d\n", + atomic_read(&the_controller->seqnum)); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + + /* unpack the pdu to a urb */ + usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0); + + /* recv transfer buffer */ + if (usbip_recv_xbuff(ud, urb) < 0) + return; + + /* recv iso_packet_descriptor */ + if (usbip_recv_iso(ud, urb) < 0) + return; + + /* restore the padding in iso packets */ + usbip_pad_iso(ud, urb); + + if (usbip_dbg_flag_vhci_rx) + usbip_dump_urb(urb); + + usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + + spin_lock(&the_controller->lock); + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + + usbip_dbg_vhci_rx("Leave\n"); +} + +static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { + pr_info("unlink->seqnum %lu\n", unlink->seqnum); + if (unlink->seqnum == pdu->base.seqnum) { + usbip_dbg_vhci_rx("found pending unlink, %lu\n", + unlink->seqnum); + list_del(&unlink->list); + + spin_unlock(&vdev->priv_lock); + return unlink; + } + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static void vhci_recv_ret_unlink(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct vhci_unlink *unlink; + struct urb *urb; + + usbip_dump_header(pdu); + + unlink = dequeue_pending_unlink(vdev, pdu); + if (!unlink) { + pr_info("cannot find the pending unlink %u\n", + pdu->base.seqnum); + return; + } + + spin_lock(&vdev->priv_lock); + urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); + spin_unlock(&vdev->priv_lock); + + if (!urb) { + /* + * I get the result of a unlink request. But, it seems that I + * already received the result of its submit result and gave + * back the URB. + */ + pr_info("the urb (seqnum %d) was already given back\n", + pdu->base.seqnum); + } else { + usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + + /* If unlink is successful, status is -ECONNRESET */ + urb->status = pdu->u.ret_unlink.status; + pr_info("urb->status %d\n", urb->status); + + spin_lock(&the_controller->lock); + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + } + + kfree(unlink); +} + +static int vhci_priv_tx_empty(struct vhci_device *vdev) +{ + int empty = 0; + + spin_lock(&vdev->priv_lock); + empty = list_empty(&vdev->priv_rx); + spin_unlock(&vdev->priv_lock); + + return empty; +} + +/* recv a pdu */ +static void vhci_rx_pdu(struct usbip_device *ud) +{ + int ret; + struct usbip_header pdu; + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + usbip_dbg_vhci_rx("Enter\n"); + + memset(&pdu, 0, sizeof(pdu)); + + /* receive a pdu header */ + ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); + if (ret < 0) { + if (ret == -ECONNRESET) + pr_info("connection reset by peer\n"); + else if (ret == -EAGAIN) { + /* ignore if connection was idle */ + if (vhci_priv_tx_empty(vdev)) + return; + pr_info("connection timed out with pending urbs\n"); + } else if (ret != -ERESTARTSYS) + pr_info("xmit failed %d\n", ret); + + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + if (ret == 0) { + pr_info("connection closed"); + usbip_event_add(ud, VDEV_EVENT_DOWN); + return; + } + if (ret != sizeof(pdu)) { + pr_err("received pdu size is %d, should be %d\n", ret, + (unsigned int)sizeof(pdu)); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + + usbip_header_correct_endian(&pdu, 0); + + if (usbip_dbg_flag_vhci_rx) + usbip_dump_header(&pdu); + + switch (pdu.base.command) { + case USBIP_RET_SUBMIT: + vhci_recv_ret_submit(vdev, &pdu); + break; + case USBIP_RET_UNLINK: + vhci_recv_ret_unlink(vdev, &pdu); + break; + default: + /* NOT REACHED */ + pr_err("unknown pdu %u\n", pdu.base.command); + usbip_dump_header(&pdu); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + break; + } +} + +int vhci_rx_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + vhci_rx_pdu(ud); + } + + return 0; +} diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c new file mode 100644 index 000000000000..211f43f67ea2 --- /dev/null +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/file.h> +#include <linux/net.h> + +#include "usbip_common.h" +#include "vhci.h" + +/* TODO: refine locking ?*/ + +/* Sysfs entry to show port status */ +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *out) +{ + char *s = out; + int i = 0; + + BUG_ON(!the_controller || !out); + + spin_lock(&the_controller->lock); + + /* + * output example: + * prt sta spd dev socket local_busid + * 000 004 000 000 c5a7bb80 1-2.3 + * 001 004 000 000 d8cee980 2-3.4 + * + * IP address can be retrieved from a socket pointer address by looking + * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a + * port number and its peer IP address. + */ + out += sprintf(out, + "prt sta spd bus dev socket local_busid\n"); + + for (i = 0; i < VHCI_NPORTS; i++) { + struct vhci_device *vdev = port_to_vdev(i); + + spin_lock(&vdev->ud.lock); + out += sprintf(out, "%03u %03u ", i, vdev->ud.status); + + if (vdev->ud.status == VDEV_ST_USED) { + out += sprintf(out, "%03u %08x ", + vdev->speed, vdev->devid); + out += sprintf(out, "%16p ", vdev->ud.tcp_socket); + out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); + + } else { + out += sprintf(out, "000 000 000 0000000000000000 0-0"); + } + + out += sprintf(out, "\n"); + spin_unlock(&vdev->ud.lock); + } + + spin_unlock(&the_controller->lock); + + return out - s; +} +static DEVICE_ATTR_RO(status); + +/* Sysfs entry to shutdown a virtual connection */ +static int vhci_port_disconnect(__u32 rhport) +{ + struct vhci_device *vdev; + + usbip_dbg_vhci_sysfs("enter\n"); + + /* lock */ + spin_lock(&the_controller->lock); + + vdev = port_to_vdev(rhport); + + spin_lock(&vdev->ud.lock); + if (vdev->ud.status == VDEV_ST_NULL) { + pr_err("not connected %d\n", vdev->ud.status); + + /* unlock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + return -EINVAL; + } + + /* unlock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); + + return 0; +} + +static ssize_t store_detach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err; + __u32 rhport = 0; + + if (sscanf(buf, "%u", &rhport) != 1) + return -EINVAL; + + /* check rhport */ + if (rhport >= VHCI_NPORTS) { + dev_err(dev, "invalid port %u\n", rhport); + return -EINVAL; + } + + err = vhci_port_disconnect(rhport); + if (err < 0) + return -EINVAL; + + usbip_dbg_vhci_sysfs("Leave\n"); + + return count; +} +static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); + +/* Sysfs entry to establish a virtual connection */ +static int valid_args(__u32 rhport, enum usb_device_speed speed) +{ + /* check rhport */ + if (rhport >= VHCI_NPORTS) { + pr_err("port %u\n", rhport); + return -EINVAL; + } + + /* check speed */ + switch (speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: + break; + default: + pr_err("Failed attach request for unsupported USB speed: %s\n", + usb_speed_string(speed)); + return -EINVAL; + } + + return 0; +} + +/* + * To start a new USB/IP attachment, a userland program needs to setup a TCP + * connection and then write its socket descriptor with remote device + * information into this sysfs file. + * + * A remote device is virtually attached to the root-hub port of @rhport with + * @speed. @devid is embedded into a request to specify the remote device in a + * server host. + * + * write() returns 0 on success, else negative errno. + */ +static ssize_t store_attach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vhci_device *vdev; + struct socket *socket; + int sockfd = 0; + __u32 rhport = 0, devid = 0, speed = 0; + int err; + + /* + * @rhport: port number of vhci_hcd + * @sockfd: socket descriptor of an established TCP connection + * @devid: unique device identifier in a remote host + * @speed: usb device speed in a remote host + */ + if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4) + return -EINVAL; + + usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", + rhport, sockfd, devid, speed); + + /* check received parameters */ + if (valid_args(rhport, speed) < 0) + return -EINVAL; + + /* Extract socket from fd. */ + socket = sockfd_lookup(sockfd, &err); + if (!socket) + return -EINVAL; + + /* now need lock until setting vdev status as used */ + + /* begin a lock */ + spin_lock(&the_controller->lock); + vdev = port_to_vdev(rhport); + spin_lock(&vdev->ud.lock); + + if (vdev->ud.status != VDEV_ST_NULL) { + /* end of the lock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + sockfd_put(socket); + + dev_err(dev, "port %d already used\n", rhport); + return -EINVAL; + } + + dev_info(dev, + "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n", + rhport, sockfd, devid, speed, usb_speed_string(speed)); + + vdev->devid = devid; + vdev->speed = speed; + vdev->ud.tcp_socket = socket; + vdev->ud.status = VDEV_ST_NOTASSIGNED; + + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + /* end the lock */ + + vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx"); + vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx"); + + rh_port_connect(rhport, speed); + + return count; +} +static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); + +static struct attribute *dev_attrs[] = { + &dev_attr_status.attr, + &dev_attr_detach.attr, + &dev_attr_attach.attr, + &dev_attr_usbip_debug.attr, + NULL, +}; + +const struct attribute_group dev_attr_group = { + .attrs = dev_attrs, +}; diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c new file mode 100644 index 000000000000..409fd99f3257 --- /dev/null +++ b/drivers/usb/usbip/vhci_tx.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb) +{ + struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv); + struct vhci_device *vdev = priv->vdev; + + usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n", + usb_pipedevice(urb->pipe), vdev->devid); + + pdup->base.command = USBIP_CMD_SUBMIT; + pdup->base.seqnum = priv->seqnum; + pdup->base.devid = vdev->devid; + pdup->base.direction = usb_pipein(urb->pipe) ? + USBIP_DIR_IN : USBIP_DIR_OUT; + pdup->base.ep = usb_pipeendpoint(urb->pipe); + + usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1); + + if (urb->setup_packet) + memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8); +} + +static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) +{ + struct vhci_priv *priv, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) { + list_move_tail(&priv->list, &vdev->priv_rx); + spin_unlock(&vdev->priv_lock); + return priv; + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static int vhci_send_cmd_submit(struct vhci_device *vdev) +{ + struct vhci_priv *priv = NULL; + + struct msghdr msg; + struct kvec iov[3]; + size_t txsize; + + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(vdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_vhci_tx("setup txdata urb %p\n", urb); + + /* 1. setup usbip_header */ + setup_cmd_submit_pdu(&pdu_header, urb); + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ + if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) { + iov[1].iov_base = urb->transfer_buffer; + iov[1].iov_len = urb->transfer_buffer_length; + txsize += urb->transfer_buffer_length; + } + + /* 3. setup iso_packet_descriptor */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + ssize_t len = 0; + + iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); + if (!iso_buffer) { + usbip_event_add(&vdev->ud, + SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iov[2].iov_base = iso_buffer; + iov[2].iov_len = len; + txsize += len; + } + + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); + kfree(iso_buffer); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); + return -1; + } + + kfree(iso_buffer); + usbip_dbg_vhci_tx("send txdata\n"); + + total_size += txsize; + } + + return total_size; +} + +static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { + list_move_tail(&unlink->list, &vdev->unlink_rx); + spin_unlock(&vdev->priv_lock); + return unlink; + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static int vhci_send_cmd_unlink(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink = NULL; + + struct msghdr msg; + struct kvec iov[3]; + size_t txsize; + + size_t total_size = 0; + + while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) { + int ret; + struct usbip_header pdu_header; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum); + + /* 1. setup usbip_header */ + pdu_header.base.command = USBIP_CMD_UNLINK; + pdu_header.base.seqnum = unlink->seqnum; + pdu_header.base.devid = vdev->devid; + pdu_header.base.ep = 0; + pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum; + + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); + return -1; + } + + usbip_dbg_vhci_tx("send txdata\n"); + + total_size += txsize; + } + + return total_size; +} + +int vhci_tx_loop(void *data) +{ + struct usbip_device *ud = data; + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + while (!kthread_should_stop()) { + if (vhci_send_cmd_submit(vdev) < 0) + break; + + if (vhci_send_cmd_unlink(vdev) < 0) + break; + + wait_event_interruptible(vdev->waitq_tx, + (!list_empty(&vdev->priv_tx) || + !list_empty(&vdev->unlink_tx) || + kthread_should_stop())); + + usbip_dbg_vhci_tx("pending urbs ?, now wake up\n"); + } + + return 0; +} diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/usb/wusbcore/Kconfig index 0e17b966e1b4..348de1d6726e 100644 --- a/drivers/usb/wusbcore/Kconfig +++ b/drivers/usb/wusbcore/Kconfig @@ -3,7 +3,6 @@ # config USB_WUSB tristate "Enable Wireless USB extensions" - depends on PCI depends on UWB select CRYPTO select CRYPTO_BLKCIPHER @@ -18,6 +17,7 @@ config USB_WUSB config USB_WUSB_CBAF tristate "Support WUSB Cable Based Association (CBA)" + depends on USB help Some WUSB devices support Cable Based Association. It's used to enable the secure communication between the host and the diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c index 9a95b2dc6d1b..50ce80d604f3 100644 --- a/drivers/usb/wusbcore/crypto.c +++ b/drivers/usb/wusbcore/crypto.c @@ -222,8 +222,6 @@ static int wusb_ccm_mac(struct crypto_blkcipher *tfm_cbc, WARN_ON(sizeof(ax) != sizeof(struct aes_ccm_block)); result = -ENOMEM; - zero_padding = sizeof(struct aes_ccm_block) - - blen % sizeof(struct aes_ccm_block); zero_padding = blen % sizeof(struct aes_ccm_block); if (zero_padding) zero_padding = sizeof(struct aes_ccm_block) - zero_padding; diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index 0677139c6065..3f4f5fbded55 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -329,7 +329,7 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc, port->wusb_dev = wusb_dev; port->status |= USB_PORT_STAT_CONNECTION; port->change |= USB_PORT_STAT_C_CONNECTION; - /* Now the port status changed to connected; khubd will + /* Now the port status changed to connected; hub_wq will * pick the change up and try to reset the port to bring it to * the enabled state--so this process returns up to the stack * and it calls back into wusbhc_rh_port_reset(). @@ -343,7 +343,7 @@ error_unlock: /* * Disconnect a Wireless USB device from its fake port * - * Marks the port as disconnected so that khubd can pick up the change + * Marks the port as disconnected so that hub_wq can pick up the change * and drops our knowledge about the device. * * Assumes there is a device connected @@ -379,7 +379,7 @@ static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc, wusbhc_gtk_rekey(wusbhc); /* The Wireless USB part has forgotten about the device already; now - * khubd's timer will pick up the disconnection and remove the USB + * hub_wq's timer will pick up the disconnection and remove the USB * device from the system */ } diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c index 95be9953cd47..cc74d669c802 100644 --- a/drivers/usb/wusbcore/security.c +++ b/drivers/usb/wusbcore/security.c @@ -33,6 +33,20 @@ static void wusbhc_gtk_rekey_work(struct work_struct *work); int wusbhc_sec_create(struct wusbhc *wusbhc) { + /* + * WQ is singlethread because we need to serialize rekey operations. + * Use a separate workqueue for security operations instead of the + * wusbd workqueue because security operations may need to communicate + * directly with downstream wireless devices using synchronous URBs. + * If a device is not responding, this could block other host + * controller operations. + */ + wusbhc->wq_security = create_singlethread_workqueue("wusbd_security"); + if (wusbhc->wq_security == NULL) { + pr_err("WUSB-core: Cannot create wusbd_security workqueue\n"); + return -ENOMEM; + } + wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + sizeof(wusbhc->gtk.data); wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY; @@ -48,6 +62,7 @@ int wusbhc_sec_create(struct wusbhc *wusbhc) /* Called when the HC is destroyed */ void wusbhc_sec_destroy(struct wusbhc *wusbhc) { + destroy_workqueue(wusbhc->wq_security); } @@ -596,5 +611,5 @@ void wusbhc_gtk_rekey(struct wusbhc *wusbhc) * and will cause a deadlock. Instead, queue a work item to do * it when the lock is not held */ - queue_work(wusbd, &wusbhc->gtk_rekey_work); + queue_work(wusbhc->wq_security, &wusbhc->gtk_rekey_work); } diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index f2a8d29e17b9..edc7267157f3 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -64,7 +64,7 @@ * * Note much of the activity is difficult to follow. For example a * device connect goes to devconnect, which will cause the "fake" root - * hub port to show a connect and stop there. Then khubd will notice + * hub port to show a connect and stop there. Then hub_wq will notice * and call into the rh.c:hwahc_rc_port_reset() code to authenticate * the device (and this might require user intervention) and enable * the port. diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 3e2e4ed20157..69af4fd9e072 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -459,14 +459,25 @@ static void __wa_xfer_abort_cb(struct urb *urb) __func__, urb->status); if (xfer) { unsigned long flags; - int done; + int done, seg_index = 0; 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, + /* skip done segs. */ + while (seg_index < xfer->segs) { + struct wa_seg *seg = xfer->seg[seg_index]; + + if ((seg->status == WA_SEG_DONE) || + (seg->status == WA_SEG_ERROR)) { + ++seg_index; + } else { + break; + } + } + /* mark remaining segs as aborted. */ + wa_complete_remaining_xfer_segs(xfer, seg_index, WA_SEG_ABORTED); done = __wa_xfer_is_done(xfer); spin_unlock_irqrestore(&xfer->lock, flags); @@ -2602,6 +2613,7 @@ static void wa_buf_in_cb(struct urb *urb) dev = &wa->usb_iface->dev; --(wa->active_buf_in_urbs); active_buf_in_urbs = wa->active_buf_in_urbs; + rpipe = xfer->ep->hcpriv; if (usb_pipeisoc(xfer->urb->pipe)) { struct usb_iso_packet_descriptor *iso_frame_desc = @@ -2659,7 +2671,6 @@ static void wa_buf_in_cb(struct urb *urb) resubmit_dti = (isoc_data_frame_count == urb_frame_count); } else if (active_buf_in_urbs == 0) { - rpipe = xfer->ep->hcpriv; dev_dbg(dev, "xfer %p 0x%08X#%u: data in done (%zu bytes)\n", xfer, wa_xfer_id(xfer), seg->index, @@ -2685,7 +2696,6 @@ static void wa_buf_in_cb(struct urb *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 0x%08X#%u: data in error %d\n", xfer, wa_xfer_id(xfer), seg->index, diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index 2384add45371..41838db7f85c 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h @@ -295,6 +295,9 @@ struct wusbhc { } __attribute__((packed)) gtk; u8 gtk_index; u32 gtk_tkid; + + /* workqueue for WUSB security related tasks. */ + struct workqueue_struct *wq_security; struct work_struct gtk_rekey_work; struct usb_encryption_descriptor *ccm1_etd; |