kernels/linux-2.4.0/drivers/usb/uhci.c
/*
 * 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 data".
 */
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 data".
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 data " .
static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size)
{
	int i, port;
	struct uhci *uhci;
	struct usb_bus *bus;
	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
	if (!uhci)
		return NULL;
	memset(uhci, 0, sizeof(*uhci));
	uhci->irq = -1;
	uhci->io_addr = io_addr;
	uhci->io_size = io_size;
	spin_lock_init(&uhci->qh_remove_lock);
	INIT_LIST_HEAD(&uhci->qh_remove_list);
	spin_lock_init(&uhci->urb_remove_lock);
	INIT_LIST_HEAD(&uhci->urb_remove_list);
	nested_init(&uhci->urblist_lock);
	INIT_LIST_HEAD(&uhci->urb_list);
	spin_lock_init(&uhci->framelist_lock);
	uhci->fl = (void *)__get_free_page(GFP_KERNEL);
	if (!uhci->fl)
		goto au_free_uhci;
	bus = usb_alloc_bus(&uhci_device_operations);
	if (!bus)
		goto au_free_fl;
	uhci->bus = bus;
	bus->hcpriv = uhci;
	for (port = 0; port < (io_size - 0x10) / 2; port++) {
		unsigned int portstatus;
		portstatus = inw(io_addr + 0x10 + (port * 2));
		if (!(portstatus & 0x0080))
			break;
	}
	if (debug)
		info("detected %d ports", port);
	if (port < 2 || port > 8) {
		info("port count misdetected? forcing to 2 ports");
		port = 2;
	}
	uhci->rh.numports = port;
	for (i = 1; i < 9; i++) {
		struct uhci_td *td = &uhci->skeltd[i];
		uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
		td->link = virt_to_bus(&uhci->skeltd[i - 1]);
	}
	uhci_fill_td(&uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
	uhci->skel_int1_td.link = virt_to_bus(&uhci->skel_ls_control_qh) | UHCI_PTR_QH;
	uhci->skel_ls_control_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
	uhci->skel_ls_control_qh.element = UHCI_PTR_TERM;
	uhci->skel_hs_control_qh.link = virt_to_bus(&uhci->skel_bulk_qh) | UHCI_PTR_QH;
	uhci->skel_hs_control_qh.element = UHCI_PTR_TERM;
	uhci->skel_bulk_qh.link = virt_to_bus(&uhci->skel_term_qh) | UHCI_PTR_QH;
	uhci->skel_bulk_qh.element = UHCI_PTR_TERM;
	uhci_fill_td(&uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
	uhci->skel_term_td.link = UHCI_PTR_TERM;
	uhci->skel_term_qh.link = UHCI_PTR_TERM;
	uhci->skel_term_qh.element = virt_to_bus(&uhci->skel_term_td);
	for (i = 0; i < 1024; i++) {
		struct uhci_td *irq = &uhci->skel_int1_td;
		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++;
							}
						}
					}
				}
			}
		}
		uhci->fl->frame[i] =  virt_to_bus(irq);
	}
	return uhci;
au_free_fl:
	free_page((unsigned long)uhci->fl);
