Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3010617
  • 博文数量: 674
  • 博客积分: 17881
  • 博客等级: 上将
  • 技术积分: 4849
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 10:15
文章分类

全部博文(674)

文章存档

2013年(34)

2012年(146)

2011年(197)

2010年(297)

分类: LINUX

2010-06-22 22:32:06

2. linux ehci device driver(ehci hcd)
2.1. linux usb subsystem arch overview(host)
2.2. ehci_hcd
2.3. ehci 实现的接口
2.3.1. ehci_pci_setup() (hc_driver->reset)
2.3.2. ehci_run() (hc_driver->start)
2.3.3. ehci_stop() (hc_driver->stop)
2.3.4. ehci_get_frame()(hc_driver-> get_frame_number)
2.3.5. ehci_urb_enqueue()(hc_driver->urb_enqueue)*
2.3.6. ehci_urb_dequeue()(hc_driver->urb_dequeue)
2.3.7. ehci_endpoint_disable()(hc_driver-> endpoint_disable)
2.3.8. ehci_irq()(hc_driver->irq)*
2.3.9. ehci_hub_control()(hc_driver->hub_control)
2.3.10. ehci_hub_status_data(hc_driver->hub_status_data)
 
1.   linux ehci device driver(ehci hcd)
1.1.linux usb subsystem arch overview(host)
           
Fig 2-1 linux usb subsystem block diagram
Fig 2-1中, 绿色线代表调用关系,绿线起点(非箭头)连接服务提供者,绿线终点(箭头)指向调用方; 红色线代表实现关系, 红线终点(箭头)指向接口定义者,红线起点(非箭头)连接接口实现方.
 
Usb Interface driver 指操作usb function的驱动,负责实现function层的协议(mass-storage, usb video class,HID…),比如usb-storage/uvc/hid等模块,一般每个(类)Interface使用一种驱动. 用户可自己开发一些标准class的驱动或者一些非标准的function驱动,系统中支持function的数目由usb 2.0 spec确定,一般来说不同(类)的function均有一个对应的Usb Interface driver, usb subsystem可以同时支持多个usb Interface driver(usb function)的运行. 一般来说,该层比较薄, class的协议都不是很复杂.
 
Linux usbcore模块是对整个linux usb subsytem的抽象, 在整个subsystem中起着承上启下的作用, 实现usb 2.0部分协议. 提供了大量的API供外部模块调用, 这些API 对应linux usbcore module中通过EXPORT_SYMBOL()导出的函数和变量; 同时,linux usbcore 也定义了接口类型, 这些接口需要usb interface driver以及usb host controller driver来实现. usbcore模块一般不需要用户改动. 该层比较厚,内容较为丰富.
 
Linux usb host controller driver指操作usb host controller(hardware)的驱动,主要负责将上层发来的URB传输请求转化成HC可识别的格式并启动HC传输,直至完成传输. 另外HC一般都集成了Root Hub的功能,hcd也要实现root hub port访问的功能 ,整个subsystem只有该驱动直接操作硬件寄存器. 该层也支持多种不同的host controller driver, 比如EHCI ,UHCI,OHCI以及一些非标准的HC实现(多用于嵌入式环境). 从软件角度该层在整个subsystem中较薄,但由于软硬件接口的复杂导致hcd driver都比较复杂. Linux Ehci device driver就属于该层.
 
整个subsystem中, 用户只需要开发或者定制usb interface driver和usb host controller driver,
Usbcore一般不需要修改.
 
Usb Interface driver需要实现usbcore中struct usb_driver定义的接口, usb host controller driver需要实现usbcore中struct hc_driver定义的接口,即Fig2-1中指向linux usbcore的两个红线.
 
1.2.ehci_hcd
struct ehci_hcd 定义了ehci host controller driver的数据结构部分,struct hc_driver则定义了基于struct usb_hcd的接口,该接口中的所有函数类型第一个参数都是struct usb_hcd*; 从面向对象角度看,两者联合起来,给出了usb_hcd “class”以及ehci_hcd “class”的定义,  struct ehci_hcd可以看作是对struct usb_hcd的继承, usb_create_hcd()会创建一个包含struct hcd以及一个struct ehci_hcd 的“对象”, 可以通过hcd_to_ehci()从struct usb_hcd *获得对应的struct ehci_hcd *, 还可以通过ehci_to_hcd()从struct ehci_hcd *获得struct usb_hcd*.
 
