您的位置:首页 > 其它

嵌入式学习笔记——字符设备驱动编写

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”。

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

因为驱动是以模块形式加载到内核中,所以整体来说,驱动就是一个模块,驱动是在模块的基础上添加驱动的代码。所以在编写时可以先编写好一个模块的框架。

#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;
}


这样一个简单的字符设备驱动就编写好了,然后要做的是将其编译好,并再编写一个见到的应用程序,

就可以对它进行一些简单的调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: