linux驱动模型浅析

 

linux设备驱动模型

在讲解设备驱动模型前,先介绍几个基本概念:

总线类型:bus_type

在linux中,所有的设备都是挂载在总线上的,比如pci,scsi,usb总线等。这些总线在sys文件系统中的反映,是在/sys/bus都会有一个属于自己的目录。linux提供了一套操作函数,比如:

bus_register():

用于向设备驱动核心注册总线,该函数会在下面做具体的 分析;

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

当一个新设备或者新的驱动被添加到这个总线上时,这个方法被调用一次或多次,诺制指定的驱动程序能够处理制定的设备,则返回非零值。

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

在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量

bus_for_each_dev():

bus_for_each_drv():

这两个函数迭代总线上的每个设备或驱动程序

2011-05-29

设备类型:device

每个设备都会有一个strut devcie结构,该结构通常会被内嵌到具体的设备结构体中。

与总线类似,在使用之前,先要对设备进行注册, 注册之前,最少需要设置parent,bus_id,bus,release成员,然后调用:

device_register();

 

设备驱动结构:device_driver

每个设备也会有一个struct device_driver结构,该结构通常也会被内嵌到具体的设备驱动结构中.设备驱动的注册通过driver_register()完成

 

 

要怎样来理解这三者的关系呢?你可以把总线看作月老,而设备是男生,设备驱动是女生。所有的设备和设备驱动在出生的时候都会在相应的总线上进行注册。而月老呢,一旦有男生或者女生注册过来,就会调用准备好的match函数,为这个男生去匹配所有的女生,或者为这个女生匹配所有的男生。如果,匹配成功,就将driver中的device指针指向这个device,把device的driver指针指向这个drive,然后,王子和公主就可以幸福的在一起了。同时,就像结婚需要登记一样,内核就像一个国家,为了统一管理,创建了一个叫/sys的文件系统。

说起文件系统,大家的概念都是在磁盘或者其它存储介质上数据的一种组织方式。而/sys有点特别,它并没有存在在具体的介质上,换句话说,它没有身体,只有灵魂。每次系统掉电以后,它就消失了。而在你访问它的时候,它是根据驱动模型的层次结构动态生成的。设备驱动模型的层次结构,又是怎样组织起来的呢?这就是我们下面具体要讲的东西了。

在驱动模型中,有几个比较总要的结构,按层次结构,从上往下分别为:

subsysterm->kset->kobject

子系统:

是对整个内核中一些高级部分的表述。它通常显示在sysfs分层结构的顶层。但在我的2.6.38内核中,貌似已经没有独立的subsysterm结构体了,而只存在kset结构体。

kset:

kset的主要功能是包容;我们可以认为它是kobject的顶层容器类,是相同类型的kobject的集合。实际上,每个kset内部都包含了一个kobject结构体,并且可以用多种处理kobject的方法来处理kset。需要注意的是,kset总是在sysfs中出现,一旦设置了kset并把它添加到了系统中,将在sysfs中创建一个目录吧。kobject不必在sysfs中表示,但是kset中的每一个kobject成员都将在sysfs中得到表述。

kobject:

kobject是组成设备模型的基本结构。单独的使用它是没有任何意义的。通常情况下,他会被内嵌到大型的数据机构中,用来控制对大型域的访问。比如最简单的字符驱动,会内嵌有kobject结构体:

struct cdev{

struct kobject kobj;

struct modle* owner;

struct file_operations* ops;

struct list_head list;

dev_t dev;

unsigned int count;

};

下面我们通过来分析bus_register这个函数,来对驱动模型有一个更加感性的认识,先贴上bus_register的代码:

/**

 * bus_register - register a bus with the system.

 * @bus: bus.

 *

 * Once we have that, we registered the bus with the kobject

 * infrastructure, then register the children subsystems it has:

 * the devices and drivers that belong to the bus.

 */

880 int bus_register(struct bus_type *bus)

