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

📄 isp116x-hcd.c

📁 usb driver for 2.6.17
💻 C
📖 第 1 页 / 共 3 页
字号:
	atomic_inc(&isp116x->atl_finishing);	unpack_fifo(isp116x);	postproc_atl_queue(isp116x);	for (ep = isp116x->atl_active; ep; ep = ep->active) {		urb =		    container_of(ep->hep->urb_list.next, struct urb, urb_list);		/* USB_PID_ACK check here avoids finishing of		   control transfers, for which TD_DATAUNDERRUN		   occured, while URB_SHORT_NOT_OK was set */		if (urb && urb->status != -EINPROGRESS		    && ep->nextpid != USB_PID_ACK)			finish_request(isp116x, ep, urb, regs);	}	atomic_dec(&isp116x->atl_finishing);}static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	u16 irqstat;	irqreturn_t ret = IRQ_NONE;	spin_lock(&isp116x->lock);	isp116x_write_reg16(isp116x, HCuPINTENB, 0);	irqstat = isp116x_read_reg16(isp116x, HCuPINT);	isp116x_write_reg16(isp116x, HCuPINT, irqstat);	if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {		ret = IRQ_HANDLED;		finish_atl_transfers(isp116x, regs);	}	if (irqstat & HCuPINT_OPR) {		u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT);		isp116x_write_reg32(isp116x, HCINTSTAT, intstat);		if (intstat & HCINT_UE) {			ERR("Unrecoverable error, HC is dead!\n");			/* IRQ's are off, we do no DMA,			   perfectly ready to die ... */			hcd->state = HC_STATE_HALT;			ret = IRQ_HANDLED;			goto done;		}		if (intstat & HCINT_RHSC)			/* When root hub or any of its ports is going			   to come out of suspend, it may take more			   than 10ms for status bits to stabilize. */			mod_timer(&hcd->rh_timer, jiffies				  + msecs_to_jiffies(20) + 1);		if (intstat & HCINT_RD) {			DBG("---- remote wakeup\n");			usb_hcd_resume_root_hub(hcd);		}		irqstat &= ~HCuPINT_OPR;		ret = IRQ_HANDLED;	}	if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {		start_atl_transfers(isp116x);	}	isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);      done:	spin_unlock(&isp116x->lock);	return ret;}/*-----------------------------------------------------------------*//* usb 1.1 says max 90% of a frame is available for periodic transfers. * this driver doesn't promise that much since it's got to handle an * IRQ per packet; irq handling latencies also use up that time. *//* out of 1000 us */#define	MAX_PERIODIC_LOAD	600static int balance(struct isp116x *isp116x, u16 period, u16 load){	int i, branch = -ENOSPC;	/* search for the least loaded schedule branch of that period	   which has enough bandwidth left unreserved. */	for (i = 0; i < period; i++) {		if (branch < 0 || isp116x->load[branch] > isp116x->load[i]) {			int j;			for (j = i; j < PERIODIC_SIZE; j += period) {				if ((isp116x->load[j] + load)				    > MAX_PERIODIC_LOAD)					break;			}			if (j < PERIODIC_SIZE)				continue;			branch = i;		}	}	return branch;}/* NB! ALL the code above this point runs with isp116x->lock   held, irqs off*//*-----------------------------------------------------------------*/static int isp116x_urb_enqueue(struct usb_hcd *hcd,			       struct usb_host_endpoint *hep, struct urb *urb,			       gfp_t mem_flags){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	struct usb_device *udev = urb->dev;	unsigned int pipe = urb->pipe;	int is_out = !usb_pipein(pipe);	int type = usb_pipetype(pipe);	int epnum = usb_pipeendpoint(pipe);	struct isp116x_ep *ep = NULL;	unsigned long flags;	int i;	int ret = 0;	urb_dbg(urb, "Enqueue");	if (type == PIPE_ISOCHRONOUS) {		ERR("Isochronous transfers not supported\n");		urb_dbg(urb, "Refused to enqueue");		return -ENXIO;	}	/* avoid all allocations within spinlocks: request or endpoint */	if (!hep->hcpriv) {		ep = kzalloc(sizeof *ep, mem_flags);		if (!ep)			return -ENOMEM;	}	spin_lock_irqsave(&isp116x->lock, flags);	if (!HC_IS_RUNNING(hcd->state)) {		kfree(ep);		ret = -ENODEV;		goto fail;	}	if (hep->hcpriv)		ep = hep->hcpriv;	else {		INIT_LIST_HEAD(&ep->schedule);		ep->udev = udev;		ep->epnum = epnum;		ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);		usb_settoggle(udev, epnum, is_out, 0);		if (type == PIPE_CONTROL) {			ep->nextpid = USB_PID_SETUP;		} else if (is_out) {			ep->nextpid = USB_PID_OUT;		} else {			ep->nextpid = USB_PID_IN;		}		if (urb->interval) {			/*			   With INT URBs submitted, the driver works with SOF			   interrupt enabled and ATL interrupt disabled. After			   the PTDs are written to fifo ram, the chip starts			   fifo processing and usb transfers after the next			   SOF and continues until the transfers are finished			   (succeeded or failed) or the frame ends. Therefore,			   the transfers occur only in every second frame,			   while fifo reading/writing and data processing			   occur in every other second frame. */			if (urb->interval < 2)				urb->interval = 2;			if (urb->interval > 2 * PERIODIC_SIZE)				urb->interval = 2 * PERIODIC_SIZE;			ep->period = urb->interval >> 1;			ep->branch = PERIODIC_SIZE;			ep->load = usb_calc_bus_time(udev->speed,						     !is_out,						     (type == PIPE_ISOCHRONOUS),						     usb_maxpacket(udev, pipe,								   is_out)) /			    1000;		}		hep->hcpriv = ep;		ep->hep = hep;	}	/* maybe put endpoint into schedule */	switch (type) {	case PIPE_CONTROL:	case PIPE_BULK:		if (list_empty(&ep->schedule))			list_add_tail(&ep->schedule, &isp116x->async);		break;	case PIPE_INTERRUPT:		urb->interval = ep->period;		ep->length = min((int)ep->maxpacket,				 urb->transfer_buffer_length);		/* urb submitted for already existing endpoint */		if (ep->branch < PERIODIC_SIZE)			break;		ret = ep->branch = balance(isp116x, ep->period, ep->load);		if (ret < 0)			goto fail;		ret = 0;		urb->start_frame = (isp116x->fmindex & (PERIODIC_SIZE - 1))		    + ep->branch;		/* sort each schedule branch by period (slow before fast)		   to share the faster parts of the tree without needing		   dummy/placeholder nodes */		DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);		for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {			struct isp116x_ep **prev = &isp116x->periodic[i];			struct isp116x_ep *here = *prev;			while (here && ep != here) {				if (ep->period > here->period)					break;				prev = &here->next;				here = *prev;			}			if (ep != here) {				ep->next = here;				*prev = ep;			}			isp116x->load[i] += ep->load;		}		hcd->self.bandwidth_allocated += ep->load / ep->period;		/* switch over to SOFint */		if (!isp116x->periodic_count++) {			isp116x->irqenb &= ~HCuPINT_ATL;			isp116x->irqenb |= HCuPINT_SOF;			isp116x_write_reg16(isp116x, HCuPINTENB,					    isp116x->irqenb);		}	}	/* in case of unlink-during-submit */	spin_lock(&urb->lock);	if (urb->status != -EINPROGRESS) {		spin_unlock(&urb->lock);		finish_request(isp116x, ep, urb, NULL);		ret = 0;		goto fail;	}	urb->hcpriv = hep;	spin_unlock(&urb->lock);	start_atl_transfers(isp116x);      fail:	spin_unlock_irqrestore(&isp116x->lock, flags);	return ret;}/*   Dequeue URBs.*/static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	struct usb_host_endpoint *hep;	struct isp116x_ep *ep, *ep_act;	unsigned long flags;	spin_lock_irqsave(&isp116x->lock, flags);	hep = urb->hcpriv;	/* URB already unlinked (or never linked)? */	if (!hep) {		spin_unlock_irqrestore(&isp116x->lock, flags);		return 0;	}	ep = hep->hcpriv;	WARN_ON(hep != ep->hep);	/* In front of queue? */	if (ep->hep->urb_list.next == &urb->urb_list)		/* active? */		for (ep_act = isp116x->atl_active; ep_act;		     ep_act = ep_act->active)			if (ep_act == ep) {				VDBG("dequeue, urb %p active; wait for irq\n",				     urb);				urb = NULL;				break;			}	if (urb)		finish_request(isp116x, ep, urb, NULL);	spin_unlock_irqrestore(&isp116x->lock, flags);	return 0;}static void isp116x_endpoint_disable(struct usb_hcd *hcd,				     struct usb_host_endpoint *hep){	int i;	struct isp116x_ep *ep = hep->hcpriv;	if (!ep)		return;	/* assume we'd just wait for the irq */	for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++)		msleep(3);	if (!list_empty(&hep->urb_list))		WARN("ep %p not empty?\n", ep);	kfree(ep);	hep->hcpriv = NULL;}static int isp116x_get_frame(struct usb_hcd *hcd){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	u32 fmnum;	unsigned long flags;	spin_lock_irqsave(&isp116x->lock, flags);	fmnum = isp116x_read_reg32(isp116x, HCFMNUM);	spin_unlock_irqrestore(&isp116x->lock, flags);	return (int)fmnum;}/*  Adapted from ohci-hub.c. Currently we don't support autosuspend.*/static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	int ports, i, changed = 0;	unsigned long flags;	if (!HC_IS_RUNNING(hcd->state))		return -ESHUTDOWN;	/* Report no status change now, if we are scheduled to be	   called later */	if (timer_pending(&hcd->rh_timer))		return 0;	ports = isp116x->rhdesca & RH_A_NDP;	spin_lock_irqsave(&isp116x->lock, flags);	isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);	if (isp116x->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))		buf[0] = changed = 1;	else		buf[0] = 0;	for (i = 0; i < ports; i++) {		u32 status = isp116x->rhport[i] =		    isp116x_read_reg32(isp116x, i ? HCRHPORT2 : HCRHPORT1);		if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC			      | RH_PS_OCIC | RH_PS_PRSC)) {			changed = 1;			buf[0] |= 1 << (i + 1);			continue;		}	}	spin_unlock_irqrestore(&isp116x->lock, flags);	return changed;}static void isp116x_hub_descriptor(struct isp116x *isp116x,				   struct usb_hub_descriptor *desc){	u32 reg = isp116x->rhdesca;	desc->bDescriptorType = 0x29;	desc->bDescLength = 9;	desc->bHubContrCurrent = 0;	desc->bNbrPorts = (u8) (reg & 0x3);	/* Power switching, device type, overcurrent. */	desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f));	desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);	/* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */	desc->bitmap[0] = 0;	desc->bitmap[1] = ~0;}/* Perform reset of a given port.   It would be great to just start the reset and let the   USB core to clear the reset in due time. However,   root hub ports should be reset for at least 50 ms, while   our chip stays in reset for about 10 ms. I.e., we must   repeatedly reset it ourself here.*/static inline void root_port_reset(struct isp116x *isp116x, unsigned port){	u32 tmp;	unsigned long flags, t;	/* Root hub reset should be 50 ms, but some devices	   want it even longer. */	t = jiffies + msecs_to_jiffies(100);	while (time_before(jiffies, t)) {		spin_lock_irqsave(&isp116x->lock, flags);		/* spin until any current reset finishes */		for (;;) {			tmp = isp116x_read_reg32(isp116x, port ?						 HCRHPORT2 : HCRHPORT1);			if (!(tmp & RH_PS_PRS))				break;			udelay(500);		}		/* Don't reset a disconnected port */		if (!(tmp & RH_PS_CCS)) {			spin_unlock_irqrestore(&isp116x->lock, flags);			break;		}		/* Reset lasts 10ms (claims datasheet) */		isp116x_write_reg32(isp116x, port ? HCRHPORT2 :				    HCRHPORT1, (RH_PS_PRS));		spin_unlock_irqrestore(&isp116x->lock, flags);		msleep(10);	}}/* Adapted from ohci-hub.c */static int isp116x_hub_control(struct usb_hcd *hcd,			       u16 typeReq,			       u16 wValue, u16 wIndex, char *buf, u16 wLength){	struct isp116x *isp116x = hcd_to_isp116x(hcd);	int ret = 0;	unsigned long flags;	int ports = isp116x->rhdesca & RH_A_NDP;	u32 tmp = 0;	switch (typeReq) {	case ClearHubFeature:		DBG("ClearHubFeature: ");		switch (wValue) {		case C_HUB_OVER_CURRENT:			DBG("C_HUB_OVER_CURRENT\n");			spin_lock_irqsave(&isp116x->lock, flags);			isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC);			spin_unlock_irqrestore(&isp116x->lock, flags);		case C_HUB_LOCAL_POWER:			DBG("C_HUB_LOCAL_POWER\n");			break;		default:			goto error;		}		break;	case SetHubFeature:		DBG("SetHubFeature: ");		switch (wValue) {		case C_HUB_OVER_CURRENT:		case C_HUB_LOCAL_POWER:			DBG("C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n");			break;		default:			goto error;		}		break;	case GetHubDescriptor:		DBG("GetHubDescriptor\n");		isp116x_hub_descriptor(isp116x,				       (struct usb_hub_descriptor *)buf);		break;	case GetHubStatus:		DBG("GetHubStatus\n");		*(__le32 *) buf = 0;		break;	case GetPortStatus:		DBG("GetPortStatus\n");		if (!wIndex || wIndex > ports)			goto error;		tmp = isp116x->rhport[--wIndex];		*(__le32 *) buf = cpu_to_le32(tmp);		DBG("GetPortStatus: port[%d]  %08x\n", wIndex + 1, tmp);		break;	case ClearPortFeature:		DBG("ClearPortFeature: ");		if (!wIndex || wIndex > ports)			goto error;		wIndex--;		switch (wValue) {		case USB_PORT_FEAT_ENABLE:			DBG("USB_PORT_FEAT_ENABLE\n");			tmp = RH_PS_CCS;			break;		case USB_PORT_FEAT_C_ENABLE:			DBG("USB_PORT_FEAT_C_ENABLE\n");			tmp = RH_PS_PESC;			break;		case USB_PORT_FEAT_SUSPEND:			DBG("USB_PORT_FEAT_SUSPEND\n");			tmp = RH_PS_POCI;			break;		case USB_PORT_FEAT_C_SUSPEND:			DBG("USB_PORT_FEAT_C_SUSPEND\n");			tmp = RH_PS_PSSC;			break;		case USB_PORT_FEAT_POWER:			DBG("USB_PORT_FEAT_POWER\n");			tmp = RH_PS_LSDA;			break;		case USB_PORT_FEAT_C_CONNECTION:			DBG("USB_PORT_FEAT_C_CONNECTION\n");			tmp = RH_PS_CSC;			break;		case USB_PORT_FEAT_C_OVER_CURRENT:			DBG("USB_PORT_FEAT_C_OVER_CURRENT\n");			tmp = RH_PS_OCIC;			break;		case USB_PORT_FEAT_C_RESET:			DBG("USB_PORT_FEAT_C_RESET\n");			tmp = RH_PS_PRSC;			break;		default:			goto error;		}		spin_lock_irqsave(&isp116x->lock, flags);		isp116x_write_reg32(isp116x, wIndex				    ? HCRHPORT2 : HCRHPORT1, tmp);		isp116x->rhport[wIndex] =		    isp116x_read_reg32(isp116x, wIndex ? HCRHPORT2 : HCRHPORT1);		spin_unlock_irqrestore(&isp116x->lock, flags);		break;	case SetPortFeature:		DBG("SetPortFeature: ");		if (!wIndex || wIndex > ports)			goto error;		wIndex--;		switch (wValue) {		case USB_PORT_FEAT_SUSPEND:			DBG("USB_PORT_FEAT_SUSPEND\n");			spin_lock_irqsave(&isp116x->lock, flags);			isp116x_write_reg32(isp116x, wIndex					    ? HCRHPORT2 : HCRHPORT1, RH_PS_PSS);			break;		case USB_PORT_FEAT_POWER:			DBG("USB_PORT_FEAT_POWER\n");			spin_lock_irqsave(&isp116x->lock, flags);			isp116x_write_reg32(isp116x, wIndex					    ? HCRHPORT2 : HCRHPORT1, RH_PS_PPS);			break;		case USB_PORT_FEAT_RESET:			DBG("USB_PORT_FEAT_RESET\n");			root_port_reset(isp116x, wIndex);			spin_lock_irqsave(&isp116x->lock, flags);			break;		default:			goto error;		}		isp116x->rhport[wIndex] =		    isp116x_read_reg32(isp116x, wIndex ? HCRHPORT2 : HCRHPORT1);		spin_unlock_irqrestore(&isp116x->lock, flags);		break;	default:	      error:		/* "protocol stall" on error */		DBG("PROTOCOL STALL\n");		ret = -EPIPE;	}	return ret;}/*-----------------------------------------------------------------*/#ifdef CONFIG_DEBUG_FSstatic void dump_irq(struct seq_file *s, char *label, u16 mask){	seq_printf(s, "%s %04x%s%s%s%s%s%s\n", label, mask,		   mask & HCuPINT_CLKRDY ? " clkrdy" : "",		   mask & HCuPINT_SUSP ? " susp" : "",		   mask & HCuPINT_OPR ? " opr" : "",		   mask & HCuPINT_AIIEOT ? " eot" : "",		   mask & HCuPINT_ATL ? " atl" : "",		   mask & HCuPINT_SOF ? " sof" : "");

⌨️ 快捷键说明

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