目录
- 说明
- 模板
- 跟正点原子代码的差异
-
- 驱动的进入和退出在哪里?
- [为什么要有 MODULE_DEVICE_TABLE](#为什么要有 MODULE_DEVICE_TABLE)
说明
- 这是一个ai生成的模板
- 生成这个模板主要是因为我看了正点原子的视频,但是都没有总结过,但是每次开发写的东西都大差不差的
模板
跟ai的对话:
写一个linux下驱动开发模板,基于platform,匹配设备树对应的compatible,设备号可以自主定义,或者交给api生成,最后在dev下生成设备
补齐上面的模板,可以执行read,write,open,release等操作
alloc_chrdev_region等错误,可以使用goto作为统一处理,避免出现过多重复释放的代码
c
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
// 定义驱动名称和作者
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Template Platform Driver");
MODULE_LICENSE("GPL");
// 定义设备类
static struct class *my_class = NULL;
// 定义设备号结构体
static dev_t my_devt;
// 定义设备结构体
struct my_device {
struct cdev cdev;
struct device *dev;
};
// 定义设备实例
static struct my_device *my_device_instance;
// 定义设备打开函数
static int my_device_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "My Device Opened\n");
return 0;
}
// 定义设备释放函数
static int my_device_release(struct inode *inode, struct file *file) {
printk(KERN_INFO "My Device Released\n");
return 0;
}
// 定义设备读函数
static ssize_t my_device_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
char temp_buffer[256];
const char *message = "Hello, World!\n";
int length = strlen(message);
if (*ppos > 0) {
return 0;
}
if (count > length) {
count = length;
}
if (copy_to_user(buf, message, count)) {
return -EFAULT;
}
*ppos += count;
return count;
}
// 定义设备写函数
static ssize_t my_device_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
char temp_buffer[256];
if (*ppos > 0) {
return 0;
}
if (count > sizeof(temp_buffer) - 1) {
count = sizeof(temp_buffer) - 1;
}
if (copy_from_user(temp_buffer, buf, count)) {
return -EFAULT;
}
temp_buffer[count] = '\0';
printk(KERN_INFO "My Device Write: %s\n", temp_buffer);
*ppos += count;
return count;
}
// 定义文件操作结构体
static const struct file_operations my_device_fops = {
.owner = THIS_MODULE,
.open = my_device_open,
.read = my_device_read,
.write = my_device_write,
.release = my_device_release,
};
// 驱动初始化函数
static int my_driver_probe(struct platform_device *pdev) {
int ret;
printk(KERN_INFO "My Driver Probe Called\n");
// 检查设备树中的compatible属性
if (!of_device_is_compatible(pdev->dev.of_node, "vendor,devicename")) {
dev_err(&pdev->dev, "Device not compatible\n");
return -ENODEV;
}
// 创建设备类
my_class = class_create(THIS_MODULE, "my_driver_class");
if (IS_ERR(my_class)) {
ret = PTR_ERR(my_class);
dev_err(&pdev->dev, "Failed to create class\n");
return ret;
}
// 动态分配设备号
ret = alloc_chrdev_region(&my_devt, 0, 1, "my_driver");
if (ret < 0) {
dev_err(&pdev->dev, "Failed to allocate char device region\n");
class_destroy(my_class);
return ret;
}
// 创建设备实例
my_device_instance = devm_kzalloc(&pdev->dev, sizeof(struct my_device), GFP_KERNEL);
if (!my_device_instance) {
ret = -ENOMEM;
goto err_device_instance;
}
// 初始化cdev
cdev_init(&my_device_instance->cdev, &my_device_fops);
my_device_instance->cdev.owner = THIS_MODULE;
// 添加cdev
ret = cdev_add(&my_device_instance->cdev, my_devt, 1);
if (ret) {
dev_err(&pdev->dev, "Failed to add cdev\n");
goto err_cdev_add;
}
// 创建设备
my_device_instance->dev = device_create(my_class, NULL, my_devt, NULL, "my_device%d", MINOR(my_devt));
if (IS_ERR(my_device_instance->dev)) {
ret = PTR_ERR(my_device_instance->dev);
dev_err(&pdev->dev, "Failed to create device\n");
goto err_device_create;
}
platform_set_drvdata(pdev, my_device_instance);
return 0;
err_device_create:
cdev_del(&my_device_instance->cdev);
err_cdev_add:
devm_kfree(&pdev->dev, my_device_instance);
err_device_instance:
unregister_chrdev_region(my_devt, 1);
class_destroy(my_class);
return ret;
}
// 驱动退出函数
static int my_driver_remove(struct platform_device *pdev) {
struct my_device *dev = platform_get_drvdata(pdev);
device_destroy(my_class, MKDEV(MAJOR(my_devt), MINOR(my_devt)));
cdev_del(&dev->cdev);
devm_kfree(&pdev->dev, dev);
unregister_chrdev_region(my_devt, 1);
class_destroy(my_class);
printk(KERN_INFO "My Driver Remove Called\n");
return 0;
}
// 设备树中compatible属性匹配表
static const struct of_device_id my_driver_dt_ids[] = {
{ .compatible = "vendor,devicename" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_driver_dt_ids);
// 定义platform_driver结构体
static struct platform_driver my_driver = {
.probe = my_driver_probe,
.remove = my_driver_remove,
.driver = {
.name = "my_platform_driver",
.of_match_table = my_driver_dt_ids,
.owner = THIS_MODULE,
},
};
module_platform_driver(my_driver);
跟正点原子代码的差异
驱动的进入和退出在哪里?
简单来说这个module_platform_driver函数可以替代module_init()、module_exit();
为什么要有 MODULE_DEVICE_TABLE
问AI:总结来说,MODULE_DEVICE_TABLE 宏和 platform_driver 结构体中的 .of_match_table 字段都用于设备和驱动的匹配,但它们在某些情况下可以提供不同的功能和好处。如果您的驱动已经通过 .of_match_table 正确匹配设备,并且没有特殊的热插拔或其他需求,那么您可能不需要额外使用 MODULE_DEVICE_TABLE 宏。