Linux驱动之设备模型(5)
2012-03-18 16:05
197 查看
6.设备
6.1 设备
l Linux设备模型中每一个设备用device结构来表示
struct device {
struct device *parent; /* 父设备,通常是某种总线或者是宿主设备 */
struct device_private *p; /* 私有数据指针:子设备链表,父设备链表节点,驱动程序链表节点,总线链表节点 */
struct kobject kobj; /* 连接到结构体系中的kobject */
const char *init_name; /* initial name of the device */
const struct device_type *type; /* 设备类型 */
struct bus_type *bus; /* 依附的总线 */
struct device_driver *driver; /* 关联的驱动程序 */
void *platform_data; /* Platform specific data*/
dev_t devt; /* dev_t, creates the sysfs"dev" */
struct klist_node knode_class; /* */
struct class *class; /* 设备所属的类 */
const struct attribute_group **groups; /* optional groups */
void (*release)(structdevice *dev); /* 当引用计数(kobject->kref)减为0时调用 */
};
6.2 设备属性
l 设备属性由device_attribute来表示
structdevice_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);
};
DEVICE_ATTR(_name,_mode, _show, _store)
l 属性操作
n 添加属性
int device_create_file(struct device *device,
conststruct device_attribute *entry);
n 删除属性
void device_remove_file(struct device *dev,
const struct device_attribute*attr);
6.3 设备注册和注销
l 设备注册和注销
intdevice_register(struct device *dev);
voiddevice_unregister(struct device *dev);
l 设备注册分析
注册函数由初始化设备(device_initialize)和添加设备到系统(device_add)中两步构成,主要分析一下第二步
n device_add,添加设备
int device_add(structdevice *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
/* 引用计数加1 */
dev = get_device(dev);
/*
* for statically allocated devices, whichshould all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev,"%s", dev->init_name);
dev->init_name = NULL;
}
/* 增加父设备的引用计数 */
parent = get_device(dev->parent);
setup_parent(dev, parent);
/* 把内嵌的kobject注册到设备模型中 */
error = kobject_add(&dev->kobj,dev->kobj.parent, NULL);
/*创建属性文件uevent */
error = device_create_file(dev,&uevent_attr);
/* 如果定义了devt,则产生dev属性 */
if (MAJOR(dev->devt)) {
error = device_create_file(dev,&devt_attr);
}
/* 创建属性文件 */
error = device_add_class_symlinks(dev);
error = device_add_attrs(dev);
/* 把设备添加到总线上,这个是重点,稍后分析*/
error = bus_add_device(dev);
/*产生KOBJ_ADD uevent */
kobject_uevent(&dev->kobj,KOBJ_ADD);
/* 给设备探测相应的驱动 */
bus_probe_device(dev);
/* 如果设备有父设备,将它加入parent的子设备链中 */
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
/* 如果设备附属于某个类,则需完成相应工作*/
if (dev->class) {
/* 把设备添加到class的设备链表中,完成关联*/
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces thatthe device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if(class_intf->add_dev)
class_intf->add_dev(dev,class_intf);
}
}
n bus_add_device
intbus_add_device(struct device *dev)
{
/* 引用计数加一 */
struct bus_type *bus =bus_get(dev->bus);
if (bus) {
/* 创建相应的属性文件 */
error = device_add_attrs(bus,dev);
error =sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj,dev_name(dev));
error =sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj,"subsystem");
/* 把设备加入到总线的设备链中,这步才是重点*/
klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);
}
}
n bus_probe_device,为设备探测相应的驱动
bus_probe_device()->device_attach()
intdevice_attach(struct device *dev)
{
if (dev->driver) {
/* 如果设备已经依附于某个驱动,进行绑定 */
ret =device_bind_driver(dev);
} else {
/* 遍历bus->p->klist_drivers寻找匹配的驱动,然后执行回调函数__device_attach */
ret = bus_for_each_drv(dev->bus, NULL, dev,__device_attach);
}
out_unlock:
device_unlock(dev);
return ret;
}
__device_attach()->driver_probe_device()->really_probe()->bus->probe()->drv->probe() 总线中定义的probe函数会优先执行,如果总线中没有定义probe才会执行驱动中定义的probe
6.4 实例解析
创建一个设备和它的属性version,并将此设备挂接到之前创建的总线上
/*
* for learn device
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
extern struct bus_type scbus_type;
extern struct device scbus;
static char *Version = "revision1.0";
/* 析构函数,引用计数减为0时调用 */
void screlease(struct device *dev)
{
printk("scbusrelease\n");
}
struct device scdevice = {
.parent = &scbus, /* 父设备,此处为依附的总线 */
.init_name = "scdevice0",
.bus = &scbus_type, /* 依附的总线类型 */
.release = screlease,
};
/*
* export device attribute
*/
static ssize_t show_device_version(structdevice *dev,
structdevice_attribute *attr, char *buf)
{
returnsprintf(buf, "%s\n", Version);
}
DEVICE_ATTR(version, 0666,show_device_version, NULL);
static int __init scdevice_init(void)
{
intret;
/*注册设备 */
ret= device_register(&scdevice);
if(ret)
returnret;
/*创建属性 */
ret= device_create_file(&scdevice, &dev_attr_version);
if(ret)
gotoerr_create;
printk("Createa scdevice");
return0;
err_create:
device_unregister(&scdevice);
returnret;
}
static void __exit scdevice_exit(void)
{
device_remove_file(&scdevice,&dev_attr_version);
device_unregister(&scdevice);
printk("Removea scdevice");
}
module_init(scdevice_init);
module_exit(scdevice_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");
试验结果:
6.1 设备
l Linux设备模型中每一个设备用device结构来表示
struct device {
struct device *parent; /* 父设备,通常是某种总线或者是宿主设备 */
struct device_private *p; /* 私有数据指针:子设备链表,父设备链表节点,驱动程序链表节点,总线链表节点 */
struct kobject kobj; /* 连接到结构体系中的kobject */
const char *init_name; /* initial name of the device */
const struct device_type *type; /* 设备类型 */
struct bus_type *bus; /* 依附的总线 */
struct device_driver *driver; /* 关联的驱动程序 */
void *platform_data; /* Platform specific data*/
dev_t devt; /* dev_t, creates the sysfs"dev" */
struct klist_node knode_class; /* */
struct class *class; /* 设备所属的类 */
const struct attribute_group **groups; /* optional groups */
void (*release)(structdevice *dev); /* 当引用计数(kobject->kref)减为0时调用 */
};
6.2 设备属性
l 设备属性由device_attribute来表示
structdevice_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);
};
DEVICE_ATTR(_name,_mode, _show, _store)
l 属性操作
n 添加属性
int device_create_file(struct device *device,
conststruct device_attribute *entry);
n 删除属性
void device_remove_file(struct device *dev,
const struct device_attribute*attr);
6.3 设备注册和注销
l 设备注册和注销
intdevice_register(struct device *dev);
voiddevice_unregister(struct device *dev);
l 设备注册分析
注册函数由初始化设备(device_initialize)和添加设备到系统(device_add)中两步构成,主要分析一下第二步
n device_add,添加设备
int device_add(structdevice *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
/* 引用计数加1 */
dev = get_device(dev);
/*
* for statically allocated devices, whichshould all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev,"%s", dev->init_name);
dev->init_name = NULL;
}
/* 增加父设备的引用计数 */
parent = get_device(dev->parent);
setup_parent(dev, parent);
/* 把内嵌的kobject注册到设备模型中 */
error = kobject_add(&dev->kobj,dev->kobj.parent, NULL);
/*创建属性文件uevent */
error = device_create_file(dev,&uevent_attr);
/* 如果定义了devt,则产生dev属性 */
if (MAJOR(dev->devt)) {
error = device_create_file(dev,&devt_attr);
}
/* 创建属性文件 */
error = device_add_class_symlinks(dev);
error = device_add_attrs(dev);
/* 把设备添加到总线上,这个是重点,稍后分析*/
error = bus_add_device(dev);
/*产生KOBJ_ADD uevent */
kobject_uevent(&dev->kobj,KOBJ_ADD);
/* 给设备探测相应的驱动 */
bus_probe_device(dev);
/* 如果设备有父设备,将它加入parent的子设备链中 */
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
/* 如果设备附属于某个类,则需完成相应工作*/
if (dev->class) {
/* 把设备添加到class的设备链表中,完成关联*/
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces thatthe device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if(class_intf->add_dev)
class_intf->add_dev(dev,class_intf);
}
}
n bus_add_device
intbus_add_device(struct device *dev)
{
/* 引用计数加一 */
struct bus_type *bus =bus_get(dev->bus);
if (bus) {
/* 创建相应的属性文件 */
error = device_add_attrs(bus,dev);
error =sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj,dev_name(dev));
error =sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj,"subsystem");
/* 把设备加入到总线的设备链中,这步才是重点*/
klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);
}
}
n bus_probe_device,为设备探测相应的驱动
bus_probe_device()->device_attach()
intdevice_attach(struct device *dev)
{
if (dev->driver) {
/* 如果设备已经依附于某个驱动,进行绑定 */
ret =device_bind_driver(dev);
} else {
/* 遍历bus->p->klist_drivers寻找匹配的驱动,然后执行回调函数__device_attach */
ret = bus_for_each_drv(dev->bus, NULL, dev,__device_attach);
}
out_unlock:
device_unlock(dev);
return ret;
}
__device_attach()->driver_probe_device()->really_probe()->bus->probe()->drv->probe() 总线中定义的probe函数会优先执行,如果总线中没有定义probe才会执行驱动中定义的probe
6.4 实例解析
创建一个设备和它的属性version,并将此设备挂接到之前创建的总线上
/*
* for learn device
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
extern struct bus_type scbus_type;
extern struct device scbus;
static char *Version = "revision1.0";
/* 析构函数,引用计数减为0时调用 */
void screlease(struct device *dev)
{
printk("scbusrelease\n");
}
struct device scdevice = {
.parent = &scbus, /* 父设备,此处为依附的总线 */
.init_name = "scdevice0",
.bus = &scbus_type, /* 依附的总线类型 */
.release = screlease,
};
/*
* export device attribute
*/
static ssize_t show_device_version(structdevice *dev,
structdevice_attribute *attr, char *buf)
{
returnsprintf(buf, "%s\n", Version);
}
DEVICE_ATTR(version, 0666,show_device_version, NULL);
static int __init scdevice_init(void)
{
intret;
/*注册设备 */
ret= device_register(&scdevice);
if(ret)
returnret;
/*创建属性 */
ret= device_create_file(&scdevice, &dev_attr_version);
if(ret)
gotoerr_create;
printk("Createa scdevice");
return0;
err_create:
device_unregister(&scdevice);
returnret;
}
static void __exit scdevice_exit(void)
{
device_remove_file(&scdevice,&dev_attr_version);
device_unregister(&scdevice);
printk("Removea scdevice");
}
module_init(scdevice_init);
module_exit(scdevice_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");
试验结果:
相关文章推荐
- linux终端tty设备驱动模型分析
- Linux总线设备驱动模型学习
- Linux 与 Windows 的设备驱动模型对比:架构、API 和开发环境比较
- Linux内核大讲堂之设备驱动的基石驱动模型(5)
- LINUX设备驱动之设备模型四--device&driver&bus(二)
- linux 2.6 总线、设备、驱动模型
- 慢慢学Linux驱动开发,第五篇,初探设备模型概念
- Linux平台总线驱动设备模型
- 宋宝华-谈Linux的总线、设备、驱动模型
- Linux设备模型之tty驱动架构分析
- linux ------ 设备驱动模型之二(bus, device, driver)
- Linux字符设备驱动模型--字符设备的注册
- Linux平台总线驱动设备模型
- linux内核部件分析之——设备驱动模型之class
- 嵌入式 linux下kernel代码中设备驱动模型之device-driver
- Linux网络设备驱动 _驱动模型
- linux 设备驱动模型 class 设备结点文件 热插拔
- linux设备模型之uart驱动架构分析
- 驱动该如何入门 关于file_operations和Linux设备模型
- Linux设备模型之tty驱动架构分析(转载)