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

📄 ohci.c

📁 超小usb协议栈
💻 C
📖 第 1 页 / 共 2 页
字号:
		printk("Bad port # passed to ohci_reset_port\n");		port = MAX_ROOT_PORTS-1;	}	writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]);  /* Reset */	/*	 * Get the time required for a root hub port to reset and wait	 * it out (adding 1ms for good measure).	 */	ms = (readl(&ohci->regs->roothub.a) >> 24) * 2 + 1;	wait_ms(ms);	/* check port status to see that the reset completed */	status = readl(&ohci->regs->roothub.portstatus[port]);	if (status & PORT_PRS) {		/* reset failed, try harder? */		printk("usb-ohci: port %d reset failed, retrying\n", port);		writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]);		wait_ms(50);	}	/* TODO we might need to re-enable the port here or is that	 * done elsewhere? */} /* ohci_reset_port *//* * This gets called if the connect status on the root hub changes. */static void ohci_connect_change(struct ohci * ohci, int port){	struct usb_device *usb_dev;	struct ohci_device *dev;	/* memory I/O address of the port status register */	void *portaddr = &ohci->regs->roothub.portstatus[port];	int portstatus;		/*	 * Because of the status change we have to forget	 * everything we think we know about the device	 * on this root hub port.  It may have changed.	 */	usb_disconnect(ohci->root_hub->usb->children + port);	portstatus = readl(portaddr);	/* disable the port if nothing is connected */	if (!(portstatus & PORT_CCS)) {		writel(PORT_CCS, portaddr);		return;	}	/*	 * Allocate a device for the new thingy that's been attached	 */	usb_dev = ohci_usb_allocate(ohci->root_hub->usb);	dev = usb_dev->hcpriv;	dev->ohci = ohci;	usb_connect(dev->usb);	/* link it into the bus's device tree */	ohci->root_hub->usb->children[port] = usb_dev;	wait_ms(200); /* wait for powerup; XXX is this needed? */	ohci_reset_port(ohci, port);	/* Get information on speed by using LSD */	usb_dev->slow = readl(portaddr) & PORT_LSDA ? 1 : 0;	/*	 * Do generic USB device tree processing on the new device.	 */	usb_new_device(usb_dev);} /* ohci_connect_change() *//* * This gets called when the root hub configuration * has changed.  Just go through each port, seeing if * there is something interesting happening. */static void ohci_check_configuration(struct ohci *ohci){	int num = 0;	int maxport = readl(&ohci->regs->roothub) & 0xff;	do {		if (readl(ohci->regs->roothub.portstatus[num]) & PORT_CSC)			ohci_connect_change(ohci, num);	} while (++num < maxport);} /* ohci_check_configuration() *//* * Get annoyed at the controller for bothering us. */static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r){	struct ohci *ohci = __ohci;	struct ohci_regs *regs = ohci->regs;	struct ohci_hcca *hcca = ohci->root_hub->hcca;	__u32 donehead = hcca->donehead;	/*	 * Check the interrupt status register if needed	 */	if (!donehead || (donehead & 1)) {		__u32 intrstatus = readl(&regs->intrstatus);		/*		 * XXX eek! printk's in an interrupt handler.  shoot me!		 */		if (intrstatus & OHCI_INTR_SO) {			printk(KERN_DEBUG "usb-ohci: scheduling overrun\n");		}		if (intrstatus & OHCI_INTR_RD) {			printk(KERN_DEBUG "usb-ohci: resume detected\n");		}		if (intrstatus & OHCI_INTR_UE) {			printk(KERN_DEBUG "usb-ohci: unrecoverable error\n");		}		if (intrstatus & OHCI_INTR_OC) {			printk(KERN_DEBUG "usb-ohci: ownership change?\n");		}		if (intrstatus & OHCI_INTR_RHSC) {			/* TODO Process events on the root hub */		}	}	/*	 * Process the done list	 */	if (donehead &= ~0x1) {		/*		 * TODO See which TD's completed..		 */	}	/* Re-enable done queue interrupts and reset the donehead */	hcca->donehead = 0;	writel(OHCI_INTR_WDH, &regs->intrenable);	} /* ohci_interrupt() *//* * Allocate the resources required for running an OHCI controller. * Host controller interrupts must not be running while calling this * function or the penguins will get angry. * * The mem_base parameter must be the usable -virtual- address of the * host controller's memory mapped I/O registers. */static struct ohci *alloc_ohci(void* mem_base){	int i;	struct ohci *ohci;	struct usb_bus *bus;	struct ohci_device *dev;	struct usb_device *usb;	ohci = kmalloc(sizeof(*ohci), GFP_KERNEL);	if (!ohci)		return NULL;	memset(ohci, 0, sizeof(*ohci));	ohci->irq = -1;	ohci->regs = mem_base;	INIT_LIST_HEAD(&ohci->interrupt_list);	bus = kmalloc(sizeof(*bus), GFP_KERNEL);	if (!bus)		return NULL;	memset(bus, 0, sizeof(*bus));	ohci->bus = bus;	bus->hcpriv = ohci;	bus->op = &ohci_device_operations;	/*	 * Here we allocate our own root hub and TDs as well as the	 * OHCI host controller communications area.  The HCCA is just	 * a nice pool of memory with pointers to endpoint descriptors	 * for the different interrupts.	 */	usb = ohci_usb_allocate(NULL);	if (!usb)		return NULL;	dev = ohci->root_hub = usb_to_ohci(usb);	usb->bus = bus;	/* Initialize the root hub */	memset(dev, 0, sizeof(*dev));	dev->ohci = ohci;    /* link back to the controller */	/*	 * Allocate the Host Controller Communications Area	 */	dev->hcca = (struct ohci_hcca *) kmalloc(sizeof(*dev->hcca), GFP_KERNEL);	/* Tell the controller where the HCCA is */	writel(virt_to_bus(dev->hcca), &ohci->regs->hcca);	/* Get the number of ports on the root hub */	usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff;	if (usb->maxchild > MAX_ROOT_PORTS) {		printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS);		usb->maxchild = MAX_ROOT_PORTS;	}	if (usb->maxchild < 1) {		printk("usb-ohci: Less than one root hub port? Impossible!\n");		usb->maxchild = 1;	}	printk("usb-ohci: %d root hub ports found\n", usb->maxchild);	printk("alloc_ohci() controller\n");	show_ohci_status(ohci);	printk("alloc_ohci() root_hub device\n");	show_ohci_device(dev);	/*	 * Initialize the ED polling "tree" (for simplicity's sake in	 * this driver many nodes in the tree will be identical)	 */	dev->ed[ED_INT_32].next_ed = virt_to_bus(&dev->ed[ED_INT_16]);	dev->ed[ED_INT_16].next_ed = virt_to_bus(&dev->ed[ED_INT_8]);	dev->ed[ED_INT_8].next_ed = virt_to_bus(&dev->ed[ED_INT_4]);	dev->ed[ED_INT_4].next_ed = virt_to_bus(&dev->ed[ED_INT_2]);	dev->ed[ED_INT_2].next_ed = virt_to_bus(&dev->ed[ED_INT_1]);	/*	 * Initialize the polling table to call interrupts at the	 * intended intervals.	 */	for (i = 0; i < NUM_INTS; i++) {		if (i == 0)			dev->hcca->int_table[i] =				virt_to_bus(&dev->ed[ED_INT_32]);		else if (i & 1)			dev->hcca->int_table[i] =				virt_to_bus(&dev->ed[ED_INT_16]);		else if (i & 2)			dev->hcca->int_table[i] =				virt_to_bus(&dev->ed[ED_INT_8]);		else if (i & 4)			dev->hcca->int_table[i] =				virt_to_bus(&dev->ed[ED_INT_4]);		else if (i & 8)			dev->hcca->int_table[i] =				virt_to_bus(&dev->ed[ED_INT_2]);		else if (i & 16)			dev->hcca->int_table[i] =				virt_to_bus(&dev->ed[ED_INT_1]);	}	/*	 * Tell the controller where the control and bulk lists are	 */	writel(virt_to_bus(&dev->ed[ED_CONTROL]), &ohci->regs->ed_controlhead);	writel(virt_to_bus(&dev->ed[ED_BULK]), &ohci->regs->ed_bulkhead);	return ohci;} /* alloc_ohci() *//* * De-allocate all resoueces.. */static void release_ohci(struct ohci *ohci){	if (ohci->irq >= 0) {		free_irq(ohci->irq, ohci);		ohci->irq = -1;	}	if (ohci->root_hub) {		/* ensure that HC is stopped before releasing the HCCA */		writel(OHCI_USB_SUSPEND, &ohci->regs->control);		free_pages((unsigned int) ohci->root_hub->hcca, 1);		free_pages((unsigned int) ohci->root_hub, 1);		ohci->root_hub->hcca = NULL;		ohci->root_hub = NULL;	}	/* unmap the IO address space */	iounmap(ohci->regs);	/* If the ohci itself were dynamic we'd free it here */} /* release_ohci() *//* * USB OHCI control thread */static int ohci_control_thread(void * __ohci){	struct ohci *ohci = (struct ohci *)__ohci;		/*	 * I'm unfamiliar with the SMP kernel locking.. where should	 * this be released?  -greg	 */	lock_kernel();	/*	 * This thread doesn't need any user-level access,	 * so get rid of all of our resources..	 */	printk("ohci_control_thread at %p\n", &ohci_control_thread);	exit_mm(current);	exit_files(current);	exit_fs(current);	strcpy(current->comm, "ohci-control");	/*	 * Damn the torpedoes, full speed ahead	 */	start_hc(ohci);	do {		interruptible_sleep_on(&ohci_configure);#ifdef CONFIG_APM		if (apm_resume) {			apm_resume = 0;			start_hc(ohci);			continue;		}#endif		ohci_check_configuration(ohci);	} while (!signal_pending(current));	reset_hc(ohci);	release_ohci(ohci);	MOD_DEC_USE_COUNT;	return 0;} /* ohci_control_thread() */#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 "usb-ohci: received extra suspend event\n");			break;		}		down = 1;		break;	case APM_NORMAL_RESUME:	case APM_CRITICAL_RESUME:		if (!down) {			printk(KERN_DEBUG "usb-ohci: received bogus resume event\n");			break;		}		down = 0;		if (waitqueue_active(&ohci_configure)) {			apm_resume = 1;			wake_up(&ohci_configure);		}		break;	}	return 0;}#endif/* ... *//* * Increment the module usage count, start the control thread and * return success if the controller is good. */static int found_ohci(int irq, void* mem_base){	int retval;	struct ohci *ohci;	/* Allocate the running OHCI structures */	ohci = alloc_ohci(mem_base);	if (!ohci)		return -ENOMEM;	printk("usb-ohci: alloc_ohci() = %p\n", ohci);	reset_hc(ohci);	retval = -EBUSY;	if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) {		int pid;		MOD_INC_USE_COUNT;		ohci->irq = irq;		/* fork off the handler */		pid = kernel_thread(ohci_control_thread, ohci,				CLONE_FS | CLONE_FILES | CLONE_SIGHAND);		if (pid >= 0)			return 0;		MOD_DEC_USE_COUNT;		retval = pid;	}	release_ohci(ohci);	return retval;} /* found_ohci() *//* * If this controller is for real, map the IO memory and proceed */static int init_ohci(struct pci_dev *dev){	unsigned int mem_base = dev->base_address[0];		printk("usb-ohci: mem_base is %p\n", (void*)mem_base);	/* If its OHCI, its memory */	if (mem_base & PCI_BASE_ADDRESS_SPACE_IO)		return -ENODEV;	/* Get the memory address and map it for IO */	mem_base &= PCI_BASE_ADDRESS_MEM_MASK;	/* 	 * FIXME ioremap_nocache isn't implemented on all CPUs (such	 * as the Alpha) [?]  What should I use instead...	 *	 * The iounmap() is done on in release_ohci.	 */	mem_base = (unsigned int) ioremap_nocache(mem_base, 4096);	if (!mem_base) {		printk("Error mapping OHCI memory\n");		return -EFAULT;	}	return found_ohci(dev->irq, (void *) mem_base);} /* init_ohci() *//* TODO this should be named following Linux convention and go in pci.h */#define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010)/* * Search the PCI bus for an OHCI USB controller and set it up * * If anyone wants multiple controllers this will need to be * updated..  Right now, it just picks the first one it finds. */int init_module(void){	int retval;	struct pci_dev *dev = NULL;	/*u8 type;*/	if (sizeof(struct ohci_device) > 4096) {		printk("usb-ohci: struct ohci_device to large\n");		return -ENODEV;	}	retval = -ENODEV;	for (;;) {		/* Find an OHCI USB controller */		dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev);		if (!dev)			break;		/* Verify that its OpenHCI by checking for MMIO */		/* pci_read_config_byte(dev, PCI_CLASS_PROG, &type);		if (!type)			continue; */		/* Ok, set it up */		retval = init_ohci(dev);		if (retval < 0)			continue;		/* TODO check module params here to determine what to load *//*		usb_mouse_init(); *//*		usb_kbd_init();		hub_init();	*/#ifdef CONFIG_APM		apm_register_callback(&handle_apm_event);#endif		return 0; /* no error */	}	return retval;} /* init_module() *//* *  Clean up when unloading the module */void cleanup_module(void){#ifdef CONFIG_APM	apm_unregister_callback(&handle_apm_event);#endif}/* vim:sw=8 */

⌨️ 快捷键说明

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