au_free_uhci:
	kfree(uhci);
	return NULL;
}
static struct uhci * alloc_uhci ( unsigned int io_addr , unsigned int io_size ) { int i , port ; struct uhci * uhci ; struct usb_bus * bus ; uhci = kmalloc ( sizeof ( * uhci ) , GFP_KERNEL ) ; if ( ! uhci ) return NULL ; memset ( uhci , 0 , sizeof ( * uhci ) ) ; uhci -> irq = -1 ; uhci -> io_addr = io_addr ; uhci -> io_size = io_size ; spin_lock_init ( & uhci -> qh_remove_lock ) ; INIT_LIST_HEAD ( & uhci -> qh_remove_list ) ; spin_lock_init ( & uhci -> urb_remove_lock ) ; INIT_LIST_HEAD ( & uhci -> urb_remove_list ) ; nested_init ( & uhci -> urblist_lock ) ; INIT_LIST_HEAD ( & uhci -> urb_list ) ; spin_lock_init ( & uhci -> framelist_lock ) ; uhci -> fl = ( void * ) __get_free_page ( GFP_KERNEL ) ; if ( ! uhci -> fl ) goto au_free_uhci ; bus = usb_alloc_bus ( & uhci_device_operations ) ; if ( ! bus ) goto au_free_fl ; uhci -> bus = bus ; bus -> hcpriv = uhci ; for ( port = 0 ; port < ( io_size - 0x10 ) / 2 ; port ++ ) { unsigned int portstatus ; portstatus = inw ( io_addr + 0x10 + ( port * 2 ) ) ; if ( ! ( portstatus & 0x0080 ) ) break ; } if ( debug ) info ( " " , port ) ; if ( port < 2 || port > 8 ) { info ( " " ) ; port = 2 ; } uhci -> rh . numports = port ; for ( i = 1 ; i < 9 ; i ++ ) { struct uhci_td * td = & uhci -> skeltd [ i ] ; uhci_fill_td ( td , 0 , ( UHCI_NULL_DATA_SIZE << 21 ) | ( 0x7f << 8 ) | USB_PID_IN , 0 ) ; td -> link = virt_to_bus ( & uhci -> skeltd [ i - 1 ] ) ; } uhci_fill_td ( & uhci -> skel_int1_td , 0 , ( UHCI_NULL_DATA_SIZE << 21 ) | ( 0x7f << 8 ) | USB_PID_IN , 0 ) ; uhci -> skel_int1_td . link = virt_to_bus ( & uhci -> skel_ls_control_qh ) | UHCI_PTR_QH ; uhci -> skel_ls_control_qh . link = virt_to_bus ( & uhci -> skel_hs_control_qh ) | UHCI_PTR_QH ; uhci -> skel_ls_control_qh . element = UHCI_PTR_TERM ; uhci -> skel_hs_control_qh . link = virt_to_bus ( & uhci -> skel_bulk_qh ) | UHCI_PTR_QH ; uhci -> skel_hs_control_qh . element = UHCI_PTR_TERM ; uhci -> skel_bulk_qh . link = virt_to_bus ( & uhci -> skel_term_qh ) | UHCI_PTR_QH ; uhci -> skel_bulk_qh . element = UHCI_PTR_TERM ; uhci_fill_td ( & uhci -> skel_term_td , 0 , ( UHCI_NULL_DATA_SIZE << 21 ) | ( 0x7f << 8 ) | USB_PID_IN , 0 ) ; uhci -> skel_term_td . link = UHCI_PTR_TERM ; uhci -> skel_term_qh . link = UHCI_PTR_TERM ; uhci -> skel_term_qh . element = virt_to_bus ( & uhci -> skel_term_td ) ; for ( i = 0 ; i < 1024 ; i ++ ) { struct uhci_td * irq = & uhci -> skel_int1_td ; 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 ++ ; } } } } } } uhci -> fl -> frame [ i ] = virt_to_bus ( irq ) ; } return uhci ; au_free_fl : free_page ( ( unsigned long ) uhci -> fl ) ; au_free_uhci : kfree ( uhci ) ; return NULL ; }
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.
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 .
/* We need exactly one page (per UHCI specs), how convenient */ /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */ /* 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 */ /* This is experimental so anything less than 2 or greater than 8 is */ /*  something weird and we'll ignore it */ /*
	 * 9 Interrupt queues; link int2 to int1, int4 to int2, etc
	 * then link int1 to control and control to bulk
	 */ /* This dummy TD is to work around a bug in Intel PIIX controllers */ /*
	 * 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.
	 */ /* Only place we don't use the frame list routines */ /*
 * error exits:
 */