📄 hcd.c
字号:
spin_unlock (&urb->lock);
local_irq_restore (flags);
return;
}
length = hcd->driver->hub_status_data (hcd, urb->transfer_buffer);
/* complete the status urb, or retrigger the timer */
spin_lock (&hcd_data_lock);
if (length > 0) {
hcd->rh_timer.data = 0;
urb->actual_length = length;
urb->status = 0;
urb->hcpriv = 0;
} else
mod_timer (&hcd->rh_timer, HZ/4);
spin_unlock (&hcd_data_lock);
spin_unlock (&urb->lock);
/* local irqs are always blocked in completions */
if (length > 0)
usb_hcd_giveback_urb (hcd, urb, NULL);
local_irq_restore (flags);
}
/*-------------------------------------------------------------------------*/
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;
}
/*-------------------------------------------------------------------------*/
void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
//unsigned long flags;
/* note: always a synchronous unlink */
del_timer_sync (&hcd->rh_timer);
hcd->rh_timer.data = 0;
local_irq_save (flags);
urb->hcpriv = 0;
usb_hcd_giveback_urb (hcd, urb, NULL);
local_irq_restore (flags);
}
/*-------------------------------------------------------------------------*/
/* 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);
}
/*-------------------------------------------------------------------------*/
/**
* usb_bus_init - shared initialization code
* @bus: the bus structure being initialized
*
* This code is used to initialize a usb_bus structure, memory for which is
* separately managed.
*/
void STDCALL usb_bus_init (struct usb_bus *bus)
{
memset (&bus->devmap, 0, sizeof(struct usb_devmap));
bus->devnum_next = 1;
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 STDCALL *usb_alloc_bus (struct usb_operations *op)
{
struct usb_bus *bus;
bus = kmalloc (sizeof *bus, GFP_KERNEL);
if (!bus)
return NULL;
usb_bus_init (bus);
bus->op = op;
return 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 STDCALL 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);
}
/*-------------------------------------------------------------------------*/
/**
* 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 STDCALL 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);
dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
}
/**
* 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 STDCALL usb_deregister_bus (struct usb_bus *bus)
{
dev_info (bus->controller, "USB bus %d deregistered\n", 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);
}
/**
* 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 STDCALL usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev)
{
int retval;
sprintf (&usb_dev->dev.bus_id[0], "usb%d", usb_dev->bus->busnum);
usb_dev->state = USB_STATE_DEFAULT;
retval = usb_new_device (usb_dev, parent_dev);
if (retval)
dev_err (parent_dev, "can't register root hub for %s, %d\n",
usb_dev->dev.bus_id, retval);
return retval;
}
/*-------------------------------------------------------------------------*/
/**
* 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
* @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 STDCALL 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;
}
}
/*
* 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 STDCALL 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_MODE
#ifdef DEBUG
char *mode =
#ifdef CONFIG_USB_BANDWIDTH
"";
#else
"would have ";
#endif
dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n",
mode, old_alloc, bustime, new_alloc);
#endif
#endif
#ifdef CONFIG_USB_BANDWIDTH
bustime = -ENOSPC; /* report error */
#endif
}
return bustime;
}
/**
* 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 STDCALL 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
dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n",
bustime,
isoc ? "ISOC" : "INTR",
dev->bus->bandwidth_allocated,
dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
#endif
}
/**
* 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 STDCALL 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
dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n",
urb->bandwidth,
isoc ? "ISOC" : "INTR",
dev->bus->bandwidth_allocated,
dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
#endif
urb->bandwidth = 0;
}
/*-------------------------------------------------------------------------*/
/*
* Generic HC operations.
*/
/*-------------------------------------------------------------------------*/
/* called from khubd, or root hub init threads for hcd-private init */
static int hcd_alloc_dev (struct usb_device *udev)
{
struct hcd_dev *dev;
struct usb_hcd *hcd;
unsigned long flags;
if (!udev || udev->hcpriv)
return -EINVAL;
if (!udev->bus || !udev->bus->hcpriv)
return -ENODEV;
hcd = udev->bus->hcpriv;
if (hcd->state == USB_STATE_QUIESCING)
return -ENOLINK;
dev = (struct hcd_dev *) kmalloc (sizeof *dev, GFP_KERNEL);
if (dev == NULL)
return -ENOMEM;
memset (dev, 0, sizeof *dev);
INIT_LIST_HEAD (&dev->dev_list);
INIT_LIST_HEAD (&dev->urb_list);
spin_lock_irqsave (&hcd_data_lock, flags);
list_add (&dev->dev_list, &hcd->dev_list);
// refcount is implicit
udev->hcpriv = dev;
spin_unlock_irqrestore (&hcd_data_lock, flags);
return 0;
}
/*-------------------------------------------------------------------------*/
static void urb_unlink (struct urb *urb)
{
unsigned long flags;
struct usb_device *dev;
/* Release any periodic transfer bandwidth */
if (urb->bandwidth)
usb_release_bandwidth (urb->dev, urb,
usb_pipeisoc (urb->pipe));
/* clear all state linking urb to this dev (and hcd) */
spin_lock_irqsave (&hcd_data_lock, flags);
list_del_init (&urb->urb_list);
dev = urb->dev;
spin_unlock_irqrestore (&hcd_data_lock, flags);
usb_put_dev (dev);
}
/* may be called in any context with a valid urb->dev usecount
* caller surrenders "ownership" of urb
* expects usb_submit_urb() to have sanity checked and conditioned all
* inputs in the urb
*/
static int hcd_submit_urb (struct urb *urb, int mem_flags)
{
int status;
struct usb_hcd *hcd = urb->dev->bus->hcpriv;
struct hcd_dev *dev = urb->dev->hcpriv;
unsigned long flags;
if (!hcd || !dev)
return -ENODEV;
//printk("submit_urb %p, # %i, t %i\n",urb,urb->dev->devnum,usb_pipetype(urb->pipe));
/*
* FIXME: make urb timeouts be generic, keeping the HCD cores
* as simple as possible.
*/
// NOTE: a generic device/urb monitoring hook would go here.
// hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)
// It would catch submission paths for all urbs.
/*
* Atomically queue the urb, first to our records, then to the HCD.
* Access to urb->status is controlled by urb->lock ... changes on
* i/o completion (normal or fault) or unlinking.
*/
// FIXME: verify that quiescing hc works right (RH cleans up)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -