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

📄 usb-uhci.c

📁 Usb1.1驱动c语言源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	}	if (mode) {		qh->last_used = now;		list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion	}}/*-------------------------------------------------------------------*/// 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;	spin_lock_irqsave (&s->urb_list_lock, flags);	if (urb->status == -EINPROGRESS) {		// URB probably still in work		dequeue_urb (s, urb);		s->unlink_urb_done=1;		spin_unlock_irqrestore (&s->urb_list_lock, flags);						urb->status = -ENOENT;	// mark urb as killed				urb_priv = urb->hcpriv;		switch (usb_pipetype (urb->pipe)) {		case PIPE_ISOCHRONOUS:		case PIPE_INTERRUPT:			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:			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);			spin_lock_irqsave (&s->urb_list_lock, flags);			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		if (urb->complete) {			dbg("unlink_urb: calling completion");			urb->complete ((struct urb *) urb);		}		usb_dec_dev_use (urb->dev);		return 0;	}	else		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);				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->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				usb_dec_dev_use (dev);#ifdef DEBUG_SLAB			kmem_cache_free (urb_priv_kmem, urb_priv);#else			kfree (urb_priv);#endif			switch (usb_pipetype (pipe)) {			case PIPE_ISOCHRONOUS:			case PIPE_INTERRUPT:				uhci_clean_iso_step2(s, urb_priv);				break;			}			list_del (&urb->urb_list);		}	}}/*-------------------------------------------------------------------*/_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) {		((urb_priv_t*)urb->hcpriv)->started = ~0;		dequeue_urb (s, urb);		list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb		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_ISOCHRONOUS:		case PIPE_INTERRUPT:			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;}/*-------------------------------------------------------------------*/_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);		ret = uhci_unlink_urb_async(s, urb);		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;	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |		(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe) |		(((urb->transfer_buffer_length - 1) & 0x7ff) << 21);	info = destination | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);	fill_td (td, status, info, virt_to_bus (urb->transfer_buffer));	list_add_tail (&td->desc_list, &urb_priv->desc_list);	urb->status = -EINPROGRESS;	queue_urb (s, urb);	insert_td_horizontal (s, s->int_chain[nint], td);	// store in INT-TDs	usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));	return 0;}/*-------------------------------------------------------------------*/_static int uhci_submit_iso_urb (urb_t *urb){	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;	urb_priv_t *urb_priv = urb->hcpriv;	int pipe=urb->pipe;	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));	int n, ret, last=0;	uhci_desc_t *td, **tdm;	int status, destination;	unsigned long flags;	__save_flags(flags);	__cli();		      // Disable IRQs to schedule all ISO-TDs in time	ret = iso_find_start (urb);	// adjusts urb->start_frame for later use		if (ret)		goto err;	tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);	if (!tdm) {		ret = -ENOMEM;		goto err;	}	// First try to get all TDs	for (n = 0; n < urb->number_of_packets; n++) {		dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length);		if (!urb->iso_frame_desc[n].length) {			// allows ISO striping by setting length to zero in iso_descriptor			tdm[n] = 0;			continue;		}				if(urb->iso_frame_desc[n].length > maxsze) {#ifdef ISO_SANITY_CHECK			err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);			tdm[n] = 0;			ret=-EINVAL;			goto inval;#endif		}				ret = alloc_td (&td, UHCI_PTR_DEPTH);	inval:		if (ret) {			int i;	// Cleanup allocated TDs			for (i = 0; i < n; n++)				if (tdm[i])					 delete_desc(tdm[i]);			kfree (tdm);			goto err;		}		last=n;		tdm[n] = td;	}	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;	//| (urb->transfer_flags&USB_DISABLE_SPD?0:TD_CTRL_SPD);	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe);		// Queue all allocated TDs	for (n = 0; n < urb->number_of_packets; n++) {		td = tdm[n];		if (!td)			continue;					if (n  == last)			status |= TD_CTRL_IOC;		fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),			 virt_to_bus (urb->transfer_buffer + urb->iso_frame_desc[n].offset));		list_add_tail (&td->desc_list, &urb_priv->desc_list);			if (n == last) {			urb->status = -EINPROGRESS;			queue_urb (s, urb);		}		insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td);	// store in iso-tds		//uhci_show_td(td);	}	kfree (tdm);	dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023);	ret = 0;      err:	__restore_flags(flags);	return ret;}/*-------------------------------------------------------------------*/// returns: 0 (no transfer queued), urb* (this urb already queued) _static urb_t* search_dev_ep (uhci_t *s, urb_t *urb){	struct list_head *p;	urb_t *tmp;	unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);	dbg("search_dev_ep:");	p=s->urb_list.next;	for (; p != &s->urb_list; p = p->next) {		tmp = list_entry (p, urb_t, urb_list);		dbg("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_urb (urb_t *urb){	uhci_t *s;	urb_priv_t *urb_priv;	int ret = 0;	unsigned long flags;	urb_t *bulk_urb=NULL;			if (!urb->dev || !urb->dev->bus)

⌨️ 快捷键说明

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