linux 驱动开发相关

一、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);  
  1. 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 驱动模型中,这是两个不同但相关的概念:

  1. Platform 设备 :代表底层硬件抽象,是硬件与驱动程序之间的接口

  2. 字符设备 :是提供给用户空间程序访问的设备节点

应该如何处理?

这取决于您的设计意图:

  1. 如果 dma_mode 是硬件配置参数 :
  • 当前实现在 platform 设备下创建是合理的,因为它代表硬件配置

  • 这使得属性文件出现在 /sys/devices/platform/example-pdrv/ 目录下

  1. 如果 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");

insmod.sh

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/

remod.sh

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"
相关推荐
IT逆夜1 小时前
实现Yum本地仓库自动同步的完整方案(CentOS 7)
linux·运维·windows
S***26751 小时前
linux上redis升级
linux·运维·redis
赖small强2 小时前
【Linux 网络基础】Linux 平台 DHCP 运作原理与握手过程详解
linux·网络·dhcp
s***4533 小时前
Linux 下安装 Golang环境
linux·运维·golang
J***51685 小时前
Linux安装Redis以及Redis三种启动方式
linux·redis·bootstrap
4***17545 小时前
Linux 下安装 Golang环境
linux·运维·golang
Lenyiin5 小时前
《 Linux 修炼全景指南: 七 》 指尖下的利刃:深入理解 Vim 的高效世界
linux·运维·服务器·vim·lenyiin
sulikey7 小时前
Linux基础指令与权限管理深度解析:从入门到精通
linux·运维·服务器·ubuntu·centos·linux命令·linux权限
s***46987 小时前
linux 设置tomcat开机启动
linux·运维·tomcat