首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux那些事儿之我是Hub(17)八大重量级函数闪亮登场(一)

Linux那些事儿之我是Hub(17)八大重量级函数闪亮登场(一)

还有人记得1996年那部史诗般的电视剧<<英雄无悔>>?那年我初二.这部戏让我认识了濮存昕,也是这部戏确立了濮存昕少妇杀手的地位,后来大肆那年濮存昕去过复旦,宣传艾滋病方面的知识,尽管那时候我正在求职的路上遭遇种种挫折,但还是抽出了时间去五教看了听了他的讲座,完了之后他还即兴了一段朗诵.我觉得他身上那种健康的形象是我喜欢的,因为这种积极向上的东西我太缺了.
<<英雄无悔>>里面濮存昕扮演的公安局长高天有一句最经典的话:世上注定会有撑船人和坐船人,而他应该是那个执著的撑船仔.其实hubusb世界里扮演的又何尝不是这种角色呢?我们来看这个循环,这是Hub驱动中最核心的代码,然而这段代码却自始至终是在为别的设备服务.
在这个循环中,主要涉及这么八个重量级函数,先点明它们的角色分工.
第一个函数,usb_alloc_dev(),为一个struct usb_device结构体指针,申请内存,这个结构体指针可不是为hub准备的,它正是为了hub这个端口所接的设备而申请的,别忘了我们此时此刻的上下文,之所以进入到了这个循环,是因为我们的Hub检测到某个端口有设备连接了进来,所以我们作为Hub驱动当然就义不容辞的要为该设备做点什么.
第二个函数,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驱动永恒的话题.



