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

📄 usb-uhci.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
// mode: 0: unlink but no deletion mark (step 1 of async_unlink)//       1: regular (unlink/delete-mark)//       2: deletion mark for QH (step 2 of async_unlink)// looks a bit complicated because of all the bulk queueing goodies_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode){	uhci_desc_t *bqh, *nqh, *prevqh, *prevtd;	int now;	urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;	now=UHCI_GET_CURRENT_FRAME(s);	bqh=priv->bottom_qh;			if (!priv->next_queued_urb)  { // no more appended bulk queues		queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p",urb, qh, bqh, priv->next_qh);				if (priv->prev_queued_urb) {  // qh not top of the queue			urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;			if (mode != 2) {				unsigned long flags;								spin_lock_irqsave (&s->qh_lock, flags);				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);				prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical);				prevtd->hw.td.link = virt_to_bus(priv->bottom_qh) | UHCI_PTR_QH; // skip current qh				mb();				queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd);				spin_unlock_irqrestore (&s->qh_lock, flags);				ppriv->bottom_qh = priv->bottom_qh;				ppriv->next_queued_urb = NULL;			}		}		else {   // queue is dead, qh is top of the queue						if (mode!=2)				unlink_qh(s, qh); // remove qh from horizontal chain			if (bqh) {  // remove remainings of bulk queue				nqh=priv->next_qh;				if (mode != 2) 					unlink_qh(s, nqh);  // remove nqh from horizontal chain								if (mode) {					nqh->last_used = bqh->last_used = now;					list_add_tail (&nqh->horizontal, &s->free_desc);					list_add_tail (&bqh->horizontal, &s->free_desc);				}						}		}	}	else { // there are queued urbs following		  queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p",		       urb, priv->prev_queued_urb,  priv->next_queued_urb, qh, bqh, priv->next_qh);	       			if (mode !=2) {	// no work for cleanup at unlink-completion			urb_t *nurb;			unsigned long flags;			nurb = priv->next_queued_urb;			spin_lock_irqsave (&s->qh_lock, flags);					if (!priv->prev_queued_urb) { // top QH								prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);				prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;				list_del (&qh->horizontal);  // remove this qh form horizontal chain				list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain			}			else {		// intermediate QH				urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;				urb_priv_t* npriv=(urb_priv_t*)nurb->hcpriv;				uhci_desc_t * bnqh;								bnqh = list_entry (npriv->desc_list.next, uhci_desc_t, desc_list);				ppriv->bottom_qh = bnqh;				ppriv->next_queued_urb = nurb;								prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);				prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;			}			mb();			spin_unlock_irqrestore (&s->qh_lock, flags);			((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;		}			}	if (mode) {		qh->last_used = now;			list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion/kfree	}}/*-------------------------------------------------------------------*/// Release bandwidth for Interrupt or Isoc. transfers _static void uhci_release_bandwidth(urb_t *urb){       	if (urb->bandwidth) {		switch (usb_pipetype(urb->pipe)) {		case PIPE_INTERRUPT:			usb_release_bandwidth (urb->dev, urb, 0);			break;		case PIPE_ISOCHRONOUS:			usb_release_bandwidth (urb->dev, urb, 1);			break;		default:			break;		}	}	}/*-------------------------------------------------------------------*/// unlinks an urb by dequeuing its qh, waits some frames and forgets it_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb){	uhci_desc_t *qh;	urb_priv_t *urb_priv;	unsigned long flags=0;	struct usb_device *usb_dev;	spin_lock_irqsave (&s->urb_list_lock, flags);	if (!in_interrupt())		// shouldn't be called from interrupt at all...		spin_lock(&urb->lock); 		if (urb->status == -EINPROGRESS) {		// URB probably still in work		dequeue_urb (s, urb);		uhci_switch_timer_int(s);		s->unlink_urb_done=1;		uhci_release_bandwidth(urb);		urb->status = -ENOENT;	// mark urb as killed				if (!in_interrupt())				spin_unlock(&urb->lock); 		spin_unlock_irqrestore (&s->urb_list_lock, flags);						urb_priv = urb->hcpriv;		switch (usb_pipetype (urb->pipe)) {		case PIPE_INTERRUPT:			usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));		case PIPE_ISOCHRONOUS:			uhci_clean_iso_step1(s, urb_priv);			uhci_wait_ms(1);			uhci_clean_iso_step2(s, urb_priv);			break;		case PIPE_BULK:		case PIPE_CONTROL:			spin_lock_irqsave (&s->urb_list_lock, flags);			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);			uhci_clean_transfer(s, urb, qh, 1);			spin_unlock_irqrestore (&s->urb_list_lock, flags);			uhci_wait_ms(1);		}		#ifdef DEBUG_SLAB		kmem_cache_free (urb_priv_kmem, urb->hcpriv);#else		kfree (urb->hcpriv);#endif		usb_dev = urb->dev;		if (urb->complete) {			dbg("unlink_urb: calling completion");			urb->dev = NULL;			urb->complete ((struct urb *) urb);		}		usb_dec_dev_use (usb_dev);	}	else {		if (!in_interrupt())				spin_unlock(&urb->lock); 		spin_unlock_irqrestore (&s->urb_list_lock, flags);	}	return 0;}/*-------------------------------------------------------------------*/// async unlink_urb completion/cleanup work// has to be protected by urb_list_lock!// features: if set in transfer_flags, the resulting status of the killed// transaction is not overwritten_static void uhci_cleanup_unlink(uhci_t *s, int force){	struct list_head *q;	urb_t *urb;	struct usb_device *dev;	int pipe,now;	urb_priv_t *urb_priv;	q=s->urb_unlinked.next;	now=UHCI_GET_CURRENT_FRAME(s);	while (q != &s->urb_unlinked) {		urb = list_entry (q, urb_t, urb_list);		urb_priv = (urb_priv_t*)urb->hcpriv;		q = urb->urb_list.next;				if (force || 		    ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) {			async_dbg("async cleanup %p",urb);			switch (usb_pipetype (urb->pipe)) { // process descriptors			case PIPE_CONTROL:				process_transfer (s, urb, 2);  // 2: don't unlink (already done)				break;			case PIPE_BULK:				if (!s->avoid_bulk.counter)					process_transfer (s, urb, 2); // don't unlink (already done)				else					continue;				break;			case PIPE_ISOCHRONOUS:				process_iso (s, urb, 1); // force, don't unlink				break;			case PIPE_INTERRUPT:				process_interrupt (s, urb);				break;			}			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))		  		urb->status = -ECONNRESET; // mark as asynchronously killed			pipe = urb->pipe;		// completion may destroy all...			dev = urb->dev;			urb_priv = urb->hcpriv;			if (urb->complete) {				spin_unlock(&s->urb_list_lock);				urb->dev = NULL;				urb->complete ((struct urb *) urb);				spin_lock(&s->urb_list_lock);			}			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))				urb->status = -ENOENT;  // now the urb is really dead			switch (usb_pipetype (pipe)) {			case PIPE_ISOCHRONOUS:			case PIPE_INTERRUPT:				uhci_clean_iso_step2(s, urb_priv);				break;			}				usb_dec_dev_use (dev);#ifdef DEBUG_SLAB			kmem_cache_free (urb_priv_kmem, urb_priv);#else			kfree (urb_priv);#endif			list_del (&urb->urb_list);		}	}}/*-------------------------------------------------------------------*/// needs urb_list_lock!_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb){	uhci_desc_t *qh;	urb_priv_t *urb_priv;		async_dbg("unlink_urb_async called %p",urb);	if ((urb->status == -EINPROGRESS) ||	    ((usb_pipetype (urb->pipe) ==  PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags))	{		((urb_priv_t*)urb->hcpriv)->started = ~0;		dequeue_urb (s, urb);		list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb		uhci_switch_timer_int(s);					s->unlink_urb_done = 1;				urb->status = -ECONNABORTED;	// mark urb as "waiting to be killed"			urb_priv = (urb_priv_t*)urb->hcpriv;		switch (usb_pipetype (urb->pipe)) {		case PIPE_INTERRUPT:			usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));				case PIPE_ISOCHRONOUS:			uhci_clean_iso_step1 (s, urb_priv);			break;		case PIPE_BULK:		case PIPE_CONTROL:			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);			uhci_clean_transfer (s, urb, qh, 0);			break;		}		((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);		return -EINPROGRESS;  // completion will follow	}			return 0;    // URB already dead}/*-------------------------------------------------------------------*/_static int uhci_unlink_urb (urb_t *urb){	uhci_t *s;	unsigned long flags=0;	dbg("uhci_unlink_urb called for %p",urb);	if (!urb || !urb->dev)		// you never know...		return -EINVAL;		s = (uhci_t*) urb->dev->bus->hcpriv;	if (usb_pipedevice (urb->pipe) == s->rh.devnum)		return rh_unlink_urb (urb);	if (!urb->hcpriv)		return -EINVAL;	if (urb->transfer_flags & USB_ASYNC_UNLINK) {		int ret;       		spin_lock_irqsave (&s->urb_list_lock, flags);		// The URB needs to be locked if called outside completion context		if (!in_interrupt())			spin_lock(&urb->lock);		uhci_release_bandwidth(urb);		ret = uhci_unlink_urb_async(s, urb);		if (!in_interrupt())			spin_unlock(&urb->lock);		spin_unlock_irqrestore (&s->urb_list_lock, flags);			return ret;	}	else		return uhci_unlink_urb_sync(s, urb);}/*-------------------------------------------------------------------*/// 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 (urb_t *urb, unsigned int *start, unsigned int *end){	urb_t *u, *last_urb = NULL;	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;	struct list_head *p;	int ret=-1;	unsigned long flags;		spin_lock_irqsave (&s->urb_list_lock, flags);	p=s->urb_list.prev;	for (; p != &s->urb_list; p = p->prev) {		u = list_entry (p, urb_t, urb_list);		// 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) & 1023;		ret=0;	}		spin_unlock_irqrestore(&s->urb_list_lock, flags);		return ret;}/*-------------------------------------------------------------------*/// adjust start_frame according to scheduling constraints (ASAP etc)_static int iso_find_start (urb_t *urb){	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;	unsigned int now;	unsigned int start_limit = 0, stop_limit = 0, queued_size;	int limits;	now = UHCI_GET_CURRENT_FRAME (s) & 1023;	if ((unsigned) urb->number_of_packets > 900)		return -EFBIG;		limits = find_iso_limits (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) urb->number_of_packets) {				info("iso_find_start: gap in seamless isochronous scheduling");				dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",					now, urb->start_frame, urb->number_of_packets, urb->pipe);				urb->start_frame = (now + 5) & 1023;	// 5ms setup should be enough //FIXME!			}		}	}	else {		urb->start_frame &= 1023;		if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) {			dbg("iso_find_start: now between start_frame and end");			return -EAGAIN;		}	}	/* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */	if (limits)		return 0;	if (((urb->start_frame - start_limit) & 1023) < queued_size ||	    ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) {		dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u",			urb->start_frame, urb->number_of_packets, start_limit, stop_limit);		return -EAGAIN;	}	return 0;}/*-------------------------------------------------------------------*/// submits USB interrupt (ie. polling ;-) // ASAP-flag set implicitely// if period==0, the the transfer is only done once_static int uhci_submit_int_urb (urb_t *urb){	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;	urb_priv_t *urb_priv = urb->hcpriv;	int nint, n, ret;	uhci_desc_t *td;	int status, destination;	int info;	unsigned int pipe = urb->pipe;	if (urb->interval < 0 || urb->interval >= 256)		return -EINVAL;	if (urb->interval == 0)		nint = 0;	else {		for (nint = 0, n = 1; nint <= 8; nint++, n += n)	// round interval down to 2^n		 {			if (urb->interval < n) {				urb->interval = n / 2;				break;			}		}		nint--;	}	dbg("Rounded interval to %i, chain  %i", urb->interval, nint);	urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023;	// remember start frame, just in case...	urb->number_of_packets = 1;	// INT allows only one packet	if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)))		return -EINVAL;	ret = alloc_td (&td, UHCI_PTR_DEPTH);	if (ret)		return -ENOMEM;

⌨️ 快捷键说明

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