ohci之usb_submit_urb 一

         usb控制器与usb设备之间是通过urb结构来传递数据,urb是usb通信基础。

         驱动在使用urb之前要先通过usb_alloc_urb来创建struct urb结构,并通过usb_fill_xx_urb来初始化创建的urb,然后通过usb_submit_urb把urb提交给主控制器,由控制器进行实际发送,发送完给控制器把urb所有权交还给驱动,并可通过回调函数获取urb发送状况。

        在申请和初始化urb时, 对于不同usb接口标准(ohci,uhci,ehci),是没有区别的. 由于各usb接口标准在其硬件和软件功能分工上存在差异,如uhci在硬件实现逻辑上比较简单,而软件驱动实现比较复杂,ohci硬件逻辑比较复杂,软件实现相对简单,所以 对于不同标准类型的usb_submit_urb低层实现是不同的,其实现是与接口标准相关联的。本文研究对象是在嵌入式系统中应用得比较广泛的ohci接口标准,通过分析ohci的usb_submit_urb来掌握ohci控制器与驱动的任务分工,ohci对于urb处理机制,cpu是samsung的s3c6410.

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)

{
	int				xfertype, max;
	struct usb_device		*dev;
	struct usb_host_endpoint	*ep;
	int				is_out;
	if (!urb || urb->hcpriv || !urb->complete)
		return -EINVAL;
	dev = urb->dev;
	if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
		return -ENODEV;

	ep = usb_pipe_endpoint(dev, urb->pipe);
	if (!ep)
		return -ENOENT;

	urb->ep = ep;
	urb->status = -EINPROGRESS;
	urb->actual_length = 0;

	xfertype = usb_endpoint_type(&ep->desc);
	if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
		struct usb_ctrlrequest *setup =
				(struct usb_ctrlrequest *) urb->setup_packet;

		if (!setup)
			return -ENOEXEC;
		is_out = !(setup->bRequestType & USB_DIR_IN) ||
				!setup->wLength;
	} else {
		is_out = usb_endpoint_dir_out(&ep->desc);
	}

	/* Clear the internal flags and cache the direction for later use */
	urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
			URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
			URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
			URB_DMA_SG_COMBINED);
	urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);

	if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
			dev->state < USB_STATE_CONFIGURED)
		return -ENODEV;

	max = le16_to_cpu(ep->desc.wMaxPacketSize);
	if (max <= 0) {
		dev_dbg(&dev->dev,
			"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
			usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
			__func__, max);
		return -EMSGSIZE;
	}
	if (xfertype == USB_ENDPOINT_XFER_ISOC) {
		int	n, len;

		if (dev->speed == USB_SPEED_SUPER) {
			int     burst = 1 + ep->ss_ep_comp.bMaxBurst;
			int     mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
			max *= burst;
			max *= mult;
		}

		if (dev->speed == USB_SPEED_HIGH) {
			int	mult = 1 + ((max >> 11) & 0x03);
			max &= 0x07ff;
			max *= mult;
		}

		if (urb->number_of_packets <= 0)
			return -EINVAL;
		for (n = 0; n < urb->number_of_packets; n++) {
			len = urb->iso_frame_desc[n].length;
			if (len < 0 || len > max)
				return -EMSGSIZE;
			urb->iso_frame_desc[n].status = -EXDEV;
			urb->iso_frame_desc[n].actual_length = 0;
		}
	}
	if (urb->transfer_buffer_length > INT_MAX)
		return -EMSGSIZE;
	switch (xfertype) {
	case USB_ENDPOINT_XFER_ISOC:
	case USB_ENDPOINT_XFER_INT:
		switch (dev->speed) {
		case USB_SPEED_WIRELESS:
			if (urb->interval < 6)
				return -EINVAL;
			break;
		default:
			if (urb->interval <= 0)
				return -EINVAL;
			break;
		}
		switch (dev->speed) {
		case USB_SPEED_SUPER:	/* units are 125us */
			/* Handle up to 2^(16-1) microframes */
			if (urb->interval > (1 << 15))
				return -EINVAL;
			max = 1 << 15;
			break;
		case USB_SPEED_WIRELESS:
			if (urb->interval > 16)
				return -EINVAL;
			break;
		case USB_SPEED_HIGH:	/* units are microframes */
			if (urb->interval > (1024 * 8))
				urb->interval = 1024 * 8;
			max = 1024 * 8;
			break;
		case USB_SPEED_FULL:	/* units are frames/msec */
		case USB_SPEED_LOW:
			if (xfertype == USB_ENDPOINT_XFER_INT) {
				if (urb->interval > 255)
					return -EINVAL;
				/* NOTE ohci only handles up to 32 */
				max = 128;
			} else {
				if (urb->interval > 1024)
					urb->interval = 1024;
				/* NOTE usb and ohci handle up to 2^15 */
				max = 1024;
			}
			break;
		default:
			return -EINVAL;
		}
		if (dev->speed != USB_SPEED_WIRELESS) {
			/* Round down to a power of 2, no more than max */
			urb->interval = min(max, 1 << ilog2(urb->interval));
		}
	}

	return usb_hcd_submit_urb(urb, mem_flags);
}
1-5行 进行了一些基本逻辑判断,urb为提交给hc的数据结构,当然不能为空,而hcpriv是在hcd中初始化的,这里应该为空,complete为回调函数,urb结束就必须运行的,所以必须定义;
7-9行 usb_dev结构中存放着IN,OUT类型的endpoint指针数组,通过urb的pipe来获取endpoint的方向和端口,来确定要接收urb的endpoint. 
15-26行 对于控制类型的urb除要发送正常数据外,还得包含request请求包,并通过endpoint desc来确定端口的方向。
29-33行 清除并设置flags标准。
39-46行 获取端口最大发送数据长度。
47-72行 对于高速和超速的等时传输一帧或微帧可传2~3次transaction,从端口的desc属性里获取其最长发送长度后对等时传输数据长度进行判断,并初始化。
75-125行 对于interrupt,isochronous传输类型还得根据usb协议确定访问endpoint list间隔,对于高速,端点desc里的wmaxpacketsize中的bit11,1用来确定可以额外几次transaction次数,如果00表示没有额外传输,01表示有1次,10表示2次,11保留。
127行 调用usb_hcd_submit_urb。

