美文网首页
3-字符设备框架_创建设备文件

3-字符设备框架_创建设备文件

作者: ibo | 来源:发表于2017-02-03 15:37 被阅读0次
    字符设备框架:
    • 字符设备类包含了同种的字符设备。/sys/class
    • 每种设备都有struct cdev来描述的:
    struct file_operations *ops = &hello_op    
    cdev_init(&cdev,&hello_op)
    dev_t *dev = &devno                             
    cdev_add(&cdev,devno,1)
    
    用户空间
    • 应用程序(用到设备文件名——mknod "/dev/设备文件名" c 主设备号 次设备号)
    内核空间
            系统调用
                |
               vfs  (struct cdev   struct file_operations)
                |
              驱动  设备号 = MKDEV(主设备号,次设备号);
    
    //struct cdev cdev;
    struct cdev *cdev;
    3、初始化file_operations结构体
    struct file_operations op = {
        .owner = THIS_MODULE,//代表了当前模块的意思,不写也没错
        .open = hello_open
    };
    
    加载函数
    • 1、申请设备号——dev_t devno = mar_num << 20 | min_num 或者调用 MKDEV(主设备号,次设备号)
    • 2、注册设备号
      * 静态注册:register_chrdev_region(devno,需要注册的设备个数,设备文件名);
      * 动态注册:alloc_chrdev_region(devno,起始次设备号,次设备号个数,设备文件名);
      * 静态注册的优点是设备启动需要的时间短,但是容易造成和已存在设备号冲突
      * cdev = kzalloc(sizeof(struct cdev),GFP_KERNEL);
    • 4、初始化字符设备 cdev_init(&cdev,&op);
    • 5、添加字符设备到内核中 cdev_add(&cdev,devno,1);
    卸载函数
    • 6、cdev_del(&cdev);
    • 7、注销设备号
      • unregister_chrdev_region(devno,注销的字符设备个数);
    规避版权
    自动创建设备节点 描述时设备节点就是设备文件
    • 在讲自动创建设备节点前,必须先了解另一部分知识点:sysfs这种文件系统默认被挂载到了/sys目录下.
      它的作用是给用户空间和内核空间提供交互的接口——会将内核中的信息(例如设备号)导出到用户空间,
      并且将信息存放到/sys/module/目录名(以模块名称命名的)/uevent文件中.
      在用户空间中有一个小程序叫做udev,每次开机的时候会去遍历所有的uevent文件,提取出设备号然后在
      /dev/目录下创建设备文件,一旦创建完成后udev阻塞,当我们新加载一个驱动时,sysfs会将新加载的驱动
      信息导出到用户空间同时会产生新的uevent文件,一旦产生uevent文件,udev会唤醒。
    struct class *cls;
    cls = class_create(THIS_MODULE,"hello");
    这里的hello代表了类的名字,会在/sys/class目录下出现一个文件夹叫做hello,而hello的下面可能出现很多代表子设备的软连接
    
    创建设备文件接口
    struct device *devs;
    devs = device_create(类结构体指针,父设备的结构体指针,);
    
    struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
    
    • 功能:在/dev目录下自动创建设备文件
    • 参数1:类结构体指针cls
    • 参数2:父设备的device结构体指针,如果没有要写NULL
    • 参数3:设备号
    • 参数4:一些私有数据,如果不用写NULL
    • 参数5:设备文件名,但不一定是完整的设备文件名,如果不使用第六个参数,那么第五个参数就是完整的设备文件名
      • 如果使用第六个参数,那么设备文件名则是由第五和第六两个参数构成的。
    • 参数6:不需要可以省略
    sturct inode
    {
        umode_t i_mode 判断设备类型
        struct cdev *i_cdev 存放的是驱动层中cdev结构体的首地址
    }
    inode结构体是静态的,最初是存放到磁盘上的,第一次打开文件时会被加载到内核中。inode结构体对于一个文件来讲只有一个。
    
    
    struct file {
        const struct file_operations *f_op; 存放了驱动中的file_operations结构体的首地址
    }
    用来描述文件的动态信息的,只要打开一次文件就会出现一个新的struct file结构体
    
    int (*open) (struct inode *, struct file *);
    驱动层中的open完成的功能:打开文件,申请资源,识别次设备号,存放私有数据
    
    用户空间
    • 系统调用接口open 上层的open通过系统调用号来匹配系统调用源码
    内核空间
    • 系统调用
    内核中:
    • 1、
      vi arch/arm/include/uapi/asm/unistd.h
      33 #define __NR_open           (__NR_SYSCALL_BASE+  5)
      5是系统调用号
      
    • 2、
      跟进
      __Nr_open
      713 __SYSCALL(__NR_open, sys_open)
      通过系统调用找到了sys_open 内核中一个函数
      
    • 3、
      SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
      
               return do_sys_open(AT_FDCWD, filename, flags, mode);
                          ||
                          \/
              struct file *f = do_filp_open(dfd, tmp, &op);
                                  ||
                                  \/
                  filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
                              ||
                              \/
                  3181     error = do_last(nd, &path, file, op, &opened, pathname);
                                      ||
                                      \/
                          2882         goto finish_open;
                                              ||
                                              \/
                                  794     error = do_dentry_open(file, open, current_cred());
                                                      ||
                                                      \/
                                           727         open = f->f_op->open;
                                          其中f->f_op->open;    我们在驱动中自己实现的函数接口,比如.open = hello_open
      
    无论应用层还是底层所谓的读写,都是站在应用层的角度来看待的。
    • 读:数据从内核空间流向用户空间
    • 写:相反
    读 :
    • 功能:将内核空间的数据拷贝到用户空间
    • 参数1:第一个参数内核创建。
    • 参数2:用户空间地址
    • 参数3:从内核空间传递给用户空间的数据大小
    • 参数4:偏移量
        size_t read(struct file *,char __user *,size_t ,loff_t *)
        {
            将内核空间的数据拷贝到用户空间 copy_to_user();
        }
    

    static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n);
    
    • 返回值:成功返回0,失败返回错误码
    • 参数1:用户空间的某个地址
    • 参数2: 内核空间的某个地址
    • 参数3: 需要给用户空间拷贝的字节数

        size_t write(struct file *,const char __user *,size_t ,loff_t *)
        {
            将用户空间的数据拷贝到内核空间 copy_from_user();
        }
    

        static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
    
    • 返回值:成功返回0,失败返回错误码
    • 参数1:内核空间的某个地址
    • 参数2:用户空间某个地址
    • 参数3:用户空间给内核空间传递的数据的字节数

    应用层:
    • int ioctl(int fd,int cmd,...);

    • 参数1:文件描述符

    • 参数2: 命令

    • 参数3:如果不需要则省略,如果需要可能是一个普通变量也可能是一个地址

      • 如果参数3需要传递一个结构,这个时候需要传递这个结构的首地址,同时驱动层要想获取数据必须调用copy_from_user或者copy_to_user
      • 如果参数3只是传递一个基本类型的数据,驱动层直接通过第三个参数来接收应用层的数据。
    • 驱动层:

      • long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
      • 参数2:命令
      • 参数3:接收应用程序传过来的数据
      cmd是一个32位的无符号整数,这个整数分成4个部分
                   8位                       8位          2位            14位
         幻数(代表某个设备,通常用字符)    代表了序号      方向     传递的参数的类型大小
      

    虚拟地址 = ioremap(物理地址,物理地址占用字节数)

    练习 :

    Makefile
    ifeq ($(KERNELRELEASE),)
    #KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    KERNELDIR ?= /home/linux/linux-3.14/
    PWD ?= $(shell pwd)
    modules:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
        cp *.ko /rootfs
    app:
        arm-none-linux-gnueabi-gcc test.c -o test
        cp test /rootfs
    clean:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
    .PHONY: modules clean
    else
        obj-m += led.o
    endif
    

    head.h
    #ifndef     __HEAD_H_
    #define     __HEAD_H_
    
    #define     MAGIC   'l'
    
    #define     LED2_ON     _IO(MAGIC,0)
    #define     LED2_OFF    _IO(MAGIC,1)
    #define     LED3_ON     _IO(MAGIC,2)
    #define     LED3_OFF    _IO(MAGIC,3)
    #define     LED4_ON     _IO(MAGIC,4)
    #define     LED4_OFF    _IO(MAGIC,5)
    #define     LED5_ON     _IO(MAGIC,6)
    #define     LED5_OFF    _IO(MAGIC,7)
    #define     LEDALL_ON   _IO(MAGIC,8)
    #define     LEDALL_OFF  _IO(MAGIC,9)
    
    #endif
    

    led.c
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    #include "head.h"
    
    #define     LED_MAJOR   505
    #define     LED_MINOR   0
    #define     LED_NUM     1
    #define     LED_NAME    "ledx"
    #define     CLS_NAME    "led_cls"
    #define     DEV_NAME    "led"
    
    #define     GPX2CON     0x11000c40
    #define     GPX1CON     0x11000c20
    #define     GPF3CON     0x114001e0
    
    volatile unsigned int * gpx2con;
    volatile unsigned int * gpx1con;
    volatile unsigned int * gpf3con;
    volatile unsigned int * gpx2dat;
    volatile unsigned int * gpx1dat;
    volatile unsigned int * gpf3dat;
    
    dev_t devno;
    struct cdev led_cdev;
    struct class * cls;
    int led_open (struct inode *inode, struct file *file)
    {
         printk(" led_open!!!\n");
         return 0;
    }
    int led_release (struct inode *inode, struct file *file)
    {
         printk(" led_release!!!\n");
         return 0;
    }
    long led_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
    {
        printk(" led_ioctl !!!\n");
        switch (cmd)
        {
        case LED2_ON:
            *gpx2dat = (*gpx2dat)|(0x1 << 7);
            *gpx1dat = (*gpx1dat)&~(0x1 << 0);
            *gpf3dat = (*gpf3dat)&~(0x3 << 4);
            break;
        case LED2_OFF:
            *gpx2dat = (*gpx2dat)&~(0x1 << 7);
            *gpx1dat = (*gpx1dat)&~(0x1 << 0);
            *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
            break;
        case LED3_ON:
            *gpx2dat = (*gpx2dat)&~(0x1 << 7);
            *gpx1dat = (*gpx1dat)|(0x1 << 0);
            *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
            break;
        case LED3_OFF:
            *gpx2dat = (*gpx2dat)&~(0x1 << 7);
            *gpx1dat = (*gpx1dat)&~(0x1 << 0);
            *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
            break;
        case LED4_ON:
            *gpx2dat = (*gpx2dat)&~(0x1 << 7);
            *gpx1dat = (*gpx1dat)&~(0x1 << 0);
            *gpf3dat = (*gpf3dat)|(0x1 << 4);
            *gpf3dat = (*gpf3dat)&~(0x1 << 5);  
            break;
        case LED4_OFF:
            *gpx2dat = (*gpx2dat)&~(0x1 << 7);
            *gpx1dat = (*gpx1dat)&~(0x1 << 0);
            *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
            break;
        case LED5_ON:
            *gpx2dat = (*gpx2dat)&~(0x1 << 7);
            *gpx1dat = (*gpx1dat)&~(0x1 << 0);
            *gpf3dat = (*gpf3dat)|(0x1 << 5);
            *gpf3dat = (*gpf3dat)&~(0x1 << 4);  
            break;
        case LED5_OFF:
            *gpx2dat = (*gpx2dat)&~(0x1 << 7);
            *gpx1dat = (*gpx1dat)&~(0x1 << 0);
            *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
            break;
        case LEDALL_ON:
            *gpx2dat = (*gpx2dat)|(0x1 << 7);
            *gpx1dat = (*gpx1dat)|(0x1 << 0);
            *gpf3dat = (*gpf3dat)|(0x3 << 4);  
            break;
        case LEDALL_OFF:
            *gpx2dat = (*gpx2dat)&~(0x1 << 7);
            *gpx1dat = (*gpx1dat)&~(0x1 << 0);
            *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
            break;
        default:
            printk(" fault cmd!!!\n");
            return -EFAULT;
            break;
        }
        return 0;
    }
    struct file_operations led_fops={
         .owner          = THIS_MODULE,
         .open           = led_open,
         .release        = led_release,
         .unlocked_ioctl = led_ioctl,
    };
    
    
    int __init led_init(void)
    {
         int ret;
         printk(" led_init!!!\n");
         devno = MKDEV(LED_MAJOR,LED_MINOR);
         ret = register_chrdev_region(devno,LED_NUM,LED_NAME);
         if (ret < 0)
         {
             printk(" register_chrdev_region fail!!!\n");
             return -EFAULT;
         }
         printk(" register_chrdev_region success!!!\n");
         printk(" major=%d,minor=%d\n",MAJOR(devno),MINOR(devno));
    
        cdev_init(&led_cdev,&led_fops);
        led_cdev.owner = THIS_MODULE;
    
        cdev_add(&led_cdev,devno,LED_NUM);
    
        cls = class_create(THIS_MODULE,CLS_NAME);
        if (IS_ERR(cls))
        {
            printk(" class_create fail!!!\n");
            return -EFAULT;
        }
        device_create(cls,NULL,devno,NULL,DEV_NAME);
    
        gpx2con = ioremap(GPX2CON,0x4);
        if (NULL == gpx2con)
        {
            printk(" gpx2con ioremap fail!!!\n");
            return -EFAULT;
        }
        gpx1con = ioremap(GPX1CON,0x4);
        if (NULL == gpx1con )
        {
            printk(" gpx1con ioremap fail!!!\n");
            return -EFAULT;
        }
        gpf3con = ioremap(GPF3CON,0x4);
        if (NULL == gpf3con)
        {
            printk(" gpf3con ioremap fail!!!\n");
            return -EFAULT;
        }
        gpx2dat = gpx2con + 1;
        //gpx2dat = ioremap(0x1100c44,0x4);
        gpx1dat = gpx1con + 1;
        gpf3dat = gpf3con + 1;
    
    
        *gpx2con = ((*gpx2con)&~(0xf << 28))|(0x1 << 28);
        *gpx1con = ((*gpx1con)&~(0xf <<  0))|(0x1 <<  0);
        *gpf3con = ((*gpf3con)&~(0xff <<16))|(0x11 << 16);
    
        *gpx2dat =(*gpx2dat)|(0x1 << 7);
        *gpx1dat =(*gpx1dat)|(0x1 << 0);
        *gpf3dat =(*gpf3dat)|(0x3 << 4);
    
    
    
        return 0;
    }
    module_init(led_init);
    
    void __exit led_exit(void)
    {
         printk(" led_exit!!!\n");
         iounmap(gpf3con);
         iounmap(gpx1con);
         iounmap(gpx2con);
    
         device_destroy(cls,devno);
         class_destroy(cls);
    
         cdev_del(&led_cdev);
         unregister_chrdev_region(devno,LED_NUM);
    }
    module_exit(led_exit);
    
    MODULE_LICENSE("GPL");
    

    led.c
    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include "head.h"
    
    int main(int argc, char *argv[])
    {
        int fd;
        fd = open("/dev/led",O_RDWR);
        if (fd < 0)
        {
            printf(" open fail!!!\n");
            return -1;
        }
        for(;;)
        {
            ioctl(fd,LED2_ON);
            sleep(1);
            ioctl(fd,LED3_ON);
            sleep(1);
            ioctl(fd,LED4_ON);
            sleep(1);
            ioctl(fd,LED5_ON);
            sleep(1);
            ioctl(fd,LEDALL_ON);
            sleep(1);
        }
        close(fd);
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:3-字符设备框架_创建设备文件

          本文链接:https://www.haomeiwen.com/subject/pjydittx.html