Linux设备驱动——虚拟总线platform

在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中注册了两个设备。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值