Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1833420
  • 博文数量: 195
  • 博客积分: 4227
  • 博客等级: 上校
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-04 10:39
文章分类

全部博文(195)

文章存档

2013年(1)

2012年(26)

2011年(168)

分类: LINUX

2011-03-17 18:50:49

初始化

谨以此文纪念过往的岁月

一.   前言

在上文中讲述了usb 子系统的初始化,在该文中来看usb hub的驱动。不过在这里需要说明一点,host controller usb hub是不同的东西。hc是指usb的控制器,而hub是一种usb集线器,其本质还是一种usb设备,只不过这种usb设备有点特殊而已。一般的MCU都会自带一个usb hub,以s3c6410为例,其带有一个hub,该hub有两个下行口,就是俗话所说的两个usb接口。你可以在把两个usb设备插到该hub下,再多就不行了。而该hub就是传说中的root hub,以该root hub为例,如果在该hub下还挂载一个hub,那插入的hub不是root hub而是一个hub设备,对于linux而言其会为每一个hub创建一条总线,用于该hub下的设备与驱动绑定。对于一般系统而言,一般一个hc对应一个root hub

二.hub设备

关于root hub的设备注册还记否,在上文OHCI中已经涉及到了,不过特意点明而已,就是在register_root_hub中。

三.hub驱动

usb hub其实也是一种usb设备,其驱动不脱离usb驱动的模板。在usb_init中会调用usb_hub_init函数来注册hub的驱动。usb_register(&hub_driver),注册一个hub驱动。

那下面来看该hub_driver的具体每一个函数。

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,

         .ioctl =      hub_ioctl,

         .id_table =        hub_id_table,

         .supports_autosuspend =        1,

};

2.1 hub_probe

hub探测函数,在驱动注册或设备注册时会调用。

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)

{

         struct usb_host_interface *desc;

         struct usb_endpoint_descriptor *endpoint;

         struct usb_device *hdev;

         struct usb_hub *hub;

 

         desc = intf->cur_altsetting;

         hdev = interface_to_usbdev(intf);

        

if (hdev->level == MAX_TOPO_LEVEL) { --只只支持六层嵌套,就是所谓的hubhub,最多接六层。

                   return -E2BIG;

         }

         if ((desc->desc.bInterfaceSubClass != 0) && (desc->desc.bInterfaceSubClass != 1)) {--一些湖北的subclass = 1。根据usbspec这是不允许的不过这还是可以工作的的,这世界总有些特殊的嘛,即是在协议界。

descriptor_error:

                   return -EIO;

         }

         if (desc->desc.bNumEndpoints != 1)  --记住hub的端点只能有一个!!

                   goto descriptor_error;

        

endpoint = &desc->endpoint[0].desc;

         if (!usb_endpoint_is_int_in(endpoint))   --而且这个端点还必须是中断in类型。

                   goto descriptor_error;

         hub = kzalloc(sizeof(*hub), GFP_KERNEL); --在上述情况都满足的情况下才能说有一个hub存在。

         if (!hub) {

                            return -ENOMEM;

         }

 

         kref_init(&hub->kref);           --hub参数的初始化。

         INIT_LIST_HEAD(&hub->event_list); --事件链表,每一次的插拔都会引起hub的中断。会将该链表添加到hub_event_list链表中,同时唤醒khubd_waithub守护程序。

         hub->intfdev = &intf->dev;  --接口设备

         hub->hdev = hdev;    --hub的实体。

         (&hub->leds, led_work);

         INIT_DELAYED_WORK(&hub->init_work, NULL);

         usb_get_intf(intf);

 

         usb_set_intfdata (intf, hub);

         intf->needs_remote_wakeup = 1;

 

         if (hdev->speed == USB_SPEED_HIGH)

                   highspeed_hubs++;

 

         if ((hub, endpoint) >= 0)

                   return 0;

 

         hub_disconnect (intf);

         return -ENODEV;

}

的实例后,就需要对hub的配置。你会发现这是一个巨大的函数,不要畏惧其的巨大,还是一如既往的去分析他吧!

static int hub_configure(struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint)

