From de31213fb8f1cc25f7e9096029a44dee7a774167 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 25 Mar 2015 12:21:59 -0600 Subject: dm: usb: Add a uclass for USB controllers Add a uclass that can represent a USB controller. For now we do not create devices for things attached to the controller. This will be added later. Signed-off-by: Simon Glass Reviewed-by: Marek Vasut diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index b4a9442..a4414ef 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -35,6 +35,20 @@ config USB if USB +config DM_USB + bool "Enable driver model for USB" + depends on USB && DM + help + Enable driver model for USB. The USB interface is then implemented + by the USB uclass. Multiple USB controllers of different types + (XHCI, EHCI) can be attached and used. The 'usb' command works as + normal. OCHI is not supported at present. + + Much of the code is shared but with this option enabled the USB + uclass takes care of device enumeration. USB devices can be + declared with the USB_DEVICE() macro and will be automatically + probed when found on the bus. + source "drivers/usb/host/Kconfig" config USB_STORAGE diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index eb6f34b..9419295 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -5,6 +5,10 @@ # SPDX-License-Identifier: GPL-2.0+ # +ifdef CONFIG_DM_USB +obj-$(CONFIG_CMD_USB) += usb-uclass.o +endif + # ohci obj-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o obj-$(CONFIG_USB_ATMEL) += ohci-at91.o diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c new file mode 100644 index 0000000..22dcd14 --- /dev/null +++ b/drivers/usb/host/usb-uclass.c @@ -0,0 +1,392 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +extern bool usb_started; /* flag for the started/stopped USB status */ +static bool asynch_allowed; + +int usb_disable_asynch(int disable) +{ + int old_value = asynch_allowed; + + asynch_allowed = !disable; + return old_value; +} + +int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length, int interval) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->interrupt) + return -ENOSYS; + + return ops->interrupt(bus, udev, pipe, buffer, length, interval); +} + +int submit_control_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, struct devrequest *setup) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->control) + return -ENOSYS; + + return ops->control(bus, udev, pipe, buffer, length, setup); +} + +int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->bulk) + return -ENOSYS; + + return ops->bulk(bus, udev, pipe, buffer, length); +} + +int usb_alloc_device(struct usb_device *udev) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + /* This is only requird by some controllers - current XHCI */ + if (!ops->alloc_device) + return 0; + + return ops->alloc_device(bus, udev); +} + +int usb_stop(void) +{ + struct udevice *bus; + struct uclass *uc; + int err = 0, ret; + + /* De-activate any devices that have been activated */ + ret = uclass_get(UCLASS_USB, &uc); + if (ret) + return ret; + uclass_foreach_dev(bus, uc) { + ret = device_remove(bus); + if (ret && !err) + err = ret; + } + + usb_stor_reset(); + usb_hub_reset(); + usb_started = 0; + + return err; +} + +static int usb_scan_bus(struct udevice *bus, bool recurse) +{ + struct usb_bus_priv *priv; + struct udevice *dev; + int ret; + + priv = dev_get_uclass_priv(bus); + + assert(recurse); /* TODO: Support non-recusive */ + + ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev); + if (ret) + return ret; + + return priv->next_addr; +} + +int usb_init(void) +{ + int controllers_initialized = 0; + struct udevice *bus; + struct uclass *uc; + int count = 0; + int ret; + + asynch_allowed = 1; + usb_hub_reset(); + + ret = uclass_get(UCLASS_USB, &uc); + if (ret) + return ret; + + uclass_foreach_dev(bus, uc) { + /* init low_level USB */ + count++; + printf("USB"); + printf("%d: ", bus->seq); + ret = device_probe(bus); + if (ret == -ENODEV) { /* No such device. */ + puts("Port not available.\n"); + controllers_initialized++; + continue; + } + + if (ret) { /* Other error. */ + printf("probe failed, error %d\n", ret); + continue; + } + /* + * lowlevel init is OK, now scan the bus for devices + * i.e. search HUBs and configure them + */ + controllers_initialized++; + printf("scanning bus %d for devices... ", bus->seq); + debug("\n"); + ret = usb_scan_bus(bus, true); + if (ret < 0) + printf("failed, error %d\n", ret); + else if (!ret) + printf("No USB Device found\n"); + else + printf("%d USB Device(s) found\n", ret); + usb_started = true; + } + + debug("scan end\n"); + /* if we were not able to find at least one working bus, bail out */ + if (!count) + printf("No controllers found\n"); + else if (controllers_initialized == 0) + printf("USB error: all controllers failed lowlevel init\n"); + + return usb_started ? 0 : -1; +} + +int usb_reset_root_port(void) +{ + return -ENOSYS; +} + +static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) +{ + struct usb_device *udev; + struct udevice *dev; + + if (!device_active(parent)) + return NULL; + udev = dev_get_parentdata(parent); + if (udev->devnum == devnum) + return udev; + + for (device_find_first_child(parent, &dev); + dev; + device_find_next_child(&dev)) { + udev = find_child_devnum(dev, devnum); + if (udev) + return udev; + } + + return NULL; +} + +struct usb_device *usb_get_dev_index(struct udevice *bus, int index) +{ + struct udevice *hub; + int devnum = index + 1; /* Addresses are allocated from 1 on USB */ + + device_find_first_child(bus, &hub); + if (device_get_uclass_id(hub) == UCLASS_USB_HUB) + return find_child_devnum(hub, devnum); + + return NULL; +} + +int usb_post_bind(struct udevice *dev) +{ + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +int usb_port_reset(struct usb_device *parent, int portnr) +{ + unsigned short portstatus; + int ret; + + debug("%s: start\n", __func__); + + if (parent) { + /* reset the port for the second time */ + assert(portnr > 0); + debug("%s: reset %d\n", __func__, portnr - 1); + ret = legacy_hub_port_reset(parent, portnr - 1, &portstatus); + if (ret < 0) { + printf("\n Couldn't reset port %i\n", portnr); + return ret; + } + } else { + debug("%s: reset root\n", __func__); + usb_reset_root_port(); + } + + return 0; +} + +int usb_legacy_port_reset(struct usb_device *parent, int portnr) +{ + return usb_port_reset(parent, portnr); +} + +int usb_scan_device(struct udevice *parent, int port, + enum usb_device_speed speed, struct udevice **devp) +{ + struct udevice *dev; + bool created = false; + struct usb_dev_platdata *plat; + struct usb_bus_priv *priv; + struct usb_device *parent_udev; + int ret; + ALLOC_CACHE_ALIGN_BUFFER(struct usb_device, udev, 1); + struct usb_interface_descriptor *iface = &udev->config.if_desc[0].desc; + + *devp = NULL; + memset(udev, '\0', sizeof(*udev)); + ret = usb_get_bus(parent, &udev->controller_dev); + if (ret) + return ret; + priv = dev_get_uclass_priv(udev->controller_dev); + + /* + * Somewhat nasty, this. We create a local device and use the normal + * USB stack to read its descriptor. Then we know what type of device + * to create for real. + * + * udev->dev is set to the parent, since we don't have a real device + * yet. The USB stack should not access udev.dev anyway, except perhaps + * to find the controller, and the controller will either be @parent, + * or some parent of @parent. + * + * Another option might be to create the device as a generic USB + * device, then morph it into the correct one when we know what it + * should be. This means that a generic USB device would morph into + * a network controller, or a USB flash stick, for example. However, + * we don't support such morphing and it isn't clear that it would + * be easy to do. + * + * Yet another option is to split out the USB stack parts of udev + * into something like a 'struct urb' (as Linux does) which can exist + * independently of any device. This feels cleaner, but calls for quite + * a big change to the USB stack. + * + * For now, the approach is to set up an empty udev, read its + * descriptor and assign it an address, then bind a real device and + * stash the resulting information into the device's parent + * platform data. Then when we probe it, usb_child_pre_probe() is called + * and it will pull the information out of the stash. + */ + udev->dev = parent; + udev->speed = speed; + udev->devnum = priv->next_addr + 1; + udev->portnr = port; + debug("Calling usb_setup_device(), portnr=%d\n", udev->portnr); + parent_udev = device_get_uclass_id(parent) == UCLASS_USB_HUB ? + dev_get_parentdata(parent) : NULL; + ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev, port); + debug("read_descriptor for '%s': ret=%d\n", parent->name, ret); + if (ret) + return ret; + ret = usb_find_child(parent, &udev->descriptor, iface, &dev); + debug("** usb_find_child returns %d\n", ret); + + /* TODO: Find a suitable driver and create the device */ + return -ENOENT; +} + +int usb_child_post_bind(struct udevice *dev) +{ + struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); + const void *blob = gd->fdt_blob; + int val; + + if (dev->of_offset == -1) + return 0; + + /* We only support matching a few things */ + val = fdtdec_get_int(blob, dev->of_offset, "usb,device-class", -1); + if (val != -1) { + plat->id.match_flags |= USB_DEVICE_ID_MATCH_DEV_CLASS; + plat->id.bDeviceClass = val; + } + val = fdtdec_get_int(blob, dev->of_offset, "usb,interface-class", -1); + if (val != -1) { + plat->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS; + plat->id.bInterfaceClass = val; + } + + return 0; +} + +int usb_get_bus(struct udevice *dev, struct udevice **busp) +{ + struct udevice *bus; + + *busp = NULL; + for (bus = dev; bus && device_get_uclass_id(bus) != UCLASS_USB; ) + bus = bus->parent; + if (!bus) { + /* By design this cannot happen */ + assert(bus); + debug("USB HUB '%s' does not have a controller\n", dev->name); + return -EXDEV; + } + *busp = bus; + + return 0; +} + +int usb_child_pre_probe(struct udevice *dev) +{ + struct udevice *bus; + struct usb_device *udev = dev_get_parentdata(dev); + struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); + int ret; + + ret = usb_get_bus(dev, &bus); + if (ret) + return ret; + udev->controller_dev = bus; + udev->dev = dev; + udev->devnum = plat->devnum; + udev->slot_id = plat->slot_id; + udev->portnr = plat->portnr; + udev->speed = plat->speed; + debug("** device '%s': getting slot_id=%d\n", dev->name, plat->slot_id); + + ret = usb_select_config(udev); + if (ret) + return ret; + + return 0; +} + +UCLASS_DRIVER(usb) = { + .id = UCLASS_USB, + .name = "usb", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_bind = usb_post_bind, + .per_child_auto_alloc_size = sizeof(struct usb_device), + .per_device_auto_alloc_size = sizeof(struct usb_bus_priv), + .child_post_bind = usb_child_post_bind, + .child_pre_probe = usb_child_pre_probe, + .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), +}; diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 053d945..7d90ebc 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -180,7 +180,7 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) return NULL; /* URB still pending */ } -void usb_reset_root_port(void) +int usb_reset_root_port(void) { void *mbase = host->mregs; u8 power; @@ -208,6 +208,8 @@ void usb_reset_root_port(void) (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? USB_SPEED_FULL : USB_SPEED_LOW; mdelay((host_speed == USB_SPEED_LOW) ? 200 : 50); + + return 0; } int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 79b51d3..95bd249 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -40,6 +40,7 @@ enum uclass_id { UCLASS_PCH, /* x86 platform controller hub */ UCLASS_ETH, /* Ethernet device */ UCLASS_LPC, /* x86 'low pin count' interface */ + UCLASS_USB, /* USB bus */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/usb.h b/include/usb.h index 2c3d506..67615ea 100644 --- a/include/usb.h +++ b/include/usb.h @@ -2,6 +2,9 @@ * (C) Copyright 2001 * Denis Peter, MPL AG Switzerland * + * Adapted for U-Boot driver model + * (C) Copyright 2015 Google, Inc + * * SPDX-License-Identifier: GPL-2.0+ * Note: Part of this code has been derived from linux * @@ -9,6 +12,7 @@ #ifndef _USB_H_ #define _USB_H_ +#include #include #include #include @@ -85,6 +89,19 @@ enum { PACKET_SIZE_64 = 3, }; +/** + * struct usb_device - information about a USB device + * + * With driver model both UCLASS_USB (the USB controllers) and UCLASS_USB_HUB + * (the hubs) have this as parent data. Hubs are children of controllers or + * other hubs and there is always a single root hub for each controller. + * Therefore struct usb_device can always be accessed with + * dev_get_parentdata(dev), where dev is a USB device. + * + * Pointers exist for obtaining both the device (could be any uclass) and + * controller (UCLASS_USB) from this structure. The controller does not have + * a struct usb_device since it is not a device. + */ struct usb_device { int devnum; /* Device number on USB bus */ int speed; /* full/low/high */ @@ -123,13 +140,19 @@ struct usb_device { unsigned long int_pending; /* 1 bit per ep, used by int_queue */ int act_len; /* transfered bytes */ int maxchild; /* Number of ports if hub */ - int portnr; + int portnr; /* Port number, 1=first */ +#ifndef CONFIG_DM_USB + /* parent hub, or NULL if this is the root hub */ struct usb_device *parent; struct usb_device *children[USB_MAXCHILDREN]; - void *controller; /* hardware controller private data */ +#endif /* slot_id - for xHCI enabled devices */ unsigned int slot_id; +#ifdef CONFIG_DM_USB + struct udevice *dev; /* Pointer to associated device */ + struct udevice *controller_dev; /* Pointer to associated controller */ +#endif }; struct int_queue; @@ -160,8 +183,9 @@ enum usb_init_type { int usb_lowlevel_init(int index, enum usb_init_type init, void **controller); int usb_lowlevel_stop(int index); -#ifdef CONFIG_MUSB_HOST -void usb_reset_root_port(void); + +#if defined(CONFIG_MUSB_HOST) || defined(CONFIG_DM_USB) +int usb_reset_root_port(void); #else #define usb_reset_root_port() #endif @@ -245,7 +269,6 @@ int usb_stop(void); /* stop the USB Controller */ int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol); int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id); -struct usb_device *usb_get_dev_index(int index); int usb_control_msg(struct usb_device *dev, unsigned int pipe, unsigned char request, unsigned char requesttype, unsigned short value, unsigned short index, @@ -423,6 +446,258 @@ struct usb_hub_device { struct usb_hub_descriptor desc; }; +#ifdef CONFIG_DM_USB +/** + * struct usb_platdata - Platform data about a USB controller + * + * Given a USB controller (UCLASS_USB) dev this is dev_get_platdata(dev) + */ +struct usb_platdata { + enum usb_init_type init_type; +}; + +/** + * struct usb_dev_platdata - Platform data about a USB device + * + * Given a USB device dev this structure is dev_get_parent_platdata(dev). + * This is used by sandbox to provide emulation data also. + * + * @id: ID used to match this device + * @speed: Stores the speed associated with a USB device + * @devnum: Device address on the USB bus + * @slot_id: USB3 slot ID, which is separate from the device address + * @portnr: Port number of this device on its parent hub, numbered from 1 + * (0 mean this device is the root hub) + * @strings: List of descriptor strings (for sandbox emulation purposes) + * @desc_list: List of descriptors (for sandbox emulation purposes) + */ +struct usb_dev_platdata { + struct usb_device_id id; + enum usb_device_speed speed; + int devnum; + int slot_id; + int portnr; /* Hub port number, 1..n */ +#ifdef CONFIG_SANDBOX + struct usb_string *strings; + /* NULL-terminated list of descriptor pointers */ + struct usb_generic_descriptor **desc_list; +#endif + int configno; +}; + +/** + * struct usb_bus_priv - information about the USB controller + * + * Given a USB controller (UCLASS_USB) 'dev', this is + * dev_get_uclass_priv(dev). + * + * @next_addr: Next device address to allocate minus 1. Incremented by 1 + * each time a new device address is set, so this holds the + * number of devices on the bus + * @desc_before_addr: true if we can read a device descriptor before it + * has been assigned an address. For XHCI this is not possible + * so this will be false. + */ +struct usb_bus_priv { + int next_addr; + bool desc_before_addr; +}; + +/** + * struct dm_usb_ops - USB controller operations + * + * This defines the operations supoorted on a USB controller. Common + * arguments are: + * + * @bus: USB bus (i.e. controller), which is in UCLASS_USB. + * @udev: USB device parent data. Controllers are not expected to need + * this, since the device address on the bus is encoded in @pipe. + * It is used for sandbox, and can be handy for debugging and + * logging. + * @pipe: An assortment of bitfields which provide address and packet + * type information. See create_pipe() above for encoding + * details + * @buffer: A buffer to use for sending/receiving. This should be + * DMA-aligned. + * @length: Buffer length in bytes + */ +struct dm_usb_ops { + /** + * control() - Send a control message + * + * Most parameters are as above. + * + * @setup: Additional setup information required by the message + */ + int (*control)(struct udevice *bus, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup); + /** + * bulk() - Send a bulk message + * + * Parameters are as above. + */ + int (*bulk)(struct udevice *bus, struct usb_device *udev, + unsigned long pipe, void *buffer, int length); + /** + * interrupt() - Send an interrupt message + * + * Most parameters are as above. + * + * @interval: Interrupt interval + */ + int (*interrupt)(struct udevice *bus, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int interval); + /** + * alloc_device() - Allocate a new device context (XHCI) + * + * Before sending packets to a new device on an XHCI bus, a device + * context must be created. If this method is not NULL it will be + * called before the device is enumerated (even before its descriptor + * is read). This should be NULL for EHCI, which does not need this. + */ + int (*alloc_device)(struct udevice *bus, struct usb_device *udev); +}; + +#define usb_get_ops(dev) ((struct dm_usb_ops *)(dev)->driver->ops) +#define usb_get_emul_ops(dev) ((struct dm_usb_ops *)(dev)->driver->ops) + +#ifdef CONFIG_MUSB_HOST +int usb_reset_root_port(void); +#endif + +/** + * usb_get_dev_index() - look up a device index number + * + * Look up devices using their index number (starting at 0). This works since + * in U-Boot device addresses are allocated starting at 1 with no gaps. + * + * TODO(sjg@chromium.org): Remove this function when usb_ether.c is modified + * to work better with driver model. + * + * @bus: USB bus to check + * @index: Index number of device to find (0=first). This is just the + * device address less 1. + */ +struct usb_device *usb_get_dev_index(struct udevice *bus, int index); + +/** + * usb_legacy_port_reset() - Legacy function to reset a hub port + * + * @hub: Hub device + * @portnr: Port number (1=first) + */ +int usb_legacy_port_reset(struct usb_device *hub, int portnr); + +/** + * usb_setup_device() - set up a device ready for use + * + * @dev: USB device pointer. This need not be a real device - it is + * common for it to just be a local variable with its ->dev + * member (i.e. @dev->dev) set to the parent device + * @do_read: true to read the device descriptor before an address is set + * (should be false for XHCI buses, true otherwise) + * @parent: Parent device (either UCLASS_USB or UCLASS_USB_HUB) + * @portnr: Port number on hub (1=first) or 0 for none + * @return 0 if OK, -ve on error */ +int usb_setup_device(struct usb_device *dev, bool do_read, + struct usb_device *parent, int portnr); + +/** + * usb_hub_scan() - Scan a hub and find its devices + * + * @hub: Hub device to scan + */ +int usb_hub_scan(struct udevice *hub); + +/** + * usb_scan_device() - Scan a device on a bus + * + * Scan a device on a bus. It has already been detected and is ready to + * be enumerated. This may be either the root hub (@parent is a bus) or a + * normal device (@parent is a hub) + * + * @parent: Parent device + * @port: Hub port number (numbered from 1) + * @speed: USB speed to use for this device + * @devp: Returns pointer to device if all is well + * @return 0 if OK, -ve on error + */ +int usb_scan_device(struct udevice *parent, int port, + enum usb_device_speed speed, struct udevice **devp); + +/** + * usb_get_bus() - Find the bus for a device + * + * Search up through parents to find the bus this device is connected to. This + * will be a device with uclass UCLASS_USB. + * + * @dev: Device to check + * @busp: Returns bus, or NULL if not found + * @return 0 if OK, -EXDEV is somehow this bus does not have a controller (this + * indicates a critical error in the USB stack + */ +int usb_get_bus(struct udevice *dev, struct udevice **busp); + +/** + * usb_select_config() - Set up a device ready for use + * + * This function assumes that the device already has an address and a driver + * bound, and is ready to be set up. + * + * This re-reads the device and configuration descriptors and sets the + * configuration + * + * @dev: Device to set up + */ +int usb_select_config(struct usb_device *dev); + +/** + * usb_child_pre_probe() - Pre-probe function for USB devices + * + * This is called on all children of hubs and USB controllers (i.e. UCLASS_USB + * and UCLASS_USB_HUB) when a new device is about to be probed. It sets up the + * device from the saved platform data and calls usb_select_config() to + * finish set up. + * + * Once this is done, the device's normal driver can take over, knowing the + * device is accessible on the USB bus. + * + * This function is for use only by the internal USB stack. + * + * @dev: Device to set up + */ +int usb_child_pre_probe(struct udevice *dev); + +struct ehci_ctrl; + +/** + * usb_setup_ehci_gadget() - Set up a USB device as a gadget + * + * TODO(sjg@chromium.org): Tidy this up when USB gadgets can use driver model + * + * This provides a way to tell a controller to start up as a USB device + * instead of as a host. It is untested. + */ +int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp); + +/** + * usb_stor_reset() - Prepare to scan USB storage devices + * + * Empty the list of USB storage devices in preparation for scanning them. + * This must be called before a USB scan. + */ +void usb_stor_reset(void); + +#else /* !CONFIG_DM_USB */ + +struct usb_device *usb_get_dev_index(int index); + +#endif + +bool usb_device_has_child_on_port(struct usb_device *parent, int port); + int usb_hub_probe(struct usb_device *dev, int ifnum); void usb_hub_reset(void); int hub_port_reset(struct usb_device *dev, int port, -- cgit v0.10.2