在Linux设备中有的是没有对应的物理总线的,但为了适配Linux的总线模型,内核针对这种没有物理总线的设备开发了一种虚拟总线——platform总线。
一、平台设备(device)
1.平台设备是用struct platform_device
结构来表示的,它的定义如下:
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
驱动开发者关心的主要成员有:
name: 设备的名字,在平台总线的match函数中可用于同平台driver的匹配;
id: 设备的ID,用于区分同类型不同的device;
dev: 内嵌的struct device;
nem_resources: 平台设备使用的资源个数;
resource: 平台设备的资源列表,指向资源数组中的首元素;
id_entry: 用于同平台驱动匹配的ID,在平台总线的match函数中首先尝试匹配该ID,如果不成功在尝试用name成员来匹配。
下面为我定义的两个device:
struct platform_device pdev0 = {
.name = "pdev",
.id = 0,
.num_resources = 1,
.resource = &pdev_resources[0],
.dev = {
.release = pdev_release,
},
};
struct platform_device pdev1 = {
.name = "pdev",
.id = 1,
.num_resources = 1,
.resource = &pdev_resources[1],
.dev = {
.release = pdev_release,
},
};
2.在平台设备中,最关键的是struct resource
,这是实现device和driver分离的关键,其定义如下:
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
驱动开发者关心的主要成员如下:
start: 资源的开始,对于内存来说是内存起始地址,对于中断资源来说是起始中断号,对于DMA资源来说是DMA起始通道号;
end: 资源的结束;
flags: 资源的标志,常见的有一下几种:
- IORESOURCE_MEM:内存资源;
- IORESOURCE_IRQ:中断资源;
- IORESOURCE_DMA:DMA资源。
下面为我定义的resource:
static char buf0[200] = {0};
static char buf1[200] = {0};
static struct resource pdev_resources[] = {
[0] = {buf0, buf0+sizeof(buf0)-1, "pdev0-resource", IORESOURCE_MEM,},
[1] = {buf1, buf1+sizeof(buf1)-1, "pdev1-resource", IORESOURCE_MEM,},
};
3.向platform总线注册和注销device的主要函数如下:
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);
二、平台驱动(driver)
1.平台驱动(driver)用struct platform_driver
结构来表示,其定义如下:
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;
bool prevent_deferred_probe;
};
驱动开发者关心的主要成员如下:
probe: platform总线发现有匹配的平台设备(device)时调用;
remove: 驱动的平台设备(device)被移除或平台驱动(driver)注销时调用;
shutdown、suspend和resume: 电源管理函数;
id_table: 平台驱动(driver)可以驱动的平台设备ID列表。
下面为我定义的driver:
struct platform_driver pdrv = {
.driver = {
.name = "pdev",
.owner = THIS_MODULE,
.pm = &pdrv_pm_ops,
},
.probe = pdrv_probe,
.remove = pdrv_remove,
};
2.向platform总线注册和注销驱动的主要函数如下:
platform_driver_register(drv);
platform_driver_unregister(drv);
三、示例代码
在这里我实现了一个简单的platform驱动,其中device注册了两个设备,如下:
pltdev.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
static char buf0[200] = {0};
static char buf1[200] = {0};
static struct resource pdev_resources[] = {
[0] = {buf0, buf0+sizeof(buf0)-1, "pdev0-resource", IORESOURCE_MEM,},
[1] = {buf1, buf1+sizeof(buf1)-1, "pdev1-resource", IORESOURCE_MEM,},
};
static void pdev_release(struct device *dev)
{
}
struct platform_device pdev0 = {
.name = "pdev",
.id = 0,
.num_resources = 1,
.resource = &pdev_resources[0],
.dev = {
.release = pdev_release,
},
};
struct platform_device pdev1 = {
.name = "pdev",
.id = 1,
.num_resources = 1,
.resource = &pdev_resources[1],
.dev = {
.release = pdev_release,
},
};
static int __init pltdev_init(void)
{
strcpy(buf0, "hello");
strcpy(buf1, "world");
printk(KERN_EMERG"pltdev_init!\n");
platform_device_register(&pdev0);
platform_device_register(&pdev1);
return 0;
}
static void __exit pltdev_exit(void)
{
printk(KERN_EMERG"pltdev_exit!\n");
platform_device_unregister(&pdev1);
platform_device_unregister(&pdev0);
}
module_init(pltdev_init);
module_exit(pltdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaodongzhao");
MODULE_DESCRIPTION("register a platfom device");
pltdrv.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
static int pdrv_suspend(struct device *dev)
{
printk(KERN_EMERG"pdev: suspend\n");
return 0;
}
static int pdrv_resume(struct device *dev)
{
printk(KERN_EMERG"pdev: resume\n");
return 0;
}
static const struct dev_pm_ops pdrv_pm_ops = {
.suspend = pdrv_suspend,
.resume = pdrv_resume,
};
static int pdrv_probe(struct platform_device *pdev)
{
struct resource* mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
printk(KERN_EMERG"id:%d name:%s buf:%s\n", pdev->id, mem_res->name, (char*)mem_res->start);
return 0;
}
static int pdrv_remove(struct platform_device *pdev)
{
printk(KERN_EMERG"pdrv_remove!\n");
return 0;
}
struct platform_driver pdrv = {
.driver = {
.name = "pdev",
.owner = THIS_MODULE,
.pm = &pdrv_pm_ops,
},
.probe = pdrv_probe,
.remove = pdrv_remove,
};
static int __init pdev_init(void)
{
int ret;
printk(KERN_EMERG"pdev_init!\n");
ret = platform_driver_register(&pdrv);
return ret;
}
static void __exit pdev_exit(void)
{
printk(KERN_EMERG"pdev_exit!\n");
platform_driver_unregister(&pdrv);
}
module_init(pdev_init);
module_exit(pdev_exit);
//module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaodongzhao");
MODULE_DESCRIPTION("A simple platform driver");
MODULE_ALIAS("platform:pdev");
测试:
测试结果如下:
# insmod pltdev.ko
pltdev_init!
# insmod pltdrv.ko
id:0 name:pdev0-resource buf:hello
id:1 name:pdev1-resource buf:world
从上述结果可知,在插入pltdrv.ko的时候driver被匹配了两次,因为在device中注册了两个设备。