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

📄 usb-uhci-q.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 3 页
字号:
/*  
    UHCI HCD (Host Controller Driver) for USB, UHCI transfer processing
    
    (c) 1999-2002 
    Georg Acher      +    Deti Fliegl    +    Thomas Sailer
    georg@acher.org      deti@fliegl.de   sailer@ife.ee.ethz.ch
   
    with the help of
    David Brownell, david-b@pacbell.net
    Adam Richter, adam@yggdrasil.com
    Roman Weissgaerber, weissg@vienna.at
    
    HW-initalization based on material of
    Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds 

    $Id: usb-uhci-q.c,v 1.3 2002/05/25 16:42:41 acher Exp $
*/

/*-------------------------------------------------------------------*/
static inline void finish_urb (struct uhci_hcd *uhci, struct urb *urb)
{
	if (urb->hcpriv) 
		uhci_free_priv (uhci, urb, urb->hcpriv);
	
	usb_hcd_giveback_urb (&uhci->hcd, urb);
}
/*###########################################################################*/
//                        URB SUBMISSION STUFF
//     assembles QHs und TDs for control, bulk, interrupt  and isochronous
/*###########################################################################*/

// returns: 0 (no transfer queued), urb* (this urb already queued) 
static struct urb* search_dev_ep (struct uhci_hcd *uhci, struct urb *urb)
{
	struct list_head *p;
	struct urb *tmp;
	urb_priv_t *priv;
	unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);

	p=uhci->urb_list.next;

	for (; p != &uhci->urb_list; p = p->next) {
		priv = list_entry (p,  urb_priv_t, urb_list);
		tmp = priv->urb;
		dbg("search_dev_ep urb: %p", tmp);
		// we can accept this urb if it is not queued at this time 
		// or if non-iso transfer requests should be scheduled for the same device and pipe
		if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
		    (urb == tmp))
			return tmp;	// found another urb already queued for processing
	}
	return 0;
}
/*-------------------------------------------------------------------*/

static int uhci_submit_control_urb (struct uhci_hcd *uhci, struct urb *urb)
{
	uhci_desc_t *qh, *td;
	urb_priv_t *urb_priv = urb->hcpriv;
	unsigned long destination, status;
	int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
	int depth_first = ctrl_depth;  // UHCI descriptor chasing method
	unsigned long len;
	char *data;

//	err("uhci_submit_control start, buf %p", urb->transfer_buffer);
	if (alloc_qh (uhci, &qh))		// alloc qh for this request
		return -ENOMEM;

	if (alloc_td (uhci, &td, UHCI_PTR_DEPTH * depth_first))		// get td for setup stage
		goto fail_unmap_enomem;

	/* The "pipe" thing contains the destination in bits 8--18 */
	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
	
	status = TD_CTRL_ACTIVE
		| (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD)
		| (3 << 27);                      /* 3 errors */

	if (urb->dev->speed == USB_SPEED_LOW)
		status |= TD_CTRL_LS;

	/*  Build the TD for the control request, try forever, 8 bytes of data */
	fill_td (td, status, destination | (7 << 21), urb_priv->setup_packet_dma);

	insert_td (uhci, qh, td, 0);	// queue 'setup stage'-td in qh
#if 0
	{
		char *sp=urb->setup_packet;
		dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
		    sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
	}
	//uhci_show_td(td);
#endif

	len = urb->transfer_buffer_length;
	data = urb->transfer_buffer;

	/* If direction is "send", change the frame from SETUP (0x2D)
	   to OUT (0xE1). Else change it from SETUP to IN (0x69). */

	destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);

	while (len > 0) {
		int pktsze = len;

		if (alloc_td (uhci, &td, UHCI_PTR_DEPTH * depth_first))
			goto fail_unmap_enomem;

		if (pktsze > maxsze)
			pktsze = maxsze;

		destination ^= 1 << TD_TOKEN_TOGGLE;	// toggle DATA0/1

		// Status, pktsze bytes of data
		fill_td (td, status, destination | ((pktsze - 1) << 21),
			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));

		insert_td (uhci, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue 'data stage'-td in qh

		data += pktsze;
		len -= pktsze;
	}

	/* Build the final TD for control status 
	   It's only IN if the pipe is out AND we aren't expecting data */

	destination &= ~UHCI_PID;

	if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
		destination |= USB_PID_IN;
	else
		destination |= USB_PID_OUT;

	destination |= 1 << TD_TOKEN_TOGGLE;	/* End in Data1 */

	if (alloc_td (uhci, &td, UHCI_PTR_DEPTH))
		goto fail_unmap_enomem;

	status &=~TD_CTRL_SPD;

	/* no limit on errors on final packet, 0 bytes of data */
	fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21), 0);

	insert_td (uhci, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue status td

	list_add (&qh->desc_list, &urb_priv->desc_list);

	queue_urb (uhci, urb);	// queue _before_ inserting in desc chain

	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);

	/* Start it up... put low speed first */
	if (urb->dev->speed == USB_SPEED_LOW)
		insert_qh (uhci, uhci->control_chain, qh, 0);
	else
		insert_qh (uhci, uhci->bulk_chain, qh, 0);

	return 0;

fail_unmap_enomem:
	delete_qh(uhci, qh);
	return -ENOMEM;
}
/*-------------------------------------------------------------------*/
// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!

static int uhci_submit_bulk_urb (struct uhci_hcd *uhci, struct urb *urb, struct urb *bulk_urb)
{
	urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
	uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL;
	unsigned long destination, status;
	char *data;
	unsigned int pipe = urb->pipe;
	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
	int info, len, last;
	int depth_first = bulk_depth;  // UHCI descriptor chasing method

	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
		return -EPIPE;

	queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
		  urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);

	upriv = (urb_priv_t*)urb->hcpriv;

	if (!bulk_urb) {
		if (alloc_qh (uhci, &qh))		// get qh for this request
			return -ENOMEM;

		if (urb->transfer_flags & USB_QUEUE_BULK) {
			if (alloc_qh(uhci, &nqh)) // placeholder for clean unlink
				goto fail_unmap_enomem;
			upriv->next_qh = nqh;
			queue_dbg("new next qh %p",nqh);
		}
	}
	else { 
		bpriv = (urb_priv_t*)bulk_urb->hcpriv;
		qh = bpriv->bottom_qh;  // re-use bottom qh and next qh
		nqh = bpriv->next_qh;
		upriv->next_qh=nqh;	
		upriv->prev_queued_urb=bulk_urb;
	}

	if (urb->transfer_flags & USB_QUEUE_BULK) {
		if (alloc_qh (uhci, &bqh))  // "bottom" QH
			goto fail_unmap_enomem;

		set_qh_element(bqh, UHCI_PTR_TERM);
		set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element
		upriv->bottom_qh = bqh;
	}
	queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh);

	/* The "pipe" thing contains the destination in bits 8--18. */
	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
	
	status = TD_CTRL_ACTIVE
		| ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD)
		| (3 << 27);                   /* 3 errors */

	if (urb->dev->speed == USB_SPEED_LOW)
		status |= TD_CTRL_LS;

	/* Build the TDs for the bulk request */
	len = urb->transfer_buffer_length;
	data = urb->transfer_buffer;
	
	do {					// TBD: Really allow zero-length packets?
		int pktsze = len;

		if (alloc_td (uhci, &td, UHCI_PTR_DEPTH * depth_first))
			goto fail_unmap_enomem;

		if (pktsze > maxsze)
			pktsze = maxsze;

		// pktsze bytes of data 
		info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) |
			(uhci_get_toggle (urb) << TD_TOKEN_TOGGLE);

		fill_td (td, status, info,
			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));

		data += pktsze;
		len -= pktsze;
		// Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet
		last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET)));

		if (last)
			set_td_ioc(td);	// last one generates INT

		insert_td (uhci, qh, td, UHCI_PTR_DEPTH * depth_first);
		if (!first_td)
			first_td=td;
		uhci_do_toggle (urb);

	} while (!last);

	if (bulk_urb && bpriv)   // everything went OK, link with old bulk URB
		bpriv->next_queued_urb=urb;

	list_add (&qh->desc_list, &urb_priv->desc_list);

	if (urb->transfer_flags & USB_QUEUE_BULK)
		append_qh(uhci, td, bqh, UHCI_PTR_DEPTH * depth_first);

	queue_urb_unlocked (uhci, urb);
	
	if (urb->transfer_flags & USB_QUEUE_BULK)
		set_qh_element(qh, first_td->dma_addr);
	else
		qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);    // arm QH

	if (!bulk_urb) { 					// new bulk queue	
		if (urb->transfer_flags & USB_QUEUE_BULK) {
			spin_lock (&uhci->td_lock);		// both QHs in one go
			insert_qh (uhci, uhci->chain_end, qh, 0);	// Main QH
			insert_qh (uhci, uhci->chain_end, nqh, 0);	// Helper QH
			spin_unlock (&uhci->td_lock);
		}
		else
			insert_qh (uhci, uhci->chain_end, qh, 0);
	}
	
	//dbg("uhci_submit_bulk_urb: exit\n");
	return 0;

