======== beckham ======== 相信大家看过了N次下面这篇文章: http://www.lisoleg.net/lisoleg/d ... driver-howto-1.html 引用: 如何编写Linux操作系统下的设备驱动程序 Roy G 序言 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和 思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的 区别.在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是 支持函数少,只能依赖kernel中的 他给的源代码,一是年代久远,内核版本差异太大,二则有些错误不知是否故意为之, 总之是错误一大堆。 源代码如下:(红色即有错之处 ,我后面再贴出自己的) #include <linux/types.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/errno.h> #include <asm/segment.h> unsigned int test_major = 0; static int read_test(struct inode *node,struct file *file, char *buf,int count) //2.4的内核定义的read入口参数不同,请见usr/src/linux-X.XX-X/include/linux/fs.h { int left; if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT ) //少引入了一个定义VERIFY_WRITE头文件<asm/uaccess.h> return -EFAULT; for(left = count ; left > 0 ; left--) { __put_user(1,buf,1); //在2.4内核源码中,__put_user只有两个入口参数 /* * #define put_user(x,ptr) * __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) */ buf++; } return count; } static int write_tibet(struct inode *inode,struct file *file, const char *buf,int count) //错误同read,而且故意把write_test写成write_tibet { return count; } static int open_tibet(struct inode *inode,struct file *file ) { MOD_INC_USE_COUNT; return 0; } static void release_tibet(struct inode *inode,struct file *file ) { MOD_DEC_USE_COUNT; } //新内核的file_operations定义了更多数据类型,因此这样的方式不太好,详见我的程序 struct file_operations test_fops = { NULL, read_test, write_test, NULL, /* test_readdir */ NULL, NULL, /* test_ioctl */ NULL, /* test_mmap */ open_test, release_test, NULL, /* test_fsync */ NULL, /* test_fasync */ /* nothing more, fill with NULLs */ }; int init_module(void) { int result; result = register_chrdev(0, "test", &test_fops); if (result < 0) { printk(KERN_INFO "test: can't get major number/n"); return result; } if (test_major == 0) test_major = result; /* dynamic */ return 0; } void cleanup_module(void) { unregister_chrdev(test_major, "test"); } 我改正后的程序(在rethat9.0,内核版本2.4.20-8下通过): 引用: //========================================= #define __NO_VERSION__ #include <linux/module.h> #include <linux/config.h> #include <linux/version.h> #include <asm/uaccess.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/errno.h> #include <asm/segment.h> unsigned int test_major = 0; static ssize_t read_test(struct file *file,char *buf,size_t count,loff_t *f_pos) { int left; if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT ) return -EFAULT; for(left = count ; left > 0 ; left--) { __put_user(1,buf); buf++; } return count; } static ssize_t write_test(struct file *file, const char *buf, size_t count, loff_t *f_pos) { return count; } static int open_test(struct inode *inode,struct file *file ) { MOD_INC_USE_COUNT; return 0; } static int release_test(struct inode *inode,struct file *file ) { MOD_DEC_USE_COUNT; return 0; } struct file_operations test_fops = { read:read_test, write:write_test, open: open_test, release:release_test }; int init_module(void) { int result; result = register_chrdev(0, "test", &test_fops); if (result < 0) { printk(KERN_INFO "test: can't get major number/n"); return result; } if (test_major == 0) test_major = result; /* dynamic */ return 0; } void cleanup_module(void) { unregister_chrdev(test_major, "test"); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("BECKHAM"); //=================================== 这个程序存为test.c ,它的makefile: CC=gcc MODCFLAGS:=-Wall -DMODULE -D__KERNEL__ -DLINUX -I /usr/src/linux-2.4.20-8/include test.o:test.c $(CC) $(MODCFLAGS) -c test.c (make file的写法见http://bbs.chinaunix.net/forum/viewtopic.php?p=2102868#2102868 存为test.mk 在命令行输入# make -f test.mk 驱动程序已经编译好了,现在把它安装到系统中去。 $ insmod -f chardev.o 如果安装成功,在/proc/devices文件中就可以看到设备test, 并可以看到它的主设备号。 要卸载的话,运行 $ rmmod test 下一步要创建设备文件。 mknod /dev/test c major minor c 是指字符设备,major是主设备号,就是在/proc/devices里看到的。 minor是从设备号,设置成0就可以了。 如: mknod /dev/test c 254 0 我们现在可以通过设备文件来访问我们的驱动程序。写一个小小的测试程序。 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> main() { int testdev; int i; char buf[10]; testdev = open("/dev/test",O_RDWR); if ( testdev == -1 ) { printf("Cann't open file /n"); exit(0); } read(testdev,buf,10); for (i = 0; i < 10;i++) printf("%d/n",buf); close(testdev); } 编译运行,看看是不是打印出全1 ? ========================= 转载注明作者和出处
insmod时的内核版本问题的解决方案(原创)
本篇文章是我在Linux 操作系统上实现内核模块编译平台的笔记,现在记录下来希望对碰到和我一样问题的朋友有帮助。 首先vi hello.c 原程序来自<linux编程白皮书>;: #include <linux/kernel.h>; #include <linux/module.h>; #if CONFIG_MODVERSIONS==1 #define MODVERSIONS #include <linux/modversions.h>; #endif int init_module(void) { printk("Hello,World!/n" ; return 0; } void cleanup_module(void) { printk("Goodbye cruel world /n" ; } 原书给的makefile: CC=gcc MODCFLAGS:=-Wall -DMODULE-D__KERNEL__ -DLINUX hello.o:hello.c /usr/include/linux/version.h $(CC) $(MODCFLAGS) -c hello.c echo insmod echo rmmod echo 然后输入make 命令. 然后insmod hello.o 结果出现问题: hello.o: kernel-module version mismatch hello.o was compiled for kernel version 2.4.20 while this kernel is version 2.4.20-8. 找了不少资料,总算解决了. 只要在makefile中gcc的参数加上-I /usr/src/linux-2.4.20-8/include (注意:linux-X.X.XX-X请根据自己的机子来配,看看/usr/src/下的linux-?就知道是什么了) 于是makefile改为: CC=gcc MODCFLAGS:=-Wall -DMODULE -D__KERNEL__ -DLINUX -I /usr/src/linux-2.4.20-8/include hello.o:hello.c /usr/include/linux/version.h $(CC) $(MODCFLAGS) -c hello.c echo insmod echo rmmod echo 然后insmod hello.o出现了第二个问题: Warning: loading hello.o will taint the kernel: no license See http://www.tux.org/lkml/#export-tainted for information about tainted modules Module hello loaded, with warnings 又是找资料, 于是知道了,在原程序中加入MODULE_LICENSE("GPL" ; 即: #include <linux/kernel.h>; #include <linux/module.h>; #if CONFIG_MODVERSIONS==1 #define MODVERSIONS #include <linux/modversions.h>; #endif int init_module(void) { MODULE_LICENSE("GPL" ; printk("Hello,World!/n" ; return 0; } void cleanup_module(void) { printk("Goodbye cruel world /n" ; } 然后make,insmod 都没问题了. 最后: 下面我们来查看这个模块所执行的显示内容,这些内容是不会在终端显示的。要执行命令: dmesg 来查看。在最下面显示 Hello,World! Goodbye cruel world Hello,World! 。 还有一种方法可以看到这个模块是否加载成功否。 $cat /proc/modules 从中可以看到hello 这个模块,这就是我加载的。 这个文件中的信息分为四列。 第一列:模块名。第二列:模块使用内存的字节数。第三列:模块的当前使用计数。第四列:备注。 好了,我们来卸载这个模块吧。 rmmod hello 再用 dmesg 来查看卸载时发出的信息吧。 ======================== 转载注明作者及出处。