前言
对于一个设备的驱动程序来说,其实上层用户主要看到的、用到的就是设备文件和设备类,当然用得最多的是设备文件,虽然设备类用得不多,但也是每一个设备注册实例化时必须要用到的东西,本篇博文就以一个简单的例子说明设备类的功能。
设备类的本质
所谓设备类,本质上就是“物以类聚,人以群分”思想的体现,它允许每个设备有一个自己的所属类,说白了就是所属分组,假如某几个设备的所属类是相同的,那么我们就能对这些设备进行一些统一的操作。
下面以一个实际例子看下设备类在Linux嵌入式驱动开发中是如何被定义和使用的。
例子的问题背景和源码
假设我们有 3 个 LED 灯设备(功能相似),它们共享一个驱动程序,每个设备可以独立地开关操作。设备类可以在以下方面帮助实现分组管理:
- 在Linux的 /sys/class/ 目录中,将这些设备归类到一个统一的类目录下。
- 通过类属性实现对所有设备的统一操作,比如一键控制所有 LED 的开关。
我们这里就利用设备类的概念来一键控制所有 LED 的开关。
源码如下:
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#define LED_COUNT 3 // 三个 LED 设备
static struct class *led_class;
static struct cdev led_cdev;
static dev_t dev;
static int led_status[LED_COUNT]; // 每个 LED 的状态(0: 关,1: 开)
// 模拟控制 LED 的硬件操作
static void led_control(int index, int status)
{
printk(KERN_INFO "LED %d is now %s\n", index, status ? "ON" : "OFF");
led_status[index] = status;
}
// 打开设备的回调函数
static int led_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode); // 获取设备次设备号
printk(KERN_INFO "LED device %d opened\n", minor);
return 0;
}
// 写入设备数据的回调函数
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int minor = iminor(file_inode(file)); // 获取次设备号
int status;
// 模拟接收用户的控制命令,'1' 为开,'0' 为关
if (copy_from_user(&status, buf, sizeof(int)))
return -EFAULT;
if (status != 0 && status != 1)
return -EINVAL;
// 控制对应的 LED
led_control(minor, status);
return sizeof(int);
}
// 文件操作结构体
static const struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
// 统一控制所有 LED 的类属性
static ssize_t led_all_control_store(struct class *cls, struct class_attribute *attr, const char *buf, size_t count)
{
int status, i;
if (kstrtoint(buf, 10, &status) || (status != 0 && status != 1))
return -EINVAL;
for (i = 0; i < LED_COUNT; i++)
led_control(i, status);
return count;
}
// 定义类属性
CLASS_ATTR_WO(led_all_control);
// 模块初始化函数
static int __init led_init(void)
{
int ret, i;
// 分配主设备号和次设备号范围
ret = alloc_chrdev_region(&dev, 0, LED_COUNT, "led");
if (ret < 0) {
printk(KERN_ERR "Failed to allocate device numbers\n");
return ret;
}
// 初始化 cdev 并注册
cdev_init(&led_cdev, &led_fops);
ret = cdev_add(&led_cdev, dev, LED_COUNT);
if (ret < 0) {
printk(KERN_ERR "Failed to add cdev\n");
unregister_chrdev_region(dev, LED_COUNT);
return ret;
}
// 创建设备类
led_class = class_create(THIS_MODULE, "led_class");
if (IS_ERR(led_class)) {
printk(KERN_ERR "Failed to create class\n");
cdev_del(&led_cdev);
unregister_chrdev_region(dev, LED_COUNT);
return PTR_ERR(led_class);
}
// 添加类属性
ret