linux设备模型深探(2)

在前面一篇 linux设备模型深探(1) 我们详细了解了底层元素 kset,kobject,ktype 之间的关系后,本节讲解下驱动模型中另外几个概念( bus driver device )为后面具体分析特定驱动(platform,pci)模型打个基础。

 

BUS

在设备模型中,所有的 device 都是通过总线 bus 连接,这里的 bus 包括通常意义的总线如 usb pci ,也包括虚拟的 platform 总线。

[root@wangp bus]# pwd

/sys/bus

[root@wangp bus]# ls

ac97  acpi  bluetooth  gameport  i2c  ide  pci  pci_express  pcmcia  platform  pnp  scsi  serio  usb

[root@wangp platform]# pwd

/sys/bus/platform

[root@wangp platform]# ls

devices  drivers

 

struct bus_type {

       const char              * name;

       struct module         * owner;

 

       struct kset             subsys;

       struct kset             drivers;

       struct kset             devices;

       struct klist              klist_devices;

       struct klist              klist_drivers;

 

       struct blocking_notifier_head bus_notifier;

 

       struct bus_attribute * bus_attrs;

       struct device_attribute    * dev_attrs;

       struct driver_attribute     * drv_attrs;

 

       int           (*match)(struct device * dev, struct device_driver * drv);

       int           (*uevent)(struct device *dev, struct kobj_uevent_env *env);

       int           (*probe)(struct device * dev);

       int           (*remove)(struct device * dev);

       void         (*shutdown)(struct device * dev);

 

       int (*suspend)(struct device * dev, pm_message_t state);

       int (*suspend_late)(struct device * dev, pm_message_t state);

       int (*resume_early)(struct device * dev);

       int (*resume)(struct device * dev);

 

       unsigned int drivers_autoprobe:1;

};

 

name 是总线的名字,每个总线下都有自己的子系统,其中包含 2 kset deviece driver ,分别代表已知总线的驱动和插入总线的设备

platform 总线的声明如下:

 

struct bus_type platform_bus_type = {

       .name             = "platform",

       .dev_attrs       = platform_dev_attrs,

       .match            = platform_match,

       .uevent          = platform_uevent,

       .suspend  = platform_suspend,

       .suspend_late  = platform_suspend_late,

       .resume_early  = platform_resume_early,

       .resume          = platform_resume,

};

 

只有很少的 bus_type 成员需要初始化,大部分交给 kernel 来处理

 

 

关于总线的操作常用的如下:

int  bus_register(struct bus_type * bus);

void bus_unregister(struct bus_type * bus);

/* iterator helpers for buses */

 

列举总线上从 start 之后的每个设备,并进行 fn 操作,通常用途是对 bus 上的设备和驱动进行绑定

int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, int (*fn)(struct device *, void *));

 

int driver_attach(struct device_driver * drv)

{

       return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

}

 

static int __driver_attach(struct device * dev, void * data)

{

       struct device_driver * drv = data;

 

       /*

         * Lock device and try to bind to it. We drop the error

         * here and always return 0, because we need to keep trying

         * to bind to devices and some drivers will return an error

         * simply if it didn't support the device.

         *

         * driver_probe_device() will spit a warning if there

         * is an error.

         */

 

       if (dev->parent)      /* Needed for USB */

              down(&dev->parent->sem);

       down(&dev->sem);

       if (!dev->driver)

              driver_probe_device(drv, dev);

       up(&dev->sem);

       if (dev->parent)

              up(&dev->parent->sem);

 

       return 0;

}

 

几乎 linux 设备模型的每一层都提供了添加属性的函数,总线也不例外

struct bus_attribute {

       struct attribute       attr;

       ssize_t (*show)(struct bus_type *, char * buf); // 显示属性

       ssize_t (*store)(struct bus_type *, const char * buf, size_t count); // 设置属性

};

创建属于一个总线的属性使用(在模块的加载时间完成)

int  bus_create_file(struct bus_type *,struct bus_attribute *);

void bus_remove_file(struct bus_type *, struct bus_attribute *);

 

在说明下 bus sysfs 里面的结构,刚才已经讲过, bus_type 中有 2 kset 结构对应于 device driver ,也就是说每个 bus 下面都会有 device driver2 个文件夹。

 

