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

📄 ohci-hcd.c

📁 ohci-hcd linux代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net> * * [ Initialisation is based on Linus'  ] * [ uhci code and gregs ohci fragments ] * [ (C) Copyright 1999 Linus Torvalds  ] * [ (C) Copyright 1999 Gregory P. Smith] * * * OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller * interfaces (though some non-x86 Intel chips use it).  It supports * smarter hardware than UHCI.  A download link for the spec available * through the http://www.usb.org website. * * This file is licenced under the GPL. */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/usb.h>#include <linux/usb/otg.h>#include <linux/dma-mapping.h>#include <linux/dmapool.h>#include <linux/reboot.h>#include <linux/workqueue.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>#include <asm/byteorder.h>#include "../core/hcd.h"#define DRIVER_VERSION "2006 August 04"#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"/*-------------------------------------------------------------------------*/#undef OHCI_VERBOSE_DEBUG	/* not always helpful *//* For initializing controller (mask in an HCFS mode too) */#define	OHCI_CONTROL_INIT	OHCI_CTRL_CBSR#define	OHCI_INTR_INIT \		(OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \		| OHCI_INTR_RD | OHCI_INTR_WDH)#ifdef __hppa__/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */#define	IR_DISABLE#endif#ifdef CONFIG_ARCH_OMAP/* OMAP doesn't support IR (no SMM; not needed) */#define	IR_DISABLE#endif/*-------------------------------------------------------------------------*/static const char	hcd_name [] = "ohci_hcd";#define	STATECHANGE_DELAY	msecs_to_jiffies(300)#include "ohci.h"static void ohci_dump (struct ohci_hcd *ohci, int verbose);static int ohci_init (struct ohci_hcd *ohci);static void ohci_stop (struct usb_hcd *hcd);#if defined(CONFIG_PM) || defined(CONFIG_PCI)static int ohci_restart (struct ohci_hcd *ohci);#endif#include "ohci-hub.c"#include "ohci-dbg.c"#include "ohci-mem.c"#include "ohci-q.c"/* * On architectures with edge-triggered interrupts we must never return * IRQ_NONE. */#if defined(CONFIG_SA1111)  /* ... or other edge-triggered systems */#define IRQ_NOTMINE	IRQ_HANDLED#else#define IRQ_NOTMINE	IRQ_NONE#endif/* Some boards misreport power switching/overcurrent */static int distrust_firmware = 1;module_param (distrust_firmware, bool, 0);MODULE_PARM_DESC (distrust_firmware,	"true to distrust firmware power/overcurrent setup");/* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */static int no_handshake = 0;module_param (no_handshake, bool, 0);MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");/*-------------------------------------------------------------------------*//* * queue up an urb for anything except the root hub */static int ohci_urb_enqueue (	struct usb_hcd	*hcd,	struct urb	*urb,	gfp_t		mem_flags) {	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);	struct ed	*ed;	urb_priv_t	*urb_priv;	unsigned int	pipe = urb->pipe;	int		i, size = 0;	unsigned long	flags;	int		retval = 0;#ifdef OHCI_VERBOSE_DEBUG	urb_print(urb, "SUB", usb_pipein(pipe), -EINPROGRESS);#endif	/* every endpoint has a ed, locate and maybe (re)initialize it */	if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval)))		return -ENOMEM;	/* for the private part of the URB we need the number of TDs (size) */	switch (ed->type) {		case PIPE_CONTROL:			/* td_submit_urb() doesn't yet handle these */			if (urb->transfer_buffer_length > 4096)				return -EMSGSIZE;			/* 1 TD for setup, 1 for ACK, plus ... */			size = 2;			/* FALLTHROUGH */		// case PIPE_INTERRUPT:		// case PIPE_BULK:		default:			/* one TD for every 4096 Bytes (can be upto 8K) */			size += urb->transfer_buffer_length / 4096;			/* ... and for any remaining bytes ... */			if ((urb->transfer_buffer_length % 4096) != 0)				size++;			/* ... and maybe a zero length packet to wrap it up */			if (size == 0)				size++;			else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0				&& (urb->transfer_buffer_length					% usb_maxpacket (urb->dev, pipe,						usb_pipeout (pipe))) == 0)				size++;			break;		case PIPE_ISOCHRONOUS: /* number of packets from URB */			size = urb->number_of_packets;			break;	}	/* allocate the private part of the URB */	urb_priv = kzalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),			mem_flags);	if (!urb_priv)		return -ENOMEM;	INIT_LIST_HEAD (&urb_priv->pending);	urb_priv->length = size;	urb_priv->ed = ed;	/* allocate the TDs (deferring hash chain updates) */	for (i = 0; i < size; i++) {		urb_priv->td [i] = td_alloc (ohci, mem_flags);		if (!urb_priv->td [i]) {			urb_priv->length = i;			urb_free_priv (ohci, urb_priv);			return -ENOMEM;		}	}	spin_lock_irqsave (&ohci->lock, flags);	/* don't submit to a dead HC */	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {		retval = -ENODEV;		goto fail;	}	if (!HC_IS_RUNNING(hcd->state)) {		retval = -ENODEV;		goto fail;	}	retval = usb_hcd_link_urb_to_ep(hcd, urb);	if (retval)		goto fail;	/* schedule the ed if needed */	if (ed->state == ED_IDLE) {		retval = ed_schedule (ohci, ed);		if (retval < 0) {			usb_hcd_unlink_urb_from_ep(hcd, urb);			goto fail;		}		if (ed->type == PIPE_ISOCHRONOUS) {			u16	frame = ohci_frame_no(ohci);			/* delay a few frames before the first TD */			frame += max_t (u16, 8, ed->interval);			frame &= ~(ed->interval - 1);			frame |= ed->branch;			urb->start_frame = frame;			/* yes, only URB_ISO_ASAP is supported, and			 * urb->start_frame is never used as input.			 */		}	} else if (ed->type == PIPE_ISOCHRONOUS)		urb->start_frame = ed->last_iso + ed->interval;	/* fill the TDs and link them to the ed; and	 * enable that part of the schedule, if needed	 * and update count of queued periodic urbs	 */	urb->hcpriv = urb_priv;	td_submit_urb (ohci, urb);fail:	if (retval)		urb_free_priv (ohci, urb_priv);	spin_unlock_irqrestore (&ohci->lock, flags);	return retval;}/* * decouple the URB from the HC queues (TDs, urb_priv). * reporting is always done * asynchronously, and we might be dealing with an urb that's * partially transferred, or an ED with other urbs being unlinked. */static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status){	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);	unsigned long		flags;	int			rc;#ifdef OHCI_VERBOSE_DEBUG	urb_print(urb, "UNLINK", 1, status);#endif	spin_lock_irqsave (&ohci->lock, flags);	rc = usb_hcd_check_unlink_urb(hcd, urb, status);	if (rc) {		;	/* Do nothing */	} else if (HC_IS_RUNNING(hcd->state)) {		urb_priv_t  *urb_priv;		/* Unless an IRQ completed the unlink while it was being		 * handed to us, flag it for unlink and giveback, and force		 * some upcoming INTR_SF to call finish_unlinks()		 */		urb_priv = urb->hcpriv;		if (urb_priv) {			if (urb_priv->ed->state == ED_OPER)				start_ed_unlink (ohci, urb_priv->ed);		}	} else {		/*		 * with HC dead, we won't respect hc queue pointers		 * any more ... just clean up every urb's memory.		 */		if (urb->hcpriv)			finish_urb(ohci, urb, status);	}	spin_unlock_irqrestore (&ohci->lock, flags);	return rc;}/*-------------------------------------------------------------------------*//* frees config/altsetting state for endpoints, * including ED memory, dummy TD, and bulk/intr data toggle */static voidohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep){	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);	unsigned long		flags;	struct ed		*ed = ep->hcpriv;	unsigned		limit = 1000;	/* ASSERT:  any requests/urbs are being unlinked */	/* ASSERT:  nobody can be submitting urbs for this any more */	if (!ed)		return;rescan:	spin_lock_irqsave (&ohci->lock, flags);	if (!HC_IS_RUNNING (hcd->state)) {sanitize:		ed->state = ED_IDLE;		if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)			ohci->eds_scheduled--;		finish_unlinks (ohci, 0);	}	switch (ed->state) {	case ED_UNLINK:		/* wait for hw to finish? */		/* major IRQ delivery trouble loses INTR_SF too... */		if (limit-- == 0) {			ohci_warn(ohci, "ED unlink timeout\n");			if (quirk_zfmicro(ohci)) {				ohci_warn(ohci, "Attempting ZF TD recovery\n");				ohci->ed_to_check = ed;				ohci->zf_delay = 2;			}			goto sanitize;		}		spin_unlock_irqrestore (&ohci->lock, flags);		schedule_timeout_uninterruptible(1);		goto rescan;	case ED_IDLE:		/* fully unlinked */		if (list_empty (&ed->td_list)) {			td_free (ohci, ed->dummy);			ed_free (ohci, ed);			break;		}		/* else FALL THROUGH */	default:		/* caller was supposed to have unlinked any requests;		 * that's not our job.  can't recover; must leak ed.		 */		ohci_err (ohci, "leak ed %p (#%02x) state %d%s\n",			ed, ep->desc.bEndpointAddress, ed->state,			list_empty (&ed->td_list) ? "" : " (has tds)");		td_free (ohci, ed->dummy);		break;	}	ep->hcpriv = NULL;	spin_unlock_irqrestore (&ohci->lock, flags);	return;}static int ohci_get_frame (struct usb_hcd *hcd){	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);	return ohci_frame_no(ohci);}static void ohci_usb_reset (struct ohci_hcd *ohci){	ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);	ohci->hc_control &= OHCI_CTRL_RWC;	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);}/* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and * other cases where the next software may expect clean state from the * "firmware".  this is bus-neutral, unlike shutdown() methods. */static voidohci_shutdown (struct usb_hcd *hcd){	struct ohci_hcd *ohci;	ohci = hcd_to_ohci (hcd);	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);	ohci_usb_reset (ohci);	/* flush the writes */	(void) ohci_readl (ohci, &ohci->regs->control);}static int check_ed(struct ohci_hcd *ohci, struct ed *ed){	return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0		&& (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK)			== (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK)		&& !list_empty(&ed->td_list);}/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes * an interrupt TD but neglects to add it to the donelist.  On systems with * this chipset, we need to periodically check the state of the queues to look * for such "lost" TDs. */static void unlink_watchdog_func(unsigned long _ohci){	unsigned long	flags;	unsigned	max;	unsigned	seen_count = 0;	unsigned	i;	struct ed	**seen = NULL;	struct ohci_hcd	*ohci = (struct ohci_hcd *) _ohci;	spin_lock_irqsave(&ohci->lock, flags);	max = ohci->eds_scheduled;	if (!max)		goto done;	if (ohci->ed_to_check)		goto out;	seen = kcalloc(max, sizeof *seen, GFP_ATOMIC);	if (!seen)		goto out;	for (i = 0; i < NUM_INTS; i++) {		struct ed	*ed = ohci->periodic[i];		while (ed) {			unsigned	temp;			/* scan this branch of the periodic schedule tree */			for (temp = 0; temp < seen_count; temp++) {				if (seen[temp] == ed) {					/* we've checked it and what's after */					ed = NULL;					break;				}			}			if (!ed)				break;			seen[seen_count++] = ed;			if (!check_ed(ohci, ed)) {				ed = ed->ed_next;				continue;			}			/* HC's TD list is empty, but HCD sees at least one			 * TD that's not been sent through the donelist.			 */			ohci->ed_to_check = ed;			ohci->zf_delay = 2;			/* The HC may wait until the next frame to report the			 * TD as done through the donelist and INTR_WDH.  (We			 * just *assume* it's not a multi-TD interrupt URB;			 * those could defer the IRQ more than one frame, using			 * DI...)  Check again after the next INTR_SF.			 */			ohci_writel(ohci, OHCI_INTR_SF,					&ohci->regs->intrstatus);			ohci_writel(ohci, OHCI_INTR_SF,					&ohci->regs->intrenable);			/* flush those writes */			(void) ohci_readl(ohci, &ohci->regs->control);			goto out;		}	}out:	kfree(seen);	if (ohci->eds_scheduled)		mod_timer(&ohci->unlink_watchdog, round_jiffies_relative(HZ));done:	spin_unlock_irqrestore(&ohci->lock, flags);}/*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*//* init memory, and kick BIOS/SMM off */static int ohci_init (struct ohci_hcd *ohci){	int ret;	struct usb_hcd *hcd = ohci_to_hcd(ohci);	disable (ohci);	ohci->regs = hcd->regs;	/* REVISIT this BIOS handshake is now moved into PCI "quirks", and	 * was never needed for most non-PCI systems ... remove the code?	 */#ifndef IR_DISABLE	/* SMM owns the HC?  not for long! */	if (!no_handshake && ohci_readl (ohci,					&ohci->regs->control) & OHCI_CTRL_IR) {		u32 temp;		ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");		/* this timeout is arbitrary.  we make it long, so systems		 * depending on usb keyboards may be usable even if the		 * BIOS/SMM code seems pretty broken.		 */		temp = 500;	/* arbitrary: five seconds */		ohci_writel (ohci, OHCI_INTR_OC, &ohci->regs->intrenable);		ohci_writel (ohci, OHCI_OCR, &ohci->regs->cmdstatus);		while (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_IR) {			msleep (10);			if (--temp == 0) {				ohci_err (ohci, "USB HC takeover failed!"					"  (BIOS/SMM bug)\n");				return -EBUSY;			}		}		ohci_usb_reset (ohci);	}#endif	/* Disable HC interrupts */	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);	/* flush the writes, and save key bits like RWC */	if (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_RWC)		ohci->hc_control |= OHCI_CTRL_RWC;	/* Read the number of ports unless overridden */	if (ohci->num_ports == 0)		ohci->num_ports = roothub_a(ohci) & RH_A_NDP;	if (ohci->hcca)		return 0;	ohci->hcca = dma_alloc_coherent (hcd->self.controller,			sizeof *ohci->hcca, &ohci->hcca_dma, 0);	if (!ohci->hcca)		return -ENOMEM;	if ((ret = ohci_mem_init (ohci)) < 0)		ohci_stop (hcd);	else {		create_debug_files (ohci);	}	return ret;}/*-------------------------------------------------------------------------*//* Start an OHCI controller, set the BUS operational * resets USB and controller * enable interrupts */static int ohci_run (struct ohci_hcd *ohci){	u32			mask, temp;	int			first = ohci->fminterval == 0;	struct usb_hcd		*hcd = ohci_to_hcd(ohci);	disable (ohci);	/* boot firmware should have set this up (5.1.1.3.1) */	if (first) {		temp = ohci_readl (ohci, &ohci->regs->fminterval);		ohci->fminterval = temp & 0x3fff;		if (ohci->fminterval != FI)			ohci_dbg (ohci, "fminterval delta %d\n",				ohci->fminterval - FI);		ohci->fminterval |= FSMP (ohci->fminterval) << 16;		/* also: power/overcurrent flags in roothub.a */	}	/* Reset USB nearly "by the book".  RemoteWakeupConnected was	 * saved if boot firmware (BIOS/SMM/...) told us it's connected,	 * or if bus glue did the same (e.g. for PCI add-in cards with	 * PCI PM support).	 */	if ((ohci->hc_control & OHCI_CTRL_RWC) != 0			&& !device_may_wakeup(hcd->self.controller))		device_init_wakeup(hcd->self.controller, 1);	switch (ohci->hc_control & OHCI_CTRL_HCFS) {

⌨️ 快捷键说明

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