ohci之usb_submit_urb 一

  85人阅读  评论(0)  收藏  举报

         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)

[plain]  view plain copy
  1. {  
  2.     int             xfertype, max;  
  3.     struct usb_device       *dev;  
  4.     struct usb_host_endpoint    *ep;  
  5.     int             is_out;  
[plain]  view plain copy
  1.     if (!urb || urb->hcpriv || !urb->complete)  
  2.         return -EINVAL;  
  3.     dev = urb->dev;  
  4.     if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))  
  5.         return -ENODEV;  
  6.   
  7.     ep = usb_pipe_endpoint(dev, urb->pipe);  
  8.     if (!ep)  
  9.         return -ENOENT;  
  10.   
  11.     urb->ep = ep;  
  12.     urb->status = -EINPROGRESS;  
  13.     urb->actual_length = 0;  
  14.   
  15.     xfertype = usb_endpoint_type(&ep->desc);  
  16.     if (xfertype == USB_ENDPOINT_XFER_CONTROL) {  
  17.         struct usb_ctrlrequest *setup =  
  18.                 (struct usb_ctrlrequest *) urb->setup_packet;  
  19.   
  20.         if (!setup)  
  21.             return -ENOEXEC;  
  22.         is_out = !(setup->bRequestType & USB_DIR_IN) ||  
  23.                 !setup->wLength;  
  24.     } else {  
  25.         is_out = usb_endpoint_dir_out(&ep->desc);  
  26.     }  
  27.   
  28.     /* Clear the internal flags and cache the direction for later use */  
  29.     urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |  
  30.             URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |  
  31.             URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |  
  32.             URB_DMA_SG_COMBINED);  
  33.     urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);  
  34.   
  35.     if (xfertype != USB_ENDPOINT_XFER_CONTROL &&  
  36.             dev->state < USB_STATE_CONFIGURED)  
  37.         return -ENODEV;  
  38.   
  39.     max = le16_to_cpu(ep->desc.wMaxPacketSize);  
  40.     if (max <= 0) {  
  41.         dev_dbg(&dev->dev,  
  42.             "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",  
  43.             usb_endpoint_num(&ep->desc), is_out ? "out" : "in",  
  44.             __func__, max);  
  45.         return -EMSGSIZE;  
  46.     }  
  47.     if (xfertype == USB_ENDPOINT_XFER_ISOC) {  
  48.         int n, len;  
  49.   
  50.         if (dev->speed == USB_SPEED_SUPER) {  
  51.             int     burst = 1 + ep->ss_ep_comp.bMaxBurst;  
  52.             int     mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);  
  53.             max *= burst;  
  54.             max *= mult;  
  55.         }  
  56.   
  57.         if (dev->speed == USB_SPEED_HIGH) {  
  58.             int mult = 1 + ((max >> 11) & 0x03);  
  59.             max &= 0x07ff;  
  60.             max *= mult;  
  61.         }  
  62.   
  63.         if (urb->number_of_packets <= 0)  
  64.             return -EINVAL;  
  65.         for (n = 0; n < urb->number_of_packets; n++) {  
  66.             len = urb->iso_frame_desc[n].length;  
  67.             if (len < 0 || len > max)  
  68.                 return -EMSGSIZE;  
  69.             urb->iso_frame_desc[n].status = -EXDEV;  
  70.             urb->iso_frame_desc[n].actual_length = 0;  
  71.         }  
  72.     }  
  73.     if (urb->transfer_buffer_length > INT_MAX)  
  74.         return -EMSGSIZE;  
  75.     switch (xfertype) {  
  76.     case USB_ENDPOINT_XFER_ISOC:  
  77.     case USB_ENDPOINT_XFER_INT:  
  78.         switch (dev->speed) {  
  79.         case USB_SPEED_WIRELESS:  
  80.             if (urb->interval < 6)  
  81.                 return -EINVAL;  
  82.             break;  
  83.         default:  
  84.             if (urb->interval <= 0)  
  85.                 return -EINVAL;  
  86.             break;  
  87.         }  
  88.         switch (dev->speed) {  
  89.         case USB_SPEED_SUPER:   /* units are 125us */  
  90.             /* Handle up to 2^(16-1) microframes */  
  91.             if (urb->interval > (1 << 15))  
  92.                 return -EINVAL;  
  93.             max = 1 << 15;  
  94.             break;  
  95.         case USB_SPEED_WIRELESS:  
  96.             if (urb->interval > 16)  
  97.                 return -EINVAL;  
  98.             break;  
  99.         case USB_SPEED_HIGH:    /* units are microframes */  
  100.             if (urb->interval > (1024 * 8))  
  101.                 urb->interval = 1024 * 8;  
  102.             max = 1024 * 8;  
  103.             break;  
  104.         case USB_SPEED_FULL:    /* units are frames/msec */  
  105.         case USB_SPEED_LOW:  
  106.             if (xfertype == USB_ENDPOINT_XFER_INT) {  
  107.                 if (urb->interval > 255)  
  108.                     return -EINVAL;  
  109.                 /* NOTE ohci only handles up to 32 */  
  110.                 max = 128;  
  111.             } else {  
  112.                 if (urb->interval > 1024)  
  113.                     urb->interval = 1024;  
  114.                 /* NOTE usb and ohci handle up to 2^15 */  
  115.                 max = 1024;  
  116.             }  
  117.             break;  
  118.         default:  
  119.             return -EINVAL;  
  120.         }  
  121.         if (dev->speed != USB_SPEED_WIRELESS) {  
  122.             /* Round down to a power of 2, no more than max */  
  123.             urb->interval = min(max, 1 << ilog2(urb->interval));  
  124.         }  
  125.     }  
  126.   
  127.     return usb_hcd_submit_urb(urb, mem_flags);  
  128. }  
[plain]  view plain copy
  1. 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。

[html]  view plain copy
  1. int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)  
  2. {  
  3.     int         status;  
  4.     struct usb_hcd      *hcd = bus_to_hcd(urb->dev->bus);  
  5.   
  6.     usb_get_urb(urb);  
  7.     atomic_inc(&urb->use_count);  
  8.     atomic_inc(&urb->dev->urbnum);  
  9.     usbmon_urb_submit(&hcd->self, urb);  
  10.   
  11.     if (is_root_hub(urb->dev)) {  
  12.         status = rh_urb_enqueue(hcd, urb);  
  13.     } else {  
  14.         status = map_urb_for_dma(hcd, urb, mem_flags);  
  15.         if (likely(status == 0)) {  
  16.             status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);  
  17.             if (unlikely(status))  
  18.                 unmap_urb_for_dma(hcd, urb);  
  19.         }  
  20.     }  
  21.   
  22.     if (unlikely(status)) {  
  23.         usbmon_urb_submit_error(&hcd->self, urb, status);  
  24.         urb->hcpriv = NULL;  
  25.         INIT_LIST_HEAD(&urb->urb_list);  
  26.         atomic_dec(&urb->use_count);  
  27.         atomic_dec(&urb->dev->urbnum);  
  28.         if (atomic_read(&urb->reject))  
  29.             wake_up(&usb_kill_urb_queue);  
  30.         usb_put_urb(urb);  
  31.     }  
  32.     return status;  
  33. }  
6-9行 对urb结构中一些计数器进行初始化。

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

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

[html]  view plain copy
  1. static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)  
  2. {  
  3.     if (usb_endpoint_xfer_int(&urb->ep->desc))  
  4.         return rh_queue_status (hcd, urb);  
  5.     if (usb_endpoint_xfer_control(&urb->ep->desc))  
  6.         return rh_call_control (hcd, urb);  
  7.     return -EINVAL;  
  8. }  

a. 中断类型hub root endpoint

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