struct ehci_hcd {                 /* one per controller */
       /* glue to PCI and HCD framework */
       struct ehci_caps __iomem *caps;
       struct ehci_regs __iomem *regs;
       struct ehci_dbg_port __iomem *debug;
 
       __u32                   hcs_params;   /* cached register copy */
       spinlock_t              lock; /*用于对ehci_hcd数据结构以及对ehci 操作的保护*/
 
       /* async schedule support */
       struct ehci_qh        *async;
       struct ehci_qh        *reclaim;
       unsigned         reclaim_ready : 1;
       unsigned         scanning : 1;
 
       /* periodic schedule support */
       unsigned         periodic_size;     /*一般为1024, 有些ehci hc可以支持256/512*/
       __le32                   *periodic;       /* hw periodic table */
       dma_addr_t           periodic_dma;
       unsigned         i_thresh; /* uframes HC might cache */
 
       union ehci_shadow *pshadow;      /* mirror hw periodic table */
       int                  next_uframe; /* scan periodic, start here */
       unsigned         periodic_sched;      /* periodic activity count */
 
       /* per root hub port */
       unsigned long         reset_done [EHCI_MAX_ROOT_PORTS];
 
       /* per-HC memory pools (could be per-bus, but ...) */
       struct dma_pool            *qh_pool;       /* qh per active urb */
       struct dma_pool            *qtd_pool;      /* one or more per qh */
       struct dma_pool            *itd_pool;       /* itd per iso urb */
       struct dma_pool            *sitd_pool;      /* sitd per split iso urb */
 
       struct timer_list      watchdog;
       struct notifier_block      reboot_notifier;
       unsigned long         actions;
       unsigned         stamp;
       unsigned long         next_statechange;
       u32                command;
 
       u8                  sbrn;              /* packed release number */
};
 
 
Struct hc_driver可以看作linux usbcore模块定义的需要底层host controller driver实现的接口,通过ehci driver实现这些接口,usbcore可以将更上层软件的请求传递给host controller driver以及HC,HC以及host controller driver完成后, 也会通过这些接口通知usbcore module.
 
struct hc_driver {
       const char      *description;   /* "ehci-hcd" etc */
       const char      *product_desc;      /* product/vendor string */
       size_t             hcd_priv_size; /* size of private data */
 
       /* irq handler */
       irqreturn_t      (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
 
       int    flags;
 
       /* called to init HCD and root hub */
       int    (*reset) (struct usb_hcd *hcd);
       int    (*start) (struct usb_hcd *hcd);
 
       /* NOTE: these suspend/resume calls relate to the HC as
        * a whole, not just the root hub; they're for PCI bus glue.
        */
       /* called after suspending the hub, before entering D3 etc */
       int    (*suspend) (struct usb_hcd *hcd, pm_message_t message);
 
       /* called after entering D0 (etc), before resuming the hub */
       int    (*resume) (struct usb_hcd *hcd);
 
       /* cleanly make HCD stop writing memory and doing I/O */
       void (*stop) (struct usb_hcd *hcd);
 
       /* return current frame number */
       int    (*get_frame_number) (struct usb_hcd *hcd);
 
       /* manage i/o requests, device state */
       int    (*urb_enqueue) (struct usb_hcd *hcd,
                                   struct usb_host_endpoint *ep,
                                   struct urb *urb,
                                   gfp_t mem_flags);
       int    (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
 
       /* hw synch, freeing endpoint resources that urb_dequeue can't */
       void       (*endpoint_disable)(struct usb_hcd *hcd,
                     struct usb_host_endpoint *ep);
 
       /* root hub support */
       int           (*hub_status_data) (struct usb_hcd *hcd, char *buf);
       int           (*hub_control) (struct usb_hcd *hcd,
                            u16 typeReq, u16 wValue, u16 wIndex,
                            char *buf, u16 wLength);
       int           (*bus_suspend)(struct usb_hcd *);
       int           (*bus_resume)(struct usb_hcd *);
       int           (*start_port_reset)(struct usb_hcd *, unsigned port_num);
       void        (*hub_irq_enable)(struct usb_hcd *);
              /* Needed only if port-change IRQs are level-triggered */
};
 
Ehci device driver的主要工作就是实现struct hc_driver中定义的主要接口, 一般来说以下接口是必须要实现的:
        irqreturn_t      (*irq) (struct usb_hcd *hcd, struct pt_regs *regs) //ehci hcd的irq handler
        int    (*reset) (struct usb_hcd *hcd);
           int    (*start) (struct usb_hcd *hcd); //启动HC
        void (*stop) (struct usb_hcd *hcd); //停止HC
        int    (*get_frame_number) (struct usb_hcd *hcd);//获得当前的frame号
       
 
 
    /*根据hcd和ep的信息,安排urb的schedule到EHCI,该URB的传输完成后,会调用urb->complete ()通知usbcore*/
int     (*urb_enqueue) (struct usb_hcd *hcd,
                                   struct usb_host_endpoint *ep,
                                   struct urb *urb,
                                   gfp_t mem_flags);
 
    /*该接口用于用户取消已经enqueue 的urb,主要为usbcore的 unlink_urb()所调用*/
       int    (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
      
    /*disable the ep , 并且释放该ep上资源(unlink 该ep上的qh)*/
void       (*endpoint_disable)(struct usb_hcd *hcd,
                   struct usb_host_endpoint *ep);
   
/*获取root hub port的状态信息*/
int          (*hub_status_data) (struct usb_hcd *hcd, char *buf);
 
/*操作root hub以及port*/
       int           (*hub_control) (struct usb_hcd *hcd,
                            u16 typeReq, u16 wValue, u16 wIndex,
                            char *buf, u16 wLength);
1.3.ehci 实现的接口
1.3.1.    ehci_pci_setup() (hc_driver->reset)
原型
     static int ehci_pci_setup(struct usb_hcd *hcd)
 
调用时机
     usbcore 的API ---usb_add_hcd()会通过hcd->driver->reset(hcd)来调用.
     一般,在 ehci pci device的probe()函数会调用 usb_add_hcd().
 
调用说明
     无
    
主要流程
     1),初始化ehci寄存器基地址(ehci->regs和ehci->caps);
     2),将ehci的Capability Parameters读入到ehci->hcs_params缓冲起来;
     3),调用ehci_halt() 强制ehci进入halt状态;
     4),调用 ehci_init() 初始化ehci 的数据结构:
           a),spin_lock_init(&ehci->lock);
           b),初始化watchdog timer;(主要用于发现和处理irq lost的情况)
           c),令ehci->periodic_size = DEFAULT_I_TDPS,调用ehci_mem_init()分配并初始化HC schedule所需的数据结构,主要有
               .预先分配一定数量的qtd,qh,itd以及sitd到ehci->qtd_pool, ehci->qh_pool, ehci->itd_pool和ehci->sitd_pool中作为cache;
               .从ehci->qh_pool分配一个qh, 并使得ehci->async指向该qh,这个qh用作asynchronous schedule的reclamation list head (H bit为1),实现Empty Asynchronous Schedule Detection;
               .调用dma_alloc_coherent()分配Hardware periodic table,并令ehci->periodic指向其,ehci->periodic_size设为1024,ehci->periodic_dma返回表格对应的物理地址;初始化表格中每项初值为EHCI_LIST_END,即不包含periodic schedule data structure;
               .分配software shadow of hardware table, 令ehci->ehci->pshadow指向其,并初始化表格内容为全0;
            d),根据ehci->caps->hcc_params指向的参数初始化ehci->i_thresh,该参数代表了HC会预取多少个micro-frame的periodic schedule data structure;
            e),初始化asynchronous schedule data structure:
                 ehci->reclaim = NULL;
                    ehci->reclaim_ready = 0;
                    ehci->next_uframe = -1;
                 初始化ehci->async;
            d),依据irq_thresh, park mode, periodic size等信息构造ehci->command缺省值;
            e),安装 reboot 回掉函数: ehci_reboot().
     5),对一些个别厂商的hc ic, 做特定的处理;
     6),调用ehci_pci_reinit():
         a),视chip支持情况设置 ehci->debug;
         b),调用ehci_port_power()打开ehci 每个port的电源(通过调用ehci_hub_control()完成);
1.3.2.    ehci_run() (hc_driver->start)
原型
static int ehci_run (struct usb_hcd *hcd)
 
调用时机
    usbcore 的API ---usb_add_hcd()在分配完root hub usbdevice后会通过hcd->driver->start(hcd)来调用.
 
调用说明
     无
    
主要流程
    0),该函数依照ehci spec 4.1实现;
1),调用ehci_reset() 复位HC;
2),写入periodic schedule list地址以及asynchronous schedule list地址到HC的相应寄存器:
    writel(ehci->periodic_dma, &ehci->regs->frame_list);
       writel((u32)ehci->async->qh_dma, &ehci->regs->async_next);
 
 3),对64bit 模式(HC作为bus master生成64bit地址)的处理:
       if (HCC_64BIT_ADDR(hcc_params)) {
              writel(0, &ehci->regs->segment);
       }
 
 4),启动HC:
    ehci->command
&= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
       ehci->command |= CMD_RUN;
       writel (ehci->command, &ehci->regs->command);
 
 5), 使得EHCI作为root hub port的owner;
    hcd->state = HC_STATE_RUNNING;
       writel (FLAG_CF, &ehci->regs->configured_flag);
       readl (&ehci->regs->command);   /* unblock posted writes */
 
 6),使能中断:
    writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
 使能了STS_IAA , STS_FATAL , STS_PCD ,STS_ERR 以及STS_INT五个中断.
 
1.3.3.    ehci_stop() (hc_driver->stop)
原型
static void ehci_stop (struct usb_hcd *hcd)
 
调用时机
1), usb_remove_hcd() 会通过hcd->driver->stop(hcd) 调用;
2), usb_add_hcd()中分配 root hub usbdevice时候出错也会通过hcd->driver->stop(hcd)调用;
 
调用说明
     无
   
主要流程
1),调用   ehci_port_power (ehci, 0) 关闭root hub上每个port的电源;
2),删除watchdog timer;
3),强制HC从running state 进入 idle状态,并复位HC chip, disable 所有中断;
    spin_lock_irq(&ehci->lock);
       if (HC_IS_RUNNING (hcd->state))
              ehci_quiesce (ehci); //from running to idle
       ehci_reset (ehci);
       writel (0, &ehci->regs->intr_enable);
       spin_unlock_irq(&ehci->lock);
 
4), 将root hub的port 控制权交给companion HC;
       writel (0, &ehci->regs->configured_flag);
       unregister_reboot_notifier (&ehci->reboot_notifier);
 
5), 清除未完成的asynchronous schedule QH结构;
       spin_lock_irq (&ehci->lock);
       if (ehci->async)
              ehci_work (ehci, NULL); //unlink未完成的asynchronous qhs;
       spin_unlock_irq (&ehci->lock);
 ehci_work()在ehci_work() 中会详细说明.
 
1.3.4.    ehci_get_frame()(hc_driver-> get_frame_number)
原型
static int ehci_get_frame (struct usb_hcd *hcd)
 
调用时机
1), hcd_get_frame_number():hcd.c, 该函数为struct usb_operations定义的接口;
   
调用说明
     无
   
主要流程
1),返回当前usb bus的frame number;
 
1.3.5.    ehci_urb_enqueue()(hc_driver->urb_enqueue)*
原型
static int ehci_urb_enqueue (
       struct usb_hcd       *hcd,
       struct usb_host_endpoint *ep,
       struct urb       *urb,
       gfp_t              mem_flags
)
 
调用时机
1),被hcd_submit_urb():hcd.c以hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags)方式所调用;
    2), hcd_submit_urb():hcd.c为struct usb_operations所定义的接口;
 
调用说明
     1),该函数是ehci hcd所要实现的重点函数,主要实现:将来自usbcore层的urb的传输请求转换成ehci 可识别的传输描述结构(iTD,siTD,qTD等),然后安排到echi的periodic schedule list或者asynchronous schedule list的合适位置,随后等HC完成urb对应的传输, ehci hcd通过urb->complete()通知usbcore 对应的传输结果;(该函数并不处理红色部分)
     2),该函数不会阻塞, 处理后即返回;(这一点也是usb 的传输特性, 即由usb host所主导,无论是读或者写,都是host 发起,然后等待完成)
     3),每个ep只会对应1个QH;
 
主要流程
1), 初始化一个 链表头结构:  INIT_LIST_HEAD (&qtd_list);
2), 获得 urb 所要发送到endpoint的类型;
3), 如果2)获得类型为CONTROL或者BULK:
    a), 调用qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)从urb生成一系列qTD结构,并将这些结构连接到qtd_list;
    b), 调用submit_async (ehci, ep, urb, &qtd_list, mem_flags)将qtd_list链接的qTD结构分配到ep对应的QH, 将该QH安排到ehci asynchronous schedule list中,转6);
4), 如果2)获得类型为INTERRUPT:
    a), 调用qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)从urb生成一系列qTD结构,并将这些结构连接到qtd_list;
    b), 调用intr_submit (ehci, ep, urb, &qtd_list, mem_flags)将qtd_list链接的qTD结构分配到ep对应的QH, 将该QH安排到ehci asynchronous schedule list中,转6);
5), 如果2)获得类型为ISOCHRONOUS:
    a), 如果是high speed device, 调用itd_submit (ehci, urb, mem_flags)将urb转换为iTDs,并安排到periodic schedule list中,转6);
    b), 如果是full speed device, 调用sitd_submit (ehci, urb, mem_flags)将urb转换为siTDs,并安排到periodic schedule list中,转6);
6),返回.
 
 
1.3.5.1.          qh_urb_transaction ()
原型
static struct list_head *
qh_urb_transaction (
       struct ehci_hcd             *ehci,
       struct urb              *urb,
       struct list_head       *head,
       gfp_t                     flags
)
1),参数说明
   Ehci: ehci hcd 变量(input)
   Urb : 用于生成qtd的urb(input)
   Head:  生成的qtd会依次链接在head指向链表尾部(output)
   Flags: 用于分配qtd结构,内存分配函数需要该参数(input)
   返回值:
         正常返回head指针;
         异常返回NULL.
 
调用时机
1), 仅仅为ehci_urb_enqueue()所调用;
   
调用说明
     无
   
主要流程
1),该函数根据urb中的pipe, transfer_dma, transfer_buffer_length等信息, 分配一系列的qTD结构,这些qTD结构在软件层次上依次链接到head指向的链表尾部,同时硬件层面依次通过qTD->hw_next链接到下一个qTD的qtd_dma field;
2),对分配的每一个qTD调用qtd_fill()填充qTD的hw_token, hw_buf[],hw_buf_hi []以及length等信息;
3), control/int/bulk transfer均使用该函数构造qTD linked list;
1.3.5.2.          submit_async ()
原型
static int submit_async (
       struct ehci_hcd             *ehci,
       struct usb_host_endpoint *ep,
       struct urb              *urb,
       struct list_head       *qtd_list,
       gfp_t                     mem_flags
)
1),参数说明:
    Ehci : ehci hcd 变量;(input)
    Ep:   host侧endpoint 描述信息, 由hcd_submit_urb ():hcd.c 传到ehci_urb_enqueue(),再到该函数(input)
    Urb: 上层传来的urb传输请求(input)
    Qtd_list: 根据urb生成的qtd 链表头指针(input)
    Mem_flags: 用于动态分配内存时候使用;
 
调用时机
1), 该函数仅被ehci_urb_enqueue()所调用;
 
调用说明
     无
   
主要流程
1), 该函数体内锁住了ehci->lock, 并关闭中断;
2), 判断ehci 硬件当前是否允许访问,如果不可以,那么返回- ESHUTDOWN;
3), 调用 qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv) 返回qh, 并将qtd_list添加到该qh;
4), 如果返回的qh->state ==  QH_STATE_IDLE,那么调用qh_link_async (ehci, qh_get (qh)) 将该qh链接到asynchronous schedule list中;
5), 结束.
 
其他说明
1),对qh->overlay的更新需要注意;
2),对qtd_list添加到qh(参考qh_append_tds())的理解是个难点;
1.3.5.3.          intr_submit ()
原型
static int intr_submit (
       struct ehci_hcd             *ehci,
       struct usb_host_endpoint *ep,
       struct urb              *urb,
       struct list_head       *qtd_list,
       gfp_t                     mem_flags
)
1),参数说明:
    Ehci : ehci hcd 变量;(input)
    Ep:   host侧endpoint 描述信息, 由hcd_submit_urb ():hcd.c 传到ehci_urb_enqueue(),再到该函数(input)
    Urb: 上层传来的urb传输请求(input)
    Qtd_list: 根据urb生成的qtd 链表头指针(input)
    Mem_flags: 用于动态分配内存时候使用;
 
