很简单的字符设备驱动

这篇文章借鉴了 http://edsionte.com/techblog/archives/2977(主要增加了注释)

 

 

HelloWorld 之后可以练习下这个

字符设备驱动程序:

#include < linux/init.h > //初始化头文件 #include < linux/module.h > //最基本的文件,支持动态添加和卸载模块。Hello World驱动要这一个文件就可以了 #include < linux/types.h > //对一些特殊类型的定义,例如dev_t, off_t, pid_t.其实这些类型大部分都是unsigned int型通过 //一连串的typedef变过来的,只是为了方便阅读。 #include < linux/fs.h > //包含了struct inode 的定义,MINOR、MAJOR的头文件。 #include < asm/uaccess.h > #include < linux/cdev.h > //包含了cdev 结构及相关函数的定义。 /* 模块的版权及作者的声明等附加信息. 该部分使用一系列的宏实现.举例如下: 指定模块的版权,或使用的许可证:MODULE_LICNSE("GPL"),该声明表示使用了GPL许可证. 作者的声明:MODULE_AUTHOR("AUTHOR_NAME"),""内部为作者姓名. */ MODULE_AUTHOR("lufeiop"); MODULE_LICENSE("GPL"); //231应该是没有被使用的主设备号,可用cat /proc/devices命名查看设备号使用情况 #define MYCDEV_MAJOR 231 /*the predefined mycdev's major devno*/ #define MYCDEV_SIZE 100 static int mycdev_open(struct inode *inode, struct file *fp) { return 0; } static int mycdev_release(struct inode *inode, struct file *fp) { return 0; } /* 关于ssize_t: 在<linux/types.h>中:typedef __kernel_ssize_t ssize_t; 继续深入:typedef long __kernel_ssize_t; */ /* 关于size_t: 在<linux/types.h>中:typedef __kernel_size_t size_t; 继续深入:typedef unsigned long __kernel_size_t; */ /* 关于loff_t: 在<linux/types.h>中: typedef __kernel_loff_t loff_t; 继续深入:typedef long long __kernel_loff_t; */ /* 关于__user,暂时没找到详细资料,应该是标识 *buf 属于用户空间 */ /* ssize_t (*read) (struct file * filp, char __user * buffer, size_t size , loff_t * p); (指针参数 filp 为进行读取信息的目标文件,指针参数buffer 为对应放置信息的缓冲区(即用户空间内存地址), 参数size为要读取的信息长度,参数 p 为读的位置相对于文件开头的偏移,在读取信息后,这个指针一般都会移动, 移动的值为要读取信息的长度值) 这个函数用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). */ static ssize_t mycdev_read(struct file *fp, char __user *buf, size_t size, loff_t *pos) { unsigned long p = *pos; unsigned int count = size; int i; char kernel_buf[MYCDEV_SIZE] = "This is mycdev!"; if(p >= MYCDEV_SIZE) //读取的偏移距离超过MYCDEV本身大小 return -1; if(count > MYCDEV_SIZE) //要读取的字符数大于所剩字符,所有要修正count count = MYCDEV_SIZE - p; /* copy_to_user(void __user *to, const void *from, unsigned long n)其功能是将内核空间的内容复制到用户空间 To 目标地址,这个地址是用户空间的地址; From 源地址,这个地址是内核空间的地址; */ if (copy_to_user(buf, kernel_buf, count) != 0) { printk("read error!/n"); return -1; } /* for (i = 0; i < count; i++) { __put_user(i, buf);//write 'i' from kernel space to user space's buf; buf++; } */ printk("edsionte's reader: %d bytes was read.../n", count); return count; } static ssize_t mycdev_write(struct file *fp, const char __user *buf, size_t size, loff_t *pos) { return size; } /*filling the mycdev's file operation interface in the struct file_operations*/ //file_operations结构体定义在头文件linux/fs.h //没有每个项都实现 static const struct file_operations mycdev_fops = { .owner = THIS_MODULE, //指向拥有这个结构的模块的指针 .read = mycdev_read, .write = mycdev_write, .open = mycdev_open, .release = mycdev_release, //当最后一个打开设备的用户进程执行close()系统调用的时候, //内核将调用驱动程序release()函数 }; /*module loading function*/ /* __init位置:include/asm-i386/Init.h   定义:#define __init __attribute__ ((__section__ (".text.init")))   注释:这个标志符和函数声明放在一起,表示gcc编译器在编译的时候需要把这个函数放.text.init section中, 而这个section在内核完成初始化之后,会被释放掉。   举例:asmlinkage void __init start_kernel(void){...} */ //以__init修饰的函数并非程序中显式调用的,而是被放到特定的(代码、数据)段中,在初始化的时候调用。 /* 模块入口函数. 模块入口函数即模块初始化函数,在模块初始化时运行,负责注册模块所提供的,可以被应用程序访问的新功能. 模块入口函数的原型为: static int __init my_init(void). 其中的几个部分含义如下: 1.函数为static形式. 2.返回值为int. 3.__init表示该函数只在初始化期间使用,模块被装载以后,模块初始化函数即被模块装载器丢弃,这样可以释放该函书使用的内存. 4.my_init为入口函数的名称,可以任意命名. 5.void表示入口函数没有参量. */ static int __init mycdev_init(void) { int ret; printk("mycdev module is staring../n"); /* 关于int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); 其中,major是为设备驱动程序向系统申请的主设备号,如果为0则系统为此驱动程序动态地分配一个主设备号。 name是设备名。fops就是前面所说的对各个调用的入口点的说明。此函数返回0表示成功。返回-EINVAL表示申请的主设备号非法,一般来说是主设备号大于系统所允许的最大设备号。返回-EBUSY表示所申请的主设备号正在被其它设备驱动程序使用。 如果是动态分配主设备号成功,此函数将返回所分配的主设备号。如果register_chrdev操作成功, 设备名(参数name)就会出现在/proc/devices文件里。 在成功的向系统注册了设备驱动程序后(调用register_chrdev()成功后),就可以用mknod命令来把设备映射为一个特别文件, 其它程序使用这个设备的时候,只要对此特别文件进行操作就行了。 */ //在Linux2.6版本里面,register_chrdev_region()是register_chrdev()的升级版本。 ret=register_chrdev(MYCDEV_MAJOR,"edsionte_cdev",&mycdev_fops); if(ret<0) { printk("register failed../n"); return 0; } else { printk("register success../n"); } return 0; } /* 模块出口函数. 模块出口函数负责本模块的清理工作,在模块被移除前注销模块接口并向系统返回所有资源,例如函数中动态分配的内存等. 模块出口函数的原型为: static void __exit my__exit(exit). 其中: 1.函数为static形式. 2.函数没有返回值. 3.__exit的含义与入口函数的__init类似,仅用于模块卸载,如果模块被直接嵌入内核,或者内核配置不允许卸载模块,则该函数被直接 抛弃. */ /*module unloading function*/ static void __exit mycdev_exit(void) { printk("mycdev module is leaving../n"); unregister_chrdev(MYCDEV_MAJOR,"edsionte_cdev"); } /* 出口及入口函数的注册部分. 模块的出口及入口函数分别使用宏module_init()及module_exit(). 入口函数注册:module_init(my_init),其中my_init为模块入口函数名. 出口函数注册:module_exit(my_exit),其中my_exit为模块出口函数名. */ module_init(mycdev_init); module_exit(mycdev_exit);

 

