Linux Platform Bus Driver Device Model

Linux Platform Bus Driver Device ModelEmbedded Linux QQ Group: 175159209, enthusiasts are welcome to join and discuss technical issues!

The platform bus is a virtual bus, and the corresponding device is a platform_device, while the driver is a platform_driver. In the device driver model of Linux 2.6, I2C, RTC, LCD, etc. are classified as platform_device.

The bus binds devices and drivers; when a device is registered in the system, it looks for a matching driver; conversely, when a driver is registered, it looks for a matching device, with matching done by the bus.

In the Linux 2.6 system, an instance of bus_type called platform_bus_type is defined.

  1. struct bus_type platform_bus_type = {

  2. .name = “platform”,

  3. .dev_attrs = platform_dev_attrs,

  4. .match = platform_match, // The match function is used by devices and drivers to determine if they match

  5. .uevent = platform_uevent,

  6. .pm = PLATFORM_PM_OPS_PTR,

  7. };

  1. /* The platform_match function is used to match drivers and devices on the bus */

  2. static int platform_match(struct device *dev, struct device_driver *drv)

  3. {

  4. struct platform_device *pdev = to_platform_device(dev);

  5. struct platform_driver *pdrv = to_platform_driver(drv);

  6. /* Match against the id table first */

  7. if (pdrv->id_table)

  8. return platform_match_id(pdrv->id_table, pdev) != NULL;

  9. /* Fall-back to driver name match */

  10. return (strcmp(pdev->name, drv->name) == 0);

  11. }

The platform_match function first checks for an id_table; if present, it uses the id_table for matching; otherwise, it checks the names in the platform_device and platform_driver members. If the name fields are the same, a match occurs, and if matched, the probe function of the platform_driver is called.

Definition of the platform_device structure

  1. struct platform_device {

  2. const char * name; /* Name */

  3. int id;

  4. struct device dev;

  5. u32 num_resources; /* Total number of resources */

  6. struct resource * resource; /* Resources */

  7. struct platform_device_id *id_entry;

  8. };

One important member is resource, which contains the resource information for the device, such as IO addresses and interrupt numbers.

  1. struct resource {

  2. resource_size_t start; // Starting value of the resource

  3. resource_size_t end; // Ending value of the resource

  4. const char *name;

  5. unsigned long flags; // Resource type, such as IORESOURCE_IO, IORESOURCE_MEM, IORESOURCE_IRQ, IORESOURCE_DMA

  6. struct resource *parent, *sibling, *child;

  7. };

Some devices may have multiple resources, and the platform_get_resource function is typically used to obtain resources.

  1. /**

  2. * platform_get_resource – get a resource for a device

  3. * @dev: platform device

  4. * @type: resource type

  5. * @num: resource index

  6. */

  7. struct resource *platform_get_resource(struct platform_device *dev,

  8. unsigned int type, unsigned int num)

  9. {

  10. int i;

  11. for (i = 0; i < dev->num_resources; i++) {

  12. struct resource *r = &dev->resource[i];

  13. if (type == resource_type(r) && num– == 0)

  14. return r;

  15. }

  16. return NULL;

  17. }

The registration of the platform device is done using the platform_device_register function.

  1. int platform_device_register(struct platform_device *pdev)

  2. {

  3. device_initialize(&pdev->dev);

  4. return platform_device_add(pdev);

  5. }

The platform_device_register function first initializes the device member of platform_device using device_initialize, then calls platform_device_add to add a platform device to the kernel.

  1. int platform_device_add(struct platform_device *pdev)

  2. {

  3. int i, ret = 0;

  4. if (!pdev) /* If pdev is NULL, return EINVAL */

  5. return -EINVAL;

  6. /* If pdev->dev.parent is NULL, set pdev->dev.parent to platform_bus */

  7. if (!pdev->dev.parent)

  8. pdev->dev.parent = &platform_bus;

  9. pdev->dev.bus = &platform_bus_type; /* Set bus type */

  10. if (pdev->id != -1) /* If id = -1, it indicates automatic name allocation */

  11. dev_set_name(&pdev->dev, “%s.%d”, pdev->name, pdev->id);

  12. else

  13. dev_set_name(&pdev->dev, pdev->name);

  14. for (i = 0; i < pdev->num_resources; i++) {

  15. struct resource *p, *r = &pdev->resource[i]; /* Get resources */

  16. if (r->name == NULL)

  17. r->name = dev_name(&pdev->dev);

  18. p = r->parent;

  19. if (!p) {

  20. if (resource_type(r) == IORESOURCE_MEM) /* Set resource type */

  21. p = &iomem_resource;

  22. else if (resource_type(r) == IORESOURCE_IO)

  23. p = &ioport_resource;

  24. }

  25. if (p && insert_resource(p, r)) {

  26. printk(KERN_ERR

  27. “%s: failed to claim resource %d\n”,

  28. dev_name(&pdev->dev), i);

  29. ret = -EBUSY;

  30. goto failed;

  31. }

  32. }

  33. pr_debug(“Registering platform device ‘%s’. Parent at %s\n”,

  34. dev_name(&pdev->dev), dev_name(pdev->dev.parent));

  35. /* Add a device to the kernel */

  36. ret = device_add(&pdev->dev);

  37. if (ret == 0)

  38. return ret;

  39. failed:

  40. while (–i >= 0) {

  41. struct resource *r = &pdev->resource[i];

  42. unsigned long type = resource_type(r);

  43. if (type == IORESOURCE_MEM || type == IORESOURCE_IO)

  44. release_resource(r);

  45. }

  46. return ret;

  47. }

The platform_device_add function ultimately calls device_add to complete the registration of the platform device.

Conversely, to unregister a platform device, use the platform_device_unregister function.

  1. void platform_device_unregister(struct platform_device *pdev)

  2. {

  3. platform_device_del(pdev);

  4. platform_device_put(pdev);

  5. }

The platform_device_unregister function calls platform_device_del to unregister the platform device.

  1. void platform_device_del(struct platform_device *pdev)

  2. {

  3. int i;

  4. if (pdev) {

  5. device_del(&pdev->dev);

  6. for (i = 0; i < pdev->num_resources; i++) {

  7. struct resource *r = &pdev->resource[i];

  8. unsigned long type = resource_type(r);

  9. if (type == IORESOURCE_MEM || type == IORESOURCE_IO)

  10. release_resource(r);

  11. }

  12. }

  13. }

The platform_device_del function calls device_del to delete the platform device. Accordingly, to release resources, the release_resource function should be called, provided that the resource type is either IORESOURCE_MEM or IORESOURCE_IO.

Definition of platform_driver:

  1. struct platform_driver {

  2. int (*probe)(struct platform_device *);

  3. int (*remove)(struct platform_device *);

  4. void (*shutdown)(struct platform_device *);

  5. int (*suspend)(struct platform_device *, pm_message_t state);

  6. int (*resume)(struct platform_device *);

  7. struct device_driver driver;

  8. const struct platform_device_id *id_table;

  9. };

Definition of device_driver:

  1. struct device_driver {

  2. const char *name;

  3. struct bus_type *bus;

  4. struct module *owner;

  5. const char *mod_name; /* used for built-in modules */

  6. bool suppress_bind_attrs; /* disables bind/unbind via sysfs */

  7. const struct of_device_id *of_match_table;

  8. const struct acpi_device_id *acpi_match_table;

  9. int (*probe) (struct device *dev);

  10. int (*remove) (struct device *dev);

  11. void (*shutdown) (struct device *dev);

  12. int (*suspend) (struct device *dev, pm_message_t state);

  13. int (*resume) (struct device *dev);

  14. const struct attribute_group **groups;

  15. const struct dev_pm_ops *pm;

  16. struct driver_private *p;

  17. };

The platform_driver structure contains a device_driver member, with fields as described above. The device_driver also has probe, remove, shutdown, and other functions, which are initialized when the platform driver is registered.

As mentioned earlier, when there are platform devices and platform drivers in the system, the match function of the bus matches them, and then the probe function of the platform_driver is called with the parameter being the platform_device, sometimes using the id_table to determine if they match.

  1. struct platform_device_id {

  2. char name[PLATFORM_NAME_SIZE];

  3. kernel_ulong_t driver_data

  4. __attribute__((aligned(sizeof(kernel_ulong_t))));

  5. };

Platform drivers are registered using the platform_driver_register function.

  1. int platform_driver_register(struct platform_driver *drv)

  2. {

  3. drv->driver.bus = &platform_bus_type;

  4. if (drv->probe)

  5. drv->driver.probe = platform_drv_probe;

  6. if (drv->remove)

  7. drv->driver.remove = platform_drv_remove;

  8. if (drv->shutdown)

  9. drv->driver.shutdown = platform_drv_shutdown;

  10. if (drv->suspend)

  11. drv->driver.suspend = platform_drv_suspend;

  12. if (drv->resume)

  13. drv->driver.resume = platform_drv_resume;

  14. return driver_register(&drv->driver);

  15. }

First, initialize the driver in the platform_driver by setting the bus type to platform_bus_type; set the driver probe to platform_drv_probe; set the driver remove to platform_drv_remove; set the driver shutdown to platform_drv_shutdown; set the driver suspend to platform_drv_suspend; set the driver resume to platform_drv_resume; and finally call driver_register to register the platform driver.

Conversely, to unregister a platform driver, use the platform_driver_unregister function.

  1. void platform_driver_unregister(struct platform_driver *drv)

  2. {

  3. driver_unregister(&drv->driver);

  4. }

The platform_driver_unregister function calls driver_unregister to unregister the platform driver.

Embedded Linux Chinese Site

The most professional Chinese Embedded Linux website, with over eight years of experience and tens of thousands of registered users!!

Share Embedded & Linux Technical content, tutorials, news, high-paying positions

SubscribeClick on ‘Embedded Linux Chinese Site’ below the title

ShareClick the share button at the top right corner

Submit[email protected]

DiscussionQQ Group:175159209

Linux Platform Bus Driver Device Model

Click below “Read the original text” for more information
Linux Platform Bus Driver Device Model

Leave a Comment