platform bus matching principle

Linux Platform probe matching
There is a lot of information on the platform driver model online. Here we mainly analyze the probe process from driver registration.

driver registration platform_driver_register(xxx)
called platform_driver_register (include/linux/platform_device.h)
#define platform_driver_register(drv)
__platform_driver_register(drv, THIS_MODULE)
__platform_driver_register function (drivers/base/platform.c), filled in the drv->driver structure

int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{<!-- -->
drv->driver.owner = owner;
drv->driver.bus = & amp;platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
return driver_register( & amp;drv->driver);
}
EXPORT_SYMBOL_GPL(__platform_driver_register);

driver_register(drivers/base/driver.c), key function bus_add_driver

int driver_register(struct device_driver *drv)
{<!-- -->
int ret;
struct device_driver *other;
    BUG_ON(!drv->bus->p);

Detect whether the operation function of the bus and the operation function of the driver exist at the same time. If they exist at the same time, it will prompt to use the bus.
//The operation function provided by the line
if ((drv->bus->probe & amp; & amp; drv->probe) ||
(drv->bus->remove & amp; & amp; drv->remove) ||
(drv->bus->shutdown & amp; & amp; drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\\
", drv->name);
Check whether this driver has been registered on the bus and increase the reference count. If it has been registered, return a prompt message.
other = driver_find(drv->name, drv->bus);
if (other) {<!-- -->
    //If it has been registered, return an error message.
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\\
", drv->name);
return -EBUSY;
}
//If it has not been registered yet, register the driver on the bus
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {<!-- -->
bus_remove_driver(drv);
return ret;
}
kobject_uevent( & amp;drv->p->kobj, KOBJ_ADD);

return ret;
}
EXPORT_SYMBOL_GPL(driver_register);

bus_add_driver(drivers/base/bus.c), key function driver_attach. kset as a device driver model will be analyzed in detail in subsequent articles.

int bus_add_driver(struct device_driver *drv)
{<!-- -->
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
//Used to increase the reference count of the kobject of the top-level bus to which the bus belongs, and return the pointer of the top-level bus to which it belongs.
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;

pr_debug("bus: '%s': add driver %s\\
", bus->name, drv->name);

priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {<!-- -->
error = -ENOMEM;
goto out_put_bus;
}
klist_init( & amp;priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add( & amp;priv->kobj, & amp;driver_ktype, NULL,
"%s", drv->name);
if(error)
goto out_unregister;
//Mount to the corresponding bus driver list
klist_add_tail( & amp;priv->knode_bus, & amp;bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {<!-- -->
if (driver_allows_async_probing(drv)) {<!-- -->
pr_debug("bus: '%s': probing driver %s asynchronously\\
",
drv->bus->name, drv->name);
async_schedule(driver_attach_async, drv);
} else {<!-- -->
error = driver_attach(drv);
if(error)
goto out_unregister;
}
}
module_add_driver(drv->owner, drv);
//Create uevent attribute file
error = driver_create_file(drv, & amp;driver_attr_uevent);
if (error) {<!-- -->
printk(KERN_ERR "%s: uevent attr (%s) failed\\
",
__func__, drv->name);
}
error = driver_add_groups(drv, bus->drv_groups);
if (error) {<!-- -->
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_create_groups(%s) failed\\
",
__func__, drv->name);
}

if (!drv->suppress_bind_attrs) {<!-- -->
error = add_bind_files(drv);
if (error) {<!-- -->
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\\
",
__func__, drv->name);
}
}

return 0;

out_unregister:
kobject_put( & amp;priv->kobj);
/* drv->p is freed in driver_release() */
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}

(drivers/base/dd.c)

int driver_attach(struct device_driver *drv)
{<!-- -->
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);

bus_for_each_dev (drivers/base/bus.c), device iterator, traverses the devices mounted on the bus and performs matching. The key function is fn, which is the passed in __driver_attach function pointer. The while loop detects the situation when the return value is not 0.
i

nt bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{<!-- -->
struct klist_iter i;
struct device *dev;
int error = 0;

if (!bus || !bus->p)
return -EINVAL;

klist_iter_init_node( & amp;bus->p->klist_devices, & amp;i,
(start ? & amp;start->p->knode_bus : NULL));
while ((dev = next_device( & amp;i)) & amp; & amp; !error)
    //The parameters of fn are pointers of device and device_driver respectively.
error = fn(dev, data);
klist_iter_exit( & amp;i);
return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

__driver_attach (drivers/base/dd.c), focus on driver_match_device (the matching between device and device_driver is carried out here), driver_probe_device (when the probe function in platform_driver is called is controlled here)
s

tatic int __driver_attach(struct device *dev, void *data)
{<!-- -->
struct device_driver *drv = data;
int ret;

/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/

ret = driver_match_device(drv, dev);
if (ret == 0) {<!-- -->
/* no match */
return 0;
} else if (ret == -EPROBE_DEFER) {<!-- -->
dev_dbg(dev, "Device match requests probe deferral\\
");
driver_deferred_probe_add(dev);
} else if (ret < 0) {<!-- -->
dev_dbg(dev, "Bus failed to match device: %d", ret);
return ret;
} /* ret > 0 means positive match */

if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);

return 0;
}

driver_match_device (drivers/base/base.h), first determine whether drv->bus->match is empty, then where is this function pointer initialized? In fact, as mentioned at the beginning of the article, when calling __platform_driver_register, drv is initialized. In fact, what is called is the match in the platform_bus_type structure, that is, the platform_match function.

static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{<!-- -->
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

platform_match, there are three matching methods, only of_driver_match_device (device tree matching) is analyzed here

static int platform_match(struct device *dev, struct device_driver *drv)
{<!-- -->

/* to_platform_device calls the familiar container_of to find the first address */
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);
}
of_driver_match_device (include/linux/of_device.h), the parameter drv->of_match_table gets the of_match_table variable of platform_driver, which stores the compatible information in dts
static inline int of_driver_match_device(struct device *dev,
const struct device_driver *drv)
{<!-- -->
return of_match_device(drv->of_match_table, dev) != NULL;
}
of_match_device
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct device *dev)
{<!-- -->
if ((!matches) || (!dev->of_node))
return NULL;
return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
of_match_node
const struct of_device_id *of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{<!-- -->
const struct of_device_id *match;
unsigned long flags;

raw_spin_lock_irqsave( & amp;devtree_lock, flags);
match = __of_match_node(matches, node);
raw_spin_unlock_irqrestore( & amp;devtree_lock, flags);
return match;
}
EXPORT_SYMBOL(of_match_node);

__of_match_node matches nodes, __of_match_node matches by name, type, compatible

static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{<!-- -->
const struct of_device_id *best_match = NULL;
int score, best_score = 0;

if (!matches)
return NULL;

for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches + + ) {<!-- -->
score = __of_device_is_compatible(node, matches->compatible,
matches->type, matches->name);
if (score > best_score) {<!-- -->
best_match = matches;
best_score = score;
}
}

return best_match;
}

Let’s take a look at driver_probe_device

int driver_probe_device(struct device_driver *drv, struct device *dev)
{<!-- -->
int ret = 0;

if (!device_is_registered(dev))
return -ENODEV;

pr_debug("bus: '%s': %s: matched device %s with driver %s\\
",
drv->bus->name, __func__, dev_name(dev), drv->name);
    // powermanager runtime mechanism, the path is in drivers/base/power/runtime.c, we will not analyze it in depth here, it is mainly used for power management.
pm_runtime_get_suppliers(dev);
if (dev->parent)
pm_runtime_get_sync(dev->parent);

pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_request_idle(dev);

if (dev->parent)
pm_runtime_put(dev->parent);

pm_runtime_put_suppliers(dev);
return ret;
}

really_probe function

static int really_probe(struct device *dev, struct device_driver *drv)
{<!-- -->
...
//Establish a connection in the sys directory pointing to your own drivers in sys
if (driver_sysfs_add(dev)) {<!-- -->
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\\
",
__func__, dev_name(dev));
goto probe_failed;
}

if (dev->pm_domain & amp; & amp; dev->pm_domain->activate) {<!-- -->
ret = dev->pm_domain->activate(dev);
if (ret)
goto probe_failed;
}
//When initializing dev, the bus does not specify the probe function, so ret = drv->probe(dev); is called directly.
if (dev->bus->probe) {<!-- -->
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {<!-- -->
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
...
}

The above probe will directly call platform_drv_probe, and finally call the driver’s probe function through ret = drv->probe(dev);. It can also be seen here that the probe function returns a value of 0 under normal circumstances.

static int platform_drv_probe(struct device *_dev)
{<!-- -->
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
int ret;

ret = of_clk_set_defaults(_dev->of_node, false);
if (ret < 0)
return ret;
ret = dev_pm_domain_attach(_dev, true);
if (ret != -EPROBE_DEFER) {<!-- -->
if (drv->probe) {<!-- -->
ret = drv->probe(dev);
if (ret)
dev_pm_domain_detach(_dev, true);
} else {<!-- -->
/* don't fail if just dev_pm_domain_attach failed */
ret = 0;
}
}

if (drv->prevent_deferred_probe & amp; & amp; ret == -EPROBE_DEFER) {<!-- -->
dev_warn(_dev, "probe deferral not supported\\
");
ret = -ENXIO;
}

return ret;
}

Summarize
After following a bunch of code, how does the platform match and execute the probe function? In fact, it is very simple. We know that when the hardware first comes up, the bootloader lets the kernel select the appropriate dts by passing parameters. When the kernel receives the correct dts, it will register each appropriate node (platform_device_register) and hang it on the bus. When the underlying driver registers (platform_driver_register), it will first traverse the device on the bus (bus_for_each_dev), and then match it through compatible in dts (executed in platform_match, dts matching is only one of the matching methods), when the driver and device names are found When the same, OK, we can execute the probe we need (driver_probe_device).