int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
	int			status;
	struct usb_hcd		*hcd = bus_to_hcd(urb->dev->bus);

	usb_get_urb(urb);
	atomic_inc(&urb->use_count);
	atomic_inc(&urb->dev->urbnum);
	usbmon_urb_submit(&hcd->self, urb);

	if (is_root_hub(urb->dev)) {
		status = rh_urb_enqueue(hcd, urb);
	} else {
		status = map_urb_for_dma(hcd, urb, mem_flags);
		if (likely(status == 0)) {
			status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
			if (unlikely(status))
				unmap_urb_for_dma(hcd, urb);
		}
	}

	if (unlikely(status)) {
		usbmon_urb_submit_error(&hcd->self, urb, status);
		urb->hcpriv = NULL;
		INIT_LIST_HEAD(&urb->urb_list);
		atomic_dec(&urb->use_count);
		atomic_dec(&urb->dev->urbnum);
		if (atomic_read(&urb->reject))
			wake_up(&usb_kill_urb_queue);
		usb_put_urb(urb);
	}
	return status;
}
6-9行 对urb结构中一些计数器进行初始化。

11-20行 根据urb是传送给root hub还是HC选择不同运行路经,如果是传向root hub则运行rh_urb_enqueue,否则运行与控制器相关的urb_enqueue.

22-31行,对urb提交结果进行判断,如果提交失败,则清除相应urb及里面一些参数。

