Linux设备模型简介--随记

linux设备模型介绍

linux内核代码有80%都是driver,越来越多的设备,就会需要越来越多的驱动,这样会使内核代码越来越庞大臃肿,为降低设备多样性带来的驱动开发的复杂度,以及设备热插拔、电源管理等 ,内核有了设备模型概念(driver module)。
设备模型将硬件设备归类,抽象出一套标准的数据结构和接口,这样,开发driver,就是对内核规定的结构体做数据填充和实现。设备模型涉及总线BUS、类别Class、设备Device、和设备驱动Device Driver。
内核中这几个概念可以描述成一个拓扑模型,设备模型其实就是,各类设备都挂接在相应的总线上,然后在启动和使用设备过程中,去给各类设备匹配设备驱动,匹配好了,设备也就有了驱动,然后通过总线去和CPU互通。

总线

BUS:linux认为总线是CPU和一个或者多个设备之间信息交互的通道,为了实现设备模型的抽象,将所有的设备都连接到总线上,无论是CPU内部总线、虚拟总线或是platform bus。
总结:
1、总线是处理器和一个或多个设备之间的通道
2、在设备模型中,所有的设备都通过总线相连,甚至是内部的虚拟“platform”总线
3、总线可以相互插入,设备模型展示了总线和它们所控制的设备之间的实际连接
4、linux设备模型中,总线由bus_type结构体表示
5、每个bus_type对象都对应/sys/bus目录下的一个子目录,如PCI总线类型对应/sys/bus/pci
6、在每个目录下都存在两个子目录,devices和drivers,分别对应与bus_type结构中的devices和drivers域
7、devices子目录描述连接在该总线上的所有设备
8、drivers子目录描述和该总线关联的所有驱动程序

定义在include/linux/device.h下:

/**
 * struct bus_type - The bus type of the device
 *
 * @name:	The name of the bus.
 * @dev_name:	Used for subsystems to enumerate devices like ("foo%u", dev->id).
 * @dev_root:	Default device to use as the parent.
 * @bus_groups:	Default attributes of the bus.
 * @dev_groups:	Default attributes of the devices on the bus.
 * @drv_groups: Default attributes of the device drivers on the bus.
 * @match:	Called, perhaps multiple times, whenever a new device or driver
 *		is added for this bus. It should return a positive value if the
 *		given device can be handled by the given driver and zero
 *		otherwise. It may also return error code if determining that
 *		the driver supports the device is not possible. In case of
 *		-EPROBE_DEFER it will queue the device for deferred probing.
 * @uevent:	Called when a device is added, removed, or a few other things
 *		that generate uevents to add the environment variables.
 * @probe:	Called when a new device or driver add to this bus, and callback
 *		the specific driver's probe to initial the matched device.
 * @remove:	Called when a device removed from this bus.
 * @shutdown:	Called at shut-down time to quiesce the device.
 *
 * @online:	Called to put the device back online (after offlining it).
 * @offline:	Called to put the device offline for hot-removal. May fail.
 *
 * @suspend:	Called when a device on this bus wants to go to sleep mode.
 * @resume:	Called to bring a device on this bus out of sleep mode.
 * @num_vf:	Called to find out how many virtual functions a device on this
 *		bus supports.
 * @dma_configure:	Called to setup DMA configuration on a device on
 *			this bus.
 * @pm:		Power management operations of this bus, callback the specific
 *		device driver's pm-ops.
 * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
 *              driver implementations to a bus and allow the driver to do
 *              bus-specific setup
 * @p:		The private data of the driver core, only the driver core can
 *		touch this.
 * @lock_key:	Lock class key for use by the lock validator
 * @need_parent_lock:	When probing or removing a device on this bus, the
 *			device core should lock the device's parent.
 *
 * A bus is a channel between the processor and one or more devices. For the
 * purposes of the device model, all devices are connected via a bus, even if
 * it is an internal, virtual, "platform" bus. Buses can plug into each other.
 * A USB controller is usually a PCI device, for example. The device model
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the
 * default attributes, the bus' methods, PM operations, and the driver core's
 * private data.
 */