首先在总线上注册的驱动会得到一个文件夹 driver ,如 platform 驱动

[root@wangp platform]# pwd

/sys/bus/ platform

[root@wangp platform]# ls

devices  drivers

[root@wangp drivers]# pwd

/sys/bus/platform/drivers

[root@wangp drivers]# ls

i8042  pcspkr  serial8250  vesafb

 

而任何在总线 /sys/bus/xxx/ 上发现的设备会得到一个 symlink( 符号链接)即 /sys/bus/xxx/device 指向 /sys/device/xxx 下面的文件夹

[root@wangp devices]# pwd

/sys/bus/platform/ devices

[root@wangp devices]# ls -l

total 0

lrwxrwxrwx 1 root root 0 Jun  6 10:37 bluetooth -> ../../../devices/platform/bluetooth

lrwxrwxrwx 1 root root 0 Jun  6 10:37 floppy.0 -> ../../../devices/platform/floppy.0

lrwxrwxrwx 1 root root 0 Jun  6 10:37 i8042 -> ../../../devices/platform/i8042

lrwxrwxrwx 1 root root 0 Jun  6 10:37 pcspkr -> ../../../devices/platform/pcspkr

lrwxrwxrwx 1 root root 0 Jun  6 10:37 serial8250 -> ../../../devices/platform/serial8250

lrwxrwxrwx 1 root root 0 Jun  6 10:37 vesafb.0 -> ../../../devices/platform/vesafb.0

 

 

DEVICE

struct device {

       struct klist              klist_children;

       struct klist_node     knode_parent;        /* node in sibling list */

       struct klist_node     knode_driver;

       struct klist_node     knode_bus;

       struct device          *parent;  // 该设备所属的设备

 

       struct kobject kobj;

       char bus_id[BUS_ID_SIZE];     /* position on parent bus */

       struct device_type  *type;

       unsigned         is_registered:1;

       unsigned         uevent_suppress:1;

 

       struct semaphore    sem; /* semaphore to synchronize calls to

                                     * its driver.

                                     */

 

       struct bus_type      * bus;             /* type of bus device is on */

       struct device_driver *driver;    /* which driver has allocated this  device */

       void         *driver_data;   /* data private to the driver */

       void         *platform_data;      /* Platform specific data, device  core doesn't touch it */

       struct dev_pm_info power;

 

#ifdef CONFIG_NUMA

       int           numa_node;    /* NUMA node this device is close to */

#endif

       u64         *dma_mask;    /* dma mask (if dma'able device) */

       u64         coherent_dma_mask;/* Like dma_mask, but for

                                        alloc_coherent mappings as

                                        not all hardware supports

                                        64 bit addresses for consistent

                                        allocations such descriptors. */

 

       struct list_head       dma_pools;      /* dma pools (if dma'ble) */

 

       struct dma_coherent_mem     *dma_mem; /* internal for coherent mem   override */

       /* arch specific additions */

       struct dev_archdata archdata;

 

       spinlock_t        devres_lock;

       struct list_head       devres_head;

 

       /* class_device migration path */

       struct list_head       node;

       struct class             *class;

       dev_t                    devt;              /* dev_t, creates the sysfs "dev" */

       struct attribute_group    **groups;       /* optional groups */

 

       void  (*release)(struct device * dev);

};

这个结构相当于一个基类,对于基于特定总线的设备,会派生出特定的 device 结构( linux 的驱动模型有很多结构都可以基于类来看待)

 

struct platform_device {

       const char       * name;

       int           id;

       struct device   dev;

       u32         num_resources;

       struct resource       * resource;

};

 

一个总线设备用如下函数注册(注册总线类型)

int device_register(struct device *dev)

{

       device_initialize(dev);

       return device_add(dev);

}

以完成 parent name bus_id bus 几个成员的初始化,注册后可以在 /sys/devices 下面看到

void device_unregister(struct device *dev);

 

同时和其相关的属性为

struct device_attribute {

       struct attribute       attr;

       ssize_t (*show)(struct device *dev, struct device_attribute *attr,char *buf);

       ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);

};

int device_create_file(struct device *device,struct device_attribute * entry);

device_remove_file(struct device * dev, struct device_attribute * attr);

以下完成一个总线设备的注册:

static void simple_bus_release(struct device *dev)

{

       printk("simple bus release/n");

}