{

         struct usb_device *hdev = hub->hdev;

         struct device *hub_dev = hub->intfdev;

         u16 hubstatus, hubchange;

         u16 wHubCharacteristics;

         unsigned int pipe;

         int maxp, ret;

         char *message;

 

         hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,--hub开辟缓冲区

&hub->buffer_dma);

         hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);

         mutex_init(&hub->status_mutex);

 

         hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);

        

 

         /* Request the entire hub descriptor.

          * hub->descriptor can handle USB_MAXCHILDREN ports,

          * but the hub can/will return fewer bytes here.

          */

         ret = get_hub_descriptor(hdev, hub->descriptor,sizeof(*hub->descriptor));--获取hub的描述符,记住hub从本质讲还是一种usb设备,只不过是一种特殊的usb设备而已。其设备的描述符与普通的usb设备是不同的。关于hub的描述符的具体说明,可以去看usb2.0协议。

         if (ret < 0) {

                   message = "can't read hub descriptor";

                   goto fail;

         } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {  --hub的下行端口最多32个。

                   message = "hub has too many ports!";

                   ret = -ENODEV;

                   goto fail;

         }

         hdev->maxchild = hub->descriptor->bNbrPorts;

         wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);--虽说对于usbhub的描述符可以看spec,但是这里还是不得不来看一下这个描述hub特性的信息。

         if ( & HUB_CHAR_COMPOUND) { --判断hub是否为混合设备的一部分。如果是的话,就需要存储其每一个下行端口是否可以被移除。

                   int    i;

                   char portstr [USB_MAXCHILDREN + 1];

 

                   for (i = 0; i < hdev->maxchild; i++)

                            portstr[i] = hub->descriptor->DeviceRemovable

                                         [((i + 1) / 8)] & (1 << ((i + 1) % 8))

                                     ? 'F' : 'R';

                  portstr[hdev->maxchild] = 0;

                   dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);

         } else

                   dev_dbg(hub_dev, "standalone hub\n");

 

         switch (wHubCharacteristics & HUB_CHAR_LPSM) { --判断hub电源管理的类型

                   case 0x00:                       --统一的电源管理

                            break;

                   case 0x01:                       --每一个端口的单独的电源管理

                            break;

                   case 0x02:                       --保留

                   case 0x03:

                            break;

         }

 

         switch (wHubCharacteristics & HUB_CHAR_OCPM) {  --过流保护类型

                   case 0x00:                      --全局的过流保护

break;

                   case 0x08:                      --每一个端口的过流保护

break;

                   case 0x10:                  --没有过流保护

                   case 0x18:

                             break;

         }

         spin_lock_init (&hub->tt.lock);

         INIT_LIST_HEAD (&hub->tt.clear_list);

         INIT_WORK (&hub->tt.kevent, hub_tt_kevent);

         switch (hdev->descriptor.bDeviceProtocol) { --hub对于高速到低速的支持

                   case 0:

                            break;

                   case 1:

                            dev_dbg(hub_dev, "Single TT\n");

                            hub->tt.hub = hdev;              --对于所有的端口而言,公用一个TT

                            break;

                   case 2:

                            ret = usb_set_interface(hdev, 0, 1);

                            if (ret == 0) {

                                     dev_dbg(hub_dev, "TT per port\n");  --每一端口均有一个TT转换

                                     hub->tt.multi = 1;

                            } else

                                     dev_err(hub_dev, "Using single TT (err %d)\n",ret);

                            hub->tt.hub = hdev;

                            break;

                   default:

                            break;

         }

 

         /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */

         switch (wHubCharacteristics & HUB_CHAR_TTTT) {   --定义TT转化时间

                   case HUB_TTTT_8_BITS:

                            if (hdev->descriptor.bDeviceProtocol != 0) {

                                     hub->tt.think_time = 666;

                            }

                            break;

                   case HUB_TTTT_16_BITS:

                            hub->tt.think_time = 666 * 2;

                            break;

                   case HUB_TTTT_24_BITS:

                            hub->tt.think_time = 666 * 3;

                            break;

                   case HUB_TTTT_32_BITS:

                            hub->tt.think_time = 666 * 4;

                            break;

         }

 

         /* probe() zeroes hub->indicator[] */

         if (wHubCharacteristics & HUB_CHAR_PORTIND) {  --是否支持指示灯

                   hub->has_indicators = 1;          }

   --在此说明描述符中的一个成员bPwrOn2PwrGood,该成员*2说明hub上电到电源稳定的时间.

         ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);--获得hub的状态

         if (ret < 2) {

                   message = "can't get hub status";

                   goto fail;

         }

         le16_to_cpus(&hubstatus);

         if (hdev == hdev->bus->root_hub) {  --如果该hubroot hub

                   if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)

                            hub->mA_per_port = 500;      

                   else {

                            hub->mA_per_port = hdev->bus_mA;

                            hub->limited_power = 1;

                   }

         } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {

                   hub->limited_power = 1;

                   if (hdev->maxchild > 0) {

                            int remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;

                            if (remaining < hdev->maxchild * 100)

                            hub->mA_per_port = 100;                 /* 7.2.1.1 */

                   }

         } else {      /* Self-powered external hub */

                   hub->mA_per_port = 500;

         }

    --上面是一些hub的电源管理。

         ret = hub_hub_status(hub, &hubstatus, &hubchange);

    --hub传输创建urb,记住hub永远是一个usb设备,其传输也是需要通过urb的。

         pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);

         maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));

 

         if (maxp > sizeof(*hub->buffer))

                   maxp = sizeof(*hub->buffer);

 

         hub->urb = usb_alloc_urb(0, GFP_KERNEL);

         if (!hub->urb) {

                   message = "couldn't allocate interrupt urb";

                   ret = -ENOMEM;

                   goto fail;

         }

 

         usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,

                   hub, endpoint->bInterval);

         hub->urb->transfer_dma = hub->buffer_dma;

         hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    --上面是一个标准中断urb的申请

         /* maybe cycle the hub leds */

         if (hub->has_indicators && blinkenlights)

                   hub->indicator [0] = INDICATOR_CYCLE;

 

         hub_activate(hub, HUB_INIT);

         return 0;

}

