点击(此处)折叠或打开
/* my_bus.c */
#include
#include
#include
#include
#include
#include "my_bus.h"
MODULE_LICENSE("Dual BSD/GPL");
#define MYBUS "mybus: "
#define PRINT(x...) printk(KERN_ALERT MYBUS x);
static char *Version = "$Revision: 1.9 $";
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
PRINT("%s\n", __func__);
return snprintf(buf, PAGE_SIZE, "my_bus: %s\n", Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
static void my_bus_release(struct device *dev)
{
PRINT("%s\n", __func__);
}
static int my_bus_match(struct device *dev, struct device_driver *drv)
{
struct my_device *device = to_my_device(dev);
PRINT("%s\n", __func__);
return !strncmp(device->name, drv->name, strlen(drv->name));
}
static int my_bus_hotplug(struct device *dev, struct kobj_uevent_env *env){
PRINT("%s\n", __func__);
return 0;
}
struct bus_type my_bus_type = {
.name = "my_bus",
.match = my_bus_match,
.uevent = my_bus_hotplug
};
static struct device my_bus = {
.init_name = "my_bus0",
.release = my_bus_release
};
/* interface to device. */
int register_my_device(struct my_device *device)
{
PRINT("%s\n", __func__);
device->device.bus = &my_bus_type;
device->device.parent = &my_bus;
device->device.release = my_bus_release;
//strncpy(device->device.bus_id, device->name, BUS_ID_SIZE);
return device_register(&device->device);
}
EXPORT_SYMBOL(register_my_device);
void unregister_my_device(struct my_device *device)
{
PRINT("%s\n", __func__);
device_unregister(&device->device);
}
EXPORT_SYMBOL(unregister_my_device);
static ssize_t show_version(struct device_driver *driver, char *buf)
{
struct my_driver *drv = to_my_driver(driver);
PRINT("%s\n", __func__);
sprintf(buf, "%s\n", drv->version);
return strlen(buf);
}
/* interface to driver. */
int register_my_driver(struct my_driver *driver)
{
int ret = 0;
PRINT("%s\n", __func__);
driver->driver.bus = &my_bus_type;
ret = driver_register(&driver->driver);
if (ret) {
PRINT("%s, driver_register %s failed!!!\n", __func__, driver->driver.name);
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;
return driver_create_file(&driver->driver, &driver->version_attr);
}
EXPORT_SYMBOL(register_my_driver);
void unregister_my_driver(struct my_driver *driver)
{
PRINT("%s\n", __func__);
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(unregister_my_driver);
static int __init my_bus_init(void)
{
int ret = 0;
ret = bus_register(&my_bus_type);
if (ret) {
PRINT("%s, bus_register failed!\n", __func__);
goto bus_register_failed;
}
ret = bus_create_file(&my_bus_type, &bus_attr_version);
if (ret) {
PRINT("%s, bus_create_file failure...!\n", __func__);
goto bus_create_file_failed;
}
ret = device_register(&my_bus);
if (ret) {
PRINT("%s, device_register failure...!\n", __func__);
goto device_register_failed;
}
PRINT("%s, bus & device register succeed!\n", __func__);
return 0;
device_register_failed:
bus_create_file_failed:
bus_unregister(&my_bus_type);
bus_register_failed:
return ret;
}
static void __exit my_bus_exit(void)
{
PRINT("%s!\n", __func__);
device_unregister(&my_bus);
bus_unregister(&my_bus_type);
}
module_init(my_bus_init);
module_exit(my_bus_exit);
my_bus.c 是实现 my_bus 的主要文件. 其实一条总线, 在 Linux 设备驱动模型中, 它也是一个设备. 所以, 需要声明 my_bus_type 的同时, 还要声明 my_bus 这个设备.(注:C语言中,非导出符号在文件中声明时用static标明.所以, 在my_bus.c中, 你将看到大多数的函数都是static.)
注册 my_bus 的步骤.
1. 声明my_bus_type, 类型为 bus_type. 作为总线.
点击(此处)折叠或打开
struct bus_type my_bus_type = {
.name = "my_bus", //总线名称.
.match = my_bus_match, //用来匹配设备和驱动的函数.
.uevent = my_bus_hotplug //用来发送uevent到用户空间.
};2. 声明my_bus,类型为 struct device, 因为my_bus也是一种设备.
点击(此处)折叠或打开
static struct device my_bus = {
.init_name = "my_bus0", //my_bus设备的名称.在device_add函数里会将init_name复制到kobject中.
.release = my_bus_release //取消注册的设备的时候要调用的函数.
};
3. 调用 bus_register(&my_bus_type) 将 my_bus_type 这个总线类型注册到系统.
4. 调用 device_register(&my_bus) 将总线设备注册到系统.
(步骤3和4都在my_bus_init函数中.)
点击(此处)折叠或打开
static int __init my_bus_init(void)
{
int ret = 0;
ret = bus_register(&my_bus_type); //注册my_bus_type到Linux设备驱动模型中.
if (ret) {
PRINT("%s, bus_register failed!\n", __func__);
goto bus_register_failed;
}
ret = bus_create_file(&my_bus_type, &bus_attr_version);//创建my_bus的一个属性.在sysfs中体现.
if (ret) {
PRINT("%s, bus_create_file failure...!\n", __func__);
goto bus_create_file_failed;
}
ret = device_register(&my_bus); //注册my_bus到Linux设备驱动模型中.
if (ret) {
PRINT("%s, device_register failure...!\n", __func__);
goto device_register_failed;
}
PRINT("%s, bus & device register succeed!\n", __func__);
return 0;
device_register_failed:
bus_create_file_failed:
bus_unregister(&my_bus_type);
bus_register_failed:
return ret;
}上面四个步骤, 就将一条总线注册到 Linux设备驱动模型中.
编译并将 my_bus.ko 插入到到系统后,
可以通过命令 ls /sys/bus/ 会看到 my_bus 文件夹.
通过命令 ls /sys/device/ 会看到 my_bus0 文件夹.
其实, 对于 Linux 设备驱动模型而言, 它管理的, 是 bus_type, device 和 device_driver 三大主体. 譬如我这里的my_bus, my_device, my_driver 都是 bus_type, device, device_driver 的拓展, 都离不开 bus_type, device, device_driver 这三个主体. 所以, 这也就是为什么对应的数据结构中间都要包含它们.(my_bus_type 本身就是 bus_type 类型.)
取消 my_bus 这条总线, 有两个步骤:
1. 取消 my_bus 设备的注册.
2. 取消 my_bus_type 的注册.
步骤1和2都是在 my_bus_exit 函数中.
点击(此处)折叠或打开
static void __exit my_bus_exit(void)
{
PRINT("%s!\n", __func__);
device_unregister(&my_bus);
bus_unregister(&my_bus_type);
}
驱动的加载和卸载都是 module_init 和 module_exit 来实现的.
module_init(my_bus_init);
module_exit(my_bus_exit);
总线上的驱动和设备的匹配函数如下. 就是通过简单的驱动的名称和设备的名称一致即可.(Linux内核很多都是这样实现的.)
点击(此处)折叠或打开
static int my_bus_match(struct device *dev, struct device_driver *drv)
{
struct my_device *device = to_my_device(dev);
PRINT("%s\n", __func__);
return !strncmp(device->name, drv->name, strlen(drv->name));
}
注册与卸载设备:
在 my_bus 上注册设备的过程, 就是将 my_device 结构体中 device 结构体进行赋值:
1. bus_type 成员设置为 my_bus_type
2. parent 设置为 my_bus.
3. release 函数设置为 my_bus_release.
然后调用 device_register 函数将 my_device 中的 device 结构加入到 Linux 设备驱动模型的管理中.
点击(此处)折叠或打开
int register_my_device(struct my_device *device)
{
PRINT("%s\n", __func__);
device->device.bus = &my_bus_type;
device->device.parent = &my_bus;
device->device.release = my_bus_release;
//strncpy(device->device.bus_id, device->name, BUS_ID_SIZE);
return device_register(&device->device);
}
EXPORT_SYMBOL(register_my_device);
卸载 my_bus 上的 my_device 过程:
1. 调用 device_unregister 函数将 my_device 中的 device 结构从 Linux 设备驱动模型中移除.
点击(此处)折叠或打开
void unregister_my_device(struct my_device *device)
{
PRINT("%s\n", __func__);
device_unregister(&device->device);
}
EXPORT_SYMBOL(unregister_my_device);
注册与卸载驱动:
在my_bus总线上注册驱动的过程, 也就是对 my_driver 中 device_driver 的处理过程.
1. 设置 device_driver 的成员 bus 为 my_bus_type.
2. 调用 driver_register 函数, 将 device_driver 添加到 Linux 设备驱动模型中管理.
点击(此处)折叠或打开
int register_my_driver(struct my_driver *driver)
{
int ret = 0;
PRINT("%s\n", __func__);
driver->driver.bus = &my_bus_type;
ret = driver_register(&driver->driver);
if (ret) {
PRINT("%s, driver_register %s failed!!!\n", __func__, driver->driver.name);
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;
return driver_create_file(&driver->driver, &driver->version_attr);
}
EXPORT_SYMBOL(register_my_driver);
在my_bus总线上卸载驱动的过程, 也是对 my_driver 中 device_driver 的处理过程.
1. 调用driver_unregister函数将 device_driver 从 Linux 设备驱动模型中移除.
点击(此处)折叠或打开
void unregister_my_driver(struct my_driver *driver)
{
PRINT("%s\n", __func__);
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(unregister_my_driver);
EXPORT_SYMBOL 是用来导出符号的. 用 EXPORT_SYMBOL 导出的符号, 其他模块也可以使用.
属性(Attribute)
在 Linux 设备驱动模型中, 总线, 设备, 驱动的特性, 都是通过属性这一工具来呈现在 sysfs 文件系统中. 导出到用户空间的.
1. 声明一个属性 bus_attr_version.
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
在内核源码中, 会看到, BUS_ATTR 是一个宏, 基于 __ATTR 这个宏而来的, 只是在 __ATTR 宏的基础上, 赋予一些初值.BUS_ATTR 宏如下:
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
其中:
_mode 是属性在 sysfs 中对应文件的访问权限.
_show 是读取 sysfs 中对应文件时调用的方法.
_store 是写 sysfs 中对应的文件触发的方法.
2. 将属性和bus_type挂接. 那么, Linux 设备驱动模型会在 sysfs 文件系统下生成对应的文件.
__________________________________________________________________________________________
下面是测试 my_bus 总线使用的 驱动和设备的代码.
我的设备 my_device.c