调用时机
     1),仅被ehci_urb_enqueue()所调用;
 
调用说明
     无
   
主要流程
1), 调用spin_lock_irqsave (&ehci->lock, flags)锁住ehci->lock并关闭中断;
2), 判断ehci 硬件当前是否允许访问,如果不可以,那么status = - ESHUTDOWN,转6);
3), 调用 qh_append_tds (ehci, urb, empty, epnum, &ep->hcpriv) 返回qh,  empty为一个空的链表;
4), 如果返回的qh->state ==  QH_STATE_IDLE,那么调用staus=qh_schedule (ehci, qh) 将该qh链接到periodic schedule list中; 如果status包含错误信息,那么转6);
5), 调用 qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv)将qtd_list添加到该qh;
6), 调用spin_unlock_irqrestore (&ehci->lock, flags)解锁ehci->lock并恢复中断;
7), 错误处理部分:如果status!=0,调用qtd_list_free()释放掉qtd_list结构以及链表上的qtd结构;
 
其他说明
    1),该函数和submit_async()比较类似,不同之处在于intr_submit ()先将qh schedule到HC,然后添加qtd_list到qh,而submit_async()则与之相反; intr_submit ()这样做的目的在于,事先调用qh_schedule(ehci,qh)可以发现HC能否完成该中断传输,如果不能的话可以及早错误处理,如果能够完成,直接将qtd_list添加到已经schedule 到periodic schedule list的qh也不会有什么问题;
 
1.3.5.4.          itd_submit ()
原型
static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
       gfp_t mem_flags)
1),参数说明:
    ehci : ehci hcd 变量;(input)
    urb: 指向提交到HC的同步传输请求(input)
    mem_flags:分配内存的标志(input)
 
调用时机
     1),仅被ehci_urb_enqueue()所调用;
 
调用说明
     无
   
主要流程
    1), status = -EINVAL;
       2), Get iso_stream head
        stream = iso_stream_find (ehci, urb);
3), if (stream==NULL || (urb->interval != stream->interval)),转8);
4),调用itd_urb_transaction (stream, ehci, urb, mem_flags)分配iTD结构和struct ehci_iso_sched结构; 如果返回出错,转8);
5), 调用spin_lock_irqsave (&ehci->lock, flags)锁住ehci->lock并关闭中断;
6), 判断ehci 硬件当前是否允许访问,如果不可以,那么status = - ESHUTDOWN,转8);
   否则调用status = iso_stream_schedule (ehci, urb, stream)判断stream代表的同步传输HC是否可以满足;
7), 将stream代表的同步传输(iTDs)链接到periodic schedule list;
if (status == 0)  itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
8), 判断status并且返回;
if (unlikely (status < 0))
                 iso_stream_put (ehci, stream);
          return status;
      
其他说明
    1), struct ehci_iso_stream类似与struct ehci_qh,只不过ehci_qh是echi spec定义的HC可识别的的结构, ehci_iso_stream有与echi_qh相似的软件功能,但不是ehci spec所定义也不是HC认识的数据结构,每个isochronous endpoint对应一个ehci_iso_stream, 也是保存在struct usb_host_endpoint结构的hcpriv field, 保存了许多调度需要的信息;
 
  
 
 
1.3.5.5.          sitd_submit ()
原型
static int
sitd_link_urb (
       struct ehci_hcd             *ehci,
       struct urb              *urb,
       unsigned         mod,
       struct ehci_iso_stream   *stream
)
调用时机
1), ehci_urb_enqueue();
 
调用说明
     无
主要流程
     略
其他说明
    1),该函数和itd_submit()非常类似,就不再说明;
1.3.5.6.          qh_append_tds()
原型
static struct ehci_qh *qh_append_tds (
       struct ehci_hcd             *ehci,
       struct urb              *urb,
       struct list_head       *qtd_list,
       int                  epnum,
       void               **ptr
)
1),参数说明
   ehci : ehci hcd 变量;(input)
   urb: 根据urb生成的qtd 链表头指针(input)
   qtd_list: 根据urb生成的qtd 链表头指针(input)
   epnum: 主要用来标记是否为control endpoint(0)(input)
   ptr: 传入一个指向qh指针的指针, 如果指向的qh指针为空,该函数创建一个qh, 然后通过该参数将其指向qh的指针返回( output)
 
调用时机
1), 被submit_async()所调用;
2), 被intr_submit()所调用;
 
调用说明
     无
   
主要流程
1), 判断ptr指向是否为空,如果是,则通过qh_make (ehci, urb, GFP_ATOMIC)分配一个qh,并令*ptr=qh;
2), 令qtd 指向链表qtd_list中第一个qtd结构;
3), 如果epnum等于0, 对qh->hw_info1做一些patch处理;
4), 如果qtd指向一个有效结构,那么:
    a), 交换dummy 以及 qtd;
令dummy= qh->dummy,交换dummy指向内容和qtd指向内容;(使dummy-> qtd_dma保持原来的值)
 list_del (&qtd->qtd_list);
          list_add (&dummy->qtd_list, qtd_list);
    b), 在qh->qtd_list链表的末尾添加qtd_list 链表;
    c), 新的dummy qtd的初始化
            ehci_qtd_init (qtd, qtd->qtd_dma);
                     qh->dummy = qtd;
 
                     /* hc must see the new dummy at list end */
                     dma = qtd->qtd_dma;
                     qtd = list_entry (qh->qtd_list.prev,
                                   struct ehci_qtd, qtd_list);
                     qtd->hw_next = QTD_NEXT (dma);
 
                     /* let the hc process these next qtds */
                     wmb ();
                     dummy->hw_token = token;
 5), 返回qh;
 
注:
1>, only hc or qh_refresh() ever modify the overlay.
2>, 步骤4) 交换qh->dummy内容以及qtd内容的原因:qh overlay area和HC中已经缓冲了qh->dummy->qtd_dma,使用交换的方法可以使HC避免race condition,此处理解是个难点,可以结合Advance Queue来理解;
 
1.3.5.7.          qh_link_async
原型
static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
调用时机
     1), 被submit_async()所调用;
     2), 被end_unlink_async()所调用;
 
调用说明
     无
   
主要流程
    1),将qh插入到ehci->async后,如果需要的话enable asynchronous schedule;
2),修改qh->qh_state = QH_STATE_LINKED;
 
 
1.3.5.8.          qh_make()
原型
static struct ehci_qh *
qh_make (
       struct ehci_hcd             *ehci,
       struct urb              *urb,
       gfp_t                     flags
)
 
调用时机
1),该函数仅被qh_append_tds()所调用;
 
调用说明
     无
   
主要流程
1), 调用 ehci_qh_alloc()分配一个ehci_qh结构,令qh指向其;
2), 计算中断schedule 参数,保存qh的相关field中;
     .usecs, c_usecs, gap_uf, period, tt_usecs等;
3), 初始化 hw相关field;
     .init as live, toggle clear, advance to dummy
 
1.3.5.9.          qh_schedule()
原型
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
调用时机
1), intr_submit();
2), qh_completions();
3), ehci_urb_dequeue();
 
调用说明
     无
   
主要流程
1), 初始化 qh: 更新qh的overlay区域,以及qh->hw_next = EHCI_LIST_END;
2), 为该qh选择一个start_frame和uFrame,需要满足如下条件:
      a), start_frameperiod ,if (qh->period>0)
start_frame=0, if (qh->period==0)
          b),
         .if (qh->period == 0)
         {
           for(N=start_frame;N< ehci->periodic_size;N++)
               第N frame中的每个uFrame可以预留qh->usecs,并使得包括qh->usecs的同步带宽占用小于当前uFrame总带宽的80%;
         }
         else
         {
for(N=start_frame;N< ehci->periodic_size;N+=qh-               第N frame中的uFrame可以预留qh->usecs,并使得包括qh->usecs的同步带宽占用小于当前uFrame总带宽的80%;
               如果是FS/LS transfer,那么还要满足CS需要的时间: 第N frame中的uFrame+qh->gap_uf, uFrame+qh->gap_uf+1两个微帧内,可以预留qh->c_usecs, 并使得包括qh->c_usecs的同步带宽占用小于当前uFrame总带宽的80%;
}
 
c), if(FS/LS transfer )  uFrame < 6
以上条件主要通过check_intr_schedule()完成选择.
 
     3), 使用2)选择的start_frame 和 uFrame 设置qh的一些field:
          qh->start = frame;
 
               /* reset S-frame and (maybe) C-frame masks */
               qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK));
               qh->hw_info2 |= qh->period? cpu_to_le32 (1 << uframe): __constant_cpu_to_le32 (QH_SMASK);
                 qh->hw_info2 |= c_mask; //用于cs, 在2)中返回;
      4),调用 qh_link_periodic (ehci, qh) 链接到peridic schedule list;
 
    
