现在的位置: 首页 > 综合 > 正文

Linux内核大讲堂之设备驱动的基石驱动模型(4)

2013年11月11日 ⁄ 综合 ⁄ 共 8816字 ⁄ 字号 评论关闭

转自:无为和尚的Linux内核大讲堂系列。http://blog.csdn.net/z2007b/article/details/6388753

今天又升级了最新版内核2.6.38.5,编了一下,我之前写的几个模块都可以用,看来这一块没有太大差异。顺便把内核升级写了一个相关的文档。大家如果没升级过内核的可以参考一下:http://blog.csdn.net/z2007b/archive/2011/05/09/6407458.aspx 。好了。上节我们用最直观的方式知道了kobject_create_and_add("wwhs_drvmode",NULL)这个函数做了些什么事。那kset呢?经过我们在最前面两小节的分析可以知道kset_register其实就比kobject多了一个函数kobject_uevent。先给出kset_register的定义吧:

int kset_register(struct kset *k)

{

         int err;

         if (!k)

                   return -EINVAL;

         kset_init(k);

         err = kobject_add_internal(&k->kobj);

         if (err)

                   return err;

         kobject_uevent(&k->kobj, KOBJ_ADD);

         return 0;

}

记起来了吧。这个讲还是为时过早,反正大家只要知道这个东西是用来通知用户空间的就行了。

接下来我们来个大家感兴趣一点的:bus_register。下面这个是经过我改装过的小例子。总共三个文件:

1.wwhs_public.h

#include <linux/module.h>

#include <linux/device.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/stat.h>

#include <linux/slab.h>

#include <linux/kobject.h>

 

#define wwhs_dbg(dbgbuf) printk(KERN_ERR"wwhs:%s/n",dbgbuf);

2.wwhs_bus.c

 

#include "../wwhs_public.h"

 

static int wwhs_bus_match(struct device *dev, struct device_driver *drv)

{

         return 1;

}

 

struct bus_type wwhs_bus_type = {

         .name                = "wwhs_bus",

         .match               = wwhs_bus_match,

};

 

static int __init wwhs_bus_init()

{

         wwhs_dbg("bus register");

         return bus_register(&wwhs_bus_type);

}

 

static void __exit wwhs_bus_exit()

{

         wwhs_dbg("bus unregister");

         bus_unregister(&wwhs_bus_type);

}

 

module_init(wwhs_bus_init);

module_exit(wwhs_bus_exit);

MODULE_AUTHOR("wwhs");

MODULE_DESCRIPTION("wwhs_bus test");

MODULE_LICENSE("GPL");

 

3.Makefile

obj-m+=wwhs_bus.o

KERNELDIR=/opt/kernel/linux-2.6.38/linux-2.6.38.5

PWD:=$(shell pwd)

all:

         make -C $(KERNELDIR) M=$(PWD) modules

clean:

         rm -rf *.o* *.ko* *.mod.c *.cmd *.symvers .tmp_versions .*.cmd

 

编译并安装模块,我们发现在/sys/bus下面多了一个目录:wwhs_bus。进去看看:

Ls

devices  drivers  drivers_autoprobe  drivers_probe  uevent

多了五个东西。

下面我们来结合源码对bug_register进行详细的分析。

int bus_register(struct bus_type *bus)

{

         int retval;

         struct subsys_private *priv;

 

         priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);

         if (!priv)

                   return -ENOMEM;

 

         priv->bus = bus;

         bus->p = priv;

         BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

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

         if (retval)

                   goto out;

         priv->subsys.kobj.kset = bus_kset;

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

         priv->drivers_autoprobe = 1;

         retval = kset_register(&priv->subsys);

         if (retval)

                   goto out;

         retval = bus_create_file(bus, &bus_attr_uevent);

         if (retval)

                   goto bus_uevent_fail;

 

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

                                                         &priv->subsys.kobj);

         if (!priv->devices_kset) {

                   retval = -ENOMEM;

                   goto bus_devices_fail;

         } 

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

                                                         &priv->subsys.kobj);

         if (!priv->drivers_kset) {

                   retval = -ENOMEM;

                   goto bus_drivers_fail;

         }

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

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

 

         retval = add_probe_files(bus);

         if (retval)

                   goto bus_probe_files_fail;

 

         retval = bus_add_attrs(bus);

         if (retval)

                   goto bus_attrs_fail;

 

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

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

out:

         kfree(bus->p);

         bus->p = NULL;

         return retval;

}

在函数中首先们感兴趣的是:

priv->subsys.kobj.kset = bus_kset;

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

         priv->drivers_autoprobe = 1;

我们通过对代码进行查找发现bus_kset其定义中下:

static struct kset *bus_kset;

很明显会有别的地方对它已经进行初始化了。我们在bus.c文件中发现有这样的一个函数:

int __init buses_init(void)

{

         bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);

         if (!bus_kset)

                   return -ENOMEM;

         return 0;

}

而bus_uevent_ops的定义如下:

static const struct kset_uevent_ops bus_uevent_ops = {

         .filter = bus_uevent_filter,

};

其中函数bus_uevent_filter,定义如下:

static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)

{

         struct kobj_type *ktype = get_ktype(kobj);

 

         if (ktype == &bus_ktype)

                   return 1;

         return 0;

}

OK,下一个, bus_ktype:

static struct kobj_type bus_ktype = {

         .sysfs_ops        = &bus_sysfs_ops,

};

static const struct sysfs_ops bus_sysfs_ops = {

         .show       = bus_attr_show,

         .store       = bus_attr_store,

};

static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)

{

         struct bus_attribute *bus_attr = to_bus_attr(attr);

         struct subsys_private *subsys_priv = to_subsys_private(kobj);

         ssize_t ret = 0;

 

         if (bus_attr->show)

                   ret = bus_attr->show(subsys_priv->bus, buf);

         return ret;

}

 

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)

{

         struct bus_attribute *bus_attr = to_bus_attr(attr);

         struct subsys_private *subsys_priv = to_subsys_private(kobj);

         ssize_t ret = 0;

 

         if (bus_attr->store)

                   ret = bus_attr->store(subsys_priv->bus, buf, count);

         return ret;

}

接下来是:

kset_register(&priv->subsys);

又见到老朋友了,这个老朋友将会帮我们在/sys/bus下创建一个名为wwhs_bus的目录。

接下来是bus_create_file(bus, &bus_attr_uevent);

bus_attr_uevent 是什么呢?用sourceinsight看是黑的,直接点击的话是跳转不了的,另外有很多高手推荐的vim、emacs,或者再装上高级的插件cscope、ctags也肯定是直接找不到的。

除非你自已根据linux内核的语法规则自已再写个插件还差不多,但前提条件是你要先手工找到熟悉规则才能写得出来,否则,你永远也别想直接找到。

不过没事,哥已经帮你找到了,在这:

static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);

再看gh BUS_ATTR这个宏:

#define BUS_ATTR(_name, _mode, _show, _store)        /

struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)

明白了吧。

static ssize_t bus_uevent_store(struct bus_type *bus, const char *buf, size_t count)

{

         enum kobject_action action;

 

         if (kobject_action_type(buf, count, &action) == 0)

                   kobject_uevent(&bus->p->subsys.kobj, action);

         return count;

}

很明显我们上面分析的这一堆代码会在/sys/bus/wwhs_bus目录之下创建一个叫uevent的文件。并且我们如果往这个文件中写入字符的时候会触发bus_uevent_store函数的执行。这个函数实在简单的连我这种笨鸟都不想分析,各位大侠就自个看一看吧。

接下来要登场的是一女一男,女的不但身材绝美,面容娇好,身怀绝技,且用情专一,一生只为一个人服务。男的虽然才高八斗,学富五车,但是确是个花心大萝卜,只要能达到他的条件,都会为女方服务,比如我们马上要讲的这位男同志,就只要身材好,漂亮,他就愿意为人家服务。扯了半天,我们的美女就是device,帅哥就是device_driver。总线作为红娘,肯定同一时间不止为一对男女提供机会,<<非诚勿扰>>和<<我们约会吧>>这种市井节目都知道一次邀请多位男女同志一起配对,难道我们英明神武的内核开发者不会?不可能嘛。所以我们内核作者和何炅老师一样的聪明,他分别利用下面这段代码创建了一个美女阵营和一个帅哥阵营。

priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj);         //美女阵营

         if (!priv->devices_kset) {

                   retval = -ENOMEM;

                   goto bus_devices_fail;

         }

 

         priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);      //帅哥阵营

         if (!priv->drivers_kset) {

                   retval = -ENOMEM;

                   goto bus_drivers_fail;

         }

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  //美女阵营

klist_init(&priv->klist_drivers, NULL, NULL);                                                 //帅哥阵营

接下来就是:

static int add_probe_files(struct bus_type *bus)

{

         int retval;

         retval = bus_create_file(bus, &bus_attr_drivers_probe);

         if (retval)

                   goto out;

         retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);

         if (retval)

                   bus_remove_file(bus, &bus_attr_drivers_probe);

out:

         return retval;

}

凭着刚才对bus_attr_uevent 的分析,我们可以利用人本身的惰性来查找上面的几个在sourceinsight下的灰色成员。

是的这两个成员就是:

static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);

static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO, show_drivers_autoprobe, store_drivers_autoprobe);

在这里可以告诉大家一个技巧:linux内核在用宏定义这类变量时,都会在需要调用这个变量的最近的地方定义,不会离的很远。

显然,我们会在/sys/bus/wwhs_bus/目录下再创建两个文件:drivers_autoprobe 、drivers_probe。都是很简单的几个函数。列一下吧:

static ssize_t store_drivers_probe(struct bus_type *bus, const char *buf, size_t count)

{

         struct device *dev;

         dev = bus_find_device_by_name(bus, NULL, buf);

         if (!dev)

                   return -ENODEV;

         if (bus_rescan_devices_helper(dev, NULL) != 0)

                   return -EINVAL;

         return count;

}

static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)

{

         return sprintf(buf, "%d/n", bus->p->drivers_autoprobe);

}

 

static ssize_t store_drivers_autoprobe(struct bus_type *bus, const char *buf, size_t count)

{

         if (buf[0] == '0')

                   bus->p->drivers_autoprobe = 0;

         else

                   bus->p->drivers_autoprobe = 1;

         return count;

}

至此bus_register分析完了,六个文件(目录也是文件)也创建完成了。

休息一下,不抽烟,健康生活,多喝开水,哥先喝口水。^_^

抱歉!评论已关闭.