struct bus_type {
	const char		*name;					总线名称
	const char		*dev_name;
	struct device		*dev_root;
	const struct attribute_group **bus_groups;					总线属性
	const struct attribute_group **dev_groups;					该总线上所有设备的默认属性
	const struct attribute_group **drv_groups;					该总线上所有驱动的默认属性

	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 (*online)(struct device *dev);
	int (*offline)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);				挂起睡眠梳理
	int (*resume)(struct device *dev);					设备恢复处理

	int (*num_vf)(struct device *dev);

	int (*dma_configure)(struct device *dev);

	const struct dev_pm_ops *pm;					电源管理处理方法集合

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;					私有数据
	struct lock_class_key lock_key;

	bool need_parent_lock;
};

bus_type有一个匹配函数match,一般通过比较驱动和设备的名字来进行匹配,当涉及到真实硬件,match函数常常在有设备自身提供的硬件ID和驱动提供的ID之间,做一些比较,match匹配成功返回1,失败返回0

bus_type有一个用户空间的热插拔通知辅助函数uevent,在设备注册、移除或者状态更改时,内核负责发送通知事件到用户空间,uevent在事件发送到用户空间之前调用,用来给事件添加总线特定的环境变量

设备

Device:抽象系统中的所有硬件设备,描述它的名字、属性、从属的BUS、从属的Class等信息
总结:
1、内核提供了相应的函数用于操作device对象:
device_register()函数将一个新的device对象插入设备模型中,并且自动在sys/devices下创建一个对应的目录
device_unregister()完成相反的操作,注销设别对象
2、通常device结构不单独使用,而是包含在更大的结构中作为一个子结构使用,比如描述PCI设备的struct pci_dev

定义在include/linux/device.h下:

/**
 * struct device - The basic device structure
 * @parent:	The device's "parent" device, the device to which it is attached.
 * 		In most cases, a parent device is some sort of bus or host
 * 		controller. If parent is NULL, the device, is a top-level device,
 * 		which is not usually what you want.
 * @p:		Holds the private data of the driver core portions of the device.
 * 		See the comment of the struct device_private for detail.
 * @kobj:	A top-level, abstract class from which other classes are derived.
 * @init_name:	Initial name of the device.
 * @type:	The type of device.
 * 		This identifies the device type and carries type-specific
 * 		information.
 * @mutex:	Mutex to synchronize calls to its driver.
 * @lockdep_mutex: An optional debug lock that a subsystem can use as a
 * 		peer lock to gain localized lockdep coverage of the device_lock.
 * @bus:	Type of bus device is on.
 * @driver:	Which driver has allocated this
 * @platform_data: Platform data specific to the device.
 * 		Example: For devices on custom boards, as typical of embedded
 * 		and SOC based hardware, Linux often uses platform_data to point
 * 		to board-specific structures describing devices and how they
 * 		are wired.  That can include what ports are available, chip
 * 		variants, which GPIO pins act in what additional roles, and so
 * 		on.  This shrinks the "Board Support Packages" (BSPs) and
 * 		minimizes board-specific #ifdefs in drivers.
 * @driver_data: Private pointer for driver specific info.
 * @links:	Links to suppliers and consumers of this device.
 * @power:	For device power management.
 *		See Documentation/driver-api/pm/devices.rst for details.
 * @pm_domain:	Provide callbacks that are executed during system suspend,
 * 		hibernation, system resume and during runtime PM transitions
 * 		along with subsystem-level and driver-level callbacks.
 * @pins:	For device pin management.
 *		See Documentation/driver-api/pinctl.rst for details.
 * @msi_list:	Hosts MSI descriptors
 * @msi_domain: The generic MSI domain this device is using.
 * @numa_node:	NUMA node this device is close to.
 * @dma_ops:    DMA mapping operations for this device.
 * @dma_mask:	Dma mask (if dma'ble device).
 * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
 * 		hardware supports 64-bit addresses for consistent allocations
 * 		such descriptors.
 * @bus_dma_mask: Mask of an upstream bridge or bus which imposes a smaller DMA
 *		limit than the device itself supports.
 * @dma_pfn_offset: offset of DMA memory range relatively of RAM
 * @dma_parms:	A low level driver may set these to teach IOMMU code about
 * 		segment limitations.
 * @dma_pools:	Dma pools (if dma'ble device).
 * @dma_mem:	Internal for coherent mem override.
 * @cma_area:	Contiguous memory area for dma allocations
 * @archdata:	For arch-specific additions.
 * @of_node:	Associated device tree node.
 * @fwnode:	Associated device node supplied by platform firmware.
 * @devt:	For creating the sysfs "dev".
 * @id:		device instance
 * @devres_lock: Spinlock to protect the resource of the device.
 * @devres_head: The resources list of the device.
 * @knode_class: The node used to add the device to the class list.
 * @class:	The class of the device.
 * @groups:	Optional attribute groups.
 * @release:	Callback to free the device after all references have
 * 		gone away. This should be set by the allocator of the
 * 		device (i.e. the bus driver that discovered the device).
 * @iommu_group: IOMMU group the device belongs to.
 * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
 * @iommu_param: Per device generic IOMMU runtime data
 *
 * @offline_disabled: If set, the device is permanently online.
 * @offline:	Set after successful invocation of bus type's .offline().
 * @of_node_reused: Set if the device-tree node is shared with an ancestor
 *              device.
 * @dma_coherent: this particular device is dma coherent, even if the
 *		architecture supports non-coherent devices.
 *
 * At the lowest level, every device in a Linux system is represented by an
 * instance of struct device. The device structure contains the information
 * that the device model core needs to model the system. Most subsystems,
 * however, track additional information about the devices they host. As a
 * result, it is rare for devices to be represented by bare device structures;
 * instead, that structure, like kobject structures, is usually embedded within
 * a higher-level representation of the device.
 */