881{

882 int retval;

883 struct bus_type_private *priv;

884

885 priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);

886 if (!priv)

887 return -ENOMEM;

888

889 priv->bus = bus;

890 bus->p = priv;

891

892 BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

893

894 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

895 if (retval)

896 goto out;

897

898 priv->subsys.kobj.kset = bus_kset;

899 priv->subsys.kobj.ktype = &bus_ktype;

900 priv->drivers_autoprobe = 1;

901

902 retval = kset_register(&priv->subsys);

903 if (retval)

904 goto out;

905

906 retval = bus_create_file(bus, &bus_attr_uevent);

907 if (retval)

908 goto bus_uevent_fail;

909

910 priv->devices_kset = kset_create_and_add("devices", NULL,

911 &priv->subsys.kobj);

912 if (!priv->devices_kset) {

913 retval = -ENOMEM;

914 goto bus_devices_fail;

915 }

916

917 priv->drivers_kset = kset_create_and_add("drivers", NULL,

918 &priv->subsys.kobj);

919 if (!priv->drivers_kset) {

920 retval = -ENOMEM;

921 goto bus_drivers_fail;

922 }

923

924 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

925 klist_init(&priv->klist_drivers, NULL, NULL);

926

927 retval = add_probe_files(bus);

928 if (retval)

929 goto bus_probe_files_fail;

930

931 retval = bus_add_attrs(bus);

932 if (retval)

933 goto bus_attrs_fail;

934

935 pr_debug("bus: '%s': registered/n", bus->name);

936 return 0;

 

bus_attrs_fail:

remove_probe_files(bus);

bus_probe_files_fail:

kset_unregister(bus->p->drivers_kset);

bus_drivers_fail:

kset_unregister(bus->p->devices_kset);

bus_devices_fail:

bus_remove_file(bus, &bus_attr_uevent);

bus_uevent_fail:

kset_unregister(&bus->p->subsys);

kfree(bus->p);

out:

bus->p = NULL;

return retval;

}

首先看第883行,定义了一个struct bus_type_private类型的指针priv,该数据结构指针是bus_type结构体中的最后一个成员,如下:

struct bus_type_private {

struct kset subsys;

struct kset *drivers_kset;

struct kset *devices_kset;

struct klist klist_devices;

struct klist klist_drivers;

struct blocking_notifier_head bus_notifier;

unsigned int drivers_autoprobe:1;

struct bus_type *bus;

};

从结构的定义中,就可以看出,该结构定义了很多跟驱动模型相关的数据成员。看到第一个成员就是subsys,该成员实际上就是上面所说的subsysterm机构,只是在2.6.34中,该结构体已经完全呗kset结构体 所取代了。然后看第二个和第三个成员:struct kset* drivers_kset, struct kset* devices_kset,他们在下面会分别指向对应的/sys文件系统该bus目录下的device目录和driver目录。

继续看代码,第889和890行,新分配的priv结构体与bus_type进行了绑定,然后调用kobject_set_name()函数,对bus的name进行了初始化。看898行,这个很关键,他把priv->subsys.kobj.kset = bus_kset. 跳到bus_kset的定义出可以看到,实际上bus_kset也是个kset类型。他是在sys系统初始化的时候被注册到系统的,实际上/sys/bus目录就是由该bus_kset生成的。 同时,所有的总线kset的父kset都是bus_kset,所有的总线目录,比如PCI,USB等,都会在/sys/bus有相应的总线目录,比如/sys/bus/pci,/sys/bus/usb。

在902行,函数调用了kset_register()进行了注册。这是很核心的函数,需要仔细看,先贴出他的代码:

/**

       * kset_register - initialize and add a kset.

 * @k: kset.

 */

712 int kset_register(struct kset *k)

713 {

714 int err;

715

716 if (!k)

717 return -EINVAL;

718

719 kset_init(k);

720 err = kobject_add_internal(&k->kobj);

721 if (err)

722 return err;

723 kobject_uevent(&k->kobj, KOBJ_ADD);

724 return 0;

725 }

 

看到这个函数很短,但是,并不简单,原因是他调用了几个并不简单的函数。首先是kset_init():

/**

  * kset_init - initialize a kset for use

  * @k: kset

  */

671 void kset_init(struct kset *k)

672 {

673 kobject_init_internal(&k->kobj);

674 INIT_LIST_HEAD(&k->list);

675 spin_lock_init(&k->list_lock);

676 }

这个函数还是比较简单的,分别是对kobj,k->list节点,和list_lock自旋锁进行了初始化。跳进kobject_init_internal(),可以发现,对kobject的初始化,主要是初始话了kref引用指针,将其初始值设为了1,然后是初始了Kobj中的相关变量,这里不再缀诉。

让我们回到kset_register()中,看kobject_add_internal(),真正的好戏才开始:

static int kobject_add_internal(struct kobject *kobj)

{

int error = 0;

struct kobject *parent;

 

if (!kobj)

return -ENOENT;

 

if (!kobj->name || !kobj->name[0]) {

WARN(1, "kobject: (%p): attempted to be registered with empty "

"name!/n", kobj);

return -EINVAL;

}

 

parent = kobject_get(kobj->parent);

 

/* join kset if set, use it as parent if we do not already have one */

if (kobj->kset) {

if (!parent)

parent = kobject_get(&kobj->kset->kobj);

kobj_kset_join(kobj);

kobj->parent = parent;

}

 

pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",

kobject_name(kobj), kobj, __func__,

parent ? kobject_name(parent) : "<NULL>",

kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

 

error = create_dir(kobj);

if (error) {

kobj_kset_leave(kobj);

kobject_put(parent);

kobj->parent = NULL;

 

/* be noisy on error issues */

if (error == -EEXIST)

printk(KERN_ERR "%s failed for %s with "

      "-EEXIST, don't try to register things with "

      "the same name in the same directory./n",

      __func__, kobject_name(kobj));

else

printk(KERN_ERR "%s failed for %s (%d)/n",

      __func__, kobject_name(kobj), error);

dump_stack();

} else

kobj->state_in_sysfs = 1;

 

return error;

}

该函数,首先试图去获得该kobject的parent指针。该指针是干嘛的呢?前面介绍kset的时候说过,kset是相同类型的kobject的容器,而这种关系是通过什么方式体现呢?在kobjcet层,就是通过parent指针体现的。kobject会把该指针指向其所属kset结构下的kobject结构体中。而因为bus_register()函数,在之前并没有对该kobj的parent指针进行过初始化,所以:

/* join kset if set, use it as parent if we do not already have one */

if (kobj->kset) {

if (!parent)

parent = kobject_get(&kobj->kset->kobj);

kobj_kset_join(kobj);

kobj->parent = parent;

}

代码段会被执行。parent指针会被初始化为bus_kset的kobject,从而建立层次关系。

初始化好后,函数调用create_dir()函数,用来建立/sys文件系统下的具体目录结构。这个函数跟linux的VFS系统有关,有点复杂,以后在分析,至此,该bus在/sys/bus下的目录已经建立,基本的层次结构建立也已经完成。

 

让我们回到bus_register中,下面是bus_create_file(bus,&bus_attr_uevent),顾名思义,该函数在/sys/bus/$(bus_name)目录下创建了一个bus_attr_uevent的文件。下面的kset_create_and_add("devices",NULL,&priv->subsys.kobj)和kset_create_and_add("drivers",NULL,&priv->subsys.kobj)函数会在目录/sys/bus/$(bus_name)下创建名为devices和drivers的目录。跳入kset_create_and_add()实际上也只是调用了kset_register函数而已,所以在里不再重复,有兴趣的朋友可以自己看看。

接下去,bus_register()对klist_devices和klist_drivers链表进行了初始化。所有的跟总线相关的device和drives都会挂在这两条链表上。

 

 

2011-05-29

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值