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

📄 uhci.c

📁 ep9315平台下USB驱动的源码
💻 C
📖 第 1 页 / 共 5 页
字号:
	furbp = list_entry(tmp, struct urb_priv, queue_list);	lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);	lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),		uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1));	/* All qh's in the queue need to link to the next queue */	urbp->qh->link = eurbp->qh->link;	mb();			/* Make sure we flush everything */	/* Only support bulk right now, so no depth */	lltd->link = urbp->qh->dma_handle | UHCI_PTR_QH;	list_add_tail(&urbp->queue_list, &furbp->queue_list);	urbp->queued = 1;	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);}static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb){	struct urb_priv *urbp, *nurbp;	struct list_head *head, *tmp;	struct urb_priv *purbp;	struct uhci_td *pltd;	unsigned int toggle;	unsigned long flags;	urbp = urb->hcpriv;	spin_lock_irqsave(&uhci->frame_list_lock, flags);	if (list_empty(&urbp->queue_list))		goto out;	nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);	/* Fix up the toggle for the next URB's */	if (!urbp->queued)		/* We set the toggle when we unlink */		toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));	else {		/* If we're in the middle of the queue, grab the toggle */		/*  from the TD previous to us */		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,				queue_list);		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);		toggle = uhci_toggle(pltd->info) ^ 1;	}	head = &urbp->queue_list;	tmp = head->next;	while (head != tmp) {		struct urb_priv *turbp;		turbp = list_entry(tmp, struct urb_priv, queue_list);		tmp = tmp->next;		if (!turbp->queued)			break;		toggle = uhci_fixup_toggle(turbp->urb, toggle);	}	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),		usb_pipeout(urb->pipe), toggle);	if (!urbp->queued) {		nurbp->queued = 0;		_uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->urb);	} else {		/* We're somewhere in the middle (or end). A bit trickier */		/*  than the head scenario */		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,				queue_list);		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);		if (nurbp->queued)			pltd->link = nurbp->qh->dma_handle | UHCI_PTR_QH;		else			/* The next URB happens to be the beginning, so */			/*  we're the last, end the chain */			pltd->link = UHCI_PTR_TERM;	}	list_del_init(&urbp->queue_list);out:	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);}static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb){	struct urb_priv *urbp;	urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);	if (!urbp) {		err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");		return NULL;	}	memset((void *)urbp, 0, sizeof(*urbp));	urbp->inserttime = jiffies;	urbp->fsbrtime = jiffies;	urbp->urb = urb;	urbp->dev = urb->dev;		INIT_LIST_HEAD(&urbp->td_list);	INIT_LIST_HEAD(&urbp->queue_list);	INIT_LIST_HEAD(&urbp->complete_list);	urb->hcpriv = urbp;	if (urb->dev != uhci->rh.dev) {		if (urb->transfer_buffer_length) {			urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev,				urb->transfer_buffer, urb->transfer_buffer_length,				usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE :				PCI_DMA_TODEVICE);			if (!urbp->transfer_buffer_dma_handle)				return NULL;		}		if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) {			urbp->setup_packet_dma_handle = pci_map_single(uhci->dev,				urb->setup_packet, sizeof(struct usb_ctrlrequest),				PCI_DMA_TODEVICE);			if (!urbp->setup_packet_dma_handle)				return NULL;		}	}	return urbp;}/* * MUST be called with urb->lock acquired */static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td){	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	td->urb = urb;	list_add_tail(&td->list, &urbp->td_list);}/* * MUST be called with urb->lock acquired */static void uhci_remove_td_from_urb(struct uhci_td *td){	if (list_empty(&td->list))		return;	list_del_init(&td->list);	td->urb = NULL;}/* * MUST be called with urb->lock acquired */static void uhci_destroy_urb_priv(struct urb *urb){	struct list_head *head, *tmp;	struct urb_priv *urbp;	struct uhci *uhci;	urbp = (struct urb_priv *)urb->hcpriv;	if (!urbp)		return;	if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) {		warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb);		return;	}	if (!list_empty(&urb->urb_list))		warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb);	if (!list_empty(&urbp->complete_list))		warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb);	uhci = urbp->dev->bus->hcpriv;	head = &urbp->td_list;	tmp = head->next;	while (tmp != head) {		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);		tmp = tmp->next;		uhci_remove_td_from_urb(td);		uhci_remove_td(uhci, td);		uhci_free_td(uhci, td);	}	if (urbp->setup_packet_dma_handle) {		pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle,			sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);		urbp->setup_packet_dma_handle = 0;	}	if (urbp->transfer_buffer_dma_handle) {		pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle,			urb->transfer_buffer_length, usb_pipein(urb->pipe) ?			PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);		urbp->transfer_buffer_dma_handle = 0;	}	urb->hcpriv = NULL;	kmem_cache_free(uhci_up_cachep, urbp);}static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb){	unsigned long flags;	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	spin_lock_irqsave(&uhci->frame_list_lock, flags);	if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) {		urbp->fsbr = 1;		if (!uhci->fsbr++ && !uhci->fsbrtimeout)			uhci->skel_term_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH;	}	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);}static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb){	unsigned long flags;	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	spin_lock_irqsave(&uhci->frame_list_lock, flags);	if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) {		urbp->fsbr = 0;		if (!--uhci->fsbr)			uhci->fsbrtimeout = jiffies + FSBR_DELAY;	}	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);}/* * 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 -EOVERFLOW;	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 -EINVAL;}/* * Control transfers */static int uhci_submit_control(struct urb *urb){	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;	struct uhci_td *td;	struct uhci_qh *qh;	unsigned long destination, status;	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));	int len = urb->transfer_buffer_length;	dma_addr_t data = urbp->transfer_buffer_dma_handle;	/* The "pipe" thing contains the destination in bits 8--18 */	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;	/* 3 errors */	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27);	/*	 * Build the TD for the control request	 */	td = uhci_alloc_td(uhci, urb->dev);	if (!td)		return -ENOMEM;	uhci_add_td_to_urb(urb, td);	uhci_fill_td(td, status, destination | (7 << 21),		urbp->setup_packet_dma_handle);	/*	 * If direction is "send", change the frame from SETUP (0x2D)	 * to OUT (0xE1). Else change it from SETUP to IN (0x69).	 */	destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe));	if (!(urb->transfer_flags & USB_DISABLE_SPD))		status |= TD_CTRL_SPD;	/*	 * Build the DATA TD's	 */	while (len > 0) {		int pktsze = len;		if (pktsze > maxsze)			pktsze = maxsze;		td = uhci_alloc_td(uhci, urb->dev);		if (!td)			return -ENOMEM;		/* Alternate Data0/1 (start with Data1) */		destination ^= TD_TOKEN_TOGGLE;			uhci_add_td_to_urb(urb, td);		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),			data);		data += pktsze;		len -= pktsze;	}	/*	 * Build the final TD for control status 	 */	td = uhci_alloc_td(uhci, urb->dev);	if (!td)		return -ENOMEM;	/*	 * It's IN if the pipe is an output pipe or we're not expecting	 * data back.	 */	destination &= ~TD_TOKEN_PID_MASK;	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)		destination |= USB_PID_IN;	else		destination |= USB_PID_OUT;	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */	status &= ~TD_CTRL_SPD;	uhci_add_td_to_urb(urb, td);	uhci_fill_td(td, status | TD_CTRL_IOC,		destination | (UHCI_NULL_DATA_SIZE << 21), 0);	qh = uhci_alloc_qh(uhci, urb->dev);	if (!qh)		return -ENOMEM;	urbp->qh = qh;	qh->urbp = urbp;	/* Low speed or small transfers gets a different queue and treatment */	if (urb->pipe & TD_CTRL_LS) {		uhci_insert_tds_in_qh(qh, urb, 0);		uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);	} else {		uhci_insert_tds_in_qh(qh, urb, 1);		uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);		uhci_inc_fsbr(uhci, urb);	}	return -EINPROGRESS;}static int usb_control_retrigger_status(struct urb *urb);static int uhci_result_control(struct urb *urb){	struct list_head *tmp, *head;	struct urb_priv *urbp = urb->hcpriv;	struct uhci_td *td;	unsigned int status;	int ret = 0;	if (list_empty(&urbp->td_list))		return -EINVAL;	head = &urbp->td_list;	if (urbp->short_control_packet) {		tmp = head->prev;		goto status_phase;	}	tmp = head->next;	td = list_entry(tmp, struct uhci_td, list);	/* The first TD is the SETUP phase, check the status, but skip */	/*  the count */	status = uhci_status_bits(td->status);	if (status & TD_CTRL_ACTIVE)		return -EINPROGRESS;	if (status)		goto td_error;	urb->actual_length = 0;	/* The rest of the TD's (but the last) are data */	tmp = tmp->next;	while (tmp != head && tmp->next != head) {		td = list_entry(tmp, struct uhci_td, list);		tmp = tmp->next;		status = uhci_status_bits(td->status);		if (status & TD_CTRL_ACTIVE)			return -EINPROGRESS;		urb->actual_length += uhci_actual_length(td->status);		if (status)			goto td_error;		/* Check to see if we received a short packet */		if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {			if (urb->transfer_flags & USB_DISABLE_SPD) {				ret = -EREMOTEIO;				goto err;			}			if (uhci_packetid(td->info) == USB_PID_IN)				return usb_control_retrigger_status(urb);			else				return 0;		}	}status_phase:	td = list_entry(tmp, struct uhci_td, list);	/* Control status phase */	status = uhci_status_bits(td->status);#ifdef I_HAVE_BUGGY_APC_BACKUPS	/* APC BackUPS Pro kludge */	/* It tries to send all of the descriptor instead of the amount */	/*  we requested */	if (td->status & TD_CTRL_IOC &&	/* IOC is masked out by uhci_status_bits */	    status & TD_CTRL_ACTIVE &&	    status & TD_CTRL_NAK)		return 0;#endif	if (status & TD_CTRL_ACTIVE)		return -EINPROGRESS;	if (status)		goto td_error;	return 0;td_error:	ret = uhci_map_status(status, uhci_packetout(td->info));	if (ret == -EPIPE)		/* endpoint has stalled - mark it halted */		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),	    			uhci_packetout(td->info));err:	if ((debug == 1 && ret != -EPIPE) || debug > 1) {		/* Some debugging code */		dbg("uhci_result_control() failed with status %x", status);		if (errbuf) {			/* Print the chain for debugging purposes */			uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);			lprintk(errbuf);		}	}	return ret;}static int usb_control_retrigger_status(struct urb *urb){	struct list_head *tmp, *head;	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	struct uhci *uhci = urb->dev->bus->hcpriv;	urbp->short_control_packet = 1;	/* Create a new QH to avoid pointer overwriting problems */	uhci_remove_qh(uhci, urbp->qh);	/* Delete all of the TD's except for the status TD at the end */	head = &urbp->td_list;	tmp = head->next;	while (tmp != head && tmp->next != head) {		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);		tmp = tmp->next;		uhci_remove_td_from_urb(td);		uhci_remove_td(uhci, td);		uhci_free_td(uhci, td);	}	urbp->qh = uhci_alloc_qh(uhci, urb->dev);	if (!urbp->qh) {

⌨️ 快捷键说明

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