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) 支持(默认支持)
相关推荐
阿拉斯攀登15 分钟前
第 19 篇 驱动性能优化与功耗优化实战
android·驱动开发·瑞芯微·嵌入式驱动·安卓驱动
九皇叔叔24 分钟前
CentOS 7.5/RHEL 7.x 配置 YUM 源(阿里云镜像+本地源双方案)
linux·阿里云·centos
chinesegf1 小时前
DNS 验证验证SSL证书
linux·服务器·网络
道一云黑板报1 小时前
技术拆解:AI低代码架构设计与全链路落地实现
人工智能·驱动开发·低代码·ai·企业微信·ai编程·代码规范
未佩妥剑,已入江湖2 小时前
docker Windows下安装
运维·windows·docker·容器
试试勇气3 小时前
Linux学习笔记(十七)--线程概念
linux·笔记·学习
LXY_BUAA3 小时前
《嵌入式操作系统》_高级字符设备驱动_20260316
linux·运维·服务器·驱动开发
顶妙WMS海外仓管理系统3 小时前
Shopify卖家破910万,海外仓如何对接Shopify独立站?
运维·产品运营
优美的赫蒂3 小时前
香橙派5plus单独编译内核安装时的报错记录
linux·rk3588·orangepi
·醉挽清风·3 小时前
学习笔记—Linux—文件系统
linux·笔记·学习