static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{
	if (usb_endpoint_xfer_int(&urb->ep->desc))
		return rh_queue_status (hcd, urb);
	if (usb_endpoint_xfer_control(&urb->ep->desc))
		return rh_call_control (hcd, urb);
	return -EINVAL;
}

a. 中断类型hub root endpoint

hub是一种承上启下的usb设备,与控制器相连的叫root hub,root hub除了本身的endpoint0之外还有一个interrupt类型的endpoint,该端口用于检测root hub端口设备插入或拔出。

static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
{
	int		retval;
	unsigned long	flags;
	unsigned	len = 1 + (urb->dev->maxchild / 8);

	spin_lock_irqsave (&hcd_root_hub_lock, flags);
	if (hcd->status_urb || urb->transfer_buffer_length < len) {
		dev_dbg (hcd->self.controller, "not queuing rh status urb\n");
		retval = -EINVAL;
		goto done;
	}

	retval = usb_hcd_link_urb_to_ep(hcd, urb);
	if (retval)
		goto done;

	hcd->status_urb = urb;
	urb->hcpriv = hcd;	/* indicate it's queued */
	if (!hcd->uses_new_polling)
		mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));

	/* If a status change has already occurred, report it ASAP */
	else if (HCD_POLL_PENDING(hcd))
		mod_timer(&hcd->rh_timer, jiffies);
	retval = 0;
 done:
	spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
	return retval;
}
中断类型的端口用于监控 root hub上是否有设备插入,root hub通过两种方式来监控是否有设备插入,一种是通过中断方式,使能相应中断,当状态发生变化时产生中断,然后运行中断服务程序获取取状态寄存器信息;另一种方式是:当提交root hub中的interrupt endpoint上的urb时,便会启动用于查询port状态的定时器rh_timer,当定时时间到后就会运行定时器处理函数rh_timer_func。

14行把当前urb添加到endpoint对应urb列表中。

20-25行,设置定时器rh_timer的定时时间,当uses_new_polling为0时,设置定时超时时间为250ms,否则设置当前时间为超时时间,直接运行定时器function函数rh_timer_func。

不管是通过中断方式还是通过主动提交root hub状态查询urb方式 ,最终都是通过root hub中interrupt的urb来返回port状态,它通过调用usb_hcd_poll_rh_status来实现。

void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
	struct urb	*urb;
	int		length;
	unsigned long	flags;
	char		buffer[6];	/* Any root hubs with > 31 ports? */

	if (unlikely(!hcd->rh_pollable))
		return;
	if (!hcd->uses_new_polling && !hcd->status_urb)
		return;

	length = hcd->driver->hub_status_data(hcd, buffer);
	if (length > 0) {
		spin_lock_irqsave(&hcd_root_hub_lock, flags);
		urb = hcd->status_urb;
		if (urb) {
			clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
			hcd->status_urb = NULL;
			urb->actual_length = length;
			memcpy(urb->transfer_buffer, buffer, length);

			usb_hcd_unlink_urb_from_ep(hcd, urb);
			spin_unlock(&hcd_root_hub_lock);
			usb_hcd_giveback_urb(hcd, urb, 0);
			spin_lock(&hcd_root_hub_lock);
		} else {
			length = 0;
			set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
		}
		spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
	}

	if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) : (length == 0 && hcd->status_urb != NULL))
		mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
       13行 通过调用控制器驱动hub_status_data(ohci_s3c2410_hub_status_data)函数来获取状态信息,状态信息通过buffer返回,如成功获取root hub状态信息,由调用urb回调函数hub_irq,然后根据urb返回结果设置hub中event_bits位,如果相应端口产生变化则置event_bits为1,并通过kick_khubd唤醒usb的守护进程hub_thread.

       34、35行根据uses_new_polling和hub状态来设置轮训查询定时时间,本文采用ohci型控制器,在ohci初始化过程中设置了uses_new_pollling为1,且root处于running态,所以设置定时轮询时间为250ms。

