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

📄 uhci-hcd.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 5 页
字号:
				return usb_control_retrigger_status(uhci, urb);
			else
				return 0;
		}
	}

status_phase:
	td = list_entry(tmp, struct uhci_td, list);

	/* Control status phase */
	status = td_status(td);

#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 (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 (uhci_status_bits(status))
		goto td_error;

	return 0;

td_error:
	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
	if (ret == -EPIPE)
		/* endpoint has stalled - mark it halted */
		usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)),
	    			uhci_packetout(td_token(td)));

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;
}

/*
 * Interrupt transfers
 */
static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb)
{
	struct uhci_td *td;
	unsigned long destination, status;
	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;

	if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))
		return -EINVAL;

	/* The "pipe" thing contains the destination in bits 8--18 */
	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);

	status = TD_CTRL_ACTIVE | TD_CTRL_IOC;
	if (urb->dev->speed == USB_SPEED_LOW)
		status |= TD_CTRL_LS;

	td = uhci_alloc_td(uhci, urb->dev);
	if (!td)
		return -ENOMEM;

	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
	destination |= uhci_explen(urb->transfer_buffer_length - 1);

	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));

	uhci_add_td_to_urb(urb, td);
	uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle);

	uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td);

	return -EINPROGRESS;
}

static int uhci_result_interrupt(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;

	urb->actual_length = 0;

	head = &urbp->td_list;
	tmp = head->next;
	while (tmp != 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;

		if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
			if (urb->transfer_flags & USB_DISABLE_SPD) {
				ret = -EREMOTEIO;
				goto err;
			} else
				return 0;
		}
	}

	return 0;

td_error:
	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
	if (ret == -EPIPE)
		/* endpoint has stalled - mark it halted */
		usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)),
	    			uhci_packetout(td_token(td)));

err:
	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
		/* Some debugging code */
		dbg("uhci_result_interrupt/bulk() failed with status %x",
			status);

		if (errbuf) {
			/* Print the chain for debugging purposes */
			if (urbp->qh)
				uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
			else
				uhci_show_td(td, errbuf, ERRBUF_LEN, 0);

			lprintk(errbuf);
		}
	}

	return ret;
}

static void uhci_reset_interrupt(struct uhci_hcd *uhci, struct urb *urb)
{
	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
	struct uhci_td *td;
	unsigned long flags;

	spin_lock_irqsave(&urb->lock, flags);

	td = list_entry(urbp->td_list.next, struct uhci_td, list);

	td->status = (td->status & cpu_to_le32(0x2F000000)) | cpu_to_le32(TD_CTRL_ACTIVE | TD_CTRL_IOC);
	td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
	td->token |= cpu_to_le32(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));

	urb->status = -EINPROGRESS;

	spin_unlock_irqrestore(&urb->lock, flags);
}

/*
 * Bulk transfers
 */
static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
{
	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;
	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
	dma_addr_t data = urbp->transfer_buffer_dma_handle;

	if (len < 0)
		return -EINVAL;

	/* Can't have low speed bulk transfers */
	if (urb->dev->speed == USB_SPEED_LOW)
		return -EINVAL;

	/* The "pipe" thing contains the destination in bits 8--18 */
	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);

	/* 3 errors */
	status = TD_CTRL_ACTIVE | uhci_maxerr(3);
	if (!(urb->transfer_flags & USB_DISABLE_SPD))
		status |= TD_CTRL_SPD;

	/*
	 * Build the DATA TD's
	 */
	do {	/* Allow zero length packets */
		int pktsze = len;

		if (pktsze > maxsze)
			pktsze = maxsze;

		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(pktsze - 1) |
			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
			data);

		data += pktsze;
		len -= maxsze;

		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
			usb_pipeout(urb->pipe));
	} while (len > 0);

	/*
	 * USB_ZERO_PACKET means adding a 0-length packet, if
	 * direction is OUT and the transfer_length was an
	 * exact multiple of maxsze, hence
	 * (len = transfer_length - N * maxsze) == 0
	 * however, if transfer_length == 0, the zero packet
	 * was already prepared above.
	 */
	if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) &&
	   !len && urb->transfer_buffer_length) {
		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(UHCI_NULL_DATA_SIZE) |
			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
			data);

		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
			usb_pipeout(urb->pipe));
	}

	/* Set the flag on the last packet */
	td->status |= cpu_to_le32(TD_CTRL_IOC);

	qh = uhci_alloc_qh(uhci, urb->dev);
	if (!qh)
		return -ENOMEM;

	urbp->qh = qh;
	qh->urbp = urbp;

	/* Always assume breadth first */
	uhci_insert_tds_in_qh(qh, urb, 1);

	if (urb->transfer_flags & USB_QUEUE_BULK && eurb)
		uhci_append_queued_urb(uhci, eurb, urb);
	else
		uhci_insert_qh(uhci, uhci->skel_bulk_qh, urb);

	uhci_inc_fsbr(uhci, urb);

	return -EINPROGRESS;
}

/* We can use the result interrupt since they're identical */
#define uhci_result_bulk uhci_result_interrupt

/*
 * Isochronous transfers
 */
