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

📄 uhci.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 4 页
字号:
{	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;	unsigned char *data = urb->transfer_buffer;	/* 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(urb->dev);	if (!td)		return -ENOMEM;	uhci_add_td_to_urb(urb, td);	uhci_fill_td(td, status, destination | (7 << 21),		virt_to_bus(urb->setup_packet));	/*	 * 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(urb->dev);		if (!td)			return -ENOMEM;		/* Alternate Data0/1 (start with Data1) */		destination ^= 1 << TD_TOKEN_TOGGLE;			uhci_add_td_to_urb(urb, td);		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),			virt_to_bus(data));		data += pktsze;		len -= pktsze;	}	/*	 * Build the final TD for control status 	 */	td = uhci_alloc_td(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_PID;	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)		destination |= USB_PID_IN;	else		destination |= USB_PID_OUT;	destination |= 1 << 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(urb->dev);	if (!qh)		return -ENOMEM;	/* 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, qh);	} else {		uhci_insert_tds_in_qh(qh, urb, 1);		uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh);		uhci_inc_fsbr(uhci, urb);	}	urbp->qh = qh;	uhci_add_urb_list(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 (!urbp)		return -EINVAL;	head = &urbp->list;	if (head->next == head)		return -EINVAL;	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;		if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) &&		    !(td->status & TD_CTRL_ACTIVE)) {			uhci_inc_fsbr(urb->dev->bus->hcpriv, urb);			urbp->fsbr_timeout = 0;			td->status &= ~TD_CTRL_IOC;		}		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 && ret != -EPIPE) {		/* Some debugging code */		dbg("uhci_result_control() failed with status %x", status);		/* Print the chain for debugging purposes */		uhci_show_urb_queue(urb);	}	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->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(urb, td);		uhci_remove_td(uhci, td);		uhci_free_td(td);	}	urbp->qh = uhci_alloc_qh(urb->dev);	if (!urbp->qh) {		err("unable to allocate new QH for control retrigger");		return -ENOMEM;	}	/* 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->pipe & TD_CTRL_LS)		uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);	else		uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);	return -EINPROGRESS;}/* * Interrupt transfers */static int uhci_submit_interrupt(struct urb *urb){	struct uhci_td *td;	unsigned long destination, status;	struct uhci *uhci = (struct uhci *)urb->dev->bus->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 = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC;	td = uhci_alloc_td(urb->dev);	if (!td)		return -ENOMEM;	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);	destination |= ((urb->transfer_buffer_length - 1) << 21);	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,		virt_to_bus(urb->transfer_buffer));	uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td);	uhci_add_urb_list(uhci, urb);	return -EINPROGRESS;}static int uhci_result_interrupt(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 (!urbp)		return -EINVAL;	urb->actual_length = 0;	head = &urbp->list;	tmp = head->next;	while (tmp != head) {		td = list_entry(tmp, struct uhci_td, list);		tmp = tmp->next;		if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) &&		    !(td->status & TD_CTRL_ACTIVE)) {			uhci_inc_fsbr(urb->dev->bus->hcpriv, urb);			urbp->fsbr_timeout = 0;			td->status &= ~TD_CTRL_IOC;		}		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;		if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {			usb_settoggle(urb->dev, uhci_endpoint(td->info),				uhci_packetout(td->info),				uhci_toggle(td->info) ^ 1);			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->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 && ret != -EPIPE) {		/* Some debugging code */		dbg("uhci_result_interrupt/bulk() failed with status %x",			status);		/* Print the chain for debugging purposes */		if (urbp->qh)			uhci_show_urb_queue(urb);		else			uhci_show_td(td);	}	return ret;}static void uhci_reset_interrupt(struct urb *urb){	struct list_head *tmp;	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	struct uhci_td *td;	if (!urbp)		return;	tmp = urbp->list.next;	td = list_entry(tmp, struct uhci_td, list);	if (!td)		return;	td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;	td->info &= ~(1 << TD_TOKEN_TOGGLE);	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));	urb->status = -EINPROGRESS;}/* * Bulk transfers */static int uhci_submit_bulk(struct urb *urb, struct urb *eurb){	struct uhci_td *td;	struct uhci_qh *qh;	unsigned long destination, status;	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));	int len = urb->transfer_buffer_length;	unsigned char *data = urb->transfer_buffer;	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	if (len < 0)		return -EINVAL;	/* Can't have low speed bulk transfers */	if (urb->pipe & TD_CTRL_LS)		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 | (3 << TD_CTRL_C_ERR_SHIFT);	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(urb->dev);		if (!td)			return -ENOMEM;		uhci_add_td_to_urb(urb, td);		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) |			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),			virt_to_bus(data));		data += pktsze;		len -= maxsze;		if (len <= 0)			td->status |= TD_CTRL_IOC;		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),			usb_pipeout(urb->pipe));	} while (len > 0);	qh = uhci_alloc_qh(urb->dev);	if (!qh)		return -ENOMEM;	urbp->qh = qh;	/* Always assume depth first */	uhci_insert_tds_in_qh(qh, urb, 1);	if (urb->transfer_flags & USB_QUEUE_BULK && eurb) {		urbp->queued = 1;		uhci_append_queued_urb(uhci, eurb, urb);	} else		uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);	uhci_add_urb_list(uhci, 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 urb *urb, unsigned int *start, unsigned int *end){	struct urb *last_urb = NULL;	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;	struct list_head *tmp, *head = &uhci->urb_list;	int ret = 0;	unsigned long flags;	nested_lock(&uhci->urblist_lock, flags);	tmp = head->next;	while (tmp != head) {		struct urb *u = list_entry(tmp, struct urb, urb_list);		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 */	nested_unlock(&uhci->urblist_lock, flags);	return ret;}static int isochronous_find_start(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(urb, &start, &end);	if (urb->transfer_flags & USB_ISO_ASAP) {		if (limits) {			int curframe;			curframe = uhci_get_current_frame_number(urb->dev) % 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;}static int uhci_submit_isochronous(struct urb *urb){	struct uhci_td *td;	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;	int i, ret, framenum;	int status, destination;	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);	ret = isochronous_find_start(urb);	if (ret)		return ret;	framenum = urb->start_frame;	for (i = 0; i < urb->number_of_packets; i++, framenum++) {		if (!urb->iso_frame_desc[i].length)			continue;		td = uhci_alloc_td(urb->dev);		if (!td)			return -ENOMEM;		uhci_add_td_to_urb(urb, td);		uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21),			virt_to_bus(urb->transfer_buffer + urb->iso_frame_desc[i].offset));		if (i + 1 >= urb->number_of_packets)			td->status |= TD_CTRL_IOC;		uhci_insert_td_frame_list(uhci, td, framenum);	}	uhci_add_urb_list(uhci, urb);	return -EINPROGRESS;}static int uhci_result_isochronous(struct urb *urb){	struct list_head *tmp, *head;	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	int status;	int i, ret = 0;	if (!urbp)		return -EINVAL;	urb->actual_length = 0;	i = 0;	head = &urbp->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_CTRL_ACTIVE)			return -EINPROGRESS;		actlength = uhci_actual_length(td->status);		urb->iso_frame_desc[i].actual_length = actlength;		urb->actual_length += actlength;		status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe));		urb->iso_frame_desc[i].status = status;		if (status != 0) {			urb->error_count++;			ret = status;		}		i++;	}	return ret;}static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb){	struct list_head *tmp, *head = &uhci->urb_list;	unsigned long flags;	struct urb *u = NULL;	if (usb_pipeisoc(urb->pipe))		return NULL;	nested_lock(&uhci->urblist_lock, flags);

⌨️ 快捷键说明

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