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

📄 uhci-hcd.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 5 页
字号:
	/* Find the first URB in the queue */
	if (eurbp->queued) {
		struct list_head *head = &eurbp->queue_list;

		tmp = head->next;
		while (tmp != head) {
			struct urb_priv *turbp =
				list_entry(tmp, struct urb_priv, queue_list);

			if (!turbp->queued)
				break;

			tmp = tmp->next;
		}
	} else
		tmp = &eurbp->queue_list;

	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(td_token(lltd)) ^ 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 = cpu_to_le32(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_hcd *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(td_token(pltd)) ^ 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 = cpu_to_le32(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_hcd *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);
	INIT_LIST_HEAD(&urbp->urb_list);

	list_add_tail(&urbp->urb_list, &uhci->urb_list);

	urb->hcpriv = urbp;

	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 uhci_hcd *uhci, struct urb *urb)
{
	struct list_head *head, *tmp;
	struct urb_priv *urbp;

	urbp = (struct urb_priv *)urb->hcpriv;
	if (!urbp)
		return;

	if (!list_empty(&urbp->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);

	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_hcd *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 = cpu_to_le32(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_hcd *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 uhci_hcd *uhci, struct urb *urb)
{
	struct urb_priv *urbp = (struct urb_priv *)urb->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 = TD_CTRL_ACTIVE | uhci_maxerr(3);
	if (urb->dev->speed == USB_SPEED_LOW)
		status |= TD_CTRL_LS;

	/*
	 * 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 | uhci_explen(7),
		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 | uhci_explen(pktsze - 1),
			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_explen(UHCI_NULL_DATA_SIZE), 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->dev->speed == USB_SPEED_LOW) {
		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 uhci_hcd *uhci, struct urb *urb)
{
	struct list_head *tmp, *head;
	struct urb_priv *urbp = (struct urb_priv *)urb->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) {
		err("unable to allocate new QH for control retrigger");
		return -ENOMEM;
	}

	urbp->qh->urbp = urbp;

	/* One TD, who cares about Breadth first? */
	uhci_insert_tds_in_qh(urbp->qh, urb, 0);

	/* Low speed or small transfers gets a different queue and treatment */
	if (urb->dev->speed == USB_SPEED_LOW)
		uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
	else
		uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);

	return -EINPROGRESS;
}


static int uhci_result_control(struct uhci_hcd *uhci, 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(td));
	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(td));
		if (status & TD_CTRL_ACTIVE)
			return -EINPROGRESS;

		urb->actual_length += uhci_actual_length(td_status(td));

		if (status)
			goto td_error;

		/* Check to see if we received a short packet */
		if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
			if (urb->transfer_flags & USB_DISABLE_SPD) {
				ret = -EREMOTEIO;
				goto err;
			}

			if (uhci_packetid(td_token(td)) == USB_PID_IN)

⌨️ 快捷键说明

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