Linux驱动-platform总线设备模型

        在未引进platform总线设备驱动模型之前,Linux驱动的代码都将硬件资源与寄存器的操作写死在代码中,这样的做法虽然能快速实现功能,但是可拓展性非常差,代码变得非常冗余,不便于后续的管理维护与二次开发。

        为了解决这个问题,Linux内核引进了platform总线设备模型,该总线设备模型提取了设备操作的共有属性进行抽象,并将这部分抽象的共有属性放在Linux内核中实现,为添加设备(platform_ device)和驱动(platform_driver)的操作提供统一的接口,将硬件资源与驱动程序分离开来。

        platform总线设备模型是一条虚拟的总线,platform_device表示设备,该设备相关的硬件资源配置放在platform_device中描述;platform_driver表示驱动,在驱动程序中获取platform_device中描述的硬件资源并操作硬件资源;在硬件资源发生变化时,只需要修改platform_device中的硬件资源信息即可,提高了安全性和可移植性。

        platform总线设备模型框图如下:

platform总线设备模型框图 

  • platform_device :指定硬件资源
  • platform_driver:在probe函数中获取硬件资源,并进行分配,设备,注册字符设备,创建设备节点等操作
     

 重要的数据结构

  • struct bus_type
  • struct platform_device
  • struct resource
  • struct platform_driver

 struct bus_type

// include/linux/device.h
struct bus_type {
	const char		*name;  //总线名字
	const char		*dev_name;
	struct device		*dev_root;
	......
	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);
    ......

    /*
     内核才能访问的私有数据,该数据结构有两条链表为struct klist klist_devices、
      struct klist klist_drivers,分别存放注册成功的platform_device设备节点和
      platform_driver设备节点 
    */
	struct subsys_private *p; 
 
	struct lock_class_key lock_key;
	bool need_parent_lock;
};

 struct platform_device

// include/linux/platform_device.h
struct platform_device {
	const char	*name; // 设备名称,要和platform_driver的name匹配
	int		id;       // 插入总线下相同name的设备编号,一个驱动支持多个设备
	bool		id_auto;

    /*该数据成员里面存放了总线设备链表的节点,与该设备匹配成功的platform_driver节点*/
	struct device	dev;

	u64		dma_mask;
	u32		num_resources;   //资源个数,有多个资源一般使用ARRAY_SIZE()来算资源个数
	struct resource	*resource;  // 设备资源,设备硬件信息

	const struct platform_device_id	*id_entry;

    /*匹配的最高优先级,优先与platform_driver下的struct device_driver driver里的name匹配*/
	char *driver_override; /* Driver name to force a match */ 

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

struct resource

// include/linux/ioport.h
struct resource {
    resource_size_t start;	// 设备在CPU总线的线性起始物理地址
    resource_size_t end;	// 设备在CPU总线的线性结尾物理地址
    const char *name;	// 设备的名称
    unsigned long flags;	// 设备的标志位
    unsigned long desc;
    struct resource *parent, *sibling, *child;
};

 struct platform_driver

// include/linux/platform_device.h
struct platform_driver {

    /*当有platform_device设备与platform_driver匹配成功时,
        则会调用probe函数分配、设置、注册file_operations、字符设备等;
        获取platform_device设备里的硬件资源信息,从而操作硬件资源
    */
	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 *);
    
    /*
        该数据成员里的name用于与设备匹配
    */
	struct device_driver driver;

    /*该数据成员用于与设备匹配*/
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};

 struct platform_device_id

// include/linux/mod_devicetable.h

/*id_table是一个数组,可以支持一个或多个设备名;数组的最后一个成员应该为空;*/
struct platform_device_id {
	char name[PLATFORM_NAME_SIZE];
	kernel_ulong_t driver_data;
};

platform初始化

platform的初始化操作在内核启动时完成,不需要驱动开发者修改;下面是内核对platform的初始化的源码

// drivers/base/platform.c
int __init platform_bus_init(void)
{
    int error;

    early_platform_cleanup();

    error = device_register(&platform_bus);
    if (error)
        return error;
    error = bus_register(&platform_bus_type);
    if (error)
        device_unregister(&platform_bus);
    of_platform_register_reconfig_notifier();
    return error;
}

bus_register()

注册成功后可在sysfs下看到/sys/bus/platform;

// drivers/base/platform.c

struct device platform_bus = {
    .init_name  = "platform",
};
/*将platform_bus外部引用给其他模块使用*/
EXPORT_SYMBOL_GPL(platform_bus);

struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_groups = platform_dev_groups,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
};
/*将platform_bus_type外部引用给其他模块使用*/
EXPORT_SYMBOL_GPL(platform_bus_type);

注册过程

  • platform_device_register函数
// drivers/base/platform.c
/**
 * 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);
	setup_pdev_dma_masks(pdev);
	return platform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);

函数调用流程

/*同一列表示同一级*/
platform_device_register
	platform_device_add
	    device_add
	        bus_add_device // 放入链表
            /*probe枚举设备,与platform_driver_list节点一一匹配,即找到匹配的(dev, drv)*/
	        bus_probe_device  
	            device_initial_probe
	                __device_attach
	                    bus_for_each_drv(...,__device_attach_driver,...)
	                        __device_attach_driver
	                            driver_match_device(drv, dev) // 是否匹配
	                            driver_probe_device         // 调用drv的probe

  • platform_driver_register函数
// include/linux/platform_device.h
#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)
extern int __platform_driver_register(struct platform_driver *,
					struct module *);
// drivers/base/platform.c
/**
 * __platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 * @owner: owning module/driver
 */
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__platform_driver_register);

函数调用流程

platform_driver_register
	__platform_driver_register
	    driver_register
	        bus_add_driver // 放入链表
	            driver_attach(drv)
	                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
	                        __driver_attach
	                            driver_match_device(drv, dev) // 是否匹配
	                            driver_probe_device         // 调用drv的probe

匹配流程

调用匹配函数 driver_match_device,最终会调用drivers\base\platform.c下的platform_match函数

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);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);
    
    /*用于设备树节点匹配,暂时先不看*/
	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	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);
}

匹配规则

  1. 最先比较:platform_device. driver_override和platform_driver.driver.name
    可以设置platform_device的driver_override,强制选择某个platform_driver。
  2. 用设备树节点进行匹配,这里不讲,详细可看另一篇设备树文章。(51条消息) Linux驱动-设备树_cola冲冲冲的博客-CSDN博客
  3. 然后比较:platform_device. name和platform_driver.id_table[i].name
    Platform_driver.id_table是“platform_device_id”指针,表示该drv支持若干个device,它里面列出了各个device的{.name, .driver_data},其中的“name”表示该drv支持的设备的名字,driver_data是些提供给该device的私有数据

  4. 最后比较:platform_device.name和platform_driver.driver.name
    platform_driver.id_table可能为空,
    这时可以根据platform_driver.driver.name来寻找同名的platform_device。

调用probe函数

driver_probe_device()函数通过really_probe()函数,调用drv->bus->probe,就是指向platform_bus_type的probe函数指针,即platform_probe()函数,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值