* (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
* (C) Copyright 1999 Deti Fliegl (new USB architecture)
*
- * $id$
- *
* 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
#include <linux/usbdevice_fs.h>
#include <asm/uaccess.h>
+#include "hcd.h"
+
#define MAX_TOPO_LEVEL 6
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
* count = device count at this level
*/
/* If this is the root hub, display the bandwidth information */
+ /* FIXME high speed reserves 20% frametime for non-periodic,
+ * while full/low speed reserves only 10% ... so this is wrong
+ * for high speed busses. also, change how bandwidth is recorded.
+ */
if (level == 0)
data_end += sprintf(data_end, format_bandwidth, bus->bandwidth_allocated,
FRAME_TIME_MAX_USECS_ALLOC,
/*
- * Copyright (c) 2001 by David Brownell
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2002
*
* 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
#include <linux/interrupt.h>
#include <linux/uts.h> /* for UTS_SYSNAME */
-#ifndef CONFIG_USB_DEBUG
- #define CONFIG_USB_DEBUG /* this is experimental! */
-#endif
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#include <asm/byteorder.h>
+// #define USB_BANDWIDTH_MESSAGES
+
/*-------------------------------------------------------------------------*/
/*
* usb client device drivers.
*
* Contributors of ideas or unattributed patches include: David Brownell,
- * Roman Weissgaerber, Rory Bolt, ...
+ * Roman Weissgaerber, Rory Bolt, Greg Kroah-Hartman, ...
*
* HISTORY:
+ * 2002-02-21 Pull in most of the usb_bus support from usb.c; some
+ * associated cleanup. "usb_hcd" still != "usb_bus".
* 2001-12-12 Initial patch version for Linux 2.5.1 kernel.
*/
/*-------------------------------------------------------------------------*/
/* host controllers we manage */
-static LIST_HEAD (hcd_list);
+LIST_HEAD (usb_bus_list);
+
+/* used when allocating bus numbers */
+#define USB_MAXBUS 64
+struct usb_busmap {
+ unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
+};
+static struct usb_busmap busmap;
/* used when updating list of hcds */
-static DECLARE_MUTEX (hcd_list_lock);
+DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */
/* used when updating hcd data */
static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
/*-------------------------------------------------------------------------*/
+#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)
+#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)
+
/* usb 2.0 root hub device descriptor */
static const u8 usb2_rh_dev_descriptor [18] = {
0x12, /* __u8 bLength; */
0x00, 0x00, /* __u16 idVendor; */
0x00, 0x00, /* __u16 idProduct; */
- 0x40, 0x02, /* __u16 bcdDevice; (v2.4) */
+ KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */
0x03, /* __u8 iManufacturer; */
0x02, /* __u8 iProduct; */
0x00, 0x00, /* __u16 idVendor; */
0x00, 0x00, /* __u16 idProduct; */
- 0x40, 0x02, /* __u16 bcdDevice; (v2.4) */
+ KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */
0x03, /* __u8 iManufacturer; */
0x02, /* __u8 iProduct; */
usb_hcd_giveback_urb (hcd, urb);
}
+/*-------------------------------------------------------------------------*/
+
+/* exported only within usbcore */
+void usb_bus_get (struct usb_bus *bus)
+{
+ atomic_inc (&bus->refcnt);
+}
+
+/* exported only within usbcore */
+void usb_bus_put (struct usb_bus *bus)
+{
+ if (atomic_dec_and_test (&bus->refcnt))
+ kfree (bus);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* shared initialization code */
+static void usb_init_bus (struct usb_bus *bus)
+{
+ memset (&bus->devmap, 0, sizeof(struct usb_devmap));
+
+#ifdef DEVNUM_ROUND_ROBIN
+ bus->devnum_next = 1;
+#endif /* DEVNUM_ROUND_ROBIN */
+
+ bus->root_hub = NULL;
+ bus->hcpriv = NULL;
+ bus->busnum = -1;
+ bus->bandwidth_allocated = 0;
+ bus->bandwidth_int_reqs = 0;
+ bus->bandwidth_isoc_reqs = 0;
+
+ INIT_LIST_HEAD (&bus->bus_list);
+
+ atomic_set (&bus->refcnt, 1);
+}
+
+/**
+ * usb_alloc_bus - creates a new USB host controller structure
+ * @op: pointer to a struct usb_operations that this bus structure should use
+ * Context: !in_interrupt()
+ *
+ * Creates a USB host controller bus structure with the specified
+ * usb_operations and initializes all the necessary internal objects.
+ *
+ * If no memory is available, NULL is returned.
+ *
+ * The caller should call usb_free_bus() when it is finished with the structure.
+ */
+struct usb_bus *usb_alloc_bus (struct usb_operations *op)
+{
+ struct usb_bus *bus;
+
+ bus = kmalloc (sizeof *bus, GFP_KERNEL);
+ if (!bus)
+ return NULL;
+ usb_init_bus (bus);
+ bus->op = op;
+ return bus;
+}
+EXPORT_SYMBOL (usb_alloc_bus);
+
+/**
+ * usb_free_bus - frees the memory used by a bus structure
+ * @bus: pointer to the bus to free
+ *
+ * To be invoked by a HCD, only as the last step of decoupling from
+ * hardware. It is an error to call this if the reference count is
+ * anything but one. That would indicate that some system component
+ * did not correctly shut down, and thought the hardware was still
+ * accessible.
+ */
+void usb_free_bus (struct usb_bus *bus)
+{
+ if (!bus)
+ return;
+ if (atomic_read (&bus->refcnt) != 1)
+ err ("usb_free_bus #%d, count != 1", bus->busnum);
+ usb_bus_put (bus);
+}
+EXPORT_SYMBOL (usb_free_bus);
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_register_bus - registers the USB host controller with the usb core
+ * @bus: pointer to the bus to register
+ * Context: !in_interrupt()
+ *
+ * Assigns a bus number, and links the controller into usbcore data
+ * structures so that it can be seen by scanning the bus list.
+ */
+void usb_register_bus(struct usb_bus *bus)
+{
+ int busnum;
+
+ down (&usb_bus_list_lock);
+ busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
+ if (busnum < USB_MAXBUS) {
+ set_bit (busnum, busmap.busmap);
+ bus->busnum = busnum;
+ } else
+ warn ("too many buses");
+
+ usb_bus_get (bus);
+
+ /* Add it to the list of buses */
+ list_add (&bus->bus_list, &usb_bus_list);
+ up (&usb_bus_list_lock);
+
+ usbfs_add_bus (bus);
+
+ info ("new USB bus registered, assigned bus number %d", bus->busnum);
+}
+EXPORT_SYMBOL (usb_register_bus);
+
+/**
+ * usb_deregister_bus - deregisters the USB host controller
+ * @bus: pointer to the bus to deregister
+ * Context: !in_interrupt()
+ *
+ * Recycles the bus number, and unlinks the controller from usbcore data
+ * structures so that it won't be seen by scanning the bus list.
+ */
+void usb_deregister_bus (struct usb_bus *bus)
+{
+ info ("USB bus %d deregistered", bus->busnum);
+
+ /*
+ * NOTE: make sure that all the devices are removed by the
+ * controller code, as well as having it call this when cleaning
+ * itself up
+ */
+ down (&usb_bus_list_lock);
+ list_del (&bus->bus_list);
+ up (&usb_bus_list_lock);
+
+ usbfs_remove_bus (bus);
+
+ clear_bit (bus->busnum, busmap.busmap);
+
+ usb_bus_put (bus);
+}
+EXPORT_SYMBOL (usb_deregister_bus);
+
+/**
+ * usb_register_root_hub - called by HCD to register its root hub
+ * @usb_dev: the usb root hub device to be registered.
+ * @parent_dev: the parent device of this root hub.
+ *
+ * The USB host controller calls this function to register the root hub
+ * properly with the USB subsystem. It sets up the device properly in
+ * the driverfs tree, and then calls usb_new_device() to register the
+ * usb device.
+ */
+int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev)
+{
+ int retval;
+
+ usb_dev->dev.parent = parent_dev;
+ strcpy (&usb_dev->dev.name[0], "usb_name");
+ strcpy (&usb_dev->dev.bus_id[0], "usb_bus");
+ retval = usb_new_device (usb_dev);
+ if (retval)
+ put_device (&usb_dev->dev);
+ return retval;
+}
+EXPORT_SYMBOL (usb_register_root_hub);
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * usb_calc_bus_time:
+ * Returns approximate bus time in nanoseconds for a periodic transaction.
+ * See USB 2.0 spec section 5.11.3
+ */
+static long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
+{
+ unsigned long tmp;
+
+ switch (speed) {
+ case USB_SPEED_LOW: /* INTR only */
+ if (is_input) {
+ tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ } else {
+ tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ }
+ case USB_SPEED_FULL: /* ISOC or INTR */
+ if (isoc) {
+ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+ } else {
+ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (9107L + BW_HOST_DELAY + tmp);
+ }
+ case USB_SPEED_HIGH: /* ISOC or INTR */
+ // FIXME merge from EHCI code; caller will need to handle
+ // each part of a split separately.
+ return 0;
+ default:
+ dbg ("bogus device speed!");
+ return -1;
+ }
+}
+
+/*
+ * usb_check_bandwidth():
+ *
+ * old_alloc is from host_controller->bandwidth_allocated in microseconds;
+ * bustime is from calc_bus_time(), but converted to microseconds.
+ *
+ * returns <bustime in us> if successful,
+ * or -ENOSPC if bandwidth request fails.
+ *
+ * FIXME:
+ * This initial implementation does not use Endpoint.bInterval
+ * in managing bandwidth allocation.
+ * It probably needs to be expanded to use Endpoint.bInterval.
+ * This can be done as a later enhancement (correction).
+ *
+ * This will also probably require some kind of
+ * frame allocation tracking...meaning, for example,
+ * that if multiple drivers request interrupts every 10 USB frames,
+ * they don't all have to be allocated at
+ * frame numbers N, N+10, N+20, etc. Some of them could be at
+ * N+11, N+21, N+31, etc., and others at
+ * N+12, N+22, N+32, etc.
+ *
+ * Similarly for isochronous transfers...
+ *
+ * Individual HCDs can schedule more directly ... this logic
+ * is not correct for high speed transfers.
+ */
+int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
+{
+ unsigned int pipe = urb->pipe;
+ long bustime;
+ int is_in = usb_pipein (pipe);
+ int is_iso = usb_pipeisoc (pipe);
+ int old_alloc = dev->bus->bandwidth_allocated;
+ int new_alloc;
+
+
+ bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso,
+ usb_maxpacket (dev, pipe, !is_in)));
+ if (is_iso)
+ bustime /= urb->number_of_packets;
+
+ new_alloc = old_alloc + (int) bustime;
+ if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) {
+#ifdef DEBUG
+ char *mode =
+#ifdef CONFIG_USB_BANDWIDTH
+ "";
+#else
+ "would have ";
+#endif
+ dbg ("usb_check_bandwidth %sFAILED: %d + %ld = %d usec",
+ mode, old_alloc, bustime, new_alloc);
+#endif
+#ifdef CONFIG_USB_BANDWIDTH
+ bustime = -ENOSPC; /* report error */
+#endif
+ }
+
+ return bustime;
+}
+EXPORT_SYMBOL (usb_check_bandwidth);
+
+
+/**
+ * usb_claim_bandwidth - records bandwidth for a periodic transfer
+ * @dev: source/target of request
+ * @urb: request (urb->dev == dev)
+ * @bustime: bandwidth consumed, in (average) microseconds per frame
+ * @isoc: true iff the request is isochronous
+ *
+ * Bus bandwidth reservations are recorded purely for diagnostic purposes.
+ * HCDs are expected not to overcommit periodic bandwidth, and to record such
+ * reservations whenever endpoints are added to the periodic schedule.
+ *
+ * FIXME averaging per-frame is suboptimal. Better to sum over the HCD's
+ * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable
+ * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how
+ * large its periodic schedule is.
+ */
+void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
+{
+ dev->bus->bandwidth_allocated += bustime;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs++;
+ else
+ dev->bus->bandwidth_int_reqs++;
+ urb->bandwidth = bustime;
+
+#ifdef USB_BANDWIDTH_MESSAGES
+ dbg ("bandwidth alloc increased by %d (%s) to %d for %d requesters",
+ bustime,
+ isoc ? "ISOC" : "INTR",
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+#endif
+}
+EXPORT_SYMBOL (usb_claim_bandwidth);
+
+
+/**
+ * usb_release_bandwidth - reverses effect of usb_claim_bandwidth()
+ * @dev: source/target of request
+ * @urb: request (urb->dev == dev)
+ * @isoc: true iff the request is isochronous
+ *
+ * This records that previously allocated bandwidth has been released.
+ * Bandwidth is released when endpoints are removed from the host controller's
+ * periodic schedule.
+ */
+void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)
+{
+ dev->bus->bandwidth_allocated -= urb->bandwidth;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs--;
+ else
+ dev->bus->bandwidth_int_reqs--;
+
+#ifdef USB_BANDWIDTH_MESSAGES
+ dbg ("bandwidth alloc reduced by %d (%s) to %d for %d requesters",
+ urb->bandwidth,
+ isoc ? "ISOC" : "INTR",
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+#endif
+ urb->bandwidth = 0;
+}
+EXPORT_SYMBOL (usb_release_bandwidth);
+
+
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PCI
* usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed
* @id: pci hotplug id connecting controller to HCD framework
+ * Context: !in_interrupt()
*
* Allocates basic PCI resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
unsigned long resource, len;
void *base;
u8 latency, limit;
- struct usb_bus *bus;
struct usb_hcd *hcd;
int retval, region;
char buf [8], *bufp = buf;
!= 0) {
err ("request interrupt %s failed", bufp);
retval = -EBUSY;
-clean_3:
driver->hcd_free (hcd);
goto clean_2;
}
(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
base);
-// FIXME simpler: make "bus" be that data, not pointer to it.
- bus = usb_alloc_bus (&hcd_operations);
- if (bus == NULL) {
- dbg ("usb_alloc_bus fail");
- retval = -ENOMEM;
- free_irq (dev->irq, hcd);
- goto clean_3;
- }
- hcd->bus = bus;
+ usb_init_bus (&hcd->self);
+ hcd->self.op = &hcd_operations;
+ hcd->self.hcpriv = (void *) hcd;
+ hcd->bus = &hcd->self;
hcd->bus_name = dev->slot_name;
- bus->hcpriv = (void *) hcd;
INIT_LIST_HEAD (&hcd->dev_list);
- INIT_LIST_HEAD (&hcd->hcd_list);
- down (&hcd_list_lock);
- list_add (&hcd->hcd_list, &hcd_list);
- up (&hcd_list_lock);
-
- usb_register_bus (bus);
+ usb_register_bus (&hcd->self);
if ((retval = driver->start (hcd)) < 0)
usb_hcd_pci_remove (dev);
/**
* usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
* @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_pci_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
pci_resource_len (dev, hcd->region));
}
- down (&hcd_list_lock);
- list_del (&hcd->hcd_list);
- up (&hcd_list_lock);
-
usb_deregister_bus (hcd->bus);
- usb_free_bus (hcd->bus);
+ if (atomic_read (&hcd->self.refcnt) != 1)
+ err ("usb_hcd_pci_remove %s, count != 1", hcd->bus_name);
hcd->bus = NULL;
hcd->driver->hcd_free (hcd);
* usb_hcd_giveback_urb - return URB from HCD to device driver
* @hcd: host controller returning the URB
* @urb: urb being returned to the USB device driver.
+ * Context: in_interrupt()
*
* This hands the URB from HCD to its USB device driver, using its
* completion function. The HCD has freed all per-urb resources
struct usb_device *dev;
/* Release periodic transfer bandwidth */
- if (urb->bandwidth) {
- switch (usb_pipetype (urb->pipe)) {
- case PIPE_INTERRUPT:
- usb_release_bandwidth (urb->dev, urb, 0);
- break;
- case PIPE_ISOCHRONOUS:
- usb_release_bandwidth (urb->dev, urb, 1);
- break;
- }
- }
+ if (urb->bandwidth)
+ usb_release_bandwidth (urb->dev, urb,
+ usb_pipeisoc (urb->pipe));
/* clear all state linking urb to this dev (and hcd) */
/*
- * Copyright (c) 2001 by David Brownell
- *
+ * Copyright (c) 2001-2002 by 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
*/
+#ifdef __KERNEL__
+
/*-------------------------------------------------------------------------*/
/*
/*
* housekeeping
*/
- struct usb_bus *bus; /* hcd is-a bus */
- struct list_head hcd_list;
+ struct usb_bus *bus; /* FIXME only use "self" */
+ struct usb_bus self; /* hcd is-a bus */
const char *bus_name;
/*-------------------------------------------------------------------------*/
+/*
+ * FIXME usb_operations should vanish or become hc_driver,
+ * when usb_bus and usb_hcd become the same thing.
+ */
+
+struct usb_operations {
+ int (*allocate)(struct usb_device *);
+ int (*deallocate)(struct usb_device *);
+ int (*get_frame_number) (struct usb_device *usb_dev);
+ int (*submit_urb) (struct urb *urb, int mem_flags);
+ int (*unlink_urb) (struct urb *urb);
+};
+
/* each driver provides one of these, and hardware init support */
struct hc_driver {
/* return current frame number */
int (*get_frame_number) (struct usb_hcd *hcd);
-// FIXME: rework generic-to-specific HCD linkage (specific contains generic)
-
/* memory lifecycle */
struct usb_hcd *(*hcd_alloc) (void);
void (*hcd_free) (struct usb_hcd *hcd);
extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
#ifdef CONFIG_PCI
-
+struct pci_dev;
+struct pci_device_id;
extern int usb_hcd_pci_probe (struct pci_dev *dev,
const struct pci_device_id *id);
extern void usb_hcd_pci_remove (struct pci_dev *dev);
#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic bandwidth allocation constants/support
+ */
+#define FRAME_TIME_USECS 1000L
+#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
+ /* Trying not to use worst-case bit-stuffing
+ of (7/6 * 8 * bytecount) = 9.33 * bytecount */
+ /* bytecount = data payload byte count */
+
+#define NS_TO_US(ns) ((ns + 500L) / 1000L)
+ /* convert & round nanoseconds to microseconds */
+
+extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,
+ int bustime, int isoc);
+extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb,
+ int isoc);
+
+/*
+ * Full/low speed bandwidth allocation constants/support.
+ */
+#define BW_HOST_DELAY 1000L /* nanoseconds */
+#define BW_HUB_LS_SETUP 333L /* nanoseconds */
+ /* 4 full-speed bit times (est.) */
+
+#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
+#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
+#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
+
+extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
+
+/*-------------------------------------------------------------------------*/
+
+extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
+extern void usb_free_bus (struct usb_bus *);
+
+extern void usb_register_bus (struct usb_bus *);
+extern void usb_deregister_bus (struct usb_bus *);
+
+extern int usb_register_root_hub (struct usb_device *usb_dev,
+ struct device *parent_dev);
+
+/*-------------------------------------------------------------------------*/
+
+/* exported only within usbcore */
+
+extern struct list_head usb_bus_list;
+extern struct semaphore usb_bus_list_lock;
+
+extern void usb_bus_get (struct usb_bus *bus);
+extern void usb_bus_put (struct usb_bus *bus);
+
/*-------------------------------------------------------------------------*/
/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
#define RUN_CONTEXT (in_irq () ? "in_irq" \
: (in_interrupt () ? "in_interrupt" : "can sleep"))
+
+
+#endif /* __KERNEL__ */
+
#define OHCI_USE_NPS // force NoPowerSwitching mode
// #define OHCI_VERBOSE_DEBUG /* not always helpful */
+#include "hcd.h"
#include "usb-ohci.h"
#define VERSTR "$Revision: 1.275 $ time " __TIME__ " " __DATE__
#include <linux/usb.h>
+#include "hcd.h"
#include "usb-uhci.h"
#include "usb-uhci-debug.h"
#endif
#include <linux/usb.h>
-static const int usb_bandwidth_option =
-#ifdef CONFIG_USB_BANDWIDTH
- 1;
-#else
- 0;
-#endif
+#include "hcd.h"
extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
* We have a per-interface "registered driver" list.
*/
LIST_HEAD(usb_driver_list);
-LIST_HEAD(usb_bus_list);
-struct semaphore usb_bus_list_lock;
devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */
-static struct usb_busmap busmap;
-
static struct usb_driver *usb_minors[16];
/**
/**
* usb_scan_devices - scans all unclaimed USB interfaces
+ * Context: !in_interrupt ()
*
* Goes through all unclaimed USB interfaces, and offers them to all
* registered USB drivers through the 'probe' function.
/**
* usb_deregister - unregister a USB driver
* @driver: USB operations of the driver to unregister
+ * Context: !in_interrupt ()
*
* Unlinks the specified driver from the internal USB driver list.
*/
return NULL;
}
-/*
- * usb_calc_bus_time:
- *
- * returns (approximate) USB bus time in nanoseconds for a USB transaction.
- */
-static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount)
-{
- unsigned long tmp;
-
- if (low_speed) /* no isoc. here */
- {
- if (input_dir)
- {
- tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
- }
- else
- {
- tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
- }
- }
-
- /* for full-speed: */
-
- if (!isoc) /* Input or Output */
- {
- tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (9107L + BW_HOST_DELAY + tmp);
- } /* end not Isoc */
-
- /* for isoc: */
-
- tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
-}
-
-/*
- * usb_check_bandwidth():
- *
- * old_alloc is from host_controller->bandwidth_allocated in microseconds;
- * bustime is from calc_bus_time(), but converted to microseconds.
- *
- * returns <bustime in us> if successful,
- * or -ENOSPC if bandwidth request fails.
- *
- * FIXME:
- * This initial implementation does not use Endpoint.bInterval
- * in managing bandwidth allocation.
- * It probably needs to be expanded to use Endpoint.bInterval.
- * This can be done as a later enhancement (correction).
- * This will also probably require some kind of
- * frame allocation tracking...meaning, for example,
- * that if multiple drivers request interrupts every 10 USB frames,
- * they don't all have to be allocated at
- * frame numbers N, N+10, N+20, etc. Some of them could be at
- * N+11, N+21, N+31, etc., and others at
- * N+12, N+22, N+32, etc.
- * However, this first cut at USB bandwidth allocation does not
- * contain any frame allocation tracking.
- */
-int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
-{
- int new_alloc;
- int old_alloc = dev->bus->bandwidth_allocated;
- unsigned int pipe = urb->pipe;
- long bustime;
-
- bustime = usb_calc_bus_time (dev->speed == USB_SPEED_LOW,
- usb_pipein(pipe), usb_pipeisoc(pipe),
- usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
- if (usb_pipeisoc(pipe))
- bustime = NS_TO_US(bustime) / urb->number_of_packets;
- else
- bustime = NS_TO_US(bustime);
-
- new_alloc = old_alloc + (int)bustime;
- /* what new total allocated bus time would be */
-
- if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC)
- dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld us",
- usb_bandwidth_option ? "" : "would have ",
- old_alloc, new_alloc, bustime);
-
- if (!usb_bandwidth_option) /* don't enforce it */
- return (bustime);
- return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime : -ENOSPC;
-}
-
-void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
-{
- dev->bus->bandwidth_allocated += bustime;
- if (isoc)
- dev->bus->bandwidth_isoc_reqs++;
- else
- dev->bus->bandwidth_int_reqs++;
- urb->bandwidth = bustime;
-
-#ifdef USB_BANDWIDTH_MESSAGES
- dbg("bandwidth alloc increased by %d to %d for %d requesters",
- bustime,
- dev->bus->bandwidth_allocated,
- dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
-#endif
-}
-
-/*
- * usb_release_bandwidth():
- *
- * called to release a pipe's bandwidth (in microseconds)
- */
-void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc)
-{
- dev->bus->bandwidth_allocated -= urb->bandwidth;
- if (isoc)
- dev->bus->bandwidth_isoc_reqs--;
- else
- dev->bus->bandwidth_int_reqs--;
-
-#ifdef USB_BANDWIDTH_MESSAGES
- dbg("bandwidth alloc reduced by %d to %d for %d requesters",
- urb->bandwidth,
- dev->bus->bandwidth_allocated,
- dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
-#endif
- urb->bandwidth = 0;
-}
-
-static void usb_bus_get(struct usb_bus *bus)
-{
- atomic_inc(&bus->refcnt);
-}
-
-static void usb_bus_put(struct usb_bus *bus)
-{
- if (atomic_dec_and_test(&bus->refcnt))
- kfree(bus);
-}
-
-/**
- * usb_alloc_bus - creates a new USB host controller structure (usbcore-internal)
- * @op: pointer to a struct usb_operations that this bus structure should use
- *
- * Creates a USB host controller bus structure with the specified
- * usb_operations and initializes all the necessary internal objects.
- * (For use only by USB Host Controller Drivers.)
- *
- * If no memory is available, NULL is returned.
- *
- * The caller should call usb_free_bus() when it is finished with the structure.
- */
-struct usb_bus *usb_alloc_bus(struct usb_operations *op)
-{
- struct usb_bus *bus;
-
- bus = kmalloc(sizeof(*bus), GFP_KERNEL);
- if (!bus)
- return NULL;
-
- memset(&bus->devmap, 0, sizeof(struct usb_devmap));
-
-#ifdef DEVNUM_ROUND_ROBIN
- bus->devnum_next = 1;
-#endif /* DEVNUM_ROUND_ROBIN */
-
- bus->op = op;
- bus->root_hub = NULL;
- bus->hcpriv = NULL;
- bus->busnum = -1;
- bus->bandwidth_allocated = 0;
- bus->bandwidth_int_reqs = 0;
- bus->bandwidth_isoc_reqs = 0;
-
- INIT_LIST_HEAD(&bus->bus_list);
-
- atomic_set(&bus->refcnt, 1);
-
- return bus;
-}
-
-/**
- * usb_free_bus - frees the memory used by a bus structure (usbcore-internal)
- * @bus: pointer to the bus to free
- *
- * (For use only by USB Host Controller Drivers.)
- */
-void usb_free_bus(struct usb_bus *bus)
-{
- if (!bus)
- return;
-
- usb_bus_put(bus);
-}
-
-/**
- * usb_register_bus - registers the USB host controller with the usb core (usbcore-internal)
- * @bus: pointer to the bus to register
- *
- * (For use only by USB Host Controller Drivers.)
- *
- * This call is synchronous, and may not be used in an interrupt context.
- */
-void usb_register_bus(struct usb_bus *bus)
-{
- int busnum;
-
- down (&usb_bus_list_lock);
- busnum = find_next_zero_bit(busmap.busmap, USB_MAXBUS, 1);
- if (busnum < USB_MAXBUS) {
- set_bit(busnum, busmap.busmap);
- bus->busnum = busnum;
- } else
- warn("too many buses");
-
- usb_bus_get(bus);
-
- /* Add it to the list of buses */
- list_add(&bus->bus_list, &usb_bus_list);
- up (&usb_bus_list_lock);
-
- usbfs_add_bus(bus);
-
- info("new USB bus registered, assigned bus number %d", bus->busnum);
-}
-
-/**
- * usb_deregister_bus - deregisters the USB host controller (usbcore-internal)
- * @bus: pointer to the bus to deregister
- *
- * (For use only by USB Host Controller Drivers.)
- *
- * This call is synchronous, and may not be used in an interrupt context.
- */
-void usb_deregister_bus(struct usb_bus *bus)
-{
- info("USB bus %d deregistered", bus->busnum);
-
- /*
- * NOTE: make sure that all the devices are removed by the
- * controller code, as well as having it call this when cleaning
- * itself up
- */
- down (&usb_bus_list_lock);
- list_del(&bus->bus_list);
- up (&usb_bus_list_lock);
-
- usbfs_remove_bus(bus);
-
- clear_bit(bus->busnum, busmap.busmap);
-
- usb_bus_put(bus);
-}
-
/*
* This function is for doing a depth-first search for devices which
* have support, for dynamic loading of driver modules.
* usb_alloc_dev - allocate a usb device structure (usbcore-internal)
* @parent: hub to which device is connected
* @bus: bus used to access the device
+ * Context: !in_interrupt ()
*
* Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this.
atomic_inc(&dev->refcnt);
}
-/* ----------------------------------------------------------------------
- * New USB Core Functions
- * ----------------------------------------------------------------------*/
/**
* usb_alloc_urb - creates a new urb for a USB driver to use
* @iso_packets: number of iso packets for this urb
- * @mem_flags: the type of memory to allocate, see kmalloc() for a list of valid options for this.
+ * @mem_flags: the type of memory to allocate, see kmalloc() for a list of
+ * valid options for this.
*
* Creates an urb for the USB driver to use, initializes a few internal
* structures, incrementes the usage counter, and returns a pointer to it.
void usb_free_urb(struct urb *urb)
{
if (urb)
- if (atomic_dec_and_test(&urb->count))
+ if (atomic_dec_and_test(&urb->count)) {
+ info ("really freeing urb");
kfree(urb);
+ }
}
/**
/**
* usb_submit_urb - asynchronously issue a transfer request for an endpoint
* @urb: pointer to the urb describing the request
- * @mem_flags: the type of memory to allocate, see kmalloc() for a list of valid options for this.
+ * @mem_flags: the type of memory to allocate, see kmalloc() for a list
+ * of valid options for this.
*
* This submits a transfer request, and transfers control of the URB
* describing that request to the USB subsystem. Request completion will
* @index: USB message index value
* @data: pointer to the data to send
* @size: length in bytes of the data to send
- * @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever)
+ * @timeout: time in jiffies to wait for the message to complete before
+ * timing out (if 0 the wait is forever)
+ * Context: !in_interrupt ()
*
* This function sends a simple control message to a specified endpoint
* and waits for the message to complete, or timeout.
* If successful, it returns 0, otherwise a negative error number.
*
* Don't use this function from within an interrupt context, like a
- * bottom half handler. If you need a asyncronous message, or need to send
+ * bottom half handler. If you need an asynchronous message, or need to send
* a message from within interrupt context, use usb_submit_urb()
*/
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
* @data: pointer to the data to send
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred in bytes
- * @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever)
+ * @timeout: time in jiffies to wait for the message to complete before
+ * timing out (if 0 the wait is forever)
+ * Context: !in_interrupt ()
*
* This function sends a simple bulk message to a specified endpoint
* and waits for the message to complete, or timeout.
* actual_length paramater.
*
* Don't use this function from within an interrupt context, like a
- * bottom half handler. If you need a asyncronous message, or need to
+ * bottom half handler. If you need an asynchronous message, or need to
* send a message from within interrupt context, use usb_submit_urb()
*/
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
* Returns the current frame number for the USB host controller
* used with the given USB device. This can be used when scheduling
* isochronous requests.
+ *
+ * Note that different kinds of host controller have different
+ * "scheduling horizons". While one type might support scheduling only
+ * 32 frames into the future, others could support scheduling up to
+ * 1024 frames into the future.
*/
int usb_get_current_frame_number(struct usb_device *dev)
{
/**
* usb_disconnect - disconnect a device (usbcore-internal)
* @pdev: pointer to device being disconnected
+ * Context: !in_interrupt ()
*
* Something got disconnected. Get rid of it, and all of its children.
*
* @index: the number of the descriptor
* @buf: where to put the descriptor
* @size: how big is "buf"?
+ * Context: !in_interrupt ()
*
* Gets a USB descriptor. Convenience functions exist to simplify
* getting some types of descriptors. Use
* @index: the number of the descriptor
* @buf: where to put the string
* @size: how big is "buf"?
+ * Context: !in_interrupt ()
*
* Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character,
* in little-endian byte order).
/**
* usb_get_device_descriptor - (re)reads the device descriptor
* @dev: the device whose device descriptor is being updated
+ * Context: !in_interrupt ()
*
* Updates the copy of the device descriptor stored in the device structure,
* which dedicates space for this purpose. Note that several fields are
* @type: USB_RECIP_*; for device, interface, or endpoint
* @target: zero (for device), else interface or endpoint number
* @data: pointer to two bytes of bitmap data
+ * Context: !in_interrupt ()
*
* Returns device, interface, or endpoint status. Normally only of
* interest to see if the device is self powered, or has enabled the
* usb_clear_halt - tells device to clear endpoint halt/stall condition
* @dev: device whose endpoint is halted
* @pipe: endpoint "pipe" being cleared
+ * Context: !in_interrupt ()
*
* This is used to clear halt conditions for bulk and interrupt endpoints,
* as reported by URB completion status. Endpoints that are halted are
* @dev: the device whose interface is being updated
* @interface: the interface being updated
* @alternate: the setting being chosen.
+ * Context: !in_interrupt ()
*
* This is used to enable data transfers on interfaces that may not
* be enabled by default. Not all devices support such configurability.
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
* @configuration: the configuration being chosen.
+ * Context: !in_interrupt ()
*
* This is used to enable non-default device modes. Not all devices
* support this kind of configurability. By default, configuration
* @index: the number of the descriptor
* @buf: where to put the string
* @size: how big is "buf"?
+ * Context: !in_interrupt ()
*
* This converts the UTF-16LE encoded strings returned by devices, from
* usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
* @dev: the device whose path is being constructed
* @buf: where to put the string
* @size: how big is "buf"?
+ * Context: !in_interrupt ()
*
* Returns length of the string (>= 0) or out of memory status (< 0).
+ *
+ * NOTE: prefer to use use dev->devpath directly.
*/
int usb_make_path(struct usb_device *dev, char *buf, size_t size)
{
return 0;
}
-/**
- * usb_register_root_hub - called by a usb host controller to register the root hub device in the system
- * @usb_dev: the usb root hub device to be registered.
- * @parent_dev: the parent device of this root hub.
- *
- * The USB host controller calls this function to register the root hub
- * properly with the USB subsystem. It sets up the device properly in
- * the driverfs tree, and then calls usb_new_device() to register the
- * usb device.
- */
-int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev)
-{
- int retval;
-
- usb_dev->dev.parent = parent_dev;
- strcpy (&usb_dev->dev.name[0], "usb_name");
- strcpy (&usb_dev->dev.bus_id[0], "usb_bus");
- retval = usb_new_device (usb_dev);
- if (retval)
- put_device (&usb_dev->dev);
- return retval;
-}
-
static int usb_open(struct inode * inode, struct file * file)
{
int minor = minor(inode->i_rdev);
*/
static int __init usb_init(void)
{
- init_MUTEX(&usb_bus_list_lock);
usb_major_init();
usbfs_init();
usb_hub_init();
EXPORT_SYMBOL(usb_register);
EXPORT_SYMBOL(usb_deregister);
EXPORT_SYMBOL(usb_scan_devices);
-EXPORT_SYMBOL(usb_alloc_bus);
-EXPORT_SYMBOL(usb_free_bus);
-EXPORT_SYMBOL(usb_register_bus);
-EXPORT_SYMBOL(usb_deregister_bus);
-EXPORT_SYMBOL(usb_register_root_hub);
+
EXPORT_SYMBOL(usb_alloc_dev);
EXPORT_SYMBOL(usb_free_dev);
EXPORT_SYMBOL(usb_inc_dev_use);
EXPORT_SYMBOL(usb_connect);
EXPORT_SYMBOL(usb_disconnect);
-EXPORT_SYMBOL(usb_check_bandwidth);
-EXPORT_SYMBOL(usb_claim_bandwidth);
-EXPORT_SYMBOL(usb_release_bandwidth);
-
EXPORT_SYMBOL(__usb_get_extra_descriptor);
EXPORT_SYMBOL(usb_get_current_frame_number);
/*
* USB directions
*/
-#define USB_DIR_OUT 0
-#define USB_DIR_IN 0x80
+#define USB_DIR_OUT 0 /* to device */
+#define USB_DIR_IN 0x80 /* to host */
/*
* Endpoints
unsigned long devicemap[128 / (8*sizeof(unsigned long))];
};
-#define USB_MAXBUS 64
-
-struct usb_busmap {
- unsigned long busmap[USB_MAXBUS / (8*sizeof(unsigned long))];
-};
-
struct usb_device;
/*-------------------------------------------------------------------------*/
* work to connect to a device should be done when the device is opened,
* and undone at the last close. The disconnect code needs to address
* concurrency issues with respect to open() and close() methods, as
- * well as cancel any I/O requests that are still pending.
+ * well as forcing all pending I/O requests to complete (by unlinking
+ * them as necessary, and blocking until the unlinks complete).
*/
struct usb_driver {
struct module *owner;
/* Host Controller Driver (HCD) support */
-struct usb_operations {
- int (*allocate)(struct usb_device *);
- int (*deallocate)(struct usb_device *);
- int (*get_frame_number) (struct usb_device *usb_dev);
- int (*submit_urb) (struct urb *urb, int mem_flags);
- int (*unlink_urb) (struct urb *urb);
-};
+struct usb_operations;
#define DEVNUM_ROUND_ROBIN /***** OPTION *****/
atomic_t refcnt;
};
-extern struct usb_bus *usb_alloc_bus(struct usb_operations *);
-extern void usb_free_bus(struct usb_bus *);
-extern void usb_register_bus(struct usb_bus *);
-extern void usb_deregister_bus(struct usb_bus *);
-extern int usb_register_root_hub(struct usb_device *usb_dev, struct device *parent_dev);
-
-extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
-extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,
- int bustime, int isoc);
-extern void usb_release_bandwidth(struct usb_device *dev, struct urb *urb,
- int isoc);
+// FIXME: root_hub_string vanishes when "usb_hcd" conversion is done,
+// along with pre-hcd versions of the OHCI and UHCI drivers.
extern int usb_root_hub_string(int id, int serial,
char *type, __u8 *data, int len);
-/*
- * Some USB 1.1 bandwidth allocation constants.
- */
-#define BW_HOST_DELAY 1000L /* nanoseconds */
-#define BW_HUB_LS_SETUP 333L /* nanoseconds */
- /* 4 full-speed bit times (est.) */
-
-#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
-#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
-#define FRAME_TIME_USECS 1000L
-#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
-
-#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
- /* Trying not to use worst-case bit-stuffing
- of (7/6 * 8 * bytecount) = 9.33 * bytecount */
- /* bytecount = data payload byte count */
-
-#define NS_TO_US(ns) ((ns + 500L) / 1000L)
- /* convert & round nanoseconds to microseconds */
-
/*
* As of USB 2.0, full/low speed devices are segregated into trees.
* One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
/* -------------------------------------------------------------------------- */
/*
- * bus and driver list
+ * driver list
* exported only for usbfs (not visible outside usbcore)
*/
extern struct list_head usb_driver_list;
-extern struct list_head usb_bus_list;
-extern struct semaphore usb_bus_list_lock;
/*
* USB device fs stuff