usb hub

   (1)特别的爱给特别的Root Hub    
    
    
  通常做芯片的人把usb主机控制器和root hub集成在一起。   
    
    
    
    
timg.jpeg
    
    
    
    
    
    
   (2)一样的精灵不一样的API    
    
    
   usb_init() ---> usb_hub_init( )--->   
  {   
  1.kthread_run(hub_thread, NULL, "khubd")   
   if (!IS_ERR(khubd_task)) 
//IS_ERR()来判断指针是否有效.IS_ERR()为1就表示指针有错,或者准确一点说叫做指针无效.   
  //如果IS_ERR()返回值是0,那么说明没有问题,于是return 0,也就是说usb_hub_init()就这么结束了.反之,就会执行usb_deregister(),因为内核线程没有成功创建,hub就没法驱动起来了..最后返回-1.回到usb_init()中我们会知道,接下来usb_hub_cleanup()就会被调用.usb_hub_cleanup()同样定义于drivers/usb/core/hub.c中   
    
    
  2.usb_deregister(&hub_driver)  //注册的是hub的驱动程序   
    
    
  }   
    
    
  static struct usb_driver hub_driver = {   
      .name =        "hub",   
      .probe =     hub_probe ,   
      .disconnect =    hub_disconnect,   
      .suspend =    hub_suspend,   
      .resume =    hub_resume,   
      .reset_resume =    hub_reset_resume,   
      .pre_reset =    hub_pre_reset,   
      .post_reset =    hub_post_reset,   
      .unlocked_ioctl = hub_ioctl,   
      .id_table =    hub_id_table,   
      .supports_autosuspend =    1,   
  };   
    
    
  hub本身就是两种,一种是普通的hub,一种是root hub.对于普通hub,它完全可能也是和U盘一样,在某个时刻被你插入,然后这种情况下hub_probe被调用,但是对于root hub就不需要这么多废话了,root hub肯定是有的,只要你有host controller,就一定会有root hub,所以hub_probe()基本上是很自然的就被调用了,不用说非得等待某个插入事件的发生,没这个必要.只要你有usb host controller,那么在usb host controller的驱动程序初始化的过程中,它就会调用hub_probe()来探测root hub,不管你的host controller是ohci,uhci,还是ehci的接口.   
    
    
  vod  usb_hub_cleanup (void)---> kthread_stop (khubd_task)结束这个内核线程   
    
    
    
    
    
    
  static inline long __must_check  IS_ERR (const void *ptr)   
    
    
  所有的驱动程序都是运行在内核空间,内核空间虽然很大,但总是有限的.而在这有限的空间中,其最后一个page是专门保留的,也就是说一般人不可能用到内核空间最后一个page的指针.你在写设备驱动程序的过程中,涉及到的任何一个指针,必然有三种情况,一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针.而所谓的错误指针就是指其已经到达了最后一个page.比如对于32bit的系统来说,内核空间最高地址0xffffffff,那么最后一个page就是指的0xfffff000~0xffffffff(假设4k一个page).这段地址是被保留的,一般人不得越雷池半步,如果你发现你的一个指针指向这个范围中的某个地址,那么恭喜你,你的代码肯定出错了   
    
    
  MAX_ERRNO定义为4095 就是最大错误号   
  最常见的几个是-EBUSY,-EINVAL,-ENODEV,-EPIPE,-EAGAIN,-ENOMEM 错误   
    
    
    
    
  1.对于Linux内核来说,不管任何体系结构,最多最多,错误号不会超过4095.而4095又正好是比4k小1,即4096减1.而我们知道一个page可能是4k。也可能是更多,比如8k,但至少它也是4k,所以留出一个page出来就可以让我们把内核空间的指针来记录错误了.   
    
    
  2.比如我们这里的IS_ERR(),它就是判断kthread_run()返回的指针是否有错,如果指针并不是指向最后一个page,那么没有问题,申请成功了,如果指针指向了最后一个page,那么说明实际上这不是一个有效的指针,这个指针里保存的实际上是一种错误代码.   
    
    
  3.而通常很常用的方法就是先用IS_ERR()来判断是否是错误,然后如果是,那么就调用PTR_ERR()来返回这个错误代码.只不过咱们这里,没有调用PTR_ERR()而已,因为起决定作用的还是IS_ERR(),而PTR_ERR()只是返回错误代码,也就是提供一个信息给调用者,如果你只需要知道是否出错,而不在乎因为什么而出错,那你当然不用调用PTR_ERR()了,这里如果出错了的话,最终usb_deregister()会被调用,并且usb_hub_init()会返回-1.   
    
    
    
    
   (3)那些队列,那些队列操作函数    
    
    
  static int hub_thread(void *__unused)   
  {   
     do {   
          hub_events();   
          wait_event_freezable(khubd_wait,!list_empty(& hub_event_list ) ||kthread_should_stop());//list_empty判断队列hub_event_list是否为空,不为空就去处理,或者说就去触发hub_events()函数   
      } while (!kthread_should_stop() || !list_empty(&hub_event_list)); //如果kthread_should_stop为真,进程结束   
    
    
  }   
    
    
  #define LIST_HEAD_INIT(name) { &(name), &(name) }   
  #define LIST_HEAD(name) \   
        struct list_head name = LIST_HEAD_INIT(name)   
      
    static inline void INIT_LIST_HEAD(struct list_head *list)  //struct list_head hub_event_list    
    {   
        list->next = list;   
        list->prev = list;   
    } //初始化双向链表   
    
    
    
    
  static inline void  list_add_tail (struct list_head *new, struct list_head *head) //往队列里添加   
  {   
   __list_add(new, head->prev, head);   
  }   
    
    
    static inline void  __list_add (struct list_head *new,                                                           
                      struct list_head *prev,                                                                      
                      struct list_head *next)                                                                      
    {                                                                                                              
        next->prev = new;                                                                                          
        new->next = next;                                                                                          
        new->prev = prev;                                                                                          
        prev->next = new;                                                                                          
    }//队列的末尾加一个元素   
    
    
    
    
   static inline void  list_del_init (struct list_head *entry) //从队列里删除元素,并将该元素进行初始化                 
   {                                                                                                              
       __list_del_entry(entry);                                                                                   
       INIT_LIST_HEAD(entry);  //初始化为最初的状态,空链表                                                                                  
   }   
    
    
  #define  list_entry (ptr, type, member)    container_of(ptr, type, member)    
    
    
  struct  usb_hub  {   
    
    
  struct list_head    event_list;   
    
    
  }   
  //struct list_head event_list,这个结构体变量将为我们组建一个队列,或者说组建一个链表。一个struct usb_hub代表一个Hub,每一个struct usb_hub有一个event_list,即每一个Hub都有它自己的一个事件列表。要知道Hub可以有一个或者多个,而Hub驱动只需要一个,或者说khubd这个精灵进程永远都只有一个。而我们的做法是,不管实际上有多少个Hub,我们最终都会将其event_list挂入到全局链表hub_event_list中来统一处理(hub_event_list是对于整个USB系统来说是全局的,但对于整个内核来说当然是局部的,毕竟它前面有一个static)。   
    
    
  //因为最终处理所有Hub事务的都是这一个精灵进程khubd,它只需要判断hub_event_list是否为空,不为空就去处理。或者说就去触发hub_events()函数,但当我们真的要处理Hub的事件时,当然要知道具体是哪个Hub触发了这起事件。而list_entry的作用就是,从struct list_head event_list得到它所对应的struct usb_hub结构体变量   
    
    
   struct list_head *tmp;      
   struct usb_hub *hub;      
   tmp=hub_event_list.next;      
   hub=list_entry(tmp,struct usb_hub,event_list);  //从全局链表hub_event_list中取出一个来,叫做tmp,然后通过tmp获得它所对应的struct usb_hub。   
    
    
   static void  hub_events (void)    
   {    
   tmp = hub_event_list.next;    
   list_del_init(tmp);    
   hub =  list_entry (tmp, struct usb_hub, event_list);    
   }    
    
    
  总分总思想:   
  1.先设置一个链表hub_event_list,设置一个总的函数hub_events(),这是"总";   
  2.然后每一个Hub都有一个event_list,每当有一个hub的event_list出现了变化,就把它的event_list插入到hub_event_list中来,这是"分";   
  3.然后触发总函数hub_events(),这又是"总";   
  4.然后在hub_events()里又根据event_list来确定是哪个struct usb_hub,或者说是哪个Hub有问题,又针对该Hub进行具体处理,这又是"分"。   
  这就是Linux中Hub驱动的中心思想。   
    
    
    
    
    
    
   (4)等待 只因曾经承诺    
    
    
  bool set_freezable(void)   
  {   
    try_to_freeze(); //   
  }   
    
    
  每个任务都有4个标志用于任务冷冻机制,PF_NOFREEZE, PF_FROZEN, TIF_FREEZE和PF_FREEZER_SKIP(最后一个是辅助的[auxiliary])。没有设置PF_NOFREEZE标志的任务(包括所有的用户空间进程和部分内核线程)被认为是“可冷冻的”   
    
    
  对用户空间进程而言,信号处理程序会自动调用try_to_freeze(),但是可冷冻的内核线程则需要在合适的地方显示地调用它,或者使用wait_event_freezable()或wait_event_freezable_timeout()宏(定义在include/linux/freezer.h),这些宏在TIF_FREEZE标志被置位时将 内核线程转入可中断睡眠态 ,并且调用try_to_freeze()。一个可冷冻的内核线程的主循环可以采用下面的框架:   
    
    
   set_freezable() ;   
           do {   
                     hub_events();   
                     wait_event_freezable(khubd_wait,   
                                       !list_empty(&hub_event_list) ||   
                                       kthread_should_stop());   
           } while (!kthread_should_stop() || !list_empty(&hub_event_list));   
           如果一个内核线程在“冰箱”为其设置TIF_FREEZE后调用try_to_freeze()失败,便意味着任务冷冻的失败,整个休眠操作都应该被取消。出于此原因,可冷冻的内核线程必须在某处调用try_to_freeze()函数,或者使用宏wait_event_freezable()或wait_event_freezable_timeout()中的一个。   
           在系统内存状态从一个休眠映像中恢复,并且设备被重新初始化之后,需要调用解冻函数thaw_processes()清除所有冷冻任务的PF_FROZEN标志。此后,被解冻的任务离开refrigerator(),继续运行   
    
    
    
    
  static void hub_events(void)   
  {   
   while (1) {   
          spin_lock_irq(&hub_event_lock);   
          if ( list_empty (&hub_event_list))   
          {   
              spin_unlock_irq(&hub_event_lock);   
              break;   
          } //一个while(1)循环;判断hub_event_list是否为空,第一次调用这个函数时,hub_event_list就是初值,初值为空,所以这里就是空,即list_empty()返回1,然后break语句跳出while循环。while循环的结尾就是这个hub_events()函数的结尾,也就是说在这里几百行的代码就结束了,我们直接退出这个函数,返回到hub_thread()中,调用wait_event_interruptible()进入睡眠,然后等待有事件发生。   
    
    
    
    
    
    
  }   
    
    
  对于Hub来说,当你插入一个设备到Hub口里,就会触发一件事件。而第一件事件的发生其实是Hub驱动程序本身的初始化,,由于Root Hub的存在,所以hub_probe必然会被调用.在主机控制器的驱动程序中初始化代码中都会调用一个叫做hcd_register_root()的函数,进而转到usb_register_root_hub(),几经周转,最终hub_probe就会被调用.   
    
    
    
    
  wait_event_freezable( khubd_wait. ....)   
  /* Wakes up khubd */   
  static  DECLARE_WAIT_QUEUE_HEAD(khubd_wait) ; 定义   
  //一个等待队列头,将来要唤醒这个睡眠进程的一定是类似这样的一行代码:wake_up(&khubd_wait)。整个内核代码中只有一个地方会调用这个代码,那就是kick_khubd()   
    
    
    
    
    
    
   (5)最熟悉的陌生人----probe    
    
    
  Hub的传输是中断传输   
   hub_probe {   
       endpoint = &desc->endpoint[0].desc; // Hub只有一个端点(除去端点0)也就是中断端点 ,得到这个唯一的端点所对应的端点描述符   
    
    
     if (!usb_endpoint_is_int_in(endpoint)) //判断这个端点是不是中断端点   
    
    
      hub = kzalloc(sizeof(*hub), GFP_KERNEL); //申请usb_hub结构体内存,kzalloc = kmalloc + memset   
    
    
      INIT_LIST_HEAD(&hub->event_list); //总分的结构,一个总的事件队列,hub_event_list,然后各个Hub都有一个分的事件队列,就是这里的hub->event_list,前面已经初始化了全局的hub_event_list,这里对单个Hub就得为其初始化一个event_list。   
      
      hub->intfdev = &intf->dev;   
      hub->hdev = hdev;    
  //struct usb_hub中的两个成员:struct device *intfdev;struct usb_device *hdev   
  第一个,不管你是USB设备也好,PCI设备也好,SCSI设备也好,Linux内核中都为你准备一个struct device结构体来描述,所以intfdev就是和Hub相关联的struct device指针;   
  第二个,不管是Hub也好,U盘也好,移动硬盘也好,USB鼠标也好,USB Core都准备一个struct usb_device来描述,所以hdev是与这个Hub相对应的struct usb_device指针。   
    
    
    
    
  }   
    
    
  static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)     
  ---->  终端打印hub 1-0:1.0: USB hub found   
    
    
  hub_configure   
  -->   终端打印 hub 1-0:1.0: 1 port detected   
    
    
    
    
   (6)蝴蝶效应    
    
    
  工作队列:   
    
    
  #define  INIT_WORK (_work, _func)                     \   
      do {                                \   
          __INIT_WORK((_work), (_func), 0);           \   
      } while (0)   
    
    
    
    
    
    
  #define  INIT_DEFERRABLE_WORK (_work, _func)              \   
      __INIT_DELAYED_WORK(_work, _func, TIMER_DEFERRABLE)   
    
    
  #define  __INIT_DELAYED_WORK (_work, _func, _tflags)          \   
      do {                                \   
          INIT_WORK(&(_work)->work, (_func));         \   
          __setup_timer(&(_work)->timer, delayed_work_timer_fn,   \   
                    (unsigned long)(_work),           \   
                    (_tflags) | TIMER_IRQSAFE);       \   
      } while (0)   
    
    
    
    
    
    
   hub_probe {   
    
    
      INIT_DELAYED_WORK(&hub->leds,  led_work ); //初始化工作队列 两个参数,&hub->leds和led_work()的关系,就最终让hub_leds这个struct work_struct结构体和函数led_work()相绑定了起来   
    
    
      INIT_DELAYED_WORK(&hub->init_work, NULL);   
  //使用INIT_WORK()或者INIT_DELAYED_WORK()来初始化这么一个工作,或者叫任务,初始化了之后,将来如果咱们希望调用这个led_work()函数,那么咱们只要用一句schedule_work()或者schedule_delayed_work()就可以了,这里使用的是 INIT_DELAYED_WORK() ,那么之后我们就会调用 schedule_delayed_work() ,这俩是一对.它表示,您希望经过一段延时然后再执行某个函数,所以,咱们今后会见到schedule_delayed_work()这个函数的,而它所需要的参数,一个就是咱们这里的&hub->leds,另一个就是具体自己需要的延时.&hub->leds是什么呢?struct usb_hub中的成员,struct delayed_work leds,专门用于延时工作的,再看struct delayed_work   
    
    
  }   
    
    
    
    
  struct  usb_hub  {   
       struct  delayed_work  leds;   
  }   
    
    
  struct  delayed_work  {   
      struct  work_struct  work;   
      struct timer_list timer;                                                                                
      /* target workqueue and CPU ->timer uses to queue ->work */                                          
      struct workqueue_struct *wq;                                                                         
      int cpu;                                                                                             
  };   
    
    
  struct  work_struct  {   
      atomic_long_t data;   
      struct list_head entry;   
       work_func_t func;  //初始化工作队列,只是把func指向led_work, 调用schedule_delayed_work()的时候,只要传递struct work_struct的结构体参数即可    
  #ifdef CONFIG_LOCKDEP   
      struct lockdep_map lockdep_map;   
  #endif   
  };   
    
    
  Linux内核中工作队列机制提供的接口,两对函数 INIT_DELAYED_WORK()对schedule_delayed_work() , INIT_WORK()对schedule_work().    
    
    
  工作队列机制:cancel_delayed_work(struct delayed_work *work)和flush_scheduled_work().   
  其中cancel_delayed_work(),对一个延迟执行的工作来说,这个函数的作用是在这个工作还未执行的时候就把它给取消掉.而flush_scheduled_work()的作用,是为了防止有竞争条件的出现,基本上每次cancel_delayed_work之后您都得调用flush_scheduled_work()这个函数,   
    
    
    
    
    
    
   hub_configure()--->hub_activate()--->schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);    
    
    
    
    
    
    
    
    
    
    
   (7)While You Were Sleeping(一)    
    
    
   hub_probe {   
    
    
        usb_set_intfdata (intf, hub);//作用就是让intf和hub关联起来,我们知道struct usb_interface *intf,就可以追溯到与之关联的struct usb_hub指针.   
    
    
      if (hdev->speed == USB_SPEED_HIGH)                                                                   
          highspeed_hubs++; //如果是高速设备,  highspeed_hubs加一   
    
    
       if (hub_configure(hub, endpoint) >= 0)                                                               
          return 0; //配置hub的,返回值小于0就算出错了,没出错那么hub_probe就返回0                                                                                        
                                                                                                           
      hub_disconnect (intf);                                                                               
      return -ENODEV;  // hub_configure出错的话执行hub_disconnect断开连接   
    
    
  }   
    
    
    
    
  static int  hub_configure (struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint) //对hub进行必要的配置,然后就启动hub   
    
    
    
    
    
    
    
    
   (8)While You Were Sleeping(二)    
    
    
  static int  hub_configure (struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint)   
  {   
    
    
     ret =  get_hub_descriptor (hdev, hub->descriptor); //步入hub协议   
    
    
    
    
  }   
    
    
    
    
  static int  get_hub_descriptor (struct usb_device *hdev, void *data)   
  {   
  for (i = 0; i < 3; i++) { //循环三次,为了防止通信错误   
      ret =  usb_control_msg (hdev, usb_rcvctrlpipe(hdev, 0),USB_REQ_GET_DESCRIPTOR, USB_DIR_IN |   
  USB_RT_HUB,dtype << 8, 0, data, size,USB_CTRL_GET_TIMEOUT); //发送一个request控制传输的控制请求,以获得hub的描述符   
  }   
  }//回值将是成功传输的字节长度,hub描述符至少9个字节   
    
    
  了解USB_REQ_GET_DESCRIPTOR这个请求的协议要求   
    
    
    
    
    
    
    
    
   (8)While You Were Sleeping(三)    
    
    
  struct  usb_hub_descriptor  {  //hub描述符  至少9个字节   
      __u8  bDescLength;   
      __u8  bDescriptorType;         
      __u8  bNbrPorts;   
      __le16 wHubCharacteristics;   
      __u8  bPwrOn2PwrGood;      
      __u8  bHubContrCurrent;    
      /* 2.0 and 3.0 hubs differ here */   
      union {   
          struct {   
              /* add 1 bit for hub status change; round to bytes */   
              __u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];   
              __u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];   
          }  __attribute__ ((packed)) hs;   
          struct {   
              __u8 bHubHdrDecLat;   
              __le16 wHubDelay;   
              __le16 DeviceRemovable;   
          }  __attribute__ ((packed)) ss;   
      } u;   
  } __attribute__ ((packed));   
    
    
  bNbrPorts,它代表Number of downstream facing ports that this hub supports,就是说这个hub所支持的下行端口,   
  bHubContrCurrent是Hub控制器的最大电流需求,   
  DeviceRemoveable是用来判断这个端口连接的设备是否是可以移除的,每一个bit代表一个端口,如果该bit为0,则说明可以被移除,为1,就说明不可以移除.   
    
    
    
    
  static int  hub_configure (struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint)   
  {   
  ret = usb_get_status(hdev,  USB_RECIP_DEVICE , 0, &hubstatus); //   
  //这个请求的类型有三种,一种是获得Device的状态,一种是获得Interface的状态,一种是获得端点的状态,这里传递的是USB_RECIP_DEVICE,也就是获得Device的状态.那么函数返回之后,Device的状态就被保存在了hubstatus里面.   
    
    
   maxp =  usb_maxpacket (hdev, pipe, usb_pipeout(pipe));//获得一个endpoint描述符里面的wMaxPacketSize,赋给maxp,一个端点一次最多传输的数据就是wMaxPacketSize   
    
    
  hub->urb =  usb_alloc_urb (0, GFP_KERNEL);//申请一个urb,然后填充一个urb,usb_fill_int_urb()   
    
    
     usb_fill_int_urb (hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,  hub, endpoint->bInterval);   
    
    
    
    
  }   
    
    
  int  usb_get_status (struct usb_device *dev, int type, int target, void *data)   
  {   
   et =  usb_control_msg (dev, usb_rcvctrlpipe(dev, 0),   
          USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status,   
          sizeof(*status), USB_CTRL_GET_TIMEOUT);   
    
    
  }   
  发送一个控制传输请求(获得状态)   
  USB_REQ_GET_STATUS   
    
    
    
    
  static inline void  usb_fill_int_urb (struct urb *urb,struct usb_device *dev,unsigned int pipe, void *transfer_buffer, int buffer_length,usb_complete_t complete_fn, void *context, int interval)   
  {   
      urb->dev = dev;    
      urb->pipe = pipe;   
      urb->transfer_buffer = transfer_buffer;   
      urb->transfer_buffer_length = buffer_length;   
      urb->complete = complete_fn;   
      urb->context = context;   
      if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER)   
          urb->interval = 1 << (interval - 1);   
      else    
          urb->interval = interval;   
      urb->start_frame = -1;   
  }   
  //transfer_buffer就是hub->buffer,transfer_buffer_length就是maxp,complete就是hub_irq,context被赋为了hub,而interval被赋为endpoint->bInterval,usb协议,frame,即帧,微帧,即microframe.一个帧是1ms,而一个微帧是1/8ms,也就是125us.   
    
    
    
    
   (9)While You Were Sleeping(四)    
    
    
  hub里面的中断端点是IN的,不是OUT的.并不是中断传输数据一定是从设备到主机,不过hub需要的确实只是IN的传输.中断是由设备产生的.在usb的世界里两个重要角色,主机,设备.   
    
    
  对于IN的中断端点,主机会定期向设备访问。一个端点的端点描述符里bInterval是总线访问周期,期望host隔多久访问她.设备要进行中断传输,需要提交一个urb,里面注明这个探访周期。   
    
    
  host controller定期给你发送一个IN token,就是说发送这么一个包, 而你如果有中断等在那里,那你就告诉她,你有了(中断).同时你会把与这个中断相关的数据发送给主机,这就是中断传输的数据阶段,显然,这就是IN方向的传输.然后主机接收到数据之后他会发送回来一个包,向你确认一下.   
    
    
  OUT类型的中断端点是如何进行数据传输,虽然Hub里根本就没有这种款式的端点.分三步走:   
  第一步,host发送一个OUT token包,   
  第二步就直接发送数据包,   
  第三步,设备回应,也许回应ACK,表示成功接收到了数据,也许回应NAK,表示失败了,也许回应STALL,表示设备端点本身有问题,传输没法进行.   
    
    
  不同速度的interval的单位不一样:   
  1. 对于高速设备来说,比如它的端点的bInterval的值为n,那么这表示它渴望的周期是2的(n-1)次方个微帧,比如n为4,那么就表示2的3次方个微帧,即8个125微秒,换句话说就是1毫秒.对于高速设备来说,spec里规定,n的取值必须在1到16之间,   
  2.对于全速设备来说,其渴望周期在spec里有规定,必须是在1ms到255ms之间,   
  3.对于低速设备来说,其渴望周期必须在10毫秒到255毫秒之间.可见,对于全速/低速设备来说,不存在这种指数关系,所以urb->interval直接被赋值为bInterval   
    
    
  static int  hub_configure (struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint)   
  {   
    
    
  static void  hub_activate (struct usb_hub *hub, enum hub_activation_type type)   
    
    
  }   
    
    
    
    
  static void  hub_activate (struct usb_hub *hub, enum hub_activation_type type)   
  {   
      if (type == HUB_INIT2)   
          goto init2;   
      if (type == HUB_INIT3)   
          goto init3;   
    
    
    
    
   init3:   
      hub->quiescing = 0;   
    
    
      status =  usb_submit_urb (hub->urb, GFP_NOIO);   
  //前面我们调用usb_fill_int_urb()填充好了一个urb,这会儿就该提交了,然后host controller就知道了,然后如果一切顺利的话,host controller就会定期来询问hub,问它有没有中断,有的话就进行中断传输.   
      if (status < 0)   
          dev_err(hub->intfdev, "activate --> %d\n", status);   
      if (hub->has_indicators && blinkenlights)   
           schedule_delayed_work (&hub->leds, LED_CYCLE_PERIOD);   
  //执行一个函数,schedule_delayed_work()延时调用,调用的是leds对应的那个work函数,即我们当初注册的那个led_work().这里LED_CYCLE_PERIOD就是一个宏,表明延时多久, #define LED_CYCLE_PERIOD        ((2*HZ)/3)   关于这个指示灯的代码我们以后再分析   
      /* Scan all ports that need attention */   
       kick_khubd (hub);   
      /* Allow autosuspend if it was suppressed */   
      if (type <= HUB_INIT3)   
          usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));   
    
    
    
    
  }   
    
    
    
    
  static void  kick_khubd (struct usb_hub *hub)   
  {   
      unsigned long   flags;   
    
    
      spin_lock_irqsave(&hub_event_lock, flags);   
      if (!hub->disconnected && list_empty(&hub->event_list)) {   
           list_add_tail(&hub->event_list, &hub_event_list);    
    
    
          /* Suppress autosuspend until khubd runs */   
          usb_autopm_get_interface_no_resume(   
                  to_usb_interface(hub->intfdev));   
           wake_up(&khubd_wait);    
      }   
      spin_unlock_irqrestore(&hub_event_lock, flags);   
  //if判断语句,很显然,现在我们是第一次来到这里, 不用说,hub->event_list是空的,所以,条件满足,于是list_add_tail()会被执行,往那个总的队列hub_event_list里面加入hub->event_list,然后调用wake_up(&khubd_wait)去唤醒hub_thread().从此hub_events()函数将再次被执行.   
  }   
    
    
    
    
  整个关于hub的配置就讲完了,从现在开始,hub就可以正式上班了.唤醒了那个该死的hub_thread(),进入hub_events().   
    
    
    
    
   (10) 再向虎山行    
    
    
    
    
  static void  hub_events (void)   
  {   
       */   
      while (1) {   
          /* Grab the first entry at the beginning of the list */   
          spin_lock_irq(&hub_event_lock);   
          if ( list_empty (&hub_event_list)) {   
              spin_unlock_irq(&hub_event_lock);   
              break;   
          }   
           tmp = hub_event_list.next;                                                                        
           list_del_init (tmp);                                                                              
                                                                                                           
          hub =  list_entry (tmp, struct usb_hub, event_list);                                               
          kref_get(&hub->kref);                                                                            
          spin_unlock_irq(&hub_event_lock);                                                                
                                                                                                           
          hdev = hub->hdev;                                                                                
          hub_dev = hub->intfdev;                                                                          
          intf = to_usb_interface(hub_dev);   
  //hub_event_list这一次不是空的了,我们刚刚在kick_khubd()里面才执行了往这个队列里插入的操作,所以我们不会再像第一次一样,从2621行的break跳出循环。相反,我们直接走到2624行,把刚才插入队列的那个节点取出来,存为tmp,然后把tmp从队列里删除掉(是从队列里删除,不是把tmp本身给删除)。   
  list_entry(),这是一个经典的函数,或者说宏。通过这个宏这里得到的是触发hub_events()的Hub。同时用局部变量hdev记录hub->hdev。又得到对应的struct usb_interface和struct device。         
    
    
    
    
      if (hub->error) {   
              dev_dbg (hub_dev, "resetting for error %d\n",   
                  hub->error);   
              ret =  usb_reset_device (hdev); //hub->error不为0的话,复位usb device   
              if (ret) {   
                  dev_dbg (hub_dev,   
                      "error resetting hub: %d\n", ret);   
                  goto loop_autopm;   
              }   
              hub->nerrors = 0;   
              hub->error = 0;   
          }   
  }   
    
    
    
    
    
    
    
    
    
    
   (11)一个都不能少    
    
    
  static void  hub_events (void)   
  {   
         for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {   
              if (test_bit(i, hub->busy_bits))   
                  continue;   
              connect_change = test_bit(i, hub->change_bits);   
              wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);   
              if (!test_and_clear_bit(i, hub->event_bits) &&   
                      !connect_change && !wakeup_change)   
                  continue;   
    
    
              ret =  hub_port_status (hub, i,    
                      &portstatus, &portchange);   
              if (ret < 0)   
                  continue;   
    
    
              if (connect_change)  
                   hub_port_connect_change (hub, i,portstatus, portchange);  
    
    
    
    
    
  }   
    
    
    
    
  static int hub_port_status(struct usb_hub *hub, int port1,u16 *status, u16 *change)  
    
  {  
    
   ret = get_port_status(hub->hdev, port1, &hub->status->port);  
    
    
    
  }   
    
    
    
    
    
  static int get_port_status(struct usb_device *hdev, int port1,  
          struct usb_port_status *data)  
  {  
      int i, status = -ETIMEDOUT;  
    
      for (i = 0; i < USB_STS_RETRIES &&                                                                            
              (status == -ETIMEDOUT || status == -EPIPE); i++) {  
          status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),  
               USB_REQ_GET_STATUS , USB_DIR_IN | USB_RT_PORT, 0, port1,                                               
              data, sizeof(*data), USB_STS_TIMEOUT); //又一次控制传输  
      }                                                                                                             
      return status; //get_port_status()返回值就是GET PORT STATUS请求的返回数据的长度  
  }   
    
    
    
    
   (12)盖茨家对Linux代码的影响   
    
    
    
  /* Handle physical or logical connection change events.  
   * This routine is called when:  
   *  a port connection-change occurs;  
   *  a port enable-change occurs (often caused by EMI);  
   *  usb_reset_and_verify_device() encounters changed descriptors (as from  
   *      a firmware download)  
   * caller already locked the hub  
    
   */  
    
  当hub端口上有连接变化时调用这个函数,有三种情况会调用这个函数:  
  1.一个是连接有变化,  
  2.一个是端口本身重新使能,即所谓的enable,这种情况通常就是为了对付电磁干扰的  
  3.复位一个设备的时候发现其描述符变了   
    
  static void  hub_port_connect_change (struct usb_hub *hub, int port1,  
                      u16 portstatus, u16 portchange)  
  {  
    
   dev_dbg (hub_dev,"port %d, status %04x, change %04x, %s\n",port1, portstatus, portchange,  portspeed (hub, portstatus));  
    
    
    
    
    /* Try to resuscitate an existing device */  
       udev = hub->ports[port1 - 1]->child;   
      if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&  
              udev->state != USB_STATE_NOTATTACHED) {  
          usb_lock_device(udev);  
          if (portstatus & USB_PORT_STAT_ENABLE) {  
              status = 0;     /* Nothing to do */  
    
  #ifdef CONFIG_PM_RUNTIME  
          } else if (udev->state == USB_STATE_SUSPENDED &&  
                  udev->persist_enabled) {  
              /* For a suspended device, treat this as a  
               * remote wakeup event.  
               */  
              status = usb_remote_wakeup(udev);  
  #endif  
    
          } else {  
              status = -ENODEV;   /* Don't resuscitate */  
          }  
          usb_unlock_device(udev);  
     if (status == 0) {  
              clear_bit(port1, hub->change_bits);  
              return;  
          }  
      }  
    
      /* Disconnect any existing devices under this port */  
      if (udev) {  
          if (hcd->phy && !hdev->parent &&  
                  !(portstatus & USB_PORT_STAT_CONNECTION))  
              usb_phy_notify_disconnect(hcd->phy, udev->speed);  
           usb_disconnect(&hub->ports[port1 - 1]->child);   
      }  
       clear_bit(port1, hub->change_bits);   
    
  //端口连接有变化,有两个方向:  
  1.原来没有设备现在有了  
  2.一个是原来有设备而现在没有.  
  对于前者,hdev->children[port1-1]肯定为空,而对于后者这个指针应该就不为空  
  对于前者,我们接下来要做的事情就是对新连进来的设备进行初始化进行配置分配地址然后为该设备寻找相应的设备驱动程序,如果找到合适的了就调用该设备驱动程序提供的指针函数来再进行更细致更深入更对口的初始化.但是对于后者,就没必要那么麻烦了,直接调用usb_disconnect()函数执行一些清扫工作,并且把hub的change_bits清掉.然后再确定一下端口上确实没有连接什么设备,那就可以返回了.  
    
    
    
    
    
    
    
    
    
      if (!(portstatus & USB_PORT_STAT_CONNECTION) ||  
              (portchange & USB_PORT_STAT_C_CONNECTION))  
          clear_bit(port1, hub->removed_bits);  
    
      if (portchange & (USB_PORT_STAT_C_CONNECTION |  
                  USB_PORT_STAT_C_ENABLE)) {  
          status =  hub_port_debounce_be_stable (hub, port1);  
          if (status < 0) {  
              if (status != -ENODEV &&  printk_ratelimit( ))  
                  dev_err(hub_dev, "connect-debounce failed, "  
                          "port %d disabled\n", port1);  
              portstatus &= ~USB_PORT_STAT_CONNECTION;  
          } else {  
              portstatus = status;  
          }  
      }   
    
    
    
  //对于连接从有到无的变化,刚才我们可是清掉了hub的change_bits,所以这里再次判断portchange&USB_PORT_STAT_C_CONNECTION的意思就是对于连接从无到有的情况,我们还必须做一件事情,那就是判断反弹.在hub中也是需要的.spec规定,只有持续了100ms的插入才算真正的插入,或者说才算稳定的插入.hub_port_debounce就是干这件事情的,这个函数会让你至少等待100ms,如果设备依然在,那么说明稳定了,这种情况下函数返回值就是端口的状态.  
    
  printk_ratelimit是printk的变种,printk_ratelimit的用途就是当你某条消息可能会重复的被多次打印的,甚至极限情况下,打印个成千上万条,直接导致日志文件溢出,把别的信息都冲掉了,所以这样是不好的,于是进化出来一个printk_ratelimit(),它会控制打印消息的频率,如果短期内连续出现打印消息,那么它把消息抛弃,这种情况下这个函数返回0,所以,只有返回非0值的情况下才会真正打印.  
    
    
    
    
    
    
    
    
       for (i = 0; i < SET_CONFIG_TRIES; i++)  {  
    
          /* reallocate for each attempt, since references  
           * to the previous one can escape in various ways  
           */  
           udev = usb_alloc_dev(hdev, hdev->bus, port1);   
          if (!udev) {  
              dev_err (hub_dev,  
                  "couldn't allocate port %d usb_device\n",  
                  port1);  
              goto done;  
          }  
    
          usb_set_device_state(udev, USB_STATE_POWERED);  
          udev->bus_mA = hub->mA_per_port;  
          udev->level = hdev->level + 1;  
          udev->wusb = hub_is_wusb(hub);  
    
          /* Only USB 3.0 devices are connected to SuperSpeed hubs. */  
          if (hub_is_superspeed(hub->hdev))  
              udev->speed = USB_SPEED_SUPER;  
          else  
              udev->speed = USB_SPEED_UNKNOWN;  
    
          choose_devnum(udev);  
          if (udev->devnum <= 0) {  
              status = -ENOTCONN; /* Don't retry */  
              goto loop;  
          }}  
    
  (13)八大重量级函数闪亮登场(一)    
 
 
 static void  hub_port_connect_change (struct usb_hub *hub, int port1,u16 portstatus, u16 portchange) 
 { 
   for (i = 0; i < SET_CONFIG_TRIES; i++) { 
 第一个函数,usb_alloc_dev(),为一个struct usb_device结构体指针,申请内存 
 第二个函数,usb_set_device_state(),这个函数用来设置设备的状态,struct usb_device结构体中,有一个成员,enum usb_device_state state,这一刻,会把这个设备的状态设置为USB_STATE_POWERED,即上电状态. 
 第三个函数,choose_address(),为设备选择一个地址. 
 第四个函数,hub_port_init()端口初始化,主要就是前面说的获取设备的描述符. 
 第五个函数,usb_get_status(),这个函数是专门为hub准备的,不是为当前的这个hub,而是说当前hub的这个端口上连接的如果又是hub,那么和连接普通设备当然不一样. 
 第六个函数,check_highspeed(),不同速度的设备当然待遇不一样. 
 第七个函数,usb_new_device().寻找驱动程序,调用驱动程序的probe,跟踪这个函数就能一直跟踪到设备驱动程序的probe()函数的调用. 
 第八个函数,hub_power_remaining(),别忘了,电源管理将是hub驱动永恒的话题. 
   
   }  
 }   
 void  usb_set_device_state (struct usb_device *udev, 
         enum usb_device_state new_state) 
 { 
     unsigned long flags; 
     int wakeup = -1; 
 
 
     spin_lock_irqsave(&device_state_lock, flags); 
     if (udev->state == USB_STATE_NOTATTACHED) 
         ;   /* do nothing */ 
     else if (new_state != USB_STATE_NOTATTACHED) { 
 
 
         /* root hub wakeup capabilities are managed out-of-band 
          * and may involve silicon errata ... ignore them here. 
          */ 
         if (udev->parent) { 
             if (udev->state == USB_STATE_SUSPENDED 
                     || new_state == USB_STATE_SUSPENDED) 
                 ;   /* No change to wakeup settings */ 
             else if (new_state == USB_STATE_CONFIGURED) 
                 wakeup = udev->actconfig->desc.bmAttributes 
                      & USB_CONFIG_ATT_WAKEUP; 
             else 
                 wakeup = 0; 
         } 
         if (udev->state == USB_STATE_SUSPENDED && 
             new_state != USB_STATE_SUSPENDED) 
             udev->active_duration -= jiffies; 
         else if (new_state == USB_STATE_SUSPENDED && 
                 udev->state != USB_STATE_SUSPENDED) 
             udev->active_duration += jiffies; 
         udev->state = new_state; 
     } else   
          recursively_mark_NOTATTACHED (udev); 
 //原来的状态不是USB_STATE_NOTATTACHED而现在要设置成USB_STATE_NOTATTACHED.这又是一个递归函数.递归的把各设备都设置成NOTATTACHED状态. 
 每一个设备遍历自己的子节点,一个个调用recursively_mark_NOTATTACHED()函数把其state设置为USB_STATE_NOTATTACHED. 
     spin_unlock_irqrestore(&device_state_lock, flags); 
     if (wakeup >= 0) 
          device_set_wakeup_capable (&udev->dev, wakeup); 
 }   
    
   (14)八大重量级函数闪亮登场(二)    
 
 
 static void  hub_port_connect_change (struct usb_hub *hub, int port1,u16 portstatus, u16 portchange) 
 { 
   for (i = 0; i < SET_CONFIG_TRIES; i++) { 
        udev->bus_mA = hub->mA_per_port;                                                               
         udev->level = hdev->level + 1;                                                                 
         udev->wusb = hub_is_wusb(hub);                                                                 
                                                                                                        
         /* Only USB 3.0 devices are connected to SuperSpeed hubs. */                                   
         if (hub_is_superspeed(hub->hdev))                                                              
             udev->speed = USB_SPEED_SUPER;                                                             
         else                                                                                           
             udev->speed = USB_SPEED_UNKNOWN;  
 } 
  choose_devnum (udev); //Choose a device number 
         if (udev->devnum <= 0) { 
             status = -ENOTCONN; /* Don't retry */ 
             goto loop; 
         } 
 } 
  struct  usb_bus 
        struct  usb_devmap  devmap;   /* device address allocation map */ 地址映射表,一条总线就是一颗大树,一个设备就是这颗树的叶子。这个地址映射表记录每一个叶子节点 
 
 
   int  devnum_next ;        /* Next open device number in * round-robin allocation */ 总线初始化的置为1 
 } 
  struct  usb_device 
     int     devnum; 
 } 
  /* USB device number allocation bitmap */ 
 struct  usb_devmap 
     unsigned long devicemap[128 / (8*sizeof(unsigned long))]; // 128/8*4=4btyes = 32bits 最后这个数组devicemap[4] 是128bits 
 }; 
    
    
    
   (15)八大重量级函数闪亮登场(三)   
  
   
 
 
 
   
 static void  hub_port_connect_change (struct usb_hub *hub, int port1,u16 portstatus, u16 portchange) 
 
 { 
   
   
     /* reset (non-USB 3.0 devices) and get descriptor */
         status =  hub_port_init (hub, udev, port1, i);  //基本思想就是做初始化,首先把一个设备复位(reset),然后分配地址,接着获得设备描述符
         if (status < 0)
             goto loop;
   
 
 
 }
 
 
 
 
    
    
  static int  hub_port_init  (struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter)
 {
    retval =  hub_port_reset (hub, port1, udev, delay, false);
   
    if (udev->wusb == 0) {
             for (j = 0; j < SET_ADDRESS_TRIES; ++j) {                              
                 retval =  hub_set_address (udev, devnum);
                 if (retval >= 0)
                     break;
                 msleep(200);
             }
             if (retval < 0) {
                 if (retval != -ENODEV)
                     dev_err(&udev->dev, "device not accepting address %d, error %d\n",
                             devnum, retval);
              
 
    
  }  
    
    
  static int  hub_port_reset (struct usb_hub *hub, int port1,struct usb_device *udev, unsigned int delay, bool warm)
   
 {
   
    /* Reset the port */
     for (i = 0; i < PORT_RESET_TRIES; i++) {
         status =  set_port_feature (hub->hdev, port1, (warm ?
                     USB_PORT_FEAT_BH_PORT_RESET :
                     USB_PORT_FEAT_RESET)); //发送一个设置feature的请求
 
 
   
 
   
 }  
 
 
 
 
 
   
 
 static int  hub_set_address (struct usb_device *udev, int devnum) 
 
 
 {
 
 设好了地址之后,把设备的状态从开始的USB_STATE_DEFAULT变成了USB_STATE_ ADDRESS
 
 
 
 } 
 
 
 
 
 
 void  usb_ep0_reinit (struct usb_device *udev)
 {
     usb_disable_endpoint(udev, 0 + USB_DIR_IN, true);
     usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true);
     usb_enable_endpoint(udev, &udev->ep0, true);
 }
 
 
 
 
 
 
    
    
    
   (16)八大重量级函数闪亮登场(五)
 
   
 
 
 
   
 static void  hub_port_connect_change (struct usb_hub *hub, int port1,u16 portstatus, u16 portchange) 
 
 { 
   
   spin_lock_irq(&device_state_lock);
         if (hdev->state == USB_STATE_NOTATTACHED)
             status = -ENOTCONN;
         else 
              hub->ports[port1 - 1]->child = udev;  //设备正式挂上设备这个大树
         spin_unlock_irq(&device_state_lock);
 
         /* Run it through the hoops (find a driver, etc) */
         if (!status) {
             status =  usb_new_device (udev);
             if (status) {
                 spin_lock_irq(&device_state_lock);
                 hub->ports[port1 - 1]->child = NULL;
                 spin_unlock_irq(&device_state_lock);
             }    
         } 
 
  
 
 
 
 
 
 
 
 
 
 }
 
 
 int usb_new_device(struct usb_device *udev) 
 
 {
 
   err =  usb_enumerate_device (udev);   /* Read descriptors */
     if (err < 0) 
         goto fail;
     dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
             udev->devnum, udev->bus->busnum,
             (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
     /* export the usbdev device-node for libusb */
     udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
             (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
 
     /* Tell the world! */
     announce_device(udev);
 
 
 err =  device_add (&udev->dev);//从作用上来说,这个函数一执行,系统里就真正有了这个设备,/sysfs下面也能看到了,而且将会去遍历注册到USB总线上的所有的驱动程序,如果找到合适的,就去调用该驱动的probe函数,对于U盘来说,最终将调用storage_probe()函数,对于Hub来说,最终将调用hub_probe()函数。
     if (err) {
         dev_err(&udev->dev, "can't device_add, error %d\n", err);
         goto fail;
     } 
 
 
 } 
 
 
 
 
 
 static int  usb_enumerate_device (struct usb_device *udev)
 {
     int err;
 
     if (udev->config == NULL) {
         err =  usb_get_configuration( udev);//获得接口,设备,端口描述符,
         if (err < 0) {
             if (err != -ENODEV)
                 dev_err(&udev->dev, "can't read configurations, error %d\n",
                         err);
             return err;
         }
     }
     if (udev->wusb == 1 && udev->authorized == 0) {
         udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
         udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
         udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);//lsusb 能查看usb的一些设置,字符描述符。不用一直发送请求,获得字符描述符。发送一次请求,保存好。
     }
     else {
         /* read the standard strings and cache them if present */
         udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
         udev->manufacturer = usb_cache_string(udev,
                               udev->descriptor.iManufacturer);
         udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
     }
     err = usb_enumerate_device_otg(udev);
     if (err < 0)
         return err;
 
     usb_detect_interface_quirks(udev);
 
     return 0;
 }
 
 
 
  带着问题去学习思考:
  (1)Hub驱动在设备插入之后会做一些什么事情,会如何为设备服务,并最终把控制权交给设备驱动。而hub_thread()/hub_events()将永远这么循环下去。
 
 不过我的故事可没有结束,最起码还有一个重要的函数没有讲,那就是hub_irq。之前我们这里讲的内容都是基于一个事实就是我们主动去读了Hub端口的状态,而以后正常工作的Hub驱动是不会莫名其妙就去读Hub端口状态,只有发生了中断才会去读。而这个中断的服务函数就是hub_irq(),也就是说,凡是真正的有端口变化事件发生,hub_irq就会被调用,而hub_irq()最终会调用kick_khubd(),触发Hub的event_list,于是再次调用hub_events()函数
 
 
 
 
 
 
  (17)所谓的热插拔
 
 
  hub_probe--->hub_configure
 
 
 
  usb_fill_int_urb (hub->urb, hdev, pipe, *hub->buffer, maxp,  hub_irq ,
         hub, endpoint->bInterval);
 
 
 
 
 曾经在hub_configure中讲过中断传输,当时调用了usb_fill_int_urb()函数,并且把hub_irq作为一个参数传递了进去,最终把urb->complete赋值为hub_irq。
 然后,主机控制器会定期询问Hub,每当Hub端口上有一个设备插入或者拔除时,它就会向主机控制器打小报告。具体来说,
 从硬件的角度看,就是Hub会向主机控制器返回一些信息,或者说Data,这个Data被称作"Hub and Port Status Change Bitmap",
 从软件角度来看,主机控制器的驱动程序接下来会在处理好这个过程的urb之后,调用该urb的complete函数,对于Hub来说,这个函数就是hub_irq() 
 
 
 
 
 static void  hub_irq (struct urb *urb)
 {
     struct usb_hub *hub = urb->context;
     int status = urb->status;
     unsigned i;
     unsigned long bits;
 
     switch (status) {
     case -ENOENT:       /* synchronous unlink */
     case -ECONNRESET:   /* async unlink */
     case -ESHUTDOWN:    /* hardware going away */
         return;
 
     default:        /* presumably an error */
         /* Cause a hub reset after 10 consecutive errors */
         dev_dbg (hub->intfdev, "transfer --> %d\n", status);
         if ((++hub->nerrors < 10) || hub->error)
             goto resubmit;
         hub->error = status;
         /* FALL THROUGH */
 
     /* let khubd handle things */
     case 0:         /* we got data:  port status changed */
         bits = 0; 
         for (i = 0; i < urb->actual_length; ++i) 
             bits |= ((unsigned long) ((*hub->buffer)[i]))
                     << (i*8);
         hub->event_bits[0] = bits;
         break;
     }    
 
     hub->nerrors = 0;
 
     /* Something happened, let khubd figure it out */
      kick_khubd(hub);    ---->hub_events
 
 resubmit:
     if (hub->quiescing)
         return;
 
     if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
             && status != -ENODEV && status != -EPERM)
         dev_err (hub->intfdev, "resubmit --> %d\n", status);
 }
 
 
 
(18)看代码的理由
drivers/usb/core/driver.c int  usb_suspend (struct device *dev, pm_message_t msg) 
{
  usb_suspend_both (udev, msg);
 
static int  usb_suspend_both (struct usb_device *udev, pm_message_t msg)
{
      if (udev->actconfig) {
        n = udev->actconfig->desc.bNumInterfaces;
        for (i = n - 1; i >= 0; --i) {
            intf = udev->actconfig->interface[i];
            status =  usb_suspend_interface (udev, intf, msg); //各个接口进行挂起
            /* Ignore errors during system sleep transitions */
            if (!PMSG_IS_AUTO(msg))
                status = 0; 
            if (status != 0)
                break;
        }    
    }    
    if (status == 0) { 
        status =  usb_suspend_device (udev, msg); //进行总的设备挂起
}static int  usb_suspend_interface (struct usb_device *udev,
        struct usb_interface *intf, pm_message_t msg) 
{
    struct usb_driver   *driver;
    int         status = 0; 
    if (udev->state == USB_STATE_NOTATTACHED ||
            intf->condition == USB_INTERFACE_UNBOUND)
        goto done;
    driver = to_usb_driver(intf->dev.driver);
    /* at this time we know the driver supports suspend */
     status = driver->suspend(intf, msg); // 调用具体的interface所绑定的那个驱动程序的suspend函数。比如,对于Hub来说,这里调用的就是hub_suspend()函数,hub_suspend/hub_resume
    if (status && !PMSG_IS_AUTO(msg))
        dev_err(&intf->dev, "suspend error %d\n", status);
 done:
    dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status);
    return status;
}
 
(19)电源管理四大消息  
include/linux/pm.h 
#define PM_EVENT_INVALID    (-1)
#define PM_EVENT_ON     0x0000
#define PM_EVENT_FREEZE     0x0001
#define PM_EVENT_SUSPEND    0x0002
#define PM_EVENT_HIBERNATE  0x0004
#define PM_EVENT_QUIESCE    0x0008
#define PM_EVENT_RESUME     0x0010
#define PM_EVENT_THAW       0x0020
#define PM_EVENT_RESTORE    0x0040
#define PM_EVENT_RECOVER    0x0080
#define PM_EVENT_USER       0x0100
#define PM_EVENT_REMOTE     0x0200
#define PM_EVENT_AUTO       0x0400
 
 
(20) 将suspend分析到底
 USB设备驱动程序往往是针对interface的,而不是针对device的,一个interface对应一个driver,有一些行为是针对整个device的,比如电源管理中的挂起,可能整个设备需要统一的行为,而不是说每个interface可以单独行动,想干嘛就干嘛。
一个设备多个interface,那么它们就是一个整体,一个整体对外就会有整体的表现。interface driver就是专门处理各个interface的个性的,而device driver么,就用来对付整体。而这里我们看到的两个函数usb_suspend_device和usb_suspend_interface就是这种情况的体现。而usb_suspend_device这段代码的意图更是相当明显,如果有device driver,那就调用它的suspend函数,如果没有,就调用一个通用的函数,usb_port_suspend.我们来看这个通用的suspend函数
 
 
 int  usb_port_suspend (struct usb_device *udev, pm_message_t msg) //suspend a usb device's upstream port 
挂起设备所连接的那个端口{
Suspend还包括两种,一种是全局的,叫做global suspend,另一种叫做选择性的,即可以选择单个的端口进行挂起,这叫selective suspend.hub_port_suspend所执行的当然就是所谓的选择性挂起了,因为它针对的就是某个端口,而不是整个hub.

Remote Wakeup,从硬件角度来说,usb设备定义了一个叫做Remote Wakeup的特性,所谓Remote Wakeup指的是设备可以发送一个信号,把自己唤醒,当然实际上唤醒的是总线,或者说最后的反应是唤醒主机.比如usb键盘,你半天不碰计算机可能大家都睡了,可是突然间你按一下某个键,可能就把大家都给唤醒了,因为你实际上是发送了一个硬件信号.再比如hub,可能一开始是睡眠的,但如果hub port上有设备插入或者拔出,那么基本上就会唤醒hub.
hub driver,hub_suspend被赋值给了hub_driver中的suspend成员,而hub driver是一个interface driver,实际上hub_suspend将会在当初那个usb_suspend_interface中被调用,status = driver->suspend(intf, msg);

static int  hub_suspend (struct usb_interface *intf, pm_message_t msg)
 {  
 /* stop khubd and related activity */
     hub_quiesce (hub, HUB_SUSPEND);
    return 0;
}
static void  hub_quiesce (struct usb_hub *hub, enum hub_quiescing_type type)
{
    struct usb_device *hdev = hub->hdev;
    int i;

    cancel_delayed_work_sync(&hub->init_work);

    /* khubd and related activity won't re-trigger */
    hub->quiescing = 1;

    if (type != HUB_SUSPEND) {
        /* Disconnect all the children */
        for (i = 0; i < hdev->maxchild; ++i) {
            if (hub->ports[i]->child)
                usb_disconnect(&hub->ports[i]->child);
        }
    }

    /* Stop khubd and related activity */
    usb_kill_urb(hub->urb);
    if (hub->has_indicators)
        cancel_delayed_work_sync(&hub->leds);
    if (hub->tt.hub)
        flush_work(&hub->tt.clear_work);
}
这个函数是唯一一处设置hub->quiescing为1的地方.它和另一个函数针锋相对,即hub_activate(),hub_activate()中设置hub->quiescing为0,而设置hub->activating为1.这两个函数做的事情那几乎是完全相反.那边人家调用usb_submit_urb()提交一个urb,这边就给人拆台,调用usb_kill_urb()来撤掉该urb,那边人家调用schedule_delayed_work建立一个延时工作的函数,这边就调用cancel_delayed_work给人家拆了,flush_scheduled_work()通常在cancel_delayed_work后面被调用,这个函数会使等待队列中所有的任务都被执行.

针对Root Hub的,因为通常Host Controller Driver也会提供自己的suspend函数,所以如果挂起操作已经上升到了Root Hub这一层,就应该调用hcd的suspend函数.即hcd_bus_suspend.如果挂起失败了,那么就别挂起,还是调用hub_activate()重新激活 。
 
 
   
 
    
    
    
    
    
    
    
    
    
    
    
    

猜你喜欢

转载自blog.csdn.net/sinat_37817094/article/details/80603599
今日推荐