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

📄 uhci-hcd.c

📁 host usb 主设备程序 支持sd卡 mouse keyboard 的最单单的驱动程序 gcc编译
💻 C
📖 第 1 页 / 共 2 页
字号:
	uhci->io_addr = (unsigned long) hcd->rsrc_start;	/* The UHCI spec says devices must have 2 ports, and goes on to say	 * they may have more but gives no way to determine how many there	 * are.  However according to the UHCI spec, Bit 7 of the port	 * status and control register is always set to 1.  So we try to	 * use this to our advantage.  Another common failure mode when	 * a nonexistent register is addressed is to return all ones, so	 * we test for that also.	 */	for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) {		unsigned int portstatus;		portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2));		if (!(portstatus & 0x0080) || portstatus == 0xffff)			break;	}	if (debug)		dev_info(uhci_dev(uhci), "detected %d ports\n", port);	/* Anything greater than 7 is weird so we'll ignore it. */	if (port > UHCI_RH_MAXCHILD) {		dev_info(uhci_dev(uhci), "port count misdetected? "				"forcing to 2 ports\n");		port = 2;	}	uhci->rh_numports = port;	/* Kick BIOS off this hardware and reset if the controller	 * isn't already safely quiescent.	 */	check_and_reset_hc(uhci);	return 0;}/* Make sure the controller is quiescent and that we're not using it * any more.  This is mainly for the benefit of programs which, like kexec, * expect the hardware to be idle: not doing DMA or generating IRQs. * * This routine may be called in a damaged or failing kernel.  Hence we * do not acquire the spinlock before shutting down the controller. */static void uhci_shutdown(struct pci_dev *pdev){	struct usb_hcd *hcd = (struct usb_hcd *) pci_get_drvdata(pdev);	uhci_hc_died(hcd_to_uhci(hcd));}/* * 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 in which the queues * are encountered by the hardware is: * *  - All isochronous events are 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 high-period interrupt queue. *  - The second queue is the period-1 interrupt and async *    (low-speed control, full-speed control, then bulk) queue. *  - The third queue is the terminating bandwidth reclamation queue, *    which contains no members, loops back to itself, and is present *    only when FSBR is on and there are no full-speed control or bulk QHs. */static int uhci_start(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	int retval = -EBUSY;	int i;	struct dentry *dentry;	hcd->uses_new_polling = 1;	spin_lock_init(&uhci->lock);	setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout,			(unsigned long) uhci);	INIT_LIST_HEAD(&uhci->idle_qh_list);	init_waitqueue_head(&uhci->waitqh);	if (DEBUG_CONFIGURED) {		dentry = debugfs_create_file(hcd->self.bus_name,				S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,				uhci, &uhci_debug_operations);		if (!dentry) {			dev_err(uhci_dev(uhci), "couldn't create uhci "					"debugfs entry\n");			retval = -ENOMEM;			goto err_create_debug_entry;		}		uhci->dentry = dentry;	}	uhci->frame = dma_alloc_coherent(uhci_dev(uhci),			UHCI_NUMFRAMES * sizeof(*uhci->frame),			&uhci->frame_dma_handle, 0);	if (!uhci->frame) {		dev_err(uhci_dev(uhci), "unable to allocate "				"consistent memory for frame list\n");		goto err_alloc_frame;	}	memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame));	uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),			GFP_KERNEL);	if (!uhci->frame_cpu) {		dev_err(uhci_dev(uhci), "unable to allocate "				"memory for frame pointers\n");		goto err_alloc_frame_cpu;	}	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;	}	uhci->term_td = uhci_alloc_td(uhci);	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, NULL, NULL);		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 = async	 */	for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)		uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);	uhci->skel_async_qh->link = UHCI_PTR_TERM;	uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);	/* This dummy TD is to work around a bug in Intel PIIX controllers */	uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |			(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);	uhci->term_td->link = UHCI_PTR_TERM;	uhci->skel_async_qh->element = uhci->skel_term_qh->element =			LINK_TO_TD(uhci->term_td);	/*	 * Fill the frame list: make all entries point to the proper	 * interrupt queue.	 */	for (i = 0; i < UHCI_NUMFRAMES; i++) {		/* Only place we don't use the frame list routines */		uhci->frame[i] = uhci_frame_skel_link(uhci, i);	}	/*	 * Some architectures require a full mb() to enforce completion of	 * the memory writes above before the I/O transfers in configure_hc().	 */	mb();	configure_hc(uhci);	uhci->is_initialized = 1;	start_rh(uhci);	return 0;/* * error exits: */err_alloc_skelqh:	for (i = 0; i < UHCI_NUM_SKELQH; i++) {		if (uhci->skelqh[i])			uhci_free_qh(uhci, uhci->skelqh[i]);	}	uhci_free_td(uhci, uhci->term_td);err_alloc_term_td:	dma_pool_destroy(uhci->qh_pool);err_create_qh_pool:	dma_pool_destroy(uhci->td_pool);err_create_td_pool:	kfree(uhci->frame_cpu);err_alloc_frame_cpu:	dma_free_coherent(uhci_dev(uhci),			UHCI_NUMFRAMES * sizeof(*uhci->frame),			uhci->frame, uhci->frame_dma_handle);err_alloc_frame:	debugfs_remove(uhci->dentry);err_create_debug_entry:	return retval;}static void uhci_stop(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	spin_lock_irq(&uhci->lock);	if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && !uhci->dead)		uhci_hc_died(uhci);	uhci_scan_schedule(uhci);	spin_unlock_irq(&uhci->lock);	del_timer_sync(&uhci->fsbr_timer);	release_uhci(uhci);}#ifdef CONFIG_PMstatic int uhci_rh_suspend(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	int rc = 0;	spin_lock_irq(&uhci->lock);	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))		rc = -ESHUTDOWN;	else if (!uhci->dead)		suspend_rh(uhci, UHCI_RH_SUSPENDED);	spin_unlock_irq(&uhci->lock);	return rc;}static int uhci_rh_resume(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	int rc = 0;	spin_lock_irq(&uhci->lock);	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {		dev_warn(&hcd->self.root_hub->dev, "HC isn't running!\n");		rc = -ESHUTDOWN;	} else if (!uhci->dead)		wakeup_rh(uhci);	spin_unlock_irq(&uhci->lock);	return rc;}static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	int rc = 0;	dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);	spin_lock_irq(&uhci->lock);	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)		goto done_okay;		/* Already suspended or dead */	if (uhci->rh_state > UHCI_RH_SUSPENDED) {		dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n");		rc = -EBUSY;		goto done;	};	/* All PCI host controllers are required to disable IRQ generation	 * at the source, so we must turn off PIRQ.	 */	pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);	mb();	hcd->poll_rh = 0;	/* FIXME: Enable non-PME# remote wakeup? */	/* make sure snapshot being resumed re-enumerates everything */	if (message.event == PM_EVENT_PRETHAW)		uhci_hc_died(uhci);done_okay:	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);done:	spin_unlock_irq(&uhci->lock);	return rc;}static int uhci_resume(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);	/* Since we aren't in D3 any more, it's safe to set this flag	 * even if the controller was dead.	 */	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);	mb();	spin_lock_irq(&uhci->lock);	/* FIXME: Disable non-PME# remote wakeup? */	/* The firmware or a boot kernel may have changed the controller	 * settings during a system wakeup.  Check it and reconfigure	 * to avoid problems.	 */	check_and_reset_hc(uhci);	/* If the controller was dead before, it's back alive now */	configure_hc(uhci);	if (uhci->rh_state == UHCI_RH_RESET) {		/* The controller had to be reset */		usb_root_hub_lost_power(hcd->self.root_hub);		suspend_rh(uhci, UHCI_RH_SUSPENDED);	}	spin_unlock_irq(&uhci->lock);	if (!uhci->working_RD) {		/* Suspended root hub needs to be polled */		hcd->poll_rh = 1;		usb_hcd_poll_rh_status(hcd);	}	return 0;}#endif/* Wait until a particular device/endpoint's QH is idle, and free it */static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,		struct usb_host_endpoint *hep){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	struct uhci_qh *qh;	spin_lock_irq(&uhci->lock);	qh = (struct uhci_qh *) hep->hcpriv;	if (qh == NULL)		goto done;	while (qh->state != QH_STATE_IDLE) {		++uhci->num_waiting;		spin_unlock_irq(&uhci->lock);		wait_event_interruptible(uhci->waitqh,				qh->state == QH_STATE_IDLE);		spin_lock_irq(&uhci->lock);		--uhci->num_waiting;	}	uhci_free_qh(uhci, qh);done:	spin_unlock_irq(&uhci->lock);}static int uhci_hcd_get_frame_number(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	unsigned frame_number;	unsigned delta;	/* Minimize latency by avoiding the spinlock */	frame_number = uhci->frame_number;	barrier();	delta = (inw(uhci->io_addr + USBFRNUM) - frame_number) &			(UHCI_NUMFRAMES - 1);	return frame_number + delta;}static const char hcd_name[] = "uhci_hcd";static const struct hc_driver uhci_driver = {	.description =		hcd_name,	.product_desc =		"UHCI Host Controller",	.hcd_priv_size =	sizeof(struct uhci_hcd),	/* Generic hardware linkage */	.irq =			uhci_irq,	.flags =		HCD_USB11,	/* Basic lifecycle operations */	.reset =		uhci_init,	.start =		uhci_start,#ifdef CONFIG_PM	.suspend =		uhci_suspend,	.resume =		uhci_resume,	.bus_suspend =		uhci_rh_suspend,	.bus_resume =		uhci_rh_resume,#endif	.stop =			uhci_stop,	.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_UHCI, ~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,	.shutdown =	uhci_shutdown,#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 "%s\n",			ignore_oc ? ", overcurrent ignored" : "");	if (usb_disabled())		return -ENODEV;	if (DEBUG_CONFIGURED) {		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);		if (!errbuf)			goto errbuf_failed;		uhci_debugfs_root = debugfs_create_dir("uhci", NULL);		if (!uhci_debugfs_root)			goto debug_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_register_driver(&uhci_pci_driver);	if (retval)		goto init_failed;	return 0;init_failed:	kmem_cache_destroy(uhci_up_cachep);up_failed:	debugfs_remove(uhci_debugfs_root);debug_failed:	kfree(errbuf);errbuf_failed:	return retval;}static void __exit uhci_hcd_cleanup(void) {	pci_unregister_driver(&uhci_pci_driver);	kmem_cache_destroy(uhci_up_cachep);	debugfs_remove(uhci_debugfs_root);	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 + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -