首页 > 其他分享 >kernel——platform

kernel——platform

时间:2023-01-05 22:34:59浏览次数:51  
标签:kernel struct rtc driver dev platform device

1. 最简单的示例

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

struct platform_device rtc_dev = {
    .name = "rtc-demo",
    .id   = -1,
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init rtc_device_init(void)
{
    return platform_device_register(&rtc_dev);
}

static void __exit rtc_device_exit(void)
{
    platform_device_unregister(&rtc_dev);
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};

static int rtc_driver_probe(struct platform_device *dev)
{

    irqreturn_t ret = 0;

        printk("%s: probe and init hello_device: %s\n", __func__, dev->name);
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));

    ret = request_irq(39, rtc_alarm_handler, 0,  "rtc0-test", NULL);
    if (ret == -1){
        printk("request_irq failed!\n");
        return -1;
    }

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }
    
    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    class_device = device_create(rtc_class, NULL, rtc_dev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(class_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    return 0;
}

static int rtc_driver_remove(struct platform_device *dev)
{
    cdev_del(rtc_chrdev.rtc_cdev);
    unregister_chrdev_region(rtc_chrdev.devno, 1);
    free_irq(39,NULL);
        printk("%s: driver remove: %s\n", __func__, dev->name);

        return 0;
}


static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
    {
        .name        = "rtc-wit0",
        .driver_data = 0,
    },
    {
        .name        = "rtc-wit1",
        .driver_data = 1,
    },
    { }
};

static struct platform_driver rtc_drv = {
    .id_table = arm_rtc_primcell_driver_ids,
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .driver = {
        .name = "rtc-demo",
    }
};

static int __init rtc_driver_init(void)
{
    return platform_driver_register(&rtc_drv);
}

static void __exit rtc_driver_exit(void)
{
    platform_driver_unregister(&rtc_drv);
}

2. match

  struct platform_device_id {
      char name[PLATFORM_NAME_SIZE];
      kernel_ulong_t driver_data;
  };

  static const struct platform_device_id *
  platform_match_id(const struct platform_device_id *id,
            struct platform_device *pdev)
  {
      while (id->name[0]) {
          if (strcmp(pdev->name, id->name) == 0) {
              pdev->id_entry = id;
              return id;
          }
          id++;
      }
      return NULL;
  }

  static int platform_match(struct device *dev, struct device_driver *drv)
  {
      struct platform_device *pdev = to_platform_device(dev);
      struct platform_driver *pdrv = to_platform_driver(drv);
      
      // 1) 使用名称匹配
      /* When driver_override is set, only bind to the matching driver */
      if (pdev->driver_override)
          return !strcmp(pdev->driver_override, drv->name);

      // 2) 使用设备树匹配
      /* Attempt an OF style match first */
      if (of_driver_match_device(dev, drv))
          return 1;

      /* Then try ACPI style match */
      if (acpi_driver_match_device(dev, drv))
          return 1;

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

      /* fall-back to driver name match */
      return (strcmp(pdev->name, drv->name) == 0);
  }

使用id_table

  static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
      {
          .name        = "rtc-wit0",
          .driver_data = 0,
      },
      {
          .name        = "rtc-wit1",
          .driver_data = 1,
      },
      { }
  };

  static struct platform_driver pl031_rtc_drv = {
      .id_table = arm_rtc_primcell_driver_ids,
      .probe    = pl031_driver_probe,
      .remove   = pl031_driver_remove,
      .driver   = {
          .name = "rtc-demo",
      }
  };

3. resource

platform_device 定义各种依赖的资源
platform_driver 获取资源,初始化硬件

资源用 struct resource 描述,可以描述 中断,内存等

  struct resource {
      resource_size_t start;
      resource_size_t end;
      const char *name;
      unsigned long flags;
      unsigned long desc;
      struct resource *parent, *sibling, *child;
  };

driver端使用 platform_get_resource获得资源

  // type : 资源类型
  // index : 目标资源在同类型资源数组中的下标
  struct resource *platform_get_resource(struct platform_device *dev,
                         unsigned int type, unsigned int num);

设备端定义资源

static struct resource rtc_resource[] = {
    [0] = {   // 定义虚拟内存资源,用于寄存器物理地址映射
        .start = 0x10017000,     // 内存起始地址
        .end   = 0x10017000 + 4 * 8,  // 内存结束地址
        .flags = IORESOURCE_MEM,
    },
    [1] = {   // 定义中断号
        .start = 39,
        .end   = 39,
        .flags = IORESOURCE_IRQ,
    },
};

static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

struct platform_device rtc_dev = {
    .name = "rtc-wit0",
    //.name = "rtc-demo",
    .id   = -1,
    .num_resources = 2,
    .resource = rtc_resource,
    .dev  = {
        .release = rtc_device_release,
    },
};

驱动端使用

static int rtc_driver_probe(struct platform_device *dev)
{

    irqreturn_t ret = 0;
    struct resource *res_mem, *res_irq;
    int device_irq = -1;

    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);
    // 获得内存资源
    res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
    regs = (rtc_reg_t *)ioremap(res_mem->start, res_mem->end - res_mem->start);
    
    // 获得中断资源
    //res_irq = platform_get_resource(dev, IORESOURCE_IRQ, 0);
    device_irq = platform_get_irq(dev, 0);
    ret = request_irq(device_irq, rtc_alarm_handler, 0,  "rtc0-test", NULL);
    if (ret == -1){
        printk("request_irq failed!\n");
        return -1;
    }

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }


    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    rtc_device = device_create(rtc_class, NULL,rtc_chrdev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    return 0;
}

4. 使用class抽象接口

中间层抽象出接口

  struct rtc_class_operations {
      int (*set_time)(struct rtc_time *);
      int (*set_alarm)(struct rtc_time *);
  };

  struct rtc_device {
      struct  cdev *rtc_cdev;
      dev_t devno;
      const struct rtc_class_operations *ops;
  };

中间层定义ioctl的CMD,并回调具体驱动的ops

  #define RTC_IOC_MAGIC 'R'
  #define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
  #define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)

  static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
      struct rtc_device *pdev = file->private_data;
      struct rtc_time __user *buf = (struct rtc_time __user *)arg;
      if (_IOC_TYPE(cmd) != RTC_IOC_MAGIC) {
          printk("rtc ioctl: invalid cmd!\n");
          return -EINVAL;
      }

      switch (cmd) {
          case RTC_SET_TIME:
              if (_IOC_DIR(cmd) != _IOC_WRITE) {
                  printk("rtc: invalid ioclt cmd!\n");
                  return -EINVAL;
              }
              if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
                  printk("rtc ioctl: copy_from_user error!\n");
                  return -EFAULT;
              }
              set_rtc_time(pdev, &tm);
              break;
          case RTC_SET_ALARM:
              if (_IOC_DIR(cmd) != _IOC_WRITE) {
                  printk("rtc ioctl: invalid ioclt cmd!\n");
                  return -EINVAL;
              }
              if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
                  printk("rtc ioctl: copy_from_user error!\n");
                  return -EFAULT;
              }
              set_rtc_alarm();
              break;
          default:
              printk("error cmd!\n");
              return -1;
      }

      return 0;
  }

具体的驱动在probe时,绑定自己的 ops

  static int rtc_driver_probe(struct platform_device *dev)
  {

      irqreturn_t ret = 0;
      struct resource *res_mem;
      //struct resource *res_irq;
      int device_irq = -1;

      printk("%s: probe and init hello_device: %s\n", __func__, dev->name);

      res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
      regs = (rtc_reg_t *)ioremap(res_mem->start, res_mem->end - res_mem->start);


      //res_irq = platform_get_resource(dev, IORESOURCE_IRQ, 0);
      device_irq = platform_get_irq(dev, 0);
      ret = request_irq(device_irq, rtc_alarm_handler, 0,  "rtc0-test", NULL);
      if (ret == -1){
          printk("request_irq failed!\n");
          return -1;
      }

      ret = alloc_chrdev_region(&rtc_dev.devno, 0, 1, "rtc-demo");
      if (ret) {
          printk("alloc char device number failed!\n");
          return ret;
      }
      printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_dev.devno), \
                               MINOR(rtc_dev.devno));

      rtc_dev.rtc_cdev = cdev_alloc();
      cdev_init(rtc_dev.rtc_cdev, &rtc_fops);

      ret = cdev_add(rtc_dev.rtc_cdev, rtc_dev.devno, 1);
      if (ret < 0) {
          printk("cdev_add failed..\n");
          return -1;
      }
      else {
          printk("Register char module: rtc success!\n");
      }

      rtc_dev.ops = &pl031_rtc_ops;  // 绑定

      rtc_class = class_create(THIS_MODULE, "rtc-class");
      if (IS_ERR(rtc_class)) {
          printk("class_create failed!\n");
          return -1;
      }

      class_device = device_create(rtc_class, NULL, rtc_dev.devno, NULL, \
                                 "rtc-demo%d", 0);
      if (IS_ERR(class_device)) {
          printk("device_create failed!\n");
          return -1;
      }

      return 0;
  }

具体驱动实现自己的ops

  static int pl031_set_rtc_time(struct rtc_time *t)
  {
      cur_time = rtc_tm_to_time(t);
      regs->RTCLR = cur_time;

      return 0;
  }

  struct rtc_class_operations pl031_rtc_ops = {
      .set_time = pl031_set_rtc_time,
  };

5. 资源管理

为了避免设备模型编程时内存泄漏,提供了类似于内存池的工具

  void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
  void devm_kfree(struct device *dev, const void *p)

使用devm_kmalloc分配的内存会记录到一个链表,当调用devm_kfree时,会遍历链表,使用没有释放的内存。
device在release时会调用 devm_kfree,所以即使用户忘记释放内存,device_unregister时也会释放。

标签:kernel,struct,rtc,driver,dev,platform,device
From: https://www.cnblogs.com/yangxinrui/p/17006328.html

相关文章