嵌入式学习笔记——字符设备驱动编写_ywen0756的博客-程序员秘密

技术标签: 嵌入式  

开发环境: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”。

现在说一下该驱动的编程思路和结构。

因为驱动是以模块形式加载到内核中,所以整体来说,驱动就是一个模块,驱动是在模块的基础上添加驱动的代码。所以在编写时可以先编写好一个模块的框架。
#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;
}



这样一个简单的字符设备驱动就编写好了,然后要做的是将其编译好,并再编写一个见到的应用程序,
就可以对它进行一些简单的调用。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ywen0756/article/details/20297199

智能推荐

JavaScript防抖与节流_哇哈哈矿泉水真好喝的博客-程序员秘密

防抖:防止一个函数在一段事件内疯狂执行场景:1.搜索框input事件2.鼠标移动mousemove事件3.视窗大小变化resize事件原理:不希望一个事件在短时间内执行多次,所以设置一个定时器,在一段时间后才执行,在定时器的时间内有触发时间了,就取消上一个事件的定时器,避免上次事件发生,然后又重新设置了一个新的定时器。&lt;script&gt; function deboun...

leetcode笔记刷题(一)1.两数之和 -哈希表_落花逐流水的博客-程序员秘密

之前从没有在leetcode上面刷过题,就来刷一刷,补充一下平时知识点的不足。第一道题:自己本地测试代码:#include &lt;iostream&gt;#include &lt;vector&gt;#include &lt;map&gt;using namespace std;/*注:malloc 是 c 语言中的动态分配内存,result=(int*)malloc(sizeof(int)*2); malloc 函数返回的是 void\* 型,所以要强制类型转换成.

安装pygame[ubuntu18.04lts python3.6]_ubuntu18.04安装pygame_anchor2017的博客-程序员秘密

安装pygame[ubuntu18.04lts python3.6]sudo pip3 install pygame(已经安装了pip) 注:之前按照书里写的,先安装pygame依赖的库,但是之后的pygame还是一直无法正确安装。执行sudo apt-get install python3-dev mercurial都会显示mercurial的版本已经是最新的  ...

Cartographer源码阅读02-local_trajectory_builder.cc(1)-前端匹配_你好哇zzz的博客-程序员秘密

承接上文,本文我们进行local_trajectory_builder2d.cc的阅读,这部分代码主要是激光数据,IMU,里程计等数据加入函数的实现,这部分的功能是实现前端匹配.

ListView解决嵌套冲突3种办法_zr2016mywork的博客-程序员秘密

listview解决冲突ScrollView的办法 三种办法很实用

筛选法查找100以内的素数_Javaxiaobaismc的博客-程序员秘密

解析:筛选法:是指从小到大筛去一个已知素数的所有倍数。例如:根据2,我们筛选去4,6,8,....,98,100等数,然后根据3,我们可以筛选9,15,...99等数(注意此时6、12等数早就被筛去了),由于4被筛去了,下一个用于筛选的素数是5,以此类推,最后剩余的就是100以内的素数。首先定义一个int类型的数组int[] a,初始化整个数组,全部初始化为1,第二步双重循环,从2开始,所...

随便推点

Codeforces 55D_weixin_33788244的博客-程序员秘密

基本的数位DP,注意记录那些状态可以用最小的空间判断出整除性。#include &lt;cstdio&gt;#include &lt;cstring&gt;using namespace std;#define D(x) const int MAX_DIGIT_NUM = 20;int f[MAX_DIGIT_NUM];long long memoi...

VUE----mounted()函数中无法定义初始化样式的原因_VIVI Xiao的博客-程序员秘密

基于vue框架在mounted()函数中,初始化某一个元素的css样式,发现无法成功。解决办法:使用nextTickthis.$nextTick()函数作用是等页面的数据更新完成以后,它再执行内部回调函数中的逻辑参考如下:&lt;div&gt; &lt;span ref="red"&gt;我需要初始化颜色&lt;/span&gt;&lt;/div&gt;new Vue({ ...

流式布局FloawLayout_时间胶嚢的博客-程序员秘密

一、view(组件)和viewGroup(控件)二、view主要函数onMeasure()和onDraw()viewGroup主要函数onMeasure()和onLayout()onMeasure()表示测量大小onLayout()表示子布局onDraw()表示绘制,比如绘制一个三角形等三、两者都有三种构造方法//一般情况 public FlowLayout(Context context) { super(context); }//反射,有xml p...

《架构师修炼记》【MySQL性能优化】之学习总图_JAVA6b的博客-程序员秘密

前&nbsp;&nbsp;言 先搭个架子,以后再慢慢填补。 MySQL性能优化之学习总图

Nodejs HTML抓取与内容提取_weixin_33901926的博客-程序员秘密

为什么80%的码农都做不了架构师?&gt;&gt;&gt; ...

springBoot集成Logback并将日志存储到Oracle数据库_logback-spring.xml 输出到oracle数据库_呦呦呦橙的博客-程序员秘密

参考资料:Logback官方文档:http://logback.qos.ch/manual/appenders.html,DBAppender章节Logback GutHub:https://github.com/qos-ch/logback  网上学习一下,集成还是蛮简单的,主要步骤如下:如需存储到数据库,首先需要准备logback的数据库表 添加相关依赖包 配置logb...

推荐文章

热门文章

相关标签