ohci-hub.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 648 行 · 第 1/2 页

C
648
字号
{	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);	int		ports, i, changed = 0, length = 1;	int		can_suspend = 1;	ports = roothub_a (ohci) & RH_A_NDP; 	if (ports > MAX_ROOT_PORTS) {		if (!HCD_IS_RUNNING(ohci->hcd.state))			return -ESHUTDOWN;		ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n",			ports, ohci_readl (&ohci->regs->roothub.a) & RH_A_NDP);		/* retry later; "should not happen" */		return 0;	}	/* init status */	if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))		buf [0] = changed = 1;	else		buf [0] = 0;	if (ports > 7) {		buf [1] = 0;		length++;	}	/* look at each port */	for (i = 0; i < ports; i++) {		u32	status = roothub_portstatus (ohci, i);		if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC				| RH_PS_OCIC | RH_PS_PRSC)) {			changed = 1;			if (i < 7)			    buf [0] |= 1 << (i + 1);			else			    buf [1] |= 1 << (i - 7);			continue;		}		/* can suspend if no ports are enabled; or if all all		 * enabled ports are suspended AND remote wakeup is on.		 */		if (!(status & RH_PS_CCS))			continue;		if ((status & RH_PS_PSS) && ohci->hcd.remote_wakeup)			continue;		can_suspend = 0;	}#ifdef CONFIG_PM	/* save power by suspending idle root hubs;	 * INTR_RD wakes us when there's work	 */	if (can_suspend			&& !changed			&& !ohci->ed_rm_list			&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)					& ohci->hc_control)				== OHCI_USB_OPER			&& down_trylock (&hcd->self.root_hub->serialize) == 0			) {		ohci_vdbg (ohci, "autosuspend\n");		(void) ohci_hub_suspend (&ohci->hcd);		ohci->hcd.state = USB_STATE_RUNNING;		up (&hcd->self.root_hub->serialize);	}#endif	return changed ? length : 0;}/*-------------------------------------------------------------------------*/static voidohci_hub_descriptor (	struct ohci_hcd			*ohci,	struct usb_hub_descriptor	*desc) {	u32		rh = roothub_a (ohci);	int		ports = rh & RH_A_NDP; 	u16		temp;	desc->bDescriptorType = 0x29;	desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;	desc->bHubContrCurrent = 0;	desc->bNbrPorts = ports;	temp = 1 + (ports / 8);	desc->bDescLength = 7 + 2 * temp;	temp = 0;	if (rh & RH_A_NPS)		/* no power switching? */	    temp |= 0x0002;	if (rh & RH_A_PSM) 		/* per-port power switching? */	    temp |= 0x0001;	if (rh & RH_A_NOCP)		/* no overcurrent reporting? */	    temp |= 0x0010;	else if (rh & RH_A_OCPM)	/* per-port overcurrent reporting? */	    temp |= 0x0008;	desc->wHubCharacteristics = cpu_to_le16 (temp);	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */	rh = roothub_b (ohci);	desc->bitmap [0] = rh & RH_B_DR;	if (ports > 7) {		desc->bitmap [1] = (rh & RH_B_DR) >> 8;		desc->bitmap [2] = desc->bitmap [3] = 0xff;	} else		desc->bitmap [1] = 0xff;}/*-------------------------------------------------------------------------*/#ifdef	CONFIG_USB_OTGstatic int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port){	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);	u32			status;	if (!port)		return -EINVAL;	port--;	/* start port reset before HNP protocol times out */	status = ohci_readl(&ohci->regs->roothub.portstatus [port]);	if (!(status & RH_PS_CCS))		return -ENODEV;	/* khubd will finish the reset later */	writel(RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);	return 0;}static void start_hnp(struct ohci_hcd *ohci);#else#define	ohci_start_port_reset		NULL#endif/*-------------------------------------------------------------------------*//* See usb 7.1.7.5:  root hubs must issue at least 50 msec reset signaling, * not necessarily continuous ... to guard against resume signaling. * The short timeout is safe for non-root hubs, and is backward-compatible * with earlier Linux hosts. */#ifdef	CONFIG_USB_SUSPEND#define	PORT_RESET_MSEC		50#else#define	PORT_RESET_MSEC		10#endif/* this timer value might be vendor-specific ... */#define	PORT_RESET_HW_MSEC	10/* wrap-aware logic stolen from <linux/jiffies.h> */#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)/* called from some task, normally khubd */static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port){	u32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];	u32	temp;	u16	now = readl(&ohci->regs->fmnumber);	u16	reset_done = now + PORT_RESET_MSEC;	/* build a "continuous enough" reset signal, with up to	 * 3msec gap between pulses.  scheduler HZ==100 must work;	 * this might need to be deadline-scheduled.	 */	do {		/* spin until any current reset finishes */		for (;;) {			temp = ohci_readl (portstat);			if (!(temp & RH_PS_PRS))				break;			udelay (500);		} 		if (!(temp & RH_PS_CCS))			break;		if (temp & RH_PS_PRSC)			writel (RH_PS_PRSC, portstat);		/* start the next reset, sleep till it's probably done */		writel (RH_PS_PRS, portstat);		msleep(PORT_RESET_HW_MSEC);		now = readl(&ohci->regs->fmnumber);	} while (tick_before(now, reset_done));	/* caller synchronizes using PRSC */}static int ohci_hub_control (	struct usb_hcd	*hcd,	u16		typeReq,	u16		wValue,	u16		wIndex,	char		*buf,	u16		wLength) {	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);	int		ports = hcd_to_bus (hcd)->root_hub->maxchild;	u32		temp;	int		retval = 0;	switch (typeReq) {	case ClearHubFeature:		switch (wValue) {		case C_HUB_OVER_CURRENT:			writel (RH_HS_OCIC, &ohci->regs->roothub.status);		case C_HUB_LOCAL_POWER:			break;		default:			goto error;		}		break;	case ClearPortFeature:		if (!wIndex || wIndex > ports)			goto error;		wIndex--;		switch (wValue) {		case USB_PORT_FEAT_ENABLE:			temp = RH_PS_CCS;			break;		case USB_PORT_FEAT_C_ENABLE:			temp = RH_PS_PESC;			break;		case USB_PORT_FEAT_SUSPEND:			temp = RH_PS_POCI;			if ((ohci->hc_control & OHCI_CTRL_HCFS)					!= OHCI_USB_OPER)				schedule_work (&ohci->rh_resume);			break;		case USB_PORT_FEAT_C_SUSPEND:			temp = RH_PS_PSSC;			break;		case USB_PORT_FEAT_POWER:			temp = RH_PS_LSDA;			break;		case USB_PORT_FEAT_C_CONNECTION:			temp = RH_PS_CSC;			break;		case USB_PORT_FEAT_C_OVER_CURRENT:			temp = RH_PS_OCIC;			break;		case USB_PORT_FEAT_C_RESET:			temp = RH_PS_PRSC;			break;		default:			goto error;		}		writel (temp, &ohci->regs->roothub.portstatus [wIndex]);		// ohci_readl (&ohci->regs->roothub.portstatus [wIndex]);		break;	case GetHubDescriptor:		ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);		break;	case GetHubStatus:		temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);		*(__le32 *) buf = cpu_to_le32 (temp);		break;	case GetPortStatus:		if (!wIndex || wIndex > ports)			goto error;		wIndex--;		temp = roothub_portstatus (ohci, wIndex);		*(__le32 *) buf = cpu_to_le32 (temp);#ifndef	OHCI_VERBOSE_DEBUG	if (*(u16*)(buf+2))	/* only if wPortChange is interesting */#endif		dbg_port (ohci, "GetStatus", wIndex, temp);		break;	case SetHubFeature:		switch (wValue) {		case C_HUB_OVER_CURRENT:			// FIXME:  this can be cleared, yes?		case C_HUB_LOCAL_POWER:			break;		default:			goto error;		}		break;	case SetPortFeature:		if (!wIndex || wIndex > ports)			goto error;		wIndex--;		switch (wValue) {		case USB_PORT_FEAT_SUSPEND:#ifdef	CONFIG_USB_OTG			if (ohci->hcd.self.otg_port == (wIndex + 1)					&& ohci->hcd.self.b_hnp_enable)				start_hnp(ohci);			else#endif			writel (RH_PS_PSS,				&ohci->regs->roothub.portstatus [wIndex]);			break;		case USB_PORT_FEAT_POWER:			writel (RH_PS_PPS,				&ohci->regs->roothub.portstatus [wIndex]);			break;		case USB_PORT_FEAT_RESET:			root_port_reset (ohci, wIndex);			break;		default:			goto error;		}		break;	default:error:		/* "protocol stall" on error */		retval = -EPIPE;	}	return retval;}

⌨️ 快捷键说明

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