一、静态申请字符类设备号
- 字符类设备函数在文件"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);
测试结果:
[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);
测试运行后:
[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);
测试结果:
[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);
然后是应用程序:
#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); }
然后是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)
测试结果:
[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!
不知道为什么要设两个驱动设备,按我的写法应该,这两个驱动设备没什么区别。