开始工作,那我们来看,这个具体是怎么干的。对于下面的函数最好是参考usb协议,所谓不识协议,看懂代码也枉然啊!

static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)

{

         struct usb_device *hdev = hub->hdev;

         int port1;

         int status;

         bool need_debounce_delay = false;

         unsigned delay;

 

         /* Continue a partial initialization */

         if (type == HUB_INIT2)

                   goto init2;

         if (type == HUB_INIT3)

                   goto init3;

         if (type != HUB_RESUME) {   --判断是否为恢复,因为hub挂起时,电源仍然是打开的。

                   if (type == HUB_INIT) {

                            delay = hub_power_on(hub, false); --打开电源不延时

                           

                            schedule_delayed_work(&hub->init_work,msecs_to_jiffies(delay));--其所在的工作队列线程被唤醒后,延时一段时间运行。

                            to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;

                            return;              

                   } else {

                            hub_power_on(hub, true);   --打开电源,同时延时

                   }

         }

 init2:

         for (port1 = 1; port1 <= hdev->maxchild; ++port1) {

                   struct usb_device *udev = hdev->children[port1-1];

                   u16 portstatus, portchange;

 

                   portstatus = portchange = 0;

                   status = hub_port_status(hub, port1, &portstatus, &portchange); --获取hub下行端口状态以及端口改变状态。关于其各个bit位的含义,在usb 2.0 spec中的11.24.2.7说明。在除了hub恢复挂起状态的任何情况下,都需要将所有的端口禁止。如果端口没有设备同样需要禁止。

                   if ((portstatus & USB_PORT_STAT_ENABLE) && (

type != HUB_RESUME ||

                                     !(portstatus & USB_PORT_STAT_CONNECTION) ||

                                     !udev ||

                                     udev->state == USB_STATE_NOTATTACHED)) {

                            clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);

                            portstatus &= ~USB_PORT_STAT_ENABLE;

                   }

                   if (portchange & USB_PORT_STAT_C_CONNECTION) { --如果有一个设备连接

                            need_debounce_delay = true;      --去抖

                            clear_port_feature(hub->hdev, port1,); --清除端口连接标志,记住这个其实将HcRhPortStatus[port-1]中第USB_PORT_FEAT_C_CONNECTION位定义为1,下面的是同样的。

                   }

                   if (portchange & USB_PORT_STAT_C_ENABLE) {

                            need_debounce_delay = true;

                            clear_port_feature(hub->hdev, port1,USB_PORT_FEAT_C_ENABLE);

                   }

 

                   if (!udev || udev->state == USB_STATE_NOTATTACHED) {

                            if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) –通知khubd有设备移除或有新的设备。

                                     set_bit(port1, hub->change_bits);

                   } else if (portstatus & USB_PORT_STAT_ENABLE) {

                            if (portchange)

                                     set_bit(port1, hub->change_bits);

                   } else if (udev->persist_enabled) {

#ifdef CONFIG_PM

                            udev->reset_resume = 1;

#endif

                            set_bit(port1, hub->change_bits);

 

                   } else {

                            usb_set_device_state(udev, USB_STATE_NOTATTACHED);

                            set_bit(port1, hub->change_bits);

                   }

         }

         if (need_debounce_delay) {

                   delay = HUB_DEBOUNCE_STABLE;

                   if (type == HUB_INIT2) {

                            );

                            schedule_delayed_work(&hub->init_work,msecs_to_jiffies(delay));

                            return;               /* Continues at init3: below */

                   } else {

                            msleep(delay);

                   }

         }

 init3:

         hub->quiescing = 0;

         status = usb_submit_urb(hub->urb, GFP_NOIO); --提交urb

         if (hub->has_indicators && blinkenlights)

                   schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);

 

         kick_khubd(hub); --扫描所有的端口

}

 

