设备模型跟踪所有对系统已知的驱动. 这个跟踪的主要原因是使驱动核心能匹配驱动和新
设备. 一旦驱动在系统中是已知的对象, 但是, 许多其他的事情变得有可能. 设备驱动可
输出和任何特定设备无关的信息和配置变量, 例如:
驱动由下列结构定义:
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", 但是这个名子已经被一个真实的, 物理总
线采用.