一、系统调用处理函数
open 初始化,read/write 传数据(必须用 copy 函数),release 清理。
在此先定义一个结构体
struct mydev_data {
char *buffer; /* 数据缓冲区 */
int buffer_len; /* 当前数据长度 */
int open_count; /* 打开次数统计 */
};
1、open
open 函数:用户调用 open() 时触发
static int my_open(struct inode *inode, struct file *file)
| 参数 | 说明 |
|---|---|
inode | 设备文件的 inode,可获取次设备号 iminor(inode) |
file | 文件结构体,可保存私有数据 file->private_data |
static int my_open(struct inode *inode, struct file *file)
{
/* 保存私有数据,供 read/write 使用 */
file->private_data = my_data;
/* 初始化硬件、计数等 */
my_data->open_count++;
return 0; /* 必须返回0表示成功 */
}
2、read
read 函数:用户调用 read() 时触发
static ssize_t my_read(struct file *file, char __user *buf, size_t len, loff_t *off)
| 参数 | 说明 |
|---|---|
file | 文件结构体 |
buf | 用户空间的缓冲区指针 |
len | 请求读取的字节数 |
off | 文件偏移指针 |
| 返回值 | 成功:读取的字节数;失败:负数错误码 |
struct my_device {
char buffer[256];
int buffer_len;
};
static ssize_t my_read(struct file *file, char __user *buf, size_t len, loff_t *off)
{
struct my_device *dev = file->private_data;
size_t available; /* 可读的数据量 */
size_t to_read; /* 实际要读的数据量 */
/* 计算从当前位置还有多少数据可读 */
available = dev->buffer_len - *off;
if (available == 0) {
return 0; /* EOF */
}
/* 用户要的长度和可用长度,取较小的 */
to_read = (len < available) ? len : available;
/* 拷贝数据到用户空间 */
if (copy_to_user(buf, dev->buffer + *off, to_read)) {
printk(KERN_ERR "copy_to_user failed\n");
return -EFAULT;
}
/* 更新偏移 */
*off += to_read;
printk(KERN_INFO "read %zu bytes, offset=%lld\n", to_read, *off);
return to_read;
}
3、write
write 函数:用户调用 write() 时触发
static ssize_t my_write(struct file *file, const char __user *buf, size_t len, loff_t *off)
| 参数 | 说明 |
|---|---|
file | 文件结构体 |
buf | 用户空间的缓冲区指针 |
len | 请求写入的字节数 |
off | 文件偏移指针 |
| 返回值 | 成功:写入的字节数;失败:负数错误码 |
struct my_device {
char buffer[256];
int buffer_len;
};
static ssize_t my_write(struct file *file, const char __user *buf, size_t len, loff_t *off)
{
struct my_device *dev = file->private_data;
size_t to_write;
/* 限制写入长度,防止溢出 */
to_write = (len < sizeof(dev->buffer) - 1) ? len : sizeof(dev->buffer) - 1;
if (to_write == 0) {
return -ENOMEM;
}
/* 从用户空间拷贝数据 */
if (copy_from_user(dev->buffer, buf, to_write)) {
printk(KERN_ERR "copy_from_user failed\n");
return -EFAULT;
}
/* 添加字符串结束符 */
dev->buffer[to_write] = '\0';
dev->buffer_len = to_write;
/* 写完后重置读偏移 */
*off = 0;
printk(KERN_INFO "wrote %zu bytes: %s\n", to_write, dev->buffer);
return to_write;
}
4、release
release 函数:用户调用 close() 时触发
static int my_release(struct inode *inode, struct file *file)
5、常用辅助函数
copy_to_user / copy_from_user
// 从内核空间拷贝到用户空间(必须!)
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);// 从用户空间拷贝到内核空间(必须!)
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
| 参数 | 说明 |
|---|---|
to | 用户空间的目的地址(__user 修饰) |
from | 内核空间的源地址 |
n | 要拷贝的字节数 |
返回值:0--全部拷贝成功,非零--失败的字节数
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);
| 参数 | 说明 |
|---|---|
to | 内核空间的目的地址 |
from | 用户空间的源地址(__user 修饰) |
n | 要拷贝的字节数 |
返回值:0--全部拷贝成功,非零--失败的字节数
6、用户空间测试实例
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd;
char write_buf[] = "Hello from userspace!";
char read_buf[256] = {0};
int ret;
// 打开设备
fd = open("/dev/mychardev", O_RDWR);
if (fd < 0) {
perror("open");
return -1;
}
printf("open success\n");
// 写入数据
ret = write(fd, write_buf, strlen(write_buf));
printf("write %d bytes: %s\n", ret, write_buf);
// 读取数据
ret = read(fd, read_buf, sizeof(read_buf));
printf("read %d bytes: %s\n", ret, read_buf);
// 关闭设备
close(fd);
printf("close success\n");
return 0;
}
二、模块框架函数
| 函数 | 调用时机 | 作用 |
|---|---|---|
module_init() | insmod 时 | 模块入口,初始化 |
module_exit() | rmmod 时 | 模块出口,清理 |
probe | 设备匹配成功时 | 初始化具体设备 |
remove | 设备移除时 | 清理具体设备 |
1、module_init
在 insmod 加载模块时执行,做全局的一次性初始化
static int __init my_init(void)
{
int ret;
/* 分配全局内存(如果多个设备共享) */
g_shared_buffer = kmalloc(1024, GFP_KERNEL);
if (!g_shared_buffer)
return -ENOMEM;
/* 注册 platform 驱动 */
ret = platform_driver_register(&my_driver);
if (ret) {
kfree(g_shared_buffer);
return ret;
}
/* 注册字符设备(如果不依赖设备树) */
ret = misc_register(&my_misc);
if (ret) {
platform_driver_unregister(&my_driver);
kfree(g_shared_buffer);
return ret;
}
pr_info("init success\n");
return 0;
}
2、module_exit
在 rmmod 卸载模块时执行,清理 init 做的工作。
static void __exit my_exit(void)
{
/* 1. 注销注册过的资源(与 init 顺序相反) */
misc_deregister(&my_misc);
platform_driver_unregister(&my_driver);
/* 2. 释放分配的内存 */
kfree(g_shared_buffer);
pr_info("module unloaded\n");
}
3、probe
在 设备树匹配成功时调用,初始化具体的一个设备
static int my_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct my_device *priv;
struct resource *res;
int ret;
/* ========== 1. 分配私有数据结构 ========== */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* 保存私有数据,remove 时能用 */
platform_set_drvdata(pdev, priv);
priv->pdev = pdev;
/* ========== 2. 获取寄存器地址(ioremap) ========== */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory resource\n");
return -ENXIO;
}
priv->regs = devm_ioremap(dev, res->start, resource_size(res));
if (!priv->regs) {
dev_err(dev, "ioremap failed\n");
return -ENOMEM;
}
/* ========== 3. 获取中断号并注册中断 ========== */
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0)
return priv->irq; /* 中断是必须的 */
ret = devm_request_irq(dev, priv->irq, my_isr, IRQF_TRIGGER_RISING,
"my_device", priv);
if (ret) {
dev_err(dev, "request_irq failed\n");
return ret;
}
/* ========== 4. 获取 GPIO ========== */
priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(priv->reset_gpio)) {
dev_err(dev, "failed to get reset gpio\n");
return PTR_ERR(priv->reset_gpio);
}
/* ========== 5. 从设备树读取属性 ========== */
if (device_property_read_u32(dev, "my-value", &priv->value))
priv->value = 0; /* 默认值 */
/* ========== 6. 初始化硬件 ========== */
/* 复位设备 */
gpiod_set_value(priv->reset_gpio, 1);
msleep(10);
gpiod_set_value(priv->reset_gpio, 0);
msleep(50);
/* 写寄存器初始化 */
writel(0x01, priv->regs + REG_CTRL);
/* ========== 7. 创建字符设备或注册到子系统 ========== */
ret = misc_register(&priv->miscdev);
if (ret) {
dev_err(dev, "misc_register failed\n");
return ret;
}
/* ========== 8. 创建 sysfs 文件(可选) ========== */
priv->sysfs_node = sysfs_create_group(&dev->kobj, &my_attr_group);
dev_info(dev, "probe success\n");
return 0;
}
4、remove
在 设备移除或模块卸载时调用,清理 probe 做的工作
static int my_remove(struct platform_device *pdev)
{
struct my_device *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
/* ========== 1. 注销注册过的资源(与 probe 顺序相反) ========== */
/* 先删除 sysfs */
sysfs_remove_group(&dev->kobj, &my_attr_group);
/* 再注销 misc 设备 */
misc_deregister(&priv->miscdev);
/* ========== 2. 停止硬件 ========== */
/* 关闭中断、停止 DMA、禁用设备等 */
writel(0x00, priv->regs + REG_CTRL); /* 禁用设备 */
writel(0x00, priv->regs + REG_INTEN); /* 屏蔽中断 */
/* ========== 3. 释放 GPIO(devm_ 会自动释放,但手动复位一下) ========== */
gpiod_set_value(priv->reset_gpio, 1); /* 复位设备,让给其它驱动 */
/* ========== 4. devm_ 分配的资源会自动释放,不用手动 free ========== */
dev_info(dev, "remove success\n");
return 0;
}
| 函数 | 必须写 | 可选写 |
|---|---|---|
| init | 注册驱动(platform_driver_register) | 分配全局内存、注册全局字符设备 |
| exit | 注销驱动(platform_driver_unregister) | 释放全局内存 |
| probe | 分配私有数据、获取资源、初始化硬件、注册到子系统 | 创建 sysfs、注册中断 |
| remove | 注销子系统注册、停止硬件 | 删除 sysfs、释放私有数据(devm 自动) |
三、如何匹配设备树
1. 定义设备树匹配表
static const struct of_device_id of_gpio_leds_match[] = {
{ .compatible = "gpio-leds", }, // 匹配 compatible 属性为 "gpio-leds"
{}, // 空哨兵,表示表结束
};

2. 导出设备表
MODULE_DEVICE_TABLE(of, of_gpio_leds_match);

3.定义platform_driver 结构体
static struct platform_driver gpio_led_driver = {
.probe = gpio_led_probe,
.shutdown = gpio_led_shutdown,
.driver = {
.name = "leds-gpio",
.of_match_table = of_gpio_leds_match, // 👈 关键!
},
};

4.匹配流程


4997

被折叠的 条评论
为什么被折叠?



