【Linux驱动:platform总线设备解析】

背景

嵌入式系统中有很多的物理总线:I2c、SPI、USB、uart、PCIE、APB、AHB
linux从2.6起就加入了一套新的驱动管理和注册的机制platform平台总线,是一条虚拟的总线,并不是一个物理的总线。
相比 PCI、USB,它主要用于描述SOC上的片上资源。platform 所描述的资源有一个共同点:在CPU 的总线上直接取址。
平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。
设备用platform_device表示,驱动用platform_driver进行注册。
与传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用资源,提高了代码的安全性和可移植性

三个重要的数据结构:bus_type、platform_device、platform_driver

其中,bus_type表示总线的类型,bus_type 中有个成员name 是用来指示bus类型的名字比如“platform”、"i2c"等。
每个总线类型都定义一个实体,然后会通过bus_register进行注册。如:

platform总线
在这里插入图片描述

i2c总线:
在这里插入图片描述
在这里插入图片描述

下面,来看一下三个结构体定义:

bus_type

struct bus_type {
	const char		*name;
	struct bus_attribute	*bus_attrs;
	struct device_attribute	*dev_attrs;
	struct driver_attribute	*drv_attrs;

	int (*match)(struct device *dev, struct device_driver *drv);//这个就是总线设备和驱动匹配函数
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	int (*remove)(struct device *dev);
	void (*shutdown)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;

	struct bus_type_private *p;
};

其中,要注意的是match成员,这个就是将来安装驱动的时候用来跟设备匹配的函数。该接口已经再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);

	/* match against the id table first */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);//实际上就是对比名字是否相等
}

另外就是probe接口和remove接口,用来注册和卸载bus的时候调用。

platform_device

struct platform_device {
	const char	* name;			// 平台总线下设备的名字
	int		id;
	struct device	dev;		// 所有设备通用的属性部分
	u32		num_resources;		// 设备使用到的resource的个数
	struct resource	* resource;	// 设备使用到的资源数组的首地址

	const struct platform_device_id	*id_entry;	// 设备ID表
 
	/* arch specific additions */
	struct pdev_archdata	archdata;			// 自留地,用来提供扩展性的
};

需要注意的时name ,表示设备的名字,在/sys/bus/platform/ 下会显示,其次用来跟驱动中的driver.name进行match。
另外需要注意的是:
num_resource:用来记录资源数量
resource: 指向资源结构体数组的首地址
dev:这是device 结构体实体,通常会在驱动中通过platform_device结构体传参,然后间接访问该结构体成员。比如:
在这里插入图片描述
上面的platform_data可以指向用户定义的设备属性,资源等信息。

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;// 设备ID表,可以有多个设备公用一个驱动
};

其中probe就是安装驱动时,通过platform总线找到(match)对应的设备后,调用的探测函数。在该函数内部可以进行设备资源获取,设置等操作。

另外,里面包含了struct device_driver driver成员,driver中的name就是用来跟platform_device 中的name进行match的材料。
在这里插入图片描述

注册接口

1)注册驱动platform_device_register

/**
 * platform_device_register - add a platform-level device
 * @pdev: platform device we're adding
 */
int platform_device_register(struct platform_device *pdev)
{
 device_initialize(&pdev->dev);
 arch_setup_pdev_archdata(pdev);
 return platform_device_add(pdev);
}

2) 注册设备platform_driver_register

#define platform_driver_register(drv) \
 __platform_driver_register(drv, THIS_MODULE)

注册流程

platform_device定义和注册:

  1 #include <linux/init.h>                                                                                                                                                        
  2 #include <linux/module.h>
  3 #include <linux/platform_device.h>
  4 #include <linux/ioport.h>
  5 
  6 static void hello_release(struct device *dev)
  7 {
  8      return;
  9 }
 10 static struct platform_device hello_device =
 11 {
 12     .name = "hello",
 13     .id = -1,
 14     .dev.release = hello_release,
 15 };
 16 
 17 
 18 static int hello_init(void)
 19 {
 20     printk("hello_init \n");
 21     return platform_device_register(&hello_device);
 22 
 23 }
 24 static void hello_exit(void)
 25 {
 26     printk("hello_exit \n");
 27     platform_device_unregister(&hello_device);
 28     return;
 29 }
 30 MODULE_LICENSE("GPL");
 31 module_init(hello_init);
 32 module_exit(hello_exit);

platform_driver 定义和注册:

 1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/platform_device.h>
  4 #include <linux/ioport.h>
  5 
  6 static int hello_probe(struct platform_device *pdev)
  7 {
  8     printk("match ok \n");
  9     return 0;
 10 }
 11 static  int hello_remove(struct platform_device *pdev)
 12 {
 13     printk("hello_remove \n");
 14     return 0;
 15 }
 16 static struct platform_driver hello_driver =
 17 {
 18     .probe = hello_probe,
 19     .driver.name = "helle",
 20     .remove = hello_remove,     
 21 };
 22 static int hello_init(void)
 23 {
 24     printk("hello_init \n");
 25     return platform_driver_register(&hello_driver);
 26 }
 27 static void hello_exit(void)
 28 {
 29     printk("hello_exit \n");
 30     platform_driver_unregister(&hello_driver);
 31     return;
 32 }
 33 MODULE_LICENSE("GPL");
 34 module_init(hello_init);
 35 module_exit(hello_exit);

参考:
Linux设备驱动模型
手把手教Linux驱动10-platform总线详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值