总线、设备、驱动
概述
设备:代表一个物理硬件or虚拟出来的硬件,他可以是连接到SoC上的具体硬件,如鼠标、键盘等;也可以是一个抽象出来的硬件,例如CLK PIN_CTL这些,SoC内部提供的功能,板级找不到对应的物理实体,通常会被归纳到platform bus下
驱动: 用来控制和管理特定硬件一系列方法,例如初始化probe
、移除remove
、低功耗suspend & resume
总线: 物理层面上,总线是CPU和各个组件数据交换的通道,例如APB总线,AXI总线;或者SoC和外设之间数据交换的通道,IIC总线、SPI总线。Linux内部,总线被抽象成一个软件实体。负责管理挂在总线上的驱动和设备。
类(Class):一系列具有相同功能的设备
平台设备驱动
设备注册
设备注册的两种方式
传统的注册方式
- 调用platform的注册接口
#ifndef DEVICE_REGISTERED_IN_FDT
static void plat_dev_release(struct device *dev)
{
if (!dev)
return;
pr_info("%s[%d]: %s......\n", __func__, __LINE__, dev_name(dev));
}
/*设备注册的第一种方式,但是当前已经不在推荐了*/
struct platform_device plat_dev = {
.name = "tdev",
.id = -1,
.dev = {
.release = plat_dev_release,
},
};
#endif
ret = platform_device_register(&plat_dev);
platform_device_unregister(&plat_dev);
早在Kernel2.6时期,Linus有一次著名的发飙,“this whole ARM thing is a f*cking pain in the ass”。哈哈哈,如果每个设备都要使用这种方式,那么每个平台都会有很多重复的代码,所以2.6之后,社区比较推崇设备树的注册方式。
用设备树描述device
- 在设备树中新建一个节点,我这里写的比较简单,只为match准备了一个compatible字段
wildgoose_test_dev@0 {
compatible = "wildgoose,tdev";
};
设备注册的过程
platform_device
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
const char *driver_override;
struct mfd_cell *mfd_cell;
struct pdev_archdata archdata;
};
platform_device_register
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev); //初始化struct device结构体
setup_pdev_dma_masks(pdev); //set default dma masks
return platform_device_add(pdev);
}
platform_device
int platform_device_add(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
u32 i;
int ret;
if (!dev->parent)
dev->parent = &platform_bus; //未指定父设备,父设备就是platform_bus
dev->bus = &platform_bus_type; //设备所属总线:platform_bus_type
switch (pdev->id) { //set device name, pdev->dev.kobj.name
default: //默认的命名方式
dev_set_name(dev, "%s.%d", pdev->name, pdev->id);
break;
case PLATFORM_DEVID_NONE: //pdev->id == -1
dev_set_name(dev, "%s", pdev->name);
break;
case PLATFORM_DEVID_AUTO: //pdev->id = -2,自动申请一个设备id
ret = ida_alloc(&platform_devid_ida, GFP_KERNEL);
if (ret < 0)
return ret;
pdev->id = ret;
pdev->id_auto = true;
dev_set_name(dev, "%s.%d.auto", pdev->name, pdev->id);
break;
}
for (i = 0; i < pdev->num_resources; i++) { //resource的设置
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = dev_name(dev);
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
if (p) {
ret = insert_resource(p, r);
if (ret) {
dev_err(dev, "failed to claim resource %d: %pR\n", i, r);
goto failed;
}
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n", dev_name(dev),
dev_name(dev->parent));
ret = device_add(dev); //添加设备
if (ret)
goto failed;
return 0;
failed:
......
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
device_add
int device_add(struct device *dev)
{
struct subsys_private *sp;
struct device *parent;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
struct kobject *glue_dir = NULL;
dev = get_device(dev);
if (!dev->p) { //设备私有数据
error = device_private_init(dev);
}
if (dev->init_name) { //如果dev有init_name
error = dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
if (dev_name(dev)) //device的命名
error = 0;
/* subsystems can specify simple device enumeration */
else if (dev->bus && dev->bus->dev_name)
error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
else
error = -EINVAL;
if (error)
goto name_error;
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent); //父设备
kobj = get_device_parent(dev, parent); //父设备的kobj
if (IS_ERR(kobj)) {
error = PTR_ERR(kobj);
goto parent_error;
}
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
set_dev_node(dev, dev_to_node(parent));
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //设备的尽头,kobj
if (error) {
glue_dir = kobj;
goto Error;
}
error = device_create_file(dev, &dev_attr_uevent);
if (error)
goto attrError;
error = device_add_class_symlinks(dev); //class下的链接
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
error = bus_add_device(dev); //总线上添加设备
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
if (error)
goto DevAttrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto SysEntryError;
devtmpfs_create_node(dev); //dev路径下设备节点的创建
}
bus_notify(dev, BUS_NOTIFY_ADD_DEVICE);
kobject_uevent(&dev->kobj, KOBJ_ADD);
if (dev->fwnode && !dev->fwnode->dev) {
dev->fwnode->dev = dev;
fw_devlink_link_device(dev);
}
bus_probe_device(dev); //寻找匹配的driver,match的过程
if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match)
fw_devlink_unblock_consumers(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
sp = class_to_subsys(dev->class);
if (sp) {
mutex_lock(&sp->mutex);
/* tie the class to the device */
klist_add_tail(&dev->p->knode_class, &sp->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf, &sp->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev);
mutex_unlock(&sp->mutex);
subsys_put(sp);
}
done:
put_device(dev);
return error;
......
}
EXPORT_SYMBOL_GPL(device_add);
驱动注册
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__platform_driver_register);
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
if (!bus_is_registered(drv->bus)) { //驱动注册之前,要确保总线已经被注册
.......
return -EINVAL;
}
......
ret = bus_add_driver(drv); //总线上添加driver,同时为driver匹配device
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
deferred_probe_extend_timeout();
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
int bus_add_driver(struct device_driver *drv)
{
struct subsys_private *sp = bus_to_subsys(drv->bus);
struct driver_private *priv;
int error = 0;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = sp->drivers_kset;
error = (&priv->kobj, &driver_ktype, NULL, "%s", drv->name);
klist_add_tail(&priv->knode_bus, &sp->klist_drivers);
if (sp->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_del_list;
}
error = module_add_driver(drv->owner, drv);
if (error) {
printk(KERN_ERR "%s: failed to create module links for %s\n",
__func__, drv->name);
goto out_detach;
}
error = driver_create_file(drv, &driver_attr_uevent);
error = driver_add_groups(drv, sp->bus->drv_groups);
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
}
return 0;
//error handler
......
}
设备和驱动匹配的过程
- 每当有设备注册进来,或者驱动注册进来,都会有device和driver match的过程
//驱动注册
__platform_driver_register
--->driver_register
--->bus_add_driver
--->driver_attach
-->bus_for_each_dev(drv->bus, NULL, (void *)drv, __driver_attach);
-->driver_match_device
-->drv->bus->match ? drv->bus->match(dev, drv) : 1;
//设备注册
platform_device_register
--->platform_device_add
--->device_add
--->bus_probe_device
--->device_initial_probe
--->__device_attach
--->ret = bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);
--->driver_match_device
--->drv->bus->match ? drv->bus->match(dev, drv) : 1;
殊途同归,无论是设备先挂载到总线上,还是驱动先注册到总线,都是调用platform_bus_type.match
接口
static int platform_match(struct device *dev, const struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override) //优先使用driver_override匹配应用
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv)) //driver_match_table匹配设备
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv)) //ACPI的设备匹配方式
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
driver_override
: 常用于PCI设备,直接为device指定驱动of_driver_match_device
: 通过设备树节点和driver->of_match_table
匹配acpi_driver_match_device
:没用过platform_match_id
: 通过platform_device_id.id_table
与device->name
匹配devcie->name
和driver->name
进行匹配
Test Code
#include "linux/stddef.h"
#include "linux/types.h"
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#define DEVICE_REGISTERED_IN_FDT
/************************************************************************************/
static const struct of_device_id wildgoose_tdev_of_match[] = {
{ .compatible = "wildgoose,tdev" },
{ }
};
static int plat_drv_probe(struct platform_device *dev)
{
pr_info("%s[%d]:......\n", __func__, __LINE__);
return 0;
}
static void plat_drv_remove(struct platform_device *dev)
{
pr_info("%s[%d]:......\n", __func__, __LINE__);
}
static struct platform_driver plat_dev_drv_test = {
.driver = {
.name = "tdev",
.of_match_table = wildgoose_tdev_of_match,
},
.probe = plat_drv_probe,
.remove = plat_drv_remove,
.shutdown = NULL,
.suspend = NULL,
.resume = NULL,
};
#ifndef DEVICE_REGISTERED_IN_FDT
static void plat_dev_release(struct device *dev)
{
if (!dev)
return;
pr_info("%s[%d]: %s......\n", __func__, __LINE__, dev_name(dev));
}
/*设备注册的第一种方式,但是当前已经不在推荐了*/
struct platform_device plat_dev = {
.name = "tdev",
.id = -1,
.dev = {
.release = plat_dev_release,
},
};
#endif
/*设备注册的第二种方式,通过设备树注册设备*/
static int __init plat_dev_drv_test_init(void)
{
#ifndef DEVICE_REGISTERED_IN_FDT
int ret;
ret = platform_device_register(&plat_dev);
if (ret)
return ret;
#endif
return platform_driver_register(&plat_dev_drv_test);
}
static void __exit plat_dev_drv_test_exit(void)
{
#ifndef DEVICE_REGISTERED_IN_FDT
platform_device_unregister(&plat_dev);
#endif
return platform_driver_unregister(&plat_dev_drv_test);
}
module_init(plat_dev_drv_test_init);
module_exit(plat_dev_drv_test_exit);
MODULE_DESCRIPTION("Platform Device Driver Test");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("WildGoose");
测试结果
~ # ls /sys/devices/platform/ | grep "wild"
wildgoose_test_dev@0
~ # ls /sys/devices/platform/wildgoose_test_dev@0/ -al
total 0
drwxr-xr-x 3 0 0 0 Jun 8 11:07 .
drwxr-xr-x 45 0 0 0 Jun 8 11:07 ..
lrwxrwxrwx 1 0 0 0 Jun 8 11:07 driver -> ../../../bus/platform/drivers/tdev
-rw-r--r-- 1 0 0 4096 Jun 8 11:07 driver_override
-r--r--r-- 1 0 0 4096 Jun 8 11:07 modalias
lrwxrwxrwx 1 0 0 0 Jun 8 11:07 of_node -> ../../../firmware/devicetree/base/wildgoose_test_dev@0
drwxr-xr-x 2 0 0 0 Jun 8 11:07 power
lrwxrwxrwx 1 0 0 0 Jun 8 11:07 subsystem -> ../../../bus/platform
-rw-r--r-- 1 0 0 4096 Jun 8 11:07 uevent