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) 支持(默认支持)
相关推荐
蝎子莱莱爱打怪3 小时前
Centos7中一键安装K8s集群以及Rancher安装记录
运维·后端·kubernetes
崔小汤呀5 小时前
最全的docker安装笔记,包含CentOS和Ubuntu
linux·后端
何中应5 小时前
vi编辑器使用
linux·后端·操作系统
何中应5 小时前
Linux进程无法被kill
linux·后端·操作系统
何中应5 小时前
rm-rf /命令操作介绍
linux·后端·操作系统
何中应5 小时前
Linux常用命令
linux·操作系统
葛立国5 小时前
从 / 和 /dev 说起:Linux 文件系统与挂载点一文理清
linux
DianSan_ERP20 小时前
电商API接口全链路监控:构建坚不可摧的线上运维防线
大数据·运维·网络·人工智能·git·servlet
哇哈哈202121 小时前
信号量和信号
linux·c++
呉師傅21 小时前
火狐浏览器报错配置文件缺失如何解决#操作技巧#
运维·网络·windows·电脑