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

📄 usb-uhci.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 5 页
字号:
		*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 (struct urb *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 transfer is only done once

_static int uhci_submit_int_urb (struct urb *urb)
{
	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
	urb_priv_t *urb_priv = urb->hcpriv;
	int nint, n;
	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;

	if (alloc_td (s, &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 = (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, urb_priv->transfer_buffer_dma);
	list_add_tail (&td->desc_list, &urb_priv->desc_list);

	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 (struct urb *urb, int mem_flags)
{
	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
	urb_priv_t *urb_priv = urb->hcpriv;
#ifdef ISO_SANITY_CHECK
	int pipe=urb->pipe;
	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
#endif
	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*), mem_flags);

	if (!tdm) {
		ret = -ENOMEM;
		goto err;
	}

	memset(tdm, 0, urb->number_of_packets * sizeof (uhci_desc_t*));

	// First try to get all TDs. Cause: Removing already inserted TDs can only be done 
	// racefree in three steps: unlink TDs, wait one frame, delete TDs. 
	// So, this solutions seems simpler...

	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)
			continue;  // allows ISO striping by setting length to zero in iso_descriptor


#ifdef ISO_SANITY_CHECK
		if(urb->iso_frame_desc[n].length > maxsze) {

			err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
			ret=-EINVAL;		
		}
		else
#endif
		if (alloc_td (s, &td, UHCI_PTR_DEPTH)) {
			int i;	// Cleanup allocated TDs

			for (i = 0; i < n; n++)
				if (tdm[i])
					 delete_desc(s, tdm[i]);
			kfree (tdm);
			goto err;
		}
		last=n;
		tdm[n] = td;
	}

	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;

	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;
			queue_urb (s, urb);
		}

		fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),
			 urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset);
		list_add_tail (&td->desc_list, &urb_priv->desc_list);
	
		insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td);	// store in iso-tds
	}

	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 struct urb* search_dev_ep (uhci_t *s, struct urb *urb)
{
	struct list_head *p;
	struct urb *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, struct urb, 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 (struct urb *urb, int mem_flags)
{
	uhci_t *s;
	urb_priv_t *urb_priv;
	int ret = 0, type;
	unsigned long flags;
	struct urb *queued_urb=NULL;
	int bustime;
		
	if (!urb->dev || !urb->dev->bus)
		return -ENODEV;

	s = (uhci_t*) urb->dev->bus->hcpriv;
	//dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
	
	if (!s->running)
		return -ENODEV;
	
	type = usb_pipetype (urb->pipe);

	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
		return rh_submit_urb (urb);	/* virtual root hub */

	// Sanity checks
	if (usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)) <= 0) {		
		err("uhci_submit_urb: pipesize for pipe %x is zero", urb->pipe);
		return -EMSGSIZE;
	}

	if (urb->transfer_buffer_length < 0 && type != PIPE_ISOCHRONOUS) {
		err("uhci_submit_urb: Negative transfer length for urb %p", urb);
		return -EINVAL;
	}

	/* increment the reference count of the urb, as we now also control it */
	urb = usb_get_urb (urb);

	usb_get_dev (urb->dev);

	spin_lock_irqsave (&s->urb_list_lock, flags);

	queued_urb = search_dev_ep (s, urb); // returns already queued urb for that pipe

	if (queued_urb) {

		queue_dbg("found bulk urb %p\n", queued_urb);

		if (( type != PIPE_BULK) ||
		    ((type == PIPE_BULK) &&
		     (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) {
			spin_unlock_irqrestore (&s->urb_list_lock, flags);
			usb_put_dev (urb->dev);
			usb_put_urb (urb);
			err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb);
			return -ENXIO;	// urb already queued
		}
	}

#ifdef DEBUG_SLAB
	urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
#else
	urb_priv = kmalloc (sizeof (urb_priv_t), mem_flags);
#endif
	if (!urb_priv) {
		usb_put_dev (urb->dev);
		spin_unlock_irqrestore (&s->urb_list_lock, flags);
		usb_put_urb (urb);
		return -ENOMEM;
	}

	memset(urb_priv, 0, sizeof(urb_priv_t));
	urb->hcpriv = urb_priv;
	INIT_LIST_HEAD (&urb_priv->desc_list);

	dbg("submit_urb: scheduling %p", urb);
	
	if (type == PIPE_CONTROL)
		urb_priv->setup_packet_dma = pci_map_single(s->uhci_pci, urb->setup_packet,
							    sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);

	if (urb->transfer_buffer_length)
		urb_priv->transfer_buffer_dma = pci_map_single(s->uhci_pci,
							       urb->transfer_buffer,
							       urb->transfer_buffer_length,
							       usb_pipein(urb->pipe) ?
							       PCI_DMA_FROMDEVICE :
							       PCI_DMA_TODEVICE);

	if (type == PIPE_BULK) {
	
		if (queued_urb) {
			while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb)  // find last queued bulk
				queued_urb=((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb;
			
			((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb=urb;
		}
		atomic_inc (&s->avoid_bulk);
		ret = uhci_submit_bulk_urb (urb, queued_urb);
		atomic_dec (&s->avoid_bulk);
		spin_unlock_irqrestore (&s->urb_list_lock, flags);
	}
	else {
		spin_unlock_irqrestore (&s->urb_list_lock, flags);
		switch (type) {
		case PIPE_ISOCHRONOUS:			
			if (urb->bandwidth == 0) {      /* not yet checked/allocated */
				if (urb->number_of_packets <= 0) {
					ret = -EINVAL;
					break;
				}

				bustime = usb_check_bandwidth (urb->dev, urb);
				if (bustime < 0) 
					ret = bustime;
				else {
					ret = uhci_submit_iso_urb(urb, mem_flags);
					if (ret == 0)
						usb_claim_bandwidth (urb->dev, urb, bustime, 1);
				}
			} else {        /* bandwidth is already set */
				ret = uhci_submit_iso_urb(urb, mem_flags);
			}
			break;
		case PIPE_INTERRUPT:
			if (urb->bandwidth == 0) {      /* not yet checked/allocated */
				bustime = usb_check_bandwidth (urb->dev, urb);
				if (bustime < 0)
					ret = bustime;
				else {
					ret = uhci_submit_int_urb(urb);
					if (ret == 0)
						usb_claim_bandwidth (urb->dev, urb, bustime, 0);
				}
			} else {        /* bandwidth is already set */
				ret = uhci_submit_int_urb(urb);
			}
			break;
		case PIPE_CONTROL:
			ret = uhci_submit_control_urb (urb);
			break;
		default:
			ret = -EINVAL;
		}
	}

	dbg("submit_urb: scheduled with ret: %d", ret);
	
	if (ret != 0) {
		uhci_urb_dma_unmap(s, urb, urb_priv);
		usb_put_dev (urb->dev);
#ifdef DEBUG_SLAB
		kmem_cache_free(urb_priv_kmem, urb_priv);
#else
		kfree (urb_priv);
#endif
		usb_put_urb (urb);
		return ret;
	}

	return 0;
}

// Checks for URB timeout and removes bandwidth reclamation if URB idles too long
_static void uhci_check_timeouts(uhci_t *s)
{
	struct list_head *p,*p2;
	struct urb *urb;
	int type;	

	p = s->urb_list.prev;	

	while (p != &s->urb_list) {
		urb_priv_t *hcpriv;

		p2 = p;
		p = p->prev;
		urb = list_entry (p2, struct urb, urb_list);
		type = usb_pipetype (urb->pipe);

		hcpriv = (urb_priv_t*)urb->hcpriv;
		
		if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) {
			urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
			async_dbg("uhci_check_timeout: timeout for %p",urb);
			uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
		}
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
		else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
			 (hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT))
			disable_desc_loop(s, urb);
#endif

	}
	s->timeout_check=jiffies;
}

/*-------------------------------------------------------------------
 Virtual Root Hub
 -------------------------------------------------------------------*/

_static __u8 root_hub_dev_des[] =
{
	0x12,			/*  __u8  bLength; */
	0x01,			/*  __u8  bDescriptorType; Device */
	0x00,			/*  __u16 bcdUSB; v1.0 */
	0x01,
	0x09,			/*  __u8  bDeviceClass; HUB_CLASSCODE */

⌨️ 快捷键说明

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