[html]  view plain copy
  1. static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)  
  2. {  
  3.     int     retval;  
  4.     unsigned long   flags;  
  5.     unsigned    len = 1 + (urb->dev->maxchild / 8);  
  6.   
  7.     spin_lock_irqsave (&hcd_root_hub_lock, flags);  
  8.     if (hcd->status_urb || urb->transfer_buffer_length < len) {  
  9.         dev_dbg (hcd->self.controller, "not queuing rh status urb\n");  
  10.         retval = -EINVAL;  
  11.         goto done;  
  12.     }  
  13.   
  14.     retval = usb_hcd_link_urb_to_ep(hcd, urb);  
  15.     if (retval)  
  16.         goto done;  
  17.   
  18.     hcd->status_urb = urb;  
  19.     urb->hcpriv = hcd;   /* indicate it's queued */  
  20.     if (!hcd->uses_new_polling)  
  21.         mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));  
  22.   
  23.     /* If a status change has already occurred, report it ASAP */  
  24.     else if (HCD_POLL_PENDING(hcd))  
  25.         mod_timer(&hcd->rh_timer, jiffies);  
  26.     retval = 0;  
  27.  done:  
  28.     spin_unlock_irqrestore (&hcd_root_hub_lock, flags);  
  29.     return retval;  
  30. }  
中断类型的端口用于监控 root hub上是否有设备插入,root hub通过两种方式来监控是否有设备插入,一种是通过中断方式,使能相应中断,当状态发生变化时产生中断,然后运行中断服务程序获取取状态寄存器信息;一种是定时轮询方式,当定时时间到了运行定时器程时指定的定时器rh_timer的function函数rh_timer_func。

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

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

不管是通过中断方式还是通过定时轮询方式 ,最终都是通过调用usb_hcd_poll_rh_status来实现的root hub状态查询。

[html]  view plain copy
  1. void usb_hcd_poll_rh_status(struct usb_hcd *hcd)  
  2. {  
  3.     struct urb  *urb;  
  4.     int     length;  
  5.     unsigned long   flags;  
  6.     char        buffer[6];  /* Any root hubs with > 31 ports? */  
  7.   
  8.     if (unlikely(!hcd->rh_pollable))  
  9.         return;  
  10.     if (!hcd->uses_new_polling && !hcd->status_urb)  
  11.         return;  
  12.   
  13.     length = hcd->driver->hub_status_data(hcd, buffer);  
  14.     if (length > 0) {  
  15.         spin_lock_irqsave(&hcd_root_hub_lock, flags);  
  16.         urb = hcd->status_urb;  
  17.         if (urb) {  
  18.             clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);  
  19.             hcd->status_urb = NULL;  
  20.             urb->actual_length = length;  
  21.             memcpy(urb->transfer_buffer, buffer, length);  
  22.   
  23.             usb_hcd_unlink_urb_from_ep(hcd, urb);  
  24.             spin_unlock(&hcd_root_hub_lock);  
  25.             usb_hcd_giveback_urb(hcd, urb, 0);  
  26.             spin_lock(&hcd_root_hub_lock);  
  27.         } else {  
  28.             length = 0;  
  29.             set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);  
  30.         }  
  31.         spin_unlock_irqrestore(&hcd_root_hub_lock, flags);  
  32.     }  
  33.   
  34.     if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) : (length == 0 && hcd->status_urb != NULL))  
  35.         mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));  
  36. }  
       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。

[html]  view plain copy
  1. static int ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf)  
  2. {  
  3.     struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);  
  4.     struct s3c2410_hcd_port *port;  
  5.     int orig;  
  6.     int portno;  
  7.   
  8.     orig  = ohci_hub_status_data(hcd, buf);  
  9.   
  10.     if (info == NULL)  
  11.         return orig;  
  12.   
  13.     port = &info->port[0];  
  14.   
  15.     for (portno = 0; portno < 2; port++, portno++) {  
  16.         if (port->oc_changed == 1 &&  
  17.             port->flags & S3C_HCDFLG_USED) {  
  18.             dev_dbg(hcd->self.controller,  
  19.                 "oc change on port %d\n", portno);  
  20.   
  21.             if (orig < 1)  
  22.                 orig = 1;  
  23.   
  24.             buf[0] |= 1<<(portno+1);  
  25.         }  
  26.     }  
  27.   
  28.     return orig;  
  29. }  