Ok,下面就让我们来认真的逐个看一看这八大函数.
usb_alloc_dev(),drivers/usb/core/usb.c:
    226 /**
    227  * usb_alloc_dev - usb device constructor (usbcore-internal)
    228  * @parent: hub to which device is connected; null to allocate a root hub
    229  * @bus: bus used to access the device
    230  * @port1: one-based index of port; ignored for root hubs
    231  * Context: !in_interrupt()
    232  *
    233  * Only hub drivers (including virtual root hub drivers for host
    234  * controllers) should ever call this.
    235  *
    236  * This call may not be used in a non-sleeping context.
    237  */
    238 struct usb_device *
    239 usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
    240 {
    241         struct usb_device *dev;
    242
    243         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    244         if (!dev)
    245                 return NULL;
    246
    247         if (!usb_get_hcd(bus_to_hcd(bus))) {
    248                 kfree(dev);
    249                 return NULL;
    250         }
    251
    252         device_initialize(&dev->dev);
    253         dev->dev.bus = &usb_bus_type;
    254         dev->dev.type = &usb_device_type;
    255         dev->dev.dma_mask = bus->controller->dma_mask;
    256         dev->state = USB_STATE_ATTACHED;
    257
    258         INIT_LIST_HEAD(&dev->ep0.urb_list);
    259         dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
    260         dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
    261         /* ep0 maxpacket comes later, from device descriptor */
    262         dev->ep_in[0] = dev->ep_out[0] = &dev->ep0;
    263
    264         /* Save readable and stable topology id, distinguishing devices
265          * by location for diagnostics, tools, driver model, etc.  The
    266          * string is a path along hub ports, from the root.  Each device's
    267          * dev->devpath will be stable until USB is re-cabled, and hubs
    268          * are often labeled with these port numbers.  The bus_id isn't
    269          * as stable:  bus->busnum changes easily from modprobe order,
    270          * cardbus or pci hotplugging, and so on.
    271          */
    272         if (unlikely(!parent)) {
    273                 dev->devpath[0] = '0';
    274
    275                 dev->dev.parent = bus->controller;
    276                 sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
    277         } else {
    278                 /* match any labeling on the hubs; it's one-based */
    279                 if (parent->devpath[0] == '0')
    280                         snprintf(dev->devpath, sizeof dev->devpath,
    281                                 "%d", port1);
    282                 else
    283                         snprintf(dev->devpath, sizeof dev->devpath,
    284                                 "%s.%d", parent->devpath, port1);
    285
    286                 dev->dev.parent = &parent->dev;
    287                 sprintf(&dev->dev.bus_id[0], "%d-%s",
    288                         bus->busnum, dev->devpath);
    289
    290                 /* hub driver sets up TT records */
    291         }
    292
    293         dev->portnum = port1;
    294         dev->bus = bus;
    295         dev->parent = parent;
    296         INIT_LIST_HEAD(&dev->filelist);
    297
    298 #ifdef  CONFIG_PM
    299         mutex_init(&dev->pm_mutex);
    300         INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
    301         dev->autosuspend_delay = usb_autosuspend_delay * HZ;
    302 #endif
    303         return dev;
304 }
首先是申请内存,申请一个struct usb_device结构体指针的内存,这个指针就是dev,bus就是总线,这里传递进来的两个实参我们看到,一个是hdev,一个是hdve->bus,hdev自然很明确,就是hub所对应的那个struct usb_device指针,它的bus当然更加明确,一个主机控制器就对应一条总线,那么不管我们在哪里说,都是那条总线.busstruct usb_bus结构体指针,bus_to_hcd()得到的就是该总线对应的主机控制器,江湖上的人都知道,主机控制器就是代表一条总线.主机控制器由一个结构体struct usb_hcd表示,struct usb_hcd定义于drivers/usb/core/hcd.c,它有一个成员,struct usb_bus self.那么这里bus_to_hcd()就是得到该bus所属于的那个struct usb_hcd所对应的指针,usb_get_hcd()是增加引用计数,具体怎么实现咱们在设备模型里面有过交待.这里很显然,因为这个主机控制器的总线上多了一个设备,当然得为主机控制器增加引用计数,只要有设备在,主机控制器的数据结构就得在,否则系统肯定挂了.这里如果usb_get_hcd()失败了那就没啥好说的了,释放内存吧,哪凉快去哪待着.
然后device_initialize(&dev->dev),初始化设备,第一个devstruct usb_device结构体指针,而第二个devstruct device结构体,这是设备模型里面一个最最基本的结构体,使用它必然要先初始化,device_initialize就是内核提供给咱们的初始化函数.
接下来,关于struct device结构体,我们迫不得已必须讲两句了,熟悉2.6设备模型的兄弟们一定知道struct device.我们其实已经见过很多次了,usb-storage的故事里见过,hub的故事里此前也见过,只是一直没有把它的定义贴出来,因为那时候总觉得没有必要,但现在,关于这个结构体的成员出镜率越来越高,我不得不把它的定义贴出来.正如<<无间道>>里所说的那样,从来只有事情改变人,人不可能改变事情.这个结构体定义于include/linux/device.h:
    410 struct device {
    411         struct klist            klist_children;
    412         struct klist_node       knode_parent;           /* node in sibling list */
    413         struct klist_node       knode_driver;
    414         struct klist_node       knode_bus;
    415         struct device           *parent;
    416
    417         struct kobject kobj;
    418         char    bus_id[BUS_ID_SIZE];    /* position on parent bus */
    419         struct device_type      *type;
    420         unsigned                is_registered:1;
    421         unsigned                uevent_suppress:1;
    422         struct device_attribute uevent_attr;
    423         struct device_attribute *devt_attr;
    424
    425         struct semaphore        sem;    /* semaphore to synchronize calls to
    426                                          * its driver.
    427                                          */
    428
    429         struct bus_type * bus;          /* type of bus device is on */
    430         struct device_driver *driver;   /* which driver has allocated this
    431                                            device */
    432         void            *driver_data;   /* data private to the driver */
    433         void            *platform_data; /* Platform specific data, device
    434                                            core doesn't touch it */
    435         struct dev_pm_info      power;
    436
    437 #ifdef CONFIG_NUMA
    438         int             numa_node;      /* NUMA node this device is close to */
    439 #endif
    440         u64             *dma_mask;      /* dma mask (if dma'able device) */
    441         u64             coherent_dma_mask;/* Like dma_mask, but for
    442                                              alloc_coherent mappings as
    443                                              not all hardware supports
    444                                              64 bit addresses for consistent
    445                                              allocations such descriptors. */
    446
    447         struct list_head        dma_pools;      /* dma pools (if dma'ble) */
    448
    449         struct dma_coherent_mem *dma_mem; /* internal for coherent mem
    450                                              override */
    451         /* arch specific additions */
    452         struct dev_archdata     archdata;
    453
    454         spinlock_t              devres_lock;
    455         struct list_head        devres_head;
    456
    457         /* class_device migration path */
    458         struct list_head        node;
    459         struct class            *class;
    460         dev_t                   devt;           /* dev_t, creates the sysfs "dev" */
    461         struct attribute_group  **groups;       /* optional groups */
    462
    463         void    (*release)(struct device * dev);
464 };
又是一个暴长的结构体,所以说其实当初我不贴出来也是为了你好.
对着这个结构体来解释代码,dev.bus就是struct bus_type的指针,bus_type顾名思义就是总线类型,对于usb设备来说,你可以搜索整个drivers/usb/目录下的代码,你会发现,其实总共就是定义过两个类型,一个是定义于drivers/usb/core/driver.c中的struct bus_type usb_bus_type,另一个是定义于drivers/usb/serial/usb-serial.c中的struct bus_type usb_serial_bus_type.后者干嘛用的不用我说了吧,整个drivers/usb/serial/目录就是专门为usb-to-serial而准备的,串口方面的,咱们不必理睬.所以在咱们的故事中,不管你在哪里看到struct bus_type的指针,它的指向都一样,就是usb_bus_type.drivers/usb/core/driver.c里面看一下这个变量的定义.
   1523 struct bus_type usb_bus_type = {
   1524         .name =         "usb",
   1525         .match =        usb_device_match,
   1526         .uevent =       usb_uevent,
   1527         .suspend =      usb_suspend,
   1528         .resume =       usb_resume,
   1529 };