fail_unmap_enomem:
	delete_qh(uhci, qh);
	if (bqh) 
		delete_qh(uhci, bqh);
	if (!bulk_urb && nqh)
		delete_qh(uhci, nqh);

	return -ENOMEM;
}
/*---------------------------------------------------------------------------*/
// submits USB interrupt (ie. polling ;-) 
// ASAP-flag set implicitely
// if period==0, the transfer is only done once

static int uhci_submit_int_urb (struct uhci_hcd *uhci, struct urb *urb)
{
	urb_priv_t *urb_priv = urb->hcpriv;
	int nint;
	uhci_desc_t *td;
	int status, destination;
	int info;
	unsigned int pipe = urb->pipe;
	
	if (urb->interval == 0)
		nint = 0;
	else {  
		// log2-function (urb->interval already 2^n)
		nint = ffs(urb->interval);
		if (nint>7)
			nint=7;
	}

	dbg("INT-interval %i, chain  %i", urb->interval, nint);
	// remember start frame, just in case...
	urb->start_frame = UHCI_GET_CURRENT_FRAME (uhci) & 1023;	

	urb->number_of_packets = 1;  // INT allows only one packet
	
	if (alloc_td (uhci, &td, UHCI_PTR_DEPTH))
		return -ENOMEM;

	status = TD_CTRL_ACTIVE | TD_CTRL_IOC
		| (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD)
		| (3 << 27);

	if (urb->dev->speed == USB_SPEED_LOW)
		status |= TD_CTRL_LS;

	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe) |
		(((urb->transfer_buffer_length - 1) & 0x7ff) << 21);

	info = destination | (uhci_get_toggle (urb) << TD_TOKEN_TOGGLE);
	fill_td (td, status, info, urb_priv->transfer_buffer_dma);
	list_add_tail (&td->desc_list, &urb_priv->desc_list);
	queue_urb (uhci, urb);
	insert_td_horizontal (uhci, uhci->int_chain[nint], td);	// store in INT-TDs
	uhci_do_toggle (urb);

	return 0;
}
/*###########################################################################*/
//                  ISOCHRONOUS TRANSFERS
/*###########################################################################*/

// In case of ASAP iso transfer, search the URB-list for already queued URBs
// for this EP and calculate the earliest start frame for the new
// URB (easy seamless URB continuation!)
static int find_iso_limits (struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
{
	struct urb *u, *last_urb = NULL;
	urb_priv_t *priv;
	struct list_head *p;
	int ret=-1;
	unsigned long flags;
	
	spin_lock_irqsave (&uhci->urb_list_lock, flags);
	p=uhci->urb_list.prev;

	for (; p != &uhci->urb_list; p = p->prev) {
		priv = list_entry (p, urb_priv_t, urb_list);
		u = priv->urb;
		// look for pending URBs with identical pipe handle
		// works only because iso doesn't toggle the data bit!
		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) {
			if (!last_urb)
				*start = u->start_frame;
			last_urb = u;
		}
	}
	
	if (last_urb) {
		*end = (last_urb->start_frame + last_urb->number_of_packets*last_urb->interval) & 1023;
		ret=0;
	}
	
	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
	
	return ret;
}
/*-------------------------------------------------------------------*/
// adjust start_frame according to scheduling constraints (ASAP etc)

static int iso_find_start (struct uhci_hcd *uhci, struct urb *urb)
{
	unsigned int now;
	unsigned int start_limit = 0, stop_limit = 0, queued_size, number_of_frames;
	int limits;

	now = UHCI_GET_CURRENT_FRAME (uhci) & 1023;

	number_of_frames = (unsigned) (urb->number_of_packets*urb->interval);

	if ( number_of_frames > 900)
		return -EFBIG;
	
	limits = find_iso_limits (uhci, urb, &start_limit, &stop_limit);
	queued_size = (stop_limit - start_limit) & 1023;

	if (urb->transfer_flags & USB_ISO_ASAP) {
		// first iso
		if (limits) {
			// 10ms setup should be enough //FIXME!
			urb->start_frame = (now + 10) & 1023;
		}
		else {
			urb->start_frame = stop_limit;		// seamless linkage

			if (((now - urb->start_frame) & 1023) <= (unsigned) number_of_frames) {
				info("iso_find_start: gap in seamless isochronous scheduling");
				dbg("iso_find_start: now %u start_frame %u number_of_packets %u interval %u pipe 0x%08x",
					now, urb->start_frame, urb->number_of_packets, urb->interval, urb->pipe);
				urb->start_frame = (now + 5) & 1023;	// 5ms setup should be enough
			}
		}
	}
	else {
		urb->start_frame &= 1023;
		if (((now - urb->start_frame) & 1023) < number_of_frames) {

⌨️ 快捷键说明

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