s3c6410usb控制器属于ohci接口标准,所以通过ohc_hub_status_data来读取hub信息。

[html]  view plain copy
  1. static int ohci_hub_status_data (struct usb_hcd *hcd, char *buf)  
  2. {  
  3.     struct ohci_hcd *ohci = hcd_to_ohci (hcd);  
  4.     int     i, changed = 0length = 1;  
  5.     int     any_connected = 0;  
  6.     int     rhsc_status;  
  7.     unsigned long   flags;  
  8.   
  9.     spin_lock_irqsave (&ohci->lock, flags);  
  10.     if (!HCD_HW_ACCESSIBLE(hcd))  
  11.         goto done;  
  12.   
  13.     if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))  
  14.         buf [0] = changed = 1;  
  15.     else  
  16.         buf [0] = 0;  
  17.     if (ohci->num_ports > 7) {  
  18.         buf [1] = 0;  
  19.         length++;  
  20.     }  
  21.   
  22.     ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus);  
  23.     rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC;  
  24.   
  25.     for (i = 0; i < ohci->num_ports; i++) {  
  26.         u32 status = roothub_portstatus (ohci, i);  
  27.   
  28.         any_connected |= (status & RH_PS_CCS);  
  29.   
  30.         if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC)) {  
  31.             changed = 1;  
  32.             if (i < 7)  
  33.                 buf [0] |= 1 << (i + 1);  
  34.             else  
  35.                 buf [1] |= 1 << (i - 7);  
  36.         }  
  37.     }  
  38.   
  39.     if (ohci_root_hub_state_changes(ohci, changed,any_connected, rhsc_status))  
  40.         set_bit(HCD_FLAG_POLL_RH, &hcd->flags);  
  41.     else  
  42.         clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);  
  43.   
  44. done:  
  45.     spin_unlock_irqrestore (&ohci->lock, flags);  
  46.   
  47.     return changed ? length : 0;  
  48. }  
13行中roothub_status()用来读取roothub寄存器进而的status标志;