static void kick_khubd(struct usb_hub *hub)

{

         unsigned long  flags;

         to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; --在处理该函数时禁止挂起

         spin_lock_irqsave(&hub_event_lock, flags);

         if (!hub->disconnected && list_empty(&hub->event_list)) {

                   list_add_tail(&hub->event_list, &hub_event_list); 

                   wake_up(&khubd_wait);

         }

         spin_unlock_irqrestore(&hub_event_lock, flags);

}

也许看上面的代码很困惑,怎么会那么乱呢?其实这个函数会被分为三步来执行的。那我们来一步步来看吧。

话说在hub_probe时会调用hub_active是参数为HUB_INIT,此时会执行下面代码。

delay = hub_power_on(hub, false);

PREPARE_DELAYED_WORK(&hub->init_work, );

schedule_delayed_work(&hub->init_work,msecs_to_jiffies())

其实上述代码是等待一段时间后再执行,即会在delayms后再执行,在该段时间内等待电源稳定。在电源稳定后调用hub_init_func2,在该函数中hub_activate(hub, HUB_INIT2);该函数会直接跳到init2之处的函数来执行代码,即查看各个端口的状态以及端口改变状况。在之后会执行

PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3);

                            schedule_delayed_work(&hub->init_work,msecs_to_jiffies(delay));

在等待一段时间后,继续执行执行hub_init_func3,在该函数中会调用hub_activate(hub, HUB_INIT3);会直接跳到init3中。呵呵,下面就能好懂很多了吧。关于android的电源管理以后再看。

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-font-kerning:1.0pt;}

我们再看来huburb中断处理hub_irq,在一切都很正常的情况下,将会调用kick_khubd函数。在端口发生状态发生改变时,会调用该中断。

static void hub_irq(struct urb *urb)

{

         struct usb_hub *hub = urb->context;

         int status = urb->status;

         int i;

         unsigned long bits;

 

         switch (status) {

         case 0:                        --正常情况下会会保存port改变信息

                   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);

 

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);

}

kick_khubd函数如下

static void (struct usb_hub *hub)

{

         unsigned long  flags;

         to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;  --禁止自动挂起,知道khubd执行

         spin_lock_irqsave(&hub_event_lock, flags);

         if (!hub->disconnected && list_empty(&hub->event_list)) {

                   list_add_tail(&hub->event_list, &hub_event_list);

                   wake_up(&khubd_wait);   --唤醒守护程序。

         }

         spin_unlock_irqrestore(&hub_event_lock, flags);

}

总结

   Hub是一种usb设备,只不过这种设备有特殊而已。关于hub具体的处理下文再看。

阅读(10414) | 评论(0) | 转发(0) |
0

上一篇:linux usb hub驱动

下一篇:Linux usb hub 处理

给主人留下些什么吧!~~