linux driver设备模型简介
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中,有了设备节点,设备成功实现。