一、miscdevice :
只有基础设备节点,无 /sys/class/misc/myled/ 详细属性
cpp
// misc_register() 内部实现
int misc_register(struct miscdevice *misc)
{
// ... 省略错误检查
dev = MKDEV(MISC_MAJOR, misc->minor); // 强制主设备号10
cdev_init(&misc->cdev, misc->fops); // 内部使用cdev
cdev_add(&misc->cdev, dev, 1);
device_create(misc_class, NULL, dev, misc, "%s", misc->name);
}
miscdevice 本质上是预配置好设备号、类、节点名称的 cdev 快捷方式 。
二、cdev + class :
- 自动生成完整的
/sys/class/myled_class/myled/目录,支持udev规则
cpp
static int __init my_init(void)
{
// 1. 分配设备号
alloc_chrdev_region(&dev_num, 0, 1, "myled");
// 2. 初始化cdev
cdev_init(&my_cdev, &led_fops);
cdev_add(&my_cdev, dev_num, 1);
// 3. 创建sysfs类
my_class = class_create(THIS_MODULE, "myled_class");
// 4. 创建设备节点
device_create(my_class, NULL, dev_num, NULL, "myled");
return 0;
}
cpp
1. 旧式简化API(已不推荐使用)
// 一句话完成:注册主设备号 + cdev_init/add
// 缺点:无法动态次设备号、不灵活、隐藏细节
register_chrdev(major, "name", &fops);


- Platform_Driver
- 私有数据管理方法1:
cpp
// 你的私有数据结构
struct my_private_data {
struct gpio_desc *led;
int value;
};
static int example_probe(struct platform_device *pdev)
{
// 在probe中赋值
pdev->dev.driver_data = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
// 问题1:无类型检查,任何void*都能赋值
// 问题2:在remove中使用时:
struct my_private_data *priv = pdev->dev.driver_data; // 隐式转换,无警告
}
- 方私有数据管理方法2(好):
cpp
// 在probe中:
static int example_probe(struct platform_device *pdev)
{
struct my_private_data *priv = devm_kzalloc(...);
platform_set_drvdata(pdev, priv); // 宏内部有类型检查
}
static int example_remove(struct platform_device *pdev)
{
// 在remove中:
struct my_private_data *priv = platform_get_drvdata(pdev); // 返回正确类型
}
关键实现(include/linux/platform_device.h):
static inline void *platform_get_drvdata(const struct platform_device *pdev)
{
return dev_get_drvdata(&pdev->dev);
}
static inline void platform_set_drvdata(struct platform_device *pdev, void *data)
{
dev_set_drvdata(&pdev->dev, data);
}
早期设备树(Device Tree)
某些驱动让设备树或ACPI负责设备宣告:
cpp
// 只需注册cdev,设备文件由dt-overlay描述
// 内核自动解析并创建,无需手动class_create


三、设备树
生成plateform_device
设备树生成 platform_device 是 Linux 内核启动时的核心流程 ,由 drivers/of/platform.c 驱动完成。以下是完整机制解析:
在内核启动早期,arch/arm/mach-xxx/ 或通用代码调用:
cpp
// arch/arm/mach-imx/mach-imx8m.c
void __init imx8m_init_machine(void)
{
of_platform_populate(NULL, NULL, NULL, NULL); // ← 关键函数
}
该函数启动对整个设备树的递归扫描 ,将符合条件的节点 转换为 platform_device。
在 Linux 内核中,platform_device 注册后在 sysfs 中生成的是文件夹(目录),而不是文件。
Platform_device 设备与字符设备的关系
如果给 Platform_device添加ATTR
具体的方法:
在platreform_driver_xxx.c 中添加
cpp
// 显示当前 DMA 状态
static ssize_t dma_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct example_driver_data *drv_data = platform_get_drvdata(pdev);
if (!drv_data) {
return -ENODEV;
}
// 返回当前 DMA 模式的字符串表示
switch (drv_data->dma_mode) {
case 0:
return sprintf(buf, "disabled\n");
case 1:
return sprintf(buf, "direct\n");
case 2:
return sprintf(buf, "scatter-gather\n");
default:
return sprintf(buf, "unknown (%d)\n", drv_data->dma_mode);
}
}
// 动态设置 DMA 状态
static ssize_t dma_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct example_driver_data *drv_data = platform_get_drvdata(pdev);
int mode;
int ret;
if (!drv_data) {
return -ENODEV;
}
// 从用户空间读取模式值
ret = kstrtoint(buf, 10, &mode);
if (ret < 0) {
// 尝试解析字符串形式的模式
if (strncmp(buf, "disabled", 8) == 0) {
mode = 0;
} else if (strncmp(buf, "direct", 6) == 0) {
mode = 1;
} else if (strncmp(buf, "scatter-gather", 14) == 0) {
mode = 2;
} else {
return -EINVAL; // 无效的模式值
}
}
// 验证模式值是否有效
if (mode < 0 || mode > 2) {
return -EINVAL;
}
// 设置新的 DMA 模式
drv_data->dma_mode = mode;
printk(KERN_INFO "Example platform driver: DMA mode set to %d\n", mode);
return count;
}
// 定义设备属性
static DEVICE_ATTR_RW(dma_mode);
// DEVICE_ATTR_RW 宏展开后自动创建:
// - dma_mode 属性
// - .show = dma_mode_show (读回调)
// - .store = dma_mode_store (写回调)
在probe 中添加
cpp
static int example_probe(struct platform_device *pdev)
{
---------其它代码省略----------
/* 创建 sysfs 属性文件 - 在 platform 设备目录下 */
ret = device_create_file(&pdev->dev, &dev_attr_dma_mode);
if (ret < 0) {
printk(KERN_ERR "Failed to create sysfs attribute: %d\n", ret);
return ret;
}
}
在这个probe代码中创建了字符设备,接着注册了sysfs 文件属性,我们目前的实现在 platform 设备下创建了 dma_mode 属性文件,但这确实引发了一个关于设备属性归属的问题。让我来解释一下:
在 Linux 驱动模型中,这是两个不同但相关的概念:
-
Platform 设备 :代表底层硬件抽象,是硬件与驱动程序之间的接口
-
字符设备 :是提供给用户空间程序访问的设备节点
应该如何处理?
这取决于您的设计意图:
- 如果 dma_mode 是硬件配置参数 :
-
当前实现在 platform 设备下创建是合理的,因为它代表硬件配置
-
这使得属性文件出现在 /sys/devices/platform/example-pdrv/ 目录下
- 如果 dma_mode 是字符设备的运行时配置 :
-
应该在字符设备上创建设备属性
-
这样属性文件会出现在 /sys/class/example-driver/example-driver/ 目录下
设备级属性(platform_device)
/sys/devices/platform/soc/4000f000.serial/dma_mode
↑修改只影响USART3
类级属性(class)
/sys/class/stm32-uart/global_log_level
↑修改影响所有STM32 UART设备
四、转换流程(四步过滤)
Step 1:设备树节点解析
cpp
// drivers/of/platform.c
static int of_platform_bus_create(...)
{
struct device_node *child;
// 遍历所有子节点
for_each_child_of_node(bus, child) {
of_platform_device_create_pdata(child, ...);
}
}
Step 2:四重过滤判断
内核严格检查节点属性,任一条件不满足则跳过:
cpp
static struct platform_device *of_platform_device_create_pdata(...)
{
struct device_node *np = pdev->dev.of_node;
// ① 必须有 compatible 属性
if (!of_get_property(np, "compatible", NULL))
return NULL; // ❌ 无compatible,不是设备
// ② status 必须是 "ok" 或 "okay"
if (!of_device_is_available(np))
return NULL; // ❌ status = "disabled"
// ③ 不能有 "interrupt-parent" 且父节点是根节点(特殊情况)
// ④ 必须匹配父级的 #address-cells 和 #size-cells(总线设备)
// ✅ 通过所有检查,创建 platform_device
return of_device_alloc(np, bus_id, parent);
}
Step 3:创建 platform_device 实例
通过检查的设备节点被转换为内核结构体:
cpp
struct platform_device *of_device_alloc(...)
{
struct platform_device *pdev;
// 1. 分配 platform_device 结构体
pdev = platform_device_alloc("", PLATFORM_DEVID_NONE);
// 2. 关联 device_node(保留设备树信息)
pdev->dev.of_node = of_node_get(np);
// 3. 解析 reg 属性,填充 resource(IO内存/中断)
of_address_to_resource(np, 0, &res); // 解析reg为IO内存资源
of_irq_to_resource(np, 0, &res); // 解析interrupts为中断资源
// 4. 注册到 platform 总线
platform_device_add(pdev);
return pdev;
}
Step 4:与 platform_driver 匹配
生成的 platform_device 等待匹配的驱动:
cpp
// drivers/base/platform.c
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
// 1. 优先匹配 of_node 的 compatible
of_driver_match_device(dev, drv);
// 对比 pdev->dev.of_node->compatible 和 pdrv->driver->of_match_table
// 2. 备选:匹配 platform_device_id
// 3. 备选:匹配设备名
}
5:设备树和plateform_device 对应关系
1. 设备树节点(dts源码)
cpp
usart3: serial@40004800 {
compatible = "st,stm32-usart", "st,stm32-uart"; // ①
reg = <0x40004800 0x400>; // ②
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>; // ③
clocks = <&rcc 0 STM32_AHB1_CLOCK(RCC_AHB1ENR_USART3EN)>; // ④
status = "okay"; // ⑤
};
2. 内核生成的 platform_device 结构体
cpp
// 内核自动填充(drivers/of/platform.c 中创建)
struct platform_device {
.name = "40004800.serial", // 来自节点名 serial@40004800
.id = PLATFORM_DEVID_NONE, // 无ID
.dev = {
.of_node = { // 保留完整的设备树节点
.name = "serial",
.full_name = "/soc/serial@40004800",
.properties = {
{ .name = "compatible", .value = "st,stm32-usart\0st,stm32-uart\0" },
{ .name = "reg", .value = <0x40004800 0x400> },
{ .name = "interrupts", .value = <...> },
{ .name = "clocks", .value = <...> },
{ .name = "status", .value = "okay\0" },
}
},
.platform_data = NULL, // 通常不用,驱动直接从of_node解析
.dma_mask = &some_mask,
.coherent_dma_mask = 0xffffffff,
},
.resource = { // 自动解析 reg 和 interrupts 生成
[0] = { // reg 解析为 IORESOURCE_MEM
.start = 0x40004800,
.end = 0x40004800 + 0x400 - 1,
.flags = IORESOURCE_MEM,
.name = NULL, // 使用节点名
},
[1] = { // interrupts 解析为 IORESOURCE_IRQ
.start = 39, // IRQ号
.end = 39,
.flags = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
.name = NULL,
}
},
.num_resources = 2, // 2个资源项(MEM + IRQ)
};
3. 生成的 sysfs 文件系统路径
cpp
# 设备注册后,出现在 /sys/devices 和 /sys/bus/platform
$ ls -l /sys/devices/platform/soc/40004800.serial/
drwxr-xr-x 4 root root 0 Jan 1 00:00 power/
-rw-r--r-- 1 root root 4096 Jan 1 00:00 driver_override
-r--r--r-- 1 root root 4096 Jan 1 00:00 modalias
lrwxrwxrwx 1 root root 0 Jan 1 00:00 driver -> ../../../bus/platform/drivers/stm32-usart
lrwxrwxrwx 1 root root 0 Jan 1 00:00 of_node -> ../../../firmware/devicetree/base/soc/serial@40004800
-r--r--r-- 1 root root 4096 Jan 1 00:00 resource0 # 对应 reg 的内存映射
-r--r--r-- 1 root root 4096 Jan 1 00:00 resource1 # 对应 interrupts 的IRQ号
# 驱动匹配后,生成 /sys/class/tty/ 下的设备节点
$ ls -l /sys/class/tty/ttySTM3
lrwxrwxrwx 1 root root 0 Jan 1 00:00 /sys/class/tty/ttySTM3 -> ../../devices/platform/soc/40004800.serial/tty/ttySTM3
4. 驱动匹配(platform_driver)
cpp
// drivers/tty/serial/stm32-usart.c
static const struct of_device_id stm32_match[] = {
{ .compatible = "st,stm32-usart" }, // ① 匹配设备树 compatible
{ .compatible = "st,stm32-uart" },
{}
};
MODULE_DEVICE_TABLE(of, stm32_match);
static struct platform_driver stm32_usart_driver = {
.probe = stm32_usart_probe, // ② 匹配成功后调用
.remove = stm32_usart_remove,
.driver = {
.name = "stm32-usart",
.of_match_table = stm32_match, // ③ 绑定匹配表
},
};
// 匹配成功后,内核自动调用
static int stm32_usart_probe(struct platform_device *pdev)
{
// ④ 从 platform_device 提取资源
struct resource *res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
// ⑤ 从 of_node 提取时钟、DMA等复杂属性
struct device_node *np = pdev->dev.of_node;
clk = of_clk_get(np, 0);
// ⑥ 请求内存区域
request_mem_region(res_mem->start, resource_size(res_mem), "stm32-usart");
// ⑦ ioremap 映射到内核虚拟地址
base = ioremap(res_mem->start, resource_size(res_mem));
// ⑧ 注册中断
request_irq(res_irq->start, stm32_irq, IRQF_TRIGGER_HIGH, "stm32-usart", port);
// ⑨ 注册 tty 设备
uart_add_one_port(&stm32_uart_driver, &stm32_port->port);
return 0;
}
5. 用户空间最终看到的设备
cpp
# 字符设备节点(由 tty 子系统创建)
$ ls -l /dev/ttySTM3
crw-rw---- 1 root dialout 250, 0 Jan 1 00:00 /dev/ttySTM3
# 设备树原始信息
$ ls -l /sys/firmware/devicetree/base/soc/serial@40004800/
-r--r--r-- 1 root root 8 Jan 1 00:00 name
-r--r--r-- 1 root root 24 Jan 1 00:00 compatible
-r--r--r-- 1 root root 8 Jan 1 00:00 reg
-r--r--r-- 1 root root 4 Jan 1 00:00 interrupts
-r--r--r-- 1 root root 4 Jan 1 00:00 clocks
五、plateform_device 和plateform_driver
Makefile
cpp
#Makefile
obj-m += platform_device_example.o
obj-m += platform_driver_example.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
platform_device_example.c
cpp
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/err.h>
/* 使用0避免与系统资源冲突 */
#define EXAMPLE_DEV_MEM_BASE 0x0 /* 使用0避免冲突 */
#define EXAMPLE_DEV_MEM_SIZE 0x1000
#define EXAMPLE_DEV_IRQ 0 /* 使用0表示无真实中断 */
/* 定义设备资源 - 在Ubuntu测试环境中使用虚拟资源 */
static struct resource example_resources[] = {
/* 注释掉可能导致冲突的资源定义 */
/*
[0] = {
.start = EXAMPLE_DEV_MEM_BASE,
.end = EXAMPLE_DEV_MEM_BASE + EXAMPLE_DEV_MEM_SIZE - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = EXAMPLE_DEV_IRQ,
.end = EXAMPLE_DEV_IRQ,
.flags = IORESOURCE_IRQ,
},
*/
};
/* 设备私有数据 */
static struct example_dev_data {
int version;
char name[32];
} example_data = {
.version = 1,
.name = "example-platform-device",
};
/* 定义platform_device结构体 */
/* 设备释放函数 - 用于正确管理设备的生命周期 */
static void example_dev_release(struct device *dev)
{
printk(KERN_INFO "Example platform device: release function called\n");
}
static struct platform_device example_platform_device = {
.name = "example-pdrv", /* 必须与platform_driver的name匹配 */
.id = -1, /* 单一设备 */
.num_resources = 0, /* 0个资源,避免冲突 */
.resource = example_resources,
.dev = {
.platform_data = &example_data, /* 传递给驱动的私有数据 */
.release = example_dev_release, /* 设置release函数 */
},
};
static int __init example_device_init(void)
{
int ret;
printk(KERN_INFO "Example platform device: registering\n");
/* 注册platform_device */
ret = platform_device_register(&example_platform_device);
if (ret) {
printk(KERN_ERR "Failed to register platform device: %d\n", ret);
return ret;
}
printk(KERN_INFO "Example platform device: registered successfully\n");
return 0;
}
static void __exit example_device_exit(void)
{
printk(KERN_INFO "Example platform device: unregistering\n");
/* 注销platform_device */
platform_device_unregister(&example_platform_device);
printk(KERN_INFO "Example platform device: unregistered successfully\n");
}
module_init(example_device_init);
module_exit(example_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Example Author");
MODULE_DESCRIPTION("Platform Device Example");
MODULE_VERSION("1.0");
platform_driver_example.c
cpp
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
//#define platform_device_attr 1
/* 设备私有数据结构体 - 与platform_device_example.c中的定义保持一致 */
struct example_dev_data {
int version;
char name[32];
};
/* 驱动私有数据结构 */
struct example_driver_data {
void __iomem *base_addr; /* 映射后的内存地址 */
int irq; /* 中断号 */
struct resource *mem_res; /* 内存资源 */
struct resource *irq_res; /* 中断资源 */
dev_t dev_num; /* 设备号 */
struct cdev cdev; /* 字符设备结构体 */
struct class *class; /* 设备类 */
struct device *device; /* 设备 */
char data[256]; /* 用于演示的数据缓冲区 */
loff_t data_len; /* 数据长度 - 与pos类型保持一致 */
int dma_mode; /* DMA 工作模式:0-关闭,1-直接模式,2-分散/聚集模式 */
};
/* 设备树匹配表 - 用于支持设备树 */
static const struct of_device_id example_of_match[] = {
{ .compatible = "example,example-pdrv", },
{},
};
MODULE_DEVICE_TABLE(of, example_of_match);
/* ID表 - 用于传统匹配方式 */
static const struct platform_device_id example_id_table[] = {
{ "example-pdrv", 0 },
{},
};
MODULE_DEVICE_TABLE(platform, example_id_table);
/* 文件操作函数声明 */
static int example_open(struct inode *inode, struct file *file);
static int example_release(struct inode *inode, struct file *file);
static ssize_t example_read(struct file *file, char __user *buf, size_t count, loff_t *pos);
static ssize_t example_write(struct file *file, const char __user *buf, size_t count, loff_t *pos);
/* 字符设备初始化函数 */
static int example_setup_char_device(struct example_driver_data *drv_data);
/* 字符设备资源清理函数 */
static void example_cleanup_char_device(struct example_driver_data *drv_data);
/* probe函数 - 当设备和驱动匹配成功时调用 */
/* 文件操作结构体定义 */
static struct file_operations example_fops = {
.owner = THIS_MODULE,
.open = example_open,
.release = example_release,
.read = example_read,
.write = example_write,
};
static int example_open(struct inode *inode, struct file *file)
{
struct example_driver_data *drv_data;
drv_data = container_of(inode->i_cdev, struct example_driver_data, cdev);
file->private_data = drv_data;
printk(KERN_INFO "Example driver: device opened\n");
return 0;
}
static int example_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Example driver: device released\n");
return 0;
}
static ssize_t example_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
struct example_driver_data *drv_data = file->private_data;
size_t read_size;
loff_t available_bytes;
if (*pos >= drv_data->data_len) {
return 0; /* 已到达文件末尾 */
}
/* 计算可用字节数 */
available_bytes = drv_data->data_len - *pos;
/* 避免使用min()函数比较不同类型,完全使用条件判断 */
if (available_bytes > (loff_t)count) {
read_size = count;
} else {
/* 直接转换,确保类型安全 */
read_size = (size_t)available_bytes;
}
/* 额外的安全检查 */
if (read_size > count) {
read_size = count;
}
if (copy_to_user(buf, drv_data->data + *pos, read_size)) {
return -EFAULT;
}
*pos += read_size;
printk(KERN_INFO "Example driver: read %zu bytes\n", read_size);
return read_size;
}
static ssize_t example_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
struct example_driver_data *drv_data = file->private_data;
size_t write_size;
if (*pos >= sizeof(drv_data->data)) {
return -ENOSPC; /* 缓冲区已满 */
}
write_size = min(count, sizeof(drv_data->data) - *pos);
if (copy_from_user(drv_data->data + *pos, buf, write_size)) {
return -EFAULT;
}
*pos += write_size;
/* 避免使用max()函数比较不同类型的值 */
if (*pos > drv_data->data_len) {
drv_data->data_len = *pos;
}
printk(KERN_INFO "Example driver: written %zu bytes\n", write_size);
return write_size;
}
// 显示当前 DMA 状态
static ssize_t dma_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
#ifdef platform_device_attr
//将dma_mode属性文件创建在platform设备上
struct platform_device *pdev = to_platform_device(dev);
struct example_driver_data *drv_data = platform_get_drvdata(pdev);
#else
//将dma_mode属性文件创建在字符设备上
struct example_driver_data *drv_data = dev_get_drvdata(dev);
#endif
if (!drv_data) {
return -ENODEV;
}
// 返回当前 DMA 模式的字符串表示
switch (drv_data->dma_mode) {
case 0:
return sprintf(buf, "disabled\n");
case 1:
return sprintf(buf, "direct\n");
case 2:
return sprintf(buf, "scatter-gather\n");
default:
return sprintf(buf, "unknown (%d)\n", drv_data->dma_mode);
}
}
// 动态设置 DMA 状态
static ssize_t dma_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
#ifdef platform_device_attr
//将dma_mode属性文件创建在platform设备上
struct platform_device *pdev = to_platform_device(dev);
struct example_driver_data *drv_data = platform_get_drvdata(pdev);
#else
//将dma_mode属性文件创建在字符设备上
struct example_driver_data *drv_data = dev_get_drvdata(dev);
#endif
int mode;
int ret;
if (!drv_data) {
return -ENODEV;
}
// 从用户空间读取模式值
ret = kstrtoint(buf, 10, &mode);
if (ret < 0) {
// 尝试解析字符串形式的模式
if (strncmp(buf, "disabled", 8) == 0) {
mode = 0;
} else if (strncmp(buf, "direct", 6) == 0) {
mode = 1;
} else if (strncmp(buf, "scatter-gather", 14) == 0) {
mode = 2;
} else {
return -EINVAL; // 无效的模式值
}
}
// 验证模式值是否有效
if (mode < 0 || mode > 2) {
return -EINVAL;
}
// 设置新的 DMA 模式
drv_data->dma_mode = mode;
printk(KERN_INFO "Example platform driver: DMA mode set to %d\n", mode);
return count;
}
// 定义设备属性
static DEVICE_ATTR_RW(dma_mode);
// DEVICE_ATTR_RW 宏展开后自动创建:
// - dma_mode 属性
// - .show = dma_mode_show (读回调)
// - .store = dma_mode_store (写回调)
static int example_probe(struct platform_device *pdev)
{
struct example_driver_data *drv_data;
struct example_dev_data *dev_data;
int ret = 0;
printk(KERN_INFO "Example platform driver: probe function called\n");
/* 分配驱动私有数据 */
drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
if (!drv_data) {
return -ENOMEM;
}
/* 获取设备私有数据 */
dev_data = pdev->dev.platform_data;
if (dev_data) {
printk(KERN_INFO "Example platform driver: device version %d, name %s\n",
dev_data->version, dev_data->name);
}
/* 初始化数据缓冲区 */
strcpy(drv_data->data, "Example Driver Initial Data\n");
drv_data->data_len = (loff_t)strlen(drv_data->data);
/* 初始化 DMA 模式为禁用状态 */
drv_data->dma_mode = 0;
/* 设置字符设备 - 调用独立的函数处理字符设备注册 */
ret = example_setup_char_device(drv_data);
if (ret < 0) {
printk(KERN_ERR "Failed to setup char device: %d\n", ret);
return ret;
}
/* 保存驱动数据到设备中 */
platform_set_drvdata(pdev, drv_data);
#ifdef platform_device_attr
/* 创建 sysfs 属性文件 - 在 platform 设备目录下 */
ret = device_create_file(&pdev->dev, &dev_attr_dma_mode);
if (ret < 0) {
printk(KERN_ERR "Failed to create sysfs attribute: %d\n", ret);
return ret;
}
#else
/* 同时保存驱动数据到字符设备中,供sysfs属性使用 */
dev_set_drvdata(drv_data->device, drv_data);
/* 创建 sysfs 属性文件 - 在字符设备目录下 */
ret = device_create_file(drv_data->device, &dev_attr_dma_mode);
if (ret < 0) {
printk(KERN_ERR "Failed to create sysfs attribute: %d\n", ret);
return ret;
}
#endif
printk(KERN_INFO "Example platform driver: probe successful, device node created\n");
return 0;
}
/* 字符设备初始化函数 */
static int example_setup_char_device(struct example_driver_data *drv_data)
{
int ret = 0;
/* 注册字符设备 */
ret = alloc_chrdev_region(&drv_data->dev_num, 0, 1, "example-driver");
if (ret < 0) {
printk(KERN_ERR "Failed to allocate char device region\n");
return ret;
}
/* 初始化并添加cdev */
cdev_init(&drv_data->cdev, &example_fops);
drv_data->cdev.owner = THIS_MODULE;
ret = cdev_add(&drv_data->cdev, drv_data->dev_num, 1);
if (ret < 0) {
printk(KERN_ERR "Failed to add char device\n");
unregister_chrdev_region(drv_data->dev_num, 1);
return ret;
}
/* 创建设备类 */
drv_data->class = class_create(THIS_MODULE, "example-driver");
if (IS_ERR(drv_data->class)) {
printk(KERN_ERR "Failed to create class\n");
cdev_del(&drv_data->cdev);
unregister_chrdev_region(drv_data->dev_num, 1);
return PTR_ERR(drv_data->class);
}
/* 创建设备节点 */
drv_data->device = device_create(drv_data->class, NULL, drv_data->dev_num, NULL, "example-driver");
if (IS_ERR(drv_data->device)) {
printk(KERN_ERR "Failed to create device\n");
class_destroy(drv_data->class);
cdev_del(&drv_data->cdev);
unregister_chrdev_region(drv_data->dev_num, 1);
return PTR_ERR(drv_data->device);
}
return 0;
}
static int example_setup_char_device_parent(struct example_driver_data *drv_data, struct platform_device *pdev)
{
int ret = 0;
/* 注册字符设备 */
ret = alloc_chrdev_region(&drv_data->dev_num, 0, 1, "example-driver");
if (ret < 0) {
printk(KERN_ERR "Failed to allocate char device region\n");
return ret;
}
/* 初始化并添加cdev */
cdev_init(&drv_data->cdev, &example_fops);
drv_data->cdev.owner = THIS_MODULE;
ret = cdev_add(&drv_data->cdev, drv_data->dev_num, 1);
if (ret < 0) {
printk(KERN_ERR "Failed to add char device\n");
unregister_chrdev_region(drv_data->dev_num, 1);
return ret;
}
/* 创建设备类 */
drv_data->class = class_create(THIS_MODULE, "example-driver");
if (IS_ERR(drv_data->class)) {
printk(KERN_ERR "Failed to create class\n");
cdev_del(&drv_data->cdev);
unregister_chrdev_region(drv_data->dev_num, 1);
return PTR_ERR(drv_data->class);
}
/* 创建设备节点 - 使用platform设备作为父设备 */
drv_data->device = device_create(drv_data->class, &pdev->dev, drv_data->dev_num, NULL, "example-driver");
if (IS_ERR(drv_data->device)) {
printk(KERN_ERR "Failed to create device\n");
class_destroy(drv_data->class);
cdev_del(&drv_data->cdev);
unregister_chrdev_region(drv_data->dev_num, 1);
return PTR_ERR(drv_data->device);
}
/* 在sysfs中创建从字符设备到platform设备的链接,方便识别关联关系 */
ret = sysfs_create_link(&drv_data->device->kobj, &pdev->dev.kobj, "device");
if (ret) {
printk(KERN_WARNING "Failed to create sysfs link to platform device\n");
/* 链接创建失败不影响主要功能,继续执行 */
}
return 0;
}
/* remove函数 - 当设备被移除时调用 */
static int example_remove(struct platform_device *pdev)
{
struct example_driver_data *drv_data = platform_get_drvdata(pdev);
printk(KERN_INFO "Example platform driver: remove function called\n");
#ifdef platform_device_attr
/* 移除 sysfs 属性文件 */
device_remove_file(&pdev->dev, &dev_attr_dma_mode);
#else
/* 移除 sysfs 属性文件 */
device_remove_file(drv_data->device, &dev_attr_dma_mode);
#endif
/* 清理字符设备相关资源 - 调用独立的清理函数 */
if (drv_data) {
example_cleanup_char_device(drv_data);
}
printk(KERN_INFO "Example platform driver: remove successful\n");
return 0;
}
/* 字符设备资源清理函数 */
static void example_cleanup_char_device(struct example_driver_data *drv_data)
{
if (drv_data->device && !IS_ERR(drv_data->device)) {
/* 只销毁设备,不清理设备属性文件(设备属性在example_remove中已处理) */
device_destroy(drv_data->class, drv_data->dev_num);
}
if (drv_data->class && !IS_ERR(drv_data->class)) {
class_destroy(drv_data->class);
}
cdev_del(&drv_data->cdev);
unregister_chrdev_region(drv_data->dev_num, 1);
}
/* 定义platform_driver结构体 */
static struct platform_driver example_platform_driver = {
.probe = example_probe,
.remove = example_remove,
.driver = {
.name = "example-pdrv", /* 必须与platform_device的name匹配 */
.owner = THIS_MODULE,
.of_match_table = example_of_match, /* 设备树匹配表 */
},
.id_table = example_id_table, /* ID匹配表 */
};
static int __init example_driver_init(void)
{
int ret;
printk(KERN_INFO "Example platform driver: registering\n");
/* 注册platform_driver */
ret = platform_driver_register(&example_platform_driver);
if (ret) {
printk(KERN_ERR "Failed to register platform driver: %d\n", ret);
return ret;
}
printk(KERN_INFO "Example platform driver: registered successfully\n");
return 0;
}
static void __exit example_driver_exit(void)
{
printk(KERN_INFO "Example platform driver: unregistering\n");
/* 注销platform_driver */
platform_driver_unregister(&example_platform_driver);
printk(KERN_INFO "Example platform driver: unregistered successfully\n");
}
module_init(example_driver_init);
module_exit(example_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Example Author");
MODULE_DESCRIPTION("Platform Driver Example");
MODULE_VERSION("1.0");
bash
#!/bin/bash
# 首先清除所有之前的内核日志
sudo dmesg -c
echo "insmod..."
sudo insmod platform_device_example.ko
sudo insmod platform_driver_example.ko
echo "log:"
dmesg | grep "Example platform"
ls /sys/bus/platform/drivers/example-pdrv
ls /sys/devices/platform/example-pdrv
ls /sys/class/example-driver/example-driver/
bash
#!/bin/bash
echo "rmmod..."
sudo rmmod platform_driver_example.ko
sudo rmmod platform_device_example.ko
echo "lsmod:"
lsmod | grep example
echo "log:"
dmesg | grep "Example platform"
echo "
clear log..."
sudo dmesg -c
echo "finish"