关于字符设备驱动

 一.  使用一个结构 struct cdev 描述字符设备

struct cdev {
            struct kobject kobj;  //文件系统相关的sysfs---4 一般由系统来管理,不需要我们自己添加
            struct module *owner; //THIS_MODULE ---用于模块计数
            const struct file_operations *ops;  //操作方法集 ====>操作的是硬件, 向下操作
            struct list_head list;   //用于管理字符设备的链表
            dev_t dev;               //设备号  ==== 把软件和硬件结合起来  ===>主设备号<<20+次设备号
            unsigned int count;      //隶属于同一主设备号的次设备号的个数
        };

这个结构中的 struct file_operations 成员是操作字符设备的方法集,这个结构中包含的都是函数指针,是在驱动程序中自己实现操作底层硬件的接口。

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);
    
}; // 这里只是贴出部分

 

二.  关于设备号 是通过一个宏制作的 如下图所示:

设备号包含主设备号+此设备号;主设备号用于描述是哪一类设备,次设备号描述具体哪个设备;设备号 devnum = MKDEV(主设备号, 次设备号) 

#define MINORBITS      20
#define MINORMASK     ((1U << MINORBITS) - 1)
#define MAJOR(dev)   ((unsigned int) ((dev) >> MINORBITS))  // 用来获取次设备号的
#define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))   // 用来获取次设备号的
#define MKDEV(ma,mi)  (((ma) << MINORBITS) | (mi))       // 用来制作设备号的

可以看出使用 4 字节 32 bit 描述一个设备号 高12 bit 描述主设备号,低20 bit 描述次设备号

 

 

三。 字符设备的操作流程:

  1. 自己指定静态的主设备号,但是不一定申请成功

    (1)定义一个字符设备:struct cdev *cdev;

    (2)为字符设备分配空间:cdev_alloc(void);

    (3)初始化字符设备对象(对硬件操作的方法集): cdev_init(struct cdev *, const struct file_operations *);

    (4)向内核申请一个设备号:register_chrdev_region(dev_t from, unsigned count, const char * name);

    (5)添加当前的字符设备到内核中:cdev_add(struct cdev *p, dev_t dev , unsigned count);

    (6)卸载字符设备对象:

      a)  cdev_del(struct cdev *);   删除设备

      b)  unregister_chrdev_region(dev_t from, unsigned count);  // 释放申请的设备号

      设备号是固定的:查看设备号:【 cat /proc/devices 

经过上面的 步骤 操作就可以在内核中注册一个设备并申请设备号,供这个模块使用;要想在应用层使用这个设备还需要,创建这个设备的节点(应用层操作的文件)。

需要手动创建:

在应用层只需要打开 【 /dev/mychardev0 】这个设备就可以实现操作底层了,操作底层的具体实现就这与这个设备关联的驱动程序中实现的

 

2. 动态获取主设备号,不用手动创建设备节点(mknod )

 (1) // 动态获取主设备号,并注册到内核中

  static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops); 

    参数: major :主设备号,填 0 时表示自动获取,不是 0 表示静态申请自己指定设备号,静态申请时成功返回 0

        name:告诉内核这个设备的名称,在 /proc/devices 下查看设备的名字

          fops :就是上面说的文件操作方法集,在驱动程序中重新实现,操作硬件,然后在应用层直接调用相应的IO 操作函数即可。

    返回值: 动态:返回值是动态申请到的设备号

        静态;成功返回0;负值和错误码表示失败

 (2)创建一个目录 

  

  

   这是一个宏函数参数:owner :这是一个宏,推向编译模块时自动创建的__thismodule 

            clsname:创建在  /sys/class/clsname  目录下

          返回值: struct class * 的结构指针

 

  (2)创建设备节点: 

      struct device *device_create(struct class *class,  struct device *parent,dev_t devt,  void *drvdata,  const char *fmt...)

      参数: class :指向应该注册此设备的 struct class 的指针,也就是上面的 class_create 的返回值

         parent:指向此新设备的父结构设备的指针(如果有的话)

            devt :添加的字符设备的设备号

         drvdata :要添加到回调设备中的数据

         fmt :用于设备名称的字符串,也就是设备节点的名称,在应用层操作的设备节点 

      如:

  过程:在加载模块时向内核申请主设备号,并注册到内核中,这个步骤完成了(a)定义并分配字符设备的空间;(b)向内核申请设备号,并添加到内核中;

    接下来就是为创建的设备节点创建一个目录,最后就是创建设备节点(如: /dev/Demochar0);在这 3 个步骤中,只要后面其中一个步骤出错就要释放上面申请的资源。

 

 

  3. 对于注册设备号,创建目录,创建设备节点,在卸载设备时就要相应的释放设备号,删除目录和删除设备节点

  (1)删除设备节点 :void device_destroy(struct class *class, dev_t devt)

      参数: class:模块入口时创建的目录

          devt: 指定设备的设备号(这个设备号包含主设备号和次设备号)

  (2)删除目录:void class_destroy(struct class *cls)

        参数: cls : 模块入口时创建的目录,

  (3)释放设备号等:static inline void unregister_chrdev(unsigned int major, const char *name)

        参数:major :删除设备的主设备号(通过 MKDEV() 制作)

            name : 注册设备号时,传递给内核的名字,要释放这个资源

 

     对于模块的入口主要是初始化一些操作,模块的处出口主要是把入口申请的资源释放

 

posted on 2019-07-06 16:40  管理员D  阅读(310)  评论(0编辑  收藏  举报

导航