linux驱动学习(字符型设备驱动)

本文介绍了 Linux 下设备驱动的基本概念及实现方式,包括如何通过 mknod 创建设备文件,驱动程序如何通过系统调用与设备交互,以及核心的文件操作结构体 fops 的填充方法。

参考文档:https://segmentfault.com/a/1190000004474802


记录具体操作和出现的问题,以及一些扩展知识。


对于linux ,设备也被看做是一个文件,对于用户来说, 通过系统调用和驱动用操作普通文件的方式打开、读写设备,屏蔽硬件细节。

创建设备:mknod (类似创建文件节点) 这时指定一个主设备号。加载设备驱动,驱动中也有一个设备号,通过该设备号将驱动和设备对应起来。


c文件

代码转自上述参考链接,有少许改动。


#include<asm/uaccess.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/init.h>

//int init_module(void);
//void cleanup_module(void);
static ssize_t device_read(struct file *,char *,size_t,loff_t *);
static ssize_t device_write(struct file *,const char *,size_t,loff_t *);
static int device_open(struct inode *,struct file *);
static int device_release(struct inode *,struct file *);

//name of device
#define DEVICE_NAME "ninafirst"

static int major_version;
static int device_is_open = 0;
static char msg[1024];
static char *pmsg;

//what the device is permitted to do			//驱动程序最重要的就是填充这四个函数。应用程序通过设备号找到设备对应的驱动程序,在入口函数处指定了该操作结构体
							//并确定其文件操作结构体,将控制权交给对应的函数指针
static struct file_operations fops={
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .release = device_release
};

//module register function
static int __init init_chardev(void){							//设备注册函数,也就是驱动程序的入口,一般命名为init_module 
											//这里使用了 __init 宏
        major_version = register_chrdev(0,DEVICE_NAME,&fops);		//major 参数为零,系统将随机分配一个主设备号,成功返回大于零的主设备号
        if(major_version < 0){
                printk(KERN_ALERT "Register failed,error %d.\n",major_version);
                return major_version;
        }
        printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n",DEVICE_NAME,major_version);//提示手动创建设备文件节点,主设备号要与系统分配的主设备号一致,设备名不一定相同
										//后面结果里贴图
        return 0;
}
module_init(init_chardev);

static void __exit exit_chrdev(){
        unregister_chrdev(major_version,DEVICE_NAME);			//该函数并不真的删除设备文件,只是卸载该模块,对于上层而言,就是没有的操作设备的驱动。
}
module_exit(exit_chrdev);

static ssize_t device_read(struct file * filp,char *buffer,size_t length,loff_t *offset){	//以下就是四个操作的具体实现函数
        int bytes=0;
        if(*pmsg == 0){
                return 0;
        }
        while(length && *pmsg){
                put_user(*(pmsg++),buffer++);
                length--;
                bytes++;
        }
return bytes;
}

static ssize_t device_write(struct file *filp,const char *buff,size_t length,loff_t *offset){
        return -EINVAL;					//ENINVAL :向函数传递了无效参数
}

static int device_open(struct inode *inode,struct file *file){
        static int counter=0;
        if(device_is_open){
                return -EBUSY;				//错误异常,存疑
        }
        device_is_open=1;
        sprintf(msg,"Device open for %d times.\n",++counter);
        pmsg = msg;
        try_module_get(THIS_MODULE);
        return 0;
}

static int device_release(struct inode *inode,struct file *file){
        device_is_open = 0;
        module_put(THIS_MODULE);			//结构体struct module在内核中代表一个内核模块,
						//通过insmod(实际执行init_module系统调用)把自己编写的内核模块插入内核时,
						//模块便与一个 struct module结构体相关联,并成为内核的一部分。
						//THIS_MODULE 宏用来引用当前模块的 struct module。引用自http://blog.youkuaiyun.com/lizhiguo0532/article/details/6346958

 
return 0; }

安装模块后,dmesg 查看提示,创建设备,如果设备名不一致仍然能操作成功







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值