[html]  view plain copy
  1. static inline u32 roothub_status (struct ohci_hcd *hc)  
  2. {  
[html]  view plain copy
  1. <span style="white-space:pre">  </span> return ohci_readl (hc, &hc->regs->roothub.status);  
[html]  view plain copy
  1. }  
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函数。
[html]  view plain copy
  1. static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)  
  2. {  
  3.     struct usb_ctrlrequest *cmd;  
  4.     u16     typeReq, wValue, wIndex, wLength;  
  5.     u8      *ubuf = urb->transfer_buffer;  
  6.     u8      tbuf [sizeof (struct usb_hub_descriptor)]  
  7.         __attribute__((aligned(4)));  
  8.     const u8    *bufp = tbuf;  
  9.     unsigned    len = 0;  
  10.     int     status;  
  11.     u8      patch_wakeup = 0;  
  12.     u8      patch_protocol = 0;  
  13.   
  14.     might_sleep();  
  15.   
  16.     spin_lock_irq(&hcd_root_hub_lock);  
  17.     status = usb_hcd_link_urb_to_ep(hcd, urb);  
  18.     spin_unlock_irq(&hcd_root_hub_lock);  
  19.     if (status)  
  20.         return status;  
  21.     urb->hcpriv = hcd;   /* Indicate it's queued */  
  22.   
  23.     cmd = (struct usb_ctrlrequest *) urb->setup_packet;  
  24.     typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;  
  25.     wValue   = le16_to_cpu (cmd->wValue);  
  26.     wIndex   = le16_to_cpu (cmd->wIndex);  
  27.     wLength  = le16_to_cpu (cmd->wLength);  
  28.   
  29.     if (wLength > urb->transfer_buffer_length)  
  30.         goto error;  
  31.   
  32.     urb->actual_length = 0;  
  33.     switch (typeReq) {  
  34.   
  35.     /* DEVICE REQUESTS */  
  36.   
  37.     case DeviceRequest | USB_REQ_GET_STATUS:  
  38.         tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)  
  39.                     << USB_DEVICE_REMOTE_WAKEUP)  
  40.                 | (1 << USB_DEVICE_SELF_POWERED);  
  41.         tbuf [1] = 0;  
  42.         len = 2;  
  43.         break;  
  44.     case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:  
  45.         if (wValue == USB_DEVICE_REMOTE_WAKEUP)  
  46.             device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);  
  47.         else  
  48.             goto error;  
  49.         break;  
  50.     case DeviceOutRequest | USB_REQ_SET_FEATURE:  
  51.         if (device_can_wakeup(&hcd->self.root_hub->dev)  
  52.                 && wValue == USB_DEVICE_REMOTE_WAKEUP)  
  53.             device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);  
  54.         else  
  55.             goto error;  
  56.         break;  
  57.     case DeviceRequest | USB_REQ_GET_CONFIGURATION:  
  58.         tbuf [0] = 1;  
  59.         len = 1;  
  60.             /* FALLTHROUGH */  
  61.     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:  
  62.         break;  
  63.     case DeviceRequest | USB_REQ_GET_DESCRIPTOR:  
  64.         switch (wValue & 0xff00) {  
  65.         case USB_DT_DEVICE << 8:  
  66.             switch (hcd->speed) {  
  67.             case HCD_USB3:  
  68.                 bufp = usb3_rh_dev_descriptor;  
  69.                 break;  
  70.             case HCD_USB2:  
  71.                 bufp = usb2_rh_dev_descriptor;  
  72.                 break;  
  73.             case HCD_USB11:  
  74.                 bufp = usb11_rh_dev_descriptor;  
  75.                 break;  
  76.             default:  
  77.                 goto error;  
  78.             }  
  79.             len = 18;  
  80.             if (hcd->has_tt)  
  81.                 patch_protocol = 1;  
  82.             break;  
  83.         case USB_DT_CONFIG << 8:  
  84.             switch (hcd->speed) {  
  85.             case HCD_USB3:  
  86.                 bufp = ss_rh_config_descriptor;  
  87.                 len = sizeof ss_rh_config_descriptor;  
  88.                 break;  
  89.             case HCD_USB2:  
  90.                 bufp = hs_rh_config_descriptor;  
  91.                 len = sizeof hs_rh_config_descriptor;  
  92.                 break;  
  93.             case HCD_USB11:  
  94.                 bufp = fs_rh_config_descriptor;  
  95.                 len = sizeof fs_rh_config_descriptor;  
  96.                 break;  
  97.             default:  
  98.                 goto error;  
  99.             }  
  100.             if (device_can_wakeup(&hcd->self.root_hub->dev))  
  101.                 patch_wakeup = 1;  
  102.             break;  
  103.         case USB_DT_STRING << 8:  
  104.             if ((wValue & 0xff) < 4)  
  105.                 urb->actual_length = rh_string(wValue & 0xff,hcd, ubuf, wLength);  
  106.             else /* unsupported IDs --> "protocol stall" */  
  107.                 goto error;  
  108.             break;  
  109.         default:  
  110.             goto error;  
  111.         }  
  112.         break;  
  113.     case DeviceRequest | USB_REQ_GET_INTERFACE:  
  114.         tbuf [0] = 0;  
  115.         len = 1;  
  116.             /* FALLTHROUGH */  
  117.     case DeviceOutRequest | USB_REQ_SET_INTERFACE:  
  118.         break;  
  119.     case DeviceOutRequest | USB_REQ_SET_ADDRESS:  
  120.         // wValue == urb->dev->devaddr  
  121.         dev_dbg (hcd->self.controller, "root hub device address %d\n",  
  122.             wValue);  
  123.         break;  
  124.   
  125.     /* ENDPOINT REQUESTS */  
  126.   
  127.     case EndpointRequest | USB_REQ_GET_STATUS:  
  128.         // ENDPOINT_HALT flag  
  129.         tbuf [0] = 0;  
  130.         tbuf [1] = 0;  
  131.         len = 2;  
  132.             /* FALLTHROUGH */  
  133.     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:  
  134.     case EndpointOutRequest | USB_REQ_SET_FEATURE:  
  135.         dev_dbg (hcd->self.controller, "no endpoint features yet\n");  
  136.         break;  
  137.   
  138.     default:  
  139.         /* non-generic request */  
  140.         switch (typeReq) {  
  141.         case GetHubStatus:  
  142.         case GetPortStatus:  
  143.             len = 4;  
  144.             break;  
  145.         case GetHubDescriptor:  
  146.             len = sizeof (struct usb_hub_descriptor);  
  147.             break;  
  148.         }  
  149.         status = hcd->driver->hub_control (hcd,typeReq, wValue, wIndex, tbuf, wLength);  
  150.         break;  
  151. error:  
  152.         /* "protocol stall" on error */  
  153.         status = -EPIPE;  
  154.     }  
  155.   
  156.     if (status)   
  157.         len = 0;  
  158.     if (len) {  
  159.         if (urb->transfer_buffer_length < len)  
  160.             len = urb->transfer_buffer_length;  
  161.         urb->actual_length = len;  
  162.         memcpy (ubuf, bufp, len);  
  163.   
  164.         /* report whether RH hardware supports remote wakeup */  
  165.         if (patch_wakeup && len > offsetof (struct usb_config_descriptor, bmAttributes))  
  166.             ((struct usb_config_descriptor *)ubuf)->bmAttributes |= USB_CONFIG_ATT_WAKEUP;  
  167.   
  168.         /* report whether RH hardware has an integrated TT */  
  169.         if (patch_protocol && len > offsetof(struct usb_device_descriptor,bDeviceProtocol))  
  170.             ((struct usb_device_descriptor *) ubuf)->bDeviceProtocol = 1;  
  171.     }  
  172.   
  173.     spin_lock_irq(&hcd_root_hub_lock);  
  174.     usb_hcd_unlink_urb_from_ep(hcd, urb);  
  175.   
  176.     spin_unlock(&hcd_root_hub_lock);  
  177.     usb_hcd_giveback_urb(hcd, urb, status);  
  178.     spin_lock(&hcd_root_hub_lock);  
  179.   
  180.     spin_unlock_irq(&hcd_root_hub_lock);  
  181.     return 0;  
  182. }  
