Linux设备模型(中)之上层容器

本文详细介绍了Linux设备模型的核心组成部分,包括总线(bus_type)、设备(device)和驱动(device_driver)的数据结构,并通过示例代码展示了如何在Linux内核中实现这些组件。

努力成为linux kernel hacker的人李万鹏原创作品,转载请标明出处

http://blog.youkuaiyun.com/woshixingaaa/archive/2011/05/05/6396620.aspx

Linux设备模型是由总线(bus_type),设备(device),驱动(device_driver)这三个数据结构来描述的。在设备模型中,所有的设备都通过总线来连接。即使有些设备没有连接到一根物理上的总线,Linux为其设置了一个内部的,虚拟的platform总线,来维持总线,驱动,设备的关系。总线是处理器与一个或者多个设备之间的通道。比如一个USB控制器通常是一个PCI设备,设备模型展示了总线和他们所控制的设备之间的连接。
一般来说可以这么理解,整个的设备模型是一个OO的体系结构,总线,设备,驱动都是其中鲜活存在的对象,kobject是他们的基类,所实现的只是一些公共的接口,kset是同种类型的kobject对象的集合,也可以说是对象的容器。只是因为C语言里不可能会有C++语言里类的class继承等概念,只有通过kobject嵌入到对象结构中来实现。这样,内核使用kobject将各个对象连接起来组成一个分层的体系结构。kobject结构中包含了parent成员,指向了另一个kobject结构,也就是这个分层结构的上一层结点。而kset是通过链表来实现的。

kobject是Linux在2.6中新引进的统一的设备管理模型,目的是对Linux的2.6系统所有的设备进行统一的管理。kobject是组成设备模型的基本结构。kobject是驱动程序模型中的一个核心数据结构,与sysfs文件系统自然的绑定在一起:——每个kobject对应sysfs文件系统中的一个目录。kobject往往被嵌入到设备驱动程序模型中的组件中,如总线,设备和驱动程序的描述符。kobject的作用是,为所属“容器”提供
.引用计数器
.维持容器的层次列表或组
.为容器的属性提供一种用户态查看的视图
kset是同类型kobject结构的一个集合体,通过kset数据结构可将kobjects组成一棵层次树。

struct bus_type { const char *name; //总线类型的名称 struct bus_attribute *bus_attrs; //总线属性 struct device_attribute *dev_attrs; //设备属性 struct driver_attribute *drv_attrs; //驱动属性 int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*suspend_late)(struct device *dev, pm_message_t state); int (*resume_early)(struct device *dev); int (*resume)(struct device *dev); struct dev_pm_ops *pm; struct bus_type_private *p; }; struct bus_type_private { struct kset subsys; //该总线的subsystem 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; }; struct bus_attribute { struct attribute attr; ssize_t (*show)(struct bus_type *bus, char *buf); ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count); };

subsys描述该总线的子系统,subsys是一个kset结构,他连接到一个全局变量kset bus_subsys中。这样,每一根总线系统都会通过bus_subsys结构连接起来。kset *devices_kset是指向该总线所有设备的集合的指针,kset *drivers_kset是指向该总线所有驱动的集合的指针。该总线上的设备和驱动分别用一个链表连接在一起,分别是klist_devices,klist_drivers。每次都用kset *drivers_kset,kset *devices_kset遍历所有设备/驱动很麻烦,用klist比较直接方便。

struct device { struct klist klist_children; struct klist_node knode_parent; /* node in sibling list */ struct klist_node knode_driver; struct klist_node knode_bus; struct device *parent; struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ unsigned uevent_suppress:1; const char *init_name; /* initial name of the device */ struct device_type *type; struct semaphore sem; /* semaphore to synchronize calls to * its driver. */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *driver_data; /* data private to the driver */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata; dev_t devt; /* dev_t, creates the sysfs "dev" */ spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class *class; struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); }; struct device_private { struct klist klist_children; struct klist_node knode_parent; struct klist_node knode_driver; struct klist_node knode_bus; struct device *device; }; struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); };

需要注意的是,总线也是设备,也必须按设备注册。这里的parent是指该设备所属的父设备,struct kobject kobj;表示该设备并把它连接到结构体系中的kobject。请注意,作为一个通用准则,device->kobj->parent与&device->parent->kobj是相同的。bus_id是在总线上唯一标识该设备的字符串。struct bus_type *bus;标识了该设备连接在何种类型的总线上。struct device_driver *driver;管理该设备的驱动。void (*release)(struct device *dev);当指向设备的最后一个引用被删除时,内核调用该方法。它将从内嵌的kobject的release方法中调用。device_private中的knode_parent,knode_driver,knode_bus分别是挂入parent,驱动,总线链表中的指针。

struct device_driver { const char *name; //设备驱动程序的名称 struct bus_type *bus; //该驱动所管理的设备挂接的总线类型 struct module *owner; const char *mod_name; /* used for built-in modules */ int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); struct attribute_group **groups; struct dev_pm_ops *pm; struct driver_private *p; }; struct driver_private { struct kobject kobj; struct klist klist_devices; //该驱动所管理的设备链表头 struct klist_node knode_bus; //挂入总线链表中的指针 struct module_kobject *mkobj; struct device_driver *driver; }; struct driver_attribute { struct attribute attr; ssize_t (*show)(struct device_driver *driver, char *buf); ssize_t (*store)(struct device_driver *driver, const char *buf, size_t count); };

name指向驱动的名字,上边的device中也有一个名为bus_id的字符数组。查看一下,struct bus_type中有一个match,函数,这个是干什么用的呢。设备有了驱动才可以工作,只有驱动没有设备也是不行,驱动和设备需要关联上,这就需要这个match函数。驱动和设备是通过name来管理的,所以在match函数中要比较device的bus_id和driver中的name是否相等。如果相等,就说明驱动和设备互相找到了,这时device_driver中的probe函数被调用。我下边的例子中是这样实现的:
static ssize_t show_driver_author(struct device_driver *driver, char *buf){ return snprintf(buf, PAGE_SIZE, "%s/n", author); }

下面是一个测试程序:

BUS:

#include <linux/module.h> #include <linux/init.h> #include <linux/string.h> #include <linux/device.h> #include <linux/kernel.h> static char *author = "LiWanPeng"; static ssize_t show_bus_author(struct bus_type *bus, char *buf){ return snprintf(buf, PAGE_SIZE, "%s/n", author); } void my_bus_release(struct device *dev){ printk(KERN_DEBUG "my bus release/n"); } static int virtual_bus_match(struct device *dev, struct device_driver *drv){ return !strncmp(dev->bus_id, drv->name, strlen(drv->name)); } struct bus_type virtual_bus = { .name = "my_bus", .match = virtual_bus_match, }; struct device my_bus = { .init_name = "my_bus0", .release = my_bus_release, }; EXPORT_SYMBOL(my_bus); EXPORT_SYMBOL(virtual_bus); static BUS_ATTR(author, S_IRUGO, show_bus_author, NULL); static int __init bus_init(void){ int ret; ret = bus_register(&virtual_bus); if(ret) return ret; if(bus_create_file(&virtual_bus, &bus_attr_author)) printk(KERN_NOTICE "Unable to create author attribute/n"); ret = device_register(&my_bus); if(ret) printk(KERN_NOTICE "Fail to register device/n"); printk("bus regiter success/n"); return ret; } static void __exit bus_exit(void){ bus_unregister(&virtual_bus); device_unregister(&my_bus); } module_init(bus_init); module_exit(bus_exit); MODULE_LICENSE("GPL");

DEVICE:

#include <linux/module.h> #include <linux/init.h> #include <linux/string.h> #include <linux/device.h> char *author = "LiWanPeng"; extern struct bus_type virtual_bus; extern struct device my_bus; static ssize_t show_device_author(struct device *dev, struct device_attribute *attr, char *buf){ return snprintf(buf, PAGE_SIZE, "%s/n", author); } void virtual_device_release(struct device *dev){ printk("virtual_device is released/n"); } struct device virtual_device ={ .bus_id = "my_dev", .bus = &virtual_bus, .parent = &my_bus, .release = virtual_device_release, }; static DEVICE_ATTR(author, S_IRUGO, show_device_author, NULL); static int __init device_init(void){ int ret; ret = device_register(&virtual_device); if(ret) return ret; if(device_create_file(&virtual_device, &dev_attr_author)) printk(KERN_NOTICE "Unable to create author attribute/n"); printk("device register success/n"); return ret; } static void __exit device_exit(void){ device_unregister(&virtual_device); } module_init(device_init); module_exit(device_exit); MODULE_AUTHOR("liwanpeng"); MODULE_LICENSE("GPL");

DRIVER:

#include <linux/module.h> #include <linux/init.h> #include <linux/string.h> #include <linux/device.h> #include <linux/kernel.h> extern struct bus_type virtual_bus; char *author = "LiWanPeng"; static ssize_t show_driver_author(struct device_driver *driver, char *buf){ return snprintf(buf, PAGE_SIZE, "%s/n", author); } int my_driver_remove(struct device *dev){ printk("driver is removed/n"); return 0; } int my_driver_probe(struct device *dev){ printk("driver can handle the device/n"); return 0; } struct device_driver virtual_driver = { .name = "my_dev", .bus = &virtual_bus, .probe = my_driver_probe, .remove = my_driver_remove, }; static DRIVER_ATTR(author, S_IRUGO, show_driver_author, NULL); static int __init my_driver_init(void){ int ret; ret = driver_register(&virtual_driver); if(ret) return ret; if(driver_create_file(&virtual_driver, &driver_attr_author)) printk(KERN_NOTICE "Unable to create author attribute/n"); printk("driver register success/n"); return ret; } static void __exit my_driver_exit(void){ driver_unregister(&virtual_driver); } module_init(my_driver_init); module_exit(my_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("liwanpeng");

Makefile:

ifneq ($(KERNELRELEASE),) obj-m:= driver.o bus.o device.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clear: rm -rf *.o endif

测试:

root@hacker:/home/hacker/program# dmesg [ 500.120888] bus regiter success [ 503.635832] device register success [ 515.237701] driver can handle the device [ 515.237772] driver register success root@hacker:/home/hacker/program# dmesg [ 627.552494] bus regiter success [ 631.652273] driver register success [ 641.867854] driver can handle the device [ 641.867861] device register success root@hacker:/sys/bus/my_bus/drivers/my_dev# ls -l total 0 -r--r--r-- 1 root root 4096 2011-05-06 22:46 author --w------- 1 root root 4096 2011-05-06 22:46 bind lrwxrwxrwx 1 root root 0 2011-05-06 22:46 my_dev -> ../../../../devices/my_bus0/my_dev --w------- 1 root root 4096 2011-05-06 22:46 uevent --w------- 1 root root 4096 2011-05-06 22:46 unbind root@hacker:/sys/bus/my_bus/drivers/my_dev# cat author LiWanPeng

参考:
《Linux设备驱动程序(第三版)》
《Linux那些事儿之我是USB》

Linux常见驱动源码分析(kernel hacker修炼之道)--李万鹏 李万鹏 IBM Linux Technology Center kernel team 驱动资料清单内容如下: Linux设备模型(中)之上容器.pdf Linux设备模型(上)之底模型.pdf Linux驱动修炼之道-驱动中一些常见的宏.pdf Linux驱动修炼之道-内存映射.pdf Linux驱动修炼之道-看门狗框架源码分析.pdf Linux驱动修炼之道-触摸屏驱动之s3c2410_ts源码分析.pdf Linux驱动修炼之道-SPI驱动框架源码分析().pdf Linux驱动修炼之道-SPI驱动框架源码分析().pdf Linux驱动修炼之道-SPI驱动框架源码分析().pdf Linux驱动修炼之道-RTC子系统框架源码分析.pdf Linux驱动修炼之道-platform.pdf Linux驱动修炼之道-LCD背光gpio控制.pdf Linux驱动修炼之道-INPUT子系统().pdf Linux驱动修炼之道-INPUT子系统().pdf Linux驱动修炼之道-framebuffer(中).pdf Linux驱动修炼之道-framebuffer(下).pdf Linux驱动修炼之道-framebuffer(上).pdf Linux驱动修炼之道-DMA框架源码分析().pdf Linux驱动修炼之道-DMA框架源码分析().pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼之道-clock框架.pdf Linux驱动修炼之道-ADC驱动.pdf Linux内核访问外设I O资源的方式.pdf LINUX内核USB子系统学习笔记之初识USB.pdf kernel hacker修炼之道之驱动-流水灯.pdf kernel hacker修炼之道之驱动-混杂设备.pdf kernel hacker修炼之道之驱动-按键.pdf kernel hacker修炼之道之PCI subsystem().pdf kernel hacker修炼之道之PCI subsystem().pdf kernel hacker修炼之道之PCI subsystem().pdf kernel hacker修炼之道之PCI subsystem().pdf kernel hacker修炼之道之PCI subsystem().pdf
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值