实际上在usb系统的一开始的初始化代码就有这么一句,bus_register(&usb_bus_type),就这句让系统知道有这么一个类型的总线.不过需要强调的是,一个总线类型和一条总线可别混淆了,从硬件上来讲,一个host controller就会连出一条usb总线,而从软件上来讲,不管你有多少个host controller,或者说有多少条总线,它们通通属于usb_bus_type这么一个类型,只是每一条总线对应一个struct usb_bus结构体变量,也正是前面我们刚刚说过的那个struct usb_hcd中那个叫做self的成员(struct usb_hcdstruct usb_bus self),这个变量在主机控制器的驱动程序中来申请.总线类型和具体的总线这种关系正如C++中的类和对象,不要跟我说你不知道类和对象的关系.那我只能解释说,我身边有两类人,一类是有对象的,一类是没有对象的
Ok,确定了dev.bus指向usb_bus_type之后,下一个dev.type,struct device_type结构体指针,这个就真的不用再解释了,其作用和struct bus_type一样,即总线有总线的类型,设备也有设备的类型,正所谓物以类聚.这里等号右边的usb_device_type来自drivers/usb/core/usb.c,
    195 struct device_type usb_device_type = {
    196         .name =         "usb_device",
    197         .release =      usb_release_dev,
198 };
现在我们还不需要去认识这个结构体内部,只是知道我们的所有的usb设备都会属于usb_device_type这么一类就可以了,这个只是为了让我们从软件的角度来说方便管理而已.
然后dma_mask,这个就是与DMA传输相关的了,设备能不能进行dma传输,得看主机控制器的脸色,主机控制器不支持的话设备自作多情那也没有用.所以这里dma_mask被设置为host controllerdma_mask.注意到我们在struct usb_hcd结构体里有一个struct usb_bus self,而在struct usb_bus结构体里又有一个struct device * controller,实际上struct usb_hcdstruct usb_bus都是为主机控制器而准备的数据结构,只是分工不一样,你要是看它们不爽也可以考虑把它们合并为一个结构体,不过我估计开源社区的那帮家伙不会同意你这样乱来.
256,dev->state,这里dev就是struct usb_device的意思了,而关于state,我们必须很清楚,这是调试usb设备驱动的基础.你如果连usb设备有哪些状态都不知道的话,你不要跟人说你会调试usb设备驱动程序,真的,丢不起那人.struct usb_device里面有一个成员,enum usb_device_state state,这是一个枚举的数据类型.
include/linux/usb/ch9.h中有定义,
    557 enum usb_device_state {
    558         /* NOTATTACHED isn't in the USB spec, and this state acts
    559          * the same as ATTACHED ... but it's clearer this way.
    560          */
    561         USB_STATE_NOTATTACHED = 0,
    562
    563         /* chapter 9 and authentication (wireless) device states */
    564         USB_STATE_ATTACHED,
    565         USB_STATE_POWERED,                      /* wired */
    566         USB_STATE_UNAUTHENTICATED,              /* auth */
    567         USB_STATE_RECONNECTING,                 /* auth */
    568         USB_STATE_DEFAULT,                      /* limited function */
    569         USB_STATE_ADDRESS,
    570         USB_STATE_CONFIGURED,                   /* most functions */
    571
    572         USB_STATE_SUSPENDED
    573
    574         /* NOTE:  there are actually four different SUSPENDED
    575          * states, returning to POWERED, DEFAULT, ADDRESS, or
    576          * CONFIGURED respectively when SOF tokens flow again.
    577          */
578 };
这些都是usb设备的可能状态,usb spec中有定义,但是有些在usb spec里面没有,只是这里设置的,从软件的角度来说对usb设备的状态进行更细的划分.usb spec 2.0中一共定义了6种状态,分别是Attached,Powered,Default,Address,Configured,Suspended.那么咱们这里所谓的USB_STATE_NOTATTACHED就是表征设备并没有Attached,而咱们此时此刻设置的是USB_STATE_ATTACHED就是做为当检测到设备时的初始状态.
端点0的特殊地位决定了她必将受到特殊的待遇.struct usb_device结构体中就有一个成员struct usb_host_endpoint ep0,而它的urb_list也是在这里就要先初始化了.
ep0.desc就是该端点的描述符,端点描述符的长度总是7个字节,这是usb spec规定的,endpoint的描述符中bLength7.USB_DT_ENDPOINT_SIZE就是被定义为7.bDescriptorType就是描述符类型,描述符既然有设备描述符,有接口描述符,有端点描述符,等等,当然就有一个类别的说法,要不怎么区分呢?如下图所示,usb spec里定义了端点描述符的类别就是5,


而咱们的代码里定义了USB_DT_ENDPOINT这个宏就是0x05,所以这里没什么好说的.一切按规矩办事.
ep_inep_out就是两个数组,struct usb_device中的两个成员,struct usb_host_endpoint *ep_in[16]struct usb_host_endpoint *ep_out[16],一个设备就是最多有32个管道,16个进,16个出,基本上一个端点对应一个管道.考你一下,知道我为什么不直接说一个设备最多有32个端点?理由很简单,端点0太太太太太特殊了,别的端点不是进就是出,但是端点0是双向的,既可以收也可以发.所以对它,我们需要以ep_in[0]ep_out[0]来记录.
接下来的几个变量,parent自不必说,没有它就没法形成一棵usb设备树,USB设备树上的每一个设备都得有parent,只有Root Hub没有,正所谓树欲静而风不止,子欲孝而亲不在.当我刚看到这段if语句的时候,觉得它真是荒唐,咱们现在不是在hub驱动程序中,在判断端口有连接设备了才执行这段代码的吗,hub端口里面怎么会连接有Root Hub?但转念一想,不对,这种想法太幼稚了点,有一位伟人的话说,我这叫too simple, sometimes naïve.的确我们是在这种情景下调用usb_alloc_dev,可是这个函数并非只有我们调用啊,别人也会调用,host controller的驱动中就会调用,它就要为Root hub申请内存啊,所以对于usb_alloc_dev函数来说当然就应该判断这个设备是不是Root Hub.Host Controller驱动程序中,调用这个函数的时候传递的parent就是NULL,所以这里这个if就是用来区分这两种情况的.我们来仔细看一下这个if内部的赋值.
首先是devpath,然后是dev.bus_id,最后是busnum.重点来看devpath, struct usb_device结构体中char devpath[16],很显然这将会用来记录一个字符串,这个字符串啥意思?给你看个直观的东西,
localhost:~ # ls /sys/bus/usb/devices/
1-0:1.0  2-1      2-1:1.1  4-0:1.0  4-5:1.0  usb2  usb4
2-0:1.0  2-1:1.0  3-0:1.0  4-5      usb1     usb3
Sysfs文件系统下,我们看到这些乱七八糟的东西,它们都是啥?usb1/usb2/usb3/usb4表示哥们的计算机上接了4usb总线,4usb主机控制器,事物多了自然就要编号,就跟我们中学或大学里面的学号一样,就是用于区分多个个体,4-0:1.0表示什么?4表示是4号总线,或者说4Root Hub,0就是这里我们说的devpath,1表示配置为1,0表示接口号为0.也即是说,4号总线的0号端口的设备,使用的是1号配置,接口号为0.那么devpath是否就是端口号呢?显然不是,这里我列出来的这个例子是只有Root Hub没有级联Hub的情况,如果在Root Hub上又接了别的Hub,然后一级一级连下去,子又生孙,孙又生子,子又有子,子又有孙.子子孙孙,无穷匮也.那么如何在sysfs里面来表征这整个大家族呢?这就是devpath的作用,顶级的设备其devpath就是其连在Root Hub上的端口号,而次级的设备就是其父Hubdevpath后面加上其端口号,即如果4-0:1.0如果是一个Hub,那么它下面的1号端口的设备就可以是4-0.1:1.0,2号端口的设备就可以是4-0.2:1.0,3号端口就可以是4-0.3:1.0.总的来说,就是端口号一级一级往下加.这个思想是很简单的,也是很朴实的.
至此,我们可以来一次性看一下272行到291行这一段代码了.首先判断如果是Root Hub,那么直接赋值dev->devpath[0]’0’,以示特殊,然后父设备设为controller,同时把dev->bus_id[]设置为像usb1/usb2/usb3/usb4这样的字符串.如果不是Root Hub,那么两种情况,第一种,如果是连接在Root Hub,dev->path就是等于端口号,第二种,如果不是连接在Root Hub,那么就是在父设备的devpath基础上加上一个端口号,在父设备的devpath和端口号之间用一个点”.”连接起来.最后把bus_id[]设置成usb1/usb2/usb3/usb4这样的字符串后面连接上devpath.
接下来293295这三行就不用多说了,白痴都知道.
296,初始化一个队列.这个队列干嘛用的?usbfs会用到,只要你够帅,你就能看到在/proc/bus/usb/下面有很多usb相关的信息.每个usb设备会在这下面对应一个文件,这个文件系统称为USB设备文件系统.你可以用下面这条命令挂载这个文件系统:
mount –t usbfs none /proc/bus/usb
然后你cat /proc/bus/usb/devices就能看到那些系统里usb设备的信息.usbfs的实现在drivers/usb/core/inode.c以及drivers/usb/core/devices.c这两个文件中.现在把这个队列初始化为空,不久的将来,当您打开设备在usbfs中对应的文件时,这个队列就会加入东西.
298,又涉及到电源管理的部分了,首先是初始化一个互斥锁,pm_mutex,struct usb_device中的成员,struct mutex pm_mutex.然后是调用INIT_DELAYED_WORK(),这个咱们不陌生吧,前面专门讲过的.不过需要解释的是,autosuspend,自动挂起,struct usb_device中也有一个成员,struct delayed_work autosuspend,usb_autosuspend_work()是定义于drivers/usb/core/driver.c中的一个函数.先不管这个函数干嘛的,至少咱们通过这句话可以知道,在明天的明天的明天,在未来的某年某月某一天的某个时刻,usb_autosuspend_work()函数会被调用.具体是什么时刻,咱们先甭管,到时候再说.autosuspend_delay就很简单了,就是延时,一样的,等到用的时候再来说,不过需要注意的是,这个冬冬被赋值为usb_autosuspend_delay*HZ,其中usb_autosuspend_delay又是一个模块加载参数,
     54 #ifdef  CONFIG_USB_SUSPEND
     55 static int usb_autosuspend_delay = 2;           /* Default delay value,
     56                                                  * in seconds */
     57 module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
     58 MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
     59
     60 #else
     61 #define usb_autosuspend_delay           0
     62 #endif
如果您打开了CONFIG_USB_SUSPEND,那么这个值默认就是2,您当然可以自己修改,在您加载模块usbcore模块的时候作为一个参数传递进来就可以了.如果您没打开CONFIG_USB_SUSPEND,那么不必说,直接就是定义为0,也没有意义.
Ok,到这里,我们走完了八大函数的第一个.路漫漫其修远兮,让我们荡起双桨上下而求索.
返回列表