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

📄 usb-ohci.c

📁 Usb Host/Periphel Control TD1120 codes
💻 C
📖 第 1 页 / 共 5 页
字号:
					: PCI_DMA_FROMDEVICE);
			*/
			urb->complete (urb);

			/* implicitly requeued */
  			urb->actual_length = 0;
  			urb->status = USB_ST_URB_PENDING;
  			if (urb_priv->state != URB_DEL)
  				td_submit_urb (urb);
  			break;
  			
		case PIPE_ISOCHRONOUS:
			for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
			if (urbt) { /* send the reply and requeue URB */	
				/*
				pci_unmap_single (hc->ohci_dev,
					urb_priv->td [0]->data_dma,
					urb->transfer_buffer_length,
					usb_pipeout (urb->pipe)
						? PCI_DMA_TODEVICE
						: PCI_DMA_FROMDEVICE);
				*/
				urb->complete (urb);
				spin_lock_irqsave (&usb_ed_lock, flags);
				urb->actual_length = 0;
  				urb->status = USB_ST_URB_PENDING;
  				urb->start_frame = urb_priv->ed->last_iso + 1;
  				if (urb_priv->state != URB_DEL) {
  					for (i = 0; i < urb->number_of_packets; i++) {
  						urb->iso_frame_desc[i].actual_length = 0;
  						urb->iso_frame_desc[i].status = -EXDEV;
  					}
  					td_submit_urb (urb);
  				}
  				spin_unlock_irqrestore (&usb_ed_lock, flags);
  				
  			} else { /* unlink URB, call complete */
				urb_rm_priv (urb);
				urb->complete (urb); 	
			}		
			break;
  				
		case PIPE_BULK:
		case PIPE_CONTROL: /* unlink URB, call complete */
			urb_rm_priv (urb);
			urb->complete (urb);	
			break;
	}
	return 0;
}

/*-------------------------------------------------------------------------*/

/* get a transfer request */
 
static int sohci_submit_urb (urb_t * urb)
{
	ohci_t * ohci;
	ed_t * ed;
	urb_priv_t * urb_priv;
	unsigned int pipe = urb->pipe;
	int i, size = 0;
	unsigned long flags;
	int bustime = 0;
	int mem_flags = ALLOC_FLAGS;
	
	if (!urb->dev || !urb->dev->bus)
		return -ENODEV;

	if (urb->hcpriv)			/* urb already in use */
		return -EINVAL;

//	if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) 
//		return -EPIPE;
	
	usb_inc_dev_use (urb->dev);
	ohci = (ohci_t *) urb->dev->bus->hcpriv;
	
#ifdef DEBUG
	urb_print (urb, "SUB", usb_pipein (pipe));
#endif
	
	/* handle a request to the virtual root hub */
	if (usb_pipedevice (pipe) == ohci->rh.devnum) 
		return rh_submit_urb (urb);
	
	/* when controller's hung, permit only roothub cleanup attempts
	 * such as powering down ports */
	if (ohci->disabled) {
		usb_dec_dev_use (urb->dev);	
		return -ESHUTDOWN;
	}

	/* every endpoint has a ed, locate and fill it */
	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
		usb_dec_dev_use (urb->dev);	
		return -ENOMEM;
	}

	/* for the private part of the URB we need the number of TDs (size) */
	switch (usb_pipetype (pipe)) {
		case PIPE_BULK:	/* one TD for every 4096 Byte */
			size = (urb->transfer_buffer_length - 1) / 4096 + 1;
			break;
		case PIPE_ISOCHRONOUS: /* number of packets from URB */
			size = urb->number_of_packets;
			if (size <= 0) {
				usb_dec_dev_use (urb->dev);	
				return -EINVAL;
			}
			for (i = 0; i < urb->number_of_packets; i++) {
  				urb->iso_frame_desc[i].actual_length = 0;
  				urb->iso_frame_desc[i].status = -EXDEV;
  			}
			break;
		case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
			size = (urb->transfer_buffer_length == 0)? 2: 
						(urb->transfer_buffer_length - 1) / 4096 + 3;
			break;
		case PIPE_INTERRUPT: /* one TD */
			size = 1;
			break;
	}

	/* allocate the private part of the URB */
	urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), 
							in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
	if (!urb_priv) {
		usb_dec_dev_use (urb->dev);	
		return -ENOMEM;
	}
	memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
	
	/* fill the private part of the URB */
	urb_priv->length = size;
	urb_priv->ed = ed;	

	/* allocate the TDs (updating hash chains) */
	spin_lock_irqsave (&usb_ed_lock, flags);
	for (i = 0; i < size; i++) { 
		urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC);
		if (!urb_priv->td[i]) {
			urb_priv->length = i;
			urb_free_priv (ohci, urb_priv);
			spin_unlock_irqrestore (&usb_ed_lock, flags);
			usb_dec_dev_use (urb->dev);	
			return -ENOMEM;
		}
	}	

	if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
		urb_free_priv (ohci, urb_priv);
		spin_unlock_irqrestore (&usb_ed_lock, flags);
		usb_dec_dev_use (urb->dev);	
		return -EINVAL;
	}
	
	/* allocate and claim bandwidth if needed; ISO
	 * needs start frame index if it was't provided.
	 */
	switch (usb_pipetype (pipe)) {
		case PIPE_ISOCHRONOUS:
			if (urb->transfer_flags & USB_ISO_ASAP) { 
				urb->start_frame = ((ed->state == ED_OPER)
					? (ed->last_iso + 1)
					: (le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff;
			}	
			/* FALLTHROUGH */
		case PIPE_INTERRUPT:
			if (urb->bandwidth == 0) {
				bustime = usb_check_bandwidth (urb->dev, urb);
			}
			if (bustime < 0) {
				urb_free_priv (ohci, urb_priv);
				spin_unlock_irqrestore (&usb_ed_lock, flags);
				usb_dec_dev_use (urb->dev);	
				return bustime;
			}
			usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));
#ifdef	DO_TIMEOUTS
			urb->timeout = 0;
#endif
	}

	urb->actual_length = 0;
	urb->hcpriv = urb_priv;
	urb->status = USB_ST_URB_PENDING;

	/* link the ed into a chain if is not already */
	if (ed->state != ED_OPER)
		ep_link (ohci, ed);

	/* fill the TDs and link it to the ed */
	td_submit_urb (urb);

