📄 uhci-hcd.c
字号:
if (!uhci->hcd.pdev ||
uhci->hcd.pdev->vendor != PCI_VENDOR_ID_INTEL ||
uhci->hcd.pdev->device != PCI_DEVICE_ID_INTEL_82371AB_2)
return 1;
/* This is a 82371AB/EB/MB USB controller which has a bug that
* causes false resume indications if any port has an
* over current condition. To prevent problems, we will not
* allow a global suspend if any ports are OC.
*
* Some motherboards using the 82371AB/EB/MB (but not the USB portion)
* appear to hardwire the over current inputs active to disable
* the USB ports.
*/
/* check for over current condition on any port */
for (i = 0; i < uhci->rh_numports; i++) {
if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
return 0;
}
return 1;
}
static void hc_state_transitions(struct uhci_hcd *uhci)
{
switch (uhci->state) {
case UHCI_RUNNING:
/* global suspend if nothing connected for 1 second */
if (!ports_active(uhci) && suspend_allowed(uhci)) {
uhci->state = UHCI_SUSPENDING_GRACE;
uhci->state_end = jiffies + HZ;
}
break;
case UHCI_SUSPENDING_GRACE:
if (ports_active(uhci))
uhci->state = UHCI_RUNNING;
else if (time_after_eq(jiffies, uhci->state_end))
suspend_hc(uhci);
break;
case UHCI_SUSPENDED:
/* wakeup if requested by a device */
if (uhci->resume_detect)
wakeup_hc(uhci);
break;
case UHCI_RESUMING_1:
case UHCI_RESUMING_2:
case UHCI_RUNNING_GRACE:
if (time_after_eq(jiffies, uhci->state_end))
wakeup_hc(uhci);
break;
default:
break;
}
}
static void start_hc(struct uhci_hcd *uhci)
{
unsigned int io_addr = uhci->io_addr;
int timeout = 1000;
/*
* 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) {
printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n");
break;
}
}
/* Turn on all interrupts */
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
io_addr + USBINTR);
/* Start at frame 0 */
outw(0, io_addr + USBFRNUM);
outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
/* Run and mark it configured with a 64-byte max packet */
uhci->state = UHCI_RUNNING_GRACE;
uhci->state_end = jiffies + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
uhci->hcd.state = USB_STATE_READY;
}
/*
* De-allocate all resources..
*/
static void release_uhci(struct uhci_hcd *uhci)
{
int i;
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
if (uhci->term_td) {
uhci_free_td(uhci, uhci->term_td);
uhci->term_td = NULL;
}
if (uhci->qh_pool) {
pci_pool_destroy(uhci->qh_pool);
uhci->qh_pool = NULL;
}
if (uhci->td_pool) {
pci_pool_destroy(uhci->td_pool);
uhci->td_pool = NULL;
}
if (uhci->fl) {
pci_free_consistent(uhci->hcd.pdev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
}
#ifdef CONFIG_PROC_FS
if (uhci->proc_entry) {
remove_proc_entry(uhci->hcd.self.bus_name, uhci_proc_root);
uhci->proc_entry = NULL;
}
#endif
}
/*
* Allocate a frame list, and then setup the skeleton
*
* The hardware doesn't really know any difference
* in the queues, but the order does matter for the
* protocols higher up. The order is:
*
* - any isochronous events handled before any
* of the queues. We don't do that here, because
* we'll create the actual TD entries on demand.
* - The first queue is the interrupt queue.
* - The second queue is the control queue, split into low and high speed
* - The third queue is bulk queue.
* - The fourth queue is the bandwidth reclamation queue, which loops back
* to the high speed control queue.
*/
static int __devinit uhci_start(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i, port;
unsigned io_size;
dma_addr_t dma_handle;
struct usb_device *udev;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *ent;
#endif
uhci->io_addr = (unsigned long) hcd->regs;
io_size = pci_resource_len(hcd->pdev, hcd->region);
#ifdef CONFIG_PROC_FS
ent = create_proc_entry(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
if (!ent) {
err("couldn't create uhci proc entry");
retval = -ENOMEM;
goto err_create_proc_entry;
}
ent->data = uhci;
ent->proc_fops = &uhci_proc_operations;
ent->size = 0;
uhci->proc_entry = ent;
#endif
/* Reset here so we don't get any interrupts from an old setup */
/* or broken setup */
reset_hc(uhci);
uhci->fsbr = 0;
uhci->fsbrtimeout = 0;
spin_lock_init(&uhci->qh_remove_list_lock);
INIT_LIST_HEAD(&uhci->qh_remove_list);
spin_lock_init(&uhci->urb_remove_list_lock);
INIT_LIST_HEAD(&uhci->urb_remove_list);
spin_lock_init(&uhci->urb_list_lock);
INIT_LIST_HEAD(&uhci->urb_list);
spin_lock_init(&uhci->complete_list_lock);
INIT_LIST_HEAD(&uhci->complete_list);
spin_lock_init(&uhci->frame_list_lock);
uhci->fl = pci_alloc_consistent(hcd->pdev, sizeof(*uhci->fl), &dma_handle);
if (!uhci->fl) {
err("unable to allocate consistent memory for frame list");
goto err_alloc_fl;
}
memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
uhci->fl->dma_handle = dma_handle;
uhci->td_pool = pci_pool_create("uhci_td", hcd->pdev,
sizeof(struct uhci_td), 16, 0);
if (!uhci->td_pool) {
err("unable to create td pci_pool");
goto err_create_td_pool;
}
uhci->qh_pool = pci_pool_create("uhci_qh", hcd->pdev,
sizeof(struct uhci_qh), 16, 0);
if (!uhci->qh_pool) {
err("unable to create qh pci_pool");
goto err_create_qh_pool;
}
/* Initialize the root hub */
/* 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. However, according to the UHCI spec, Bit 7 is always set */
/* to 1. So we try to use this to our advantage */
for (port = 0; port < (io_size - 0x10) / 2; port++) {
unsigned int portstatus;
portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
if (!(portstatus & 0x0080))
break;
}
if (debug)
info("detected %d ports", port);
/* This is experimental so anything less than 2 or greater than 8 is */
/* something weird and we'll ignore it */
if (port < 2 || port > 8) {
info("port count misdetected? forcing to 2 ports");
port = 2;
}
uhci->rh_numports = port;
hcd->self.root_hub = udev = usb_alloc_dev(NULL, &hcd->self);
if (!udev) {
err("unable to allocate root hub");
goto err_alloc_root_hub;
}
hcd->pdev->bus = (struct pci_bus *)udev; /* Fix bus pointer for initial device */
uhci->term_td = uhci_alloc_td(uhci, udev);
if (!uhci->term_td) {
err("unable to allocate terminating TD");
goto err_alloc_term_td;
}
for (i = 0; i < UHCI_NUM_SKELQH; i++) {
uhci->skelqh[i] = uhci_alloc_qh(uhci, udev);
if (!uhci->skelqh[i]) {
err("unable to allocate QH %d", i);
goto err_alloc_skelqh;
}
}
/*
* 8 Interrupt queues; link int2 to int1, int4 to int2, etc
* then link int1 to control and control to bulk
*/
uhci->skel_int128_qh->link = cpu_to_le32(uhci->skel_int64_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int64_qh->link = cpu_to_le32(uhci->skel_int32_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int32_qh->link = cpu_to_le32(uhci->skel_int16_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int16_qh->link = cpu_to_le32(uhci->skel_int8_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int8_qh->link = cpu_to_le32(uhci->skel_int4_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int4_qh->link = cpu_to_le32(uhci->skel_int2_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int2_qh->link = cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_hs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
/* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
uhci->skel_term_qh->link = UHCI_PTR_TERM;
uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
/*
* Fill the frame list: make all entries point to
* the proper interrupt queue.
*
* This is probably silly, but it's a simple way to
* scatter the interrupt queues in a way that gives
* us a reasonable dynamic range for irq latencies.
*/
for (i = 0; i < UHCI_NUMFRAMES; i++) {
int irq = 0;
if (i & 1) {
irq++;
if (i & 2) {
irq++;
if (i & 4) {
irq++;
if (i & 8) {
irq++;
if (i & 16) {
irq++;
if (i & 32) {
irq++;
if (i & 64)
irq++;
}
}
}
}
}
}
/* Only place we don't use the frame list routines */
uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[7 - irq]->dma_handle);
}
start_hc(uhci);
init_stall_timer(hcd);
/* disable legacy emulation */
pci_write_config_word(hcd->pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
usb_connect(udev);
udev->speed = USB_SPEED_FULL;
if (usb_register_root_hub(udev, &hcd->pdev->dev) != 0) {
err("unable to start root hub");
retval = -ENOMEM;
goto err_start_root_hub;
}
return 0;
/*
* error exits:
*/
err_start_root_hub:
reset_hc(uhci);
del_timer_sync(&uhci->stall_timer);
err_alloc_skelqh:
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
uhci_free_td(uhci, uhci->term_td);
uhci->term_td = NULL;
err_alloc_term_td:
usb_put_dev(udev);
hcd->self.root_hub = NULL;
err_alloc_root_hub:
pci_pool_destroy(uhci->qh_pool);
uhci->qh_pool = NULL;
err_create_qh_pool:
pci_pool_destroy(uhci->td_pool);
uhci->td_pool = NULL;
err_create_td_pool:
pci_free_consistent(hcd->pdev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
err_alloc_fl:
#ifdef CONFIG_PROC_FS
remove_proc_entry(hcd->self.bus_name, uhci_proc_root);
uhci->proc_entry = NULL;
err_create_proc_entry:
#endif
return retval;
}
static void uhci_stop(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
del_timer_sync(&uhci->stall_timer);
/*
* At this point, we're guaranteed that no new connects can be made
* to this bus since there are no more parents
*/
uhci_free_pending_qhs(uhci);
uhci_remove_pending_qhs(uhci);
reset_hc(uhci);
uhci_free_pending_qhs(uhci);
release_uhci(uhci);
}
#ifdef CONFIG_PM
static int uhci_suspend(struct usb_hcd *hcd, u32 state)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
/* Don't try to suspend broken motherboards, reset instead */
if (suspend_allowed(uhci))
suspend_hc(uhci);
else
reset_hc(uhci);
return 0;
}
static int uhci_resume(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
pci_set_master(uhci->hcd.pdev);
if (uhci->state == UHCI_SUSPENDED)
uhci->resume_detect = 1;
else {
reset_hc(uhci);
start_hc(uhci);
}
uhci->hcd.state = USB_STATE_READY;
return 0;
}
#endif
static struct usb_hcd *uhci_hcd_alloc(void)
{
struct uhci_hcd *uhci;
uhci = (struct uhci_hcd *)kmalloc(sizeof(*uhci), GFP_KERNEL);
if (!uhci)
return NULL;
memset(uhci, 0, sizeof(*uhci));
return &uhci->hcd;
}
static void uhci_hcd_free(struct usb_hcd *hcd)
{
kfree(hcd_to_uhci(hcd));
}
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
{
return uhci_get_current_frame_number(hcd_to_uhci(hcd));
}
static const char hcd_name[] = "uhci-hcd";
static 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,
.hcd_alloc = uhci_hcd_alloc,
.hcd_free = uhci_hcd_free,
.urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue,
.get_frame_number = uhci_hcd_get_frame_number,
.hub_status_data = uhci_hub_status_data,
.hub_control = uhci_hub_control,
};
const struct pci_device_id __devinitdata uhci_pci_ids[] = { {
/* handle any USB UHCI controller */
.class = ((PCI_CLASS_SERIAL_USB << 8) | 0x00),
.cl
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -