设备模型中有三个重要组件,总线(bus_type),设备(device),驱动(driver)。这是一个抽象模型,其他所有的具体总线类型都是建立在这个基础之上的,如I2C,platform等。
一,总线
1.总线基本数据类型,简单列举和总线相关的一些数据类型
总线bus_type,其定义在include/linux/device.h
-
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 (*resume)(struct device *dev); //恢复函数
-
-
const struct dev_pm_ops *pm; //电源管理的函数集
-
-
struct bus_type_private *p; //总线私有数据
-
};
总线属性类型
-
struct bus_attribute {
-
struct attribute attr; //和kobject中属性是一个定义,在include/linux/sysfs.h
-
ssize_t (*show)(struct bus_type *bus, char *buf); //属性读取函数
-
ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count); //属性写入函数
-
};
生成一个总线属性对象。一般可以使用宏,
-
#define BUS_ATTR(_name, _mode, _show, _store) \
-
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
-
-
#define __ATTR(_name,_mode,_show,_store) { \
-
.attr = {.name = __stringify(_name), .mode = _mode }, \
-
.show = _show, \
-
.store = _store, \
-
}
总线bus_type上的match函数,如果定义了一个总线,那就必须实现match函数,此函数给出其总线上设备和驱动匹配的方法。
总线bus_type上的uevent函数,热插拔事件中传递环境变量参数的函数
总线私有数据类型,bus_type_private,这个数据结构中主要是一些kset类型的容器。
-
/**
-
* struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure.
-
*
-
* @subsys - the struct kset that defines this bus. This is the main kobject
-
* @drivers_kset - the list of drivers associated with this bus
-
* @devices_kset - the list of devices associated with this bus
-
* @klist_devices - the klist to iterate over the @devices_kset
-
* @klist_drivers - the klist to iterate over the @drivers_kset
-
* @bus_notifier - the bus notifier list for anything that cares about things
-
* on this bus.
-
* @bus - pointer back to the struct bus_type that this structure is associated
-
* with.
-
*
-
* This structure is the one that is the actual kobject allowing struct
-
* bus_type to be statically allocated safely. Nothing outside of the driver
-
* core should ever touch these fields.
-
*/
-
struct bus_type_private {
-
struct kset subsys; //总线bus子系统的kset
-
struct kset *drivers_kset; //总线上所有驱动的kset
-
struct kset *devices_kset; //总线上所有设备的kset
-
struct klist klist_devices; //总线上所有的设备的list,和devices_kset里的list相同
-
struct klist klist_drivers; //总线上所有的驱动的list,和drivers_kset里的list相同
-
struct blocking_notifier_head bus_notifier; //
-
unsigned int drivers_autoprobe:1; //是否在驱动加载时自动,自动探测(probe)设备
-
struct bus_type *bus; //反指回该总线类型
-
};
2.总线注册
总线相关的基本数据类型看完之后,我们看如何注册一个总线(bus_type)。使用函数bus_register().在driver/base/bus.c中,注册一个bus总线将在sysfs中生成总线的目录,和其devices,drivers等目录以及它们的属性文件。
-
/**
-
* bus_register - register a bus with the system.
-
* @bus: bus.
-
*
-
* Once we have that, we registered the bus with the kobject
-
* infrastructure, then register the children subsystems it has:
-
* the devices and drivers that belong to the bus.
-
*/
-
int bus_register(struct bus_type *bus)
-
{
-
int retval;
-
struct bus_type_private *priv;
-
-
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); //分配一个总线私有数据(bus_type_private)对象
-
if (!priv)
-
return -ENOMEM;
-
-
priv->bus = bus; //私有数据中的bus反指回总线
-
bus->p = priv; //总线的私有数据指向这个生成的总线私有数据
-
-
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); //初始化通知链表
-
-
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //设置总结(私有数据)的kobject的名称
-
if (retval)
-
goto out;
-
-
priv->subsys.kobj.kset = bus_kset;
-
//设置总线私有数据中的kset的kobject(kset中的kobject是此kset中包含kokject的父kobject)的kset。
-
//bus_kset将在bus_init函数中生成对象实体(1)
-
priv->subsys.kobj.ktype = &bus_ktype; //设置属性,包含属性的默认方法
-
priv->drivers_autoprobe = 1; //开启自动侦测
-
-
retval = kset_register(&priv->subsys); //将总线私有数据的bus子系统kset注册
-
if (retval)
-
goto out;
-
-
retval = bus_create_file(bus, &bus_attr_uevent); //创建uevent的属性(bus_attr_uevent)文件
-
if (retval)
-
goto bus_uevent_fail;
-
-
priv->devices_kset = kset_create_and_add("devices", NULL,
-
&priv->subsys.kobj);
-
//创建并注册一个kset到sysfs中,名称为devices,父节点为总线kobject。
-
//即会在相应总线目录下创建一个devices目录。
-
if (!priv->devices_kset) {
-
retval = -ENOMEM;
-
goto bus_devices_fail;
-
}
-
-
priv->drivers_kset = kset_create_and_add("drivers", NULL, //同上,将创建drivers目录
-
&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); //配置了热插拔,则此函数会添加bus总线相关
-
if (retval)
-
goto bus_probe_files_fail;
-
-
retval = bus_add_attrs(bus); //为bus总线添加默认的属性(2)
-
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:
-
bus->p = NULL;
-
return retval;
-
}
(1)bus_init() 被__init修饰,将在内核初始化时执行。它生成了bus_kset对象,函数定义如下,
-
int __init buses_init(void)
-
{
-
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
-
if (!bus_kset)
-
return -ENOMEM;
-
return 0;
-
}
(2)为bus总线添加默认的属性,bus_add_attrs()
-
/**
-
* bus_add_attrs - Add default attributes for this bus.
-
* @bus: Bus that has just been registered.
-
*/
-
static int bus_add_attrs(struct bus_type *bus)
-
{
-
int error = 0;
-
int i;
-
-
if (bus->bus_attrs) { //如果此bus总线上配置了属性数组,则生成它们的文件
-
for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
-
error = bus_create_file(bus, &bus->bus_attrs[i]); //
-
if (error)
-
goto err;
-
}
-
}
-
done:
-
return error;
-
err:
-
while (--i >= 0)
-
bus_remove_file(bus, &bus->bus_attrs[i]);
-
goto done;
-
}
-
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
-
{
-
int error;
-
if (bus_get(bus)) {
-
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr); //将在bus总线目录下创建属性文件
-
bus_put(bus);
-
} else
-
error = -EINVAL;
-
return error;
-
}
二,设备
设备驱动模型中的每一个设备是由device结构体来描述的。
-
struct device {
-
struct device *parent; //父设备的指针
-
-
struct device_private *p; //设备的私有数据
-
-
struct kobject kobj; //设备的kobject
-
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 *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; //设备所属的类
-
const struct attribute_group **groups; /* optional groups */ //设备组属性
-
-
void (*release)(struct device *dev);
-
};
注册一个device结构(drvier/base/core.c)
-
int device_register(struct device *dev)
-
{
-
device_initialize(dev); //初始化device
-
return device_add(dev); //添加device
-
}
-
/**
-
* device_initialize - init device structure.
-
* @dev: device.
-
*
-
* This prepares the device for use by other layers by initializing
-
* its fields.
-
* It is the first half of device_register(), if called by
-
* that function, though it can also be called separately, so one
-
* may use @dev's fields. In particular, get_device()/put_device()
-
* may be used for reference counting of @dev after calling this
-
* function.
-
*
-
* NOTE: Use put_device() to give up your reference instead of freeing
-
* @dev directly once you have called this function.
-
*/
-
void device_initialize(struct device *dev)
-
{
-
dev->kobj.kset = devices_kset; //默认的device的kset (1)
-
//device_kset 在系统初始化时创建(driver/base/core.c,devices_init())。将生成/sys/device目录
-
kobject_init(&dev->kobj, &device_ktype); //初始化dev的kobject (2)
-
INIT_LIST_HEAD(&dev->dma_pools); //初始化dev链表
-
init_MUTEX(&dev->sem); //初始化互斥量
-
spin_lock_init(&dev->devres_lock);//
-
INIT_LIST_HEAD(&dev->devres_head);
-
device_init_wakeup(dev, 0); //电源管理相关
-
device_pm_init(dev); //电源管理相关
-
set_dev_node(dev, -1); //NUMA有关 不用管
-
}
-
(1)
-
int __init devices_init(void)
-
{
-
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
-
//创建的kset的名称为devices。uevent操作集device_uevent_ops,kset的父kobject为NULL
-
//这将在sys根目录下创建device目录
-
if (!devices_kset)
-
return -ENOMEM;
-
dev_kobj = kobject_create_and_add("dev", NULL);//
-
if (!dev_kobj)
-
goto dev_kobj_err;
-
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
-
if (!sysfs_dev_block_kobj)
-
goto block_kobj_err;
-
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
-
if (!sysfs_dev_char_kobj)
-
goto char_kobj_err;
-
-
return 0;
-
-
char_kobj_err:
-
kobject_put(sysfs_dev_block_kobj);
-
block_kobj_err:
-
kobject_put(dev_kobj);
-
dev_kobj_err:
-
kset_unregister(devices_kset);
-
return -ENOMEM;
-
}
-
(2)这个给设备(device)的kobject中的kobj_type的默认成员device_ktype将在这段有后分析。
-
/**
-
* device_add - add device to device hierarchy.
-
* @dev: device.
-
*
-
* This is part 2 of device_register(), though may be called
-
* separately _iff_ device_initialize() has been called separately.
-
*
-
* This adds @dev to the kobject hierarchy via kobject_add(), adds it
-
* to the global and sibling lists for the device, then
-
* adds it to the other relevant subsystems of the driver model.
-
*
-
* NOTE: _Never_ directly free @dev after calling this function, even
-
* if it returned an Always use put_device() to give up your
-
* reference instead.
-
*/
-
int device_add(struct device *dev)
-
{
-
struct device *parent = NULL;
-
struct class_interface *class_intf;
-
int error = -EINVAL;
-
-
dev = get_device(dev); //增加dev的引用
-
if (!dev)
-
goto done;
-
-
if (!dev->p) {
-
error = device_private_init(dev);
-
if (error)
-
goto done;
-
}
-
-
/*
-
* for statically allocated devices, which should 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); //将设置kobject的名字
-
dev->init_name = NULL;
-
}
-
-
if (!dev_name(dev))
-
goto name_error;
-
-
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
-
-
parent = get_device(dev->parent); //增加对父设备的引用,注意这里是device中的父。不是kobject中的父(1)
-
setup_parent(dev, parent);//将设置dev中的kobject的父。在后面的添加kobject中,没有parent将设置为kset的kobject
-
-
/* use parent numa_node */
-
if (parent)
-
set_dev_node(dev, dev_to_node(parent));
-
-
/* first, register with generic layer. */
-
/* we require the name to be set before, and pass NULL */
-
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);//添加dev的kobject,其init在device_initialize中
-
if (error)
-
goto Error;
-
-
/* notify platform of device entry */
-
if (platform_notify)
-
platform_notify(dev);
-
-
error = device_create_file(dev, &uevent_attr); //添加uevevt属性文件
-
if (error)
-
goto attrError;
-
-
if (MAJOR(dev->devt)) { //如果有主设备号
-
error = device_create_file(dev, &devt_attr); //创建设备下的一个默认属性文件。(1)
-
if (error)
-
goto ueventattrError;
-
-
error = device_create_sys_dev_entry(dev); //生成相应目录的连接文件
-
if (error)
-
goto devtattrError;
-
-
devtmpfs_create_node(dev); //
-
}
-
-
error = device_add_class_symlinks(dev); //生成class的连接文件
-
if (error)
-
goto SymlinkError;
-
error = device_add_attrs(dev); //生成类的属性文件和其他文件(?)
-
if (error)
-
goto AttrsError;
-
error = bus_add_device(dev); //将设备添加到其bus总线上,将在设备目录下与总线目录下生成相互指向的文件
-
if (error)
-
goto BusError;
-
error = dpm_sysfs_add(dev); //生成power节点在这个设备的目录下
-
if (error)
-
goto DPMError;
-
device_pm_add(dev); //添加为活跃设备链表
-
-
/* Notify clients of device addition. This call must come
-
* after dpm_sysf_add() and before kobject_uevent().
-
*/
-
if (dev->bus) //将添加这个总线上所有的设备
-
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
-
BUS_NOTIFY_ADD_DEVICE, dev);
-
-
kobject_uevent(&dev->kobj, KOBJ_ADD);
-
bus_probe_device(dev); // 为这个新的设备寻找对于驱动,在注册device时候会match相应的driver并probe,
-
//在后面我们将详细分析注册driver时也将match相应的device并probe,这两者的逻辑都差不多
-
if (parent)
-
klist_add_tail(&dev->p->knode_parent,
-
&parent->p->klist_children);
-
-
if (dev->class) { //设备的类以后再详细分析
-
mutex_lock(&dev->class->p->class_mutex);
-
/* tie the class to the device */
-
klist_add_tail(&dev->knode_class,
-
&dev->class->p->class_devices);
-
-
/* notify any interfaces that the 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);
-
mutex_unlock(&dev->class->p->class_mutex);
-
}
-
done:
-
put_device(dev);
-
return error;
-
DPMError:
-
bus_remove_device(dev);
-
BusError:
-
device_remove_attrs(dev);
-
AttrsError:
-
device_remove_class_symlinks(dev);
-
SymlinkError:
-
if (MAJOR(dev->devt))
-
device_remove_sys_dev_entry(dev);
-
devtattrError:
-
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);
-
name_error:
-
kfree(dev->p);
-
dev->p = NULL;
-
goto done;
-
}
(1)device_create_file此函数将在dev设备目录下创建attr的属性文件。
-
int device_create_file(struct device *dev, struct device_attribute *attr)
-
{
-
int error = 0;
-
if (dev)
-
error = sysfs_create_file(&dev->kobj, &attr->attr);
-
return error;
-
}
-
static struct device_attribute devt_attr =
-
__ATTR(dev, S_IRUGO, show_dev, NULL);
-
-
device_attribute
-
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);
-
};
-
#define __ATTR(_name,_mode,_show,_store) { \
-
.attr = {.name = __stringify(_name), .mode = _mode }, \
-
.show = _show, \
-
.store = _store, \
-
}
-
static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
-
char *buf)
-
{
-
return print_dev_t(buf, dev->devt);
-
}
-
#define print_dev_t(buffer, dev) \
-
sprintf((buffer), "%u:%u\n", MAJOR(dev), MINOR(dev))
以上代码看出,devt_attr类型是device_attribute。通过宏__ATTR创建了一个名为dev的属性。属性的操作方法show。功能是显示主次设备号。
我们再简单看一下bus_add_device,这个函数将device添加到其bus下
-
int bus_add_device(struct device *dev)
-
{
-
struct bus_type *bus = bus_get(dev->bus);
-
int error = 0;
-
-
if (bus) {
-
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
-
error = device_add_attrs(bus, dev); //添加这种总线下设备应有的属性文件
-
if (error)
-
goto out_put;
-
error = sysfs_create_link(&bus->p->devices_kset->kobj, //在bus目录下创建这个设备的连接文件
-
&dev->kobj, dev_name(dev));
-
if (error)
-
goto out_id;
-
error = sysfs_create_link(&dev->kobj, //在设备目录下创建指向总线的连接,连接文件名为subsystem
-
&dev->bus->p->subsys.kobj, "subsystem");
-
if (error)
-
goto out_subsys;
-
error = make_deprecated_bus_links(dev);
-
if (error)
-
goto out_deprecated;
-
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
-
}
-
return 0;
-
-
out_deprecated:
-
sysfs_remove_link(&dev->kobj, "subsystem");
-
out_subsys:
-
sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
-
out_id:
-
device_remove_attrs(bus, dev);
-
out_put:
-
bus_put(dev->bus);
-
return error;
-
}
设备的kobj_type
设备kobject的默认kobj_type,定义如下
-
static struct kobj_type device_ktype = {
-
.release = device_release, //设备kobject的默认release函数(1)
-
.sysfs_ops = &dev_sysfs_ops, //设备kobject的属性默认操作函数集(2)
-
};
(1)
-
static void device_release(struct kobject *kobj)
-
{
-
struct device *dev = to_dev(kobj); //取的包含具体设备kobject的device结构
-
struct device_private *p = dev->p;
-
-
if (dev->release) //如果有设备(device)结构的release指向的函数则调用
-
dev->release(dev);
-
else if (dev->type && dev->type->release) //否则,有设备(device)结构的device_type,并且它有release函数,则调用
-
dev->type->release(dev);
-
else if (dev->class && dev->class->dev_release)//否则,有设备(device)结构的class,并且它有release函数,则调用
-
dev->class->dev_release(dev);
-
else //如果都没有则警告
-
WARN(1, KERN_ERR "Device '%s' does not have a release() "
-
"function, it is broken and must be fixed.\n",
-
dev_name(dev));
-
kfree(p); //是否设备中的device_private数据
-
}
上面函数可以看出,device层中的kobject的release函数,只是一个中介,在设备的kobject被销毁时,函数将依次寻找并调用,dev->release;dev->type->release;dev->class->dev_release.中的函数。而这些函数则必须在具体的设备相关中实现。
(2)kobj_type中的sysfs_ops,是相应kobject的属性的操作函数集。
-
static struct sysfs_ops dev_sysfs_ops = {
-
.show = dev_attr_show, //对属性的读操作
-
.store = dev_attr_store, //对属性的写操作
-
};
-
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
-
{
-
struct device_attribute *dev_attr = to_dev_attr(attr); //取出包含attr的device_attribute类型变量
-
struct device *dev = to_dev(kobj); //取出包含kobj的device
-
ssize_t ret = -EIO;
-
-
if (dev_attr->show) //调用dev_attr中的show函数
-
ret = dev_attr->show(dev, dev_attr, buf);
-
if (ret >= (ssize_t)PAGE_SIZE) {
-
print_symbol("dev_attr_show: %s returned bad count\n",
-
(unsigned long)dev_attr->show);
-
}
-
return ret;
-
}
和上面一样,在device层中。这些属性函数都是一些中介作用。他是要调用device_attribute(设备属性结构)中的show,store函数。而device_attribute变量的实例是要在具体设备中实现的。
阅读(1110) | 评论(0) | 转发(0) |