当前位置: 首页 > news >正文

Linux驱动之平台总线

Linux驱动之平台总线

参考视频地址

【北京迅为】嵌入式学习之Linux驱动(第六期_平台总线_全新升级)_基于RK3568_哔哩哔哩_bilibili

平台总线介绍

一、什么是平台总线模型?

​ 平台总线模型也叫platform总线模型。平台总线是Linux系统虚拟出来的总线。

二、平台总线模型的使用

​ 平台总线模型将一个驱动分成了两个部分,分别是device.c和driver.c,device.c用来描述硬件,driver.c用来控制硬件。

三、平台总线模型是如何工作的

​ 平台总线通过字符串比较,将 name 相同的 device.c 和 driver.c 匹配到一起来控制硬件。

在这里插入图片描述

​ 平台总线原则:先分离,后搭档

四、平台总线模型的优点

  1. 减少编写重复代码,提高效率。
  2. 提高代码的利用率。

注册platform设备

platform_device

struct platform_device用于描述平台设备(如SoC上的外设)的硬件资源,包括:

  • 寄存器地址(通过struct resource
  • 中断号
  • 其他设备特定资源

这个结构体定义在include/linux/platform_device.h文档中,结构体如下所示:

struct platform_device {const char *name;int id;                   struct device dev;        // 内嵌的通用设备结构体u32 num_resources;        // 资源数量struct resource *resource; // 资源数组指针// 其他可选成员(不同内核版本可能略有差异)const struct platform_device_id *id_entry;bool id_auto;             // 用于自动生成设备IDchar *driver_override;struct mfd_cell *mfd_cell; // 用于MFD(多功能设备)子系统struct pdev_archdata archdata; // 架构特定数据
};

关键成员说明

  • name: 设备名称,需与驱动中的platform_driver.name匹配。
  • id: 设备实例编号(如同一设备有多个实例)。
  • num_resourcesresource: 描述硬件资源(如内存、中断),通过struct resource数组定义。
  • dev: 继承自通用设备模型,包含设备树、电源管理等核心信息。

resource

struct resource用于描述硬件资源(如内存区域、中断号等)的地址范围和属性,是平台设备和驱动之间传递资源信息的基础结构。头文件定义在include/linux/ioport.h, 结构体如下所示:

// 定义路径:include/linux/ioport.h
struct resource {resource_size_t start;  // 资源起始地址(物理地址或中断号)resource_size_t end;    // 资源结束地址const char *name;       // 资源名称(可选,用于调试)unsigned long flags;    // 资源类型标志(如IORESOURCE_MEM、IORESOURCE_IRQ)unsigned long desc;     // 资源描述符(特定用途,如DMA通道、总线号等)struct resource *parent, *sibling, *child; // 资源树管理指针(用于层次化资源分配)
};

关键成员说明

  • startend
    表示资源的地址范围。例如:
    • 内存资源:start=0xFE000000, end=0xFE000FFF(表示0xFE000000~0xFE000FFF的内存区域)。
    • 中断资源:start=42, end=42(表示中断号为42)。
  • flags
    标志位,用于标识资源类型,常见值包括:
    • IORESOURCE_TYPE_BITS: 源类型位域掩码
    • IORESOURCE_MEM:内存资源
    • IORESOURCE_IRQ:中断资源
    • IORESOURCE_IO:I/O端口资源
    • IORESOURCE_REG: 寄存器偏移(特定场景使用)
    • IORESOURCE_DMA:DMA通道资源
    • IORESOURCE_BUS:总线资源(如PCI总线号)
    • IORESOURCE_PREFETCH:可预取内存(如PCI设备)
    • IORESOURCE_MEM_64:64位内存地址
    • IORESOURCE_WINDOW:桥接器的地址窗口
    • IORESOURCE_DISABLED:资源已被禁用
    • IORESOURCE_UNSET:资源未初始化
    • IORESOURCE_AUTO:自动分配资源
  • desc
    附加描述符,具体含义依赖于资源类型。例如:
    • 对于中断资源,可能表示中断触发方式(高电平、边沿等)。
    • 对于DMA资源,可能表示DMA通道号。
  • 资源树指针(parent/sibling/child
    用于构建资源管理树,确保资源分配不冲突(如内存区域或中断号的嵌套分配)。

struct resource举个例子:

static struct resource mem_res[] = {[0] = {.start = 0xFE000000,  //寄存器起始地址.end = 0xFE000FFF,    //寄存器终止地址.flags = IORESOURCE_MEM,.name = "device_memory",},[1] = {.start = 13,.end = 13,.flags = IORESOURCE_MEM,.name = "device1_memory",},
};

platform 设备加载和卸载

platform_device_register
int platform_device_register(struct platform_device *pdev)

功能:加载platform设备。

示例代码

#include <linux/platform_device.h>
#include <linux/module.h>// 1. 定义资源(内存和中断)
static struct resource my_device_resources[] = {[0] = {.start = 0xFE000000,   // 寄存器起始地址.end = 0xFE000FFF,     // 寄存器结束地址.flags = IORESOURCE_MEM, // 内存资源},[1] = {.start = 42,           // 中断号.end = 42,.flags = IORESOURCE_IRQ, // 中断资源},
};// 2. 定义platform_device结构体
static struct platform_device my_device = {.name = "my_platform_device", // 设备名称(需与驱动匹配).id = -1,                     // 设备实例ID(-1表示单实例).num_resources = ARRAY_SIZE(my_device_resources),.resource = my_device_resources,
};// 3. 模块初始化函数(加载设备)
static int __init my_device_init(void) {int ret;ret = platform_device_register(&my_device);if (ret) {pr_err("Failed to register platform device\n");return ret;}pr_info("Platform device registered\n");return 0;
}module_init(my_device_init);

platform_device_unregister
void platform_device_unregister(struct platform_device *pdev)

功能:卸载platform设备。

示例代码

// 4. 模块退出函数(卸载设备)
static void __exit my_device_exit(void) {platform_device_unregister(&my_device);pr_info("Platform device unregistered\n");
}module_exit(my_device_exit);// 5. 模块元信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Platform Device Example");

测试实验

​ 我么定义一个资源名为my_resources的 结构体数组中,具体内容如下:

​ 内存资源:

​ 起始地址:MEM_START_ADDR(0xFEC30004)

​ 结束地址:MEM_END_ADDR(0xFEC30008)

​ 标记:IORESOURCE_MEM

​ 中断资源:

​ 中断资源号:IRQ_NUMBER(112)

​ 标记:IORESOURCE_IRQ

​ 代码路径:/home/topeet/Linux/my-test/36_platform_device/platform_dev.c,代码如下所示:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>#define MEM_START_ADDR 0xFEC3004
#define MEM_END_ADDR   0xFEC3008
#define IRQ_NUMBER  112static struct resource my_resource[] = {[0] = {.start = MEM_START_ADDR,.end = MEM_END_ADDR,.flags = IORESOURCE_MEM,},[1] = {.start = IRQ_NUMBER,.end = IRQ_NUMBER,.flags = IORESOURCE_IRQ,},
};static void my_platform_device_release(struct device *dev)
{}static struct platform_device my_platform_device = {.name = "my_platform_device",.id = -1,.num_resources = ARRAY_SIZE(my_resource),.resource = my_resource,.dev = {.release = my_platform_device_release,},
};static int __init my_platform_device_init(void)
{int ret;ret = platform_device_register(&my_platform_device);if( ret ){printk(KERN_ERR "Failed to register platform device\n");return ret;}printk(KERN_INFO "Platform device resistered.\n");return 0;
}static void __exit my_platform_device_exit(void)
{platform_device_unregister(&my_platform_device);printk(KERN_INFO "Platform device unregistered.\n");
}module_init(my_platform_device_init);
module_exit(my_platform_device_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("YAN");
MODULE_VERSION("v1.0");

实验现象:

//加载驱动
root@topeet:/run# insmod platform_dev.ko

​ 加载完驱动后,查看路径/sys/bus/platform/devices/,是否有存在my_platform_device,如下图所示:

在这里插入图片描述


注册platform驱动

platform_driver

platform_driver用于实现平台设备的软件驱动逻辑,其核心功能包括:

  • 设备探测与初始化:通过probe函数初始化硬件。
  • 资源释放:通过remove函数卸载驱动时释放资源。
  • 电源管理:通过suspend/resume函数实现设备的低功耗管理。
  • 设备与驱动匹配:通过id_table或设备树(Device Tree)的compatible字段匹配设备。
// 定义路径:include/linux/platform_device.h
struct platform_driver {int (*probe)(struct platform_device *);         // 设备探测函数(必需)int (*remove)(struct platform_device *);        // 设备移除函数(必需)void (*shutdown)(struct platform_device *);     // 设备关机回调int (*suspend)(struct platform_device *, pm_message_t state); // 设备挂起(电源管理)int (*resume)(struct platform_device *);        // 设备恢复(电源管理)struct device_driver driver;                    // 内嵌的通用驱动结构体(含名称、总线等)const struct platform_device_id *id_table;      // 设备ID匹配表(用于非设备树匹配)bool prevent_deferred_probe;                    // 是否禁止延迟探测(内核高级特性)
};

关键成员

  • probe
    驱动与设备匹配成功后自动调用,负责初始化硬件、申请资源(如内存映射、中断注册)。
  • remove
    驱动卸载时调用,释放资源(如释放内存、注销中断)。
  • driver
    通用驱动结构体,必须包含.name字段(与platform_device.name匹配),或通过.of_match_table匹配设备树节点。
  • id_table
    设备ID匹配表,用于非设备树场景下的设备驱动匹配,示例如下:
static const struct platform_device_id my_driver_ids[] = {{ "my_platform_device", 0 }, // 匹配platform_device.name{ /* Sentinel */ }
};

注册于卸载函数

int platform_driver_register(struct platform_driver *drv):注册驱动。void platform_driver_unregister(struct platform_driver *drv):注销驱动。

测试实验

​ 代码路径/home/topeet/Linux/my-test/37_platform_driver/platform_drv.c,代码如下所示:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>int my_platform_probe(struct platform_device *pdev)
{printk("my_platform_driver: Probing platform device.\n");return 0;
}int my_platform_remove(struct platform_device *pdev)
{printk("my_platform_driver: Removing platform device.\n");return 0;
}static struct platform_driver my_platform_driver = {.probe = my_platform_probe,.remove = my_platform_remove,.driver = {.name = "my_platform_device",.owner = THIS_MODULE,},};static int __init my_platform_driver_init(void)
{int ret;ret = platform_driver_register(&my_platform_driver);if( ret ){printk(KERN_ERR "Failed to register platform driver.\n");return ret;}printk(KERN_INFO "my_platform_driver: Platform driver initialized.\n");return 0;
}static void __exit my_platform_driver_exit(void)
{platform_driver_unregister(&my_platform_driver);printk(KERN_INFO "my_platform_driver: Platform driver exited.\n");
}module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("YAN");
MODULE_VERSION("v1.0");

实验现象

// 加载device驱动
insmod platform_dev.ko// 加载platform_drv.ko
insmod platform_drv.ko//输出probe 函数的printk
[ 2930.477208] Platform device resistered.
[ 2957.043437] my_platform_driver: Probing platform device.
[ 2957.044529] my_platform_driver: Platform driver initialized.//查看/sys下路径是否有对应生成my_platform_device文件
root@topeet:/run# ls /sys/bus/platform/drivers/my_platform_device/
bind  module  my_platform_device  uevent  unbind

probe函数

​ 驱动是要控制硬件的。但是平台总线模型对硬件的描述是在设备(device)中的。所以在驱动(driver)中,我们需要得到设备(device)中的硬件资源。当设备(device)和驱动(driver)匹配成功以后,会执行驱动(driver)中的probe函数,所以我们要在probe函数中拿到设备(device)中的硬件资源。所以,本节课的重点是要如何拿到,怎么拿到这些硬件资源呢

获取设备资源接口API

platform_get_resource()

功能:获取特定类型的硬件资源(如内存、I/O、中断等)。
原型

struct resource *platform_get_resource(struct platform_device *pdev, unsigned int type, unsigned int index
);

参数

  • pdev:关联的platform_device指针。
  • type:资源类型(如IORESOURCE_MEMIORESOURCE_IRQ)。
  • index:资源索引(从0开始,按类型分组)。

返回值

  • 成功:返回指向struct resource的指针。
  • 失败:返回NULL(资源不存在或索引越界)。

调用示例:

// 获取第0个内存资源
struct resource *mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {dev_err(&pdev->dev, "Memory resource not found\n");return -ENODEV;
}
platform_get_resource_byname()

功能:通过资源名称获取特定资源(需在resource中定义name字段)。
原型

struct resource *platform_get_resource_byname(struct platform_device *pdev, unsigned int type, const char *name
);

参数

  • name:资源名称(需与resource.name匹配)。

调用示例

// 定义资源时指定名称
static struct resource my_resources[] = {[0] = {.start = 0xFE000000,.end = 0xFE000FFF,.flags = IORESOURCE_MEM,.name = "device_memory",},
};// 通过名称获取资源
struct resource *mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "device_memory");
platform_get_irq()

功能:获取设备的中断号(IRQ)。
原型

int platform_get_irq(struct platform_device *pdev, unsigned int index
);

参数

  • index:中断资源的索引(从0开始)。

返回值

  • 成功:返回中断号(正数)。
  • 失败:返回负数错误码(如-ENXIO表示中断未找到)。

示例

int irq_num = platform_get_irq(pdev, 0);
if (irq_num < 0) {dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq_num);return irq_num;
}
devm_ioremap_resource()

功能:将物理内存资源映射到内核虚拟地址空间,并自动管理资源释放(无需手动iounmap)。
原型

void __iomem *devm_ioremap_resource(struct device *dev, const struct resource *res
);

参数

  • dev:设备指针(通常为&pdev->dev)。
  • res:已通过platform_get_resource获取的资源指针。

返回值

  • 成功:返回映射后的虚拟地址指针。
  • 失败:返回ERR_PTR()错误码(如-ENOMEM)。

示例

void __iomem *reg_base = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(reg_base)) {return PTR_ERR(reg_base);
}
devm_request_irq()

功能:注册中断处理函数,并自动释放中断(防止资源泄漏)。
原型

int devm_request_irq(struct device *dev,unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev_id
);

参数

  • irq:中断号(通过platform_get_irq获取)。
  • handler:中断处理函数。
  • flags:中断标志(如IRQF_TRIGGER_RISING)。
  • name:中断名称(显示在/proc/interrupts)。
  • dev_id:传递给中断处理函数的私有数据。

示例

ret = devm_request_irq(&pdev->dev, irq_num, my_irq_handler,IRQF_TRIGGER_RISING, "my_device", NULL);
if (ret) {dev_err(&pdev->dev, "Failed to request IRQ\n");return ret;
}
devm_platform_ioremap_resource()

功能:直接通过索引映射内存资源,简化设备树资源获取流程。
原型

void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev,unsigned int index
);

示例

void __iomem *reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg_base)) {return PTR_ERR(reg_base);
}

测试实验

​ 代码路径:/home/topeet/Linux/my-test/38_platform_probe/platform_drv.c, 这里主要是probe()函数的实现,获取device resource中的内存资源和中断资源,代码如下所示:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/mod_devicetable.h>int my_platform_probe(struct platform_device *pdev)
{struct resource *res_mem, *res_irq;res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);if( !res_mem ){dev_err(&pdev->dev, "Failed to get memory resource.\n");return -ENODEV;}res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if( !res_irq ){dev_err(&pdev->dev, "Failed to get IRQ resource.\n");return -ENODEV;}printk("Method 2: Memory Resource: start = 0x%llx, end = 0x%llx\n", res_mem->start, res_mem->end);printk("Method 2: IRQ Reosurce: number = %lld\n", res_irq->start);return 0;
}int my_platform_remove(struct platform_device *pdev)
{printk("my_platform_driver: Removing platform device.\n");return 0;
}const struct platform_device_id mydriver_id_table = {.name = "my_platform_device",
};static struct platform_driver my_platform_driver = {.probe = my_platform_probe,.remove = my_platform_remove,.driver = {.name = "my_platform_device",.owner = THIS_MODULE,},.id_table = &mydriver_id_table,};static int __init my_platform_driver_init(void)
{int ret;ret = platform_driver_register(&my_platform_driver);if( ret ){printk(KERN_ERR "Failed to register platform driver.\n");return ret;}printk(KERN_INFO "my_platform_driver: Platform driver initialized.\n");return 0;
}static void __exit my_platform_driver_exit(void)
{platform_driver_unregister(&my_platform_driver);printk(KERN_INFO "my_platform_driver: Platform driver exited.\n");
}module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("YAN");
MODULE_VERSION("v1.0");

实验现象

// 加载platform_dev.ko 和 加载platform_drv.ko
insmod platform_dev.ko
insmod platform_drv.ko// 加载驱动完成后,我们能看到probe() 函数里面加的printk(),信息被打印
[14427.361364] Method 2: Memory Resource: start = 0xfec3004, end = 0xfec3008
[14427.361415] Method 2: IRQ Reosurce: number = 112
[14427.361565] my_platform_driver: Platform driver initialized.

LED驱动改为platform

​ 代码路径:/home/topeet/Linux/my-test/39_platform_led, 设备驱动代码路径:device/platform_dev.c, 代码如下所示:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>#define DR_MEM_START_ADDR 0xFEC30004
#define DR_MEM_END_ADDR   0xFEC30008
#define DIR_MEM_START_ADDR 0xFEC3000C
#define DIR_MEM_END_ADDR   0xFEC30010
#define IRQ_NUMBER  112static struct resource my_resource[] = {[0] = {.start = DR_MEM_START_ADDR,  // 配置GPIO2C4 数据寄存器地址。.end = DR_MEM_END_ADDR,.flags = IORESOURCE_MEM,},[1] = {.start = DIR_MEM_START_ADDR,  // 配置GPIO2C4 方向寄存器地址。.end = DIR_MEM_END_ADDR,.flags = IORESOURCE_MEM,},[2] = {.start = IRQ_NUMBER,.end = IRQ_NUMBER,.flags = IORESOURCE_IRQ,},
};static void my_platform_device_release(struct device *dev)
{}static struct platform_device my_platform_device = {.name = "my_platform_device",.id = -1,.num_resources = ARRAY_SIZE(my_resource),.resource = my_resource,.dev = {.release = my_platform_device_release,},
};static int __init my_platform_device_init(void)
{int ret;ret = platform_device_register(&my_platform_device);if( ret ){printk(KERN_ERR "Failed to register platform device\n");return ret;}printk(KERN_INFO "Platform device resistered.\n");return 0;
}static void __exit my_platform_device_exit(void)
{platform_device_unregister(&my_platform_device);printk(KERN_INFO "Platform device unregistered.\n");
}module_init(my_platform_device_init);
module_exit(my_platform_device_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("YAN");
MODULE_VERSION("v1.0");

​ 接着是驱动程序路径:driver/platform_drv.c, 代码如下所示:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/mod_devicetable.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/io.h>struct device_test
{dev_t dev_num;int major;int minor;struct cdev myCdev;struct class *myClass;struct device *myDevice;char kbuf[32];unsigned int *vir_gpio_dr;unsigned int *vir_gpio_dir;
};struct device_test test;static int myCdev_open(struct inode *inode, struct file *file)
{printk("This is myCdev_open.\n");file->private_data = &test;return 0;
}static ssize_t myCdev_read(struct file *file, char __user *buf, size_t size, loff_t *off)
{struct device_test *test = (struct device_test *)file->private_data;if( copy_to_user(buf, test->kbuf, strlen(test->kbuf)) != 0 ){printk("copy_to_user error.\n");return -1;}printk("This is myCdev_read.\n");return 0;
}static ssize_t myCdev_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{struct device_test *test = (struct device_test *)file->private_data;if( copy_from_user(test->kbuf, buf, size ) != 0 ){printk("copy_from_user error.\n");return -1;}// 如果应用层传入的数据是1,则设置GPIO为高电平if( test->kbuf[0] == 1 ){*(test->vir_gpio_dr) |= 0x00100010;}// 如果应用层传入的数据是0,则设置GPIO为低电平else if( test->kbuf[0] == 0 ){// *(test->vir_gpio_dr) |= 0x00100000;*(test->vir_gpio_dr) = (*(test->vir_gpio_dr) | (1 << 20)) & ~(1 << 4);}return 0;
}static int myCdev_release(struct inode *inode, struct file *file)
{return 0;
}struct file_operations myCdev_fops = {.owner = THIS_MODULE,.open = myCdev_open,.read = myCdev_read,.write = myCdev_write,.release = myCdev_release,
};int my_platform_probe(struct platform_device *pdev)
{int ret;struct resource *res1_mem, *res2_mem,*res_irq;res1_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);if( !res1_mem ){dev_err(&pdev->dev, "Failed to get memory resource 1.\n");return -ENODEV;}res2_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);if( !res2_mem ){dev_err(&pdev->dev, "Failed to get memory resource 2.\n");return -ENODEV;}res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if( !res_irq ){dev_err(&pdev->dev, "Failed to get IRQ resource.\n");return -ENODEV;}printk("Method 2: Memory Resource_1: start = 0x%llx, end = 0x%llx\n", res1_mem->start, res1_mem->end);printk("Method 2: Memory Resource_2: start = 0x%llx, end = 0x%llx\n", res2_mem->start, res2_mem->end);printk("Method 2: IRQ Reosurce: number = %lld\n", res_irq->start);ret = alloc_chrdev_region(&test.dev_num, 0, 1, "alloc_name");if( ret < 0 )goto err_chrdev;test.major = MAJOR(test.dev_num);test.minor = MINOR(test.dev_num);test.myCdev.owner = THIS_MODULE;cdev_init(&test.myCdev, &myCdev_fops);ret = cdev_add(&test.myCdev, test.dev_num, 1);if( ret < 0 )goto err_cdev_add;test.myClass = class_create(THIS_MODULE, "test");if( IS_ERR(test.myClass) ){ret = PTR_ERR(test.myClass);goto err_class_create;}test.myDevice = device_create(test.myClass, NULL, test.dev_num, NULL, "test");if( IS_ERR(test.myDevice) ){ret = PTR_ERR(test.myDevice);goto err_device_create;}//将物理地址转化为虚拟地址test.vir_gpio_dr = ioremap(res1_mem->start, 4);if( IS_ERR(test.vir_gpio_dr) ){ret = PTR_ERR(test.vir_gpio_dr);goto err_gpio_ioremap_dr;}test.vir_gpio_dir = ioremap(res2_mem->start, 4);if( IS_ERR(test.vir_gpio_dir) ){ret = PTR_ERR(test.vir_gpio_dir);goto err_gpio_ioremap_dir;}*(test.vir_gpio_dir) |= 0x00100030;return 0;
err_gpio_ioremap_dir:iounmap(test.vir_gpio_dr);
err_gpio_ioremap_dr:device_destroy(test.myClass, test.dev_num);
err_device_create:class_destroy(test.myClass);
err_class_create:cdev_del(&test.myCdev);
err_cdev_add:unregister_chrdev_region(test.dev_num, 1);
err_chrdev:return 0;
}int my_platform_remove(struct platform_device *pdev)
{printk("my_platform_driver: Removing platform device.\n");return 0;
}const struct platform_device_id mydriver_id_table = {.name = "my_platform_device",
};static struct platform_driver my_platform_driver = {.probe = my_platform_probe,.remove = my_platform_remove,.driver = {.name = "my_platform_device",.owner = THIS_MODULE,},.id_table = &mydriver_id_table,};static int __init my_platform_driver_init(void)
{int ret;ret = platform_driver_register(&my_platform_driver);if( ret ){printk(KERN_ERR "Failed to register platform driver.\n");return ret;}printk(KERN_INFO "my_platform_driver: Platform driver initialized.\n");return 0;
}static void __exit my_platform_driver_exit(void)
{cdev_del(&test.myCdev);unregister_chrdev_region(test.dev_num, 1);device_destroy(test.myClass, test.dev_num);class_destroy(test.myClass);iounmap(test.vir_gpio_dr);iounmap(test.vir_gpio_dir);platform_driver_unregister(&my_platform_driver);printk(KERN_INFO "my_platform_driver: Platform driver exited.\n");
}module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("YAN");
MODULE_VERSION("v1.0");

​ APP代码还是沿用《字符设备基础》里面,章节“点亮一个LED灯”的代码。

实验现象

​ 按《字符设备基础》文档里面连接方式,连接上LED灯,加载platform_dev.ko,加载platform_drv.ko,并运行app应用,正常要能看到LED灯闪烁。


总结

在这里插入图片描述

相关文章:

  • 【拓扑排序】P6560 [SBCOI2020] 时光的流逝|普及+
  • 腾讯位置商业授权行政区划开发指南
  • [PCIe]Gen6 PAM4的功耗相比Gen5 NRZ增加了多少?
  • 35、请求处理-【源码分析】-自定义参数绑定原理
  • 6、修改和校正时间
  • 跨平台猫咪桌宠 BongoCat v0.4.0 绿色版
  • 【论文解读】Deformable DETR | Deformable Transformers for End-to-End Object Detection
  • 【目标检测】backbone究竟有何关键作用?
  • 2023年6月6级第一套第一篇
  • 设计模式——责任链设计模式(行为型)
  • YOLOv5 环境配置指南
  • CCPC dongbei 2025 I
  • 《Pytorch深度学习实践》ch2-梯度下降算法
  • 怎么样提高研发质量?
  • 小白的进阶之路系列之九----人工智能从初步到精通pytorch综合运用的讲解第二部分
  • 多线程——定时任务ScheduledThreadPoolExecutor用法
  • [AD] CrownJewel-1 Logon 4799+vss-ShadowCopy+NTDS.dit/SYSTEM+$MFT
  • C++实现伽罗华域生成及四则运算(三)
  • AAA基础配置
  • Cypress + TypeScript + Vue3
  • 手机如何免费做网站/免费数据统计网站
  • 张家港网站建设哪家好/seo策略分析
  • 寄生虫网站怎么做/自动优化app
  • 安徽省交通运输厅秦勤/北京网站优化外包
  • 餐饮logo创意设计/石家庄关键词优化软件
  • 大东吴建设新材料公司网站/互联网营销推广公司