在LINUX中最让人不解的大概就是/sys下面的内容了 下面首先让我们来创建一个简单的platform设备,并从这个设备的视角进行深入,在此篇文章的深入过程中,我们只看kobeject的模型 我所使用的内核版本号为2.6.26,操作系统的内核版本号为2.6.27-7,暂未发现2.6.27-7与2.6.26的重大不同 首先写一个简单的模块 #include
#include #include static int __init test_probe(struct platform_device *pdev) { int err = 0; return err; } static int test_remove(struct platform_device *pdev) { return 0; } static struct platform_device test_device = { .name = "test_ts", .id = -1, }; static struct platform_driver test_driver = { .probe = test_probe, .remove = test_remove, .driver = { .name = "test_ts", .owner = THIS_MODULE, }, }; static int __devinit test_init(void) { platform_device_register(&test_device); return platform_driver_register(&test_driver); } static void __exit test_exit(void) { platform_device_unregister(&test_device); platform_driver_unregister(&test_driver); } module_init(test_init); module_exit(test_exit); MODULE_AUTHOR("zwolf"); MODULE_DESCRIPTION("Module test"); MODULE_LICENSE("GPL"); MODULE_ALIAS("test"); 接下来是makefile #Makefile obj-m:=test.o KDIR:=/lib/modules/2.6.27-7-generic/build PWD:=$(shell pwd) default: $(MAKE) -C $(KDIR) M=$(PWD) modules KDIR中的目录请改为各位实际运行中的内核目录 make之后进行模块的加载 sudo insmod ./test.ko 现在到sys目录中查看我们的设备是否已经加载上了 首先是/sys/bus/platform/devices/ 在devices下,每一个连接文件都代
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
了一个设备 ls可看见test_ts,进入test_ts,ls可发现driver这个链接文件,ls-l查看,发现这个文件是连到/sys/bus/platform/drivers/test_ts的 这里需要说明的是连接的含义,并不是driver驱动存在于test_ts这个设备中,而是test_ts使用的驱动为/sys/bus/platform/drivers/test_ts 现在换到/sys/bus/platform/drivers这个目录下 ls查看会发现这里的文件都为目录,而非连接文件,说明这是驱动真正放置的位置 现在进入test_ts目录,然后ls,发现有一个test_ts的连接文件,ls –l查看可发现该文件连接到/sys/devices/platform/test_ts下 回到/sys/bus/platform/devices/下ls –l也会发现test_ts连接到/sys/devices/platform/test_ts 为什么test_ts这个设备放置于/sys/devices/platform下,而不是/sys/bus/platform/devices下呢 我认为和直观性有关,在sys下有这么几个目录block bus class dev devices firmware kernel module fs power devices很直观的说明了设备在这个目录下,便于大家查找 而/sys/bus/platform/devices下的连接是为了分类查找 画了张目录图,如下,绿色框的为连接文件,绿色线条为连接的对象 下载 (16.11 KB) 2009-01-19 01:27
题
快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题
外话:我自身对于这样的分类不是很喜欢,臃肿 重复 而且信息也不好规划,希望在以后的版本能对sys进行大的改造 现在来看另两个图,也就是构成sys的核心kobject,首先第一个是我去掉了连接部分的内容 也就是绿色线条的目录图 下载 (11.28 KB) 2009-01-19 01:27 第二个是组成这个目录图的核心,kobject图,我也叫他层次图 下载 (288.54 KB) 2009-02-03 15:45 不看大号绿色箭头右边的内容的话是不是发现两个架构相同? 对的,kobject的层次决定了目录的结构 kobeject图很大,但也不要担心,里面的内容其实不多,基础框架涉及3个主要结构kset kobject和ktype 在说明test_ts的注册之前,先让我们看一下sys下的两个基础目录bus,devices 首先是bus bus的注册在/drivers/base/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,这是一个uevent的操作集(我也还没清楚uevent的用途,所以uevent的内容先放着) 然后到kset_create_and_add struct kset *kset_create_and_add(const char *name, struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj) //传递进来的参数为("bus", &bus_uevent_ops, NULL) { struct kset *kset; int error; //创建一个kset容器 kset = kset_create(name, uevent_ops, parent_kobj); if (!kset) return NULL; //注册创建的kset容器 error = kset_register(kset); if (error) { kfree(kset); return NULL; } return kset; } 首先需要创建一个kset容器 static struct kset *kset_create(const char *name, struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj) //传递进来的参数为("bus", &bus_uevent_ops, NULL) { struct kset *kset; //为kset分配内存 kset = kzalloc(sizeof(*kset), GFP_KERNEL); if (!kset) return NULL; //设置kset中kobject的名字,这里为bus kobject_set_name(&kset->kobj, name); //设置uevent操作集,这里为bus_uevent_ops kset->uevent_ops = uevent_ops; //设置父对象,这里为NULL kset->kobj.parent = parent_kobj; //设置容器操作集 kset->kobj.ktype = &kset_ktype; //设置父容器 kset->kobj.kset = NULL; return kset; } 这里的ktype,也就是kset_ktype是一个操作集,用于为sys下文件的实时反馈做服务,例如我们cat name的时候就要通过ktype提供的show函数,具体什么怎么运用,将在后面讲解 现在回到kset_create_and_add中的kset_register,将建立好的kset添加进sys里 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; } kset_init进行一些固定的初始化操作,里面没有我们需要关心的内容 kobject_add_internal为重要的一个函数,他对kset里kobj的从属关系进行解析,搭建正确的架构 static int kobject_add_internal(struct kobject *kobj) { int error = 0; struct kobject *parent; //检测kobj是否为空 if (!kobj) return -ENOENT; //检测kobj名字是否为空 if (!kobj->name || !kobj->name[0]) { pr_debug("kobject: (%p): attempted to be registered with empty " "name!\n", kobj); WARN_ON(1); 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) {//在bus的kset中为空,所以不会进入到下面的代码 //检测是否已经设置父对象 if (!parent) //无则使用父容器为父对象 parent = kobject_get(&kobj->kset->kobj); //添加该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) : "", kobj->kset ? kobject_name(&kobj->kset->kobj) : ""); //建立相应的目录 error = create_dir(kobj); if (error) { kobj_kset_leave(kobj); kobject_put(parent); kobj->parent = NULL; 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; } 至此bus的目录就建立起来了 模型如下 下载 (26.82 KB) 2009-01-19 01:27 接下来是devices,在/drivers/base/core.c里 int __init devices_init(void) { devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); if (!devices_kset) return -ENOMEM; return 0; } 过程和bus的注册一致,我就不复述了~ 模型如下 下载 (33.65 KB) 2009-01-19 01:27 然后是platform的注册 在platform的注册中,分为两个部分,一部分是注册到devices中,另一部分是注册到bus中,代码在/drivers/base/platform.c中 int __init platform_bus_init(void) { int error; //注册到devices目录中 error = device_register(&platform_bus); if (error) return error; //注册到bus目录中 error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); return error; } 首先是device_register,注册的参数为platform_bus,如下所示 struct device platform_bus = { .bus_id = "platform", }; 很简单,只有一个参数,表明了目录名 int device_register(struct device *dev) { //初始化dev结构 device_initialize(dev); //添加dev至目录 return device_add(dev); } void device_initialize(struct device *dev) { //重要的一步,指明了父容器为devices_kset,而devices_kset的注册在前面已经介绍过了 dev->kobj.kset = devices_kset; //初始化kobj的ktype为device_ktype kobject_init(&dev->kobj, &device_ktype); klist_init(&dev->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); INIT_LIST_HEAD(&dev->node); init_MUTEX(&dev->sem); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); device_init_wakeup(dev, 0); set_dev_node(dev, -1); } int device_add(struct device *dev) { struct device *parent = NULL; struct class_interface *class_intf; int error; dev = get_device(dev); if (!dev || !strlen(dev->bus_id)) { error = -EINVAL; goto Done; } pr_debug("device: '%s': %s\n", dev->bus_id, __func__); parent = get_device(dev->parent); setup_parent(dev, parent); if (parent) set_dev_node(dev, dev_to_node(parent)); //设置dev->kobj的名字和父对象,并建立相应的目录 error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id); if (error) goto Error; if (platform_notify) platform_notify(dev); if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); //建立uevent文件 error = device_create_file(dev, &uevent_attr); if (error) goto attrError; if (MAJOR(dev->devt)) { error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; } //建立subsystem连接文件连接到所属class,这里没有设置class对象所以不会建立 error = device_add_class_symlinks(dev); if (error) goto SymlinkError; //建立dev的描述文件,这里没有设置描述文件所以不会建立 error = device_add_attrs(dev); if (error) goto AttrsError; //建立链接文件至所属bus,这里没有设置所属bus所以不会建立 error = bus_add_device(dev); if (error) goto BusError; //添加power文件,因为platform不属于设备,所以不会建立power文件 error = device_pm_add(dev); if (error) goto PMError; kobject_uevent(&dev->kobj, KOBJ_ADD); //检测驱动中有无适合的设备进行匹配,但没有设置bus,所以不会进行匹配 bus_attach_device(dev); if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { down(&dev->class->sem); list_add_tail(&dev->node, &dev->class->devices); list_for_each_entry(class_intf, &dev->class->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); up(&dev->class->sem); } Done: put_device(dev); return error; PMError: bus_remove_device(dev); BusError: if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: if (MAJOR(dev->devt)) device_remove_file(dev, &devt_attr); ueventattrError: device_remove_file(dev, &uevent_attr); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: cleanup_device_parent(dev); if (parent) put_device(parent); goto Done; } 在kobject_add-> kobject_add_varg-> kobject_add_internal中 //提取父对象,因为没有设置,所以为空 parent = kobject_get(kobj->parent); //父容器存在则设置父对象,在前面的dev->kobj.kset = devices_kset中设为了devices_kset if (kobj->kset) { //检测是否已经设置父对象 if (!parent) //无则使用父容器为父对象 parent = kobject_get(&kobj->kset->kobj); //添加该kobj到父容器的链表中 kobj_kset_join(kobj); //设置父对象 kobj->parent = parent; } 现在devices下的platform目录建立好了,模型如下,其中红线描绘了目录关系 下载 (59.59 KB) 2009-01-19 01:27 现在到bus_register了 注册的参数platform_bus_type如下所示 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, }; int bus_register(struct bus_type *bus) { int retval; //声明一个总线私有数据并分配空间 struct bus_type_private *priv; priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); if (!priv) return -ENOMEM; //互相关联 priv->bus = bus; bus->p = priv; BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); //设置私有数据中kobj对象的名字 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out; //设置父容器为bus_kset,操作集为bus_ktype priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; //注册bus容器 retval = kset_register(&priv->subsys); if (retval) goto out; //建立uevent属性文件 retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; //建立devices目录 priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } //建立drivers目录 priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; } //初始化klist_devices和klist_drivers链表 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); //增加probe属性文件 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); kfree(bus->p); out: return retval; } 在kset_register-> kobject_add_internal中 //提取父对象,因为没有设置父对象,所以为空 parent = kobject_get(kobj->parent); //父容器存在则设置父对象,在上文中设置了父容器priv->subsys.kobj.kset = bus_kset if (kobj->kset) { //检测是否已经设置父对象 if (!parent) //无则使用父容器为父对象 parent = kobject_get(&kobj->kset->kobj); //添加该kobj到父容器的链表中 kobj_kset_join(kobj); //设置父对象 kobj->parent = parent; } 在retval = kset_register(&priv->subsys)完成之后platform在bus下的模型如下图 下载 (84.78 KB) 2009-01-19 01:31 有印象的话大家还记得在platform下面有两个目录devices和drivers吧~ 现在就到这两个目录的注册了 priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj); priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); 注意这两条语句的头部 priv->devices_kset = kset_create_and_add priv->drivers_kset = kset_create_and_add 可以清楚的看到bus_type_private下的devices_kset, drivers_kset分别连接到了devices,drivers的kset上 现在来看kset_create_and_add("devices", NULL,&priv->subsys.kobj); struct kset *kset_create_and_add(const char *name, struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj) //参数为"devices", NULL,&priv->subsys.kobj { struct kset *kset; int error; //创建一个kset容器 kset = kset_create(name, uevent_ops, parent_kobj); if (!kset) return NULL; //注册创建的kset容器 error = kset_register(kset); if (error) { kfree(kset); return NULL; } return kset; } 在kset_create 中比较重要的操作为 kset->kobj.ktype = &kset_ktype //设置了ktype,为kset_ktype kset->kobj.parent = parent_kobj; //设置了父对象,为priv->subsys.kobj,也就是platform_bus_type->p->subsys.kobj kset->kobj.kset = NULL; //设置父容器为空 在kset_register中 //提取父对象 parent = kobject_get(kobj->parent); //在之前设置为了 //父容器存在则设置父对象,由于父容器为空,不执行以下代码 if (kobj->kset) { //检测是否已经设置父对象 if (!parent) //无则使用父容器为父对象 parent = kobject_get(&kobj->kset->kobj); //添加该kobj到父容器的链表中 kobj_kset_join(kobj); //设置父对象 kobj->parent = parent; } 至此, devices的模型就建立好了,drivers模型的建立和devices是一致的,只是名字不同而已,我就不复述了,建立好的模型如下 下载 (111.97 KB) 2009-01-19 01:31 好了~ 到了这里,bus,devices和platform的基础模型就就建立好了,就等设备来注册了 在platform模型设备的建立中,需要2个部分的注册,驱动的注册和设备的注册 platform_device_register(&test_device); platform_driver_register(&test_driver); 首先看platform_device_register 注册参数为test_device,结构如下 static struct platform_device test_device = { .name = "test_ts", .id = -1, //. resource //.dev }; 这个结构主要描述了设备的名字,ID和资源和私有数据,其中资源和私有数据我们在这里不使用,将在别的文章中进行讲解 int platform_device_register(struct platform_device *pdev) { //设备属性的初始化 device_initialize(&pdev->dev); //将设备添加进platform里 return platform_device_add(pdev); } void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; //设置kset为devices_kset,则将设备挂接上了devices目录 kobject_init(&dev->kobj, &device_ktype); //初始化kobeject,置ktype为device_ktype klist_init(&dev->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); INIT_LIST_HEAD(&dev->node); init_MUTEX(&dev->sem); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); device_init_wakeup(dev, 0); set_dev_node(dev, -1); } int platform_device_add(struct platform_device *pdev) { int i, ret = 0; if (!pdev) return -EINVAL; //检测是否设置了dev中的parent,无则赋为platform_bus if (!pdev->dev.parent) pdev->dev.parent = &platform_bus; //设置dev中的bus为platform_bus_type pdev->dev.bus = &platform_bus_type; //检测id,id为-1表明该设备只有一个,用设备名为bus_id //不为1则表明该设备有数个,需要用序号标明bus_id if (pdev->id != -1) snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name, pdev->id); else strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); //增加资源到资源树中 for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource; if (r->name == NULL) r->name = pdev->dev.bus_id; p = r->parent; if (!p) { if (r->flags & IORESOURCE_MEM) p = &iomem_resource; else if (r->flags & IORESOURCE_IO) p = &ioport_resource; } if (p && insert_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d\n",pdev->dev.bus_id, i); ret = -EBUSY; goto failed; } } pr_debug("Registering platform device '%s'. Parent at %s\n",pdev->dev.bus_id, pdev->dev.parent->bus_id); //添加设备到设备层次中 ret = device_add(&pdev->dev); if (ret == 0) return ret; failed: while (--i >= 0) if (pdev->resource.flags & (IORESOURCE_MEM|IORESOURCE_IO)) release_resource(&pdev->resource); return ret; } int device_add(struct device *dev) { struct device *parent