Linux设备驱动——虚拟总线platform

- 日理万妓 2021-12-09 05:49 458阅读 0赞

在Linux设备中有的是没有对应的物理总线的,但为了适配Linux的总线模型,内核针对这种没有物理总线的设备开发了一种虚拟总线——platform总线。

一、平台设备(device)

1.平台设备是用struct platform_device结构来表示的,它的定义如下:

  1. struct platform_device {
  2. const char *name;
  3. int id;
  4. bool id_auto;
  5. struct device dev;
  6. u32 num_resources;
  7. struct resource *resource;
  8. const struct platform_device_id *id_entry;
  9. char *driver_override; /* Driver name to force a match */
  10. /* MFD cell pointer */
  11. struct mfd_cell *mfd_cell;
  12. /* arch specific additions */
  13. struct pdev_archdata archdata;
  14. };

驱动开发者关心的主要成员有:
name: 设备的名字,在平台总线的match函数中可用于同平台driver的匹配;
id: 设备的ID,用于区分同类型不同的device;
dev: 内嵌的struct device;
nem_resources: 平台设备使用的资源个数;
resource: 平台设备的资源列表,指向资源数组中的首元素;
id_entry: 用于同平台驱动匹配的ID,在平台总线的match函数中首先尝试匹配该ID,如果不成功在尝试用name成员来匹配。

下面为我定义的两个device:

  1. struct platform_device pdev0 = {
  2. .name = "pdev",
  3. .id = 0,
  4. .num_resources = 1,
  5. .resource = &pdev_resources[0],
  6. .dev = {
  7. .release = pdev_release,
  8. },
  9. };
  10. struct platform_device pdev1 = {
  11. .name = "pdev",
  12. .id = 1,
  13. .num_resources = 1,
  14. .resource = &pdev_resources[1],
  15. .dev = {
  16. .release = pdev_release,
  17. },
  18. };

2.在平台设备中,最关键的是struct resource,这是实现device和driver分离的关键,其定义如下:

  1. struct resource {
  2. resource_size_t start;
  3. resource_size_t end;
  4. const char *name;
  5. unsigned long flags;
  6. struct resource *parent, *sibling, *child;
  7. };

驱动开发者关心的主要成员如下:
start: 资源的开始,对于内存来说是内存起始地址,对于中断资源来说是起始中断号,对于DMA资源来说是DMA起始通道号;
end: 资源的结束;
flags: 资源的标志,常见的有一下几种:

  • IORESOURCE_MEM:内存资源;
  • IORESOURCE_IRQ:中断资源;
  • IORESOURCE_DMA:DMA资源。

下面为我定义的resource:

  1. static char buf0[200] = {
  2. 0};
  3. static char buf1[200] = {
  4. 0};
  5. static struct resource pdev_resources[] = {
  6. [0] = {
  7. buf0, buf0+sizeof(buf0)-1, "pdev0-resource", IORESOURCE_MEM,},
  8. [1] = {
  9. buf1, buf1+sizeof(buf1)-1, "pdev1-resource", IORESOURCE_MEM,},
  10. };

3.向platform总线注册和注销device的主要函数如下:

  1. int platform_device_register(struct platform_device *pdev);
  2. void platform_device_unregister(struct platform_device *pdev);

二、平台驱动(driver)

1.平台驱动(driver)用struct 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. bool prevent_deferred_probe;
  10. };

驱动开发者关心的主要成员如下:
probe: platform总线发现有匹配的平台设备(device)时调用;
remove: 驱动的平台设备(device)被移除或平台驱动(driver)注销时调用;
shutdown、suspend和resume: 电源管理函数;
id_table: 平台驱动(driver)可以驱动的平台设备ID列表。
下面为我定义的driver:

  1. struct platform_driver pdrv = {
  2. .driver = {
  3. .name = "pdev",
  4. .owner = THIS_MODULE,
  5. .pm = &pdrv_pm_ops,
  6. },
  7. .probe = pdrv_probe,
  8. .remove = pdrv_remove,
  9. };

2.向platform总线注册和注销驱动的主要函数如下:

  1. platform_driver_register(drv);
  2. platform_driver_unregister(drv);

三、示例代码

在这里我实现了一个简单的platform驱动,其中device注册了两个设备,如下:

pltdev.c

  1. #include <linux/init.h>
  2. #include <linux/kernel.h>
  3. #include <linux/module.h>
  4. #include <linux/platform_device.h>
  5. static char buf0[200] = {
  6. 0};
  7. static char buf1[200] = {
  8. 0};
  9. static struct resource pdev_resources[] = {
  10. [0] = {
  11. buf0, buf0+sizeof(buf0)-1, "pdev0-resource", IORESOURCE_MEM,},
  12. [1] = {
  13. buf1, buf1+sizeof(buf1)-1, "pdev1-resource", IORESOURCE_MEM,},
  14. };
  15. static void pdev_release(struct device *dev)
  16. {
  17. }
  18. struct platform_device pdev0 = {
  19. .name = "pdev",
  20. .id = 0,
  21. .num_resources = 1,
  22. .resource = &pdev_resources[0],
  23. .dev = {
  24. .release = pdev_release,
  25. },
  26. };
  27. struct platform_device pdev1 = {
  28. .name = "pdev",
  29. .id = 1,
  30. .num_resources = 1,
  31. .resource = &pdev_resources[1],
  32. .dev = {
  33. .release = pdev_release,
  34. },
  35. };
  36. static int __init pltdev_init(void)
  37. {
  38. strcpy(buf0, "hello");
  39. strcpy(buf1, "world");
  40. printk(KERN_EMERG"pltdev_init!\n");
  41. platform_device_register(&pdev0);
  42. platform_device_register(&pdev1);
  43. return 0;
  44. }
  45. static void __exit pltdev_exit(void)
  46. {
  47. printk(KERN_EMERG"pltdev_exit!\n");
  48. platform_device_unregister(&pdev1);
  49. platform_device_unregister(&pdev0);
  50. }
  51. module_init(pltdev_init);
  52. module_exit(pltdev_exit);
  53. MODULE_LICENSE("GPL");
  54. MODULE_AUTHOR("xiaodongzhao");
  55. MODULE_DESCRIPTION("register a platfom device");

pltdrv.c

  1. #include <linux/init.h>
  2. #include <linux/kernel.h>
  3. #include <linux/module.h>
  4. #include <linux/platform_device.h>
  5. static int pdrv_suspend(struct device *dev)
  6. {
  7. printk(KERN_EMERG"pdev: suspend\n");
  8. return 0;
  9. }
  10. static int pdrv_resume(struct device *dev)
  11. {
  12. printk(KERN_EMERG"pdev: resume\n");
  13. return 0;
  14. }
  15. static const struct dev_pm_ops pdrv_pm_ops = {
  16. .suspend = pdrv_suspend,
  17. .resume = pdrv_resume,
  18. };
  19. static int pdrv_probe(struct platform_device *pdev)
  20. {
  21. struct resource* mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  22. printk(KERN_EMERG"id:%d name:%s buf:%s\n", pdev->id, mem_res->name, (char*)mem_res->start);
  23. return 0;
  24. }
  25. static int pdrv_remove(struct platform_device *pdev)
  26. {
  27. printk(KERN_EMERG"pdrv_remove!\n");
  28. return 0;
  29. }
  30. struct platform_driver pdrv = {
  31. .driver = {
  32. .name = "pdev",
  33. .owner = THIS_MODULE,
  34. .pm = &pdrv_pm_ops,
  35. },
  36. .probe = pdrv_probe,
  37. .remove = pdrv_remove,
  38. };
  39. static int __init pdev_init(void)
  40. {
  41. int ret;
  42. printk(KERN_EMERG"pdev_init!\n");
  43. ret = platform_driver_register(&pdrv);
  44. return ret;
  45. }
  46. static void __exit pdev_exit(void)
  47. {
  48. printk(KERN_EMERG"pdev_exit!\n");
  49. platform_driver_unregister(&pdrv);
  50. }
  51. module_init(pdev_init);
  52. module_exit(pdev_exit);
  53. //module_platform_driver(pdrv);
  54. MODULE_LICENSE("GPL");
  55. MODULE_AUTHOR("xiaodongzhao");
  56. MODULE_DESCRIPTION("A simple platform driver");
  57. MODULE_ALIAS("platform:pdev");

测试:
测试结果如下:

  1. # insmod pltdev.ko
  2. pltdev_init!
  3. # insmod pltdrv.ko
  4. id:0 name:pdev0-resource buf:hello
  5. id:1 name:pdev1-resource buf:world

从上述结果可知,在插入pltdrv.ko的时候driver被匹配了两次,因为在device中注册了两个设备。

发表评论

表情:
评论列表 (有 0 条评论,458人围观)

还没有评论,来说两句吧...

相关阅读

    相关 999、platform设备驱动实验

    一、驱动的分离和分隔 驱动的分隔,也就是将主机驱动和设备驱动分隔开来,比如 I2C、 SPI 等等都会采用驱动分隔的方式来简化驱动的开发。在实际的驱动开发中,一般 I2C