static int ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf)
{
	struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);
	struct s3c2410_hcd_port *port;
	int orig;
	int portno;

	orig  = ohci_hub_status_data(hcd, buf);

	if (info == NULL)
		return orig;

	port = &info->port[0];

	for (portno = 0; portno < 2; port++, portno++) {
		if (port->oc_changed == 1 &&
		    port->flags & S3C_HCDFLG_USED) {
			dev_dbg(hcd->self.controller,
				"oc change on port %d\n", portno);

			if (orig < 1)
				orig = 1;

			buf[0] |= 1<<(portno+1);
		}
	}

	return orig;
}
s3c6410usb控制器属于ohci接口标准,所以通过ohc_hub_status_data来读取hub信息。

static int ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
	int		i, changed = 0, length = 1;
	int		any_connected = 0;
	int		rhsc_status;
	unsigned long	flags;

	spin_lock_irqsave (&ohci->lock, flags);
	if (!HCD_HW_ACCESSIBLE(hcd))
		goto done;

	if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
		buf [0] = changed = 1;
	else
		buf [0] = 0;
	if (ohci->num_ports > 7) {
		buf [1] = 0;
		length++;
	}

	ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus);
	rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC;

	for (i = 0; i < ohci->num_ports; i++) {
		u32	status = roothub_portstatus (ohci, i);

		any_connected |= (status & RH_PS_CCS);

		if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC)) {
			changed = 1;
			if (i < 7)
			    buf [0] |= 1 << (i + 1);
			else
			    buf [1] |= 1 << (i - 7);
		}
	}

	if (ohci_root_hub_state_changes(ohci, changed,any_connected, rhsc_status))
		set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
	else
		clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);

done:
	spin_unlock_irqrestore (&ohci->lock, flags);

	return changed ? length : 0;
}
13行中roothub_status()用来读取roothub寄存器进而的status标志;

static inline u32 roothub_status (struct ohci_hcd *hc)
{
	 return ohci_readl (hc, &hc->regs->roothub.status);
 }
roothub.status中主要包含:localpowerstatus,  overcurrentidicator, deviceremotewakeupenable, localpowerstatuschange, overcurrentindicatorchange, clearremotewakeupenable.这里主要关注localpowerstatuschange和overcurrentindicatorchange,前者由于ohci root hub不支持local power status feature,所以读出来永远为“0”,后者表示过流状态是否发生变化。

13-20行通过roothub_status来获取root hub本身状态没有发生变化,设置并设初始化用来表示各个端口状态的变量buf,buf[0]的bit0用来表示root hub自身状态,剩下的7位用来表示port1-port6状态,如果root hub下的接的设备数量num_ports大于7,则还需要buf[1]来表示端口状态;

25-37行用来读取root hub下各port的状态,如果发生变化,则设置相应位。

回到usb_hcd_poll_rh_status的14行,如果返回长度大于0,则设置urb的真实长度,并调用回调函数hub_irq。

只要root hub上有设备插入或拔出,通过中断和轮询机制,usb系统就能知道,并运行相应服务程序。

b. 控制类型root hub endpoint

