Linux Platform 之 platform 结构体

2018/11/05 linux Driver

Linux Platform 之 platform_*

上篇文章 我们简要的分析了 platform 驱动开发中,最常见的 probe 函数匹配原则,而在 driver 中的 probe 函数的参数为 platform_device,这一篇文章我们来看一下 platform 设备总线模型中,最常用的结构体 platform_busplatform_device 以及 platform_driver

platform_driver

先从我们最经常打交道的 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: 当 device 和 driver 匹配成功后,会调用 probe,整个驱动的逻辑都在这了
  • remove: probe 失败后需要做的操作
  • suspend: suspend,挂起,例如Android系统在按下power熄屏,需要调用的函数
  • resume: resume,恢复,例如Android按下power亮屏,需要调用的函数
  • driver: 这个
  • id_table:probe 函数匹配规则,自从引入 dts 后,一般不使用了
  • prevent_deferred_probe: 一般默认,为 true 时,是用在不支持热插拔驱动中

device_driver

struct device_driver {
	const char		*name;
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
	enum probe_type probe_type;

	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;

	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 attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;
};
  • name,当前 driver 的名称,sysfs 中,ls /sys/bus/platform/drivers/,名称就是这个 name 控制,旧版本内核中用于和 device 匹配,引入 dts 后,一般在 of_match_table 中进行匹配,当然 name 机制也在用,只不过优先级比较低而已
  • bus,该driver所驱动设备的总线设备。为什么driver需要记录总线设备的指针呢?因为内核要保证在driver运行前,设备所依赖的总线能够正确初始化,当然针对平台总线,对应的 bus 就是 platform,在驱动进行注册时,会对其初始化,所以这个在驱动代码中一般不需要实现。详见(__platform_driver_register)
  • owner,所有者,无需驱动填充
  • mod_name,未知
  • suppress_bind_attrs,例如在 sysfs 中是否暴露接口给用户空间进行 bind/unbind 驱动, ls /sys/bus/platform/drivers/xxx/,会有 bind uevent unbind 出现,为 true 则会隐藏
  • of_match_table,和 device 进行匹配
  • probe\remove,这部分代码在进行驱动注册时,内核会帮忙填充,那为什么这里还需要 probe 函数呢?其实类似c++的重载,只是为了在调用 driver 的 probe,还需要进行其他的一些处理
  • shutdown\suspend\resume\pm 都和电源管理有关系了,这里先不分析
  • *p,私有数据

platform_device

代码路径在 include/linux/platform_device.h

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

这边着重关注 struct device(include/linux/device.h)

struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set/get_drvdata */
	struct dev_links_info	links;
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
	struct irq_domain	*msi_domain;
#endif
#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
	struct list_head	msi_list;
#endif

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	const struct dma_map_ops *dma_ops;
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
	struct removed_region *removed_mem;
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */
	struct fwnode_handle	*fwnode; /* firmware device node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
	struct iommu_group	*iommu_group;
	struct iommu_fwspec	*iommu_fwspec;

	bool			offline_disabled:1;
	bool			offline:1;
	bool			of_node_reused:1;
};

涉及的成员变量太多了,代码注释里面有很详细的描述,这里关注几个主要的:

  • parent,这个和 platform_bus 相关, platform 驱动模型认为,device 的父设备为 platform device
  • p,一个用于struct device的私有数据结构指针,该指针中会保存子设备链表、用于添加到bus/driver/prent等设备中的链表头等等
  • kobj,设计 Linux 的设备驱动模型,这个之后再分析
  • init_name,该设备的名称
  • mutex,互斥锁
  • *bus,标记在哪种总线上,针对 platform 设备驱动,则挂载的就是 platform,这样子,每个 device 就像葫芦一样挂在了藤蔓上了(bus)
  • *driver,该 device 对应的 device_driver
  • platform_data,一个指针,用于保存具体的平台相关的数据。具体的driver模块,可以将一些私有的数据,暂存在这里,需要使用的时候,再拿出来,因此设备模型并不关心该指针得实际含义。
  • of_node,dts 相关,设备的 node 名称就保存在这里了
  • groups,该设备的默认attribute集合。将会在设备注册时自动在sysfs中创建对应的文件。

小结

这篇文章简要分析了 struct device 已经 struct device_driver,然而在平常使用过程中,一般不会直接打交道,内核基于它们拓展了 soc device 、platform device等设备驱动,这里只是通过 platform 设备驱动模型,推出较为底层的 device 以及 device_driver而已。后续在驱动开发过程中遇到的问题再进行补充。

Search

    Table of Contents