N21 动态驱动、Platform 总线、设备树驱动
一、动态加载驱动程序
杂项设备驱动:
static struct miscdevice misc=
{
.minor=MISC_DYNAMIC_MINOR
.name=DEV_NAME
.fops=&fopsint
}
ret = misc_register(&misc);
misc_deregister(&misc);
这是最关键的一行。声明该模块遵守 GNU General Public License (GPL) 协议。❌ 不加则报错:不声明 GPL,内核会被视为 “被污染”(tainted kernel),导致无法调用内核内部核心函(如
ioremap、register_chrdev),编译或运行会报错。
MODULE_LICENSE("GPL");
编译内核模块:make modules (生成xxx.ko文件)
内核模块的编译与使用:
编译:
Kconfig中变量类型为tristate---YNM
makee menuconfig 对应选项配置为M
make modules编译内核模(生成的.ko文件和对应的.c同一目录下)
使用:
insmod xxx.ko动态加载内核模块
Ismod 查看动态加载的内核模块
rmmodXXX 卸载内核模块
二、usb driver
①流程:
1. 应用层 → 驱动层
应用程序通过系统调用(如write/ioctl)向 USB 驱动发送控制指令(如 “点亮 LED”)。
2. 驱动层 → USB 总线
USB 驱动(usb driver)接收指令后,通过USB 读写操作,将控制命令封装为 USB 协议数据包,发送给 USB 外设硬件。
3. USB 硬件 → 寄存器操作
USB 外设控制器解析 USB 数据包,操作内部的 GPIO 寄存器:
- 先通过
gdir配置引脚为输出模式 - 再通过
dr数据寄存器写入高 / 低电平 pad/sw-mux寄存器确保引脚电气特性和复用功能正确
4. 硬件引脚 → 外设执行
GPIO 引脚输出对应电平,控制外接 LED、按键等外设完成动作。
bus_type 是抽象父类,iic_bus/spi_bus/platform 是具体实现子类:
②总线
- 子类继承父类的通用接口,同时实现自身的总线特性(如 I2C 的地址匹配、SPI 的片选逻辑)
- 内核通过
bus_type统一管理所有总线,实现驱动框架的标准化。

| 总线类型 | 匹配方式 | 适用场景 |
|---|---|---|
| Platform 总线 | name / of_match_table |
片上外设(GPIO、UART、ADC、定时器) |
| I2C 总线 | 设备地址 / id_table |
外接 I2C 传感器、EEPROM |
| SPI 总线 | 片选信号 / id_table |
外接 SPI Flash、显示屏 |
| USB 总线 | VID/PID / id_table |
外接 USB 设备 |
platform_driver() 核心生命周期回调:
| 函数指针 | 核心作用 | 执行时机 |
|---|---|---|
match() |
设备 - 驱动匹配 | 总线遍历设备 / 驱动链表,判断是否配对(Platform 总线用 name/of_match_table,I2C 用地址) |
uevent() |
热插拔事件通知 | 设备插入 / 拔出时,向用户空间发送 uevent 事件(udev 监听) |
probe() |
设备探测初始化 | 匹配成功后,调用驱动的 probe 函数,初始化硬件 |
remove() |
设备移除 | 驱动卸载 / 设备断开时,释放资源、清理硬件 |
shutdown() |
系统关机 | 系统关机 / 重启时,关闭设备、保存状态 |

