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

📄 ohci-q.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
		}		break;	// case PIPE_INTERRUPT:	// case PIPE_ISOCHRONOUS:	default:		periodic_unlink (ohci, ed);		break;	}}/*-------------------------------------------------------------------------*//* get and maybe (re)init an endpoint. init _should_ be done only as part * of enumeration, usb_set_configuration() or usb_set_interface(). */static struct ed *ed_get (	struct ohci_hcd		*ohci,	struct usb_host_endpoint *ep,	struct usb_device	*udev,	unsigned int		pipe,	int			interval) {	struct ed		*ed; 	unsigned long		flags;	spin_lock_irqsave (&ohci->lock, flags);	if (!(ed = ep->hcpriv)) {		struct td	*td;		int		is_out;		u32		info;		ed = ed_alloc (ohci, GFP_ATOMIC);		if (!ed) {			/* out of memory */			goto done;		}  		/* dummy td; end of td list for ed */		td = td_alloc (ohci, GFP_ATOMIC); 		if (!td) {			/* out of memory */			ed_free (ohci, ed);			ed = NULL;			goto done;		}		ed->dummy = td;		ed->hwTailP = cpu_to_hc32 (ohci, td->td_dma);		ed->hwHeadP = ed->hwTailP;	/* ED_C, ED_H zeroed */		ed->state = ED_IDLE;		is_out = !(ep->desc.bEndpointAddress & USB_DIR_IN);		/* FIXME usbcore changes dev->devnum before SET_ADDRESS		 * suceeds ... otherwise we wouldn't need "pipe".		 */		info = usb_pipedevice (pipe);		ed->type = usb_pipetype(pipe);		info |= (ep->desc.bEndpointAddress & ~USB_DIR_IN) << 7;		info |= le16_to_cpu(ep->desc.wMaxPacketSize) << 16;		if (udev->speed == USB_SPEED_LOW)			info |= ED_LOWSPEED;		/* only control transfers store pids in tds */		if (ed->type != PIPE_CONTROL) {			info |= is_out ? ED_OUT : ED_IN;			if (ed->type != PIPE_BULK) {				/* periodic transfers... */				if (ed->type == PIPE_ISOCHRONOUS)					info |= ED_ISO;				else if (interval > 32)	/* iso can be bigger */					interval = 32;				ed->interval = interval;				ed->load = usb_calc_bus_time (					udev->speed, !is_out,					ed->type == PIPE_ISOCHRONOUS,					le16_to_cpu(ep->desc.wMaxPacketSize))						/ 1000;			}		}		ed->hwINFO = cpu_to_hc32(ohci, info);		ep->hcpriv = ed;	}done:	spin_unlock_irqrestore (&ohci->lock, flags);	return ed; }/*-------------------------------------------------------------------------*//* request unlinking of an endpoint from an operational HC. * put the ep on the rm_list * real work is done at the next start frame (SF) hardware interrupt * caller guarantees HCD is running, so hardware access is safe, * and that ed->state is ED_OPER */static void start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed){    	ed->hwINFO |= cpu_to_hc32 (ohci, ED_DEQUEUE);	ed_deschedule (ohci, ed);	/* rm_list is just singly linked, for simplicity */	ed->ed_next = ohci->ed_rm_list;	ed->ed_prev = NULL;	ohci->ed_rm_list = ed;	/* enable SOF interrupt */	ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);	ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);	// flush those writes, and get latest HCCA contents	(void) ohci_readl (ohci, &ohci->regs->control);	/* SF interrupt might get delayed; record the frame counter value that	 * indicates when the HC isn't looking at it, so concurrent unlinks	 * behave.  frame_no wraps every 2^16 msec, and changes right before	 * SF is triggered.	 */	ed->tick = ohci_frame_no(ohci) + 1;}/*-------------------------------------------------------------------------* * TD handling functions *-------------------------------------------------------------------------*//* enqueue next TD for this URB (OHCI spec 5.2.8.2) */static voidtd_fill (struct ohci_hcd *ohci, u32 info,	dma_addr_t data, int len,	struct urb *urb, int index){	struct td		*td, *td_pt;	struct urb_priv		*urb_priv = urb->hcpriv;	int			is_iso = info & TD_ISO;	int			hash;	// ASSERT (index < urb_priv->length);	/* aim for only one interrupt per urb.  mostly applies to control	 * and iso; other urbs rarely need more than one TD per urb.	 * this way, only final tds (or ones with an error) cause IRQs.	 * at least immediately; use DI=6 in case any control request is	 * tempted to die part way through.  (and to force the hc to flush	 * its donelist soonish, even on unlink paths.)	 *	 * NOTE: could delay interrupts even for the last TD, and get fewer	 * interrupts ... increasing per-urb latency by sharing interrupts.	 * Drivers that queue bulk urbs may request that behavior.	 */	if (index != (urb_priv->length - 1)			|| (urb->transfer_flags & URB_NO_INTERRUPT))		info |= TD_DI_SET (6);	/* use this td as the next dummy */	td_pt = urb_priv->td [index];	/* fill the old dummy TD */	td = urb_priv->td [index] = urb_priv->ed->dummy;	urb_priv->ed->dummy = td_pt;	td->ed = urb_priv->ed;	td->next_dl_td = NULL;	td->index = index;	td->urb = urb; 	td->data_dma = data;	if (!len)		data = 0;	td->hwINFO = cpu_to_hc32 (ohci, info);	if (is_iso) {		td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000);		*ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci,						(data & 0x0FFF) | 0xE000);		td->ed->last_iso = info & 0xffff;	} else {		td->hwCBP = cpu_to_hc32 (ohci, data); 	}				if (data)		td->hwBE = cpu_to_hc32 (ohci, data + len - 1);	else		td->hwBE = 0;	td->hwNextTD = cpu_to_hc32 (ohci, td_pt->td_dma);	/* append to queue */	list_add_tail (&td->td_list, &td->ed->td_list);	/* hash it for later reverse mapping */	hash = TD_HASH_FUNC (td->td_dma);	td->td_hash = ohci->td_hash [hash];	ohci->td_hash [hash] = td;	/* HC might read the TD (or cachelines) right away ... */	wmb ();	td->ed->hwTailP = td->hwNextTD;}/*-------------------------------------------------------------------------*//* Prepare all TDs of a transfer, and queue them onto the ED. * Caller guarantees HC is active. * Usually the ED is already on the schedule, so TDs might be * processed as soon as they're queued. */static void td_submit_urb (	struct ohci_hcd	*ohci,	struct urb	*urb) {	struct urb_priv	*urb_priv = urb->hcpriv;	dma_addr_t	data;	int		data_len = urb->transfer_buffer_length;	int		cnt = 0;	u32		info = 0;	int		is_out = usb_pipeout (urb->pipe);	int		periodic = 0;	/* OHCI handles the bulk/interrupt data toggles itself.  We just	 * use the device toggle bits for resetting, and rely on the fact	 * that resetting toggle is meaningless if the endpoint is active.	 */  	if (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) {		usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),			is_out, 1);		urb_priv->ed->hwHeadP &= ~cpu_to_hc32 (ohci, ED_C);	}	urb_priv->td_cnt = 0;	list_add (&urb_priv->pending, &ohci->pending);	if (data_len)		data = urb->transfer_dma;	else		data = 0;	/* NOTE:  TD_CC is set so we can tell which TDs the HC processed by	 * using TD_CC_GET, as well as by seeing them on the done list.	 * (CC = NotAccessed ... 0x0F, or 0x0E in PSWs for ISO.)	 */	switch (urb_priv->ed->type) {	/* Bulk and interrupt are identical except for where in the schedule	 * their EDs live.	 */	case PIPE_INTERRUPT:		/* ... and periodic urbs have extra accounting */		periodic = ohci_to_hcd(ohci)->self.bandwidth_int_reqs++ == 0			&& ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0;		/* FALLTHROUGH */	case PIPE_BULK:		info = is_out			? TD_T_TOGGLE | TD_CC | TD_DP_OUT			: TD_T_TOGGLE | TD_CC | TD_DP_IN;		/* TDs _could_ transfer up to 8K each */		while (data_len > 4096) {			td_fill (ohci, info, data, 4096, urb, cnt);			data += 4096;			data_len -= 4096;			cnt++;		}		/* maybe avoid ED halt on final TD short read */		if (!(urb->transfer_flags & URB_SHORT_NOT_OK))			info |= TD_R;		td_fill (ohci, info, data, data_len, urb, cnt);		cnt++;		if ((urb->transfer_flags & URB_ZERO_PACKET)				&& cnt < urb_priv->length) {			td_fill (ohci, info, 0, 0, urb, cnt);			cnt++;		}		/* maybe kickstart bulk list */		if (urb_priv->ed->type == PIPE_BULK) {			wmb ();			ohci_writel (ohci, OHCI_BLF, &ohci->regs->cmdstatus);		}		break;	/* control manages DATA0/DATA1 toggle per-request; SETUP resets it,	 * any DATA phase works normally, and the STATUS ack is special.	 */	case PIPE_CONTROL:		info = TD_CC | TD_DP_SETUP | TD_T_DATA0;		td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);		if (data_len > 0) {			info = TD_CC | TD_R | TD_T_DATA1;			info |= is_out ? TD_DP_OUT : TD_DP_IN;			/* NOTE:  mishandles transfers >8K, some >4K */			td_fill (ohci, info, data, data_len, urb, cnt++);		}		info = (is_out || data_len == 0)			? TD_CC | TD_DP_IN | TD_T_DATA1			: TD_CC | TD_DP_OUT | TD_T_DATA1;		td_fill (ohci, info, data, 0, urb, cnt++);		/* maybe kickstart control list */		wmb ();		ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus);		break;	/* ISO has no retransmit, so no toggle; and it uses special TDs.	 * Each TD could handle multiple consecutive frames (interval 1);	 * we could often reduce the number of TDs here.	 */	case PIPE_ISOCHRONOUS:		for (cnt = 0; cnt < urb->number_of_packets; cnt++) {			int	frame = urb->start_frame;			// FIXME scheduling should handle frame counter			// roll-around ... exotic case (and OHCI has			// a 2^16 iso range, vs other HCs max of 2^10)			frame += cnt * urb->interval;			frame &= 0xffff;			td_fill (ohci, TD_CC | TD_ISO | frame,				data + urb->iso_frame_desc [cnt].offset,				urb->iso_frame_desc [cnt].length, urb, cnt);		}		periodic = ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs++ == 0			&& ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0;		break;	}	/* start periodic dma if needed */	if (periodic) {		wmb ();		ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE;		ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);	}	// ASSERT (urb_priv->length == cnt);}/*-------------------------------------------------------------------------* * Done List handling functions *-------------------------------------------------------------------------*//* calculate transfer length/status and update the urb * PRECONDITION:  irqsafe (only for urb->status locking) */static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td){	u32	tdINFO = hc32_to_cpup (ohci, &td->hwINFO);	int	cc = 0;	list_del (&td->td_list);	/* ISO ... drivers see per-TD length/status */  	if (tdINFO & TD_ISO) { 		u16	tdPSW = ohci_hwPSW (ohci, td, 0);		int	dlen = 0;		/* NOTE:  assumes FC in tdINFO == 0, and that		 * only the first of 0..MAXPSW psws is used.		 */ 		cc = (tdPSW >> 12) & 0xF;  		if (tdINFO & TD_CC)	/* hc didn't touch? */			return;		if (usb_pipeout (urb->pipe))			dlen = urb->iso_frame_desc [td->index].length;		else {			/* short reads are always OK for ISO */			if (cc == TD_DATAUNDERRUN)				cc = TD_CC_NOERROR;			dlen = tdPSW & 0x3ff;		}		urb->actual_length += dlen;		urb->iso_frame_desc [td->index].actual_length = dlen;		urb->iso_frame_desc [td->index].status = cc_to_error [cc];		if (cc != TD_CC_NOERROR)

⌨️ 快捷键说明

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