linux 设备模型之设备
在最低层, Linux 系统中的每个设备由一个 struct device 代表:
 struct device { struct device *parent; struct kobject kobj; char bus_id[BUS_ID_SIZE];
 struct bus_type *bus; struct device_driver *driver; void *driver_data; void
 (*release)(struct device *dev); /* Several fields omitted */ };
 有许多其他的 struct device 成员只对设备核心代码感兴趣. 但是, 这些成员值得了解:
 struct device *parent
设备的 "parent" 设备 -- 它所附着到的设备. 在大部分情况, 一个父设备是某种
 总线或者主控制器. 如果 parent 是 NULL, 设备是一个顶层设备, 这常常不是你
 所要的.
 struct kobject kobj;
 代表这个设备并且连接它到层次中的 kobject. 注意, 作为一个通用的规则,
 device->kobj->parent 等同于 device->parent->kobj.
 char bus_id[BUS_ID_SIZE];
 唯一确定这个总线上的设备的字符串. PCI 设备, 例如, 使用标准的 PCI ID 格式,
 包含域, 总线, 设备, 和功能号.
 struct bus_type *bus;
 确定设备位于哪种总线.
 struct device_driver *driver;
 管理这个设备的驱动; 我们查看 struct device_driver 在下一节.
 void *driver_data;
 一个可能被设备驱动使用的私有数据成员.
 void (*release)(struct device *dev);
 当对这个设备的最后引用被去除时调用的方法; 它从被嵌入的 kobject 的
 release 方法被调用. 注册到核心的所有的设备结构必须有一个 release 方法,
 否则内核打印出慌乱的抱怨.
 最少, parent, bus_id, bus, 和 release 成员必须在设备结构被注册前设置.
设备注册
通常的注册和注销函数在:
 int device_register(struct device *dev);
 void device_unregister(struct device *dev);
 我们已经见到 lddbus 代码如何注册它的总线类型. 但是, 一个实际的总线是一个设备并
 且必须单独注册. 为简单起见, lddbus 模块只支持一个单个虚拟总线, 因此这个驱动在
 编译时建立它的设备:
 static void ldd_bus_release(struct device *dev)
 {
 printk(KERN_DEBUG "lddbus release\n");
}
 struct device ldd_bus = {
 .bus_id = "ldd0",
 .release = ldd_bus_release
 };
 这是顶级总线, 因此 parent 和 bus 成员留为 NULL. 我们有一个简单的, no-op
 release 方法, 并且, 作为第一个(并且唯一)总线, 它的名子时 ldd0. 这个总线设备被
 注册, 使用:
 ret = device_register(&ldd_bus);
 if (ret)
 printk(KERN_NOTICE "Unable to register ldd0\n");
 一旦调用完成, 新总线可在 sysfs 中 /sys/devices 下面见到. 任何加到这个总线的设
 备接着在 /sys/devices/ldd0 下显示.
设备属性
sysfs 中的设备入口可有属性. 相关的结构是:
 struct device_attribute {
 struct attribute attr;
 ssize_t (*show)(struct device *dev, char *buf);
 ssize_t (*store)(struct device *dev, const char *buf,
 size_t count);
 };
 这些属性结构可在编译时建立, 使用这些宏:
 DEVICE_ATTR(name, mode, show, store);
 结果结构通过前缀 dev_attr_ 到给定名子上来命名. 属性文件的实际管理使用通常的函
 数对来处理:
 int device_create_file(struct device *device, struct device_attribute *entry);
 void device_remove_file(struct device *dev, struct device_attribute *attr);
 struct bus_type 的 dev_attrs 成员指向一个缺省的属性列表, 这些属性给添加到总线
 的每个设备创建.
设备结构嵌入
设备结构包含设备模型核心需要的来模型化系统的信息. 大部分子系统, 但是, 跟踪关于
 它们驻留的设备的额外信息. 结果, 对设备很少由空设备结构所代表; 相反, 这个结构,
 如同 kobject 结构, 常常是嵌入一个更高级的设备表示中. 如果你查看 struct pci_dev
的定义或者 struct usb_device 的定义, 你会发现一个 struct device 埋在其中. 常常
 地, 低层驱动甚至不知道 struct device, 但是有例外.
 lddbus 驱动创建它自己的设备类型( struct ldd_device ) 并且期望单独的设备驱动来
 注册它们的设备使用这个类型. 它是一个简单结构:
 struct ldd_device {
 char *name;
 struct ldd_driver *driver;
 struct device dev;
 };
 #define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);
 这个结构允许驱动提供一个实际的名子给设备( 这可以清楚地不同于它的总线 ID, 存储
 于设备结构) 以及一个这些驱动信息的指针. 给真实设备的结构常常还包含关于供应者信
 息, 设备型号, 设备配置, 使用的资源, 等等. 可以在 struct pci_dev (<linux/pci.h>)
 或者 struct usb_device (<linux/usb.h>) 中找到好的例子. 一个方便的宏
 ( to_ldd_device ) 也为 struct ldd_device 定义, 使得容易转换指向被嵌入的结构的
 指针为 ldd_device 指针.
 lddbus 输出的注册接口看来如此:
 int register_ldd_device(struct ldd_device *ldddev)
 {
 ldddev->dev.bus = &ldd_bus_type;
 ldddev->dev.parent = &ldd_bus;
 ldddev->dev.release = ldd_dev_release;
 strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);
 return device_register(&ldddev->dev);
 }
 EXPORT_SYMBOL(register_ldd_device);
 这里, 我们简单地填充一些嵌入的设备结构成员( 单个驱动不应当需要知道这个 ), 并且
 注册这个设备到驱动核心. 如果我们想添加总线特定的属性到设备, 我们可在这里做.
 为显示这个接口如何使用, 我们介绍另一个例子驱动, 我们称为 sculld. 它是在第 8 章
 介绍的 scullp 驱动上的另一个变体. 它实现通用的内存区设备, 但是 sculld 也使用
 Linux 设备模型, 通过 lddbus 接口.
 sculld 驱动添加一个它自己的属性到它的设备入口; 这个属性, 称为 dev, 仅仅包含关
 联的设备号. 这个属性可被一个模块用来加载脚本或者热插拔子系统, 来自动创建设备节
 点, 当设备被添加到系统时. 这个属性的设置遵循常用模式:
 s tatic ssize_t sculld_show_dev(struct device *ddev, char *buf)
 {
 struct sculld_dev *dev = ddev->driver_data;
 return print_dev_t(buf, dev->cdev.dev);
 }
static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);
 接着, 在初始化时间, 设备被注册, 并且 dev 属性被创建通过下面的函数:
 static void sculld_register_dev(struct sculld_dev *dev, int index)
 {
 sprintf(dev->devname, "sculld%d", index);
 dev->ldev.name = dev->devname;
 dev->ldev.driver = &sculld_driver;
 dev->ldev.dev.driver_data = dev;
 register_ldd_device(&dev->ldev);
 device_create_file(&dev->ldev.dev, &dev_attr_dev);
 }
 注意, 我们使用 driver_data 成员来存储指向我们自己的内部的设备结构的指针.
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!