| 地址 | 内容 | 状态 |
|---|---|---|
| ... | ... | ... |
start |
寄存器基地址 | 开始 |
start+1 |
下一个寄存器 | 覆盖 |
start+2 |
再下一个 | 覆盖 |
start+3 |
第四个寄存器 | 结束 (start+4−1) |
EXPOPT_SYMBOL_GPL(platform_driver_unregister)
| 宏 | 作用 | 权限 |
|---|---|---|
EXPORT_SYMBOL() |
导出符号 | 所有驱动都能用 |
EXPORT_SYMBOL_GPL() |
GPL 专用导出 | 只有 GPL 驱动能用 |
三、设备树文件(Linux/of.h)
设备树是 Linux 内核用来「描述硬件资源」的文本文件,彻底把「硬件信息」和「驱动代码」分离开。
1.先复制系统的dts文件
cp arch/arm/boot/dts/imx6ull-alientek-emmc.dts arch/arm/boot/dts/pt.dts
2.编译makefile文件
vim arch/arm/boot/dts/Makefile
3.打开你刚才的pt.dts:vim arch/arm/boot/dts/pt.dts
在文件最后面添加一个 LED 节点
4.驱动通过
of_函数读取设备树信息5.内核启动时加载
pt.dtb,自动匹配并执行 probe
- 模块一加载,直接手动找设备树节点
/pt_led- 手动映射寄存器
- 注册 misc 设备
- 应用层控制 LED
它就是一个简单的字符驱动,自己管自己,和内核平台无关
#include <linux/init.h>
#include <linux/printk.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/export.h>
#include <asm/uaccess.h>
#include <asm/string.h>
#include <asm/io.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/of.h> 设备树操作 API
#define DEV_NAME "led"
static volatile unsigned int * sw_mux;
static volatile unsigned int * sw_pad;
static volatile unsigned int * gpio1_dr;
static volatile unsigned int * gpio1_gdir;
static void led_init(void)
{
*sw_mux = 0x05;
*sw_pad = 0x10b0;
*gpio1_gdir |= (1 << 3);
*gpio1_dr |= (1 << 3);
}
static void led_on(void)
{
*gpio1_dr &= ~(1 << 3);
}
static void led_off(void)
{
*gpio1_dr |= (1 << 3);
}
static int open(struct inode * node, struct file * file)
{
led_init();
printk("led open...\n");
return 0;
}
static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
//copy_to_user();
printk("led read...\n");
return 0;
}
static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
// "ledon" on "ledoff" off
unsigned char data[10] = {0};
size_t len_cp = len < sizeof(data) ? len : sizeof data;
int size_cp = copy_from_user(data, buf, len_cp);
if(size_cp < 0)
return size_cp;
if(!strcmp(buf, "ledon"))
led_on();
else if(!(strcmp(buf, "ledoff")))
led_off();
else
return -EINVAL;
printk("led write...\n");
return size_cp;
}
static int close(struct inode * node, struct file * file)
{
led_off();
printk("led close...\n");
return 0;
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = open,
.read = read,
.write = write,
.release = close
};
miscdevice —— 混杂设备(自动创建设备节点)
static struct miscdevice misc =
{
.minor = MISC_DYNAMIC_MINOR, 自动分配次设备号
.name = DEV_NAME,
.fops = &fops 关联操作函数
};
static int __init led1_init(void)
{
struct device_node * pnode;
const char * pcom;
const char * pname1;
u32 led_array[8] = {0};
int ret = misc_register(&misc); 注册 misc 设备
if(ret < 0)
goto err_misc;
pnode = of_find_node_by_path("/pt_led"); 查找设备树节点
if(pnode == NULL)
{
printk("of_find_node_by_path err\n");
return -1;
}
读取设备树属性
of_property_read_string(pnode, "compatible", &pcom);
of_property_read_string(pnode, "name1", &pname1);
printk("led compatible = %s name1 = %s\n", pcom, pname1);
读取 reg 数组
of_property_read_u32_array(pnode, "reg", led_array, sizeof(led_array) / sizeof(led_array[0]));
sw_mux = ioremap(led_array[0], led_array[1]);
sw_pad = ioremap(led_array[2], led_array[3]);
gpio1_gdir = ioremap(led_array[4], led_array[5]);
gpio1_dr = ioremap(led_array[6], led_array[7]);
printk("led_init ##############\n");
return 0;
err_misc:
misc_deregister(&misc);
printk("led_init failed ret = %d\n", ret);
return ret;
}
static void __exit led1_exit(void)
{
iounmap(gpio1_gdir);
iounmap(gpio1_dr);
iounmap(sw_pad);
iounmap(sw_mux);
misc_deregister(&misc);
printk("led_exit ##############\n");
}
module_init(led1_init);
module_exit(led1_exit);
MODULE_LICENSE("GPL");
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)