该函数比较长,函数主要作用就是根据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。
[html]  view plain copy
  1. static int ohci_s3c2410_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16  wIndex, char *buf, u16  wLength)  
  2. {  
  3.     struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);  
  4.     struct usb_hub_descriptor *desc;  
  5.     int ret = -EINVAL;  
  6.     u32 *data = (u32 *)buf;  
  7.   
  8.     if (info == NULL) {  
  9.         ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);  
  10.         goto out;  
  11.     }  
  12.   
  13.   
  14.     switch (typeReq) {  
  15.     case SetPortFeature:  
  16.         if (wValue == USB_PORT_FEAT_POWER) {  
  17.             dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n");  
  18.             s3c2410_usb_set_power(info, wIndex, 1);  
  19.             goto out;  
  20.         }  
  21.         break;  
  22.   
  23.     case ClearPortFeature:  
  24.         switch (wValue) {  
  25.         case USB_PORT_FEAT_C_OVER_CURRENT:  
  26.             dev_dbg(hcd->self.controller,"ClearPortFeature: C_OVER_CURRENT\n");  
  27.   
  28.             if (valid_port(wIndex)) {  
  29.                 info->port[wIndex-1].oc_changed = 0;  
  30.                 info->port[wIndex-1].oc_status = 0;  
  31.             }  
  32.   
  33.             goto out;  
  34.   
  35.         case USB_PORT_FEAT_OVER_CURRENT:  
  36.             dev_dbg(hcd->self.controller, "ClearPortFeature: OVER_CURRENT\n");  
  37.   
  38.             if (valid_port(wIndex))  
  39.                 info->port[wIndex-1].oc_status = 0;  
  40.   
  41.             goto out;  
  42.   
  43.         case USB_PORT_FEAT_POWER:  
  44.             dev_dbg(hcd->self.controller, "ClearPortFeature: POWER\n");  
  45.   
  46.             if (valid_port(wIndex)) {  
  47.                 s3c2410_usb_set_power(info, wIndex, 0);  
  48.                 return 0;  
  49.             }  
  50.         }  
  51.         break;  
  52.     }  
  53.   
  54.     ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);  
  55.     if (ret)  
  56.         goto out;  
  57.   
  58.     switch (typeReq) {  
  59.     case GetHubDescriptor:  
  60.   
  61.   
  62.         desc = (struct usb_hub_descriptor *)buf;  
  63.   
  64.         if (info->power_control == NULL)  
  65.             return ret;  
  66.   
  67.         dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n",desc->wHubCharacteristics);  
  68.   
  69.         /* remove the old configurations for power-switching, and  
  70.          * over-current protection, and insert our new configuration  
  71.          */  
  72.   
  73.         desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);  
  74.         desc->wHubCharacteristics |= cpu_to_le16(0x0001);  
  75.   
  76.         if (info->enable_oc) {  
  77.             desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM);  
  78.             desc->wHubCharacteristics |=  cpu_to_le16(0x0008 |0x0001);  
  79.         }  
  80.   
  81.         dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", desc->wHubCharacteristics);  
  82.   
  83.         return ret;  
  84.   
  85.     case GetPortStatus:  
  86.   
  87.         if (valid_port(wIndex)) {  
  88.             if (info->port[wIndex-1].oc_changed)  
  89.                 *data |= cpu_to_le32(RH_PS_OCIC);  
  90.   
  91.             if (info->port[wIndex-1].oc_status)  
  92.                 *data |= cpu_to_le32(RH_PS_POCI);  
  93.         }  
  94.     }  
  95.   
  96.  out:  
  97.     return ret;  
  98. }  
