linux 设备模型之总线

总线

一个总线是处理器和一个或多个设备之间的通道. 为设备模型的目的, 所有的设备都通过
一个总线连接, 甚至当它是一个内部的虚拟的,"平台"总线. 总线可以插入另一个 - 一个
USB 控制器常常是一个 PCI 设备, 例如. 设备模型表示在总线和它们控制的设备之间的
实际连接.
在 Linux 设备模型中, 一个总线由 bus_type 结构代表, 定义在 <linux/device.h>. 这
个结构看来象:
struct bus_type {
char *name;
struct subsystem subsys;
struct kset drivers;
struct kset devices;
int (*match)(struct device *dev, struct device_driver *drv);
struct device *(*add)(struct device * parent, char * bus_id);
int (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
/* Some fields omitted */
};
name 成员是总线的名子, 有些同 pci. 你可从这个结构中见到每个总线是它自己的子系
统; 这个子系统不位于 sysfs 的顶层, 但是. 相反, 它们在总线子系统下面. 一个总线
包含 2 个 ksets, 代表已知的总线的驱动和所有插入总线的设备. 所以, 有一套方法我
们马上将涉及.

总线注册

如同我们提过的, 例子源码包含一个虚拟总线实现称为 lddbus. 这个总线建立它的
bus_type 结构, 如下:
struct bus_type ldd_bus_type = { .name = "ldd", .match = ldd_match, .hotplug =
ldd_hotplug, };

注意很少 bus_type 成员要求初始化; 大部分由设备模型核心处理. 但是, 我们确实不得
不指定总线的名子, 以及任何伴随它的方法.
不可避免地, 一个新总线必须注册到系统, 通过一个对 bus_register 的调用. lddbus
代码这样做以这样的方式:
ret = bus_register(&ldd_bus_type);
if (ret)
return ret;
这个调用可能失败, 当然, 因此返回值必须一直检查. 如果它成功, 新总线子系统已被添
加到系统; 在 sysfs 中 /sys/bus 的下面可以见到, 并且可能启动添加设备.
如果有必要从系统中去除一个总线(当关联模块被去除, 例如), 调用调用
bus_unregister:
void bus_unregister(struct bus_type *bus);

总线方法

有几个给 bus_type 结构定义的方法; 它们允许总线代码作为一个设备核心和单独驱动之
间的中介. 在 2.6.10 内核中定义的方法是:
int (*match)(struct device *device, struct device_driver *driver);
这个方法被调用, 大概多次, 无论何时一个新设备或者驱动被添加给这个总线. 它
应当返回一个非零值如果给定的设备可被给定的驱动处理. (我们马上进入设备和
device_driver 结构的细节). 这个函数必须在总线级别处理, 因为那是合适的逻
辑存在的地方; 核心内核不能知道如何匹配每个可能总线类型的设备和驱动.
int (*hotplug) (struct device *device, char **envp, int num_envp, char *buffer,
int buffer_size);
这个模块允许总线添加变量到环境中, 在产生一个热插拔事件在用户空间之前. 参
数和 kset 热插拔方法相同( 在前面的 "热插拔事件产生" 一节中描述 ).
lddbus 驱动有一个非常简单的匹配函数, 它仅仅比较驱动和设备的名子:
static int ldd_match(struct device *dev, struct device_driver *driver)
{
return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}
当涉及到真实硬件, match 函数常常在有设备自身提供的硬件 ID 和驱动提供的 ID 之间,
做一些比较.
lddbus 热插拔方法看来象这样:

static int ldd_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int
buffer_size)
{
envp[0] = buffer;
if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s",
Version) >= buffer_size)
return -ENOMEM;
envp[1] = NULL;
return 0;
}
这里, 我们加入 lddbus 源码的当前版本号, 只是以防有人好奇.

列举设备和驱动

如果你在编写总线级别的代码, 你可能不得不对所有已经注册到你的总线的设备或驱动进
行一些操作. 它可能会诱惑人直接进入 bus_type 结构中的各种结构, 但是最好使用已经
提供的帮助函数.
为操作每个对总线已知的设备, 使用:
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
int (*fn)(struct device *, void *));
这个函数列举总线上的每个设备, 传递关联的设备结构给 fn, 连同作为 data 来传递的
值. 如果 start 是 NULL, 列举从总线的第一个设备开始; 否则列举从 start 之后的第
一个设备开始. 如果 fn 返回一个非零值, 列举停止并且那个值从 bus_for_each_dev 返
回.
有一个类似的函数来列举驱动:
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int
(*fn)(struct device_driver *, void *));
这个函数就像 buf_for_each_dev, 除了, 当然, 它替之作用于驱动.
应当注意, 这 2 个函数持有总线子系统的读者/写者旗标在工作期间. 因此试图一起使用
这 2 个会死锁 -- 每个将试图获取同一个旗标. 修改总线的操作( 例如注销设备 )也将
锁住. 因此, 小心使用 bus_for_each 函数.

总线属性

几乎 Linux 驱动模型中的每一层都提供一个添加属性的接口, 并且总线层不例外.
bus_attribute 类型定义在 <linux/device.h> 如下:
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *bus, char *buf);

ssize_t (*store)(struct bus_type *bus, const char *buf,
size_t count);
};
我们已经见到 struct attribute 在 "缺省属性" 一节. bus_attribute 类型也包含 2
个方法来显示和设置属性值. 大部分在 kobject 之上的设备模型层以这种方式工作.
已经提供了一个方便的宏为在编译时间创建和初始化 bus_attribute 结构:
BUS_ATTR(name, mode, show, store);
这个宏声明一个结构, 产生它的名子通过前缀字符串 bus_attr_ 到给定的名子.
任何属于一个总线的属性应当明确使用 bus_create_file 来创建:
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
属性也可被去除, 使用:
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
lddbus 驱动创建一个简单属性文件, 再次, 包含源码版本号. show 方法和
bus_attribute 结构设置如下:
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
创建属性文件在模块加载时间完成:
if (bus_create_file(&ldd_bus_type, &bus_attr_version))
printk(KERN_NOTICE "Unable to create version attribute\n");
这个调用创建一个属性文件(/sys/busldd/version) 包含 lddbus 代码的版本号.

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux总线设备模型是一种用于管理和操作硬件设备的框架。它提供了一种统一的方式来访问和控制连接到计算机系统上的各种设备,包括串口、USB、PCI、I2C等。 在Linux系统中,每个设备都被视为一个文件,并通过文件路径来访问。总线设备模型定义了设备之间的层次关系和通信机制,使得设备可以被识别、配置和操作。 Linux总线设备模型由以下几个重要组件组成: 1. 设备树(Device Tree):在启动过程中,设备树用于描述连接到系统的各种硬件设备和其相互关系。它是一个以文本方式描述的树状结构,包含了设备的类型、地址、中断等信息。 2. 设备驱动程序(Device Driver):驱动程序是用来控制和管理特定硬件设备的软件模块。每个设备都有相应的驱动程序,它们与设备进行通信,并提供对设备的访问接口。 3. 总线(Bus):总线是连接多个设备的物理或逻辑通道。例如,PCI总线、USB总线等。总线提供了设备之间通信的基础。 4. 设备类(Device Class):设备类是一组具有相似功能的设备的集合。例如,USB设备类包括存储设备、键盘、鼠标等。设备类可以帮助系统区分和管理不同类型的设备。 通过使用总线设备模型Linux系统可以自动检测和配置连接到系统的设备,使其能够正常工作。同时,开发者也可以编写自定义的驱动程序来支持新的硬件设备总线设备模型的设计使得设备的添加、删除和管理变得更加灵活和可扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶知秋yyds

分享是一种美德,感谢金主打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值