本文共 3370 字,大约阅读时间需要 11 分钟。
Preface
前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段!
《linux设备驱动入门篇》:
《linux设备驱动扫盲篇》:
《fedora下的字符设备驱动开发》:
开发一个基本的字符设备驱动
在Linux内核驱动中,字符设备是最基本的设备驱动。
字符设备包括了设备最基本的操作,如打开设备、关闭设备、I/O控制等。
功能:建立一个名为GlobalChar的虚拟设备,设备内部只有一个全局变量供用户操作。设备提供了读函数读取全局变量的值并且返回给用户,写函数把用户设定的值写入全局变量。
①代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | //GlobalCharDev.c #include <linux/fs.h> #include <linux/module.h> #include <asm/uaccess.h> #include <linux/init.h> MODULE_LICENSE( "GPL" ); MODULE_AUTHOR( "mystery" ); #define DEV_NAME "GlobalChar" static ssize_t GlobalRead( struct file *, char *, size_t , loff_t *); static ssize_t GlobalWrite( struct file *, const char *, size_t , loff_t *); static int char_major = 0; static int GlobalData = 0; // "GlobalData" 设备的全局变量 //初始化字符设备驱动的file_operations结构体 static const struct file_operations globalchar_fops = { .read = GlobalRead, .write = GlobalWrite }; //注意分号啊!!! //模块初始化函数 static int __init GlobalChar_init( void ) { int ret; ret = register_chrdev(char_major, DEV_NAME, &globalchar_fops); //注册设备驱动 if (ret < 0) { printk(KERN_ALERT "GlobalChar Reg Fail ! \n" ); } else { printk(KERN_ALERT "GlobalChar Reg Success ! \n" ); char_major = ret; printk(KERN_ALTER "Major = %d \n" ,char_major); } return ret; } //模块卸载函数 static void __exit GlobalChar_exit( void ) { unregister_chrdev(char_major, DEV_NAME); //注销设备驱动 return ; } //设备驱动读函数 static ssize_t GlobalRead( struct file *filp, char *buf, size_t len, loff_t *off) { if (copy_to_user(buf, &GlobalData, sizeof ( int ))) //从内核空间复制GlobalData到用户空间 { return -EFAULT; } return sizeof ( int ); } //设备驱动写函数 static ssize_t GlobalWrite( struct file *filp, const char *buf, size_t len, loff_t *off) { if (copy_from_user(&GlobalData, buf, sizeof ( int ))) //从用户空间复制GlobalData到内核空间 { return -EFAULT; } return sizeof ( int ); } module_init(GlobalChar_init); module_exit(GlobalChar_exit); |
在内核中操作数据要区分数据的来源,对于用户空间的数据要使用copy_from_user()函数复制,使用copy_to_user()函数回写,不能直接操作用户空间的数据,否则会产生内存访问错误。
②编写Makefile
1 2 3 4 5 | obj-m := GlobalCharDev.o KDIR := /lib/modules/ $(shell uname -r) /build SRCPWD := $(shell pwd ) all: make -C $(KDIR) M=$(SRCPWD) modules |
③编译并加载内核模块
④查看内核分配的主设备号
⑤使用mknod命令建立一个设备文件
mknod命令使用-m参数指定GlobalChar设备可以被所有用户访问。
249即上面查询的主设备号。
到这里,我们就已经正确地添加了一个字符设备到内核,下面需要测试一下驱动程序能否正常工作。
测试字符设备驱动
为了测试编写的字符设备是否能正常工作,我们编写一个应用程序测试一下能否正常读写字符设备。
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | //GlobalCharTest.c #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <fcntl.h> #define DEV_NAME "/dev/GlobalChar" int main() { int fd,num; fd = open(DEV_NAME, O_RDWR, S_IRUSR | S_IWUSR); //打开设备文件 if (fd < 0) { printf ( "Open Deivece Fail ! \n" ); return -1; } read(fd, &num, sizeof ( int )); printf ( "The GlobalChar is %d \n" , num); //获取当前设备数值 printf ( "Please input a number written to GlobalChar: " ); scanf ( "%d" , &num); write(fd, &num, sizeof ( int )); //写入新的数值 read(fd, &num, sizeof ( int )); printf ( "The GlobalChar is %d \n" , num); //重新读取设备数值 close(fd); return 0; } |
程序首先使用open函数打开设备文件,然后使用read()函数读取字符设备的值,open()系统调用最终会被解释为字符设备注册的read调用。
测试结果:
从程序输出结果来看,最初从设备得到的数值是0,输入520后写入到字符设备,重新读出的数值也是520,与设置相同,表示设备驱动程序功能正确。
总结
linux字符设备驱动也不过如此嘛,嘿嘿,虽然只实现了read和write两个函数,不过其它函数也大同小异。
重点:实践再实践!!!
本文转自infohacker 51CTO博客,原文链接:http://blog.51cto.com/liucw/1219217
转载地址:http://apgjx.baihongyu.com/