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

开发环境: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;
}



这样一个简单的字符设备驱动就编写好了,然后要做的是将其编译好,并再编写一个见到的应用程序,
就可以对它进行一些简单的调用。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值