struct device {
	struct kobject kobj;
	struct device		*parent;

	struct device_private	*p;

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

	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_drvdata/dev_get_drvdata */
#ifdef CONFIG_PROVE_LOCKING
	struct mutex		lockdep_mutex;
#endif
	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	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

	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. */
	u64		bus_dma_mask;	/* upstream dma_mask constraint */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

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

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

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

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	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;
	struct iommu_param	*iommu_param;

	bool			offline_disabled:1;
	bool			offline:1;
	bool			of_node_reused:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
	bool			dma_coherent:1;
#endif
};

device接口函数:

struct device *device_create(struct class *cls, struct device *parent,
			     dev_t devt, void *drvdata,
			     const char *fmt, ...);
extern __printf(6, 7)
extern void device_destroy(struct class *cls, dev_t devt);

驱动

Driver:linux设备模型用driver抽象硬件设备的驱动程序,它包含设备初始化,电源管理相关的接口实现,内核中的驱动开发,基本都围绕该抽象函数

device_driver结构体:

/**
 * struct device_driver - The basic device driver structure
 * @name:	Name of the device driver.
 * @bus:	The bus which the device of this driver belongs to.
 * @owner:	The module owner.
 * @mod_name:	Used for built-in modules.
 * @suppress_bind_attrs: Disables bind/unbind via sysfs.
 * @probe_type:	Type of the probe (synchronous or asynchronous) to use.
 * @of_match_table: The open firmware table.
 * @acpi_match_table: The ACPI match table.
 * @probe:	Called to query the existence of a specific device,
 *		whether this driver can work with it, and bind the driver
 *		to a specific device.
 * @remove:	Called when the device is removed from the system to
 *		unbind a device from this driver.
 * @shutdown:	Called at shut-down time to quiesce the device.
 * @suspend:	Called to put the device to sleep mode. Usually to a
 *		low power state.
 * @resume:	Called to bring a device from sleep mode.
 * @groups:	Default attributes that get created by the driver core
 *		automatically.
 * @pm:		Power management operations of the device which matched
 *		this driver.
 * @coredump:	Called when sysfs entry is written to. The device driver
 *		is expected to call the dev_coredump API resulting in a
 *		uevent.
 * @p:		Driver core's private data, no one other than the driver
 *		core can touch this.
 *
 * The device driver-model tracks all of the drivers known to the system.
 * The main reason for this tracking is to enable the driver core to match
 * up drivers with new devices. Once drivers are known objects within the
 * system, however, a number of other things become possible. Device drivers
 * can export information and configuration variables that are independent
 * of any specific device.
 */
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;
	void (*coredump) (struct device *dev);

	struct driver_private *p;
};

