📄 ch14s04.html
字号:
.release = ldd_bus_release}; </pre><p>这是顶级总线, 因此 parent 和 bus 成员留为 NULL. 我们有一个简单的, no-op release 方法, 并且, 作为第一个(并且唯一)总线, 它的名子时 ldd0. 这个总线设备被注册, 使用:</p><pre class="programlisting">ret = device_register(&ldd_bus);if (ret) printk(KERN_NOTICE "Unable to register ldd0\n");</pre><p>一旦调用完成, 新总线可在 sysfs 中 /sys/devices 下面见到. 任何加到这个总线的设备接着在 /sys/devices/ldd0 下显示.</p></div><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="Deviceattributes.sect3"></a>14.4.2.2. 设备属性</h4></div></div></div><p>sysfs 中的设备入口可有属性. 相关的结构是:</p><pre class="programlisting">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);};</pre><p>这些属性结构可在编译时建立, 使用这些宏:</p><pre class="programlisting">DEVICE_ATTR(name, mode, show, store);</pre><p>结果结构通过前缀 dev_attr_ 到给定名子上来命名. 属性文件的实际管理使用通常的函数对来处理:</p><pre class="programlisting">int device_create_file(struct device *device, struct device_attribute *entry);void device_remove_file(struct device *dev, struct device_attribute *attr);</pre><p>struct bus_type 的 dev_attrs 成员指向一个缺省的属性列表, 这些属性给添加到总线的每个设备创建.</p></div><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="Devicestructureembedding.sect3"></a>14.4.2.3. 设备结构嵌入</h4></div></div></div><p>设备结构包含设备模型核心需要的来模型化系统的信息. 大部分子系统, 但是, 跟踪关于它们驻留的设备的额外信息. 结果, 对设备很少由空设备结构所代表; 相反, 这个结构, 如同 kobject 结构, 常常是嵌入一个更高级的设备表示中. 如果你查看 struct pci_dev 的定义或者 struct usb_device 的定义, 你会发现一个 struct device 埋在其中. 常常地, 低层驱动甚至不知道 struct device, 但是有例外.</p><p>lddbus 驱动创建它自己的设备类型( struct ldd_device ) 并且期望单独的设备驱动来注册它们的设备使用这个类型. 它是一个简单结构:</p><pre class="programlisting">struct ldd_device { char *name; struct ldd_driver *driver; struct device dev; }; #define to_ldd_device(dev) container_of(dev, struct ldd_device, dev); </pre><p>这个结构允许驱动提供一个实际的名子给设备( 这可以清楚地不同于它的总线 ID, 存储于设备结构) 以及一个这些驱动信息的指针. 给真实设备的结构常常还包含关于供应者信息, 设备型号, 设备配置, 使用的资源, 等等. 可以在 struct pci_dev (<linux/pci.h>) 或者 struct usb_device (<linux/usb.h>) 中找到好的例子. 一个方便的宏( to_ldd_device ) 也为 struct ldd_device 定义, 使得容易转换指向被嵌入的结构的指针为 ldd_device 指针.</p><p>lddbus 输出的注册接口看来如此:</p><pre class="programlisting">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);</pre><p>这里, 我们简单地填充一些嵌入的设备结构成员( 单个驱动不应当需要知道这个 ), 并且注册这个设备到驱动核心. 如果我们想添加总线特定的属性到设备, 我们可在这里做.</p><p>为显示这个接口如何使用, 我们介绍另一个例子驱动, 我们称为 sculld. 它是在第 8 章介绍的 scullp 驱动上的另一个变体. 它实现通用的内存区设备, 但是 sculld 也使用 Linux 设备模型, 通过 lddbus 接口.</p><p>sculld 驱动添加一个它自己的属性到它的设备入口; 这个属性, 称为 dev, 仅仅包含关联的设备号. 这个属性可被一个模块用来加载脚本或者热插拔子系统, 来自动创建设备节点, 当设备被添加到系统时. 这个属性的设置遵循常用模式:</p><pre class="programlisting">static 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);</pre><p>接着, 在初始化时间, 设备被注册, 并且 dev 属性被创建通过下面的函数:</p><pre class="programlisting">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);} </pre><p>注意, 我们使用 driver_data 成员来存储指向我们自己的内部的设备结构的指针.</p></div></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="DeviceDrivers.sect2"></a>14.4.3. 设备驱动</h3></div></div></div><p>设备模型跟踪所有对系统已知的驱动. 这个跟踪的主要原因是使驱动核心能匹配驱动和新设备. 一旦驱动在系统中是已知的对象, 但是, 许多其他的事情变得有可能. 设备驱动可输出和任何特定设备无关的信息和配置变量, 例如:</p><p>驱动由下列结构定义:</p><pre class="programlisting">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);}; </pre><p>再一次, 几个结构成员被忽略( 全部内容见 <linux/device.h> ). 这里, name 是驱动的名子( 它在 sysfs 中出现 ), bus 是这个驱动使用的总线类型, kobj 是必然的 kobject, devices 是当前绑定到这个驱动的所有设备的列表, probe 是一个函数被调用来查询一个特定设备的存在(以及这个驱动是否可以使用它), remove 当设备从系统中去除时被调用, shutdown 在关闭时被调用来关闭设备.</p><p>使用 device_driver 结构的函数的形式, 现在应当看来是类似的(因此我们快速涵盖它们). 注册函数是:</p><pre class="programlisting">int driver_register(struct device_driver *drv);void driver_unregister(struct device_driver *drv);</pre><p>通常的属性结构在:</p><pre class="programlisting">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);</pre><p>以及属性文件以通常的方法创建:</p><pre class="programlisting">int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);</pre><p>bus_type 结构含有一个成员( drv_attrs ) 指向一套缺省属性, 对所有关联到这个总线的驱动都创建.</p><div class="sect3" lang="zh-cn"><div class="titlepage"><div><div><h4 class="title"><a name="Driverstructureembedding.sect"></a>14.4.3.1. 驱动结构嵌入</h4></div></div></div><p>如同大部分驱动核心结构的情形, device_driver 结构常常被发现嵌到一个更高级的, 总线特定的结构. lddbus 子系统不会和这样的趋势相反, 因此它已定义了它自己的 ldd_driver 结构:</p><pre class="programlisting">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); </pre><p>这里, 我们要求每个驱动提供特定当前软件版本, 并且 lddbus 输出这个版本字串为它知道的每个驱动. 总线特定的驱动注册函数是:</p><pre class="programlisting">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);}</pre><p>这个函数的第一部分只注册低级的 device_driver 结构到核心; 剩下的建立版本属性. 因为这个属性在运行时被创建, 我们不能使用 DRIVER_ATTR 宏; 反之, driver_attribute 结构必须手工填充. 注意我们设定属性的拥有者为驱动模块, 不是 lddbus 模块; 这样做的理由是可以在为这个属性的 show 函数的实现中见到:</p><pre class="programlisting">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);}</pre><p>有人可能认为属性拥有者应当是 lddbus 模块, 因为实现这个属性的函数在那里定义. 这个函数, 但是, 是使用驱动自身所创建的 ldd_driver 结构. 如果那个结构在一个用户空间进程试图读取版本号时要消失, 事情会变得麻烦. 指定驱动模块作为属性的拥有者阻止了模块被卸载, 在用户空间保持属性文件打开时. 因为每个驱动模块创建一个对 lddbus 模块的引用, 我们能确信 lddbus 不会在一个不合适的时间被卸载.</p><p>为完整起见, sculld 创建它的 ldd_driver 结构如下:</p><pre class="programlisting">static struct ldd_driver sculld_driver = { .version = "$Revision: 1.1 $", .module = THIS_MODULE, .driver = { .name = "sculld", }, }; </pre><p>一个简单的对 register_ldd_driver 的调用添加它到系统中. 一旦完成初始化, 驱动信息可在 sysfs 中见到:</p><pre class="screen">$ 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 </pre></div></div><div class="footnotes"><br><hr width="100" align="left"><div class="footnote"><p><sup>[<a name="ftn.id485223" href="#id485223">46</a>] </sup>这个总线的逻辑名子, 当然, 应当是"sbus", 但是这个名子已经被一个真实的, 物理总线采用.</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch14s03.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="ch14.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="ch14s05.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">14.3. 热插拔事件产生 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 14.5. 类</td></tr></table></div></body></html><div style="display:none"><script language="JavaScript" src="script.js"></script> </div>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -