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

📄 usb-ohci.c

📁 有关于USB的一些主机端驱动
💻 C
📖 第 1 页 / 共 5 页
字号:

#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)) {
//				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; 
			printf("unlink urb\n");
			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); 
				wait_ms(timeout);
				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);
				
//				printf("urb status:%X\n",urb->status);
				
			}
		} 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 = ohci_dev_alloc ((struct ohci *) usb_dev->bus->hcpriv);
	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->ohci_dev->slot_name, usb_dev->devnum, i);
					ep_unlink (ohci, ed);
				}
  				printf("free device\n");
  				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{
//				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);
				wait_ms(timeout);
				if (dev->ed_cnt) {
					printf ("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->ohci_dev->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++) 
		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; 
  
	branch = branch % interval;
	for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load;

	return branch;
}

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

/*  2^int( ld (inter)) */

static int ep_2_n_interval (int inter)
{	
	int i;
	for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); 
	return 1 << i;
}

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

/* the int tree is a binary tree 
 * in order to process it sequentially the indexes of the branches have to be mapped 
 * the mapping reverses the bits of a word of num_bits length */
 
static int ep_rev (int num_bits, int word)
{
	int i, wout = 0;

	for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1));
	return wout;
}

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

/* link an ed into one of the HC chains */

static int ep_link (ohci_t * ohci, ed_t * edi)
{	 
	int int_branch;
	int i;
	int inter;
	int interval;
	int load;
	unsigned long * ed_p;
	volatile ed_t * ed = edi;
	
	ed->state = ED_OPER;
	
	switch (ed->type) {
	case PIPE_CONTROL:
		ed->hwNextED = 0;
		if (ohci->ed_controltail == NULL) {
			writel (ed->dma, &ohci->regs->ed_controlhead);
		} else {
//			ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
			ohci->ed_controltail->hwNextED = ed->dma;
		}
		ed->ed_prev = ohci->ed_controltail;
		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
			!ohci->ed_rm_list[1] && !ohci->sleeping) {
			ohci->hc_control |= OHCI_CTRL_CLE;
			writel (ohci->hc_control, &ohci->regs->control);
		}
		ohci->ed_controltail = edi;	  
		break;
		
	case PIPE_BULK:
		ed->hwNextED = 0;
		if (ohci->ed_bulktail == NULL) {
			writel (ed->dma, &ohci->regs->ed_bulkhead);
		} else {
//			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
			ohci->ed_bulktail->hwNextED = ed->dma;
		}
		ed->ed_prev = ohci->ed_bulktail;
		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
			!ohci->ed_rm_list[1] && !ohci->sleeping) {
			ohci->hc_control |= OHCI_CTRL_BLE;
			writel (ohci->hc_control, &ohci->regs->control);
		}
		ohci->ed_bulktail = edi;	  
		break;
		
	case PIPE_INTERRUPT:
		load = ed->int_load;
		interval = ep_2_n_interval (ed->int_period);
		ed->int_interval = interval;
		int_branch = ep_int_ballance (ohci, interval, load);
		ed->int_branch = int_branch;
		
		for (i = 0; i < ep_rev (6, interval); i += inter) {
			inter = 1;
			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); 
				(*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); 
				ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
					inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
			ed->hwNextED = *ed_p; 
			*ed_p = cpu_to_le32 (ed->dma);
		}
#ifdef DEBUG
		ep_print_int_eds (ohci, "LINK_INT");
#endif
		break;
		
	case PIPE_ISOCHRONOUS:
		ed->hwNextED = 0;
		ed->int_interval = 1;
		if (ohci->ed_isotail != NULL) {
			ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
			ed->ed_prev = ohci->ed_isotail;
		} else {
			for ( i = 0; i < 32; i += inter) {
				inter = 1;
				for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); 
					*ed_p != 0; 
					ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
						inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
				*ed_p = cpu_to_le32 (ed->dma);	
			}	
			ed->ed_prev = NULL;
		}	
		ohci->ed_isotail = edi;  
#ifdef DEBUG
		ep_print_int_eds (ohci, "LINK_ISO");
#endif
		break;
	}	 	
	return 0;
}

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

/* unlink an ed from one of the HC chains. 
 * just the link to the ed is unlinked.
 * the link from the ed still points to another operational ed or 0
 * so the HC can eventually finish the processing of the unlinked ed */

static int ep_unlink (ohci_t * ohci, ed_t * ed) 
{
	int int_branch;
	int i;
	int inter;
	int interval;
	unsigned long * ed_p;

	ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);

	switch (ed->type) {
	case PIPE_CONTROL:
		if (ed->ed_prev == NULL) {
			if (!ed->hwNextED) {
				ohci->hc_control &= ~OHCI_CTRL_CLE;
				writel (ohci->hc_control, &ohci->regs->control);
			}
			writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
		} else {
			ed->ed_prev->hwNextED = ed->hwNextED;
		}
		if (ohci->ed_controltail == ed) {
			ohci->ed_controltail = ed->ed_prev;
		} else {
			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
		}
		break;
      
	case PIPE_BULK:
		if (ed->ed_prev == NULL) {
			if (!ed->hwNextED) {
				ohci->hc_control &= ~OHCI_CTRL_BLE;
				writel (ohci->hc_control, &ohci->regs->control);
			}
			writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
		} else {
			ed->ed_prev->hwNextED = ed->hwNextED;
		}
		if (ohci->ed_bulktail == ed) {
			ohci->ed_bulktail = ed->ed_prev;
		} else {
			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
		}
		break;
      
	case PIPE_INTERRUPT:
		int_branch = ed->int_branch;
		interval = ed->int_interval;

		for (i = 0; i < ep_rev (6, interval); i += inter) {
			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]), inter = 1; 
				(*ed_p != 0) && (*ed_p != ed->hwNextED); 
				ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED), 
				inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) {	
					if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
			  			*ed_p = ed->hwNextED;		
			  			break;
			  		}
			  }
		}
		for (i = int_branch; i < 32; i += interval)
		    ohci->ohci_int_load[i] -= ed->int_load;
#ifdef DEBUG
		ep_print_int_eds (ohci, "UNLINK_INT");
#endif
		break;
		
	case PIPE_ISOCHRONOUS:
		if (ohci->ed_isotail == ed)
			ohci->ed_isotail = ed->ed_prev;
		if (ed->hwNextED != 0) 
		    (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
				    
		if (ed->ed_prev != NULL) {
			ed->ed_prev->hwNextED = ed->hwNextED;
		} else {
			for (i = 0; i < 32; i++) {
				for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); 
						*ed_p != 0; 
						ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) {
					// inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
					if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
						*ed_p = ed->hwNextED;		
						break;
					}
				}
			}	

⌨️ 快捷键说明

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