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结束就必须运行的,所以必须定义;
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;
- }
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;
- }
14行把当前urb添加到endpoint对应urb列表中。
20-25行,设置定时器rh_timer的定时时间,当uses_new_polling为0时,设置定时超时时间为250ms,否则设置当前时间为超时时间,直接运行定时器function函数rh_timer_func。
不管是通过中断方式还是通过定时轮询方式 ,最终都是通过调用usb_hcd_poll_rh_status来实现的root hub状态查询。
- 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));
- }
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;
- }
- 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;
- }
- static inline u32 roothub_status (struct ohci_hcd *hc)
- {
- <span style="white-space:pre"> </span> return ohci_readl (hc, &hc->regs->roothub.status);
- }
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
- 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;
- }
- 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;
- }
- 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;
- }
17-28行处理ClearHubFeature请求,至于是清除什么feature由request中的wvalue决定,其支持的类型在usb 协议table11-17 Hub Class Feature Selectorss都有定义,对于清除C_HUB_OVER_CURRENT,可以通过操作ohci协议中定义的HcRhStatus Register来完成。
85-119中设备hub或port的feature也可以通过设置ohci中的HcRhStatus Register和HcRhPortStatus Register完成 。