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

📄 uhci.c

📁 超小usb协议栈
💻 C
📖 第 1 页 / 共 2 页
字号:
	unsigned short status;	status = inw(port);	outw(status | USBPORTSC_PR, port);		/* reset port */	wait_ms(10);	outw(status & ~USBPORTSC_PR, port);	udelay(5);	status = inw(port);	outw(status | USBPORTSC_PE, port);		/* enable port */	wait_ms(10);	status = inw(port);	if(!(status & USBPORTSC_PE)) {		outw(status | USBPORTSC_PE, port);	/* one more try at enabling port */		wait_ms(50);	}}/* * This gets called if the connect status on the root * hub (and the root hub only) changes. */static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned int nr){	struct usb_device *usb_dev;	struct uhci_device *dev;	unsigned short status;	printk("uhci_connect_change: called for %d\n", nr);	/*	 * Even if the status says we're connected,	 * the fact that the status bits changed may	 * that we got disconnected and then reconnected.	 *	 * So start off by getting rid of any old devices..	 */	usb_disconnect(&uhci->root_hub->usb->children[nr]);	status = inw(port);	/* If we have nothing connected, then clear change status and disable the port */	status = (status & ~USBPORTSC_PE) | USBPORTSC_PEC;	if (!(status & USBPORTSC_CCS)) {		outw(status, port);		return;	}	/*	 * Ok, we got a new connection. Allocate a device to it,	 * and find out what it wants to do..	 */	usb_dev = uhci_usb_allocate(uhci->root_hub->usb);	dev = usb_dev->hcpriv;	dev->uhci = uhci;	usb_connect(usb_dev);	uhci->root_hub->usb->children[nr] = usb_dev;	wait_ms(200); /* wait for powerup */	uhci_reset_port(port);	/* Get speed information */	usb_dev->slow = (inw(port) & USBPORTSC_LSDA) ? 1 : 0;	/*	 * Ok, all the stuff specific to the root hub has been done.	 * The rest is generic for any new USB attach, regardless of	 * hub type.	 */	usb_new_device(usb_dev);}/* * This gets called when the root hub configuration * has changed. Just go through each port, seeing if * there is something interesting happening. */static void uhci_check_configuration(struct uhci *uhci){	unsigned int io_addr = uhci->io_addr + USBPORTSC1;	int maxchild = uhci->root_hub->usb->maxchild;	int nr = 0;	do {		unsigned short status = inw(io_addr);		if (status & USBPORTSC_CSC)			uhci_connect_change(uhci, io_addr, nr);		nr++; io_addr += 2;	} while (nr < maxchild);}static void uhci_interrupt_notify(struct uhci *uhci){	struct list_head *head = &uhci->interrupt_list;	struct list_head *tmp;	spin_lock(&irqlist_lock);	tmp = head->next;	while (tmp != head) {		struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list);		struct list_head *next;		next = tmp->next;		if (!(td->status & (1 << 23))) {	/* No longer active? */			/* remove from IRQ list */			__list_del(tmp->prev, next);			INIT_LIST_HEAD(tmp);			if (td->completed(td->status, bus_to_virt(td->buffer), td->dev_id)) {				struct uhci_qh *interrupt_qh = td->qh;				list_add(&td->irq_list, &uhci->interrupt_list);				td->info ^= 1 << 19; /* toggle between data0 and data1 */				td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24);	/* active */				/* Remove then readd? Is that necessary */				uhci_remove_td(td);				uhci_insert_td_in_qh(interrupt_qh, td);			}			/* If completed wants to not reactivate, then it's */			/* responsible for free'ing the TD's and QH's */			/* or another function (such as run_control) */		}		tmp = next;	}	spin_unlock(&irqlist_lock);}/* * Check port status - Connect Status Change - for * each of the attached ports (defaults to two ports, * but at least in theory there can be more of them). * * Wake up the configurator if something happened, we * can't really do much at interrupt time. */static void uhci_root_hub_events(struct uhci *uhci, unsigned int io_addr){	if (waitqueue_active(&uhci_configure)) {		int ports = uhci->root_hub->usb->maxchild;		io_addr += USBPORTSC1;		do {			if (inw(io_addr) & USBPORTSC_CSC) {				wake_up(&uhci_configure);				return;			}			io_addr += 2;		} while (--ports > 0);	}}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;	/*	 * Read the interrupt status, and write it back to clear the interrupt cause	 */	status = inw(io_addr + USBSTS);	outw(status, io_addr + USBSTS);	/* Walk the list of pending TD's to see which ones completed.. */	uhci_interrupt_notify(uhci);	/* Check if there are any events on the root hub.. */	uhci_root_hub_events(uhci, io_addr);}/* * We init one packet, and mark it just IOC and _not_ * active. Which will result in no actual USB traffic, * but _will_ result in an interrupt every second. * * Which is exactly what we want. */static void uhci_init_ticktd(struct uhci *uhci){	struct uhci_device *dev = uhci->root_hub;	struct uhci_td *td = uhci_td_allocate(dev);	td->link = 1;	td->status = (1 << 24);					/* interrupt on completion */	td->info = (15 << 21) | 0x7f69;				/* (ignored) input packet, 16 bytes, device 127 */	td->buffer = 0;	td->qh = NULL;	uhci->fl->frame[0] = virt_to_bus(td);}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;	uhci_init_ticktd(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) {			printk("USBCMD_HCRESET timed out!\n");			break;		}	}	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);	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, io_addr + USBCMD);}/* * Allocate a frame list, and four regular queues. * * 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". *  - The third queue is "bulk data". * * We could certainly have multiple queues of the same * type, and maybe we should. We could have per-device * queues, for example. We begin small. */static struct uhci *alloc_uhci(unsigned int io_addr){	int i;	struct uhci *uhci;	struct usb_bus *bus;	struct uhci_device *dev;	struct usb_device *usb;	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);	if (!uhci)		return NULL;	memset(uhci, 0, sizeof(*uhci));	uhci->irq = -1;	uhci->io_addr = io_addr;	INIT_LIST_HEAD(&uhci->interrupt_list);	/* We need exactly one page (per UHCI specs), how convenient */	uhci->fl = (void *)__get_free_page(GFP_KERNEL);	bus = kmalloc(sizeof(*bus), GFP_KERNEL);	if (!bus)		return NULL;	memset(bus, 0, sizeof(*bus));	uhci->bus = bus;	bus->hcpriv = uhci;	bus->op = &uhci_device_operations;	/*	 * We allocate a 8kB area for the UHCI hub. The area	 * is described by the uhci_device structure, and basically	 * contains everything needed for normal operation.	 *	 * The first page is the actual device descriptor for the	 * hub.	 *	 * The second page is used for the frame list.	 */	usb = uhci_usb_allocate(NULL);	if (!usb)		return NULL;	dev = uhci->root_hub = usb_to_uhci(usb);	usb->bus = bus;	/* 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, so default to 2 */	usb->maxchild = 2;	usb_init_root_hub(usb);	/*	 * Initialize the queues. They all start out empty,	 * linked to each other in the proper order.	 */	for (i = 1 ; i < 9; i++) {		dev->qh[i].link = 2 | virt_to_bus(&dev->skel_control_qh);		dev->qh[i].element = 1;	}		dev->skel_control_qh.link = 2 | virt_to_bus(&dev->skel_bulk0_qh);	dev->skel_control_qh.element = 1;	dev->skel_bulk0_qh.link = 2 | virt_to_bus(&dev->skel_bulk1_qh);	dev->skel_bulk0_qh.element = 1;	dev->skel_bulk1_qh.link = 2 | virt_to_bus(&dev->skel_bulk2_qh);	dev->skel_bulk1_qh.element = 1;	dev->skel_bulk2_qh.link = 2 | virt_to_bus(&dev->skel_bulk3_qh);	dev->skel_bulk2_qh.element = 1;	dev->skel_bulk3_qh.link = 1;	dev->skel_bulk3_qh.element = 1;	/*	 * 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_qh * irq = &dev->skel_int2_qh;		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] =  2 | virt_to_bus(irq);	}	return uhci;}/* * De-allocate all resources.. */static void release_uhci(struct uhci *uhci){	if (uhci->irq >= 0) {		free_irq(uhci->irq, uhci);		uhci->irq = -1;	}#if 0	if (uhci->root_hub) {		uhci_usb_deallocate(uhci_to_usb(uhci->root_hub));		uhci->root_hub = NULL;	}#endif	if (uhci->fl) {		free_page((unsigned long)uhci->fl);		uhci->fl = NULL;	}	kfree(uhci->bus);	kfree(uhci);}static int uhci_control_thread(void * __uhci){	struct uhci *uhci = (struct uhci *)__uhci;	lock_kernel();	request_region(uhci->io_addr, 32, "usb-uhci");	/*	 * This thread doesn't need any user-level access,	 * so get rid of all our resources..	 */	printk("uhci_control_thread at %p\n", &uhci_control_thread);	exit_mm(current);	exit_files(current);	exit_fs(current);	strcpy(current->comm, "uhci-control");	/*	 * Ok, all systems are go..	 */	start_hc(uhci);	for(;;) {		siginfo_t info;		int unsigned long signr;		interruptible_sleep_on(&uhci_configure);#ifdef CONFIG_APM		if (apm_resume) {			apm_resume = 0;			start_hc(uhci);			continue;		}#endif		uhci_check_configuration(uhci);		if(signal_pending(current)) {			/* sending SIGUSR1 makes us print out some info */			spin_lock_irq(&current->sigmask_lock);			signr = dequeue_signal(&current->blocked, &info);			spin_unlock_irq(&current->sigmask_lock);			if(signr == SIGUSR1) {				printk("UHCI queue dump:\n");				show_queues(uhci);			} else {				break;			}		}	}#if 0	if(uhci->root_hub)		for(i = 0; i < uhci->root_hub->usb->maxchild; i++)			usb_disconnect(uhci->root_hub->usb->children + i);#endif	reset_hc(uhci);	release_region(uhci->io_addr, 32);	release_uhci(uhci);	MOD_DEC_USE_COUNT;	printk("uhci_control_thread exiting\n");	return 0;}	/* * If we've successfully found a UHCI, now is the time to increment the * module usage count, start the control thread, and return success.. */static int found_uhci(int irq, unsigned int io_addr){	int retval;	struct uhci *uhci;	uhci = alloc_uhci(io_addr);	if (!uhci)		return -ENOMEM;	reset_hc(uhci);	retval = -EBUSY;	if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb", uhci) == 0) {		int pid;		MOD_INC_USE_COUNT;		uhci->irq = irq;		pid = kernel_thread(uhci_control_thread, uhci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);		if (pid >= 0)			return 0;		MOD_DEC_USE_COUNT;		retval = pid;	}	release_uhci(uhci);	return retval;}static int init_uhci(struct pci_dev *dev){	int i;	/* Search for the IO base address.. */	for (i = 0; i < 6; i++) {		unsigned int io_addr = dev->base_address[i];		/* IO address? */		if (!(io_addr & 1))			continue;		io_addr &= PCI_BASE_ADDRESS_IO_MASK;		/* Is it already in use? */		if (check_region(io_addr, 32))			break;		return found_uhci(dev->irq, io_addr);	}	return -1;}#ifdef CONFIG_APMstatic int handle_apm_event(apm_event_t event){	static int down = 0;	switch (event) {	case APM_SYS_SUSPEND:	case APM_USER_SUSPEND:		if (down) {			printk(KERN_DEBUG "uhci: received extra suspend event\n");			break;		}		down = 1;		break;	case APM_NORMAL_RESUME:	case APM_CRITICAL_RESUME:		if (!down) {			printk(KERN_DEBUG "uhci: received bogus resume event\n");			break;		}		down = 0;		if (waitqueue_active(&uhci_configure)) {			apm_resume = 1;			wake_up(&uhci_configure);		}		break;	}	return 0;}#endifint init_module(void){	int retval;	struct pci_dev *dev = NULL;	u8 type;	retval = -ENODEV;	for (;;) {		dev = pci_find_class(PCI_CLASS_SERIAL_USB<<8, dev);		if (!dev)			break;		/* Is it UHCI */		pci_read_config_byte(dev, PCI_CLASS_PROG, &type);		if(type != 0)			continue;		/* Ok set it up */		retval = init_uhci(dev);		if (retval < 0)			continue;		usb_mouse_init();		usb_kbd_init();		hub_init();#ifdef CONFIG_APM		apm_register_callback(&handle_apm_event);#endif		return 0;	}	return retval;}void cleanup_module(void){#ifdef CONFIG_APM	apm_unregister_callback(&handle_apm_event);#endif}

⌨️ 快捷键说明

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