Linux驱动之平台总线
Linux驱动之平台总线
参考视频地址
【北京迅为】嵌入式学习之Linux驱动(第六期_平台总线_全新升级)_基于RK3568_哔哩哔哩_bilibili
平台总线介绍
一、什么是平台总线模型?
平台总线模型也叫platform总线模型。平台总线是Linux系统虚拟出来的总线。
二、平台总线模型的使用
平台总线模型将一个驱动分成了两个部分,分别是device.c和driver.c,device.c用来描述硬件,driver.c用来控制硬件。
三、平台总线模型是如何工作的
平台总线通过字符串比较,将 name 相同的 device.c 和 driver.c 匹配到一起来控制硬件。
平台总线原则:先分离,后搭档
四、平台总线模型的优点
- 减少编写重复代码,提高效率。
- 提高代码的利用率。
注册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_resources
和resource
: 描述硬件资源(如内存、中断),通过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; // 资源树管理指针(用于层次化资源分配)
};
关键成员说明
start
和end
:
表示资源的地址范围。例如:- 内存资源:
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_MEM
、IORESOURCE_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灯闪烁。