root hub 的控制endpoint主要用于设置和获取hub及port上信息, 在rh_urb_enqueue函数中,如果为control型endpoint,则运行rh_call_control函数。
static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
{
	struct usb_ctrlrequest *cmd;
 	u16		typeReq, wValue, wIndex, wLength;
	u8		*ubuf = urb->transfer_buffer;
	u8		tbuf [sizeof (struct usb_hub_descriptor)]
		__attribute__((aligned(4)));
	const u8	*bufp = tbuf;
	unsigned	len = 0;
	int		status;
	u8		patch_wakeup = 0;
	u8		patch_protocol = 0;

	might_sleep();

	spin_lock_irq(&hcd_root_hub_lock);
	status = usb_hcd_link_urb_to_ep(hcd, urb);
	spin_unlock_irq(&hcd_root_hub_lock);
	if (status)
		return status;
	urb->hcpriv = hcd;	/* Indicate it's queued */

	cmd = (struct usb_ctrlrequest *) urb->setup_packet;
	typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;
	wValue   = le16_to_cpu (cmd->wValue);
	wIndex   = le16_to_cpu (cmd->wIndex);
	wLength  = le16_to_cpu (cmd->wLength);

	if (wLength > urb->transfer_buffer_length)
		goto error;

	urb->actual_length = 0;
	switch (typeReq) {

	/* DEVICE REQUESTS */

	case DeviceRequest | USB_REQ_GET_STATUS:
		tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
					<< USB_DEVICE_REMOTE_WAKEUP)
				| (1 << USB_DEVICE_SELF_POWERED);
		tbuf [1] = 0;
		len = 2;
		break;
	case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
		if (wValue == USB_DEVICE_REMOTE_WAKEUP)
			device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);
		else
			goto error;
		break;
	case DeviceOutRequest | USB_REQ_SET_FEATURE:
		if (device_can_wakeup(&hcd->self.root_hub->dev)
				&& wValue == USB_DEVICE_REMOTE_WAKEUP)
			device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);
		else
			goto error;
		break;
	case DeviceRequest | USB_REQ_GET_CONFIGURATION:
		tbuf [0] = 1;
		len = 1;
			/* FALLTHROUGH */
	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
		break;
	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
		switch (wValue & 0xff00) {
		case USB_DT_DEVICE << 8:
			switch (hcd->speed) {
			case HCD_USB3:
				bufp = usb3_rh_dev_descriptor;
				break;
			case HCD_USB2:
				bufp = usb2_rh_dev_descriptor;
				break;
			case HCD_USB11:
				bufp = usb11_rh_dev_descriptor;
				break;
			default:
				goto error;
			}
			len = 18;
			if (hcd->has_tt)
				patch_protocol = 1;
			break;
		case USB_DT_CONFIG << 8:
			switch (hcd->speed) {
			case HCD_USB3:
				bufp = ss_rh_config_descriptor;
				len = sizeof ss_rh_config_descriptor;
				break;
			case HCD_USB2:
				bufp = hs_rh_config_descriptor;
				len = sizeof hs_rh_config_descriptor;
				break;
			case HCD_USB11:
				bufp = fs_rh_config_descriptor;
				len = sizeof fs_rh_config_descriptor;
				break;
			default:
				goto error;
			}
			if (device_can_wakeup(&hcd->self.root_hub->dev))
				patch_wakeup = 1;
			break;
		case USB_DT_STRING << 8:
			if ((wValue & 0xff) < 4)
				urb->actual_length = rh_string(wValue & 0xff,hcd, ubuf, wLength);
			else /* unsupported IDs --> "protocol stall" */
				goto error;
			break;
		default:
			goto error;
		}
		break;
	case DeviceRequest | USB_REQ_GET_INTERFACE:
		tbuf [0] = 0;
		len = 1;
			/* FALLTHROUGH */
	case DeviceOutRequest | USB_REQ_SET_INTERFACE:
		break;
	case DeviceOutRequest | USB_REQ_SET_ADDRESS:
		// wValue == urb->dev->devaddr
		dev_dbg (hcd->self.controller, "root hub device address %d\n",
			wValue);
		break;

	/* ENDPOINT REQUESTS */

	case EndpointRequest | USB_REQ_GET_STATUS:
		// ENDPOINT_HALT flag
		tbuf [0] = 0;
		tbuf [1] = 0;
		len = 2;
			/* FALLTHROUGH */
	case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
	case EndpointOutRequest | USB_REQ_SET_FEATURE:
		dev_dbg (hcd->self.controller, "no endpoint features yet\n");
		break;

	default:
		/* non-generic request */
		switch (typeReq) {
		case GetHubStatus:
		case GetPortStatus:
			len = 4;
			break;
		case GetHubDescriptor:
			len = sizeof (struct usb_hub_descriptor);
			break;
		}
		status = hcd->driver->hub_control (hcd,typeReq, wValue, wIndex, tbuf, wLength);
		break;
