设备基本结构
请参阅内核文档以了解 struct device 的结构。
编程接口
发现设备的总线驱动程序使用以下方法将设备注册到核心:
int device_register(struct device *dev);
总线应该初始化以下字段:
- parent
- name
- bus_id
- bus
当设备的引用计数降至 0 时,设备将从核心中移除。可以使用以下方法调整引用计数:
struct device *get_device(struct device *dev);
void put_device(struct device *dev);
如果引用计数尚未为 0(即设备正在被移除的过程中),get_device()
将返回传递给它的 struct device
指针。
驱动程序可以使用以下方法访问设备结构中的锁:
void lock_device(struct device *dev);
void unlock_device(struct device *dev);
属性
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);
};
设备的属性可以通过设备驱动程序通过 sysfs 导出。
有关 sysfs 工作原理的更多信息,请参阅 sysfs - The filesystem 以导出内核对象。
如《Everything you never wanted to know about kobjects, ksets, and ktypes》中所述,必须在生成 KOBJ_ADD uevent 之前创建设备属性。唯一的实现方式是定义一个属性组。
属性使用名为 DEVICE_ATTR
的宏声明:
#define DEVICE_ATTR(name, mode, show, store)
示例:
static DEVICE_ATTR(type, 0444, type_show, NULL);
static DEVICE_ATTR(power, 0644, power_show, power_store);
对于常见的 mode 值,有可用的辅助宏,因此上述示例可以简化为:
static DEVICE_ATTR_RO(type);
static DEVICE_ATTR_RW(power);
这声明了两个 struct device_attribute
类型的结构,分别命名为 'dev_attr_type' 和 'dev_attr_power'。这两个属性可以组织如下成为一个组:
static struct attribute *dev_attrs[] = {
&dev_attr_type.attr,
&dev_attr_power.attr,
NULL,
};
static struct attribute_group dev_group = {
.attrs = dev_attrs,
};
static const struct attribute_group *dev_groups[] = {
&dev_group,
NULL,
};
对于单个组的常见情况,有一个可用的辅助宏,因此上述两个结构可以使用以下方式声明:
ATTRIBUTE_GROUPS(dev);
然后,可以通过在调用 device_register()
之前设置 struct device
中的组指针来将这些组关联到设备:
dev->groups = dev_groups;
device_register(dev);
device_register()
函数将使用 'groups' 指针创建设备属性,而 device_unregister()
函数将使用此指针移除设备属性。
警告:虽然内核允许在任何时候对设备调用 device_create_file()
和 device_remove_file()
,但用户空间对属性创建时间有严格的期望。当在内核中注册新设备时,将生成一个 uevent 以通知用户空间(如 udev)有新设备可用。如果在设备注册后添加属性,则用户空间将不会收到通知,也不会知道新属性。
这对于需要在驱动程序探测时为设备发布附加属性的设备驱动程序非常重要。如果设备驱动程序简单地在传递给它的设备结构上调用 device_create_file()
,则用户空间将永远不会收到新属性的通知。