1、驱动架构
1.1 bus 总线
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
...
};
int __init platform_bus_init(void)
{
...
//注册platform_bus,其类型我bus_type
error = bus_register(&platform_bus_type);
...
}
1.2 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 */
}
int of_device_add(struct platform_device *ofdev)
{
//注册platform_device 其实就是将platform_device 的device 注册
return device_add(&ofdev->dev);
}
static struct platform_device *of_platform_device_create_pdata(
struct device_node *np,
const char *bus_id,
void *platform_data,
struct device *parent)
{
struct platform_device *dev;
//定义platform_device 的总线类型,表示其挂在platform_bus下面
dev->dev.bus = &platform_bus_type;
//注册platform_device
if (of_device_add(dev) != 0) {
platform_device_put(dev);
goto err_clear_flag;
}
...
}
1.3 driver 驱动
static struct platform_driver onboard_hub_driver = {
.probe = onboard_hub_probe,
.remove = onboard_hub_remove,
.driver = {
.name = "onboard-usb-hub",
.of_match_table = onboard_hub_match,
.pm = pm_ptr(&onboard_hub_pm_ops),
.dev_groups = onboard_hub_groups,
},
};
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
drv->driver.owner = owner;
//定义platform_driver的总线类型,表示其挂在platform_bus下面
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
//注册 platform_driver 其实就是注册 platform_driver的 device_driver
return driver_register(&drv->driver);
}
static int __init onboard_hub_init(void)
{
...
//注册platform_driver
ret = platform_driver_register(&onboard_hub_driver);
...
}
2、platform驱动模型
platform总线是kernel 2.6中引入的一种虚拟总线,主要用来管理CPU的片上资源,具有良好的移植性。相对于USB、PCI、I2C等物理总线来说,平台总线是Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。
usb、i2c、pci等设备都是直接挂在相应的总线下与cpu进行数据交互的,但在嵌入式系统中,并非所有的设备都能够归属于这些常见的总线类型。如SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等,都不依附于此类总线。
通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独立的资源(地址总线和IRQs),都可以用platform driver来管理,而timer,irq等小系统之内的设备则最好不用platfrom driver机制。
2.1 platform_device
struct platform_device {
const char *name; //定义平台设备的名称,和相应驱动命名一致
struct device dev;
struct resource *resource; //定义平台设备的资源
};
在这个结构里封装了struct device及struct resource。可知:platform_device由device派生而来,是一种特殊的device。
注册设备:
int platform_device_register(struct platform_device *pdev);
2.2 platform_driver
struct platform_driver {
int (*probe)(struct platform_device *);
const char *name;
const struct platform_device_id *id_table;
int (*remove)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
可见,platform_driver包含了设备操作的几个功能函数,同时包含了一个device_driver结构,说明device_driver是platform_driver的基类。 注册驱动:
int platform_driver_register(struct platform_driver *drv);
2.3 platform_bus
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.dma_configure = platform_dma_configure,
.pm = &platform_dev_pm_ops,
};
Platform driver通过platform bus获取platform_device。
2.4 匹配方法
(1) 优先匹配driver中的id_table,里面包含了支持不同平台的名字;
(2) 直接匹配driver中的名字和device中的名字。
2.5 platform_driver成员函数
platform_driver与platform_device匹配后进入driver的probe函数,当进入probe函数后,需要实现一系列函数接口,包括:
(1)注册驱动:
int platform_driver_register(struct platform_driver *drv);
将驱动添加到platform总线链表(即注册),等待设备的出现,并由总线来完成设备与驱动之间的匹配关联过程。
(2)获取设备的资源信息:
platform_get_resource(struct platform_device * dev, unsigned int type, unsigned int num);
(3)获取资源中的中断号:
struct int platform_get_irq(struct platform_device *dev, unsigned int num);
(4)driver通过file_operations成员函数实现与用户空间的交流。
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};