struct device simple_bus = {

       .bus_id ="simple_bus",

       .release = simple_bus_release

}

 

ret = device_register(&simple_bus);

if(ret)

pritnk("unable to register simple_bus/n");

完成注册后, simple_bus 就可以再 sysfs /sys/devices 下面看见,任何挂载这个 bus 上的 device 都会在 /sys/devices/simple_bus 下看到

 

DEVICE_DRIVER

 

struct device_driver {

       const char              * name;// sysfs 下显示

       struct bus_type             * bus;

 

       struct kobject         kobj;

       struct klist              klist_devices;

       struct klist_node     knode_bus;

 

       struct module         * owner;

       const char             * mod_name;  /* used for built-in modules */

       struct module_kobject    * mkobj;

 

       int    (*probe)  (struct device * dev);

       int    (*remove)       (struct device * dev);

       void  (*shutdown)   (struct device * dev);

       int    (*suspend)      (struct device * dev, pm_message_t state);

       int    (*resume)       (struct device * dev);

};

由于大多数驱动都会带有特有的针对某种特定总线的信息,因此一般都是基于 device_driver 来派生出自己

特有的驱动,如

struct platform_driver {

       int (*probe)(struct platform_device *);

       int (*remove)(struct platform_device *);

       void (*shutdown)(struct platform_device *);

       int (*suspend)(struct platform_device *, pm_message_t state);

       int (*suspend_late)(struct platform_device *, pm_message_t state);

       int (*resume_early)(struct platform_device *);

       int (*resume)(struct platform_device *);

       struct device_driver driver;

};

比较 xxx_driver device_driver 我们可以发现,结构体所带的方法基本相同,在具体应该用的时候是可以转换的。

 

驱动的注册

int driver_register(struct device_driver * drv);

void driver_unregister(struct device_driver * drv);

而大多数驱动会调用针对特定总线的诸如 platform_driver_register,pci_driver_register 之类的函数去注册

 

总线 (bus) 可以挂接一类设备 (device)

驱动( driver )可以驱动一类设备( device

因此和 bus 一样, device_driver 也有一个函数为某个驱动来遍历所有设备

int driver_for_each_dev(struct device_driver *drv, void *data,int (*callback)(struct device *dev,void *data);

所有 device_driver 完成注册后,会在 /sys/bus/xxx/driver 目录下看到驱动信息

 

同时相应的属性内容

struct driver_attribute {

       struct attribute       attr;

       ssize_t (*show)(struct device_driver *, char * buf);

       ssize_t (*store)(struct device_driver *, const char * buf, size_t count);

};

int  driver_create_file(struct device_driver *,struct driver_attribute *);

void driver_remove_file(struct device_driver *, struct driver_attribute *);

 

说了这么多,现在来理一理 kobject kset,subsys,sysfs,bus 之间的关系

 


上图反映了继承体系的一个基本结构, kset 是一组相同的 kobject 的集合, kernel 可以通过跟踪 kset 来跟踪所用的特定类型设备, platform pci i2c 等, kset 起到连接作用将设备模型和 sysfs 联系在一起。每个 kset 自身都包含一个 kobject ,这个 kobject 将作为很多其他的 kobject 的父类,从 sys 上看,某个 kobject 的父类是某个目录,那么它就是那个目录的子目录, parent 指针可以代表目录层次,这样典型的设备模型层次就建立起来了,从面向对象的观点看, kset 是顶层的容器类, kset 继承他自己的 kobject ,并且可以当做 kobject 来处理

 

如图: kset 把它的子类 kobject 放在链表里面, kset 子类链表里面那些 kobject kset 指针指向上面的 kset parent 指向父类。

 

struct kobject {

       const char              * k_name;

       struct kref              kref;

       struct list_head       entry;

       struct kobject         * parent;

       struct kset             * kset;

       struct kobj_type     * ktype;

       struct sysfs_dirent   * sd;

};

 

struct kset {

       struct kobj_type     *ktype;

       struct list_head       list;

       spinlock_t        list_lock;

       struct kobject         kobj;

       struct kset_uevent_ops  *uevent_ops;

};

 


 

                




有了这些基本单元,就可以派生出许多其他的新类别



了解了驱动模型的构架后,我们就可以分析具体的驱动(platform,pci)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值