8-11行先判断处理器本身是否提供了处理电源,过流等状况的函数接口,如果没有定义则直接使用ohci默认的ohci_hub_control函数,否则就用处理器提供的方式 去处理setportfeature,clearportfeature,GetHubDescriptort等请求。
[html]  view plain copy
  1. static int ohci_hub_control (  
  2.     struct usb_hcd  *hcd,  
  3.     u16     typeReq,  
  4.     u16     wValue,  
  5.     u16     wIndex,  
  6.     char        *buf,  
  7.     u16     wLength  
  8. ) {  
  9.     struct ohci_hcd *ohci = hcd_to_ohci (hcd);  
  10.     int     ports = ohci->num_ports;  
  11.     u32     temp;  
  12.     int     retval = 0;  
  13.   
  14.     if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))  
  15.         return -ESHUTDOWN;  
  16.   
  17.     switch (typeReq) {  
  18.     case ClearHubFeature:  
  19.         switch (wValue) {  
  20.         case C_HUB_OVER_CURRENT:  
  21.             ohci_writel (ohci, RH_HS_OCIC,  
  22.                     &ohci->regs->roothub.status);  
  23.         case C_HUB_LOCAL_POWER:  
  24.             break;  
  25.         default:  
  26.             goto error;  
  27.         }  
  28.         break;  
  29.     case ClearPortFeature:  
  30.         if (!wIndex || wIndex > ports)  
  31.             goto error;  
  32.         wIndex--;  
  33.   
  34.         switch (wValue) {  
  35.         case USB_PORT_FEAT_ENABLE:  
  36.             temp = RH_PS_CCS;  
  37.             break;  
  38.         case USB_PORT_FEAT_C_ENABLE:  
  39.             temp = RH_PS_PESC;  
  40.             break;  
  41.         case USB_PORT_FEAT_SUSPEND:  
  42.             temp = RH_PS_POCI;  
  43.             break;  
  44.         case USB_PORT_FEAT_C_SUSPEND:  
  45.             temp = RH_PS_PSSC;  
  46.             break;  
  47.         case USB_PORT_FEAT_POWER:  
  48.             temp = RH_PS_LSDA;  
  49.             break;  
  50.         case USB_PORT_FEAT_C_CONNECTION:  
  51.             temp = RH_PS_CSC;  
  52.             break;  
  53.         case USB_PORT_FEAT_C_OVER_CURRENT:  
  54.             temp = RH_PS_OCIC;  
  55.             break;  
  56.         case USB_PORT_FEAT_C_RESET:  
  57.             temp = RH_PS_PRSC;  
  58.             break;  
  59.         default:  
  60.             goto error;  
  61.         }  
  62.         ohci_writel (ohci, temp,  
  63.                 &ohci->regs->roothub.portstatus [wIndex]);  
  64.         // ohci_readl (ohci, &ohci->regs->roothub.portstatus [wIndex]);  
  65.         break;  
  66.     case GetHubDescriptor:  
  67.         ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);  
  68.         break;  
  69.     case GetHubStatus:  
  70.         temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);  
  71.         put_unaligned_le32(temp, buf);  
  72.         break;  
  73.     case GetPortStatus:  
  74.         if (!wIndex || wIndex > ports)  
  75.             goto error;  
  76.         wIndex--;  
  77.         temp = roothub_portstatus (ohci, wIndex);  
  78.         put_unaligned_le32(temp, buf);  
  79.   
  80. #ifndef OHCI_VERBOSE_DEBUG  
  81.     if (*(u16*)(buf+2)) /* only if wPortChange is interesting */  
  82. #endif  
  83.         dbg_port (ohci, "GetStatus", wIndex, temp);  
  84.         break;  
  85.     case SetHubFeature:  
  86.         switch (wValue) {  
  87.         case C_HUB_OVER_CURRENT:  
  88.             // FIXME:  this can be cleared, yes?  
  89.         case C_HUB_LOCAL_POWER:  
  90.             break;  
  91.         default:  
  92.             goto error;  
  93.         }  
  94.         break;  
  95.     case SetPortFeature:  
  96.         if (!wIndex || wIndex > ports)  
  97.             goto error;  
  98.         wIndex--;  
  99.         switch (wValue) {  
  100.         case USB_PORT_FEAT_SUSPEND:  
  101. #ifdef  CONFIG_USB_OTG  
  102.             if (hcd->self.otg_port == (wIndex + 1)  
  103.                     && hcd->self.b_hnp_enable)  
  104.                 ohci->start_hnp(ohci);  
  105.             else  
  106. #endif  
  107.             ohci_writel (ohci, RH_PS_PSS,  
  108.                 &ohci->regs->roothub.portstatus [wIndex]);  
  109.             break;  
  110.         case USB_PORT_FEAT_POWER:  
  111.             ohci_writel (ohci, RH_PS_PPS,  
  112.                 &ohci->regs->roothub.portstatus [wIndex]);  
  113.             break;  
  114.         case USB_PORT_FEAT_RESET:  
  115.             retval = root_port_reset (ohci, wIndex);  
  116.             break;  
  117.         default:  
  118.             goto error;  
  119.         }  
  120.         break;  
  121.   
  122.     default:  
  123. error:  
  124.         /* "protocol stall" on error */  
  125.         retval = -EPIPE;  
  126.     }  
  127.     return retval;  
  128. }  
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部分。
已标记关键词 清除标记