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,到这里,我们走完了八大函数的第一个.路漫漫其修远兮,让我们荡起双桨上下而求索. 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值