Linux 驱动开发:杂项设备驱动与自动设备号管理

一、为什么需要"无需mknod"的驱动?

在 Linux 中,每个设备都有一个唯一的 设备号(device number),由主设备号(major)和次设备号(minor)组成。例如:

cs 复制代码
/dev/led -> 主设备号: 255, 次设备号: 0

传统字符设备驱动的注册流程如下:

  1. 使用 register_chrdev_region() 注册设备号。
  2. 手动调用 mknod /dev/led c 255 0 创建设备节点。
  3. 用户程序通过 open("/dev/led") 访问设备。

传统方式的问题:

  • 必须预先定义主设备号(如 DEV_MAJOR = 255),容易冲突。
  • 需要用户手动运行 mknod,不适合自动化部署。
  • 若未正确设置权限或路径,无法访问设备。
  • 不利于热插拔和动态加载场景。

二、什么是杂项设备(Misc Device)?

misc 是 "miscellaneous" 的缩写,用于处理那些不归属于特定类别的小型、简单设备,如 LED、按键、蜂鸣器、ADC 等。

其核心优势在于:

  • 自动分配设备号
  • 自动创建 /dev/xxx 节点
  • 无需手动执行 mknod
  • 轻量级、易于维护

三、misc 杂项设备驱动代码

1. 头文件包含与 设备名称

cs 复制代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#define DEV_NAME "led"
  • <linux/kernel.h>:提供内核打印函数 printk()。
  • <linux/fs.h>:定义文件操作接口(如 open, read, write)。
  • <linux/uaccess.h>:提供用户空间与内核空间数据拷贝函数(如 copy_from_user)。
  • 系统会自动创建 /dev/led 节点。

2.全局变量:寄存器映射指针

cs 复制代码
static volatile unsigned long *iomux_mux_ctl;
static volatile unsigned long *iomux_pad_ctl;
static volatile unsigned long *gpio1_gdir;
static volatile unsigned long *gpio1_dr;
  • iomux_mux_ctl:复用功能配置寄存器。
  • iomux_pad_ctl:电气特性配置寄存器。
  • gpio1_gdir:方向控制寄存器。
  • gpio1_dr:数据输出寄存器。

3. 初始化函数:设置 GPIO 功能

cs 复制代码
static void led_init(void)
{
    *iomux_mux_ctl = 0x05;
    *iomux_pad_ctl = 0x1080;
    *gpio1_gdir |= (1 << 3);
}
  • 设置引脚为 GPIO 功能(0x05)。
  • 配置电气特性(上拉、速度等)。
  • 将 GPIO1_IO03 配置为输出方向(第 3 位设为 1)。

4.控制函数:点亮与熄灭 LED

cs 复制代码
static void led_on(void)
{
    *gpio1_dr &= ~(1 << 3); // 清除第3位,输出低电平
}

static void led_off(void)
{
    *gpio1_dr |= (1 << 3);  // 设置第3位,输出高电平
}

5. 文件操作函数:open()、write()、read()、close()。

cs 复制代码
static int open(struct inode *inode, struct file *file)
{
    led_init();
    printk("led open\n");
    return 0;
}
static ssize_t read(struct file *file, char __user *buf, size_t size, loff_t *loff)
{
    printk("led read\n");
    return 0;
}
static ssize_t write(struct file *file, const char __user *buf, size_t size, loff_t*loff)
{
    char data[20] = {0};
    long len = copy_from_user(data, buf, size > sizeof(data) ? sizeof(data) : size);

    if (!strcmp(data, "led_on"))
        led_on();
    else if (!strcmp(data, "led_off"))
        led_off();
    else
        return -EINVAL;

    printk("led write\n");
    return len;
}
static int close(struct inode *inode, struct file *file)
{
    led_off();
    printk("led close\n");
    return 0;
}

6. 文件操作结构体:fops

cs 复制代码
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = open,
    .read = read,
    .write = write,
    .release = close,
};
  • .owner:指定所属模块,防止模块被卸载时产生问题。
  • .open, .read, .write, .release:分别绑定对应的处理函数。

7.杂项设备结构体:miscdevice

cs 复制代码
static struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEV_NAME,
    .fops = &fops,
};
  • .minor = MISC_DYNAMIC_MINOR:让内核自动分配次设备号(推荐方式)。
  • .name = "led":设备在 /dev/ 下的名称。
  • .fops = &fops:指向文件操作函数表。

8. 模块初始化函数:led_init()

cs 复制代码
static int __init led_init(void)
2{
3    int ret = misc_register(&misc_dev);
4    if (ret) {
5        printk("misc_led_init failed ret = %d\n", ret);
6        return ret;
7    }
8
9    iomux_mux_ctl = ioremap(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03, 4);
10    iomux_pad_ctl = ioremap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03, 4);
11    gpio1_gdir = ioremap(GPIO1_GDIR, 4);
12    gpio1_dr = ioremap(GPIO1_DR, 4);
13
14    printk("####################### led_init()\n");
15    return 0;
16}
  • 调用 misc_register() 注册杂项设备,内核会自动创建 /dev/led 节点。
  • 使用 ioremap() 将硬件寄存器映射到内核虚拟地址空间。
  • 打印初始化成功信息。

9.模块退出函数:led_exit()与模块入口与出口声明

cs 复制代码
static void __exit led_exit(void)
{
    misc_deregister(&misc_dev);
    iounmap(iomux_mux_ctl);
    iounmap(iomux_pad_ctl);
    iounmap(gpio1_gdir);
    iounmap(gpio1_dr);
    printk("####################### led_exit()\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

四、关键函数讲解

函数 功能
misc_register() 注册杂项设备,返回设备号并创建 /dev/<name> 节点
misc_deregister() 卸载时释放资源
MISC_DYNAMIC_MINOR 表示让内核自动选择次设备号(推荐)
MISC_STATIC_MINOR(n) 手动指定次设备号(不推荐)

五、总结

特性 传统字符设备 misc 设备
设备号 手动分配 自动分配
设备节点 mknod 自动生成
管理复杂度
适用场景 复杂设备(如网卡、串口) 简单设备(LED、按键、蜂鸣器)
是否支持热插拔 支持(配合 udev) 支持(默认支持)
相关推荐
黄昏晓x2 小时前
Linux----缓冲区
linux·运维·服务器
2501_901147832 小时前
学习笔记:单调递增数字求解的迭代优化与工程实践
linux·服务器·笔记·学习·算法
似霰2 小时前
Linux timerfd 的基本使用
android·linux·c++
2501_924878732 小时前
AdAgent 能力成熟度模型:从 L1 自动化到 L5 自主增长引擎
运维·自动化
寄存器漫游者2 小时前
Linux 软件编程 命令、内核与 Shell
linux·运维·服务器
Kaede62 小时前
服务器硬件防火墙和软件防火墙的区别
运维·服务器
qinyia3 小时前
通过本地构建解决Cartographer编译中absl依赖缺失问题
linux·运维·服务器·mysql·ubuntu
郝亚军3 小时前
ubuntu启一个udp server,由一个client访问
linux·ubuntu·udp
嵌入式-老费3 小时前
Linux Camera驱动开发(用树莓派学习camera驱动)
驱动开发