嵌入式学习笔记——字符设备驱动编写
2014-03-02 19:25
267 查看
开发环境:Ubuntu 12.04
开发板:龙芯1B开发板 内核版本是3.0.0
交叉编译工具: gcc-4.3-ls232-softfloat
linux的外设可以分为三类:字符设备(character device)、块设备(block device)和网络接口设备(network interface)。
字符设备是能够像字节流一样被访问的设备,就是说它的读写可以以字节为单位的。
以下是以hello world字符设备驱动程序为例子,该驱动的功能是当驱动模块成功手动加载到内核时,会在内核调试信息中打印出"TEST_MODULE RUNNING!",在成功卸载该驱动后,会在内核调试信息中打印“TEST_MODULE STOP!”;当有程序通过open打开使用该驱动时会显示“I have been open!”;在调用ioctl函数时会打印出“You can use this device”。
现在说一下该驱动的编程思路和结构。
因为驱动是以模块形式加载到内核中,所以整体来说,驱动就是一个模块,驱动是在模块的基础上添加驱动的代码。所以在编写时可以先编写好一个模块的框架。
。
在建立好一个模块的框架后,就可以添加驱动方面的代码。
要创建一个字符设备驱动,为内核所识别。就必须将参数主设备号,次设备号和file_operations结构联系在一起,即要在内核中注册字符设备驱动。这就要调用到以下几个函数和结构:
因为字符设备的注册要在模块加载时完成,所以通常我们都是将注册字符设备的代码写在模块加载函数中。
这样一个简单的字符设备驱动就编写好了,然后要做的是将其编译好,并再编写一个见到的应用程序,
就可以对它进行一些简单的调用。
开发板:龙芯1B开发板 内核版本是3.0.0
交叉编译工具: gcc-4.3-ls232-softfloat
linux的外设可以分为三类:字符设备(character device)、块设备(block device)和网络接口设备(network interface)。
字符设备是能够像字节流一样被访问的设备,就是说它的读写可以以字节为单位的。
以下是以hello world字符设备驱动程序为例子,该驱动的功能是当驱动模块成功手动加载到内核时,会在内核调试信息中打印出"TEST_MODULE RUNNING!",在成功卸载该驱动后,会在内核调试信息中打印“TEST_MODULE STOP!”;当有程序通过open打开使用该驱动时会显示“I have been open!”;在调用ioctl函数时会打印出“You can use this device”。
现在说一下该驱动的编程思路和结构。
因为驱动是以模块形式加载到内核中,所以整体来说,驱动就是一个模块,驱动是在模块的基础上添加驱动的代码。所以在编写时可以先编写好一个模块的框架。
#include <linux/module.h> //所有模块都要使用头文件module.h,此文件必须包含进来。 #include <linux/init.h> //头文件init.h包含了宏_init和_exit,它们允许释放内核占用的内存。 #include <linux/kernel.h> //头文件kernel.h包含了常用的内核函数。 MODULE_LICENSE("GPL"); //模块许可证,如果不添加,在加载到内核的时候就会警告。 MODULE_AUTHOR("ywen"); //模块声明作者,可以不添加 MODULE_DESCRIPTION("test1_module"); //模块描述,可以不添加 static int __init test1_init(void); //模块加载,在加载模块时就是先调用这个函数进行初始化,在驱动编 //写中,一般的驱动初始化代码写在这里,该函数必须有。其中_init是告 //诉内核程序的入口 static void __exit test1_exit(void);//模块卸载,将模块从内核卸载的时候,会调用这个函数。这个函数必须有 //同理_exit的声明是用来告诉内核,模块卸载的出口 module_init(test1_init); //该函数用来告诉内核,模块加载函数的名称。 module_exit(test1_exit); //该函数用来告诉内核,模块卸载函数的名称
。
在建立好一个模块的框架后,就可以添加驱动方面的代码。
要创建一个字符设备驱动,为内核所识别。就必须将参数主设备号,次设备号和file_operations结构联系在一起,即要在内核中注册字符设备驱动。这就要调用到以下几个函数和结构:
#include <linux/fs.h> register_chrdev(unsigned int major,const char *name ,struct file_operations);//用于注册字符设备驱动unregister_chrdev(unsigned int major,const char * name); //用于卸载字符设备驱动 static struct file_operations test1_fops = //用于将驱动的操作和驱动联系起来。(注意test1可以 { //换成自定义的名称,但是_fops貌似不能省去,否则报错 .owner = THIS_MODULE, //这不是一个操作函数,是一个指向这个结构的指针。 .open = test1_open, //这个函数通常是打开设备的第一个操作,用来返回是否 //打开成功 .unlocked_ioctl = test1_ioctl //是系统调用提供了发出设备特定命令的方法,用于操作设备 //在一些新点版本的内核中,声明.ioctl时会报错, //那是因为在新版本的fs.h中没有了.ioctl这个函数,取而代 //之的是.unlocked_ioctl. };
因为字符设备的注册要在模块加载时完成,所以通常我们都是将注册字符设备的代码写在模块加载函数中。
static int __init test1_init(void) { int ret; int value; ret=register_chrdev(test_major,"test1",&test1_fops); if(ret<0) { printk("test1 can't be mount!\n"); return ret; } else { printk("test1 has been mount!\n"); } printk(KERN_INFO"TEST_MODULE RUNNING!\n"); return 0; } static void __exit test1_exit(void) { unregister_chrdev(test_major,"test1"); printk(KERN_INFO"TEST_MODULE STOP!\n"); } //在加载成功后,我们的程序要调用驱动,我们就要为驱动编写接口函数,即在file_operations结构中所定义的函数。 static int test1_open(struct inode *inode,struct file *file) { printk("I have been open!\n"); return 0; } static int test1_ioctl(struct file *file,unsigned int cmd,unsigned long arg) { printk("You can use this device\n"); return 0; }
这样一个简单的字符设备驱动就编写好了,然后要做的是将其编译好,并再编写一个见到的应用程序,
就可以对它进行一些简单的调用。
相关文章推荐
- 嵌入式linux学习笔记4之字符设备驱动
- [Linux驱动]字符设备驱动学习笔记(二)———实例
- 字符设备驱动-学习笔记
- [Linux驱动]字符设备驱动学习笔记(三)———高级
- linux 学习笔记--字符设备驱动相关数据结构
- 嵌入式学习-驱动开发-lesson2-LED字符设备驱动
- 嵌入式Linux驱动学习之路(十)字符设备驱动-my_led
- 字符设备驱动学习笔记(2.6.23)
- 嵌入式Linux字符设备驱动LED驱动编写
- linux字符设备驱动-重新学习-笔记-1
- linux字符设备驱动学习笔记(一):简单的字符设备驱动
- linux字符设备驱动学习笔记2
- linux字符设备驱动学习笔记3
- 学习笔记:创建一个简单字符设备驱动的过程
- 字符设备驱动学习笔记----查询方式取得按键值
- [Linux驱动]字符设备驱动学习笔记(一)
- 学习笔记 --- LINUX USB设备驱动的编写
- 字符设备驱动学习笔记
- 嵌入式Linux驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入
- 关于虚拟字符设备驱动的学习笔记globalmem