注册总线
bus_register:bus_type_private的初始化,在这条总线目录下创建/bus/busname,/device, /driver 目录,初始化这条总线上的设备链表:struct klist klist_devices;初始化这条总线上的驱动链表:struct klist klist_drivers
注册设备
不管是平台总线还是其他总线,最终通过device_register来注册设备
device_register
device_add
bus_add_device/bus_probe_device
device_initial_probe
__device_attach->bus_for_each_drv
__device_attach_driver
driver_match_device
drv->bus->match
driver_probe_device
really_probe
dev->bus->probe | drv->probe
devcie_add
1. bus_add_device将设备添加到总线上,当然总线类型,在生成设备的时候会去初始化
2. device_create调用device_add会传递设备号进去,device_register不会传递设备号,只是单纯注册设备;有设备号就会去创建设备节点文件;紧接着也会发送KOBJ_ADD通知用户层创建设备(内核创建了肯定就不会再创建了),但是这个add的行为还是要上报给用户层做其他的状态同步操作
3. 最后通过bus_probe_device探测驱动
int device_add(struct device *dev)
{
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)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
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));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error) {
glue_dir = get_glue_dir(dev);
goto Error;
}
/* notify platform of device entry */
error = device_platform_notify(dev, KOBJ_ADD);
if (error)
goto platform_error;
error = device_create_file(dev, &dev_attr_uevent);
if (error)
goto attrError;
error = device_add_class_symlinks(dev);
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);
}
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->p->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
done:
put_device(dev);
return error;
SysEntryError:
if (MAJOR(dev->devt))
device_remove_file(dev, &dev_attr_dev);
DevAttrError:
device_pm_remove(dev);
dpm_sysfs_remove(dev);
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
device_remove_file(dev, &dev_attr_uevent);
attrError:
device_platform_notify(dev, KOBJ_REMOVE);
platform_error:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
glue_dir = get_glue_dir(dev);
kobject_del(&dev->kobj);
Error:
cleanup_glue_dir(dev, glue_dir);
parent_error:
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
EXPORT_SYMBOL_GPL(device_add);
/**
* device_register - register a device with the system.
* @dev: pointer to the device structure
*
* This happens in two clean steps - initialize the device
* and add it to the system. The two steps can be called
* separately, but this is the easiest and most common.
* I.e. you should only call the two helpers separately if
* have a clearly defined need to use and refcount the device
* before it is added to the hierarchy.
*
* For more information, see the kerneldoc for device_initialize()
* and device_add().
*
* NOTE: _Never_ directly free @dev after calling this function, even
* if it returned an error! Always use put_device() to give up the
* reference initialized in this function instead.
*/
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
EXPORT_SYMBOL_GPL(device_register);
注册驱动
不管是平台总线还是其他总线,最终通过driver_register来注册驱动。流程基本跟注册设备一致
driver_register
bus_add_driver
driver_attach
bus_for_each_dev
__driver_attach
driver_match_device
driver_probe_device
platform
总线
kernel_init->do_basic_setup->driver_init(先于do_initcalls执行)->platform_bus_init(device_register(&platform_bus);bus_register(&platform_bus_type))
设备
arch_initcall(customize_machine)会把根的子节点都转换成platform_device节点;下图的两种方式都是从根节点遍历子节点,分配struct platform_device的内存,还分配了该platform device需要的resource的内存,加入统一设备模型系统中
static int __init customize_machine(void)
{
/*
* customizes platform devices, or adds new ones
* On DT based machines, we fall back to populating the
* machine from the device tree, if no callback is provided,
* otherwise we would always need an init_machine callback.
*/
of_iommu_init();
if (machine_desc->init_machine)
machine_desc->init_machine();
#ifdef CONFIG_OF
else
of_platform_populate(NULL, of_default_bus_match_table,
NULL, NULL);
#endif
return 0;
}
of_default_bus_match_table匹配表如下,根节点下的这些子节点的子节点也会生成platform_device
const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
{ .compatible = "simple-mfd", },
{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};
of_platform_populate
初始化设备树的时候,把所有设备树节点都抽象成了device_node实例;而of_platform_bus_create里会找到根节点的所有子节点来填充一个个platform_device实例,可以看到of_platform_populate里的就是找到/节点的device_node开始填充的
/**
* of_platform_populate() - Populate platform_devices from device tree data
* @root: parent of the first level to probe or NULL for the root of the tree
* @matches: match table, NULL to use the default
* @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent to hook devices from, NULL for toplevel
*
* Similar to of_platform_bus_probe(), this function walks the device tree
* and creates devices from nodes. It differs in that it follows the modern
* convention of requiring all device nodes to have a 'compatible' property,
* and it is suitable for creating devices which are children of the root
* node (of_platform_bus_probe will only create children of the root which
* are selected by the @matches argument).
*
* New board support should be using this function instead of
* of_platform_bus_probe().
*
* Returns 0 on success, < 0 on failure.
*/
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
struct device_node *child;
int rc = 0;
root = root ? of_node_get(root) : of_find_node_by_path("/");
if (!root)
return -EINVAL;
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc) {
of_node_put(child);
break;
}
}
of_node_set_flag(root, OF_POPULATED_BUS);
of_node_put(root);
return rc;
}
EXPORT_SYMBOL_GPL(of_platform_populate);
of_platform_bus_create
of_platform_bus_create函数:创建根节点下的某个子节点以及该子节点下的所有子节点对应的平台设备;
(1).而且是带有平台数据platform_data的;下面的of_platform_device_create_pdata会通过dev->dev.platform_data = platform_data;将数据填充到struct platform_device ->struct device -> platform_data成员中;这个操作也可以设置platform
(2).OF_DEV_AUXDATA的_name成员会作为bus_id 传递到of_device_alloc并通过 dev_set_name(&dev->dev, "%s", bus_id);设置device的name
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
const struct of_dev_auxdata *auxdata;
struct device_node *child;
struct platform_device *dev;
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;
/* Make sure it has a compatible property */
if (strict && (!of_get_property(bus, "compatible", NULL))) {
return 0;
}
//确保该设备节点之前没有被填充过
if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
return 0;
}
//查看lookup数组里有没有数组元素与该设备节点的compatile和内存空间首地址匹配,
//如果有匹配,返回该数组元素
auxdata = of_dev_lookup(lookup, bus);
if (auxdata) {
bus_id = auxdata->name;
//如果设备节点是在lookup数组里,是带有平台数据的,
//否则没有平台数据
platform_data = auxdata->platform_data;
}
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
//递归创建bus节点的子节点对应的平台设备
for_each_child_of_node(bus, child) {
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
}
}
//设置标志,表示该节点填充完
of_node_set_flag(bus, OF_POPULATED_BUS);
return rc;
}
lookup示例
static struct mv_usb_platform_data asr1802s_usb_pdata = {
.mode = MV_USB_MODE_OTG,
.otg_force_a_bus_req = 1,
.disable_otg_clock_gating = 0,
};
static const struct of_dev_auxdata asr1802s_auxdata_lookup[] __initconst = {
....
OF_DEV_AUXDATA("asr,mv-otg", 0xd4208100, "mv-otg",
&asr1802s_usb_pdata),
....
}
struct of_dev_auxdata {
char *compatible;
resource_size_t phys_addr;
char *name;
void *platform_data;
};
/* Macro to simplify populating a lookup table */
#define OF_DEV_AUXDATA(_compat,_phys,_name,_pdata) \
{ .compatible = _compat, .phys_addr = _phys, .name = _name, \
.platform_data = _pdata }
///
static void __init asr1802s_dt_init_machine(void)
{
asr1802s_clk_init();
pxa_init_dma(IRQ_PXA1822_DMA_INT0, 32);
mmp_entry_vector_init();
of_platform_populate(NULL, of_default_bus_match_table,
asr1802s_auxdata_lookup, NULL);
pxa_init_pmua_regdump();
pxa_init_pmum_regdump();
pxa_init_dvc_regdump();
pxa_init_apbs_regdump();
}
DT_MACHINE_START(ASR1802S_DT, "ASR ASR1802S (Device Tree Support)")
.map_io = mmp_map_io,
.init_irq = irqchip_init,
.init_time = asr1802s_timer_init,
.reserve = asr1802s_reserve,
.init_machine = asr1802s_dt_init_machine,
.dt_compat = asr1802s_dt_board_compat,
.restart = mmp_arch_restart,
MACHINE_END
of_platform_device_create_pdata
of_platform_device_create_pdata就是真正创建platform_device的地方;设备树中的reg和interrupts资源将会被转换成platform_device内的struct resources资源;其他属性:platform_device->struct device dev->struct device_node *of_node直接指向设备树转换来的device_node
1. of_device_alloc返回一个platform_device
2. of_dev_add 添加一个platform_device
/*
* 创建一个 struct platform_device 节点
*/
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;
/* 判断 status 状态 */
if (!of_device_is_available(np) ||
of_node_test_and_set_flag(np, OF_POPULATED))
return NULL;
/* 分配并初始化*/
dev = of_device_alloc(np, bus_id, parent);
if (!dev)
goto err_clear_flag;
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data; /* 这个 platform_data 应该为NULL*/
/*
* 扫描node的 dma-ranges 属性,并初始化 dev->coherent_dma_mask,dma_mask
* 如果节点有 iommus 属性
* dev->archdata.mapping = sunxi_mapping (arm_setup_iommu_dma_ops 和具体架构有关)
* dev->archdata.dma_ops = coherent ? &iommu_coherent_ops : &iommu_ops
* 否则:
* dev->archdata.dma_ops = coherent ? &arm_coherent_dma_ops : &arm_dma_ops;
*/
of_dma_configure(&dev->dev, dev->dev.of_node);
/* 没配置 CONFIG_GENERIC_MSI_IRQ_DOMAIN,此函数为空 */
of_msi_configure(&dev->dev, dev->dev.of_node);
if (of_device_add(dev) != 0) {
of_dma_deconfigure(&dev->dev);
platform_device_put(dev);
goto err_clear_flag;
}
return dev;
err_clear_flag:
of_node_clear_flag(np, OF_POPULATED);
return NULL;
}
struct platform_device *of_device_alloc(struct device_node *np,
const char *bus_id,
struct device *parent)
{
struct platform_device *dev;
int rc, i, num_reg = 0, num_irq;
struct resource *res, temp_res;
/*
* 分配并初始化一个 platform_device
* 会初始化以下成员:
* pdev.name
* pdev.id
* pdev.dev(device_initialize(&pdev.dev))
* pdev.dev.release = platform_device_release;
* 调用 arch_setup_pdev_archdata(&pdev); 该函数在ARM上为空
*/
dev = platform_device_alloc("", -1);
if (!dev)
return NULL;
/* 统计io资源:主要是检索 'reg'和 'reg-names' 属性 */
while (of_address_to_resource(np, num_reg, &temp_res) == 0)
num_reg++;
/* 统计中断资源: 主要是检索 ‘interrupts’ 属性 */
num_irq = of_irq_count(np);
/*
* 填充上述资源到 pdev->resource
* reg属性指定的地址 res的 flags = IORESOURCE_MEM
* interrupts属性指定的地址 res 的flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));
*/
if (num_irq || num_reg) {
res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
if (!res) {
platform_device_put(dev);
return NULL;
}
dev->num_resources = num_reg + num_irq;
dev->resource = res;
for (i = 0; i < num_reg; i++, res++) {
rc = of_address_to_resource(np, i, res);
WARN_ON(rc);
}
if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
pr_debug("not all legacy IRQ resources mapped for %s\n",
np->name);
}
/*
* 初始化 dev 的一些属性
*/
dev->dev.of_node = of_node_get(np);
dev->dev.fwnode = &np->fwnode;
dev->dev.parent = parent ? : &platform_bus;
if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id);
else
of_device_make_bus_id(&dev->dev);
return dev;
}
void of_device_make_bus_id(struct device *dev)
{
struct device_node *node = dev->of_node;
const __be32 *reg;
u64 addr;
const char __maybe_unused *name;
/* Construct the name, using parent nodes if necessary to ensure uniqueness */
while (node->parent) {
/*
* If the address can be translated, then that is as much
* uniqueness as we need. Make it the first component and return
*/
reg = of_get_property(node, "reg", NULL);
if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
/*
* 一般情况都是走这里
* 所以可看看出 Name = 'reg_addr.node_name.dev_name'
* 此时 dev_name = ""
*/
dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s",
(unsigned long long)addr, node->name,
dev_name(dev));
return;
}
/* format arguments only used if dev_name() resolves to NULL */
dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
strrchr(node->full_name, '/') + 1, dev_name(dev));
node = node->parent;
}
}
驱动与设备匹配
匹配流程如下
platform_driver_register(struct platform_driver *drv)
driver_register(struct device_driver * drv)
driver_match_device
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
匹配过程
platform总线的匹配函数如下:
static int platform_match(struct device *dev, 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)
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
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);
}
(1) When driver_override is set, only bind to the matching drive
如果设备有driver_override成员,强行只匹配跟device->driver_override的名称相同的驱动
(2) 用设备树中的 compatible 属性和platform_driver中的driver中的 of_match_table 来匹配;of_match_table ;一般用于系统启动时,就开启的设备
pmx: pinmux@d401e000 {
compatible = "pinconf-single";
reg = <0xd401e000 0x330>;
#address-cells = <1>;
#size-cells = <1>;
#gpio-range-cells = <3>;
ranges;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <7>;
range: gpio-range {
#pinctrl-single,gpio-range-cells = <3>;
};
};
。。。。
static struct of_device_id pcs_of_match[] = {
{ .compatible = "pinctrl-single", .data = (void *)false },
{ .compatible = "pinconf-single", .data = (void *)true },
{ },
};
MODULE_DEVICE_TABLE(of, pcs_of_match);
static struct platform_driver pcs_driver = {
.probe = pcs_probe,
.remove = pcs_remove,
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.of_match_table = pcs_of_match,
},
};
(3) 用 platform_driver 中的 id_table 中的和 platform_device 中的 name来匹配;; id_table一般也用于模块化的驱动,比如pci,usb等设备
struct platform_device pdev = {
.name = "hello1",
.id = PLATFORM_DEVID_AUTO, //自动分配
.dev = {
.release = pdev_release,
},
.resource = res,
.num_resources = ARRAY_SIZE(res),
};
。。。。。
struct platform_device_id idtable[] = {
{"hello1",0},
{"hello2",1},
{"hello3",2},
{/*end*/}
};
struct platform_driver pdrv = {
.probe = pdrv_probe,
.remove = pdrv_remove,
.driver = {
.name = "aabbccdd",
},
.id_table = idtable,
};
(4) 用platform_device中的name和 platform_driver 中的 driver 中的 name来匹配;一般用于非设备树创建的platform_device
struct platform_device pdev = {
.name = "aabbccdd",
.id = PLATFORM_DEVID_AUTO, //自动分配
.dev = {
.release = pdev_release,
},
.resource = res,
.num_resources = ARRAY_SIZE(res),
};
。。。。
struct platform_driver pdrv = {
.probe = pdrv_probe,
.remove = pdrv_remove,
.driver = {
.name = "aabbccdd",
},
};