#include <linux/module.h> #define wwhs_dbg(dbgbuf) printk(KERN_ERR"wwhs:%s\n",dbgbuf); static struct kobject *wwhs_kobj; static int __init wwhs_drvmode_init() static void __exit wwhs_drvmode_exit() module_init(wwhs_drvmode_init); obj-m+=wwhs_drvmode.o 将这两个文件放在同一目录下,然后进入root 模式。
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);
#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分析完了,六个文件(目录也是文件)也创建完成了。 |
|