驱动开发学习day2-->字符设备驱动框架

1 字符设备驱动框架

1.1 字符设备
        定义:是指只能一个字符一个字符的读写的设备,不能数据读取设备中的某一段数据,读取数据需要按照先后顺序。字符设备是面向字节流的


    常见的字符设备:鼠标 键盘 串口 控制台 led设备
    块设备:是指可以从设备的任意位置读取一定长度数据的设备。
    常见的块设备:硬盘 磁盘 u盘 光盘 sd卡。。。

1.2 字符设备框架

init:
    {
    申请设备号(静态申请 动态申请 )
    创建一个字符设备--》属性
    初始化字符设备--》方法
    将设备号和字符设备关联起来
    }
exit:
    {
    销毁字符设备
    删除申请的字符设备号
    }

买车:
    1 申请车牌号(静态申请 动态申请)陕A.88888 陕A.44444
    2 买车
    3 车牌号和车关联
卖车:
    1 卖车/销毁
    2 注销车牌号


2 设备号

         定义:设备号是设备在内核中的身份和标志,是内核区分不同设备的唯一标识符,设备号是由主设备号和次设备号构成,主设备号表是一类设备,次设备号表示该类设备中的一个设备。
    
      设备号是一个32bit位的无符号整数,高12位是主设备号,低20位是次设备号

    #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))


    
2.2申请设备号
    

方法:静态申请 动态申请


2.2.1静态申请

 register_chrdev_region
int register_chrdev_region(dev_t from, unsigned count, const char *name)
    作用:静态申请设备号
    from:设备号
    count:子设备个数
    *name:设备名称
    返回值:成功为0

查看设备号:cat /proc/devices--->已注册的设备号
    void unregister_chrdev_region(dev_t from, unsigned count)
    from:设备号
    count:子设备的个数


2.2.2动态申请

  int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned     count,const char *name)
    作用:动态申请设备号
    *dev:设备号指针
    baseminor:子设备的第一个编号
    count:子设备的个数
    *name:设备名称
    返回值:成功为0


2.3创建字符设备


    cdev_alloc
    struct cdev *cdev_alloc(void)
    作用:申请一块存放字符设备的空间
无入参,返回值是一个指向字符设备的指针,在linux内核中用struct cdev来描述一个字符设备。

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


2.4 初始化字符设备

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
    作用:初始化字符设备(给字符设备绑定驱动方法)
    *cdev:指向字符设备的指针
    *fops:指向字符设备操作函数集

    struct file_operations---》描述的是操作字符设备的函数
    {
    struct module *owner;//指向当前模块本身
    int (*open) (struct inode *, struct file *);//打开设备文件
    int (*flush) (struct file *, fl_owner_t id);//
    int (*release) (struct inode *, struct file *);//关闭设备文件
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//读
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        //写
    。。。。。
    }
           struct file_operations stFops=
    {
        .owner=THIS_MODULE;
        .open=HelloOpen,
        .release=HelloClose,
    };

    字符设备驱动框架中使用的三个结构体:
    1 struct file:代表内核中一个打开的文件。系统中每个打开的文件在内核空    间中都有一个关联的struct file。它由内核在打开文件时创建,在文件关闭    后释放。
    2 struct inode:用来记录文件的物理上的信息。它和代表打开文件的struct     file结构是不同的。一个文件可以对应多个struct file结构,但只有一个    inode结构。
    3 struct file_operations:是一个函数指针的集合,定义了能在设备上进行    的所有操作。结构体中的成员指向驱动中的函数,这些函数实现一个特定的    操作,对于不支持的操作保留或置为NULL.

2.5将字符设备和设备号关联起来


    int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    作用:将字符设备和设备号关联起来,将设备添加到内核
    *p:指向字符设备的指针
    dev:设备号
    count:子设备个数
    返回值:成功为0
                
    cdev_del(struct cdev *p)
    作用:删除字符设备
    *p:指向字符设备的指针

测试步骤:
    1 sudo insmod hello.ko
       cat /proc/devices--->查看内核中已注册的所有设备号
    2 sudo mknod /dev/haha0 c 250 0
    3 dmesg |tail
    4 sudo ./test
    5 sudo rmmod hello.ko


3 用户空间数据和内核空间数据拷贝


     用户程序对字符设备的任何操作,都要落实到该设备对应的底层操作函数上。


3.1 内核空间数据---》用户空间


       HelloRead  ---》 read
          ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
          ssize_t  Helloread(struct file *pFile,char __user *to)

unsigned long copy_to_user(void __user *to, const void *from,         unsigned long n)
    作用:将内核空间数据拷贝到用户空间
    *to:用户空间指针
    *from:数据源--》内核空间指针
    n:拷贝的字节数
    返回值:成功为0

3.2  用户空间数据--》内核空间


    write--->HelloWrite 
         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);


    unsigned long copy_from_user(void *to, const void __user *from,             unsigned long n)
    作用:将用户空间的数据拷贝到内核空间
    *to:内核指针
    *from:用户空间指针
    n:期望拷贝的字节数
    返回值:?
 

你可能感兴趣的:(驱动开发,学习,p2p)