002-字符与杂项

基本概念

  • 驱动 driver 开发(个人理解)

    • 👇设备 device (==对象== objcet

      • 设备号(住址号):区域 region ;(家庭住址,唯一性)
      • 注册设备(属性(名字 name )+行为/操作 operations):name | open relaase read write
    • 👇设备节点 Node(==类== class

    • 👆内核kernel

  • 有创建 create 就有销毁 destory

字符设备

  • 字符==设备号==的申请

    • 设备号类型 dev_t (无符号的 32 位整形类型 unsigned int :高12位表示主设备号,低20位表示次设备号)

      1
      2
      3
      4
      5
      6
      /* kernel/include/linux/types.h */
      typedef u32 __kernel_dev_t;
      typedef __kernel_dev_t dev_t;

      /* kernel/include/uapi/asm-generic/int-ll64.h */
      typedef unsigned int __u32;
      1
      2
      3
      4
      5
      6
      7
      //API
      /* kernel/include/linux/kdev_t.h */
      #define MINORBITS 20 /* 次设备号位数 */
      #define MINORMASK ((1U << MINORBITS) - 1) /* 次设备号掩码 */
      #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) /* dev右移20位得到主设备号 */
      #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) /* 与次设备掩码与,得到次设备号 */
      #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) /* MKDEV 宏将主设备号左移20位,然后与次设备号相与, 得到设备号*/
    • 静态申请

      1
      2
      3
      /* kernel/include/linux/fs.h */
      //int register_chrdev_region(设备号, 设备数量, 设备名称)
      int register_chrdev_region(dev_t from, unsigned count, const char *name);
    • 动态申请

      1
      2
      3
      /* kernel/include/linux/fs.h */
      //int alloc_chrdev_region(设备号(数组形式), 次设备号可申请最小值, 设备数量, 设备名称)
      int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
  • 字符==设备==的注册与注销

    • 字符设备cdev

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      /* kernel/include/linux/cdev.h */

      struct cdev {
      struct kobject kobj; //内嵌的内核对象.
      struct module *owner; //该字符设备所在的内核模块的对象指针.
      const struct file_operations *ops; //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.
      struct list_head list; //用来将已经向内核注册的所有字符设备形成链表.
      dev_t dev; //字符设备的设备号,由主设备号和次设备号构成.
      unsigned int count; //隶属于同一主设备号的次设备号的个数.
      };

    • 初始化 init

      1
      2
      3
      /* kernel/include/linux/cdev.h */
      //void cdev_init(字符设备/结构体/对象, 操作/结构体/行为)
      void cdev_init(struct cdev *cdev, const struct file_operations *fops);
    • 添加 add

      1
      2
      3
      /* kernel/include/linux/cdev.h */
      //int cdev_add(字符设备/结构体/对象, 设备号, 设备数量)
      int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    • 删除 del

      1
      2
      3
      /* kernel/include/linux/fs.h */
      //void cdev_del(字符设备/结构体/对象)
      void cdev_del(struct cdev *p)
  • 字符设备的==操作==

    • 文件操作集 file_operations

      1
      2
      3
      4
      5
      6
      7
      8
      /* kernel/include/linux/cdev.h */
      static struct file_operations cdev_fops_test = {
      .owner = THIS_MODULE, //将 owner 字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
      .open = chrdev_open, //将 open 字段指向 chrdev_open(...)函数
      .read = chrdev_read, //将 read 字段指向 chrdev_read(...)函数
      .write = chrdev_write, //将 write 字段指向 chrdev_write(...)函数
      .release = chrdev_release, //将 release 字段指向 chrdev_release(...)函数
      }; //定义 file_operations 结构体类型的变量 cdev_test_ops
    • 操作函数(为应用层操作函数提供接口)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      static int chrdev_open(struct inode *inode, struct file *file)
      {
      printk("This is chrdev_open \n");
      return 0;
      }

      static ssize_t chrdev_read(struct file *file,char __user *buf, size_t size, loff_t *off)
      {
      printk("This is chrdev_read \n");
      return 0;
      }

      static ssize_t chrdev_write(struct file *file,const char __user *buf,size_t size,loff_t *off)
      {
      printk("This is chrdev_write \n");
      return 0;
      }

      static int chrdev_release(struct inode *inode, struct file *file)
      {
      return 0;
      }

设备节点

  • 节点创建

    • 手动创建

      1
      2
      3
      # mknod 节点名称 设备类型 主设备号 从设备号
      # mknod NAME TYPE MAJOR MINOR
      mknod /dev/device_test c 234 0
    • 自动创建

      1
      2
      3
      /* kernel/include/linux/device/class.h */
      //class_create(节点/结构体/类, 节点名称)
      #define class_create(owner, name) ({static struct lock_class_key __key; __class_create(owner, name, &__key);})
  • 节点销毁

    • 销毁

      1
      2
      3
      /* kernel/include/linux/device/class.h */
      //void class_destroy(节点/结构体/类)
      void class_destroy(struct class *cls);

设备与节点

  • 设备:字符设备、块设备、网络设备

  • 节点:类

  • 关联

    • 设备创建

      1
      2
      3
      /* kernel/include/linux/device.h */
      //device_create(节点/结构体/类, 该设备的父设备或NULL, 设备号, 被添加到该设备回调的数据或NULL, 设备节点名称)
      device_create( (struct class *cls, struct device *parent, dev_t devt,void *drvdata, const char *fmt);
    • 设备销毁

      1
      2
      3
      /* kernel/include/linux/device.h */
      //device_create(节点/结构体/类, 设备号)
      void device_destroy(struct class *cls, dev_t devt);

模板记住

  • 字符驱动(记住顺序)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /* 头文件 */

    /* 1.定义字符设备结构体变量-对象 */
    /* 2.定义设备号-家庭住址/唯一标识ID */
    /* 3.定义file_operations结构体类型变量-操作 */

    /* 1.定义设备节点-类 */

    /* 4.实现file_operations中的操作函数集-请在3之前声明/放在3之前 */

    /* 5.模块加载 */
    /* 5.模块卸载 */

    /* 参数传入(可选) */
    /* 符号导出(可选) */
    /* 开源协议(可选) */
    /* 作者信息(可选) */
  • 杂项驱动

    • 固定主设备号10
    • 无需设置设备节点