linux 设备模型之设备驱动

设备模型跟踪所有对系统已知的驱动. 这个跟踪的主要原因是使驱动核心能匹配驱动和新

设备. 一旦驱动在系统中是已知的对象, 但是, 许多其他的事情变得有可能. 设备驱动可

输出和任何特定设备无关的信息和配置变量, 例如:

驱动由下列结构定义:

struct device_driver {

char *name;

struct bus_type *bus;

struct kobject kobj;

struct list_head devices;

int (*probe)(struct device *dev);

int (*remove)(struct device *dev);

void (*shutdown) (struct device *dev);

};

再一次, 几个结构成员被忽略( 全部内容见 <linux/device.h> ). 这里, name 是驱动的

名子( 它在 sysfs 中出现 ), bus 是这个驱动使用的总线类型, kobj 是必然的 kobject,

devices 是当前绑定到这个驱动的所有设备的列表, probe 是一个函数被调用来查询一个

特定设备的存在(以及这个驱动是否可以使用它), remove 当设备从系统中去除时被调用,

shutdown 在关闭时被调用来关闭设备.

使用 device_driver 结构的函数的形式, 现在应当看来是类似的(因此我们快速涵盖它

们). 注册函数是:

int driver_register(struct device_driver *drv);

void driver_unregister(struct device_driver *drv);

通常的属性结构在:

struct driver_attribute {

struct attribute attr;

ssize_t (*show)(struct device_driver *drv, char *buf);

ssize_t (*store)(struct device_driver *drv, const char *buf,

size_t count);

};

DRIVER_ATTR(name, mode, show, store);

以及属性文件以通常的方法创建:

int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);

void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);

bus_type 结构含有一个成员( drv_attrs ) 指向一套缺省属性, 对所有关联到这个总线

的驱动都创建.

驱动结构嵌入

如同大部分驱动核心结构的情形, device_driver 结构常常被发现嵌到一个更高级的, 总

线特定的结构. lddbus 子系统不会和这样的趋势相反, 因此它已定义了它自己的

ldd_driver 结构:

struct ldd_driver {

char *version;

struct module *module;

struct device_driver driver;

struct driver_attribute version_attr;

};

#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);

这里, 我们要求每个驱动提供特定当前软件版本, 并且 lddbus 输出这个版本字串为它知

道的每个驱动. 总线特定的驱动注册函数是:

int register_ldd_driver(struct ldd_driver *driver)

{

int ret;

driver->driver.bus = &ldd_bus_type;

ret = driver_register(&driver->driver);

if (ret)

return ret;

driver->version_attr.attr.name = "version";

driver->version_attr.attr.owner = driver->module;

driver->version_attr.attr.mode = S_IRUGO;

driver->version_attr.show = show_version;

driver->version_attr.store = NULL;

return driver_create_file(&driver->driver, &driver->version_attr);

}

这个函数的第一部分只注册低级的 device_driver 结构到核心; 剩下的建立版本属性.

因为这个属性在运行时被创建, 我们不能使用 DRIVER_ATTR 宏; 反之,

driver_attribute 结构必须手工填充. 注意我们设定属性的拥有者为驱动模块, 不是

lddbus 模块; 这样做的理由是可以在为这个属性的 show 函数的实现中见到:

static ssize_t show_version(struct device_driver *driver, char *buf)

{

struct ldd_driver *ldriver = to_ldd_driver(driver);

sprintf(buf, "%s\n", ldriver->version);

return strlen(buf);

}

有人可能认为属性拥有者应当是 lddbus 模块, 因为实现这个属性的函数在那里定义. 这

个函数, 但是, 是使用驱动自身所创建的 ldd_driver 结构. 如果那个结构在一个用户空

间进程试图读取版本号时要消失, 事情会变得麻烦. 指定驱动模块作为属性的拥有者阻止

了模块被卸载, 在用户空间保持属性文件打开时. 因为每个驱动模块创建一个对 lddbus

模块的引用, 我们能确信 lddbus 不会在一个不合适的时间被卸载.

为完整起见, sculld 创建它的 ldd_driver 结构如下:

static struct ldd_driver sculld_driver = { .version = "$Revision: 1.1

$", .module = THIS_MODULE, .driver = { .name = "sculld", }, };

一个简单的对 register_ldd_driver 的调用添加它到系统中. 一旦完成初始化, 驱动信

息可在 sysfs 中见到:

$ tree /sys/bus/ldd/drivers

/sys/bus/ldd/drivers

`-- sculld

|-- sculld0 -> ../../../../devices/ldd0/sculld0

|-- sculld1 -> ../../../../devices/ldd0/sculld1

|-- sculld2 -> ../../../../devices/ldd0/sculld2

|-- sculld3 -> ../../../../devices/ldd0/sculld3

`-- version

[46] 这个总线的逻辑名子, 当然, 应当是"sbus", 但是这个名子已经被一个真实的, 物理总

线采用.

相关推荐
热爱嵌入式的小许3 小时前
Linux基础项目开发1:量产工具——显示系统
linux·运维·服务器·韦东山量产工具
代码雕刻家4 小时前
数据结构-3.9.栈在递归中的应用
c语言·数据结构·算法
Kalika0-06 小时前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
代码雕刻家6 小时前
课设实验-数据结构-单链表-文教文化用品品牌
c语言·开发语言·数据结构
龙图:会赢的6 小时前
[C语言]--编译和链接
c语言·开发语言
韩楚风7 小时前
【linux 多进程并发】linux进程状态与生命周期各阶段转换,进程状态查看分析,助力高性能优化
linux·服务器·性能优化·架构·gnu
陈苏同学7 小时前
4. 将pycharm本地项目同步到(Linux)服务器上——深度学习·科研实践·从0到1
linux·服务器·ide·人工智能·python·深度学习·pycharm
Ambition_LAO7 小时前
解决:进入 WSL(Windows Subsystem for Linux)以及将 PyCharm 2024 连接到 WSL
linux·pycharm
Pythonliu77 小时前
茴香豆 + Qwen-7B-Chat-Int8
linux·运维·服务器
你疯了抱抱我7 小时前
【RockyLinux 9.4】安装 NVIDIA 驱动,改变分辨率,避坑版本。(CentOS 系列也能用)
linux·运维·centos