驱动的注册和移除函数也在include/linux/device.h下面定义

Class:设备模型中,Class的概念类似面向对象设计总的类Class,它主要是集合具有相似功能或属性的设备,这样可以抽象出一套在多个设备之间共用的数据结构和接口函数,因而从属于相同Class的设备的驱动程序,就不再需要重新定义这些公共资源,直接从Class中继承即可
总结:
1、类是一个设备的高层视图,抽象除了底层的实现细节,从而允许用户空间使用设备所提供的功能,而不用关心设备是如何连接和工作的
2、类成员通常由上层代码控制,而无需驱动的明确支持
3、驱动程序核心导出了一些接口,其目的之一是提供包含设备号的属性以便自动创建设备节点,udev的使用离不开类

/**
 * struct class - device classes
 * @name:	Name of the class.
 * @owner:	The module owner.
 * @class_groups: Default attributes of this class.
 * @dev_groups:	Default attributes of the devices that belong to the class.
 * @dev_kobj:	The kobject that represents this class and links it into the hierarchy.
 * @dev_uevent:	Called when a device is added, removed from this class, or a
 *		few other things that generate uevents to add the environment
 *		variables.
 * @devnode:	Callback to provide the devtmpfs.
 * @class_release: Called to release this class.
 * @dev_release: Called to release the device.
 * @shutdown_pre: Called at shut-down time before driver shutdown.
 * @ns_type:	Callbacks so sysfs can detemine namespaces.
 * @namespace:	Namespace of the device belongs to this class.
 * @get_ownership: Allows class to specify uid/gid of the sysfs directories
 *		for the devices belonging to the class. Usually tied to
 *		device's namespace.
 * @pm:		The default device power management operations of this class.
 * @p:		The private data of the driver core, no one other than the
 *		driver core can touch this.
 *
 * A class is a higher-level view of a device that abstracts out low-level
 * implementation details. Drivers may see a SCSI disk or an ATA disk, but,
 * at the class level, they are all simply disks. Classes allow user space
 * to work with devices based on what they do, rather than how they are
 * connected or how they work.
 */
struct class {
	const char		*name;
	struct module		*owner;

	const struct attribute_group	**class_groups;
	const struct attribute_group	**dev_groups;
	struct kobject			*dev_kobj;

	int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
	char *(*devnode)(struct device *dev, umode_t *mode);

	void (*class_release)(struct class *class);
	void (*dev_release)(struct device *dev);

	int (*shutdown_pre)(struct device *dev);

	const struct kobj_ns_type_operations *ns_type;
	const void *(*namespace)(struct device *dev);

	void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);

	const struct dev_pm_ops *pm;

	struct subsys_private *p;
};

4、每个类需要一个唯一的名字,它在/sys/class中,当这个类被注册,由class_groups所指向的数组中列出的所有属性被创建,还有一套缺省属性给每个添加到类中的设备;dev_groups指向它们,有通常的热插拔函数来添加变量到环境中,当事件产生时,还有两个释放方法:dev_release在无论何时从类中去除一个设备时调用,class_release在类自己被释放时调用
5、内核提供了class_create函数创建类,这个类放在sysfs文件系统下,一旦创建好了这个类,再调用device_create函数来在/dev目录下创建相应的设备节点。加载模块时,用户空间的udev会自动相应device_create函数,去/sysfs下面寻找对应的类去创建设备节点

Class接口函数:

extern struct class * __must_check __class_create(struct module *owner,
						  const char *name,
						  struct lock_class_key *key);
extern void class_destroy(struct class *cls);

sysfs

1、sysfs虚拟文件系统类似proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息,即将内核的设备和驱动信息输出到用户空间

2、主要目录:
Block:包含所有的块设备
Devices:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构
Bus:包含系统中所有的总线类型
Drivers:包含内核中所有已注册的设备驱动程序
Class:系统的设备类型(如网卡设备等)

3、sys下面的目录和文件反映了系统的状况。比如bus下就包含了系统用到的一系列总线pci、scsi、usb、spi等。

4、sysfs是一个基于内存的特殊文件系统,没有一个实际存放文件的介质

5、sysfs的信息来源是kobject层次结构,读一个sysfs文件,就是动态的从kobject结构中提取信息,生成文件,再给用户

kobject

1、kobject层次结构就是linux的设备模型

struct kobject {
	const char		*name;
	struct list_head	entry;
	struct kobject		*parent;
	struct kset		*kset;
	struct kobj_type	*ktype;
	struct kernfs_node	*sd; /* sysfs directory entry */
	struct kref		kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
	struct delayed_work	release;
#endif
	unsigned int state_initialized:1;
	unsigned int state_in_sysfs:1;
	unsigned int state_add_uevent_sent:1;
	unsigned int state_remove_uevent_sent:1;
	unsigned int uevent_suppress:1;
};

2、kobject使所有设备在底层都具有统一的接口,提供基本的对象管理,是构成设备模型的核心结构。它和sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录

3、kobject是组成设备模型的基本结构,它类似C++基类,嵌入到更大的对象中用来描述设备模型的组件,比如bus,device,driver都是典型的容器。这些对象通过kobject连接起来,形成一个树状结构,和/sys相对应

4、kobject结构为上层数据结构和子系统提供了基本的对象管理,避免了类似功能的重复实现

总结:
kobject所处理的任务和支持代码包括:
1、对象的引用计数:跟踪对象生命周期的一种方法是使用引用计数,当没有内核代码持有该对象的引用时,该对象将结束自己的有效生命期并可以被删除
2、sysfs表述:在sysfs中出现的每个对象都对应一个kobject,它和内核交互来创建它的可见表述
3、数据结构关联:整体来看,设备模型就是一个极端复杂的数据结构,通过期间的大量链接而构成一个多层次的体系结构,kobject实现了该结构并将其聚合在一起(通过kset)
4、热插拔事件处理:kobject子系统将产生的热插拔事件通知用户空间

kset、kobj_type

kset是对象的聚合和集合,ktype是对象的类型,它们是kobject在层次结构和属性结构方面的扩充

struct kset {
	struct list_head list;
	spinlock_t list_lock;
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;


struct kobj_type {
	void (*release)(struct kobject *kobj);
	const struct sysfs_ops *sysfs_ops;
	struct attribute **default_attrs;	/* use default_groups instead */
	const struct attribute_group **default_groups;
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
	void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};

1、每个kobject都有自己的parent,在没有指定parent的情况下,都会指向它所属的kset->object,然后kset内部也嵌入了kobject,这个kobject又可以指向它上一级的parent,这样就形成了一个空间上的层次关系

2、每个对象都有属性,如电源管理、热插拔事件等,因为大部分同类设备都有相同的属性,因此将这个属性隔离开来,存放在ktype中,这样就可以灵活管理,对于sysfs中的普通文件读写操作都是由kobject->ktype->sysfs_ops来完成的

3、一个kset的主要功能是当做顶层的kobject的容器类,实际上每个kset在内部容纳它自己的kobject,并且在许多情况下,如同一个kobject形同方式被对待。kset一直在sysfs中出现,一旦一个kset已被建立并且加入到系统中,会有一个sysfs目录给它,每个kset成员的kobject都会出现在那里。

接口函数:

extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);				初始化一个kobject
extern __printf(3, 4) __must_check
int kobject_add(struct kobject *kobj, struct kobject *parent,				增加一个kobject到kset中
		const char *fmt, ...);
extern __printf(4, 5) __must_check
int kobject_init_and_add(struct kobject *kobj,					init和add的结合
			 struct kobj_type *ktype, struct kobject *parent,
			 const char *fmt, ...);

extern void kobject_del(struct kobject *kobj);				将一个kobject从kset中移除

platform bus

2.6内核起,引入了一套新的驱动管理和注册机制,platform_device和platform_driver。linux中大部分设备驱动都可以使用这套机制。
platform是一个虚拟的地址总线,相比pci、usb等,它主要用于描述SOC的片上资源,比如集成控制器lcd、watchdog、rtc等,platform所描述的资源有一个共同点,就是在cpu的总线上直接取址

struct device platform_bus = {
	.init_name	= "platform",
};
EXPORT_SYMBOL_GPL(platform_bus);

1、platform_device会分到一个名称用在驱动绑定中以及一些列如地址和中断请求irq之类的资源

struct platform_device {
	const char	*name;					平台设备名称,如led
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;
	struct resource	*resource;					指向设备资源,设备树dts

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

2、platform获取设备io资源和中断等资源从dts中:

/**
 * platform_get_resource - get a resource for a device
 * @dev: platform device
 * @type: resource type
 * @num: resource index
 */
struct resource *platform_get_resource(struct platform_device *dev,
				       unsigned int type, unsigned int num)
{
	int i;

	for (i = 0; i < dev->num_resources; i++) {
		struct resource *r = &dev->resource[i];

		if (type == resource_type(r) && num-- == 0)
			return r;
	}
	return NULL;
}
EXPORT_SYMBOL_GPL(platform_get_resource);

3、platform_device添加和删除:

/**
 * 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);
}
EXPORT_SYMBOL_GPL(platform_device_register);

/**
 * platform_device_unregister - unregister a platform-level device
 * @pdev: platform device we're unregistering
 *
 * Unregistration is done in 2 steps. First we release all resources
 * and remove it from the subsystem, then we drop reference count by
 * calling platform_device_put().
 */
void platform_device_unregister(struct platform_device *pdev)
{
	platform_device_del(pdev);
	platform_device_put(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_unregister);

4、platorm_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;
};

总结:
1、系统为platform总线定义一个bus_type的实例platform_bus_type,通过其成员函数match来确定device和driver匹配
2、匹配platform_device和platform_driver主要看二者的name字段是否相同,name必须要相同才能匹配
3.、用platform_device_register函数注册单个的平台设备,一般是在平台的BSP文件中定义platform_device,通过platform_add_devices函数将平台涉笔注册到系统中

一个platform驱动设计步骤

一个platform bus驱动的实现:

struct bus_type xxx_bus = {
	.name = "xxx",
	.dev_groups = xxx,
	.match = xxx_match,
	.uevent = xxx_uevent,
	.probe = xxx_probe,
	.remove = xxx_remove,
};

1、添加头文件
#include <linux/device.h>
#include <linux/platform_device.h>
2、init模块入口函数是platform平台驱动的probe探测函数

static int i2c_gpio_probe(struct platform_device *pdev)
{
	struct i2c_gpio_private_data *priv;
	struct i2c_gpio_platform_data *pdata;
	struct i2c_algo_bit_data *bit_data;
	struct i2c_adapter *adap;
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	enum gpiod_flags gflags;
	int ret;

	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	adap = &priv->adap;
	bit_data = &priv->bit_data;
	pdata = &priv->pdata;

	if (np) {
		of_i2c_gpio_get_props(np, pdata);
	} else {
		/*
		 * If all platform data settings are zero it is OK
		 * to not provide any platform data from the board.
		 */
		if (dev_get_platdata(dev))
			memcpy(pdata, dev_get_platdata(dev), sizeof(*pdata));
	}

	/*
	 * First get the GPIO pins; if it fails, we'll defer the probe.
	 * If the SCL/SDA lines are marked "open drain" by platform data or
	 * device tree then this means that something outside of our control is
	 * marking these lines to be handled as open drain, and we should just
	 * handle them as we handle any other output. Else we enforce open
	 * drain as this is required for an I2C bus.
	 */
	if (pdata->sda_is_open_drain)
		gflags = GPIOD_OUT_HIGH;
	else
		gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
	priv->sda = i2c_gpio_get_desc(dev, "sda", 0, gflags);
	if (IS_ERR(priv->sda))
		return PTR_ERR(priv->sda);

	if (pdata->scl_is_open_drain)
		gflags = GPIOD_OUT_HIGH;
	else
		gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
	priv->scl = i2c_gpio_get_desc(dev, "scl", 1, gflags);
	if (IS_ERR(priv->scl))
		return PTR_ERR(priv->scl);

	if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl))
		dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing");
	else
		bit_data->can_do_atomic = true;

	bit_data->setsda = i2c_gpio_setsda_val;
	bit_data->setscl = i2c_gpio_setscl_val;

	if (!pdata->scl_is_output_only)
		bit_data->getscl = i2c_gpio_getscl;
	bit_data->getsda = i2c_gpio_getsda;

	if (pdata->udelay)
		bit_data->udelay = pdata->udelay;
	else if (pdata->scl_is_output_only)
		bit_data->udelay = 50;			/* 10 kHz */
	else
		bit_data->udelay = 5;			/* 100 kHz */

	if (pdata->timeout)
		bit_data->timeout = pdata->timeout;
	else
		bit_data->timeout = HZ / 10;		/* 100 ms */

	bit_data->data = priv;

	adap->owner = THIS_MODULE;
	if (np)
		strlcpy(adap->name, dev_name(dev), sizeof(adap->name));
	else
		snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);

	adap->algo_data = bit_data;
	adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
	adap->dev.parent = dev;
	adap->dev.of_node = np;

	adap->nr = pdev->id;
	ret = i2c_bit_add_numbered_bus(adap);
	if (ret)
		return ret;

	platform_set_drvdata(pdev, priv);

	/*
	 * FIXME: using global GPIO numbers is not helpful. If/when we
	 * get accessors to get the actual name of the GPIO line,
	 * from the descriptor, then provide that instead.
	 */
	dev_info(dev, "using lines %u (SDA) and %u (SCL%s)\n",
		 desc_to_gpio(priv->sda), desc_to_gpio(priv->scl),
		 pdata->scl_is_output_only
		 ? ", no clock stretching" : "");

	i2c_gpio_fault_injector_init(pdev);

	return 0;
}

3、exit模块退出函数是platform平台驱动的remove移除函数

static int i2c_gpio_remove(struct platform_device *pdev)
{
	struct i2c_gpio_private_data *priv;
	struct i2c_adapter *adap;

	i2c_gpio_fault_injector_exit(pdev);

	priv = platform_get_drvdata(pdev);
	adap = &priv->adap;

	i2c_del_adapter(adap);

	return 0;
}

4、定义platform平台的设备和驱动

#if defined(CONFIG_OF)
static const struct of_device_id i2c_gpio_dt_ids[] = {
	{ .compatible = "i2c-gpio", },
	{ /* sentinel */ }
};

MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
#endif

static struct platform_driver i2c_gpio_driver = {
	.driver		= {
		.name	= "i2c-gpio",
		.of_match_table	= of_match_ptr(i2c_gpio_dt_ids),
	},
	.probe		= i2c_gpio_probe,
	.remove		= i2c_gpio_remove,
};

5、平台设备注册和注销,即驱动模块的入口和退出接口

static int __init i2c_gpio_init(void)
{
	int ret;

	ret = platform_driver_register(&i2c_gpio_driver);
	if (ret)
		printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);

	return ret;
}
subsys_initcall(i2c_gpio_init);

static void __exit i2c_gpio_exit(void)
{
	platform_driver_unregister(&i2c_gpio_driver);
}
module_exit(i2c_gpio_exit);

MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
MODULE_DESCRIPTION("Platform-independent bitbanging I2C driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:i2c-gpio");

总结

linux设备模型比较上面的大框架是 总线—设备—类---驱动
在这个设备模型层次下面的框架是kobject—ktype—kset—sysfs
一个新的设备添加流程:
上面一点的实现层次是:
首先会去分配创建一个设备号,去注册这个设备,将设备添加到一个类里面,然后定义这个设备的名字,创建一个probe驱动探测函数,然后在总线上通过match函数去寻找匹配的驱动,找到了就挂到这个总线上。
再底层一点的实现层次是:
将这个设备添加到dts中,然后kobject去获取到这个设备信息,这个设备对象就是一个kobject,每个kobject都有ktype和kset,有了kset之后,就会自动添加到sysfs中,然后在/sys中就有了这个设备对象
一起理解实现就是:
添加一个新的设备驱动,通过probe探测函数和match匹配函数实现设备和驱动的分类和总线挂接,这个过程中也是设备对象kobject自动添加到sysfs的过程,设备模型的层次结构通过kobject对象的聚合kset实现,分类通过ktype实现,最后挂接到总线,也就添加到了sysfs中,有了设备节点,设备成功实现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值