📄 usb-uhci-hcd.c
字号:
}
/*--------------------------------------------------------------------------*/
static int hc_start (struct uhci_hcd *uhci)
{
unsigned long io_addr = (unsigned long)uhci->hcd.regs;
int timeout = 10;
struct usb_device *udev;
init_dbg("hc_start uhci %p",uhci);
/*
* Reset the HC - this will force us to get a
* new notification of any already connected
* ports due to the virtual disconnect that it
* implies.
*/
outw (USBCMD_HCRESET, io_addr + USBCMD);
while (inw (io_addr + USBCMD) & USBCMD_HCRESET) {
if (!--timeout) {
err("USBCMD_HCRESET timed out!");
break;
}
udelay(1);
}
hc_irq_run(uhci);
/* connect the virtual root hub */
uhci->hcd.self.root_hub = udev = usb_alloc_dev (NULL, &uhci->hcd.self);
uhci->hcd.state = USB_STATE_READY;
if (!udev) {
uhci->running = 0;
return -ENOMEM;
}
usb_connect (udev);
udev->speed = USB_SPEED_FULL;
if (usb_register_root_hub (udev, &uhci->hcd.pdev->dev) != 0) {
usb_free_dev (udev);
uhci->running = 0;
return -ENODEV;
}
return 0;
}
/*--------------------------------------------------------------------------*/
// Start up UHCI, find ports, init DMA lists
static int __devinit uhci_start (struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
int ret;
unsigned long io_addr=(unsigned long)hcd->regs, io_size=0x20;
init_dbg("uhci_start hcd %p uhci %p, pdev %p",hcd,uhci,hcd->pdev);
/* disable legacy emulation, Linux takes over... */
pci_write_config_word (hcd->pdev, USBLEGSUP, 0);
/* UHCI specs says devices must have 2 ports, but goes on to say */
/* they may have more but give no way to determine how many they */
/* have, so default to 2 */
/* According to the UHCI spec, Bit 7 is always set to 1. So we try */
/* to use this to our advantage */
for (uhci->maxports = 0; uhci->maxports < (io_size - 0x10) / 2; uhci->maxports++) {
unsigned int portstatus;
portstatus = inw (io_addr + 0x10 + (uhci->maxports * 2));
dbg("port %i, adr %x status %x", uhci->maxports,
io_addr + 0x10 + (uhci->maxports * 2), portstatus);
if (!(portstatus & 0x0080))
break;
}
warn("Detected %d ports", uhci->maxports);
if (uhci->maxports < 2 || uhci->maxports > 8) {
dbg("Port count misdetected, forcing to 2 ports");
uhci->maxports = 2;
}
ret=init_skel(uhci);
if (ret)
return ret;
hc_reset (uhci);
if (hc_start (uhci) < 0) {
err ("can't start %s", uhci->hcd.self.bus_name);
uhci_stop (hcd);
return -EBUSY;
}
// Enable PIRQ
pci_write_config_word (hcd->pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
set_td_ioc(uhci->td128ms); // start watchdog interrupt
uhci->last_hcd_irq=jiffies+5*HZ;
return 0;
}
/*--------------------------------------------------------------------------*/
static void uhci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
{
dbg("uhci_free_config for dev %p", udev);
uhci_unlink_urbs (hcd_to_uhci (hcd), udev, 0); // Forced unlink of remaining URBs
}
/*--------------------------------------------------------------------------*/
static void uhci_stop (struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
init_dbg("%s: stop controller", hcd->bus_name);
uhci->running=0;
hc_reset (uhci);
wait_ms (1);
uhci_unlink_urbs (uhci, 0, CLEAN_FORCED); // Forced unlink of remaining URBs
uhci_cleanup_unlink (uhci, CLEAN_FORCED); // force cleanup of async killed URBs
cleanup_skel (uhci);
}
/*--------------------------------------------------------------------------*/
// UHCI INTERRUPT PROCESSING
/*--------------------------------------------------------------------------*/
static void uhci_irq (struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
unsigned long io_addr = (unsigned long)hcd->regs;
unsigned short status;
struct list_head *p, *p2;
int restarts, work_done;
/*
* Read the interrupt status, and write it back to clear the
* interrupt cause
*/
status = inw (io_addr + USBSTS);
if (!status) /* shared interrupt, not mine */
return;
dbg("interrupt");
uhci->last_hcd_irq=jiffies; // for watchdog
if (status != 1) {
// Avoid too much error messages at a time
if (time_after(jiffies, uhci->last_error_time + ERROR_SUPPRESSION_TIME)) {
warn("interrupt, status %x, frame# %i", status,
UHCI_GET_CURRENT_FRAME(uhci));
uhci->last_error_time = jiffies;
}
// remove host controller halted state
if ((status&0x20) && (uhci->running)) {
err("Host controller halted, waiting for timeout.");
// outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
}
//uhci_show_status (s);
}
/*
* traverse the list in *reverse* direction, because new entries
* may be added at the end.
* also, because process_urb may unlink the current urb,
* we need to advance the list before
* New: check for max. workload and restart count
*/
spin_lock (&uhci->urb_list_lock);
restarts=0;
work_done=0;
restart:
uhci->unlink_urb_done=0;
p = uhci->urb_list.prev;
while (p != &uhci->urb_list && (work_done < 1024)) {
p2 = p;
p = p->prev;
process_urb (uhci, p2);
work_done++;
if (uhci->unlink_urb_done) {
uhci->unlink_urb_done=0;
restarts++;
if (restarts<16) // avoid endless restarts
goto restart;
else
break;
}
}
if (time_after(jiffies, uhci->timeout_check + (HZ/30)))
uhci_check_timeouts(uhci);
clean_descs(uhci, CLEAN_NOT_FORCED);
uhci_cleanup_unlink(uhci, CLEAN_NOT_FORCED);
uhci_switch_timer_int(uhci);
spin_unlock (&uhci->urb_list_lock);
outw (status, io_addr + USBSTS);
//dbg("uhci_interrupt: done");
}
/*--------------------------------------------------------------------------*/
// POWER MANAGEMENT
#ifdef CONFIG_PM
static int uhci_suspend (struct usb_hcd *hcd, u32 state)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
hc_reset(uhci);
return 0;
}
/*--------------------------------------------------------------------------*/
static int uhci_resume (struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
hc_start(uhci);
return 0;
}
#endif
/*--------------------------------------------------------------------------*/
static const char hcd_name [] = "usb-uhci-hcd";
static const struct hc_driver uhci_driver = {
description: hcd_name,
// generic hardware linkage
irq: uhci_irq,
flags: HCD_USB11,
// basic lifecycle operations
start: uhci_start,
#ifdef CONFIG_PM
suspend: uhci_suspend,
resume: uhci_resume,
#endif
stop: uhci_stop,
// memory lifecycle (except per-request)
hcd_alloc: uhci_hcd_alloc,
hcd_free: uhci_hcd_free,
// managing i/o requests and associated device resources
urb_enqueue: uhci_urb_enqueue,
urb_dequeue: uhci_urb_dequeue,
free_config: uhci_free_config,
// scheduling support
get_frame_number: uhci_get_frame,
// root hub support
hub_status_data: uhci_hub_status_data,
hub_control: uhci_hub_control,
};
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_INFO);
MODULE_LICENSE ("GPL");
MODULE_PARM (high_bw, "i");
MODULE_PARM_DESC (high_bw, "high_hw: Enable high bandwidth mode, 1=on (default), 0=off");
MODULE_PARM (bulk_depth, "i");
MODULE_PARM_DESC (bulk_depth, "bulk_depth: Depth first processing for bulk transfers, 0=off (default), 1=on");
MODULE_PARM (ctrl_depth, "i");
MODULE_PARM_DESC (ctrl_depth, "ctrl_depth: Depth first processing for control transfers, 0=off (default), 1=on");
static const struct pci_device_id __devinitdata pci_ids [] = { {
/* handle any USB UHCI controller */
class: (PCI_CLASS_SERIAL_USB << 8) | 0x00,
class_mask: ~0,
driver_data: (unsigned long) &uhci_driver,
/* no matter who makes it */
vendor: PCI_ANY_ID,
device: PCI_ANY_ID,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, pci_ids);
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver uhci_pci_driver = {
name: (char *) hcd_name,
id_table: pci_ids,
probe: usb_hcd_pci_probe,
remove: usb_hcd_pci_remove,
#ifdef CONFIG_PM
suspend: usb_hcd_pci_suspend,
resume: usb_hcd_pci_resume,
#endif
};
/*-------------------------------------------------------------------------*/
static int __init uhci_hcd_init (void)
{
init_dbg (DRIVER_INFO);
init_dbg ("block sizes: hq %d td %d",
sizeof (struct qh), sizeof (struct td));
info("High bandwidth mode %s.%s%s",
high_bw?"enabled":"disabled",
ctrl_depth?"CTRL depth first enabled":"",
bulk_depth?"BULK depth first enabled":"");
return pci_module_init (&uhci_pci_driver);
}
static void __exit uhci_hcd_cleanup (void)
{
pci_unregister_driver (&uhci_pci_driver);
}
module_init (uhci_hcd_init);
module_exit (uhci_hcd_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -