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

📄 usb-ohci.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	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))) {		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 */	for (i = 0; i < size; i++) { 		urb_priv->td[i] = td_alloc (ohci);		if (!urb_priv->td[i]) {			urb_free_priv (ohci, urb_priv);			usb_dec_dev_use (urb->dev);				return -ENOMEM;		}	}		if (ed->state == ED_NEW || (ed->state & ED_DEL)) {		urb_free_priv (ohci, urb_priv);		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);				usb_dec_dev_use (urb->dev);					return bustime;			}			usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));	}	spin_lock_irqsave (&usb_ed_lock, flags);	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);	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;	/* FIXME:  ED allocation with pci_consistent memory	 * must know the controller ... either pass it in here,	 * or decouple ED allocation from dev allocation.	 */	dev = dev_alloc (NULL);	if (!dev)		return -ENOMEM;			memset (dev, 0, sizeof (*dev));	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);				}  				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->ohci_dev->slot_name, usb_dev->devnum);				BUG ();			}		}	}	/* free device, and associated EDs */	dev_free (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;	__u32 * 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 (virt_to_bus (ed), &ohci->regs->ed_controlhead);		} else {			ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed));		}		ed->ed_prev = ohci->ed_controltail;		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&			!ohci->ed_rm_list[1]) {			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 (virt_to_bus (ed), &ohci->regs->ed_bulkhead);		} else {			ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));		}		ed->ed_prev = ohci->ed_bulktail;		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&			!ohci->ed_rm_list[1]) {			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) && (((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval >= interval); 				ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) 					inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);			ed->hwNextED = *ed_p; 			*ed_p = cpu_to_le32 (virt_to_bus (ed));		}#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 (virt_to_bus (ed));			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 = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) 						inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);				*ed_p = cpu_to_le32 (virt_to_bus (ed));				}				ed->ed_prev = NULL;		}			ohci->ed_isotail = edi;  #ifdef DEBUG		ep_print_int_eds (ohci, "LINK_ISO");#endif		break;

⌨️ 快捷键说明

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