uhci-hcd.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 2,592 行 · 第 1/5 页

C
2,592
字号
 * 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 full-speed *  - The third queue is bulk queue. *  - The fourth queue is the bandwidth reclamation queue, which loops back *    to the full-speed control queue. */static int uhci_start(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	int retval = -EBUSY;	int i, port;	unsigned io_size;	dma_addr_t dma_handle;	struct usb_device *udev;#ifdef CONFIG_PROC_FS	struct proc_dir_entry *ent;#endif	io_size = pci_resource_len(to_pci_dev(uhci_dev(uhci)), hcd->region);#ifdef CONFIG_PROC_FS	ent = create_proc_entry(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);	if (!ent) {		dev_err(uhci_dev(uhci), "couldn't create uhci proc entry\n");		retval = -ENOMEM;		goto err_create_proc_entry;	}	ent->data = uhci;	ent->proc_fops = &uhci_proc_operations;	ent->size = 0;	uhci->proc_entry = ent;#endif	uhci->fsbr = 0;	uhci->fsbrtimeout = 0;	spin_lock_init(&uhci->schedule_lock);	INIT_LIST_HEAD(&uhci->qh_remove_list);	INIT_LIST_HEAD(&uhci->td_remove_list);	INIT_LIST_HEAD(&uhci->urb_remove_list);	INIT_LIST_HEAD(&uhci->urb_list);	INIT_LIST_HEAD(&uhci->complete_list);	init_waitqueue_head(&uhci->waitqh);	uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),			&dma_handle, 0);	if (!uhci->fl) {		dev_err(uhci_dev(uhci), "unable to allocate "				"consistent memory for frame list\n");		goto err_alloc_fl;	}	memset((void *)uhci->fl, 0, sizeof(*uhci->fl));	uhci->fl->dma_handle = dma_handle;	uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),			sizeof(struct uhci_td), 16, 0);	if (!uhci->td_pool) {		dev_err(uhci_dev(uhci), "unable to create td dma_pool\n");		goto err_create_td_pool;	}	uhci->qh_pool = dma_pool_create("uhci_qh", uhci_dev(uhci),			sizeof(struct uhci_qh), 16, 0);	if (!uhci->qh_pool) {		dev_err(uhci_dev(uhci), "unable to create qh dma_pool\n");		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 < (io_size - 0x10) / 2; port++) {		unsigned int portstatus;		portstatus = inw(uhci->io_addr + 0x10 + (port * 2));		if (!(portstatus & 0x0080))			break;	}	if (debug)		dev_info(uhci_dev(uhci), "detected %d ports\n", 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 > UHCI_RH_MAXCHILD) {		dev_info(uhci_dev(uhci), "port count misdetected? "				"forcing to 2 ports\n");		port = 2;	}	uhci->rh_numports = port;	udev = usb_alloc_dev(NULL, &hcd->self, 0);	if (!udev) {		dev_err(uhci_dev(uhci), "unable to allocate root hub\n");		goto err_alloc_root_hub;	}	uhci->term_td = uhci_alloc_td(uhci, udev);	if (!uhci->term_td) {		dev_err(uhci_dev(uhci), "unable to allocate terminating TD\n");		goto err_alloc_term_td;	}	for (i = 0; i < UHCI_NUM_SKELQH; i++) {		uhci->skelqh[i] = uhci_alloc_qh(uhci, udev);		if (!uhci->skelqh[i]) {			dev_err(uhci_dev(uhci), "unable to allocate QH\n");			goto err_alloc_skelqh;		}	}	/*	 * 8 Interrupt queues; link all higher int queues to int1,	 * then link int1 to control and control to bulk	 */	uhci->skel_int128_qh->link =			uhci->skel_int64_qh->link =			uhci->skel_int32_qh->link =			uhci->skel_int16_qh->link =			uhci->skel_int8_qh->link =			uhci->skel_int4_qh->link =			uhci->skel_int2_qh->link =			cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;	uhci->skel_int1_qh->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_fs_control_qh->dma_handle) | UHCI_PTR_QH;	uhci->skel_fs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;	uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;	/* This dummy TD is to work around a bug in Intel PIIX controllers */	uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |		(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);	uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);	uhci->skel_term_qh->link = UHCI_PTR_TERM;	uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);	/*	 * Fill the frame list: make all entries point to the proper	 * interrupt queue.	 *	 * The interrupt queues will be interleaved as evenly as possible.	 * There's not much to be done about period-1 interrupts; they have	 * to occur in every frame.  But we can schedule period-2 interrupts	 * in odd-numbered frames, period-4 interrupts in frames congruent	 * to 2 (mod 4), and so on.  This way each frame only has two	 * interrupt QHs, which will help spread out bandwidth utilization.	 */	for (i = 0; i < UHCI_NUMFRAMES; i++) {		int irq;		/*		 * ffs (Find First bit Set) does exactly what we need:		 * 1,3,5,...  => ffs = 0 => use skel_int2_qh = skelqh[6],		 * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[5], etc.		 * ffs > 6 => not on any high-period queue, so use		 *	skel_int1_qh = skelqh[7].		 * Add UHCI_NUMFRAMES to insure at least one bit is set.		 */		irq = 6 - (int) __ffs(i + UHCI_NUMFRAMES);		if (irq < 0)			irq = 7;		/* Only place we don't use the frame list routines */		uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[irq]->dma_handle);	}	/*	 * Some architectures require a full mb() to enforce completion of	 * the memory writes above before the I/O transfers in start_hc().	 */	mb();	start_hc(uhci);	init_stall_timer(hcd);	udev->speed = USB_SPEED_FULL;	if (hcd_register_root(udev, &uhci->hcd) != 0) {		dev_err(uhci_dev(uhci), "unable to start root hub\n");		retval = -ENOMEM;		goto err_start_root_hub;	}	return 0;/* * error exits: */err_start_root_hub:	reset_hc(uhci);	del_timer_sync(&uhci->stall_timer);err_alloc_skelqh:	for (i = 0; i < UHCI_NUM_SKELQH; i++)		if (uhci->skelqh[i]) {			uhci_free_qh(uhci, uhci->skelqh[i]);			uhci->skelqh[i] = NULL;		}	uhci_free_td(uhci, uhci->term_td);	uhci->term_td = NULL;err_alloc_term_td:	usb_put_dev(udev);err_alloc_root_hub:	dma_pool_destroy(uhci->qh_pool);	uhci->qh_pool = NULL;err_create_qh_pool:	dma_pool_destroy(uhci->td_pool);	uhci->td_pool = NULL;err_create_td_pool:	dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),			uhci->fl, uhci->fl->dma_handle);	uhci->fl = NULL;err_alloc_fl:#ifdef CONFIG_PROC_FS	remove_proc_entry(hcd->self.bus_name, uhci_proc_root);	uhci->proc_entry = NULL;err_create_proc_entry:#endif	return retval;}static void uhci_stop(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	del_timer_sync(&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	 */	reset_hc(uhci);	spin_lock_irq(&uhci->schedule_lock);	uhci_free_pending_qhs(uhci);	uhci_free_pending_tds(uhci);	uhci_remove_pending_urbps(uhci);	uhci_finish_completion(hcd, NULL);	uhci_free_pending_qhs(uhci);	uhci_free_pending_tds(uhci);	spin_unlock_irq(&uhci->schedule_lock);	/* Wake up anyone waiting for an URB to complete */	wake_up_all(&uhci->waitqh);		release_uhci(uhci);}#ifdef CONFIG_PMstatic int uhci_suspend(struct usb_hcd *hcd, u32 state){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	/* Don't try to suspend broken motherboards, reset instead */	if (suspend_allowed(uhci)) {		suspend_hc(uhci);		uhci->saved_framenumber =				inw(uhci->io_addr + USBFRNUM) & 0x3ff;	} else		reset_hc(uhci);	return 0;}static int uhci_resume(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	pci_set_master(to_pci_dev(uhci_dev(uhci)));	if (uhci->state == UHCI_SUSPENDED) {		/*		 * Some systems don't maintain the UHCI register values		 * during a PM suspend/resume cycle, so reinitialize		 * the Frame Number, Framelist Base Address, Interrupt		 * Enable, and Legacy Support registers.		 */		pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,				0);		outw(uhci->saved_framenumber, uhci->io_addr + USBFRNUM);		outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);		outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC |				USBINTR_SP, uhci->io_addr + USBINTR);		uhci->resume_detect = 1;		pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,				USBLEGSUP_DEFAULT);	} else {		reset_hc(uhci);		start_hc(uhci);	}	uhci->hcd.state = USB_STATE_RUNNING;	return 0;}#endifstatic 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));	uhci->hcd.product_desc = "UHCI Host Controller";	return &uhci->hcd;}static void uhci_hcd_free(struct usb_hcd *hcd){	kfree(hcd_to_uhci(hcd));}/* Are there any URBs for a particular device/endpoint on a given list? */static int urbs_for_ep_list(struct list_head *head,		struct hcd_dev *hdev, int ep){	struct urb_priv *urbp;	list_for_each_entry(urbp, head, urb_list) {		struct urb *urb = urbp->urb;		if (hdev == urb->dev->hcpriv && ep ==				(usb_pipeendpoint(urb->pipe) |				 usb_pipein(urb->pipe)))			return 1;	}	return 0;}/* Are there any URBs for a particular device/endpoint? */static int urbs_for_ep(struct uhci_hcd *uhci, struct hcd_dev *hdev, int ep){	int rc;	spin_lock_irq(&uhci->schedule_lock);	rc = (urbs_for_ep_list(&uhci->urb_list, hdev, ep) ||			urbs_for_ep_list(&uhci->complete_list, hdev, ep) ||			urbs_for_ep_list(&uhci->urb_remove_list, hdev, ep));	spin_unlock_irq(&uhci->schedule_lock);	return rc;}/* Wait until all the URBs for a particular device/endpoint are gone */static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,		struct hcd_dev *hdev, int endpoint){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	wait_event_interruptible(uhci->waitqh,			!urbs_for_ep(uhci, hdev, endpoint));}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 */	.reset =		uhci_reset,	.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,	.endpoint_disable =	uhci_hcd_endpoint_disable,	.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 uhci_pci_ids[] = { {	/* handle any USB UHCI controller */	PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x00), ~0),	.driver_data =	(unsigned long) &uhci_driver,	}, { /* end: all zeroes */ }};MODULE_DEVICE_TABLE(pci, uhci_pci_ids);static struct pci_driver uhci_pci_driver = {	.name =		(char *)hcd_name,	.id_table =	uhci_pci_ids,	.probe =	usb_hcd_pci_probe,	.remove =	usb_hcd_pci_remove,#ifdef	CONFIG_PM	.suspend =	usb_hcd_pci_suspend,	.resume =	usb_hcd_pci_resume,#endif	/* PM */}; static int __init uhci_hcd_init(void){	int retval = -ENOMEM;	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n");	if (usb_disabled())		return -ENODEV;	if (debug) {		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);		if (!errbuf)			goto errbuf_failed;	}#ifdef CONFIG_PROC_FS	uhci_proc_root = create_proc_entry("driver/uhci", S_IFDIR, NULL);	if (!uhci_proc_root)		goto proc_failed;#endif	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))		warn("not all urb_priv's were freed!");up_failed:#ifdef CONFIG_PROC_FS	remove_proc_entry("driver/uhci", NULL);proc_failed:#endif	if (errbuf)		kfree(errbuf);errbuf_failed:	return retval;}static void __exit uhci_hcd_cleanup(void) {	pci_unregister_driver(&uhci_pci_driver);		if (kmem_cache_destroy(uhci_up_cachep))		warn("not all urb_priv's were freed!");#ifdef CONFIG_PROC_FS	remove_proc_entry("driver/uhci", NULL);#endif	if (errbuf)		kfree(errbuf);}module_init(uhci_hcd_init);module_exit(uhci_hcd_cleanup);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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