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

📄 usb-ohci.c

📁 基于S3CEB2410平台LINUX操作系统下 USB驱动源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
				urb->transfer_buffer_length,				usb_pipeout (urb->pipe)					? PCI_DMA_TODEVICE					: PCI_DMA_FROMDEVICE);#if 1			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);#else			if (urb->interval) {				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);			} else {				urb_rm_priv(urb);				urb->complete (urb);			}#endif  			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 maxps = usb_maxpacket (urb->dev, pipe, usb_pipeout (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;			/* If the transfer size is multiple of the pipe mtu,			 * we may need an extra TD to create a empty frame			 * Jean II */			if ((urb->transfer_flags & USB_ZERO_PACKET) &&			    usb_pipeout (pipe) &&			    (urb->transfer_buffer_length != 0) && 			    ((urb->transfer_buffer_length % maxps) == 0))				size++;			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) */		writel (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);				set_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);				set_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;

⌨️ 快捷键说明

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