ohci-q.c

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

C
1,097
字号
	// 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 usb_set_configuration() or usb_set_interface() ... but the USB stack * isn't very stateful, so we re-init whenever the HC isn't looking. */static struct ed *ed_get (	struct ohci_hcd		*ohci,	struct usb_device	*udev,	unsigned int		pipe,	int			interval) {	int			is_out = !usb_pipein (pipe);	int			type = usb_pipetype (pipe);	struct hcd_dev		*dev = (struct hcd_dev *) udev->hcpriv;	struct ed		*ed; 	unsigned		ep;	unsigned long		flags;	ep = usb_pipeendpoint (pipe) << 1;	if (type != PIPE_CONTROL && is_out)		ep |= 1;	spin_lock_irqsave (&ohci->lock, flags);	if (!(ed = dev->ep [ep])) {		struct td	*td;		ed = ed_alloc (ohci, GFP_ATOMIC);		if (!ed) {			/* out of memory */			goto done;		}		dev->ep [ep] = ed;  		/* 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_le32 (td->td_dma);		ed->hwHeadP = ed->hwTailP;	/* ED_C, ED_H zeroed */		ed->state = ED_IDLE;		ed->type = type;	}	/* NOTE: only ep0 currently needs this "re"init logic, during	 * enumeration (after set_address).	 */  	if (ed->state == ED_IDLE) {		u32	info;		__le32	hw_info;		info = usb_pipedevice (pipe);		info |= (ep >> 1) << 7;		info |= usb_maxpacket (udev, pipe, is_out) << 16;		hw_info = cpu_to_le32 (info);		if (udev->speed == USB_SPEED_LOW)			hw_info |= ED_LOWSPEED;		/* only control transfers store pids in tds */		if (type != PIPE_CONTROL) {			hw_info |= is_out ? ED_OUT : ED_IN;			if (type != PIPE_BULK) {				/* periodic transfers... */				if (type == PIPE_ISOCHRONOUS)					hw_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,					type == PIPE_ISOCHRONOUS,					usb_maxpacket (udev, pipe, is_out))						/ 1000;			}		}		ed->hwINFO = hw_info;	}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 |= 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 */	writel (OHCI_INTR_SF, &ohci->regs->intrstatus);	writel (OHCI_INTR_SF, &ohci->regs->intrenable);	// flush those writes, and get latest HCCA contents	(void) ohci_readl (&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->hcca) + 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_le32 (info);	if (is_iso) {		td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);		td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);		td->ed->last_iso = info & 0xffff;	} else {		td->hwCBP = cpu_to_le32 (data); 	}				if (data)		td->hwBE = cpu_to_le32 (data + len - 1);	else		td->hwBE = 0;	td->hwNextTD = cpu_to_le32 (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 &= ~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 = hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs++ == 0			&& hcd_to_bus (&ohci->hcd)->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 ();			writel (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			? 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 ();		writel (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 = hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs++ == 0			&& hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs == 0;		break;	}	/* start periodic dma if needed */	if (periodic) {		wmb ();		ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE;		writel (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 = le32_to_cpup (&td->hwINFO);	int	cc = 0;	list_del (&td->td_list);	/* ISO ... drivers see per-TD length/status */  	if (tdINFO & TD_ISO) { 		u16	tdPSW = le16_to_cpu (td->hwPSW [0]);		int	dlen = 0;		/* NOTE:  assumes FC in tdINFO == 0 (and MAXPSW == 1) */ 		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 */

⌨️ 快捷键说明

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