总线设备驱动模型---platform篇

简介:
  linux从2.6起就加入了一套新的驱动管理和注册的机制platform平台总线,是一条虚拟的总线,设备用platform_device表示,驱动用platform_driver进行注册。于传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用资源,提高了代码的安全性和可移植性。
下面来看看内核时怎么注册platform总线的过程

点击(此处)折叠或打开

  1. int __init platform_bus_init(void)
  2. {
  3.     int error;

  4.     early_platform_cleanup();

  5.     error = device_register(&platform_bus);//注册了platform的设备
  6.     if (error)
  7.         return error;
  8.     error = bus_register(&platform_bus_type);//注册了platform总线
  9.     if (error)
  10.         device_unregister(&platform_bus);
  11.     return error;
  12. }

点击(此处)折叠或打开

  1. struct device platform_bus = {
  2.     .init_name    = "platform",
  3. };

点击(此处)折叠或打开

  1. struct bus_type platform_bus_type = {
  2.     .name        = "platform",
  3.     .dev_attrs    = platform_dev_attrs,
  4.     .match        = platform_match,
  5.     .uevent        = platform_uevent,
  6.     .pm        = &platform_dev_pm_ops,
  7. };
其过程和总线的注册过程差不多,驱动和设备匹配后,调用platform的match函数。由传统的机制,也不难总结出platform的开发流程为
1、定义一个platform_device,并注册
2、定义一个platform_driver,并注册
定义platform_device过程

点击(此处)折叠或打开

  1. struct platform_device *platform_device_alloc(const char *name, int id)
  2. {
  3.     struct platform_object *pa;

  4.     pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
  5.     if (pa) {
  6.         strcpy(pa->name, name);
  7.         pa->pdev.name = pa->name;
  8.         pa->pdev.id = id;
  9.         device_initialize(&pa->pdev.dev);
  10.         pa->pdev.dev.release = platform_device_release;
  11.         arch_setup_pdev_archdata(&pa->pdev);
  12.     }

  13.     return pa ? &pa->pdev : NULL;
  14. }
下面首先来看看platform_device的注册过程

点击(此处)折叠或打开

  1. struct platform_device {
  2.     const char    * name;                  //设备名
  3.     int        id;                         //设备ID
  4.     struct device    dev;
  5.     u32        num_resources;              //设备使用的资源的数目
  6.     struct resource    * resource;         //设备使用的资源

  7.     const struct platform_device_id    *id_entry;

  8.     /* MFD cell pointer */
  9.     struct mfd_cell *mfd_cell;

  10.     /* arch specific additions */
  11.     struct pdev_archdata    archdata;
  12. };

点击(此处)折叠或打开

  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. };
在struct resource结构中我们通常只关心start、end和flags这3个字段,分别表示资源的开始值、结束值和类型,flags可以用内存资源,IO资源,中断资源等。

点击(此处)折叠或打开

  1. int platform_device_register(struct platform_device *pdev)
  2. {
  3.     device_initialize(&pdev->dev);//dev初始化
  4.     arch_setup_pdev_archdata(pdev);
  5.     return platform_device_add(pdev);//加入到dev链表
  6. }

点击(此处)折叠或打开

  1. int platform_device_add(struct platform_device *pdev)
  2. {
  3.     int i, ret = 0;

  4.     if (!pdev)
  5.         return -EINVAL;

  6.     if (!pdev->dev.parent)
  7.         pdev->dev.parent = &platform_bus;//父设备设置为platform_bus

  8.     pdev->dev.bus = &platform_bus_type;//设备挂载在platforrm总线上

  9.     if (pdev->id != -1)
  10.         dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
  11.     else
  12.         dev_set_name(&pdev->dev, "%s", pdev->name);

  13.     for (= 0; i < pdev->num_resources; i++) {    //完成资源的初始化
  14.         struct resource *p, *= &pdev->resource[i];

  15.         if (r->name == NULL)
  16.             r->name = dev_name(&pdev->dev);

  17.         p = r->parent;
  18.         if (!p) {
  19.             if (resource_type(r) == IORESOURCE_MEM)
  20.                 p = &iomem_resource;
  21.             else if (resource_type(r) == IORESOURCE_IO)
  22.                 p = &ioport_resource;
  23.         }

  24.         if (&& insert_resource(p, r)) {
  25.             printk(KERN_ERR
  26.              "%s: failed to claim resource %d\n",
  27.              dev_name(&pdev->dev), i);
  28.             ret = -EBUSY;
  29.             goto failed;
  30.         }
  31.     }

  32.     pr_debug("Registering platform device '%s'. Parent at %s\n",
  33.          dev_name(&pdev->dev), dev_name(pdev->dev.parent));

  34.     ret = device_add(&pdev->dev);
  35.     if (ret == 0)
  36.         return ret;

  37.  failed:
  38.     while (-->= 0) {
  39.         struct resource *= &pdev->resource[i];
  40.         unsigned long type = resource_type(r);

  41.         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
  42.             release_resource(r);
  43.     }

  44.     return ret;
  45. }
其上最后也是调用device_add的,其主要是将device加入到bus总线中,并由device_attach完成设备与驱动之间的匹配,这个过程在设备一篇中已经有详细的分析过程中,再看看驱动的注册过程。

点击(此处)折叠或打开

  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. };
可见,它包含了设备操作的功能函数,同时包含了device_driver结构。内核提供的platform_driver结构的注册为

点击(此处)折叠或打开

  1. int platform_driver_register(struct platform_driver *drv)
  2. {
  3.     drv->driver.bus = &platform_bus_type;//注册到总线上,drv与dev匹配通过platform_bus_type注册的platform_match完成
  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.     return driver_register(&drv->driver);
  11. }
其注册函数中比较重要的还是调用了driver_register,添加到platform总线链表,完成设备与驱动之间的匹配过程,其主要的过程在总线设备驱动模型的驱动篇已经有过分析。

点击(此处)折叠或打开

  1. int driver_register(struct device_driver *drv)
  2. {
  3.     int ret;
  4.     struct device_driver *other;

  5.     BUG_ON(!drv->bus->p);

  6.     if ((drv->bus->probe && drv->probe) ||
  7.      (drv->bus->remove && drv->remove) ||
  8.      (drv->bus->shutdown && drv->shutdown))
  9.         printk(KERN_WARNING "Driver '%s' needs updating - please use "
  10.             "bus_type methods\n", drv->name);

  11.     other = driver_find(drv->name, drv->bus);
  12.     if (other) {
  13.         put_driver(other);
  14.         printk(KERN_ERR "Error: Driver '%s' is already registered, "
  15.             "aborting...\n", drv->name);
  16.         return -EBUSY;
  17.     }

  18.     ret = bus_add_driver(drv);
  19.     if (ret)
  20.         return ret;
  21.     ret = driver_add_groups(drv, drv->groups);
  22.     if (ret)
  23.         bus_remove_driver(drv);
  24.     return ret;
  25. }
下面看看看驱动和设备的匹配过程,由以前可以看出,主要是调用bus的match函数来完成匹配。

点击(此处)折叠或打开

  1. static int platform_match(struct device *dev, struct device_driver *drv)
  2. {
  3.     struct platform_device *pdev = to_platform_device(dev);
  4.     struct platform_driver *pdrv = to_platform_driver(drv);

  5.     /* Attempt an OF style match first */
  6.     if (of_driver_match_device(dev, drv))
  7.         return 1;

  8.     /* Then try to match against the id table */
  9.     if (pdrv->id_table)
  10.         return platform_match_id(pdrv->id_table, pdev) != NULL;

  11.     /* fall-back to driver name match */
  12.     return (strcmp(pdev->name, drv->name) == 0);
  13. }
由上面可以看出,只需要比较dev的名字和drv的名字,如果是相同的话就匹配成功。
在platform的设备驱动的编写有两种方法:
1、在bsp版文件中实现定义,在文件中将platform_device被化为一个数组,最后通过platform_add_devices函数注册。对于2440来说位于arch\arm\mach-s3c2440\mach-smdk2440.c中定义

点击(此处)折叠或打开

  1. static struct platform_device *smdk2440_devices[] __initdata = {
  2.     &s3c_device_usb,
  3.     &s3c_device_lcd,
  4.     &s3c_device_wdt,
  5.     &s3c_device_i2c,
  6.     &s3c_device_iis,
  7. };
如果我们要实现一个设备的添加,那么只需要加入一个struct platform_device的数组,然后只需要编写对应的platform_driver驱动程序就可以了。从这种方法可以看出,存在一个很明显的缺点,如果要改写驱动,就要重新的编译内核。
2、第二种方法只需要单独编写一个内核模块加载到内核中。
例子:
device文件:platform_dev.c

点击(此处)折叠或打开

  1. struct platform_device *my_led_dev; 
  2.   
  3. static int __init platform_dev_init(void) 
  4. { 
  5.     int ret; 
  6.       
  7.  //分配一个 platform_device结构体
  8.     my_led_dev = platform_device_alloc("platform_led", -1); 
  9.       
  10.     ret = platform_device_add(my_led_dev);//将自定义的设备添加到内核设备架构中
  11.       
  12.     if(ret) 
  13.         platform_device_put(my_led_dev);//销毁platform设备结构 
  14.       
  15.     return ret; 
  16. } 
  17.   
  18. static void __exit platform_dev_exit(void) 
  19. { 
  20.     platform_device_unregister(my_led_dev);//注销platform_device
  21. } 
  22.   
  23. module_init(platform_dev_init); 
  24. module_exit(platform_dev_exit); 
  25.   
  26. MODULE_AUTHOR("Sola"); 
  27. MODULE_LICENSE("GPL");
drv文件:platform_drv.c

点击(此处)折叠或打开

  1. static int s3c6410_led_open(struct inode *inode, struct file *file) 
  2. { 
  3.    unsigned tmp; 
  4.           tmp = readl(S3C64XX_GPMCON); 
  5.      tmp = (tmp & ~(0xFFFF))|(0x1111U); 
  6.           writel(tmp, S3C64XX_GPMCON); 
  7.   
  8.      return 0; 
  9. } 
  10.   
  11.   
  12. static int s3c6410_led_close(struct inode *inode, struct file *file) 
  13. { 

  14.     return 0; 
  15. } 
  16.   
  17.   
  18. static int s3c6410_led_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 
  19. { 
  20.      printk("#########read######\n"); 
  21.      return count; 
  22. } 

  23. static int s3c6410_led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos) 
  24. { 
  25.      char wbuf[10]; 
  26.      unsigned tmp; 

  27.      copy_from_user(wbuf,buf,count); 
  28.   if(wbuf[0]==1)//1号灯亮
  29.      switch(wbuf[1]) 
  30.      { 
  31.          case 0: //off 
  32.              tmp = readl(S3C64XX_GPMDAT); 
  33.                            tmp |= (0x1U); 
  34.                            writel(tmp, S3C64XX_GPMDAT); 
  35.              break; 
  36.          case 1: //on 
  37.              tmp = readl(S3C64XX_GPMDAT); 
  38.                            tmp &= ~(0x1U); 
  39.                            writel(tmp, S3C64XX_GPMDAT); 
  40.              break; 
  41.          default : 
  42.              break; 
  43.      } 
  44.   
  45.   if(wbuf[0]==2)//2号灯亮
  46.      switch(wbuf[1]) 
  47.      { 
  48.          case 0: //off 
  49.              tmp = readl(S3C64XX_GPMDAT); 
  50.                            tmp |= (0x2U); 
  51.                            writel(tmp, S3C64XX_GPMDAT); 
  52.              break; 
  53.          case 1: //on 
  54.              tmp = readl(S3C64XX_GPMDAT); 
  55.                            tmp &= ~(0x2U); 
  56.                            writel(tmp, S3C64XX_GPMDAT); 
  57.              break; 
  58.          default : 
  59.              break; 
  60.      } 
  61.   
  62.   if(wbuf[0]==3)//3号灯亮
  63.      switch(wbuf[1]) 
  64.      { 
  65.          case 0: //off 
  66.              tmp = readl(S3C64XX_GPMDAT); 
  67.                            tmp |= (0x4U); 
  68.                            writel(tmp, S3C64XX_GPMDAT); 
  69.              break; 
  70.          case 1: //on 
  71.              tmp = readl(S3C64XX_GPMDAT); 
  72.                            tmp &= ~(0x4U); 
  73.                            writel(tmp, S3C64XX_GPMDAT); 
  74.              break; 
  75.          default : 
  76.              break; 
  77.      } 
  78.   
  79.   if(wbuf[0]==4)//4号灯亮
  80.      switch(wbuf[1]) 
  81.      { 
  82.          case 0: //off 
  83.              tmp = readl(S3C64XX_GPMDAT); 
  84.                            tmp |= (0x8U); 
  85.                            writel(tmp, S3C64XX_GPMDAT); 
  86.              break; 
  87.          case 1: //on 
  88.              tmp = readl(S3C64XX_GPMDAT); 
  89.                            tmp &= ~(0x8U); 
  90.                            writel(tmp, S3C64XX_GPMDAT); 
  91.              break; 
  92.          default : 
  93.              break; 
  94.      } 
  95.      return count; 
  96. } 
  97.   
  98.   
  99. static struct file_operations led_fops = { 
  100.     .owner = THIS_MODULE, 
  101.     .open = s3c6410_led_open, 
  102.     .release = s3c6410_led_close, 
  103.     .read = s3c6410_led_read,
  104.  .write = s3c6410_led_write,
  105. }; 
  106.   
  107.   
  108. static int my_plat_probe(struct platform_device *dev) 
  109. { 
  110.     int rc;
  111.  printk("Test platform_led dev\n");
  112.  //注册设备
  113.  rc = register_chrdev(LED_MAJOR,"platform_led",&led_fops);
  114.   if (rc <0) 
  115.      { 
  116.          printk ("register %s char dev error\n","led"); 
  117.          return -1; 
  118.      } 
  119.      printk ("ok!\n"); 
  120.      return 0; 
  121. } 
  122.   
  123. static int my_plat_remove(struct platform_device *dev) 
  124. { 
  125.     printk("my platfrom device has removed.\n"); 
  126.     return 0; 
  127. } 
  128.   
  129. struct platform_driver my_led_drv = { 
  130.     .probe = my_plat_probe, 
  131.     .remove = my_plat_remove, 
  132.     .driver = { 
  133.         .owner = THIS_MODULE, 
  134.         .name = "platform_led", 
  135.     }, 
  136. }; 
  137.   
  138. static int __init platform_drv_init(void) 
  139. { 
  140.     int ret; 
  141.   
  142.     ret = platform_driver_register(&my_led_drv); 
  143.       
  144.     return ret; 
  145. } 
  146.   
  147. static void __exit platform_drv_exit(void) 
  148. { 
  149.     platform_driver_unregister(&my_led_drv); 
  150. } 
  151.   
  152. module_init(platform_drv_init); 
  153. module_exit(platform_drv_exit); 
  154.   
  155. MODULE_LICENSE("GPL");
目录
相关文章
|
6月前
|
内存技术
【HARDWARE】 --- SPI接口协议介绍与应用说明
【HARDWARE】 --- SPI接口协议介绍与应用说明
168 3
|
6月前
|
测试技术 Perl
【ZYNQ】ZYNQ7000 UART 控制器及驱动应用示例
【ZYNQ】ZYNQ7000 UART 控制器及驱动应用示例
268 0
|
Linux 索引
platform设备驱动
platform设备驱动
102 0
|
Ubuntu 调度
usb摄像头驱动-core层usb设备的注册
usb摄像头驱动-core层usb设备的注册
102 0
|
存储 Linux 开发者
【Linux学习笔记】设备驱动模型详解——总线、设备、驱动和类
设备驱动是计算机系统中的重要组成部分,它们允许操作系统与硬件交互。设备驱动模型是一种通用的抽象框架,用于描述操作系统如何管理硬件设备。这里我们将介绍设备驱动模型中的四个关键概念:总线、设备、驱动和类。
LED模板驱动程序的改造:总线设备驱动模型
LED模板驱动程序的改造:总线设备驱动模型
111 0
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义
143 0
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义
|
Linux 存储控制器 芯片
ZYNQ-ZedBoard设置Quad-SPI启动和SD驱动
ZYNQ-ZedBoard设置Quad-SPI启动和SD驱动
487 0
ZYNQ-ZedBoard设置Quad-SPI启动和SD驱动