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

📄 uhci-hcd.c

📁 h内核
💻 C
📖 第 1 页 / 共 5 页
字号:
	uhci_delete_queued_urb(uhci, urb);	/* The interrupt loop will reclaim the QH's */	uhci_remove_qh(uhci, urbp->qh);	urbp->qh = NULL;}static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	unsigned long flags;	struct urb_priv *urbp;	unsigned int age;	spin_lock_irqsave(&uhci->schedule_lock, flags);	urbp = urb->hcpriv;	if (!urbp)			/* URB was never linked! */		goto done;	list_del_init(&urbp->urb_list);	uhci_unlink_generic(uhci, urb);	age = uhci_get_current_frame_number(uhci);	if (age != uhci->urb_remove_age) {		uhci_remove_pending_urbps(uhci);		uhci->urb_remove_age = age;	}	/* If we're the first, set the next interrupt bit */	if (list_empty(&uhci->urb_remove_list))		uhci_set_next_interrupt(uhci);	list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);done:	spin_unlock_irqrestore(&uhci->schedule_lock, flags);	return 0;}static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb){	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	struct list_head *head;	struct uhci_td *td;	int count = 0;	uhci_dec_fsbr(uhci, urb);	urbp->fsbr_timeout = 1;	/*	 * Ideally we would want to fix qh->element as well, but it's	 * read/write by the HC, so that can introduce a race. It's not	 * really worth the hassle	 */	head = &urbp->td_list;	list_for_each_entry(td, head, list) {		/*		 * Make sure we don't do the last one (since it'll have the		 * TERM bit set) as well as we skip every so many TD's to		 * make sure it doesn't hog the bandwidth		 */		if (td->list.next != head && (count % DEPTH_INTERVAL) ==				(DEPTH_INTERVAL - 1))			td->link |= UHCI_PTR_DEPTH;		count++;	}	return 0;}/* * uhci_get_current_frame_number() * * returns the current frame number for a USB bus/controller. */static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci){	return inw(uhci->io_addr + USBFRNUM);}static int init_stall_timer(struct usb_hcd *hcd);static void stall_callback(unsigned long ptr){	struct usb_hcd *hcd = (struct usb_hcd *)ptr;	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	struct urb_priv *up;	unsigned long flags;	int called_uhci_finish_completion = 0;	spin_lock_irqsave(&uhci->schedule_lock, flags);	if (!list_empty(&uhci->urb_remove_list) &&	    uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) {		uhci_remove_pending_urbps(uhci);		uhci_finish_completion(hcd, NULL);		called_uhci_finish_completion = 1;	}	list_for_each_entry(up, &uhci->urb_list, urb_list) {		struct urb *u = up->urb;		spin_lock(&u->lock);		/* Check if the FSBR timed out */		if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))			uhci_fsbr_timeout(uhci, u);		spin_unlock(&u->lock);	}	spin_unlock_irqrestore(&uhci->schedule_lock, flags);	/* Wake up anyone waiting for an URB to complete */	if (called_uhci_finish_completion)		wake_up_all(&uhci->waitqh);	/* Really disable FSBR */	if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {		uhci->fsbrtimeout = 0;		uhci->skel_term_qh->link = UHCI_PTR_TERM;	}	/* Poll for and perform state transitions */	hc_state_transitions(uhci);	if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))		uhci_check_resume(uhci);	init_stall_timer(hcd);}static int init_stall_timer(struct usb_hcd *hcd){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	init_timer(&uhci->stall_timer);	uhci->stall_timer.function = stall_callback;	uhci->stall_timer.data = (unsigned long)hcd;	uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100);	add_timer(&uhci->stall_timer);	return 0;}static void uhci_free_pending_qhs(struct uhci_hcd *uhci){	struct uhci_qh *qh, *tmp;	list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {		list_del_init(&qh->remove_list);		uhci_free_qh(uhci, qh);	}}static void uhci_free_pending_tds(struct uhci_hcd *uhci){	struct uhci_td *td, *tmp;	list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {		list_del_init(&td->remove_list);		uhci_free_td(uhci, td);	}}static voiduhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)__releases(uhci->schedule_lock)__acquires(uhci->schedule_lock){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	uhci_destroy_urb_priv(uhci, urb);	spin_unlock(&uhci->schedule_lock);	usb_hcd_giveback_urb(hcd, urb, regs);	spin_lock(&uhci->schedule_lock);}static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	struct urb_priv *urbp, *tmp;	list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {		struct urb *urb = urbp->urb;		list_del_init(&urbp->urb_list);		uhci_finish_urb(hcd, urb, regs);	}}static void uhci_remove_pending_urbps(struct uhci_hcd *uhci){	/* Splice the urb_remove_list onto the end of the complete_list */	list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);}static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	unsigned long io_addr = uhci->io_addr;	unsigned short status;	struct urb_priv *urbp, *tmp;	unsigned int age;	/*	 * Read the interrupt status, and write it back to clear the	 * interrupt cause.  Contrary to the UHCI specification, the	 * "HC Halted" status bit is persistent: it is RO, not R/WC.	 */	status = inw(io_addr + USBSTS);	if (!(status & ~USBSTS_HCH))	/* shared interrupt, not mine */		return IRQ_NONE;	outw(status, io_addr + USBSTS);		/* Clear it */	if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {		if (status & USBSTS_HSE)			dev_err(uhci_dev(uhci), "host system error, "					"PCI problems?\n");		if (status & USBSTS_HCPE)			dev_err(uhci_dev(uhci), "host controller process "					"error, something bad happened!\n");		if ((status & USBSTS_HCH) && uhci->state > 0) {			dev_err(uhci_dev(uhci), "host controller halted, "					"very bad!\n");			/* FIXME: Reset the controller, fix the offending TD */		}	}	if (status & USBSTS_RD)		uhci->resume_detect = 1;	spin_lock(&uhci->schedule_lock);	age = uhci_get_current_frame_number(uhci);	if (age != uhci->qh_remove_age)		uhci_free_pending_qhs(uhci);	if (age != uhci->td_remove_age)		uhci_free_pending_tds(uhci);	if (age != uhci->urb_remove_age)		uhci_remove_pending_urbps(uhci);	if (list_empty(&uhci->urb_remove_list) &&	    list_empty(&uhci->td_remove_list) &&	    list_empty(&uhci->qh_remove_list))		uhci_clear_next_interrupt(uhci);	else		uhci_set_next_interrupt(uhci);	/* Walk the list of pending URBs to see which ones completed	 * (must be _safe because uhci_transfer_result() dequeues URBs) */	list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) {		struct urb *urb = urbp->urb;		/* Checks the status and does all of the magic necessary */		uhci_transfer_result(uhci, urb);	}	uhci_finish_completion(hcd, regs);	spin_unlock(&uhci->schedule_lock);	/* Wake up anyone waiting for an URB to complete */	wake_up_all(&uhci->waitqh);	return IRQ_HANDLED;}static void reset_hc(struct uhci_hcd *uhci){	unsigned long io_addr = uhci->io_addr;	/* Turn off PIRQ, SMI, and all interrupts.  This also turns off	 * the BIOS's USB Legacy Support.	 */	pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);	outw(0, uhci->io_addr + USBINTR);	/* Global reset for 50ms */	uhci->state = UHCI_RESET;	outw(USBCMD_GRESET, io_addr + USBCMD);	msleep(50);	outw(0, io_addr + USBCMD);	/* Another 10ms delay */	msleep(10);	uhci->resume_detect = 0;}static void suspend_hc(struct uhci_hcd *uhci){	unsigned long io_addr = uhci->io_addr;	dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);	uhci->state = UHCI_SUSPENDED;	uhci->resume_detect = 0;	outw(USBCMD_EGSM, io_addr + USBCMD);}static void wakeup_hc(struct uhci_hcd *uhci){	unsigned long io_addr = uhci->io_addr;	switch (uhci->state) {		case UHCI_SUSPENDED:		/* Start the resume */			dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);			/* Global resume for >= 20ms */			outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD);			uhci->state = UHCI_RESUMING_1;			uhci->state_end = jiffies + msecs_to_jiffies(20);			break;		case UHCI_RESUMING_1:		/* End global resume */			uhci->state = UHCI_RESUMING_2;			outw(0, io_addr + USBCMD);			/* Falls through */		case UHCI_RESUMING_2:		/* Wait for EOP to be sent */			if (inw(io_addr + USBCMD) & USBCMD_FGR)				break;			/* Run for at least 1 second, and			 * mark it configured with a 64-byte max packet */			uhci->state = UHCI_RUNNING_GRACE;			uhci->state_end = jiffies + HZ;			outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP,					io_addr + USBCMD);			break;		case UHCI_RUNNING_GRACE:	/* Now allowed to suspend */			uhci->state = UHCI_RUNNING;			break;		default:			break;	}}static int ports_active(struct uhci_hcd *uhci){	unsigned long io_addr = uhci->io_addr;	int connection = 0;	int i;	for (i = 0; i < uhci->rh_numports; i++)		connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS);	return connection;}static int suspend_allowed(struct uhci_hcd *uhci){	unsigned long io_addr = uhci->io_addr;	int i;	if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)		return 1;	/* Some of Intel's USB controllers have a bug that causes false	 * resume indications if any port has an over current condition.	 * To prevent problems, we will not allow a global suspend if	 * any ports are OC.	 *	 * Some motherboards using Intel's chipsets (but not using all	 * the USB ports) appear to hardwire the over current inputs active	 * to disable the USB ports.	 */	/* check for over current condition on any port */	for (i = 0; i < uhci->rh_numports; i++) {		if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)			return 0;	}	return 1;}static void hc_state_transitions(struct uhci_hcd *uhci){	switch (uhci->state) {		case UHCI_RUNNING:			/* global suspend if nothing connected for 1 second */			if (!ports_active(uhci) && suspend_allowed(uhci)) {				uhci->state = UHCI_SUSPENDING_GRACE;				uhci->state_end = jiffies + HZ;			}			break;		case UHCI_SUSPENDING_GRACE:			if (ports_active(uhci))				uhci->state = UHCI_RUNNING;			else if (time_after_eq(jiffies, uhci->state_end))				suspend_hc(uhci);			break;		case UHCI_SUSPENDED:			/* wakeup if requested by a device */			if (uhci->resume_detect)				wakeup_hc(uhci);			break;		case UHCI_RESUMING_1:		case UHCI_RESUMING_2:		case UHCI_RUNNING_GRACE:			if (time_after_eq(jiffies, uhci->state_end))				wakeup_hc(uhci);			break;		default:			break;	}}static int start_hc(struct uhci_hcd *uhci){	unsigned long io_addr = uhci->io_addr;	int timeout = 10;	/*	 * 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 < 0) {			dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");			return -ETIMEDOUT;		}		msleep(1);	}	/* Turn on PIRQ and all interrupts */	pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,			USBLEGSUP_DEFAULT);	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,		io_addr + USBINTR);	/* Start at frame 0 */	outw(0, io_addr + USBFRNUM);	outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);	/* Run and mark it configured with a 64-byte max packet */	uhci->state = UHCI_RUNNING_GRACE;	uhci->state_end = jiffies + HZ;	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);        uhci_to_hcd(uhci)->state = USB_STATE_RUNNING;	return 0;}/* * De-allocate all resources.. */static void release_uhci(struct uhci_hcd *uhci){	int i;	for (i = 0; i < UHCI_NUM_SKELQH; i++)		if (uhci->skelqh[i]) {			uhci_free_qh(uhci, uhci->skelqh[i]);			uhci->skelqh[i] = NULL;		}	if (uhci->term_td) {		uhci_free_td(uhci, uhci->term_td);		uhci->term_td = NULL;	}	if (uhci->qh_pool) {		dma_pool_destroy(uhci->qh_pool);		uhci->qh_pool = NULL;	}	if (uhci->td_pool) {		dma_pool_destroy(uhci->td_pool);

⌨️ 快捷键说明

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