⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uhci.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 4 页
字号:
			OK(0);		case RH_C_PORT_OVER_CURRENT:			OK(0);	/* port power over current */		case RH_C_PORT_RESET:			uhci->rh.c_p_r[wIndex - 1] = 0;			OK(0);		}		break;	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:		switch (wValue) {		case RH_PORT_SUSPEND:			SET_RH_PORTSTAT(USBPORTSC_SUSP);			OK(0);		case RH_PORT_RESET:			SET_RH_PORTSTAT(USBPORTSC_PR);			wait_ms(50);	/* USB v1.1 7.1.7.3 */			uhci->rh.c_p_r[wIndex - 1] = 1;			CLR_RH_PORTSTAT(USBPORTSC_PR);			udelay(10);			SET_RH_PORTSTAT(USBPORTSC_PE);			wait_ms(10);			SET_RH_PORTSTAT(0xa);			OK(0);		case RH_PORT_POWER:			OK(0); /* port power ** */		case RH_PORT_ENABLE:			SET_RH_PORTSTAT(USBPORTSC_PE);			OK(0);		}		break;	case RH_SET_ADDRESS:		uhci->rh.devnum = wValue;		OK(0);	case RH_GET_DESCRIPTOR:		switch ((wValue & 0xff00) >> 8) {		case 0x01:	/* device descriptor */			len = min(leni, min(sizeof(root_hub_dev_des), wLength));			memcpy(data, root_hub_dev_des, len);			OK(len);		case 0x02:	/* configuration descriptor */			len = min(leni, min(sizeof(root_hub_config_des), wLength));			memcpy (data, root_hub_config_des, len);			OK(len);		case 0x03:	/* string descriptors */			len = usb_root_hub_string (wValue & 0xff,				uhci->io_addr, "UHCI-alt",				data, wLength);			if (len > 0) {				OK (min (leni, len));			} else 				stat = -EPIPE;		}		break;	case RH_GET_DESCRIPTOR | RH_CLASS:		root_hub_hub_des[2] = uhci->rh.numports;		len = min(leni, min(sizeof(root_hub_hub_des), wLength));		memcpy(data, root_hub_hub_des, len);		OK(len);	case RH_GET_CONFIGURATION:		*(__u8 *)data = 0x01;		OK(1);	case RH_SET_CONFIGURATION:		OK(0);	case RH_GET_INTERFACE | RH_INTERFACE:		*(__u8 *)data = 0x00;		OK(1);	case RH_SET_INTERFACE | RH_INTERFACE:		OK(0);	default:		stat = -EPIPE;	}	urb->actual_length = len;	urb->status = stat;	if (urb->complete)		urb->complete(urb);	return USB_ST_NOERROR;}/*-------------------------------------------------------------------------*/static int rh_unlink_urb(struct urb *urb){	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;	if (uhci->rh.urb == urb) {		uhci->rh.send = 0;		del_timer(&uhci->rh.rh_int_timer);	}	return 0;}/*-------------------------------------------------------------------*/void uhci_free_pending_qhs(struct uhci *uhci){	struct list_head *tmp, *head;	unsigned long flags;	/* Free any pending QH's */	spin_lock_irqsave(&uhci->qh_remove_lock, flags);	head = &uhci->qh_remove_list;	tmp = head->next;	while (tmp != head) {		struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);		tmp = tmp->next;		list_del(&qh->remove_list);		uhci_free_qh(qh);	}	spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);}static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs){	struct uhci *uhci = __uhci;	unsigned int io_addr = uhci->io_addr;	unsigned short status;	unsigned long flags;	struct list_head *tmp, *head;	/*	 * 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;	outw(status, io_addr + USBSTS);	if (status & ~(USBSTS_USBINT | USBSTS_ERROR)) {		if (status & USBSTS_RD)			printk(KERN_INFO "uhci: resume detected, not implemented\n");		if (status & USBSTS_HSE)			printk(KERN_ERR "uhci: host system error, PCI problems?\n");		if (status & USBSTS_HCPE)			printk(KERN_ERR "uhci: host controller process error. something bad happened\n");		if (status & USBSTS_HCH) {			printk(KERN_ERR "uhci: host controller halted. very bad\n");			/* FIXME: Reset the controller, fix the offending TD */		}	}	uhci_free_pending_qhs(uhci);	spin_lock(&uhci->urb_remove_lock);	head = &uhci->urb_remove_list;	tmp = head->next;	while (tmp != head) {		struct urb *urb = list_entry(tmp, struct urb, urb_list);		tmp = tmp->next;		list_del(&urb->urb_list);		if (urb->complete)			urb->complete(urb);	}	spin_unlock(&uhci->urb_remove_lock);	uhci_clear_next_interrupt(uhci);	/* Walk the list of pending TD's to see which ones completed */	nested_lock(&uhci->urblist_lock, flags);	head = &uhci->urb_list;	tmp = head->next;	while (tmp != head) {		struct urb *urb = list_entry(tmp, struct urb, urb_list);		tmp = tmp->next;		/* Checks the status and does all of the magic necessary */		uhci_transfer_result(urb);	}	nested_unlock(&uhci->urblist_lock, flags);}static void reset_hc(struct uhci *uhci){	unsigned int io_addr = uhci->io_addr;	/* Global reset for 50ms */	outw(USBCMD_GRESET, io_addr + USBCMD);	wait_ms(50);	outw(0, io_addr + USBCMD);	wait_ms(10);}static void start_hc(struct uhci *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(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD);	/* Run and mark it configured with a 64-byte max packet */	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);}/* * 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);	/* We need exactly one page (per UHCI specs), how convenient */	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */	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;	/* 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(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;	/*	 * 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 = &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;	/* 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 << 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);	/*	 * 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 < 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++;							}						}					}				}			}		}		/* Only place we don't use the frame list routines */		uhci->fl->frame[i] =  virt_to_bus(irq);	}	return uhci;/* * error exits: */au_free_fl:	free_page((unsigned long)uhci->fl);au_free_uhci:	kfree(uhci);	return NULL;}/* * De-allocate all resources.. */static void release_uhci(struct uhci *uhci){	if (uhci->irq >= 0) {		free_irq(uhci->irq, uhci);		uhci->irq = -1;	}	if (uhci->fl) {		free_page((unsigned long)uhci->fl);		uhci->fl = NULL;	}	usb_free_bus(uhci->bus);	kfree(uhci);}int uhci_start_root_hub(struct uhci *uhci){	struct usb_device *dev;	dev = usb_alloc_dev(NULL, uhci->bus);	if (!dev)		return -1;	uhci->bus->root_hub = dev;	usb_connect(dev);	if (usb_new_device(dev) != 0) {		usb_free_dev(dev);		return -1;	}	return 0;}/* * If we've successfully found a UHCI, now is the time to increment the * module usage count, and return success.. */static int setup_uhci(struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size){	int retval;	struct uhci *uhci;	char buf[8], *bufp = buf;#ifndef __sparc__	sprintf(buf, "%d", irq);#else	bufp = __irq_itoa(irq);#endif	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",		io_addr, bufp);	uhci = alloc_uhci(io_addr, io_size);	if (!uhci)		return -ENOMEM;	dev->driver_data = uhci;	request_region(uhci->io_addr, io_size, "usb-uhci");	reset_hc(uhci);	usb_register_bus(uhci->bus);	start_hc(uhci);	retval = -EBUSY;	if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci) == 0) {		uhci->irq = irq;		pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);		if (!uhci_start_root_hub(uhci))			return 0;	}	/* Couldn't allocate IRQ if we got here */	reset_hc(uhci);	release_region(uhci->io_addr, uhci->io_size);	release_uhci(uhci);	return retval;}static int __devinit uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id){	int i;	/* disable legacy emulation */	pci_write_config_word(dev, USBLEGSUP, 0);	if (pci_enable_device(dev) < 0)		return -ENODEV;	if (!dev->irq) {		err("found UHCI device with no IRQ assigned. check BIOS settings!");		return -ENODEV;	}	/* Search for the IO base address.. */	for (i = 0; i < 6; i++) {		unsigned int io_addr = pci_resource_start(dev, i);		unsigned int io_size = pci_resource_len(dev, i);		/* IO address? */		if (!(pci_resource_flags(dev, i) & IORESOURCE_IO))			continue;		/* Is it already in use? */		if (check_region(io_addr, io_size))			break;		pci_set_master(dev);		return setup_uhci(dev, dev->irq, io_addr, io_size);	}	return -ENODEV;}static void __devexit uhci_pci_remove(struct pci_dev *dev){	struct uhci *uhci = dev->driver_data;	if (uhci->bus->root_hub)		usb_disconnect(&uhci->bus->root_hub);	usb_deregister_bus(uhci->bus);	reset_hc(uhci);	release_region(uhci->io_addr, uhci->io_size);	uhci_free_pending_qhs(uhci);	release_uhci(uhci);}static void uhci_pci_suspend(struct pci_dev *dev){	reset_hc((struct uhci *) dev->driver_data);}static void uhci_pci_resume(struct pci_dev *dev){	reset_hc((struct uhci *) dev->driver_data);	start_hc((struct uhci *) dev->driver_data);}/*-------------------------------------------------------------------------*/static const struct pci_device_id __devinitdata uhci_pci_ids [] = { {	/* handle any USB UHCI controller */	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x00),	class_mask: 	~0,	/* 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, uhci_pci_ids);static struct pci_driver uhci_pci_driver = {	name:		"usb-uhci",	id_table:	&uhci_pci_ids [0],	probe:		uhci_pci_probe,	remove:		uhci_pci_remove,#ifdef	CONFIG_PM	suspend:	uhci_pci_suspend,	resume:		uhci_pci_resume,#endif	/* PM */}; static int __init uhci_hcd_init(void){	int retval;	retval = -ENOMEM;	/* We throw all of the TD's and QH's into a kmem cache */	/* TD's and QH's need to be 16 byte aligned and SLAB_HWCACHE_ALIGN */	/*  does this for us */	uhci_td_cachep = kmem_cache_create("uhci_td",		sizeof(struct uhci_td), 0,		SLAB_HWCACHE_ALIGN, NULL, NULL);	if (!uhci_td_cachep)		goto td_failed;	uhci_qh_cachep = kmem_cache_create("uhci_qh",		sizeof(struct uhci_qh), 0,		SLAB_HWCACHE_ALIGN, NULL, NULL);	if (!uhci_qh_cachep)		goto qh_failed;	uhci_up_cachep = kmem_cache_create("uhci_urb_priv",		sizeof(struct urb_priv), 0, 0, NULL, NULL);	if (!uhci_up_cachep)		goto up_failed;	retval = pci_module_init (&uhci_pci_driver);	if (retval)		goto init_failed;	return 0;init_failed:	if (kmem_cache_destroy(uhci_up_cachep))		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");up_failed:	if (kmem_cache_destroy(uhci_qh_cachep))		printk(KERN_INFO "uhci: not all QH's were freed\n");qh_failed:	if (kmem_cache_destroy(uhci_td_cachep))		printk(KERN_INFO "uhci: not all TD's were freed\n");td_failed:	return retval;}static void __exit uhci_hcd_cleanup (void) {	pci_unregister_driver (&uhci_pci_driver);		if (kmem_cache_destroy(uhci_up_cachep))		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");	if (kmem_cache_destroy(uhci_qh_cachep))		printk(KERN_INFO "uhci: not all QH's were freed\n");	if (kmem_cache_destroy(uhci_td_cachep))		printk(KERN_INFO "uhci: not all TD's were freed\n");}module_init(uhci_hcd_init);module_exit(uhci_hcd_cleanup);MODULE_AUTHOR("Linus Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber");MODULE_DESCRIPTION("USB Universal Host Controller Interface driver");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -