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) 支持(默认支持)
相关推荐
Lonely 净土2 小时前
Rocky Linux 安装教程
linux·运维·服务器
光影少年2 小时前
RN的Fabric 渲染流程
运维·前端·javascript·react native·react.js·fabric
A 糖醋排骨2 小时前
grafana loki alloy轻量级日志采集
运维·grafana·loki
林间码客5 小时前
从DevOps到DevSecOps:构建现代软件交付的基石与护城河
大数据·运维·devsecops·devops
李白你好6 小时前
Windows 离线 Linux 提权辅助查询工具
linux
Lust Dusk6 小时前
记一次Linux应急响应题目解析
linux·运维·服务器·网络·安全·网络安全
ShineWinsu7 小时前
对于Linux:五种IO模型以及非阻塞IO的详细解析
linux·c++·面试·io·阻塞·非阻塞·fcntl
千千寰宇7 小时前
[Linux/Ollama] Termux : 一款运行在 Android 平台上的开源终端模拟器与 Linux 环境应用、支持在手机上基于Ollama部署LLM
linux
zhangfeng11337 小时前
CodeBuddy 是否支持 SDD(Spec-Driven Development 规范驱动开发
人工智能·驱动开发
轻揉小乔 真新人7 小时前
使用memc-nginx和srcache-nginx模块构建高效透明的缓存机制
运维·nginx·缓存