Linux内核分析(五)----字符设备驱动实现
2015-02-04 14:09
567 查看
Linux内核分析(五)
昨天我们对linux内核的子系统进行简单的认识,今天我们正式进入驱动的开发,我们今后的学习为了避免大家没有硬件的缺陷,我们都会以虚拟的设备为例进行学习,所以大家不必害怕没有硬件的问题。
今天我们会分析到以下内容:
1. 字符设备驱动基础
2. 简单字符设备驱动实现
3. 驱动测试
l 字符设备基础
1. 字符设备描述结构
在linux2.6内核中,使用cdev结构体描述一个字符设备,其定义如下:
上面结构中需要我们进行初始化的有ops和dev,下面我们会对这两个成员进行分析。
注:kobject结构是驱动中很重要的一个结构,由于其复杂性,我们现在不进行介绍,后面会详细介绍。
2. 设备号
1. 何为设备号:cdev结构体中dev成员定义了设备号,而dev_t则为U32类型的也就是32位,其中12位为主设备号,20位为次设备号。我们执行ls –l /dev/可看到下图,其中左边红框为主设备号,右边为次设备号
View Code
l 驱动测试
经过上面的代码我们已经实现了一个简单的字符设备驱动,我们下面进行测试。(应用程序在https://github.com/wrjvszq/myblongs.git 上)
1. 加载内核模块
我们使用 insmod memdev.ko 命令加载内核模块
2. 获取设备号
我们的设备号是动态申请到的,所以我们要通过下面的命令查看设备号
cat /proc/devices
找到我们的设备memdev的设备号
3. 建立设备文件
使用如下命令建立设备文件
mknod /dev/文件名 c 主设备号次设备号
上面命令中文件名为我们在应用程序中打开的文件名
c代表字符设备
主设备号为上一步找到的,我的位249
次设备号非负即可,但不能超过自己所创建的设备数。
比如我的就是 mknod /dev/memdev0 c 249 0
4. 编译应用程序并测试
使用gcc对应用程序进行编译,然后先使用write对设备进行写入,在使用read对设备读取,完成测试。
昨天我们对linux内核的子系统进行简单的认识,今天我们正式进入驱动的开发,我们今后的学习为了避免大家没有硬件的缺陷,我们都会以虚拟的设备为例进行学习,所以大家不必害怕没有硬件的问题。
今天我们会分析到以下内容:
1. 字符设备驱动基础
2. 简单字符设备驱动实现
3. 驱动测试
l 字符设备基础
1. 字符设备描述结构
在linux2.6内核中,使用cdev结构体描述一个字符设备,其定义如下:
struct cdev { struct kobject kobj;/*基于kobject*/ struct module *owner; /*所属模块*/ const struct file_operations *ops; /*设备文件操作函数集*/ struct list_head list; dev_t dev; /*设备号*/ unsigned int count; /*该种类型设备数目*/ };
上面结构中需要我们进行初始化的有ops和dev,下面我们会对这两个成员进行分析。
注:kobject结构是驱动中很重要的一个结构,由于其复杂性,我们现在不进行介绍,后面会详细介绍。
2. 设备号
1. 何为设备号:cdev结构体中dev成员定义了设备号,而dev_t则为U32类型的也就是32位,其中12位为主设备号,20位为次设备号。我们执行ls –l /dev/可看到下图,其中左边红框为主设备号,右边为次设备号
#include<linux/module.h> #include<linux/init.h> #include<linux/cdev.h> #include<linux/fs.h> #include<asm/uaccess.h> #define MEM_SIZE 1024 MODULE_LICENSE("GPL"); struct mem_dev{ struct cdev cdev; int mem[MEM_SIZE];//全局内存4k dev_t devno; }; struct mem_dev my_dev; /*打开设备*/ int mem_open(struct inode *inode, struct file *filp){ int num = MINOR(inode->i_rdev);/*获取次设备号*/ if(num == 0){/*判断为那个设备*/ filp -> private_data = my_dev.mem;/*将设备结构体指针复制给文件私有数据指针*/ } return 0; } /*文件关闭函数*/ int mem_release(struct inode *inode, struct file *filp){ return 0; } static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){ int * pbase = filp -> private_data;/*获取数据地址*/ unsigned long p = *ppos;/*读的偏移*/ unsigned int count = size;/*读数据的大小*/ int ret = 0; if(p >= MEM_SIZE)/*合法性判断*/ return 0; if(count > MEM_SIZE - p)/*读取大小修正*/ count = MEM_SIZE - p; if(copy_to_user(buf,pbase + p,size)){ ret = - EFAULT; }else{ *ppos += count; ret = count; } return ret; } static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos){ unsigned long p = *ppos; unsigned int count = size; int ret = 0; int *pbase = filp -> private_data; if(p >= MEM_SIZE) return 0; if(count > MEM_SIZE - p) count = MEM_SIZE - p; if(copy_from_user(pbase + p,buf,count)){ ret = - EFAULT; }else{ *ppos += count; ret = count; } return ret; } /*seek文件定位函数*/ static loff_t mem_llseek(struct file *filp, loff_t offset, int whence){ loff_t newpos; switch(whence) { case SEEK_SET:/*从文件头开始定位*/ newpos = offset; break; case SEEK_CUR:/*从当前位置开始定位*/ newpos = filp->f_pos + offset; break; case SEEK_END: newpos = MEM_SIZE * sizeof(int)-1 + offset;/*从文件尾开始定位*/ break; default: return -EINVAL; } if ((newpos<0) || (newpos>MEM_SIZE * sizeof(int)))/*检查文件指针移动后位置是否正确*/ return -EINVAL; filp->f_pos = newpos; return newpos; } const struct file_operations mem_ops = { .llseek = mem_llseek, .open = mem_open, .read = mem_read, .write = mem_write, .release = mem_release, }; static int memdev_init(void){ int ret = -1; /*动态分配设备号*/ ret = alloc_chrdev_region(&my_dev.devno,0,1,"memdev"); if (ret >= 0){ cdev_init(&my_dev.cdev,&mem_ops);/*初始化字符设备*/ cdev_add(&my_dev.cdev,my_dev.devno,1);/*添加字符设备*/ } return ret; } static void memdev_exit(void){ cdev_del(&my_dev.cdev); unregister_chrdev_region(my_dev.devno,1); } module_init(memdev_init); module_exit(memdev_exit);
View Code
l 驱动测试
经过上面的代码我们已经实现了一个简单的字符设备驱动,我们下面进行测试。(应用程序在https://github.com/wrjvszq/myblongs.git 上)
1. 加载内核模块
我们使用 insmod memdev.ko 命令加载内核模块
2. 获取设备号
我们的设备号是动态申请到的,所以我们要通过下面的命令查看设备号
cat /proc/devices
找到我们的设备memdev的设备号
3. 建立设备文件
使用如下命令建立设备文件
mknod /dev/文件名 c 主设备号次设备号
上面命令中文件名为我们在应用程序中打开的文件名
c代表字符设备
主设备号为上一步找到的,我的位249
次设备号非负即可,但不能超过自己所创建的设备数。
比如我的就是 mknod /dev/memdev0 c 249 0
4. 编译应用程序并测试
使用gcc对应用程序进行编译,然后先使用write对设备进行写入,在使用read对设备读取,完成测试。
相关文章推荐
- Linux内核分析(五)----字符设备驱动实现
- Linux内核分析(五)----字符设备驱动实现
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】
- Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现
- Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质
- Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质
- Linux驱动学习5(详细分析字符设备驱动信号量实现互斥)
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现
- 用面向对象思想分析linux字符设备驱动开发
- 基于MTD的NANDFLASH设备驱动底层实现原理分析(四)
- linux内核部件分析(九)——设备驱动模型之device-driver
- 基于MTD的NANDFLASH设备驱动底层实现原理分析(六)
- 基于MTD的NANDFLASH设备驱动底层实现原理分析(二)
- Linux内核开发之简单字符设备驱动(上)
- 字符设备驱动浅析(内核代码分析)
- linux内核部件分析(十)——设备驱动模型之class
- 字符设备驱动之笔记-misc设备驱动分析
- 基于MTD的NANDFLASH设备驱动底层实现原理分析(五)
- 字符设备驱动 架构分析