error:
		/* "protocol stall" on error */
		status = -EPIPE;
	}

	if (status) 
		len = 0;
	if (len) {
		if (urb->transfer_buffer_length < len)
			len = urb->transfer_buffer_length;
		urb->actual_length = len;
		memcpy (ubuf, bufp, len);

		/* report whether RH hardware supports remote wakeup */
		if (patch_wakeup && len > offsetof (struct usb_config_descriptor, bmAttributes))
			((struct usb_config_descriptor *)ubuf)->bmAttributes |= USB_CONFIG_ATT_WAKEUP;

		/* report whether RH hardware has an integrated TT */
		if (patch_protocol && len > offsetof(struct usb_device_descriptor,bDeviceProtocol))
			((struct usb_device_descriptor *) ubuf)->bDeviceProtocol = 1;
	}

	spin_lock_irq(&hcd_root_hub_lock);
	usb_hcd_unlink_urb_from_ep(hcd, urb);

	spin_unlock(&hcd_root_hub_lock);
	usb_hcd_giveback_urb(hcd, urb, status);
	spin_lock(&hcd_root_hub_lock);

	spin_unlock_irq(&hcd_root_hub_lock);
	return 0;
}
该函数比较长,函数主要作用就是根据usb协议,设置不同request tbuf内容及长度,最后调用控制器驱动中的hub_control。
23-27行根据control的setup包得到相应的请求类型及各属性。
33-149行根据得到的请求类型设置相应的tbuf内容及长度。
根据usb 协议table 9-2 Format of setup定义,setup包中的bmRequestType是一个字节变量,其各位代表含义如下:
bit 7:表示数据传送方向,1表示usb设备传向控制器,0表示控制器传向设备;
bit5-6:表示传送数据的类型,0表示标准类型,1表示某一类型,2表示产家定义;
bit0-4:表示数据接收者,0表示接收者是设备,1表示接收者是接口,2表示接收者是端点;
bRequest用来指定请求类型,具体可参考usb 协议table 9-3 standard device requests.
24行中将bmRequestType和bRequest组合一起形成变量typeReq,而33-149就是根据这个typeReq进行相应的操作。
程序中的tbuf用来存放将要返回或发送的数据,len来用表示tbuf字节数,tbuf内容和len长度都是根据usb协议得到的。
如typeReq为DeviceRequet | USB_REQ_GET_STATUS时,根据usb协议table9-3DeviceRequest表示数据为设备指向控制器,请求类型为标准类型,数据接收者为设备,而USB_REQ_GET_STATUS根据usb协议table9-4 表示为get status请求,在usb协议中对各个request请求数据内容,长度都有规定,对于get status在usb 协议中的9.4.5小节有讲到,从9.4.5小节可以发现,get status请求的wLength为2,返回内容根据figure 9-4得到。
对于typeReq为DeviceRequest | USB_REQ_GET_DESCRIPTOR,根据usb协议可以发现,数据为由设备传送给控制器,请求类型为标准类型,数据接收由设备接收,USB_REQ_GET_DESCRIPTOR表示获取描述符,根据协议9.6节可知,usb中包含了多种类型描述符,设备,配置,接口,端口,字符串等描述符,和hub相关的描述符为设备,配置及字符串这三类,而接口和端口则和具体的usb设备相关,在枚举时,设备的接口和端口描述符就是与具体设备相关的,从usb设备上读取的,对于这里的请求的为何种类型可以根据请求中wValue项得到,wValue是两个字节变量,在get status中wValue中它由两部分组成,高8位表示请求描述符类型,低8位表示有多个同类描述符时的索引。所以63-99行中,先根据wValue来得到描述符类型,然后根据不同速度和协议中规定来确定tbuf和len内容。
确定好tbuf和len内容后就会调用控制器驱动的hub_control,在这里调用ohci_s3c2410_hub_control。
static int ohci_s3c2410_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16	wIndex,	char *buf, u16	wLength)
{
	struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);
	struct usb_hub_descriptor *desc;
	int ret = -EINVAL;
	u32 *data = (u32 *)buf;

	if (info == NULL) {
		ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
		goto out;
	}


	switch (typeReq) {
	case SetPortFeature:
		if (wValue == USB_PORT_FEAT_POWER) {
			dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n");
			s3c2410_usb_set_power(info, wIndex, 1);
			goto out;
		}
		break;

	case ClearPortFeature:
		switch (wValue) {
		case USB_PORT_FEAT_C_OVER_CURRENT:
			dev_dbg(hcd->self.controller,"ClearPortFeature: C_OVER_CURRENT\n");

			if (valid_port(wIndex)) {
				info->port[wIndex-1].oc_changed = 0;
				info->port[wIndex-1].oc_status = 0;
			}

			goto out;

		case USB_PORT_FEAT_OVER_CURRENT:
			dev_dbg(hcd->self.controller, "ClearPortFeature: OVER_CURRENT\n");

			if (valid_port(wIndex))
				info->port[wIndex-1].oc_status = 0;

			goto out;

		case USB_PORT_FEAT_POWER:
			dev_dbg(hcd->self.controller, "ClearPortFeature: POWER\n");

			if (valid_port(wIndex)) {
				s3c2410_usb_set_power(info, wIndex, 0);
				return 0;
			}
		}
		break;
	}

	ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
	if (ret)
		goto out;

	switch (typeReq) {
	case GetHubDescriptor:


		desc = (struct usb_hub_descriptor *)buf;

		if (info->power_control == NULL)
			return ret;

		dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n",desc->wHubCharacteristics);

		/* remove the old configurations for power-switching, and
		 * over-current protection, and insert our new configuration
		 */

		desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);
		desc->wHubCharacteristics |= cpu_to_le16(0x0001);

		if (info->enable_oc) {
			desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM);
			desc->wHubCharacteristics |=  cpu_to_le16(0x0008 |0x0001);
		}

		dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", desc->wHubCharacteristics);

		return ret;

	case GetPortStatus:

		if (valid_port(wIndex)) {
			if (info->port[wIndex-1].oc_changed)
				*data |= cpu_to_le32(RH_PS_OCIC);

			if (info->port[wIndex-1].oc_status)
				*data |= cpu_to_le32(RH_PS_POCI);
		}
	}

 out:
	return ret;
}
8-11行先判断处理器本身是否提供了处理电源,过流等状况的函数接口,如果没有定义则直接使用ohci默认的ohci_hub_control函数,否则就用处理器提供的方式 去处理setportfeature,clearportfeature,GetHubDescriptort等请求。
static int ohci_hub_control (
	struct usb_hcd	*hcd,
	u16		typeReq,
	u16		wValue,
	u16		wIndex,
	char		*buf,
	u16		wLength
) {
	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
	int		ports = ohci->num_ports;
	u32		temp;
	int		retval = 0;

	if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
		return -ESHUTDOWN;

	switch (typeReq) {
	case ClearHubFeature:
		switch (wValue) {
		case C_HUB_OVER_CURRENT:
			ohci_writel (ohci, RH_HS_OCIC,
					&ohci->regs->roothub.status);
		case C_HUB_LOCAL_POWER:
			break;
		default:
			goto error;
		}
		break;
	case ClearPortFeature:
		if (!wIndex || wIndex > ports)
			goto error;
		wIndex--;

		switch (wValue) {
		case USB_PORT_FEAT_ENABLE:
			temp = RH_PS_CCS;
			break;
		case USB_PORT_FEAT_C_ENABLE:
			temp = RH_PS_PESC;
			break;
		case USB_PORT_FEAT_SUSPEND:
			temp = RH_PS_POCI;
			break;
		case USB_PORT_FEAT_C_SUSPEND:
			temp = RH_PS_PSSC;
			break;
		case USB_PORT_FEAT_POWER:
			temp = RH_PS_LSDA;
			break;
		case USB_PORT_FEAT_C_CONNECTION:
			temp = RH_PS_CSC;
			break;
		case USB_PORT_FEAT_C_OVER_CURRENT:
			temp = RH_PS_OCIC;
			break;
		case USB_PORT_FEAT_C_RESET:
			temp = RH_PS_PRSC;
			break;
		default:
			goto error;
		}
		ohci_writel (ohci, temp,
				&ohci->regs->roothub.portstatus [wIndex]);
		// ohci_readl (ohci, &ohci->regs->roothub.portstatus [wIndex]);
		break;
	case GetHubDescriptor:
		ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
		break;
	case GetHubStatus:
		temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
		put_unaligned_le32(temp, buf);
		break;
	case GetPortStatus:
		if (!wIndex || wIndex > ports)
			goto error;
		wIndex--;
		temp = roothub_portstatus (ohci, wIndex);
		put_unaligned_le32(temp, buf);

#ifndef	OHCI_VERBOSE_DEBUG
	if (*(u16*)(buf+2))	/* only if wPortChange is interesting */
#endif
		dbg_port (ohci, "GetStatus", wIndex, temp);
		break;
	case SetHubFeature:
		switch (wValue) {
		case C_HUB_OVER_CURRENT:
			// FIXME:  this can be cleared, yes?
		case C_HUB_LOCAL_POWER:
			break;
		default:
			goto error;
		}
		break;
	case SetPortFeature:
		if (!wIndex || wIndex > ports)
			goto error;
		wIndex--;
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
#ifdef	CONFIG_USB_OTG
			if (hcd->self.otg_port == (wIndex + 1)
					&& hcd->self.b_hnp_enable)
				ohci->start_hnp(ohci);
			else
#endif
			ohci_writel (ohci, RH_PS_PSS,
				&ohci->regs->roothub.portstatus [wIndex]);
			break;
		case USB_PORT_FEAT_POWER:
			ohci_writel (ohci, RH_PS_PPS,
				&ohci->regs->roothub.portstatus [wIndex]);
			break;
		case USB_PORT_FEAT_RESET:
			retval = root_port_reset (ohci, wIndex);
			break;
		default:
			goto error;
		}
		break;

	default:
error:
		/* "protocol stall" on error */
		retval = -EPIPE;
	}
	return retval;
}
ohci_hub_control函数对ClearHubFeature,ClearPortFeature,GetHubDescriptor,GetHubStatus,GetPortStatus,SetHubFeature,SetPortFeature请求给出来默认的操作方式。

17-28行处理ClearHubFeature请求,至于是清除什么feature由request中的wvalue决定,其支持的类型在usb 协议table11-17 Hub Class Feature Selectorss都有定义,对于清除C_HUB_OVER_CURRENT,可以通过操作ohci协议中定义的HcRhStatus Register来完成。
29-65行处理clearportfeature请求,请求类型由wValue决定,可能通过操作ohci协议中定义的HcRhPortStatus Register来完成。
66-68行获取hubdescriptor,这个可以读取ohci寄存器里 的HcRhDescriptorA Register,HcRhDescriptorB Register得到。
69-84中hub或port的staus可以通过读取ohci中的HcRhStatus Register和HcRhPortStatus Register完成 。
85-119中设备hub或port的feature也可以通过设置ohci中的HcRhStatus Register和HcRhPortStatus Register完成 。

到这里为止,usb_submit_urb关于root hub部分程序已经讲完,下一篇文章会讲关于非root hub部分。
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值