For linux platform device and driver, a driver can correspond to multiple devices, match names, and call the probe function implemented inside the driver.
Taking an I2C device as an example, this paper starts with the driver i2c_add_driver() to look at the source code and analyze how to call the probe() function step by step.
The code for analysis is based on linux kernel msm-4.4.
/****************************************/
Starting with module_init(),
Definition location: kernel/msm-4.4/include/linux/module.h
Description of the function in the source code:
/** * module_init() - driver initialization entry point * @x: function to be run at kernel boot time or module insertion * * module_init() will either be called during do_initcalls() (if * builtin) or at module insertion time (if a module). There can only * be one per module. */
As an entry function for each driver, if the code is compiled into the kernel
Called by do_initcalls() when the startup kernel is started (or when the module is dynamically loaded).
How to step by step from the kernel to do_initcalls() and then call module_init(), the call chain is as follows:
start_kernel();//The first function of kernel rest_init(); kernel_init(); kernel_init_freeable(); do_basic_setup(); do_initcalls();// do_initcall_level(); //...... module_init(); /*************************************/
Then call the init function you implemented in the driver through module_init():
For an i2c device, the method is as follows:
static struct i2c_driver aw9106_i2c_driver = { .driver = { .name = AW9106_I2C_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(aw9106_dt_match), }, .probe = aw9106_i2c_probe, .remove = aw9106_i2c_remove, .id_table = aw9106_i2c_id, }; static int __init aw9106_i2c_init(void)//Entry function { int ret = 0; pr_info("aw9106 driver version %s\n", AW9106_VERSION); ret = i2c_add_driver(&aw9106_i2c_driver);//Equipment registration if(ret){ pr_err("fail to add aw9106 device into i2c\n"); return ret; } return 0; } module_init(aw9106_i2c_init); static void __exit aw9106_i2c_exit(void)//Exit function { i2c_del_driver(&aw9106_i2c_driver);//Write-off equipment } module_exit(aw9106_i2c_exit);
Load the address of aw9106_i2c_driver () into the. probe member of the struct i2c_driver you implemented.
Next, how to call your probe function from i2c_add_driver(), the call chain is as follows:
i2c_register_driver(); driver_register(); bus_add_driver(); driver_attach(); __driver_attach (for your device); driver_probe_device(); really_probe(); i2c_device_probe (this is what dev->bus->probe is for an i2c driver); aw9106_i2c_driver();
Next, a function is used to analyze what they did.
First look at i2c_register_driver().
Definition location: kernel/msm-4.4/drivers/i2c/i2c-core.c
int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { int res; /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) { printk("unlikely(WARN_ON(!i2c_bus_type.p and return -EAGAIN.\n"); return -EAGAIN; } /* add the driver to the list of i2c drivers in the driver core */ driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type;//bus type is added here INIT_LIST_HEAD(&driver->clients); /* When registration returns, the driver core * will have called probe() for all matching-but-unbound devices. */ res = driver_register(&driver->driver); //Walk in this way. if (res){ printk("driver_register return res : %d\n", res); return res; } pr_debug("driver [%s] registered\n", driver->driver.name); printk(KERN_INFO "======>lkhlog %s\n",driver->driver.name); /* iWalk the adapters that are already present */ i2c_for_each_dev(driver, __process_new_driver); return 0;
}
type is defined as follows:
struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe,//This function will be called back later, as mentioned in the call chain above. .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, };
Add bus_type and call driver_register().
Next, look at driver_register().
Definition location: kernel/msm-4.4/drivers/base/driver.c
/** * driver_register - register driver with bus Registered Bus Driver, Associated with Bus Type * @drv: driver to register * * We pass off most of the work to the bus_add_driver() call, * since most of the things we have to do deal with the bus * structures. */ int driver_register(struct device_driver *drv) { int ret; struct device_driver *other; BUG_ON(!drv->bus->p); //...... other = driver_find(drv->name, drv->bus);//Verify that the driver has been registered printk(KERN_WARNING "======>lkh driver_find, other : %d", other); //...... ret = bus_add_driver(drv);//Walk in mainly from here. printk(KERN_WARNING "======>lkh bus_add_driver, ret : %d", ret); if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) { printk(KERN_WARNING "======>lkh bus_remove_driver"); bus_remove_driver(drv); return ret; } kobject_uevent(&drv->p->kobj, KOBJ_ADD); return ret; }
Next, look at bus_add_driver().
Define location: kernel/msm-4.4/drivers/base/bus.c
/** * bus_add_driver - Add a driver to the bus. * @drv: driver. */ int bus_add_driver(struct device_driver *drv) { bus = bus_get(drv->bus);//Retrieve the bus loaded in front if (!bus) { printk(KERN_ERR "======>lkh return -EINVAL\n"); return -EINVAL; } printk(KERN_ERR "======>lkh bus_add_driver\n"); pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { error = -ENOMEM; goto out_put_bus; } klist_init(&priv->klist_devices, NULL, NULL); priv->driver = drv;//The inside pointer of driver_private points to device_driver drv->p = priv; //device_driver also has pointers pointing to driver_private, so you can get one from the other. priv->kobj.kset = bus->p->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name); if (error) goto out_unregister; klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); if (drv->bus->p->drivers_autoprobe) { printk(KERN_ERR "======>lkh drv->bus->p->drivers_autoprobe == true, name : %s\n", drv->name); if (driver_allows_async_probing(drv)) { pr_debug("bus: '%s': probing driver %s asynchronously\n", drv->bus->name, drv->name); printk(KERN_ERR "======>lkh bus: '%s': probing driver %s asynchronously\n", drv->bus->name, drv->name); async_schedule(driver_attach_async, drv); } else { printk(KERN_ERR "======>lkh enter driver_attach, name : %s\n", drv->name); error = driver_attach(drv);//Walk in this way. printk(KERN_ERR "======>lkh driver_attach, error : %d\n", error); if (error) goto out_unregister; } } printk(KERN_ERR "======>lkh bus_add_driver 2, name : %s \n", drv->name); //If the driver_attach() returns without error, //This side will go in and create relevant nodes, links module_add_driver(drv->owner, drv); //...... }
Next, look at driver_attach(), and look directly at the description of the function.
Definition location: kernel/msm-4.4/drivers/base/dd.c
/** * driver_attach - try to bind driver to devices. * @drv: driver. * * Walk the list of devices that the bus has on it and try to * match the driver with each one. If driver_probe_device() * returns 0 and the @dev->driver is set, we've found a * compatible pair. */ int driver_attach(struct device_driver *drv) { return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); } //Inside bus_for_each_dev(), each device on the bus is traversed and the _driver_attach() function is called as follows: //Define location: kernel/msm-4.9/drivers/base/bus.c /** * bus_for_each_dev - device iterator. * @bus: bus type. * @start: device to start iterating from. * @data: data for the callback. * @fn: function to be called for each device. * * Iterate over @bus's list of devices, and call @fn for each, * passing it @data. If @start is not NULL, we use that device to * begin iterating from. * */ int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *)) { //...... klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); printk(KERN_WARNING "======>lkh while\n"); //Get each device and call _driver_attach while ((dev = next_device(&i)) && !error){ error = fn(dev, data); //__driver_attach(dev,data) //printk(KERN_WARNING "======>lkh while enter\n"); //} klist_iter_exit(&i); printk(KERN_WARNING "======>lkh bus_for_each_dev end \n"); return error; }
Then go into _driver_attach() and continue to look.
Definition location: kernel/msm-4.4/drivers/base/dd.c
static int __driver_attach(struct device *dev, void *data) { struct device_driver *drv = data; int ret; //Call i2c_device_match(), match device and driver ret = driver_match_device(drv, dev); //...... if (!dev->driver){ printk(KERN_DEBUG "======>lkh enter driver_probe_device \n"); driver_probe_device(drv, dev);//Walk in this way. } device_unlock(dev); if (dev->parent) device_unlock(dev->parent); return 0;
}
Let's first look at driver_match_device().
Define location: kernel/msm-4.4/drivers/base/base.h\
kernel/msm-4.9/drivers/i2c/i2c-core.c static inline int driver_match_device(struct device_driver *drv, struct device *dev) { return drv->bus->match ? drv->bus->match(dev, drv) : 1; } static int i2c_device_match(struct device *dev, struct device_driver *drv) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; /* Attempt an OF style match */ //There are three ways to match: //Compatible match has highest priority //Matching type is better than matching name //Matching name is a bit better than not if (i2c_of_match_device(drv->of_match_table, client)) return 1; /* Then ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; driver = to_i2c_driver(drv); /* Finally an I2C match */ if (i2c_match_id(driver->id_table, client)) return 1; return 0; }
This i2c_device_match() is the function i2c_register_driver() in front of the driver - > driver. bus:
driver->driver.bus = &i2c_bus_type; struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, };
If the match succeeds, go inside driver_probe_device().
Definition location: kernel/msm-4.4/drivers/base/dd.c
/** * driver_probe_device - attempt to bind device & driver together */ int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; printk(KERN_DEBUG "======>lkh driver_probe_device enter\n"); //Whether the testing equipment has been registered or not if (!device_is_registered(dev)) return -ENODEV; //...... pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); pm_runtime_barrier(dev); ret = really_probe(dev, drv);//Walk in this way. //...... return ret; } //If the device is registered, then call really_probe(): //Define location: kernel/msm-4.9/drivers/base/dd.c static int really_probe(struct device *dev, struct device_driver *drv) { // ...... re_probe: //The device matches the driver, and the driver inside the device is equipped with its corresponding driver. dev->driver = drv; /* * Ensure devices are listed in devices_kset in correct order * It's important to move Dev to the end of devices_kset before * calling .probe, because it could be recursive and parent Dev * should always go first */ devices_kset_move_last(dev); if (dev->bus->probe) { ret = dev->bus->probe(dev);//Call i2c_device_probe() if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } //...... } //In i2c_device_probe(), you_probe_func() will be called. //Definition location: kernel/msm-4.9/drivers/i2c/i2c-core.c static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; if (!client) return 0; driver = to_i2c_driver(dev->driver); // ...... /* * When there are no more users of probe(), * rename probe_new to probe. */ if (driver->probe_new) status = driver->probe_new(client); else if (driver->probe)//Call your_probe_func() status = driver->probe(client, i2c_match_id(driver->id_table, client)); else status = -EINVAL; if (status) goto err_detach_pm_domain; return 0; err_detach_pm_domain: dev_pm_domain_detach(&client->dev, true); err_clear_wakeup_irq: dev_pm_clear_wake_irq(&client->dev); device_init_wakeup(&client->dev, false); return status; }
At this point, you call your_probe_func(), which you implemented in your own driver.
The main work of probe function is as follows:
a, power the equipment;
The main functions used are:
regulator_get() regulator_set_voltage() How much power you use and which circuit you use depends on how you configure the dtsi file, and how you configure the dtsi file. It also depends on which circuit the device is powered on the hardware and the definition of configuration options on the chip platform.
b, initialization device;
c. Create relevant node interfaces;
The main functions used are: sysfs_create_group(); //You need to implement a structure of static struct attribute_group and the interfaces you need, such as: static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_enable_sho w, your_deriver_enable_store); static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_delay_show , your_deriver_delay_store); static DEVICE_ATTR(debug, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_debug_show , your_deriver_debug_store); static DEVICE_ATTR(wake, S_IWUSR|S_IWGRP, NULL, your_d eriver_wake_store); static DEVICE_ATTR(rawdata, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_data_show, NULL); static DEVICE_ATTR(dump, S_IRUGO|S_IWUSR|S_IWGRP, your_deriver_dump_show, NULL); static struct attribute *your_deriver_attributes[] = { &dev_attr_enable.attr, &dev_attr_delay.attr, &dev_attr_debug.attr, &dev_attr_wake.attr, &dev_attr_rawdata.attr, &dev_attr_dump.attr, NULL }; static struct attribute_group your_driver_attribute_group = { .attrs = your_driver_attributes }; sysfs_create_group(&p_data->input_dev->dev.kobj, &your_driver_attribute_group);
After the nodes are created, the corresponding functions in the driver can be invoked in the linux application layer space by operating the nodes.
For example, for node enable, read (or cat), write (or echo) of delay, it corresponds to the implementation of its driver.
your_deriver_enable_show, your_deriver_enable_store your_deriver_delay_show, your_deriver_delay_store