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

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


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


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

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


c文件

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


#include
#include
#include
#include
#include

//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.csdn.net/lizhiguo0532/article/details/6346958
 
  
return 0; }

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







你可能感兴趣的:(Linux内核&驱动)