其他说明
    1), 该函数只用于将interrupt qh 调度到 periodic schedule list,而不能用于control/bulk qh; 该函数同时支持HS/FS/LS的 interrupt qh schedule;
    2), 该函数比较关键,需要选择qh被链接到periodic schedule list的start frame和uframe,保证对应的uframe内同步传输不会超过125us的80%;
 
1.3.5.10.     qh_link_periodic()
原型
static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
调用时机
     1),仅被qh_schedule()所调用;
 
调用说明
     无
   
主要流程
    1), if (period == 0) period = 1;
2),将qh链接到periodic schedule list的合适slot:
 for (i = qh->start; i < ehci->periodic_size; i += period) {    
           将qh 插入到ehci->periodic [i]指向的链表中的qh 链表部分,同时保持qh链表中qh按照period从大到小的顺序(保证poll rate 从低到高);                                                                                                   
           将qh 插入到ehci->pshadow [i]指向链表的相应位置;
}
3), 设置qh->qh_state;
qh->qh_state = QH_STATE_LINKED;
       qh_get (qh);
4), 如果尚未使能同步调度,使能其:
if (!ehci->periodic_sched++)
              return enable_periodic (ehci);
 
其他说明
 
1.3.5.11.     check_intr_schedule()
原型
static int check_intr_schedule (
       struct ehci_hcd             *ehci,
       unsigned         frame,
       unsigned         uframe,
       const struct ehci_qh      *qh,
       __le32                   *c_maskp
)
1),参数说明:
  ehci : ehci hcd 变量(input)
   frame:check 的frame number(input)
   uframe:check 的uframe number(input)
   qh : check的 interrupt qh(input)
   c_maskp : 返回的complete transaction uframe mask(output)
调用时机
1),仅为qh_schedule()所调度;
 
调用说明
     无
   
主要流程
  1), retval = -ENOSPC;
   2), if (qh->c_usecs && uframe >= 6)           /* FSTN territory? */(不支持FSTN)
              goto 7);
   3), 判断在微帧序列 frame:uframe,frame+qh->period:uframe,…中,qh->usecs是否可以完成
if (!check_period (ehci, frame, uframe, qh->period, qh->usecs))
              goto 7);
   4),如果qh不包含complete transaction,那么令*c_maskp=0,retval=0;
if (!qh->c_usecs) {
              retval = 0;
              *c_maskp = 0;
              goto 7);
       }
   5),对包含complete transaction的qh设置cs mask,安全起见连续设置了两个uframe来处理complete transaction:
       mask = 0x03 << (uframe + qh->gap_uf);
          *c_maskp = cpu_to_le32 (mask << 8);
       mask |= 1 << uframe;
   6),判断TT是否能满足该periodic schedule(frame是start frame, mask包含了相关的uframe),如果可以满足,继续判断HC是否可以满足complete transaction的需求:
if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) {
               if (!check_period (ehci, frame, uframe + qh->gap_uf + 1,
                                   qh->period, qh->c_usecs))
                      goto done;
               if (!check_period (ehci, frame, uframe + qh->gap_uf,
                                   qh->period, qh->c_usecs))
                     goto 7);
               retval = 0;
       }
7),return retval;
         
 
其他说明
    1), qh->gap_uf表示start transaction(输出还包含DATA)和complete transaction(输入还包含DATA)之间的微帧间隔数,该参数在qh_make()中计算;
1.3.5.12.     check_period()
原型
static int check_period (
       struct ehci_hcd *ehci,
       unsigned frame,
       unsigned uframe,
       unsigned period,
       unsigned usecs
)
1),参数说明:
   ehci : ehci hcd 变量(input)
   frame:check 的frame number(input)
   uframe:check 的uframe number(input)
  period: 
所要check的periodic schedule的以frame为单位的周期(input)
       0表示周期为1 uframe,即每个frame的每个uframe都需要schedule
   usecs:
   &
阅读(2480) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~