static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
{
	struct urb *last_urb = NULL;
	struct list_head *tmp, *head;
	int ret = 0;

	head = &uhci->urb_list;
	tmp = head->next;
	while (tmp != head) {
		struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
		struct urb *u = up->urb;

		tmp = tmp->next;

		/* look for pending URB's with identical pipe handle */
		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
		    (u->status == -EINPROGRESS) && (u != urb)) {
			if (!last_urb)
				*start = u->start_frame;
			last_urb = u;
		}
	}

	if (last_urb) {
		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
		ret = 0;
	} else
		ret = -1;	/* no previous urb found */

	return ret;
}

static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb)
{
	int limits;
	unsigned int start = 0, end = 0;

	if (urb->number_of_packets > 900)	/* 900? Why? */
		return -EFBIG;

	limits = isochronous_find_limits(uhci, urb, &start, &end);

	if (urb->transfer_flags & USB_ISO_ASAP) {
		if (limits) {
			int curframe;

			curframe = uhci_get_current_frame_number(uhci) % UHCI_NUMFRAMES;
			urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
		} else
			urb->start_frame = end;
	} else {
		urb->start_frame %= UHCI_NUMFRAMES;
		/* FIXME: Sanity check */
	}

	return 0;
}

/*
 * Isochronous transfers
 */
static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
	struct uhci_td *td;
	int i, ret, frame;
	int status, destination;
	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;

	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);

	ret = isochronous_find_start(uhci, urb);
	if (ret)
		return ret;

	frame = urb->start_frame;
	for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
		if (!urb->iso_frame_desc[i].length)
			continue;

		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(urb->iso_frame_desc[i].length - 1),
			urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset);

		if (i + 1 >= urb->number_of_packets)
			td->status |= cpu_to_le32(TD_CTRL_IOC);

		uhci_insert_td_frame_list(uhci, td, frame);
	}

	return -EINPROGRESS;
}

static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
	struct list_head *tmp, *head;
	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
	int status;
	int i, ret = 0;

	urb->actual_length = 0;

	i = 0;
	head = &urbp->td_list;
	tmp = head->next;
	while (tmp != head) {
		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
		int actlength;

		tmp = tmp->next;

		if (td_status(td) & TD_CTRL_ACTIVE)
			return -EINPROGRESS;

		actlength = uhci_actual_length(td_status(td));
		urb->iso_frame_desc[i].actual_length = actlength;
		urb->actual_length += actlength;

		status = uhci_map_status(uhci_status_bits(td_status(td)), usb_pipeout(urb->pipe));
		urb->iso_frame_desc[i].status = status;
		if (status) {
			urb->error_count++;
			ret = status;
		}

		i++;
	}

	return ret;
}

/*
 * MUST be called with uhci->urb_list_lock acquired
 */
static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
{
	struct list_head *tmp, *head;

	/* We don't match Isoc transfers since they are special */
	if (usb_pipeisoc(urb->pipe))
		return NULL;

	head = &uhci->urb_list;
	tmp = head->next;
	while (tmp != head) {
		struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
		struct urb *u = up->urb;

		tmp = tmp->next;

		if (u->dev == urb->dev && u->status == -EINPROGRESS) {
			/* For control, ignore the direction */
			if (usb_pipecontrol(urb->pipe) &&
			    (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
				return u;
			else if (u->pipe == urb->pipe)
				return u;
		}
	}

	return NULL;
}

static int uhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, int mem_flags)
{
	int ret = -EINVAL;
	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
	unsigned long flags;
	struct urb *eurb;
	int bustime;

	spin_lock_irqsave(&uhci->urb_list_lock, flags);

	eurb = uhci_find_urb_ep(uhci, urb);
	if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
		return -ENXIO;
	}

	if (!uhci_alloc_urb_priv(uhci, urb)) {
		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
		return -ENOMEM;
	}

	switch (usb_pipetype(urb->pipe)) {
	case PIPE_CONTROL:
		ret = uhci_submit_control(uhci, urb);
		break;
	case PIPE_INTERRUPT:
		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
			bustime = usb_check_bandwidth(urb->dev, urb);
			if (bustime < 0)
				ret = bustime;
			else {
				ret = uhci_submit_interrupt(uhci, urb);
				if (ret == -EINPROGRESS)
					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
			}
		} else		/* bandwidth is already set */
			ret = uhci_submit_interrupt(uhci, urb);
		break;
	case PIPE_BULK:
		ret = uhci_submit_bulk(uhci, urb, eurb);
		break;
	case PIPE_ISOCHRONOUS:
		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
			if (urb->number_of_packets <= 0) {
				ret = -EINVAL;
				break;
			}
			bustime = usb_check_bandwidth(urb->dev, urb);
			if (bustime < 0) {
				ret = bustime;
				break;
			}

			ret = uhci_submit_isochronous(uhci, urb);
			if (ret == -EINPROGRESS)
				usb_claim_bandwidth(urb->dev, urb, bustime, 1);
		} else		/* bandwidth is already set */
			ret = uhci_submit_isochronous(uhci, urb);
		break;
	}

⌨️ 快捷键说明

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