📄 uhci-hcd.c
字号:
outw(0, io_addr + USBCMD);
/* wait for EOP to be sent */
status = inw(io_addr + USBCMD);
while (status & USBCMD_FGR)
status = inw(io_addr + USBCMD);
uhci->is_suspended = 0;
/* Run and mark it configured with a 64-byte max packet */
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
}
static int ports_active(struct uhci_hcd *uhci)
{
unsigned int io_addr = uhci->io_addr;
int connection = 0;
int i;
for (i = 0; i < uhci->rh_numports; i++)
connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1);
return connection;
}
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 */
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
}
#ifdef CONFIG_PROC_FS
static int uhci_num = 0;
#endif
/*
* De-allocate all resources..
*/
static void release_uhci(struct uhci_hcd *uhci)
{
int i;
#ifdef CONFIG_PROC_FS
char buf[8];
#endif
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
for (i = 0; i < UHCI_NUM_SKELTD; i++)
if (uhci->skeltd[i]) {
uhci_free_td(uhci, uhci->skeltd[i]);
uhci->skeltd[i] = 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->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
}
#ifdef CONFIG_PROC_FS
if (uhci->proc_entry) {
sprintf(buf, "hc%d", uhci->num);
remove_proc_entry(buf, 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);
struct pci_dev *dev = hcd->pdev;
int retval = -EBUSY;
int i, port;
dma_addr_t dma_handle;
#ifdef CONFIG_PROC_FS
char buf[8];
struct proc_dir_entry *ent;
#endif
uhci->dev = dev;
/* Should probably move to core/hcd.c */
if (pci_set_dma_mask(dev, 0xFFFFFFFF)) {
err("couldn't set PCI dma mask");
retval = -ENODEV;
goto err_pci_set_dma_mask;
}
uhci->io_addr = pci_resource_start(dev, hcd->region);
uhci->io_size = pci_resource_len(dev, hcd->region);
#ifdef CONFIG_PROC_FS
uhci->num = uhci_num++;
sprintf(buf, "hc%d", uhci->num);
ent = create_proc_entry(buf, 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;
uhci->is_suspended = 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(dev, 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", dev,
sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC);
if (!uhci->td_pool) {
err("unable to create td pci_pool");
goto err_create_td_pool;
}
uhci->qh_pool = pci_pool_create("uhci_qh", dev,
sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC);
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 < (uhci->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 = uhci->rh_dev = usb_alloc_dev(NULL, &hcd->self);
if (!uhci->rh_dev) {
err("unable to allocate root hub");
goto err_alloc_root_hub;
}
uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh_dev);
if (!uhci->skeltd[0]) {
err("unable to allocate TD 0");
goto err_alloc_skeltd;
}
/*
* 9 Interrupt queues; link int2 to int1, int4 to int2, etc
* then link int1 to control and control to bulk
*/
for (i = 1; i < 9; i++) {
struct uhci_td *td;
td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh_dev);
if (!td) {
err("unable to allocate TD %d", i);
goto err_alloc_skeltd;
}
uhci_fill_td(td, 0, uhci_explen(UHCI_NULL_DATA_SIZE) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
td->link = cpu_to_le32(uhci->skeltd[i - 1]->dma_handle);
}
uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh_dev);
if (!uhci->skel_term_td) {
err("unable to allocate skel TD term");
goto err_alloc_skeltd;
}
for (i = 0; i < UHCI_NUM_SKELQH; i++) {
uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh_dev);
if (!uhci->skelqh[i]) {
err("unable to allocate QH %d", i);
goto err_alloc_skelqh;
}
}
uhci_fill_td(uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
uhci->skel_int1_td->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_ls_control_qh->element = UHCI_PTR_TERM;
uhci->skel_hs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_hs_control_qh->element = UHCI_PTR_TERM;
uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_bulk_qh->element = UHCI_PTR_TERM;
/* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
uhci->skel_term_td->link = cpu_to_le32(uhci->skel_term_td->dma_handle);
uhci->skel_term_qh->link = UHCI_PTR_TERM;
uhci->skel_term_qh->element = cpu_to_le32(uhci->skel_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->skeltd[irq]->dma_handle);
}
start_hc(uhci);
init_stall_timer(hcd);
/* disable legacy emulation */
pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
hcd->state = USB_STATE_READY;
usb_connect(uhci->rh_dev);
uhci->rh_dev->speed = USB_SPEED_FULL;
if (usb_register_root_hub(uhci->rh_dev, &dev->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(&uhci->stall_timer);
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
err_alloc_skelqh:
for (i = 0; i < UHCI_NUM_SKELTD; i++)
if (uhci->skeltd[i]) {
uhci_free_td(uhci, uhci->skeltd[i]);
uhci->skeltd[i] = NULL;
}
err_alloc_skeltd:
usb_free_dev(uhci->rh_dev);
uhci->rh_dev = 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(dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
err_alloc_fl:
#ifdef CONFIG_PROC_FS
remove_proc_entry(buf, uhci_proc_root);
uhci->proc_entry = NULL;
err_create_proc_entry:
#endif
err_pci_set_dma_mask:
return retval;
}
static void __devexit uhci_stop(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
if (uhci->rh_dev)
usb_disconnect(&uhci->rh_dev);
del_timer(&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);
suspend_hc(uhci);
return 0;
}
static int uhci_resume(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
pci_set_master(uhci->dev);
reset_hc(uhci);
start_hc(uhci);
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 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,
hcd_alloc: uhci_hcd_alloc,
hcd_free: uhci_hcd_free,
urb_enqueue: uhci_urb_enqueue,
urb_dequeue: uhci_urb_dequeue,
free_config: NULL,
get_frame_number: uhci_hcd_get_frame_number,
hub_status_data: uhci_hub_status_data,
hub_control: uhci_hub_control,
};
static const struct pci_device_id __devinitdata uhci_pci_ids[] = { {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -