summaryrefslogtreecommitdiff
path: root/drivers/usb/common
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/common')
-rw-r--r--drivers/usb/common/Makefile12
-rw-r--r--drivers/usb/common/common.c119
-rw-r--r--drivers/usb/common/fsl-dt-fixup.c224
-rw-r--r--drivers/usb/common/fsl-errata.c255
-rw-r--r--drivers/usb/common/usb_urb.c160
5 files changed, 770 insertions, 0 deletions
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
new file mode 100644
index 00000000000..11cc4657a0f
--- /dev/null
+++ b/drivers/usb/common/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2016 Freescale Semiconductor, Inc.
+#
+
+obj-$(CONFIG_$(XPL_)DM_USB) += common.o
+obj-$(CONFIG_USB_ISP1760) += usb_urb.o
+obj-$(CONFIG_USB_MUSB_HOST) += usb_urb.o
+obj-$(CONFIG_USB_MUSB_GADGET) += usb_urb.o
+obj-$(CONFIG_USB_R8A66597_HCD) += usb_urb.o
+obj-$(CONFIG_USB_EHCI_FSL) += fsl-dt-fixup.o fsl-errata.o
+obj-$(CONFIG_USB_XHCI_FSL) += fsl-dt-fixup.o fsl-errata.o
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
new file mode 100644
index 00000000000..13e9a61072a
--- /dev/null
+++ b/drivers/usb/common/common.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Provides code common for host and device side USB.
+ *
+ * (C) Copyright 2016
+ * Texas Instruments Incorporated, <www.ti.com>
+ */
+
+#include <dm.h>
+#include <asm/global_data.h>
+#include <linux/printk.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/phy.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *const usb_dr_modes[] = {
+ [USB_DR_MODE_UNKNOWN] = "",
+ [USB_DR_MODE_HOST] = "host",
+ [USB_DR_MODE_PERIPHERAL] = "peripheral",
+ [USB_DR_MODE_OTG] = "otg",
+};
+
+enum usb_dr_mode usb_get_dr_mode(ofnode node)
+{
+ const char *dr_mode;
+ int i;
+
+ dr_mode = ofnode_read_string(node, "dr_mode");
+ if (!dr_mode) {
+ pr_debug("usb dr_mode not found\n");
+ return USB_DR_MODE_UNKNOWN;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
+ if (!strcmp(dr_mode, usb_dr_modes[i]))
+ return i;
+
+ return USB_DR_MODE_UNKNOWN;
+}
+
+enum usb_dr_mode usb_get_role_switch_default_mode(ofnode node)
+{
+ const char *dr_mode;
+ int i;
+
+ dr_mode = ofnode_read_string(node, "role-switch-default-mode");
+ if (!dr_mode)
+ return USB_DR_MODE_UNKNOWN;
+
+ for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
+ if (!strcmp(dr_mode, usb_dr_modes[i]))
+ return i;
+
+ return USB_DR_MODE_UNKNOWN;
+}
+
+static const char *const speed_names[] = {
+ [USB_SPEED_UNKNOWN] = "UNKNOWN",
+ [USB_SPEED_LOW] = "low-speed",
+ [USB_SPEED_FULL] = "full-speed",
+ [USB_SPEED_HIGH] = "high-speed",
+ [USB_SPEED_WIRELESS] = "wireless",
+ [USB_SPEED_SUPER] = "super-speed",
+ [USB_SPEED_SUPER_PLUS] = "super-speed-plus",
+};
+
+const char *usb_speed_string(enum usb_device_speed speed)
+{
+ if (speed < 0 || speed >= ARRAY_SIZE(speed_names))
+ speed = USB_SPEED_UNKNOWN;
+ return speed_names[speed];
+}
+
+enum usb_device_speed usb_get_maximum_speed(ofnode node)
+{
+ const char *max_speed;
+ int i;
+
+ max_speed = ofnode_read_string(node, "maximum-speed");
+ if (!max_speed) {
+ pr_debug("usb maximum-speed not found\n");
+ return USB_SPEED_UNKNOWN;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(speed_names); i++)
+ if (!strcmp(max_speed, speed_names[i]))
+ return i;
+
+ return USB_SPEED_UNKNOWN;
+}
+
+#if CONFIG_IS_ENABLED(DM_USB)
+static const char *const usbphy_modes[] = {
+ [USBPHY_INTERFACE_MODE_UNKNOWN] = "",
+ [USBPHY_INTERFACE_MODE_UTMI] = "utmi",
+ [USBPHY_INTERFACE_MODE_UTMIW] = "utmi_wide",
+ [USBPHY_INTERFACE_MODE_ULPI] = "ulpi",
+ [USBPHY_INTERFACE_MODE_SERIAL] = "serial",
+ [USBPHY_INTERFACE_MODE_HSIC] = "hsic",
+};
+
+enum usb_phy_interface usb_get_phy_mode(ofnode node)
+{
+ const char *phy_type;
+ int i;
+
+ phy_type = ofnode_get_property(node, "phy_type", NULL);
+ if (!phy_type)
+ return USBPHY_INTERFACE_MODE_UNKNOWN;
+
+ for (i = 0; i < ARRAY_SIZE(usbphy_modes); i++)
+ if (!strcmp(phy_type, usbphy_modes[i]))
+ return i;
+
+ return USBPHY_INTERFACE_MODE_UNKNOWN;
+}
+#endif
diff --git a/drivers/usb/common/fsl-dt-fixup.c b/drivers/usb/common/fsl-dt-fixup.c
new file mode 100644
index 00000000000..6a68bd76c27
--- /dev/null
+++ b/drivers/usb/common/fsl-dt-fixup.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009, 2011 Freescale Semiconductor, Inc.
+ *
+ * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
+ *
+ * Author: Tor Krill tor@excito.com
+ */
+
+#include <log.h>
+#include <usb.h>
+#include <asm/io.h>
+#include <hwconfig.h>
+#include <fsl_errata.h>
+#include <fsl_usb.h>
+#include <fdt_support.h>
+
+/* USB Controllers */
+#define FSL_USB2_MPH "fsl-usb2-mph"
+#define FSL_USB2_DR "fsl-usb2-dr"
+#define CHIPIDEA_USB2 "chipidea,usb2"
+#define SNPS_DWC3 "snps,dwc3"
+
+static const char * const compat_usb_fsl[] = {
+ FSL_USB2_MPH,
+ FSL_USB2_DR,
+ SNPS_DWC3,
+ NULL
+};
+
+static int fdt_usb_get_node_type(void *blob, int start_offset,
+ int *node_offset, const char **node_type)
+{
+ int i;
+ int ret = -ENOENT;
+
+ for (i = 0; compat_usb_fsl[i]; i++) {
+ *node_offset = fdt_node_offset_by_compatible
+ (blob, start_offset,
+ compat_usb_fsl[i]);
+ if (*node_offset >= 0) {
+ *node_type = compat_usb_fsl[i];
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int fdt_fixup_usb_mode_phy_type(void *blob, const char *mode,
+ const char *phy_type, int start_offset)
+{
+ const char *prop_mode = "dr_mode";
+ const char *prop_type = "phy_type";
+ const char *node_type = NULL;
+ int node_offset;
+ int err;
+
+ err = fdt_usb_get_node_type(blob, start_offset,
+ &node_offset, &node_type);
+ if (err < 0)
+ return err;
+
+ if (mode) {
+ err = fdt_setprop(blob, node_offset, prop_mode, mode,
+ strlen(mode) + 1);
+ if (err < 0)
+ printf("WARNING: could not set %s for %s: %s.\n",
+ prop_mode, node_type, fdt_strerror(err));
+ }
+
+ if (phy_type) {
+ err = fdt_setprop(blob, node_offset, prop_type, phy_type,
+ strlen(phy_type) + 1);
+ if (err < 0)
+ printf("WARNING: could not set %s for %s: %s.\n",
+ prop_type, node_type, fdt_strerror(err));
+ }
+
+ return node_offset;
+}
+
+static int fsl_fdt_fixup_usb_erratum(void *blob, const char *prop_erratum,
+ const char *controller_type,
+ int start_offset)
+{
+ int node_offset, err;
+ const char *node_type = NULL;
+ const char *node_name = NULL;
+
+ err = fdt_usb_get_node_type(blob, start_offset,
+ &node_offset, &node_type);
+ if (err < 0)
+ return err;
+
+ if (!strcmp(node_type, FSL_USB2_MPH) || !strcmp(node_type, FSL_USB2_DR))
+ node_name = CHIPIDEA_USB2;
+ else
+ node_name = node_type;
+ if (strcmp(node_name, controller_type))
+ return err;
+
+ err = fdt_setprop(blob, node_offset, prop_erratum, NULL, 0);
+ if (err < 0) {
+ printf("ERROR: could not set %s for %s: %s.\n",
+ prop_erratum, node_type, fdt_strerror(err));
+ }
+
+ return node_offset;
+}
+
+static int fsl_fdt_fixup_erratum(int *usb_erratum_off, void *blob,
+ const char *controller_type, char *str,
+ bool (*has_erratum)(void))
+{
+ char buf[32] = {0};
+
+ snprintf(buf, sizeof(buf), "fsl,usb-erratum-%s", str);
+ if (!has_erratum())
+ return -EINVAL;
+ *usb_erratum_off = fsl_fdt_fixup_usb_erratum(blob, buf, controller_type,
+ *usb_erratum_off);
+ if (*usb_erratum_off < 0)
+ return -ENOSPC;
+ debug("Adding USB erratum %s\n", str);
+ return 0;
+}
+
+void fsl_fdt_fixup_dr_usb(void *blob, struct bd_info *bd)
+{
+ static const char * const modes[] = { "host", "peripheral", "otg" };
+ static const char * const phys[] = { "ulpi", "utmi", "utmi_dual" };
+ int usb_erratum_a006261_off = -1;
+ int usb_erratum_a007075_off = -1;
+ int usb_erratum_a007792_off = -1;
+ int usb_erratum_a005697_off = -1;
+ int usb_erratum_a008751_off = -1;
+ int usb_mode_off = -1;
+ int usb_phy_off = -1;
+ char str[5];
+ int i, j;
+ int ret;
+
+ for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) {
+ const char *dr_mode_type = NULL;
+ const char *dr_phy_type = NULL;
+ int mode_idx = -1, phy_idx = -1;
+
+ snprintf(str, 5, "%s%d", "usb", i);
+ if (hwconfig(str)) {
+ for (j = 0; j < ARRAY_SIZE(modes); j++) {
+ if (hwconfig_subarg_cmp(str, "dr_mode",
+ modes[j])) {
+ mode_idx = j;
+ break;
+ }
+ }
+
+ for (j = 0; j < ARRAY_SIZE(phys); j++) {
+ if (hwconfig_subarg_cmp(str, "phy_type",
+ phys[j])) {
+ phy_idx = j;
+ break;
+ }
+ }
+
+ if (mode_idx < 0 && phy_idx < 0) {
+ printf("WARNING: invalid phy or mode\n");
+ return;
+ }
+
+ if (mode_idx > -1)
+ dr_mode_type = modes[mode_idx];
+
+ if (phy_idx > -1)
+ dr_phy_type = phys[phy_idx];
+ }
+
+ if (has_dual_phy())
+ dr_phy_type = phys[2];
+
+ usb_mode_off = fdt_fixup_usb_mode_phy_type(blob,
+ dr_mode_type, NULL,
+ usb_mode_off);
+
+ if (usb_mode_off < 0)
+ return;
+
+ usb_phy_off = fdt_fixup_usb_mode_phy_type(blob,
+ NULL, dr_phy_type,
+ usb_phy_off);
+
+ if (usb_phy_off < 0)
+ return;
+
+ ret = fsl_fdt_fixup_erratum(&usb_erratum_a006261_off, blob,
+ CHIPIDEA_USB2, "a006261",
+ has_erratum_a006261);
+ if (ret == -ENOSPC)
+ return;
+ ret = fsl_fdt_fixup_erratum(&usb_erratum_a007075_off, blob,
+ CHIPIDEA_USB2, "a007075",
+ has_erratum_a007075);
+ if (ret == -ENOSPC)
+ return;
+ ret = fsl_fdt_fixup_erratum(&usb_erratum_a007792_off, blob,
+ CHIPIDEA_USB2, "a007792",
+ has_erratum_a007792);
+ if (ret == -ENOSPC)
+ return;
+ ret = fsl_fdt_fixup_erratum(&usb_erratum_a005697_off, blob,
+ CHIPIDEA_USB2, "a005697",
+ has_erratum_a005697);
+ if (ret == -ENOSPC)
+ return;
+ ret = fsl_fdt_fixup_erratum(&usb_erratum_a008751_off, blob,
+ SNPS_DWC3, "a008751",
+ has_erratum_a008751);
+ if (ret == -ENOSPC)
+ return;
+
+ }
+}
diff --git a/drivers/usb/common/fsl-errata.c b/drivers/usb/common/fsl-errata.c
new file mode 100644
index 00000000000..89ae73f2ba4
--- /dev/null
+++ b/drivers/usb/common/fsl-errata.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Freescale USB Controller
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ */
+
+#include <hwconfig.h>
+#include <fsl_errata.h>
+#include<fsl_usb.h>
+#if defined(CONFIG_FSL_LSCH2) || defined(CONFIG_FSL_LSCH3) || \
+ defined(CONFIG_ARM)
+#include <asm/arch/clock.h>
+#endif
+
+/* USB Erratum Checking code */
+#if defined(CONFIG_PPC) || defined(CONFIG_ARM)
+bool has_dual_phy(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+ switch (soc) {
+#ifdef CONFIG_PPC
+ case SVR_T1023:
+ case SVR_T1024:
+ case SVR_T1013:
+ case SVR_T1014:
+ return IS_SVR_REV(svr, 1, 0);
+ case SVR_T1040:
+ case SVR_T1042:
+ case SVR_T1020:
+ case SVR_T1022:
+ case SVR_T2080:
+ case SVR_T2081:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 1, 1);
+ case SVR_T4240:
+ case SVR_T4160:
+ case SVR_T4080:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0);
+#endif
+ }
+
+ return false;
+}
+
+bool has_erratum_a005275(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+ if (hwconfig("no_erratum_a005275"))
+ return false;
+
+ switch (soc) {
+#ifdef CONFIG_PPC
+ case SVR_P3041:
+ case SVR_P2041:
+ case SVR_P2040:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 1, 1);
+ case SVR_P5010:
+ case SVR_P5020:
+ case SVR_P5021:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0);
+ case SVR_P5040:
+ case SVR_P1010:
+ return IS_SVR_REV(svr, 1, 0);
+#endif
+ }
+
+ return false;
+}
+
+bool has_erratum_a006261(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+ switch (soc) {
+#ifdef CONFIG_PPC
+ case SVR_P1010:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0);
+ case SVR_P2041:
+ case SVR_P2040:
+ return IS_SVR_REV(svr, 1, 0) ||
+ IS_SVR_REV(svr, 1, 1) ||
+ IS_SVR_REV(svr, 2, 0) || IS_SVR_REV(svr, 2, 1);
+ case SVR_P3041:
+ return IS_SVR_REV(svr, 1, 0) ||
+ IS_SVR_REV(svr, 1, 1) ||
+ IS_SVR_REV(svr, 2, 0) || IS_SVR_REV(svr, 2, 1);
+ case SVR_P5010:
+ case SVR_P5020:
+ case SVR_P5021:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0);
+ case SVR_T4240:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0);
+ case SVR_P5040:
+ return IS_SVR_REV(svr, 1, 0) ||
+ IS_SVR_REV(svr, 2, 0) || IS_SVR_REV(svr, 2, 1);
+#endif
+ }
+
+ return false;
+}
+
+bool has_erratum_a007075(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+ switch (soc) {
+#ifdef CONFIG_PPC
+ case SVR_B4860:
+ case SVR_B4420:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0);
+ case SVR_P1010:
+ return IS_SVR_REV(svr, 1, 0);
+ case SVR_P4080:
+ return IS_SVR_REV(svr, 2, 0) || IS_SVR_REV(svr, 3, 0);
+#endif
+ }
+ return false;
+}
+
+bool has_erratum_a007798(void)
+{
+#ifdef CONFIG_PPC
+ return SVR_SOC_VER(get_svr()) == SVR_T4240 &&
+ IS_SVR_REV(get_svr(), 2, 0);
+#endif
+ return false;
+}
+
+bool has_erratum_a007792(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+ switch (soc) {
+#ifdef CONFIG_PPC
+ case SVR_T4240:
+ case SVR_T4160:
+ case SVR_T4080:
+ return IS_SVR_REV(svr, 2, 0);
+ case SVR_T1024:
+ case SVR_T1023:
+ return IS_SVR_REV(svr, 1, 0);
+ case SVR_T1040:
+ case SVR_T1042:
+ case SVR_T1020:
+ case SVR_T1022:
+ case SVR_T2080:
+ case SVR_T2081:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 1, 1);
+#endif
+ }
+ return false;
+}
+
+bool has_erratum_a005697(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+ switch (soc) {
+#ifdef CONFIG_PPC
+ case SVR_9131:
+ case SVR_9132:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 1, 1);
+#endif
+#ifdef ONFIG_ARM64
+ case SVR_LS1012A:
+ return IS_SVR_REV(svr, 1, 0);
+#endif
+ }
+ return false;
+}
+
+bool has_erratum_a004477(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+ switch (soc) {
+#ifdef CONFIG_PPC
+ case SVR_P1010:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0);
+ case SVR_P1022:
+ case SVR_9131:
+ case SVR_9132:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 1, 1);
+ case SVR_P2020:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0) ||
+ IS_SVR_REV(svr, 2, 1);
+ case SVR_B4860:
+ case SVR_B4420:
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 2, 0);
+ case SVR_P4080:
+ return IS_SVR_REV(svr, 2, 0) || IS_SVR_REV(svr, 3, 0);
+#endif
+ }
+
+ return false;
+}
+
+bool has_erratum_a008751(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+ switch (soc) {
+#ifdef CONFIG_ARM64
+ case SVR_LS2080A:
+ case SVR_LS2085A:
+ return IS_SVR_REV(svr, 1, 0);
+#endif
+ }
+ return false;
+}
+
+bool has_erratum_a010151(void)
+{
+ u32 svr = get_svr();
+ u32 soc = SVR_SOC_VER(svr);
+
+#ifdef CONFIG_ARM64
+ if (IS_SVR_DEV(svr, SVR_DEV(SVR_LS1043A)))
+ return IS_SVR_REV(svr, 1, 0) || IS_SVR_REV(svr, 1, 1);
+#endif
+
+ switch (soc) {
+#ifdef CONFIG_ARM64
+ case SVR_LS2080A:
+ case SVR_LS2085A:
+ /* fallthrough */
+ case SVR_LS2088A:
+ /* fallthrough */
+ case SVR_LS2081A:
+ case SVR_LS1046A:
+ case SVR_LS1012A:
+ return IS_SVR_REV(svr, 1, 0);
+#endif
+#ifdef CONFIG_ARCH_LS1021A
+ case SOC_VER_LS1020:
+ case SOC_VER_LS1021:
+ case SOC_VER_LS1022:
+ case SOC_VER_SLS1020:
+ return IS_SVR_REV(svr, 2, 0);
+#endif
+ }
+ return false;
+}
+
+#endif
diff --git a/drivers/usb/common/usb_urb.c b/drivers/usb/common/usb_urb.c
new file mode 100644
index 00000000000..be3b6b9f32e
--- /dev/null
+++ b/drivers/usb/common/usb_urb.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common code for usb urb handling, based on the musb-new code
+ *
+ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
+ *
+ */
+
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <linux/usb/usb_urb_compat.h>
+
+#include <time.h>
+#include <usb.h>
+
+#if CONFIG_IS_ENABLED(DM_USB)
+struct usb_device *usb_dev_get_parent(struct usb_device *udev)
+{
+ struct udevice *parent = udev->dev->parent;
+
+ /*
+ * When called from usb-uclass.c: usb_scan_device() udev->dev points
+ * to the parent udevice, not the actual udevice belonging to the
+ * udev as the device is not instantiated yet.
+ *
+ * If dev is an usb-bus, then we are called from usb_scan_device() for
+ * an usb-device plugged directly into the root port, return NULL.
+ */
+ if (device_get_uclass_id(udev->dev) == UCLASS_USB)
+ return NULL;
+
+ /*
+ * If these 2 are not the same we are being called from
+ * usb_scan_device() and udev itself is the parent.
+ */
+ if (dev_get_parent_priv(udev->dev) != udev)
+ return udev;
+
+ /* We are being called normally, use the parent pointer */
+ if (device_get_uclass_id(parent) == UCLASS_USB_HUB)
+ return dev_get_parent_priv(parent);
+
+ return NULL;
+}
+#else
+struct usb_device *usb_dev_get_parent(struct usb_device *udev)
+{
+ return udev->parent;
+}
+#endif
+
+static void usb_urb_complete(struct urb *urb)
+{
+ urb->dev->status &= ~USB_ST_NOT_PROC;
+ urb->dev->act_len = urb->actual_length;
+
+ if (urb->status == -EINPROGRESS)
+ urb->status = 0;
+}
+
+void usb_urb_fill(struct urb *urb, struct usb_host_endpoint *hep,
+ struct usb_device *dev, int endpoint_type,
+ unsigned long pipe, void *buffer, int len,
+ struct devrequest *setup, int interval)
+{
+ int epnum = usb_pipeendpoint(pipe);
+ int is_in = usb_pipein(pipe);
+ u16 maxpacketsize = is_in ? dev->epmaxpacketin[epnum] :
+ dev->epmaxpacketout[epnum];
+
+ memset(urb, 0, sizeof(struct urb));
+ memset(hep, 0, sizeof(struct usb_host_endpoint));
+ INIT_LIST_HEAD(&hep->urb_list);
+ INIT_LIST_HEAD(&urb->urb_list);
+ urb->ep = hep;
+ urb->complete = usb_urb_complete;
+ urb->status = -EINPROGRESS;
+ urb->dev = dev;
+ urb->pipe = pipe;
+ urb->transfer_buffer = buffer;
+ urb->transfer_dma = (unsigned long)buffer;
+ urb->transfer_buffer_length = len;
+ urb->setup_packet = (unsigned char *)setup;
+
+ urb->ep->desc.wMaxPacketSize = __cpu_to_le16(maxpacketsize);
+ urb->ep->desc.bmAttributes = endpoint_type;
+ urb->ep->desc.bEndpointAddress = ((is_in ? USB_DIR_IN : USB_DIR_OUT) |
+ epnum);
+ urb->ep->desc.bInterval = interval;
+}
+
+int usb_urb_submit(struct usb_hcd *hcd, struct urb *urb)
+{
+ const struct usb_urb_ops *ops = hcd->urb_ops;
+ unsigned long timeout;
+ int ret;
+
+ if (!ops)
+ return -EINVAL;
+
+ ret = ops->urb_enqueue(hcd, urb, 0);
+ if (ret < 0) {
+ printf("Failed to enqueue URB to controller\n");
+ return ret;
+ }
+
+ timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe);
+ do {
+ if (ctrlc())
+ return -EIO;
+ ops->isr(0, hcd);
+ } while (urb->status == -EINPROGRESS && get_timer(0) < timeout);
+
+ if (urb->status == -EINPROGRESS)
+ ops->urb_dequeue(hcd, urb, -ETIME);
+
+ return urb->status;
+}
+
+int usb_urb_submit_control(struct usb_hcd *hcd, struct urb *urb,
+ struct usb_host_endpoint *hep,
+ struct usb_device *dev, unsigned long pipe,
+ void *buffer, int len, struct devrequest *setup,
+ int interval, enum usb_device_speed speed)
+{
+ const struct usb_urb_ops *ops = hcd->urb_ops;
+
+ usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_CONTROL, pipe, buffer,
+ len, setup, 0);
+
+ /* Fix speed for non hub-attached devices */
+ if (!usb_dev_get_parent(dev)) {
+ dev->speed = speed;
+ if (ops->hub_control)
+ return ops->hub_control(hcd, dev, pipe, buffer, len,
+ setup);
+ }
+
+ return usb_urb_submit(hcd, urb);
+}
+
+int usb_urb_submit_bulk(struct usb_hcd *hcd, struct urb *urb,
+ struct usb_host_endpoint *hep, struct usb_device *dev,
+ unsigned long pipe, void *buffer, int len)
+{
+ usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_BULK, pipe, buffer, len,
+ NULL, 0);
+
+ return usb_urb_submit(hcd, urb);
+}
+
+int usb_urb_submit_irq(struct usb_hcd *hcd, struct urb *urb,
+ struct usb_host_endpoint *hep, struct usb_device *dev,
+ unsigned long pipe, void *buffer, int len, int interval)
+{
+ usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_INT, pipe, buffer, len,
+ NULL, interval);
+
+ return usb_urb_submit(hcd, urb);
+}