zoukankan      html  css  js  c++  java
  • linux SPI驱动——spidev之driver(六)

    一: spidev_init注册spidev
       1:  static int __init spidev_init(void)
       2:  {
       3:      int status;
       4:   
       5:      /* Claim our 256 reserved device numbers.  Then register a class
       6:       * that will key udev/mdev to add/remove /dev nodes.  Last, register
       7:       * the driver which manages those device numbers.
       8:       */
       9:      BUILD_BUG_ON(N_SPI_MINORS > 256);
      10:      /* 注册spi字符设备 */
      11:      status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); 
      12:      if (status < 0) {
      13:          return status;
      14:      }
      15:      /* 为该设备创建一个class spidev */
      16:      spidev_class = class_create(THIS_MODULE, "spidev"); 
      17:      if (IS_ERR(spidev_class)) {
      18:          unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
      19:          return PTR_ERR(spidev_class);
      20:      }
      21:      /* register匹配到device ,最终调用prob函数 */
      22:      status = spi_register_driver(&spidev_spi_driver); 
      23:      if (status < 0) {
      24:          class_destroy(spidev_class);
      25:          unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
      26:      }
      27:      return status;
      28:  }
      29:  module_init(spidev_init);
      30:   
      31:  static void __exit spidev_exit(void)
      32:  {
      33:      spi_unregister_driver(&spidev_spi_driver);
      34:      class_destroy(spidev_class);
      35:      unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
      36:  }
      37:  module_exit(spidev_exit);
      38:   
      39:  MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
      40:  MODULE_DESCRIPTION("User mode SPI device interface");
      41:  MODULE_LICENSE("GPL");
      42:  MODULE_ALIAS("spi:spidev");

    总结:

           1)注册一个字符设备,cat /proc/device 会查看到“153 spi”主设备号位153的设备

           2) 创建一个class spidev ,”/sys/class/spidev/”

           3) spi_register_driver 注册spi driver。

    二:spidev_probe 函数

       1:  static int __devinit spidev_probe(struct spi_device *spi)
       2:  {
       3:      struct spidev_data    *spidev;
       4:      int            status;
       5:      unsigned long        minor;
       6:      
       7:      /* Allocate driver data */
       8:      spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
       9:      if (!spidev)
      10:          return -ENOMEM;
      11:   
      12:      /* Initialize the driver data */
      13:      spidev->spi = spi;
      14:      spin_lock_init(&spidev->spi_lock);
      15:      mutex_init(&spidev->buf_lock);
      16:   
      17:      INIT_LIST_HEAD(&spidev->device_entry);
      18:   
      19:      /* If we can allocate a minor number, hook up this device.
      20:       * Reusing minors is fine so long as udev or mdev is working.
      21:       */
      22:      mutex_lock(&device_list_lock);
      23:   
      24:      minor = find_first_zero_bit(minors, N_SPI_MINORS);
      25:      if (minor < N_SPI_MINORS) {
      26:          struct device *dev;
      27:   
      28:          spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
      29:          dev = device_create(spidev_class, &spi->dev, spidev->devt,
      30:                      spidev, "spidev%d.%d",
      31:                      spi->master->bus_num, spi->chip_select);
      32:          status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
      33:      } else {
      34:          dev_dbg(&spi->dev, "no minor number available!
    ");
      35:          status = -ENODEV;
      36:      }
      37:      if (status == 0) {
      38:          set_bit(minor, minors);
      39:          list_add(&spidev->device_entry, &device_list);
      40:      }
      41:      
      42:      mutex_unlock(&device_list_lock);
      43:   
      44:      if (status == 0)
      45:          spi_set_drvdata(spi, spidev);
      46:      else
      47:          kfree(spidev);
      48:   
      49:      return status;
      50:  }
    总结: 1. spidev_prob函数很简单,调用device_create创界字符设备的结点名为"spidev%d.%d" 2. 将spidev作为spi->dev->p->driver_data 的data
     
    三:spidev_probe 函数是如何被调用的,以及什么时候被调用的追踪 status = spi_register_driver(&spidev_spi_driver)调用
       1:  int spi_register_driver(struct spi_driver *sdrv)
       2:  {
       3:      sdrv->driver.bus = &spi_bus_type;
       4:      if (sdrv->probe)
       5:          sdrv->driver.probe = spi_drv_probe;
       6:      if (sdrv->remove)
       7:          sdrv->driver.remove = spi_drv_remove;
       8:      if (sdrv->shutdown)
       9:          sdrv->driver.shutdown = spi_drv_shutdown;
      10:      return driver_register(&sdrv->driver);
      11:  }
    //sdrv->driver.probe = spi_drv_probe; 函数
       1:  static int spi_drv_probe(struct device *dev)
       2:  {
       3:      const struct spi_driver        *sdrv = to_spi_driver(dev->driver);
       4:   
       5:      return sdrv->probe(to_spi_device(dev));
       6:  }
    可以看出调用spi_drv_prob就是调用sdrv->probe,即spidev_probe 函数
     
    2)追踪driver_register(&sdrv->driver);
       1:  int driver_register(struct device_driver *drv)
       2:  {
       3:      int ret;
       4:      struct device_driver *other;
       5:   
       6:      BUG_ON(!drv->bus->p);
       7:   
       8:      if ((drv->bus->probe && drv->probe) ||
       9:          (drv->bus->remove && drv->remove) ||
      10:          (drv->bus->shutdown && drv->shutdown))
      11:          printk(KERN_WARNING "Driver '%s' needs updating - please use "
      12:              "bus_type methods
    ", drv->name);
      13:   
      14:      other = driver_find(drv->name, drv->bus);
      15:      if (other) {
      16:          put_driver(other);
      17:          printk(KERN_ERR "Error: Driver '%s' is already registered, "
      18:              "aborting...
    ", drv->name);
      19:          return -EBUSY;
      20:      }
      21:   
      22:      ret = bus_add_driver(drv);
      23:      if (ret)
      24:          return ret;
      25:      ret = driver_add_groups(drv, drv->groups);
      26:      if (ret)
      27:          bus_remove_driver(drv);
      28:      return ret;
      29:  }
    查看bus_add_driver(drv)函数调用,有一段代码
    1: if (drv->bus->p->drivers_autoprobe) {
    2:       error = driver_attach(drv);
    3:       if (error) 4: goto out_unregister;
    5: }
     
    再看一下driver_attach函数
       1:  int driver_attach(struct device_driver *drv)
       2:  {
       3:      return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
       4:  }
     
       1:  int bus_for_each_dev(struct bus_type *bus, struct device *start,
       2:               void *data, int (*fn)(struct device *, void *))
       3:  {
       4:      struct klist_iter i;
       5:      struct device *dev;
       6:      int error = 0;
       7:   
       8:      if (!bus)
       9:          return -EINVAL;
      10:   
      11:      klist_iter_init_node(&bus->p->klist_devices, &i,
      12:                   (start ? &start->p->knode_bus : NULL));
      13:      while ((dev = next_device(&i)) && !error)
      14:          error = fn(dev, data);
      15:      klist_iter_exit(&i);
      16:      return error;
      17:  }
    从上面两段代码可以看出bus_for_each_dev会遍历总线上所有设备并设用__driver_attach。我们再看一下__driver_attach所做的工作:
    1.driver_match_device(drv, dev);device与driver name时候strncmp
    2.driver_probe_device(drv, dev); 函数调用prob
       1:  static int __driver_attach(struct device *dev, void *data)
       2:  {
       3:      struct device_driver *drv = data;
       4:   
       5:   
       6:      if (!driver_match_device(drv, dev))
       7:          return 0;
       8:   
       9:      if (dev->parent)    /* Needed for USB */
      10:          device_lock(dev->parent);
      11:      device_lock(dev);
      12:      if (!dev->driver)
      13:          driver_probe_device(drv, dev);
      14:      device_unlock(dev);
      15:      if (dev->parent)
      16:          device_unlock(dev->parent);
      17:   
      18:      return 0;
      19:  }
     
    我们再继续查看一下driver_probe_device函数的调用:
       1:  int driver_probe_device(struct device_driver *drv, struct device *dev)
       2:  {
       3:      int ret = 0;
       4:   
       5:      if (!device_is_registered(dev))
       6:          return -ENODEV;
       7:   
       8:      pr_debug("bus: '%s': %s: matched device %s with driver %s
    ",
       9:           drv->bus->name, __func__, dev_name(dev), drv->name);
      10:   
      11:      pm_runtime_get_noresume(dev);
      12:      pm_runtime_barrier(dev);
      13:      
    ret = really_probe(dev, drv);
      14:      pm_runtime_put_sync(dev);
      15:   
      16:      return ret;
      17:  }
     
    总结 : 通过代码最终我们最终看到了spidev_prob函数是怎样被调用的
     
     
  • 相关阅读:
    基于jQuery的六大表单向导插件
    oracle行转列(动态行转不定列)
    PLSql自动替换---辅助写代码
    ExcelReport第一篇:使用ExcelReport导出Excel
    改HTML5里的input标签的required属性的提示为英文的
    spring boot:用redis+lua实现表单接口的幂等性(spring boot 2.2.0)
    linux(centos8):centos8.1安装(详细过程/图解)(vmware fusion/CentOS-8.1.1911-x86_64)
    linux(centos8):配置docker的cgroup driver为systemd
    linux(centos8):禁用selinux(临时关闭/永久关闭)
    linux(centos8):firewalld对于请求会选择哪个zone处理?
  • 原文地址:https://www.cnblogs.com/xuyh/p/6030788.html
Copyright © 2011-2022 走看看