#ifdef	DO_TIMEOUTS
	/* maybe add to ordered list of timeouts */
	if (urb->timeout) {
		struct list_head	*entry;

		// FIXME:  usb-uhci uses relative timeouts (like this),
		// while uhci uses absolute ones (probably better).
		// Pick one solution and change the affected drivers.
		urb->timeout += jiffies;

		list_for_each (entry, &ohci->timeout_list) {
			struct urb	*next_urb;

			next_urb = list_entry (entry, struct urb, urb_list);
			if (time_after_eq (urb->timeout, next_urb->timeout))
				break;
		}
		list_add (&urb->urb_list, entry);

		/* drive timeouts by SF (messy, but works) */
		Ohci_WriteReg (OHCI_INTR_SF, &ohci->regs->intrenable);	
	}
#endif

	spin_unlock_irqrestore (&usb_ed_lock, flags);

	return 0;	
}

/*-------------------------------------------------------------------------*/

/* deactivate all TDs and remove the private part of the URB */
/* interrupt callers must use async unlink mode */

static int sohci_unlink_urb (urb_t * urb)
{
	unsigned long flags;
	ohci_t * ohci;
	
	if (!urb) /* just to be sure */ 
		return -EINVAL;
		
	if (!urb->dev || !urb->dev->bus)
		return -ENODEV;

	ohci = (ohci_t *) urb->dev->bus->hcpriv; 

#ifdef DEBUG
	urb_print (urb, "UNLINK", 1);
#endif		  

	/* handle a request to the virtual root hub */
	if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
		return rh_unlink_urb (urb);

	if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { 
		if (!ohci->disabled) {
			urb_priv_t  * urb_priv;

			/* interrupt code may not sleep; it must use
			 * async status return to unlink pending urbs.
			 */
			if (!(urb->transfer_flags & USB_ASYNC_UNLINK)
					&& in_interrupt ()) {
				err ("bug in call from %p; use async!",
					__builtin_return_address(0));
				return -EWOULDBLOCK;
			}

			/* flag the urb and its TDs for deletion in some
			 * upcoming SF interrupt delete list processing
			 */
			spin_lock_irqsave (&usb_ed_lock, flags);
			urb_priv = urb->hcpriv;

			if (!urb_priv || (urb_priv->state == URB_DEL)) {
				spin_unlock_irqrestore (&usb_ed_lock, flags);
				return 0;
			}
				
			urb_priv->state = URB_DEL; 
			ep_rm_ed (urb->dev, urb_priv->ed);
			urb_priv->ed->state |= ED_URB_DEL;

			if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
				DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); 
				DECLARE_WAITQUEUE (wait, current);
				int timeout = OHCI_UNLINK_TIMEOUT;

				add_wait_queue (&unlink_wakeup, &wait);
				urb_priv->wait = &unlink_wakeup;
				spin_unlock_irqrestore (&usb_ed_lock, flags);

				/* wait until all TDs are deleted */
				set_current_state(TASK_UNINTERRUPTIBLE);
				while (timeout && (urb->status == USB_ST_URB_PENDING))
					timeout = schedule_timeout (timeout);
				current->state = TASK_RUNNING;
				remove_wait_queue (&unlink_wakeup, &wait); 
				if (urb->status == USB_ST_URB_PENDING) {
					err ("unlink URB timeout");
					return -ETIMEDOUT;
				}
			} else {
				/* usb_dec_dev_use done in dl_del_list() */
				urb->status = -EINPROGRESS;
				spin_unlock_irqrestore (&usb_ed_lock, flags);
			}
		} else {
			urb_rm_priv (urb);
			if (urb->transfer_flags & USB_ASYNC_UNLINK) {
				urb->status = -ECONNRESET;
				if (urb->complete)
					urb->complete (urb); 
			} else 
				urb->status = -ENOENT;
		}	
	}	
	return 0;
}

/*-------------------------------------------------------------------------*/

/* allocate private data space for a usb device */

static int sohci_alloc_dev (struct usb_device *usb_dev)
{
	struct ohci_device * dev;

	dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS);
	if (!dev)
		return -ENOMEM;

	usb_dev->hcpriv = dev;
	return 0;
}

/*-------------------------------------------------------------------------*/

/* may be called from interrupt context */
/* frees private data space of usb device */
  
static int sohci_free_dev (struct usb_device * usb_dev)
{
	unsigned long flags;
	int i, cnt = 0;
	ed_t * ed;
	struct ohci_device * dev = usb_to_ohci (usb_dev);
	ohci_t * ohci = usb_dev->bus->hcpriv;
	
	if (!dev)
		return 0;
	
	if (usb_dev->devnum >= 0) {
	
		/* driver disconnects should have unlinked all urbs
		 * (freeing all the TDs, unlinking EDs) but we need
		 * to defend against bugs that prevent that.
		 */
		spin_lock_irqsave (&usb_ed_lock, flags);	
		for(i = 0; i < NUM_EDS; i++) {
  			ed = &(dev->ed[i]);
  			if (ed->state != ED_NEW) {
  				if (ed->state == ED_OPER) {
					/* driver on that interface didn't unlink an urb */
					dbg ("driver usb-%s dev %d ed 0x%x unfreed URB",
						ohci->slot_name, usb_dev->devnum, i);
					ep_unlink (ohci, ed);
				}
  				ep_rm_ed (usb_dev, ed);
  				ed->state = ED_DEL;
  				cnt++;
  			}
  		}
  		spin_unlock_irqrestore (&usb_ed_lock, flags);
  		
		/* if the controller is running, tds for those unlinked
		 * urbs get freed by dl_del_list at the next SF interrupt
		 */
		if (cnt > 0) {

			if (ohci->disabled) {
				/* FIXME: Something like this should kick in,
				 * though it's currently an exotic case ...
				 * the controller won't ever be touching
				 * these lists again!!
				dl_del_list (ohci,
					le16_to_cpu (ohci->hcca->frame_no) & 1);
				 */
				warn ("TD leak, %d", cnt);

			} else if (!in_interrupt ()) {
				DECLARE_WAIT_QUEUE_HEAD (freedev_wakeup); 
				DECLARE_WAITQUEUE (wait, current);
				int timeout = OHCI_UNLINK_TIMEOUT;

				/* SF interrupt handler calls dl_del_list */
				add_wait_queue (&freedev_wakeup, &wait);
				dev->wait = &freedev_wakeup;
				set_current_state(TASK_UNINTERRUPTIBLE);
				while (timeout && dev->ed_cnt)
					timeout = schedule_timeout (timeout);
				current->state = TASK_RUNNING;
				remove_wait_queue (&freedev_wakeup, &wait);
				if (dev->ed_cnt) {
					err ("free device %d timeout", usb_dev->devnum);
					return -ETIMEDOUT;
				}
			} else {
				/* likely some interface's driver has a refcount bug */
				err ("bus %s devnum %d deletion in interrupt",
					ohci->slot_name, usb_dev->devnum);
				BUG ();
			}
		}
	}

	/* free device, and associated EDs */
	dev_free (ohci, dev);

	return 0;
}

/*-------------------------------------------------------------------------*/

/* tell us the current USB frame number */

static int sohci_get_current_frame_number (struct usb_device *usb_dev) 
{
	ohci_t * ohci = usb_dev->bus->hcpriv;
	
	return le16_to_cpu (ohci->hcca->frame_no);
}

/*-------------------------------------------------------------------------*/

struct usb_operations sohci_device_operations = {
	sohci_alloc_dev,
	sohci_free_dev,
	sohci_get_current_frame_number,
	sohci_submit_urb,
	sohci_unlink_urb
};

/*-------------------------------------------------------------------------*
 * ED handling functions
 *-------------------------------------------------------------------------*/  
		
/* search for the right branch to insert an interrupt ed into the int tree 
 * do some load ballancing;
 * returns the branch and 
 * sets the interval to interval = 2^integer (ld (interval)) */

static int ep_int_ballance (ohci_t * ohci, int interval, int load)
{
	int i, branch = 0;
   
	/* search for the least loaded interrupt endpoint branch of all 32 branches */
	for (i = 0; i < 32; i++) 

⌨️ 快捷键说明

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