下面的链接是我找到的一些关于关键知识点的文章,当然你可以自己google

file_operations 这个讲的比较概略

 

 file_operations结构体详细分析  这个对file_operations中的所有函数进行了讲解

 

关于linux内核和模块编程中常见的宏 包括上面的__init宏

 

Linux设备驱动程序小结

 

 

 

Makefile文件:

obj-m:=mycdev.o PWD:=$(shell pwd) CUR_PATH:=$(shell uname -r) KERNEL_PATH:=/usr/src/linux-headers-$(CUR_PATH) all: make -C $(KERNEL_PATH) M=$(PWD) modules clean: make -C $(KERNEL_PATH) M=$(PWD) clean

 

 

 

用户态测试程序:

#include < stdio.h > #include < sys/types.h > #include < sys/stat.h > #include < fcntl.h > #include < stdlib.h > int main() { int testdev; int i, ret; char buf[15]; /* open函数会调用: static int mycdev_open(struct inode *inode, struct file *fp) */ testdev = open("/dev/mycdev", O_RDWR); if (-1 == testdev) { printf("cannot open file./n"); exit(1); } /* read函数会调用: static ssize_t mycdev_read(struct file *fp, char __user *buf, size_t size, loff_t *pos) */ if (ret = read(testdev, buf, 15) < 15) { //关于15,具体数值要看在mycdev.c中定义了多长的字符串 printf("read error!/n"); exit(1); } printf("%s/n", buf); close(testdev); return 0; }

 

1.make编译mycdev.c文件,并插入到内核(insmod mycdev.ko);
2.通过cat /proc/devices 确认模块已安装成功;
3.创建设备文件结点:sudo mknod /dev/mycdev c 231 0;具体使用方法通过man mknod命令查看;
4.修改设备文件权限:sudo chmod 777 /dev/mycdev;
5.以上成功完成后,编译本用户态测试程序;运行该程序查看结果;
6.通过dmesg查看日志信息;(dmesg | tail -5)

 

你可能感兴趣的:(struct,File,Module,user,Path,linux内核)