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

📄 usb-uhci.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	void *data = urb->transfer_buffer;	int leni = urb->transfer_buffer_length;	int len = 0;	int status = 0;	int stat = 0;	int i;	unsigned int io_addr = uhci->io_addr;	__u16 cstatus;	__u16 bmRType_bReq;	__u16 wValue;	__u16 wIndex;	__u16 wLength;	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {		dbg("Root-Hub submit IRQ: every %d ms", urb->interval);		uhci->rh.urb = urb;		uhci->rh.send = 1;		uhci->rh.interval = urb->interval;		rh_init_int_timer (urb);		return 0;	}	bmRType_bReq = cmd->requesttype | cmd->request << 8;	wValue = le16_to_cpu (cmd->value);	wIndex = le16_to_cpu (cmd->index);	wLength = le16_to_cpu (cmd->length);	for (i = 0; i < 8; i++)		uhci->rh.c_p_r[i] = 0;	dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x",	     uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength);	switch (bmRType_bReq) {		/* Request Destination:		   without flags: Device, 		   RH_INTERFACE: interface, 		   RH_ENDPOINT: endpoint,		   RH_CLASS means HUB here, 		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 		 */	case RH_GET_STATUS:		*(__u16 *) data = cpu_to_le16 (1);		OK (2);	case RH_GET_STATUS | RH_INTERFACE:		*(__u16 *) data = cpu_to_le16 (0);		OK (2);	case RH_GET_STATUS | RH_ENDPOINT:		*(__u16 *) data = cpu_to_le16 (0);		OK (2);	case RH_GET_STATUS | RH_CLASS:		*(__u32 *) data = cpu_to_le32 (0);		OK (4);		/* hub power ** */	case RH_GET_STATUS | RH_OTHER | RH_CLASS:		status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1));		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |			((status & USBPORTSC_PEC) >> (3 - 1)) |			(uhci->rh.c_p_r[wIndex - 1] << (0 + 4));		status = (status & USBPORTSC_CCS) |			((status & USBPORTSC_PE) >> (2 - 1)) |			((status & USBPORTSC_SUSP) >> (12 - 2)) |			((status & USBPORTSC_PR) >> (9 - 4)) |			(1 << 8) |	/* power on ** */			((status & USBPORTSC_LSDA) << (-8 + 9));		*(__u16 *) data = cpu_to_le16 (status);		*(__u16 *) (data + 2) = cpu_to_le16 (cstatus);		OK (4);	case RH_CLEAR_FEATURE | RH_ENDPOINT:		switch (wValue) {		case (RH_ENDPOINT_STALL):			OK (0);		}		break;	case RH_CLEAR_FEATURE | RH_CLASS:		switch (wValue) {		case (RH_C_HUB_OVER_CURRENT):			OK (0);	/* hub power over current ** */		}		break;	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:		switch (wValue) {		case (RH_PORT_ENABLE):			CLR_RH_PORTSTAT (USBPORTSC_PE);			OK (0);		case (RH_PORT_SUSPEND):			CLR_RH_PORTSTAT (USBPORTSC_SUSP);			OK (0);		case (RH_PORT_POWER):			OK (0);	/* port power ** */		case (RH_C_PORT_CONNECTION):			SET_RH_PORTSTAT (USBPORTSC_CSC);			OK (0);		case (RH_C_PORT_ENABLE):			SET_RH_PORTSTAT (USBPORTSC_PEC);			OK (0);		case (RH_C_PORT_SUSPEND):/*** WR_RH_PORTSTAT(RH_PS_PSSC); */			OK (0);		case (RH_C_PORT_OVER_CURRENT):			OK (0);	/* port power over current ** */		case (RH_C_PORT_RESET):			uhci->rh.c_p_r[wIndex - 1] = 0;			OK (0);		}		break;	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:		switch (wValue) {		case (RH_PORT_SUSPEND):			SET_RH_PORTSTAT (USBPORTSC_SUSP);			OK (0);		case (RH_PORT_RESET):			SET_RH_PORTSTAT (USBPORTSC_PR);			uhci_wait_ms (10);			uhci->rh.c_p_r[wIndex - 1] = 1;			CLR_RH_PORTSTAT (USBPORTSC_PR);			udelay (10);			SET_RH_PORTSTAT (USBPORTSC_PE);			uhci_wait_ms (10);			SET_RH_PORTSTAT (0xa);			OK (0);		case (RH_PORT_POWER):			OK (0);	/* port power ** */		case (RH_PORT_ENABLE):			SET_RH_PORTSTAT (USBPORTSC_PE);			OK (0);		}		break;	case RH_SET_ADDRESS:		uhci->rh.devnum = wValue;		OK (0);	case RH_GET_DESCRIPTOR:		switch ((wValue & 0xff00) >> 8) {		case (0x01):	/* device descriptor */			len = min (leni, min (sizeof (root_hub_dev_des), wLength));			memcpy (data, root_hub_dev_des, len);			OK (len);		case (0x02):	/* configuration descriptor */			len = min (leni, min (sizeof (root_hub_config_des), wLength));			memcpy (data, root_hub_config_des, len);			OK (len);		case (0x03):	/* string descriptors */			len = usb_root_hub_string (wValue & 0xff,			        uhci->io_addr, "UHCI",				data, wLength);			if (len > 0) {				OK (min (leni, len));			} else 				stat = -EPIPE;		}		break;	case RH_GET_DESCRIPTOR | RH_CLASS:		root_hub_hub_des[2] = uhci->rh.numports;		len = min (leni, min (sizeof (root_hub_hub_des), wLength));		memcpy (data, root_hub_hub_des, len);		OK (len);	case RH_GET_CONFIGURATION:		*(__u8 *) data = 0x01;		OK (1);	case RH_SET_CONFIGURATION:		OK (0);	default:		stat = -EPIPE;	}	dbg("Root-Hub stat port1: %x port2: %x",	     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));	urb->actual_length = len;	urb->status = stat;	urb->dev=NULL;	if (urb->complete)		urb->complete (urb);	return 0;}/*-------------------------------------------------------------------------*/_static int rh_unlink_urb (urb_t *urb){	uhci_t *uhci = urb->dev->bus->hcpriv;	if (uhci->rh.urb==urb) {		dbg("Root-Hub unlink IRQ");		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: 1: regular (unlink QH), 2: QHs already unlinked (for async unlink_urb) */_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 %p, urb_priv %p, qh %p last_desc %p\n",urb,urb_priv, qh, last_desc);	/* 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->flags && 		((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 && (status != -EPIPE)) {	// if any error occurred stop processing of further TDs			// only set ret if status returned an error  is_error:			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->flags = 1; // mark as short control packet					return 0;				}			}			// all other cases: short read is OK			data_toggle = uhci_toggle (desc->hw.td.info);			break;		}		else if (status)			goto is_error;		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);	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;			((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion			spin_unlock(&s->urb_list_lock);						urb->complete ((struct urb *) urb);						spin_lock(&s->urb_list_lock);			((urb_priv_t*)urb->hcpriv)->flags=0;		       					}				if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) &&			    (urb->status != -ENOENT)) {			urb->status = -EINPROGRESS;			// Recycle INT-TD if interval!=0, else mark TD as one-shot			if (urb->interval) {								de

⌨️ 快捷键说明

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