一、Linux总线设备驱动模型框架
从Linux2.6开始Linux加入了一套驱动管理和注册机制—platform平台总线驱动模型。platform平台总线是一条虚拟总线,platform_device为相应的设备,platform_driver为相应的驱动。与传统的bus/device/driver机制相比,platform由内核统一进行管理,提高了代码的可移植性和安全性。所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段。Linux总线设备驱动模型的框架如下图所示:
从图中我们可以很清楚的看出Linux平台总线设备驱动模型的整体架构。在总线设备驱动模型中,需关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。当系统向内核注册每一个驱动程序时,都要通过调用platform_driver_register函数将驱动程序注册到总线,并将其放入所属总线的drv链表中,注册驱动的时候会调用所属总线的match函数寻找该总线上与之匹配的每一个设备,如果找到与之匹配的设备则会调用相应的probe函数将相应的设备和驱动进行绑定;同样的当系统向内核注册每一个设备时,都要通过调用platform_device_register函数将设备注册到总线,并将其放入所属总线的dev链表中,注册设备的时候同样也会调用所属总线的match函数寻找该总线上与之匹配的每一个驱动程序,如果找到与之匹配的驱动程序时会调用相应的probe函数将相应的设备和驱动进行绑定;而这一匹配的过程是由总线自动完成的。
下面是基于linux3.10
1.总线
include/device.h
1.1描述结构体
/**
* 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_attrs: Default attributes of the bus.
* @dev_attrs: Default attributes of the devices on the bus.
* @drv_attrs: 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 nonzero value if the
* given device can be handled by the given driver.
* @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.
* @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.
* @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.
*
* 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;
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 iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
1.2注册
//若成功,新总线将被添加进系统,并可在/sys/bus下看到相应目录
extern int __must_check bus_register(struct bus_type *bus);
1.3注销
extern void bus_unregister(struct bus_type *bus);
1.4示例
Tbus.c
#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/device.h>
int Tmatch(struct device *dev, struct device_driver *drv)
{
return 0;
}
struct bus_type Tbus_type =
{
.name = "Tbus",
.match = Tmatch,
};
int Tbus_init(void)
{
int ret;
ret = bus_register(&Tbus_type);
return ret;
}
void Tbus_exit(void)
{
bus_unregister(&Tbus_type);
}
module_init(Tbus_init);
module_exit(Tbus_exit);
MODULE_LICENSE("GPL");
下面向上面的my_bus总线上挂载一个驱动!
二、platform驱动的相关数据结构
1. 结构体驱动对应的结构体platform_driver
include/platform_device.h
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;
};
可以看到platform_driver结构体中包含了probe和remove等相关操作,同时还内嵌了device_driver结构体。
2. device_driver结构体
include/device/h
/**
* 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.
* @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.
* @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 */
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;
};
其中有一个指向driver_private的指针p,一些与其他的组件相关的联系都被移到这个结构变量中。
3. driver_private结构体
struct driver_private {
struct kobject kobj;// 在sysfs中代表目录本身
struct klist klist_devices;// 驱动链表即我们上面所说的drv链表
struct klist_node knode_bus;// 挂载在总线上的驱动链表的节点
struct module_kobject *mkobj;// driver与相关的module之间的联系
struct device_driver *driver;
};
#define to_driver(obj) container_of(obj, struct driver_private, kobj)
4.注册
int driver_register(struct device_driver *drv)
5.注销
void driver_unregister(struct device_driver *drv)
三、platform设备的相关数据结构
1. platform设备对应的结构体platform_device
struct mfd_cell;
struct platform_device {
const char *name;// 设备的名字这将代替device->dev_id,用作sys/device下显示的目录名
int id;// 设备id,用于给插入给该总线并且具有相同name的设备编号,如果只有一个设备的话填-1。
bool id_auto;
struct device dev;// 内嵌device结构
u32 num_resources;// 资源的数目
struct resource *resource;// 资源
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
可以看出platform_device结构体中包含了名字、id、资源和内嵌device结构体。
2. resource结构体
struct resource {
resource_size_t start; // 资源的起始地址
resource_size_t end; // 资源的结束地址
const char *name;
unsigned long flags; // 资源的类型
struct resource *parent, *sibling, *child;
};
该结构体非常重要,用于存放设备的资源信息,如IO地址、中断号等。
3.设备注册
int device_register(struct device *dev)
4.设备注销
void device_unregister(struct device *dev)
简单示例
Tdevice.c
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
extern struct bus_type Tbus_type;
static void Trelease(struct device *dev)
{
}
struct device Tdev =
{
.init_name = "Tdev",//和驱动名字一样
.bus = &Tbus_type,
.release = Trelease,
};
int Tdevice_init(void)
{
int ret;
ret = device_register(&Tdev);
return ret;
}
void Tdevice_exit(void)
{
device_unregister(&Tdev);
}
module_init(Tdevice_init);
module_exit(Tdevice_exit);
MODULE_LICENSE("GPL");
Tdriver.c
#include<linux/module.h>
#include<linux/init.h>
#include<linux/device.h>
#include<linux/kernel.h>
extern struct bus_type Tbus_type;
int Tprobe(struct device *dev)
{
printk(KERN_WARNING"driver found the device!!!\n");
return 0;
}
struct device_driver Tdriver =
{
.name = "Tdev",
.bus = &Tbus_type,
.probe = Tprobe, //当找到这个设备时将调用这个函数
};
int Tdevice_init(void)
{
int ret;
ret = driver_register(&Tdriver);//注册一个驱动
return 0;
}
void Tdevice_exit(void)
{
driver_unregister(&Tdriver);
}
module_init(Tdevice_init);
module_exit(Tdevice_exit);
MODULE_LICENSE("GPL");
Tbus.c
#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/device.h>
int Tmatch(struct device *dev, struct device_driver *drv)
{
return