香菜怎么凉拌最好吃:设备驱动的基石驱动模型(3)

来源:百度文库 编辑:偶看新闻 时间:2024/05/05 05:41:31

#include
#include
#include
#include

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

static struct kobject *wwhs_kobj;

static int __init wwhs_drvmode_init()
{
 wwhs_dbg("drvmode_init");
 wwhs_kobj = kobject_create_and_add("wwhs_drvmode",NULL);
 if (!wwhs_kobj)
 wwhs_dbg("out of memory");
 return 0;
}

static void __exit wwhs_drvmode_exit()
{
 wwhs_dbg("drvmode_exit");
 kobject_del(wwhs_kobj);
}

module_init(wwhs_drvmode_init);
module_exit(wwhs_drvmode_exit);
MODULE_AUTHOR("wwhs");
MODULE_DESCRIPTION("wwhs_drvmode");
MODULE_LICENSE("GPL");
下面给出makefile,makefile请根据自已的开发环境进行适当调整。

obj-m+=wwhs_drvmode.o
KERNELDIR=/opt/linux-2.6.32.3
PWD:=$(shell pwd)
all:
 make -C $(KERNELDIR) M=$(PWD) modules
clean:
 rm -rf *.o* *.ko* *.mod.c *.cmd *.symvers .tmp_versions .*.cmd

将这两个文件放在同一目录下,然后进入root 模式。
make一下。
如果不出意外的话应该可以生成wwhs_drvmode.ko这样一个内核模块。
执行:insmod wwhs_drvmode.ko
执行:dmesg
应该可以看到打印信息了吧。
好,我们进去/sys目录下看看。
见证奇迹的时候到了,我们可以激动的看到在/sys目录下面多了一个名为wwhs_drvmode的目录。
执行:rmmod wwhs_drvmode
执行:dmesg
可以看到有exit的打印。
再进/sys目录下,wwhs_drvmode这个目录不见了。
神奇啊!
这与我们之前的源码分析是一一对应的,真是太神奇了。
上节我们用最直观的方式知道了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

#include

#include

#include

#include

#include

#include

 

#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_ip的定义如下:

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;

}

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