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

📄 usb-uhci.c

📁 Usb1.1驱动c语言源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
		uhci->rh.send = 0;		del_timer (&uhci->rh.rh_int_timer);	}	return 0;}/*-------------------------------------------------------------------*//* * Map status to standard result codes * * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status) * <dir_out> is True for output TDs and False for input TDs. */_static int uhci_map_status (int status, int dir_out){	if (!status)		return 0;	if (status & TD_CTRL_BITSTUFF)	/* Bitstuff error */		return -EPROTO;	if (status & TD_CTRL_CRCTIMEO) {	/* CRC/Timeout */		if (dir_out)			return -ETIMEDOUT;		else			return -EILSEQ;	}	if (status & TD_CTRL_NAK)	/* NAK */		return -ETIMEDOUT;	if (status & TD_CTRL_BABBLE)	/* Babble */		return -EPIPE;	if (status & TD_CTRL_DBUFERR)	/* Buffer error */		return -ENOSR;	if (status & TD_CTRL_STALLED)	/* Stalled */		return -EPIPE;	if (status & TD_CTRL_ACTIVE)	/* Active */		return 0;	return -EPROTO;}/* * Only the USB core should call uhci_alloc_dev and uhci_free_dev */_static int uhci_alloc_dev (struct usb_device *usb_dev){	return 0;}_static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all){	unsigned long flags;	struct list_head *p;	struct list_head *p2;	urb_t *urb;	spin_lock_irqsave (&s->urb_list_lock, flags);	p = s->urb_list.prev;		while (p != &s->urb_list) {		p2 = p;		p = p->prev ;		urb = list_entry (p2, urb_t, urb_list);		dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);				//urb->transfer_flags |=USB_ASYNC_UNLINK; 					if (remove_all || (usb_dev == urb->dev)) {			spin_unlock_irqrestore (&s->urb_list_lock, flags);			warn("forced removing of queued URB %p due to disconnect",urb);			uhci_unlink_urb(urb);			urb->dev = NULL; // avoid further processing of this UR			spin_lock_irqsave (&s->urb_list_lock, flags);			p = s->urb_list.prev;			}	}	spin_unlock_irqrestore (&s->urb_list_lock, flags);}_static int uhci_free_dev (struct usb_device *usb_dev){	uhci_t *s;		if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)		return -EINVAL;		s=(uhci_t*) usb_dev->bus->hcpriv;		uhci_unlink_urbs(s, usb_dev, 0);	return 0;}/* * uhci_get_current_frame_number() * * returns the current frame number for a USB bus/controller. */_static int uhci_get_current_frame_number (struct usb_device *usb_dev){	return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv);}struct usb_operations uhci_device_operations ={	uhci_alloc_dev,	uhci_free_dev,	uhci_get_current_frame_number,	uhci_submit_urb,	uhci_unlink_urb};/*  * For IN-control transfers, process_transfer gets a bit more complicated, * since there are devices that return less data (eg. strings) than they * have announced. This leads to a queue abort due to the short packet, * the status stage is not executed. If this happens, the status stage * is manually re-executed. * mode: 0: QHs already unlinked */_static int process_transfer (uhci_t *s, urb_t *urb, int mode){	int ret = 0;	urb_priv_t *urb_priv = urb->hcpriv;	struct list_head *qhl = urb_priv->desc_list.next;	uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list);	struct list_head *p = qh->vertical.next;	uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);	uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);	int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));	// save initial data_toggle	int maxlength; 	// extracted and remapped info from TD	int actual_length;	int status = 0;	//dbg("process_transfer: urb contains bulk/control request");	/* if the status phase has been retriggered and the	   queue is empty or the last status-TD is inactive, the retriggered	   status stage is completed	 */	if (urb_priv->short_control_packet && 		((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) 		goto transfer_finished;	urb->actual_length=0;	for (; p != &qh->vertical; p = p->next) {		desc = list_entry (p, uhci_desc_t, vertical);		if (desc->hw.td.status & TD_CTRL_ACTIVE)	// do not process active TDs			return ret;			actual_length = (desc->hw.td.status + 1) & 0x7ff;		// extract transfer parameters from TD		maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;		status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));		if (status == -EPIPE) { 		// see if EP is stalled			// set up stalled condition			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));		}		if (status != 0) {		// if any error occured stop processing of further TDs			// only set ret if status returned an error			if (status != -EPIPE) 				uhci_show_td (desc);			ret = status;			urb->error_count++;			break;		}		else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)			urb->actual_length += actual_length;		// got less data than requested		if ( (actual_length < maxlength)) {			if (urb->transfer_flags & USB_DISABLE_SPD) {				status = -EREMOTEIO;	// treat as real error				dbg("process_transfer: SPD!!");				break;	// exit after this TD because SP was detected			}			// short read during control-IN: re-start status stage			if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) {				if (uhci_packetid(last_desc->hw.td.info) == USB_PID_OUT) {								qh->hw.qh.element = virt_to_bus (last_desc);  // re-trigger status stage					dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);					//uhci_show_td (desc);					//uhci_show_td (last_desc);					urb_priv->short_control_packet=1;					return 0;				}			}			// all other cases: short read is OK			data_toggle = uhci_toggle (desc->hw.td.info);			break;		}		data_toggle = uhci_toggle (desc->hw.td.info);		queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);      	}	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle); transfer_finished:		uhci_clean_transfer(s, urb, qh, (mode==0?2:1));	urb->status = status;#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH		disable_desc_loop(s,urb);#endif		queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",		urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);	return ret;}_static int process_interrupt (uhci_t *s, urb_t *urb){	int i, ret = -EINPROGRESS;	urb_priv_t *urb_priv = urb->hcpriv;	struct list_head *p = urb_priv->desc_list.next;	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);	int actual_length;	int status = 0;	//dbg("urb contains interrupt request");	for (i = 0; p != &urb_priv->desc_list; p = p->next, i++)	// Maybe we allow more than one TD later ;-)	{		desc = list_entry (p, uhci_desc_t, desc_list);		if (desc->hw.td.status & TD_CTRL_ACTIVE) {			// do not process active TDs			//dbg("TD ACT Status @%p %08x",desc,desc->hw.td.status);			break;		}		if (!desc->hw.td.status & TD_CTRL_IOC) {			// do not process one-shot TDs, no recycling			break;		}		// extract transfer parameters from TD		actual_length = (desc->hw.td.status + 1) & 0x7ff;		status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));		// see if EP is stalled		if (status == -EPIPE) {			// set up stalled condition			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));		}		// if any error occured: ignore this td, and continue		if (status != 0) {			//uhci_show_td (desc);			urb->error_count++;			goto recycle;		}		else			urb->actual_length = actual_length;	recycle:		if (urb->complete) {			//dbg("process_interrupt: calling completion, status %i",status);			urb->status = status;						spin_unlock(&s->urb_list_lock);						urb->complete ((struct urb *) urb);						spin_lock(&s->urb_list_lock);						urb->status = -EINPROGRESS;		}		// Recycle INT-TD if interval!=0, else mark TD as one-shot		if (urb->interval) {			desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);			if (status==0) {				((urb_priv_t*)urb->hcpriv)->started=jiffies;				desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),				      usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);				usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));			} else {				desc->hw.td.info |= (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),				      usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);			}			desc->hw.td.status= (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |				(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);			mb();		}		else {			desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD		}	}	return ret;}// mode: 1: force processing, don't unlink tds (already unlinked)_static int process_iso (uhci_t *s, urb_t *urb, int mode){	int i;	int ret = 0;	urb_priv_t *urb_priv = urb->hcpriv;	struct list_head *p = urb_priv->desc_list.next;	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);	dbg("urb contains iso request");	if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode)		return -EXDEV;	// last TD not finished	urb->error_count = 0;	urb->actual_length = 0;	urb->status = 0;	dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),	    urb->number_of_packets,mode,desc->hw.td.status);	for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) {		desc = list_entry (p, uhci_desc_t, desc_list);				//uhci_show_td(desc);		if (desc->hw.td.status & TD_CTRL_ACTIVE) {			// means we have completed the last TD, but not the TDs before			desc->hw.td.status &= ~TD_CTRL_ACTIVE;			dbg("TD still active (%x)- grrr. paranoia!", desc->hw.td.status);			ret = -EXDEV;			urb->iso_frame_desc[i].status = ret;			unlink_td (s, desc, 1);			// FIXME: immediate deletion may be dangerous			goto err;		}		if (!mode)			unlink_td (s, desc, 1);		if (urb->number_of_packets <= i) {			dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);			ret = -EINVAL;			goto err;		}		if (urb->iso_frame_desc[i].offset + urb->transfer_buffer != bus_to_virt (desc->hw.td.buffer)) {			// Hm, something really weird is going on			dbg("Pointer Paranoia: %p!=%p", urb->iso_frame_desc[i].offset + urb->transfer_buffer, bus_to_virt (desc->hw.td.buffer));			ret = -EINVAL;			urb->iso_frame_desc[i].status = ret;			goto err;		}		urb->iso_frame_desc[i].actual_length = (desc->hw.td.status + 1) & 0x7ff;		urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));		urb->actual_length += urb->iso_frame_desc[i].actual_length;	      err:		if (urb->iso_frame_desc[i].status != 0) {			urb->error_count++;			urb->status = urb->iso_frame_desc[i].status;		}		dbg("process_iso: %i: len:%d %08x status:%x",		     i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status);		delete_desc (desc);		list_del (p);	}		dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);	return ret;}_static int process_urb (uhci_t *s, struct list_head *p){	int ret = 0;	urb_t *urb;	urb=list_entry (p, urb_t, urb_list);	//dbg("process_urb: found queued urb: %p", urb);	switch (usb_pipetype (urb->pipe)) {	case PIPE_CONTROL:		ret = process_transfer (s, urb, 1);		break;	case PIPE_BULK:		if (!s->avoid_bulk.counter)			ret = process_transfer (s, urb, 1);		else			return 0;		break;	case PIPE_ISOCHRONOUS:		ret = process_iso (s, urb, 0);		break;	case PIPE_INTERRUPT:		ret = process_interrupt (s, urb);		break;	}	if (urb->status != -EINPROGRESS) {		int proceed = 0;		dbg("dequeued urb: %p", urb);		dequeue_urb (s, urb);#ifdef DEBUG_SLAB		kmem_cache_free(urb_priv_kmem, urb->hcpriv);#else		kfree (urb->hcpriv);#endif		if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) {			urb_t *tmp = urb->next;	// pointer to first urb			int is_ring = 0;						if (urb->next) {				do {					if (tmp->status != -EINPROGRESS) {						proceed = 1;						break;					}					tmp = tmp->next;				}				while (tmp != NULL && tmp != urb->next);				if (tmp == urb->next)					is_ring = 1;			}			spin_unlock(&s->urb_list_lock);			// In case you need the current URB status for your completion handler			if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) {				dbg("process_transfer: calling early completion");				urb->complete ((struct urb *) urb);				if (!proceed && is_ring && (urb->status != -ENOENT))					uhci_submit_urb (urb);			}			if (proceed && urb->next) {				// if there are linked urbs - handle submitting of them right now.				tmp = urb->next;	// pointer to first urb				do {					if ((tmp->status != -EINPROGRESS) && (tmp->status != -ENOENT) && uhci_submit_urb (tmp) != 0)						break;					tmp = tmp->next;				}				while (tmp != NULL && tmp != urb->next);	// submit until we reach NULL or our own pointer or submit fails				if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE)) {					dbg("process_transfer: calling completion");					urb->complete ((struct urb *) urb);				}			}			spin_lock(&s->urb_list_lock);			usb_dec_dev_use (urb->dev);		}	}	return ret;}_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs){	uhci_t *s = __uhci;	unsigned int io_addr = s->io_addr;	unsigned short status;	struct list_head *p, *p2;	/*	 * Read the interrupt status, and write it back to clear the	 * interrupt cause	 */	status = inw (io_addr + USBSTS);	if (!status)		/* shared interrupt, not mine */		return;	dbg("interrupt");	if (s

⌨️ 快捷键说明

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