一、代码结构解析
1. 设备结构体定义(核心数据结构)
c
复制
struct xxx_dev { // 自定义设备结构体
struct cdev cdev; // 内嵌字符设备结构体
/\* 可扩展其他成员:如 GPIO 编号、锁、缓冲区等 \*/
};
struct xxx_dev xxxdev; // 全局设备实例
作用:描述设备的属性和状态,将字符设备 (
cdev
) 与平台驱动关联。扩展性:可添加硬件资源(如 GPIO)、同步机制(如互斥锁)等成员。
2. 字符设备操作函数
c
复制
// 打开设备
static int xxx_open (struct inode *inode, struct file *filp) {
// 示例代码未实现具体操作,实际可能需要:
// - 初始化硬件(如配置 GPIO 方向)
// - 分配私有数据并关联到 filp->private\_data
return 0;
}
// 写入设备
static ssize_t xxx_write (struct file *filp, const char __user *buf,
size\_t cnt, loff\_t \*offt) {
// 示例代码未实现具体操作,实际可能需要:
// - 从用户空间复制数据(copy\_from\_user)
// - 控制硬件(如点亮 LED)
return 0;
}
// 文件操作集
static struct file_operations xxx_fops = {
.owner \= THIS\_MODULE,
.open \= xxx\_open,
.write \= xxx\_write,
// 可添加更多操作:. read, .release, .ioctl 等
};
关键点:通过文件接口(如
/dev/xxx
)暴露设备功能。用户空间交互:
open
、write
等函数是用户调用open()
和write()
系统调用的内核入口。
3. 平台驱动核心逻辑
c
复制
// Probe 函数:驱动与设备匹配成功后调用
static int xxx_probe (struct platform_device *pdev) {
// 1. 初始化字符设备
cdev\_init (&xxxdev. cdev, &xxx\_fops);
cdev\_add (&xxxdev. cdev, dev, 1); // 需补充设备号 (dev) 分配逻辑
// 2. 获取硬件资源(示例未实现,实际需添加):
// struct resource \*res = platform\_get\_resource(pdev, IORESOURCE\_MEM, 0);
// int irq = platform\_get\_irq(pdev, 0);
// 3. 创建设备节点(示例未实现,需补充):
// device\_create(class, NULL, dev, NULL, "xxx");
return 0;
}
// Remove 函数:驱动卸载或设备移除时调用
static int xxx_remove (struct platform_device *pdev) {
cdev\_del (&xxxdev. cdev); // 删除字符设备
// 需补充资源释放:如释放内存、GPIO、中断等
return 0;
}
// 设备树匹配表
static const struct of_device_id xxx_of_match[] = {
{ .compatible \= "xxx-gpio" }, // 必须与设备树中的 compatible 属性一致
{ /\* Sentinel \*/ }
};
// 平台驱动结构体
static struct platform_driver xxx_driver = {
.driver \= {
.name \= "xxx", // 驱动名称(与设备树无关,用于旧式匹配)
.of\_match\_table \= xxx\_of\_match, // 设备树匹配表
},
.probe \= xxx\_probe,
.remove \= xxx\_remove,
};
关键点:
设备树匹配:通过
.compatible
字段与设备树节点绑定。资源管理:实际开发中需在
probe
中调用platform_get_resource
获取内存、中断等资源。设备节点创建:需补充
class_create
和device_create
逻辑以生成/dev/xxx
节点。
4. 模块加载与卸载
c
复制
// 模块初始化
static int __init xxxdriver_init (void) {
return platform\_driver\_register (&xxx\_driver);
}
// 模块卸载
static void __exit xxxdriver_exit (void) {
platform\_driver\_unregister (&xxx\_driver);
}
module_init (xxxdriver_init);
module_exit (xxxdriver_exit);
MODULE_LICENSE (“GPL”);
MODULE_AUTHOR (“zuozhongkai”);
注册流程:
platform_driver_register
将驱动注册到平台总线。自动匹配:内核自动调用
probe
函数匹配设备。
二、执行流程分析
1. 驱动加载阶段
模块加载:执行
insmod
或modprobe
加载驱动。注册平台驱动:
platform_driver_register(&xxx_driver)
。总线匹配:内核遍历已注册的平台设备,检查设备树节点的
.compatible
是否匹配xxx_of_match
。执行 Probe:匹配成功后调用
xxx_probe
函数:初始化字符设备(
cdev_init
和cdev_add
)。获取硬件资源(如寄存器地址、中断号)。
创建设备节点(需补充
device_create
)。
2. 用户空间操作
打开设备:用户调用
open("/dev/xxx")
→ 触发内核调用xxx_open
。写入设备:用户调用
write()
→ 触发内核调用xxx_write
,实际控制硬件。
3. 驱动卸载阶段
模块卸载:执行
rmmod
卸载驱动。调用 Remove:内核调用
xxx_remove
函数:删除字符设备(
cdev_del
)。释放资源(如内存、中断、GPIO)。
三、关键改进点(示例代码缺失部分)
1. 设备号分配与节点创建
需补充以下逻辑:
c
复制
// 在模块初始化或 probe 函数中:
dev_t devno = MKDEV (major, minor); // 动态或静态分配设备号
alloc_chrdev_region (&devno, 0, 1, “xxx”); // 动态分配
cdev_add (&xxxdev. cdev, devno, 1);
// 创建设备类与节点
struct class *class = class_create (THIS_MODULE, “xxx_class”);
device_create (class, NULL, devno, NULL, “xxx”);
2. 硬件资源获取
在 probe
函数中添加资源解析:
c
复制
struct resource *res = platform_get_resource (pdev, IORESOURCE_MEM, 0);
void __iomem *base = devm_ioremap_resource (&pdev->dev, res); // 映射寄存器
int irq = platform_get_irq (pdev, 0);
request_irq (irq, irq_handler, IRQF_TRIGGER_RISING, “xxx”, NULL);
3. 错误处理
检查关键函数返回值:
c
复制
if (cdev_add (&xxxdev. cdev, devno, 1) < 0) {
printk (KERN\_ERR "Failed to add cdev\\n");
return \-EFAULT;
}
四、总结
代码定位:这是一个平台驱动与字符设备驱动结合的框架,适合通过设备树管理硬件资源,并通过文件接口暴露设备功能。
核心流程:驱动匹配 → 资源获取 → 字符设备注册 → 用户空间交互。
实际开发:需补充设备号分配、资源管理、错误处理和并发控制逻辑。