📄 hcd.c
字号:
/* any errors get returned through the urb completion */
usb_hcd_giveback_urb (hcd, urb);
return 0;
}
/*-------------------------------------------------------------------------*/
/*
* Root Hub interrupt transfers are synthesized with a timer.
* Completions are called in_interrupt() but not in_irq().
*/
static void rh_report_status (unsigned long ptr);
static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb)
{
int len = 1 + (urb->dev->maxchild / 8);
/* rh_timer protected by hcd_data_lock */
if (timer_pending (&hcd->rh_timer)
|| urb->status != -EINPROGRESS
|| !HCD_IS_RUNNING (hcd->state)
|| urb->transfer_buffer_length < len) {
dbg ("not queuing status urb, stat %d", urb->status);
return -EINVAL;
}
urb->hcpriv = hcd; /* nonzero to indicate it's queued */
init_timer (&hcd->rh_timer);
hcd->rh_timer.function = rh_report_status;
hcd->rh_timer.data = (unsigned long) urb;
/* USB 2.0 spec says 256msec; this is close enough */
hcd->rh_timer.expires = jiffies + HZ/4;
add_timer (&hcd->rh_timer);
return 0;
}
/* timer callback */
static void rh_report_status (unsigned long ptr)
{
struct urb *urb;
struct usb_hcd *hcd;
int length;
unsigned long flags;
urb = (struct urb *) ptr;
spin_lock_irqsave (&urb->lock, flags);
if (!urb->dev) {
spin_unlock_irqrestore (&urb->lock, flags);
return;
}
hcd = urb->dev->bus->hcpriv;
if (urb->status == -EINPROGRESS) {
if (HCD_IS_RUNNING (hcd->state)) {
length = hcd->driver->hub_status_data (hcd,
urb->transfer_buffer);
spin_unlock_irqrestore (&urb->lock, flags);
if (length > 0) {
urb->actual_length = length;
urb->status = 0;
urb->complete (urb);
}
spin_lock_irqsave (&hcd_data_lock, flags);
urb->status = -EINPROGRESS;
if (HCD_IS_RUNNING (hcd->state)
&& rh_status_urb (hcd, urb) != 0) {
/* another driver snuck in? */
dbg ("%s, can't resubmit roothub status urb?",
hcd->self.bus_name);
spin_unlock_irqrestore (&hcd_data_lock, flags);
BUG ();
}
spin_unlock_irqrestore (&hcd_data_lock, flags);
} else
spin_unlock_irqrestore (&urb->lock, flags);
} else {
/* this urb's been unlinked */
urb->hcpriv = 0;
spin_unlock_irqrestore (&urb->lock, flags);
usb_hcd_giveback_urb (hcd, urb);
}
}
/*-------------------------------------------------------------------------*/
static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{
if (usb_pipeint (urb->pipe)) {
int retval;
unsigned long flags;
spin_lock_irqsave (&hcd_data_lock, flags);
retval = rh_status_urb (hcd, urb);
spin_unlock_irqrestore (&hcd_data_lock, flags);
return retval;
}
if (usb_pipecontrol (urb->pipe))
return rh_call_control (hcd, urb);
else
return -EINVAL;
}
/*-------------------------------------------------------------------------*/
static void rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
unsigned long flags;
spin_lock_irqsave (&hcd_data_lock, flags);
del_timer_sync (&hcd->rh_timer);
hcd->rh_timer.data = 0;
spin_unlock_irqrestore (&hcd_data_lock, flags);
/* we rely on RH callback code not unlinking its URB! */
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: approximate periodic transaction time in nanoseconds
* @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH}
* @is_input: true iff the transaction sends data to the host
* @is_isoc: true for isochronous transactions, false for interrupt ones
* @bytecount: how many bytes in the transaction.
*
* Returns approximate bus time in nanoseconds for a periodic transaction.
* See USB 2.0 spec section 5.11.3; only periodic transfers need to be
* scheduled in software, this function is only used for such scheduling.
*/
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 adjust for input vs output
if (isoc)
tmp = HS_USECS (bytecount);
else
tmp = HS_USECS_ISO (bytecount);
return tmp;
default:
dbg ("bogus device speed!");
return -1;
}
}
EXPORT_SYMBOL (usb_calc_bus_time);
/*
* 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -