• 4412 字符类设备的设备号


    一、静态申请字符类设备号

    • 字符类设备函数在文件"include/linux/fs.h"中
    • 内核提供了三个函数来注册一组字符设备编号,这三个函数分别是
      • register_chrdev_region()
      • alloc_chrdev_region()
      • register_chrdev()
    • register_chrdev_region()是提前知道设备的主次设备号,再去申请设备号
    • alloc_chrdev_region()是动态分配主次设备号
    • register_chrdev() 是老版本的设备号注册方式,只分配主设备号。从设备号在mknod的时候指定。
    • 宏定义MKDEV的头文件"include/linux/kdev.h"
      • 在kdev_t.h头文件中有一个系列设备号处理的宏命令,用于处理各种设备号相关的数据。本期视频只介绍MKDEV,后面使用了其他宏定义再介绍
    • include/linux/cdev.h
      • cdev类型是字符设备描述的结构
      • 其次的设备号必须用"dev_t"类型来描述,高12位为主设备号,低20位为此设备号

    编写编译运行

    • 将视频"16_驱动模块传参数"中的文件"module_param.c"改成为"request_cdev_num.c",静态生成设备号
    • 编写,编译
    • 加载运行
      • 使用命令"cat /proc/devices"查看已经被注册的主设备,设备号9没有被注册
      • insmod /mnt/udisk/request_cdev_num.ko numdev_major=9 numdev_minor=0
      • 使用命令"cat /proc/devices"查看,设备号9被注册为scdev
      • rmmod request_cdev_num numdev_major=9 numdev_minor=0
    #include <linux/init.h>
    #include <linux/module.h>
    
    /* define module_param module_param_array header file */
    #include <linux/moduleparam.h>
    /* define perm's head file*/
    #include <linux/stat.h>
    /* char device register head file */
    #include <linux/fs.h>
    /* MKDEV change device ID type */
    #include <linux/kdev_t.h>
    /* define char device struct */
    #include <linux/cdev.h>
    
    #define DEVICE_NAME "scdev"
    #define DEVICE_MINOR_NUM 2
    #define DEV_MAJOR 0
    #define DEV_MINOR 0
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("TOPEET");
    
    int numdev_major = DEV_MAJOR;
    int numdev_minor = DEV_MINOR;
    
    /* input major device ID */
    module_param(numdev_major, int, S_IRUSR);
    /* input minor device ID */
    module_param(numdev_minor, int, S_IRUSR);
    
    static int hello_init(void)
    {
            int ret;
            dev_t num_dev;
    
            printk(KERN_EMERG "numdev_major is %d!
    ", numdev_major);
            printk(KERN_EMERG "numdev_minor is %d!
    ", numdev_minor);
    
            if(numdev_major) {
                    num_dev = MKDEV(numdev_major, numdev_minor);
                    ret = register_chrdev_region(num_dev, DEVICE_MINOR_NUM, DEVICE_NAME);
            } else {
                    printk(KERN_EMERG "numdev_major %d is failed
    ", numdev_major);
            }
    
            if(ret < 0) {
                    printk(KERN_EMERG "register_chrdev_region req %d is failed
    ", numdev_major);
                    unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
                    return ret;
            }
    
            printk(KERN_EMERG "Hello World enter!
    ");
            return 0;
    }
    
    static void hello_exit(void)
    {
            dev_t num_dev = MKDEV(numdev_major, numdev_minor);
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
            printk(KERN_EMERG "Hello World exit!
    ");
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    register_chrdev_region

    测试结果:

    [root@iTOP-4412]# insmod request_cdev_num.ko numdev_major=9 numdev_minor=0                                 
    [  135.652085] numdev_major is 9!
    [  135.653710] numdev_minor is 0!
    [  135.656810] Hello World enter!
    [root@iTOP-4412]# cat /proc/devices                                                                        
    Character devices:
      1 mem
      4 ttyS
      5 /dev/tty
      5 /dev/console
      5 /dev/ptmx
      9 scdev
     10 misc
     13 input
     21 sg
     29 fb
     81 video4linux
     89 i2c
    108 ppp
    116 alsa
    128 ptm
    136 pts
    153 rc522_test
    166 ttyACM
    180 usb
    188 ttyUSB
    189 usb_device
    204 ttySAC
    216 rfcomm
    243 ump
    244 mali
    249 mt3326-gps
    250 roccat
    251 BaseRemoteCtl
    252 media
    253 ttyGS
    254 rtc
    
    Block devices:
      1 ramdisk
    259 blkext
      7 loop
      8 sd
     65 sd
     66 sd
     67 sd
     68 sd
     69 sd
     70 sd
     71 sd
    128 sd
    129 sd
    130 sd
    131 sd
    132 sd
    133 sd
    134 sd
    135 sd
    179 mmc
    254 device-mapper
    [root@iTOP-4412]# rmmod request_cdev_num numdev_major=9 numdev_minor=0                                     
    [  152.245805] Hello World exit!
    [root@iTOP-4412]# cat /proc/devices                                                                        
    Character devices:
      1 mem
      4 ttyS
      5 /dev/tty
      5 /dev/console
      5 /dev/ptmx
     10 misc
     13 input
     21 sg
     29 fb
     81 video4linux
     89 i2c
    108 ppp
    116 alsa
    128 ptm
    136 pts
    153 rc522_test
    166 ttyACM
    180 usb
    188 ttyUSB
    189 usb_device
    204 ttySAC
    216 rfcomm
    243 ump
    244 mali
    249 mt3326-gps
    250 roccat
    251 BaseRemoteCtl
    252 media
    253 ttyGS
    254 rtc
    
    Block devices:
      1 ramdisk
    259 blkext
      7 loop
      8 sd
     65 sd
     66 sd
     67 sd
     68 sd
     69 sd
     70 sd
     71 sd
    128 sd
    129 sd
    130 sd
    131 sd
    132 sd
    133 sd
    134 sd
    135 sd
    179 mmc
    254 device-mapper
    [root@iTOP-4412]
    测试结果

    二、动态申请字符类设备号

    • 字符设备函数在文件"include/linux/fs.h"中
    • alloc_chrdev_region()是动态分配主次设备号
    • 宏定义MAJOR提取dev_t数据中的主设备号

    编写编译运行

    • 将视频"17"中的文件"request_cdev_num.c改写为"request_ascdev_num.c"动态生成字符设备号
    • 编写,编译
    • 加载运行
      • 使用命令"cat /proc/device"查看
      • 动态加载模块之后再查看设备号

     修改后的代码:

    #include <linux/init.h>
    #include <linux/module.h>
    
    /* define module_param module_param_array header file */
    #include <linux/moduleparam.h>
    /* define perm's head file*/
    #include <linux/stat.h>
    /* char device register head file */
    #include <linux/fs.h>
    /* MKDEV change device ID type */
    #include <linux/kdev_t.h>
    /* define char device struct */
    #include <linux/cdev.h>
    
    #define DEVICE_NAME "scdev"
    #define DEVICE_MINOR_NUM 2
    #define DEV_MAJOR 0
    #define DEV_MINOR 0
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("TOPEET");
    
    int numdev_major = DEV_MAJOR;
    int numdev_minor = DEV_MINOR;
    
    /* input major device ID */
    module_param(numdev_major, int, S_IRUSR);
    /* input minor device ID */
    module_param(numdev_minor, int, S_IRUSR);
    
    static int hello_init(void)
    {
            int ret;
            dev_t num_dev;
    
            printk(KERN_EMERG "numdev_major is %d!
    ", numdev_major);
            printk(KERN_EMERG "numdev_minor is %d!
    ", numdev_minor);
    
            if(numdev_major) {
                    num_dev = MKDEV(numdev_major, numdev_minor);
                    ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
            } else {
                    ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
                    numdev_major = MAJOR(num_dev);
                    printk(KERN_EMERG "register req major number is %d
    ", numdev_major);
            }
    
            if(ret < 0) {
                    printk(KERN_EMERG "register_chrdev_region req %d is failed
    ", numdev_major);
                    unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
                    return ret;
            }
    
            printk(KERN_EMERG "Hello World enter!
    ");
            return 0;
    }
    
    static void hello_exit(void)
    {
            dev_t num_dev = MKDEV(numdev_major, numdev_minor);
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
            printk(KERN_EMERG "Hello World exit!
    ");
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    alloc_chrdev_region代码

    测试运行后:

    [root@iTOP-4412]# insmod request_ascdev_num.ko                                                             
    [ 1335.710205] numdev_major is 0!
    [ 1335.711817] numdev_minor is 0!
    [ 1335.714861] register req major number is 248
    [ 1335.727258] Hello World enter!
    [root@iTOP-4412]# cat /proc/                                                                               
    1/               670/             bus/             mfc/
    11907/           714/             cgroups          misc
    12924/           726/             cmdline          modules
    12925/           731/             consoles         mounts
    12926/           734/             cpu/             net/
    16/              744/             cpuinfo          pagetypeinfo
    2/               745/             crypto           panic_info_dump
    3/               763/             devices          partitions
    339/             767/             diskstats        sched_debug
    341/             838/             driver/          scsi/
    343/             866/             execdomains      self/
    354/             892/             fb               softirqs
    365/             894/             filesystems      stat
    376/             896/             fs/              sys/
    429/             898/             interrupts       sysrq-trigger
    445/             900/             iomem            sysvipc/
    455/             911/             ioports          timer_list
    456/             918/             irq/             tty/
    5/               919/             kallsyms         uid_stat/
    508/             9192/            key-users        uptime
    522/             940/             kmsg             version
    528/             943/             kpagecount       vmallocinfo
    6/               956/             kpageflags       vmstat
    6437/            994/             loadavg          wakelocks
    657/             asound/          locks            zoneinfo
    662/             buddyinfo        meminfo
    [root@iTOP-4412]# cat /proc/devices                                                                        
    Character devices:
      1 mem
      4 ttyS
      5 /dev/tty
      5 /dev/console
      5 /dev/ptmx
     10 misc
     13 input
     21 sg
     29 fb
     81 video4linux
     89 i2c
    108 ppp
    116 alsa
    128 ptm
    136 pts
    153 rc522_test
    166 ttyACM
    180 usb
    188 ttyUSB
    189 usb_device
    204 ttySAC
    216 rfcomm
    243 ump
    244 mali
    248 scdev
    249 mt3326-gps
    250 roccat
    251 BaseRemoteCtl
    252 media
    253 ttyGS
    254 rtc
    测试

    三、注册字符类设备

    • 分配内存空间函数kmalloc
      • 分配连续的虚拟地址,用于小内存分配。在include/linux/slab.h文件中
      • 参数1:申请的内存大小(最大128K)
      • 参数2:GFP_KERNEL,代表优先权,内存不够可以延迟分配
    • 清空内存空间的数据函数memset
      • 可以清空内存空间,也就是全部写为0
      • 参数1:内存地址
      • 参数2:0
      • 参数3:内存长度
    • 字符设备初始化函数cdev_init
      • 在头文件include/linux/cdev.h中
      • 参数1:cdev字符设备文件结构体
      • 参数2:file_operations结构体
      • 注册设备本质是向linux设备文件中添加数据,这些数据需要初始化
    • 字符设备注册函数cdev_add
      • 在头文件include/linux/cdev.h中
      • 参数1:cdev字符设备文件结构体
      • 参数2:设备号
      • 参数3:设备范围大小
      • 向系统注册设备,也就是向linux系统添加数据
    • 卸载设备函数cdev_del
      • 参数1:cdev结构体
      • 移除字符设备
    • 将"18_动态申请字符类设备号"中的文件"request_ascdev_num.c"改写为"register_cdev.c"
    • 编译
    • 测试
      • 通过加载模块后的打印信息,可以观察到驱动加载的过程以及注册设备的反馈信息

     代码:

    #include <linux/init.h>
    #include <linux/module.h>
    
    /* define module_param module_param_array header file */
    #include <linux/moduleparam.h>
    /* define perm's head file*/
    #include <linux/stat.h>
    /* char device register head file */
    #include <linux/fs.h>
    /* MKDEV change device ID type */
    #include <linux/kdev_t.h>
    /* define char device struct */
    #include <linux/cdev.h>
    /* define memroy sapce */
    #include <linux/slab.h>
    
    #define DEVICE_NAME "scdev"
    #define DEVICE_MINOR_NUM 2
    #define DEV_MAJOR 0
    #define DEV_MINOR 0
    #define REGDEV_SIZE 3000
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("TOPEET");
    
    int numdev_major = DEV_MAJOR;
    int numdev_minor = DEV_MINOR;
    
    /* input major device ID */
    module_param(numdev_major, int, S_IRUSR);
    /* input minor device ID */
    module_param(numdev_minor, int, S_IRUSR);
    
    struct reg_dev {
            char *data;
            unsigned long size;
            struct cdev cdev;
    };
    struct reg_dev *my_devices;
    
    struct file_operations my_fops = {
            .owner = THIS_MODULE,
    };
    
    static void reg_init_cdev(struct reg_dev *dev, int index)
    {
            int err;
            int devno = MKDEV(numdev_major, numdev_minor+index);
    
            cdev_init(&dev->cdev, &my_fops);
            dev->cdev.owner = THIS_MODULE;
            dev->cdev.ops = &my_fops;
    
            err = cdev_add(&dev->cdev, devno, 1);
            if(err) {
                    printk(KERN_EMERG "cdev_add %d is fail! %d
    ", index, err);
            } else {
                    printk(KERN_EMERG "cdev_add %d is success!
    ", index);
            }
    }
    
    static int hello_init(void)
    {
            int ret, i;
            dev_t num_dev;
    
            printk(KERN_EMERG "numdev_major is %d!
    ", numdev_major);
            printk(KERN_EMERG "numdev_minor is %d!
    ", numdev_minor);
    
            if(numdev_major) {
                    num_dev = MKDEV(numdev_major, numdev_minor);
                    ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
            } else {
                    ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
                    numdev_major = MAJOR(num_dev);
                    printk(KERN_EMERG "register req major number is %d
    ", numdev_major);
            }
    
            if(ret < 0) {
                    printk(KERN_EMERG "register_chrdev_region req %d is failed
    ", numdev_major);
                    unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
                    return ret;
            }
    
            my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);
            if(!my_devices) {
                    ret = -ENOMEM;
                    printk(KERN_EMERG "kamlloc fialed!
    ");
                    goto fail;
            }
            memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));
            for(i=0;i<DEVICE_MINOR_NUM;i++) {
                    my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);
                    memset(my_devices[i].data, 0, REGDEV_SIZE);          /* data address */
                    reg_init_cdev(&my_devices[i], i);
            }
    
            printk(KERN_EMERG "Hello World enter!
    ");
            return 0;
    
    fail:
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
            return ret;
    }
    
    static void hello_exit(void)
    {
            int i;
            dev_t num_dev = MKDEV(numdev_major, numdev_minor);
            printk(KERN_EMERG "Hello World exit!
    ");
            for(i=0;i<DEVICE_MINOR_NUM;i++) {
                    cdev_del(&my_devices[i].cdev);
            }
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    代码

    测试结果:

    [root@iTOP-4412]# insmod register_cdev.ko                                                                  
    [ 1065.781646] numdev_major is 0!
    [ 1065.783265] numdev_minor is 0!
    [ 1065.786377] register req major number is 248
    [ 1065.790637] cdev_add 0 is success!
    [ 1065.793943] cdev_add 1 is success!
    [ 1065.797392] Hello World enter!
    [root@iTOP-4412]# rmmod register_cdev                                                                      
    [ 1082.904729] Hello World exit
    测试结果

    四、生成字符类设备节点

    前面介绍设备中的模型:bus,device,driver,都是有比较明显确定的定义。bus代表总线,device代表实际的设备和接口,driver代表驱动。

    class是设备类,它是一个抽象的概念,没有对应的实体。它是提供给用户接口相似的一类设备的集合。常见的输入子系统input、usb、串口tty‘块设备block等。

    • 函数class_create创建class类文件
      • 参数1:一般是THIS_MODULE
      • 参数2:设备名称
      • 创建一个设备类,用于设备节点文件的创建
      • 返回一个class结构体变量
    • class结构体变量
      • class是设备驱动模型中通用的设备结构
      • 在头文件include/linux/device.h的280行
    • 老版本:创建设备class函数class_device_create
      • 头文件include/linux/device.h中
      • 参数1:class结构体变量
      • 参数2:父设备NULL
      • 参数3:dev_t设备号
      • 参数4:数据NULL
      • 参数5:设备节点名称
    • 释放设备class函数class_destroy
      • 参数1:myclass
    • 创建设备节点函数device_create
      • 头文件include/linux/device.h中
      • 参数1:设备诶所属于的类
      • 参数2:设备的浮设备,NULL
      • 参数3:设备号
      • 参数4:设备数据,NULL
      • 参数5:设备名称
    • 摧毁设备节点函数device_destroy
      • 参数1:设备所属于的类
      • 参数2:设备号
    • 释放内存函数kfree
      • 参数1:数据指针

     编写编译运行测试

    • 将"19注册字符类设备"中的"register_cdev.c"文件为"create_cnode.c"
    • 编译
    • 加载模块"create_cnode.ko"
      • 使用命令"ls /sys/class/"可以查看到生成的class
      • 使用命令"ls /dev"可以查看到生成的两个设备节点
    • 加载模块的时候还可以使用命令生成设备节点命令,列如
      • mknod dev/test0 c 249 0
      • mknod dev/test1 c 249 1

     运行代码:

    #include <linux/init.h>
    #include <linux/module.h>
    
    /* define module_param module_param_array header file */
    #include <linux/moduleparam.h>
    /* define perm's head file*/
    #include <linux/stat.h>
    /* char device register head file */
    #include <linux/fs.h>
    /* MKDEV change device ID type */
    #include <linux/kdev_t.h>
    /* define char device struct */
    #include <linux/cdev.h>
    /* define memroy sapce */
    #include <linux/slab.h>
    
    /* include device_create class file */
    #include <linux/device.h>
    
    #define DEVICE_NAME "chardevnode"
    #define DEVICE_MINOR_NUM 2
    #define DEV_MAJOR 0
    #define DEV_MINOR 0
    #define REGDEV_SIZE 3000
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("TOPEET");
    
    int numdev_major = DEV_MAJOR;
    int numdev_minor = DEV_MINOR;
    
    /* input major device ID */
    module_param(numdev_major, int, S_IRUSR);
    /* input minor device ID */
    module_param(numdev_minor, int, S_IRUSR);
    
    static struct class *my_class;
    
    struct reg_dev {
            char *data;
            unsigned long size;
            struct cdev cdev;
    };
    struct reg_dev *my_devices;
    
    struct file_operations my_fops = {
            .owner = THIS_MODULE,
    };
    
    static void reg_init_cdev(struct reg_dev *dev, int index)
    {
            int err;
            int devno = MKDEV(numdev_major, numdev_minor+index);
    
            cdev_init(&dev->cdev, &my_fops);
            dev->cdev.owner = THIS_MODULE;
            dev->cdev.ops = &my_fops;
    
            err = cdev_add(&dev->cdev, devno, 1);
            if(err) {
                    printk(KERN_EMERG "cdev_add %d is fail! %d
    ", index, err);
            } else {
                    printk(KERN_EMERG "cdev_add %d is success!
    ", (numdev_minor+index));
            }
    }
    
    static int hello_init(void)
    {
            int ret, i;
            dev_t num_dev;
    
            printk(KERN_EMERG "numdev_major is %d!
    ", numdev_major);
            printk(KERN_EMERG "numdev_minor is %d!
    ", numdev_minor);
    
            if(numdev_major) {
                    num_dev = MKDEV(numdev_major, numdev_minor);
                    ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
            } else {
                    ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
                    numdev_major = MAJOR(num_dev);
                    printk(KERN_EMERG "register req major number is %d
    ", numdev_major);
            }
    
            if(ret < 0) {
                    printk(KERN_EMERG "register_chrdev_region req %d is failed
    ", numdev_major);
                    unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
                    return ret;
            }
    
            my_class = class_create(THIS_MODULE, DEVICE_NAME);
            my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);
            if(!my_devices) {
                    ret = -ENOMEM;
                    printk(KERN_EMERG "kmalloc fialed!
    ");
                    goto fail;
            }
            memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));
            for(i=0;i<DEVICE_MINOR_NUM;i++) {
                    my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);
                    memset(my_devices[i].data, 0, REGDEV_SIZE);          /* data address */
                    /* register device to system */
                    reg_init_cdev(&my_devices[i], i);
                    /* create device node */
                    device_create(my_class, NULL, MKDEV(numdev_major, numdev_minor+i), "NULL", DEVICE_NAME"%d", i);
            }
    
            printk(KERN_EMERG "Hello World enter!
    ");
            return 0;
    
    fail:
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
            return ret;
    }
    
    static void hello_exit(void)
    {
            int i;
            dev_t num_dev = MKDEV(numdev_major, numdev_minor);
            printk(KERN_EMERG "Hello World exit!
    ");
            for(i=0;i<DEVICE_MINOR_NUM;i++) {
                    cdev_del(&my_devices[i].cdev);
                    /* release memory*/
                    device_destroy(my_class, MKDEV(numdev_major, numdev_minor+i));
            }
    
            /* release my class*/
            class_destroy(my_class);
            /* release kfre */
            kfree(my_devices);
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    class_create

    测试结果:

    [root@iTOP-4412]# insmod create_cnode.ko                                                                   
    [12828.419006] numdev_major is 0!
    [12828.420699] numdev_minor is 0!
    [12828.423674] register req major number is 248
    [12828.429116] cdev_add 0 is success!
    [12828.432882] cdev_add 1 is success!
    [12828.436403] Hello World enter!
    
    [root@iTOP-4412]# cat /proc/devices                                                                        
    Character devices:
      1 mem
      4 ttyS
      5 /dev/tty
      5 /dev/console
      5 /dev/ptmx
     10 misc
     13 input
     21 sg
     29 fb
     81 video4linux
     89 i2c
    108 ppp
    116 alsa
    128 ptm
    136 pts
    153 rc522_test
    166 ttyACM
    180 usb
    188 ttyUSB
    189 usb_device
    204 ttySAC
    216 rfcomm
    243 ump
    244 mali
    248 chardevnode
    249 mt3326-gps
    250 roccat
    251 BaseRemoteCtl
    252 media
    253 ttyGS
    254 rtc
    
    [root@iTOP-4412]# rmmod create_cnode                                                                       
    [12439.491484] Hello World exit!
    测试结果

    五、字符驱动

    • file_operations中的函数比较多,选取用的比较多的函数简单介绍,后面的驱动教程中调用了对应的函数,再详细介绍
    • int (*open)(struct inode *, struct file *)
      • 打开函数
    • int (*release)(struct inode *, struct file *)
      • 释放close函数
    • long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long)
      • io控制函数
    • ssize_t (*read)(struct file *, char __user *, size_t, loff_t *)
      • 读函数
    • ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *)
      • 写函数
    • loff_t (*llseek)(struct file *, loff_t, int)
      • 定位函数
    • 如果需要不同的设备节点有不同的功能,只需要在注册设备的时候添加不同的file_operations结构体即可
    • 编写编译运行测试
    • 将驱动视频教程20中的"create_cnode.c"改为“char_driver.c”
    • 修改编译文件Makefile
    • 将驱动视频教程09中"invoke_hello.c"改为"invoke_char_driver.c",编译应用命令如下
      • arm-none-linux-gnueabi-gcc -o invoke_char_driver invoke_char_driver.c -static

     编写代码:

    #include <linux/init.h>
    #include <linux/module.h>
    
    /* define module_param module_param_array header file */
    #include <linux/moduleparam.h>
    /* define perm's head file*/
    #include <linux/stat.h>
    /* char device register head file */
    #include <linux/fs.h>
    /* MKDEV change device ID type */
    #include <linux/kdev_t.h>
    /* define char device struct */
    #include <linux/cdev.h>
    /* define memroy sapce */
    #include <linux/slab.h>
    
    /* include device_create class file */
    #include <linux/device.h>
    
    #define DEVICE_NAME "chardevnode"
    #define DEVICE_MINOR_NUM 2
    #define DEV_MAJOR 0
    #define DEV_MINOR 0
    #define REGDEV_SIZE 3000
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("TOPEET");
    
    int numdev_major = DEV_MAJOR;
    int numdev_minor = DEV_MINOR;
    
    /* input major device ID */
    module_param(numdev_major, int, S_IRUSR);
    /* input minor device ID */
    module_param(numdev_minor, int, S_IRUSR);
    
    static struct class *my_class;
    
    struct reg_dev {
            char *data;
            unsigned long size;
            struct cdev cdev;
    };
    struct reg_dev *my_devices;
    
    /* open */
    static int chardevnode_open(struct inode *inode, struct file *file)
    {
            printk(KERN_EMERG "chardevnode open is success!
    ");
            return 0;
    }
    
    /* close */
    static int chardevnode_release(struct inode *indoe, struct file *file)
    {
            printk(KERN_EMERG "chardevnode release is success!
    ");
            return 0;
    }
    
    /* io control */
    static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
            printk(KERN_EMERG "chardevnode release is success!cmd is %d,arg is %d
    ", cmd, arg);
            return 0;
    }
    
    /* read */
    static ssize_t chardevnode_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
    {
            return 0;
    }
    
    /* write */
    static ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t size, loff_t *ops)
    {
            return 0;
    }
    
    /* lseek */
    static loff_t chardevnode_llseek(struct file *file, loff_t offset, int whence)
    {
            return 0;
    }
    
    struct file_operations my_fops = {
            .owner = THIS_MODULE,
            .open  = chardevnode_open,
            .release = chardevnode_release,
            .unlocked_ioctl = chardevnode_ioctl,
            .read = chardevnode_read,
            .write = chardevnode_write,
            .llseek = chardevnode_llseek,
    };
    
    static void reg_init_cdev(struct reg_dev *dev, int index)
    {
            int err;
            int devno = MKDEV(numdev_major, numdev_minor+index);
    
            cdev_init(&dev->cdev, &my_fops);
            dev->cdev.owner = THIS_MODULE;
            dev->cdev.ops = &my_fops;
    
            err = cdev_add(&dev->cdev, devno, 1);
            if(err) {
                    printk(KERN_EMERG "cdev_add %d is fail! %d
    ", index, err);
            } else {
                    printk(KERN_EMERG "cdev_add %d is success!
    ", (numdev_minor+index));
            }
    }
    
    static int hello_init(void)
    {
            int ret, i;
            dev_t num_dev;
    
            printk(KERN_EMERG "numdev_major is %d!
    ", numdev_major);
            printk(KERN_EMERG "numdev_minor is %d!
    ", numdev_minor);
    
            if(numdev_major) {
                    num_dev = MKDEV(numdev_major, numdev_minor);
                    ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
            } else {
                    ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
                    numdev_major = MAJOR(num_dev);
                    printk(KERN_EMERG "register req major number is %d
    ", numdev_major);
            }
    
            if(ret < 0) {
                    printk(KERN_EMERG "register_chrdev_region req %d is failed
    ", numdev_major);
                    unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
                    return ret;
            }
    
            my_class = class_create(THIS_MODULE, DEVICE_NAME);
            my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);
            if(!my_devices) {
                    ret = -ENOMEM;
                    printk(KERN_EMERG "kmalloc fialed!
    ");
                    goto fail;
            }
            memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));
            for(i=0;i<DEVICE_MINOR_NUM;i++) {
                    my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);
                    memset(my_devices[i].data, 0, REGDEV_SIZE);          /* data address */
                    /* register device to system */
                    reg_init_cdev(&my_devices[i], i);
                    /* create device node */
                    device_create(my_class, NULL, MKDEV(numdev_major, numdev_minor+i), "NULL", DEVICE_NAME"%d", i);
            }
    
            printk(KERN_EMERG "Hello World enter!
    ");
            return 0;
    
    fail:
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
            return ret;
    }
    
    static void hello_exit(void)
    {
            int i;
            dev_t num_dev = MKDEV(numdev_major, numdev_minor);
            printk(KERN_EMERG "Hello World exit!
    ");
            for(i=0;i<DEVICE_MINOR_NUM;i++) {
                    cdev_del(&my_devices[i].cdev);
                    /* release memory*/
                    device_destroy(my_class, MKDEV(numdev_major, numdev_minor+i));
            }
    
            /* release my class*/
            class_destroy(my_class);
            /* release kfre */
            kfree(my_devices);
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    代码

    测试结果:

    [root@iTOP-4412]# insmod char_driver.ko                                                                    
    [ 2135.474056] numdev_major is 0!
    [ 2135.475744] numdev_minor is 0!
    [ 2135.478724] register req major number is 248
    [ 2135.484220] cdev_add 0 is success!
    [ 2135.487909] cdev_add 1 is success!
    [ 2135.500246] Hello World enter!
    [root@iTOP-4412]# ./invok_char                                                                             
    [ 2174.171640] chardevnode open is success!
    [ 2174.174452] chardevnode release is success!cmd is 1,arg is 6
    [ 2174.179828] chardevnode open is success!
    [ 2174.183737] chardevnode release is success!cmd is 1,arg is 6
    [ 2174.189346] chardevnode release is success!
    [ 2174.193511] chardevnode release is success!
    APP open /dev/chardevnode0 success
    APP open /dev/chardevnode0 success
    [root@iTOP-4412]# rmmod char_driver                                                                        
    [ 2199.758801] Hello World exit!
    测试结果

    六、字符类GPIOS,LED驱动编写

    • 将"21_字符驱动"中的文件“char_driver.c”改为“char_driver_leds.c”,添加gpio的初始化和操作函数,卸载模块的时候释放GPIO,将宏定义部分添加到头文件中,简单修改Makefile文件
    • 将“21_字符驱动”中的文件"incoke_char_driver.c"改为文件"invoke_char_gpios.c",并使用main参数传参数操作gpio
    • 应用编译命令
      • arm-none-linux-gnueabi-gcc -o invoke_char_gpios invoke_char_gpios.c -static
    • 操作命令
      • 类似"./invoke_char_gpios 0 1",参数1为命令,参数2为GPIO

    字符驱动程序:

    #include <linux/init.h>
    #include <linux/module.h>
    
    /* define module_param module_param_array header file */
    #include <linux/moduleparam.h>
    /* define perm's head file*/
    #include <linux/stat.h>
    /* char device register head file */
    #include <linux/fs.h>
    /* MKDEV change device ID type */
    #include <linux/kdev_t.h>
    /* define char device struct */
    #include <linux/cdev.h>
    /* define memroy sapce */
    #include <linux/slab.h>
    
    /* include device_create class file */
    #include <linux/device.h>
    
    #include <linux/gpio.h>
    #include <plat/gpio-cfg.h>
    #include <mach/gpio.h>
    #include <mach/gpio-exynos4.h>
    
    #define DEVICE_NAME "chardevnode"
    #define DEVICE_MINOR_NUM 2
    #define DEV_MAJOR 0
    #define DEV_MINOR 0
    #define REGDEV_SIZE 3000
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("TOPEET");
    
    int numdev_major = DEV_MAJOR;
    int numdev_minor = DEV_MINOR;
    
    /* input major device ID */
    module_param(numdev_major, int, S_IRUSR);
    /* input minor device ID */
    module_param(numdev_minor, int, S_IRUSR);
    
    static struct class *my_class;
    
    struct reg_dev {
        char *data;
        unsigned long size;
        struct cdev cdev;
    };
    struct reg_dev *my_devices;
    
    /* open */
    static int chardevnode_open(struct inode *inode, struct file *file)
    {
        printk(KERN_EMERG "chardevnode open is success!
    ");
        return 0;
    }
    
    /* close */
    static int chardevnode_release(struct inode *indoe, struct file *file)
    {
        printk(KERN_EMERG "chardevnode release is success!
    ");
        return 0;
    }
    
    /* io control */
    static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
        printk(KERN_EMERG "chardevnode release is success!cmd is %d,arg is %d
    ", cmd, arg);
    
        if(cmd >1 || arg> 1) {
            printk(KERN_EMERG "cmd and arg is 0 or 1");
            return 0;
        }
        switch(arg) {
            case 0:
                gpio_set_value(EXYNOS4_GPL2(0), cmd);
                break;
            case 1:
                gpio_set_value(EXYNOS4_GPK1(1), cmd);
                break;
            default:
                printk(KERN_EMERG "cmd and arg is 0 or 1");
                break;
        }
    
        return 0;
    }
    
    /* read */
    static ssize_t chardevnode_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
    {
        return 0;
    }
    
    /* write */
    static ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t size, loff_t *ops)
    {
        return 0;
    }
    
    /* lseek */
    static loff_t chardevnode_llseek(struct file *file, loff_t offset, int whence)
    {
        return 0;
    }
    
    struct file_operations my_fops = {
        .owner = THIS_MODULE,
        .open  = chardevnode_open,
        .release = chardevnode_release,
        .unlocked_ioctl = chardevnode_ioctl,
        .read = chardevnode_read,
        .write = chardevnode_write,
        .llseek = chardevnode_llseek,
    };
    
    /* GPL2_0 */
    static int led1_init(void)
    {
        int ret;
        printk(KERN_EMERG "Gpio led 1 init
    ");
    
        ret = gpio_request(EXYNOS4_GPL2(0), "LEDS");
        if(ret < 0) {
            printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed
    ");
            return ret;
        }
    
        s3c_gpio_cfgpin(EXYNOS4_GPL2(0), S3C_GPIO_OUTPUT);
    
        gpio_set_value(EXYNOS4_GPL2(0), 0);
    
        return 0; 
    }
    
    /* GPK1_1 */
    static int led2_init(void)
    {
        int ret;
        printk(KERN_EMERG "GPIO led 2 init
    ");
    
        ret = gpio_request(EXYNOS4_GPK1(1), "LEDS2");
        if(ret < 0) {
            printk(KERN_EMERG "gpio_request EXYNOS4_GPK1(1) fialed
    ");
            return ret;
        }
        s3c_gpio_cfgpin(EXYNOS4_GPK1(1), S3C_GPIO_OUTPUT);
    
        gpio_set_value(EXYNOS4_GPK1(1), 0);
    
        return 0;
    }
    
    static void reg_init_cdev(struct reg_dev *dev, int index)
    {
        int err;
        int devno = MKDEV(numdev_major, numdev_minor+index);
    
        cdev_init(&dev->cdev, &my_fops);
        dev->cdev.owner = THIS_MODULE;
        dev->cdev.ops = &my_fops;
    
        err = cdev_add(&dev->cdev, devno, 1);
        if(err) {
            printk(KERN_EMERG "cdev_add %d is fail! %d
    ", index, err);
        } else {
            printk(KERN_EMERG "cdev_add %d is success!
    ", (numdev_minor+index));
        }
    }
    
    static int hello_init(void)
    {
        int ret, i;
        dev_t num_dev;
        
        printk(KERN_EMERG "numdev_major is %d!
    ", numdev_major);
        printk(KERN_EMERG "numdev_minor is %d!
    ", numdev_minor);
    
        if(numdev_major) {
            num_dev = MKDEV(numdev_major, numdev_minor);
            ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
        } else {
            ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
            numdev_major = MAJOR(num_dev);
            printk(KERN_EMERG "register req major number is %d
    ", numdev_major);
        }
    
        if(ret < 0) {
            printk(KERN_EMERG "register_chrdev_region req %d is failed
    ", numdev_major);
            unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
            return ret;
        }
    
        my_class = class_create(THIS_MODULE, DEVICE_NAME);    
        my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);
        if(!my_devices) {
            ret = -ENOMEM;
            printk(KERN_EMERG "kmalloc fialed!
    ");
            goto fail;
        }
        memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));
        for(i=0;i<DEVICE_MINOR_NUM;i++) {
            my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);        
            memset(my_devices[i].data, 0, REGDEV_SIZE);        /* data address */
            /* register device to system */
            reg_init_cdev(&my_devices[i], i);
            /* create device node */
            device_create(my_class, NULL, MKDEV(numdev_major, numdev_minor+i), "NULL", DEVICE_NAME"%d", i);
        }
    
        led1_init();
        led2_init();
        printk(KERN_EMERG "Hello World enter!
    ");
        return 0;
    
    fail:
        unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
        return ret;
    }
    
    static void hello_exit(void)
    {
        int i;
        dev_t num_dev = MKDEV(numdev_major, numdev_minor);
        printk(KERN_EMERG "Hello World exit!
    ");
        for(i=0;i<DEVICE_MINOR_NUM;i++) {
            cdev_del(&my_devices[i].cdev);
            /* release memory*/
            device_destroy(my_class, MKDEV(numdev_major, numdev_minor+i));
        }
        
        /* release my class*/
        class_destroy(my_class);
        /* release kfre */
        kfree(my_devices);
        unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    char_driver_leds.c

    然后是应用程序:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[])
    {
            int fd0, fd1;
            char *hello_node0 = "/dev/chardevnode0";
            char *hello_node1 = "/dev/chardevnode1";
            if(argc > 2) {
                    printf("please input cmd and arg
    ");
            }
            /* O_RDWR只读打开, O_NDELAY非阻塞方式 */
            fd0 = open(hello_node0, O_RDWR|O_NDELAY);
            if(fd0 < 0) {
                    printf("APP open %s failed
    ", hello_node0);
                    exit(EXIT_FAILURE);
            } else {
                    printf("APP open %s success
    ", hello_node0);
                    ioctl(fd0, atoi(argv[1]), atoi(argv[2]));
            }
            /* O_RDWR只读打开, O_NDELAY非阻塞方式 */
    /*
            fd1 = open(hello_node0, O_RDWR|O_NDELAY);
            if(fd1 < 0) {
                    printf("APP open %s failed
    ", hello_node0);
                    exit(EXIT_FAILURE);
            } else {
                    printf("APP open %s success
    ", hello_node0);
                    ioctl(fd1, 1, 6);
            }
    */
            close(fd0);
            close(fd1);
    }
    invoke_char_driver.c

    然后是makefile:

    TARGET_NAME = char_driver_leds
    APP_NAME = invoke_char_gpios
    obj-m += $(TARGET_NAME).o
    
    KDIR := /home/topeet/chen/kernel-3.0/iTop4412_Kernel_3.0
    
    PWD ?= $(shell pwd)
    
    all:app
            make -C $(KDIR) M=$(PWD) modules
    
    app:$(APP_NAME)
            arm-none-linux-gnueabi-gcc $(APP_NAME).c -o $(APP_NAME) -static
    
    clean:
            rm -rf *.o *.ko *.mod.c *.symvers *.order *.cmd .$(TARGET_NAME)* $(APP_NAME)
    Makefile

    测试结果:

    [root@iTOP-4412]# insmod char_driver_leds.ko                                                               
    [  420.107938] numdev_major is 0!
    [  420.109549] numdev_minor is 0!
    [  420.112677] register req major number is 248
    [  420.125765] cdev_add 0 is success!
    [  420.137424] cdev_add 1 is success!
    [  420.148881] Gpio led 1 init
    [  420.150342] gpio_request EXYNOS4_GPL2(0) failed
    [  420.154743] GPIO led 2 init
    [  420.165167] Hello World enter!
    [root@iTOP-4412]# ./invoke_char_gpios 1 0                                                                  
    please input cmd [  431.050669] chardevnode open is success!
    [  431.054691] chardevnode release is success!cmd is 1,arg is 0
    [  431.060238] chardevnode release is success!
    and arg
    APP open /dev/chardevnode0 success
    [root@iTOP-4412]# ./invoke_char_gpios 1 1                                                                  
    please input cmd [  435.289936] chardevnode open is success!
    [  435.294047] chardevnode release is success!cmd is 1,arg is 1
    [  435.299498] chardevnode release is success!
    and arg
    APP open /dev/chardevnode0 success
    [root@iTOP-4412]# ./invoke_char_gpios 0 0                                                                  
    please input cmd [  440.595232] chardevnode open is success!
    [  440.599237] chardevnode release is success!cmd is 0,arg is 0
    and arg
    APP open /dev/chardevnode0 success
    [  440.609648] chardevnode release is success!
    [root@iTOP-4412]# ./invoke_char_gpios 0 1                                                                  
    please input cmd [  443.313565] chardevnode open is success!
    [  443.317679] chardevnode release is success!cmd is 0,arg is 1
    [  443.323129] chardevnode release is success!
    and arg
    APP open /dev/chardevnode0 success
    
    [root@iTOP-4412]# rmmod char_driver_leds                                                                   
    [  468.722834] Hello World exit!
    测试结果

    不知道为什么要设两个驱动设备,按我的写法应该,这两个驱动设备没什么区别。

  • 相关阅读:
    Java怎样对一个属性设置set或get方法的快捷键
    小程序怎样控制rich-text中的<img>标签自适应
    Java中Arrys数组常用的方法
    Java 怎样实现调用其他方法
    Java保留两位小数
    解决ajax请求跨域
    rand(7) 到rand(10)
    c++生成随机数
    批量该文件名
    正则表达式(=)
  • 原文地址:https://www.cnblogs.com/ch122633/p/9459904.html
Copyright © 2020-2023  润新知