linux驱动编写(字符设备编写框架)
2013-01-26 10:53
363 查看
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
上次我们编写了一个简单的字符设备,但是涉及的内容比较少,只有open和read两个函数。今天,我们打算在此基础上扩充一下内容。基本的思路是这样的:(1)编写字符设备下需要处理的各个函数,包括open、release、read、write、ioctl、lseek函数;(2)编写一个用户侧的程序来验证我们编写的驱动函数是否正确。当然,我们编写的代码部分参考了宋宝华先生的《linux设备驱动开发详解》一书,在此说明一下。
在开始今天的内容之前,其实有一些题外话可以和大家分享一下。自从工作以来,我个人一直都有一个观点。那就怎么样利用简单的代码来说明开发中的问题,或者是解释软件中的原理,这是一个很高的学问。有些道理看上去云里雾里说不清楚,其实都可以通过编写代码来验证的。os可以、cpu可以、cache可以、编译器可以、网络协议也可以,很多很多的内容完全可以通过几行代码就可以表达得非常清楚,但是事实上我们并没有这么做。我想原因无非是这么几条,一来授业者对相关知识的学习也是停留在概念上而已,二来我们的学习过于死板和教条、太关注知识、不求实践,三就是学习者自身缺少思考的能力、缺少自我反省的能力、对很多东西不求甚解。对于简单的linux设备,我们完全可以通过这几行代码说清楚问题,免得大家还要苦苦追寻,百思而不得入门。
好了,说了这么多,我们看看现在的驱动代码是怎么修改的把。
希望以上的这段内容对大家有所帮助。
上次我们编写了一个简单的字符设备,但是涉及的内容比较少,只有open和read两个函数。今天,我们打算在此基础上扩充一下内容。基本的思路是这样的:(1)编写字符设备下需要处理的各个函数,包括open、release、read、write、ioctl、lseek函数;(2)编写一个用户侧的程序来验证我们编写的驱动函数是否正确。当然,我们编写的代码部分参考了宋宝华先生的《linux设备驱动开发详解》一书,在此说明一下。
在开始今天的内容之前,其实有一些题外话可以和大家分享一下。自从工作以来,我个人一直都有一个观点。那就怎么样利用简单的代码来说明开发中的问题,或者是解释软件中的原理,这是一个很高的学问。有些道理看上去云里雾里说不清楚,其实都可以通过编写代码来验证的。os可以、cpu可以、cache可以、编译器可以、网络协议也可以,很多很多的内容完全可以通过几行代码就可以表达得非常清楚,但是事实上我们并没有这么做。我想原因无非是这么几条,一来授业者对相关知识的学习也是停留在概念上而已,二来我们的学习过于死板和教条、太关注知识、不求实践,三就是学习者自身缺少思考的能力、缺少自我反省的能力、对很多东西不求甚解。对于简单的linux设备,我们完全可以通过这几行代码说清楚问题,免得大家还要苦苦追寻,百思而不得入门。
好了,说了这么多,我们看看现在的驱动代码是怎么修改的把。
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #define CHRMEM_SIZE 0x1000 #define MEM_CLEAR 0x1 static int chr_major; struct chr_dev { struct cdev cdev; unsigned char mem[CHRMEM_SIZE]; }; struct chr_dev* char_devp; int chr_open(struct inode* inode, struct file* filp) { filp->private_data = char_devp; return 0; } int chr_release(struct inode* inode, struct file* filp) { return 0; } static int chr_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { struct chr_dev* dev = filp->private_data; switch(cmd) { case MEM_CLEAR: memset(dev->mem, 0, CHRMEM_SIZE); break; default: return -EINVAL; } return 0; } static ssize_t chr_read(struct file* filp, char __user* buf, size_t size, loff_t* ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct chr_dev* dev = filp->private_data; if(p >= CHRMEM_SIZE) { return 0; } if(count > CHRMEM_SIZE - p) { return 0; } if(copy_to_user(buf, (void*)(dev->mem + p), count)) { return -EINVAL; } else { *ppos += count; ret = count; } return ret; } static ssize_t chr_write(struct file* filp, const char __user* buf, ssize_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct chr_dev* dev = filp->private_data; if(p >= CHRMEM_SIZE) { return 0; } if(count > CHRMEM_SIZE - p) { count = CHRMEM_SIZE - p; } if(copy_from_user(dev->mem + p, buf, count)) { ret = -EINVAL; } else { *ppos += count; ret = count; } return ret; } static loff_t chr_llseek(struct file* filp, loff_t offset, int orig) { loff_t ret = 0; /* orig can be SEEK_SET, SEEK_CUR, SEEK_END */ switch(orig) { case 0: if(offset < 0) { ret = -EINVAL; break; } if((unsigned int) offset > CHRMEM_SIZE) { ret = -EINVAL; break; } filp->f_pos = (unsigned int) offset; ret = filp->f_pos; break; case 1: if((filp->f_pos + offset) > CHRMEM_SIZE) { ret = -EINVAL; break; } if((filp->f_pos + offset) < 0) { ret = -EINVAL; break; } filp->f_pos += offset; ret = filp->f_pos; break; default: ret = - EINVAL; break; } return ret; } static const struct file_operations chr_ops = { .owner = THIS_MODULE, .llseek = chr_llseek, .read = chr_read, .write = chr_write, .ioctl = chr_ioctl, .open = chr_open, .release = chr_release }; static void chr_setup_cdev(struct chr_dev* dev, int index) { int err; int devno = MKDEV(chr_major, index); cdev_init(&dev->cdev, &chr_ops); dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev, devno, 1); if(err) { printk(KERN_NOTICE "Error happend!\n"); } } int chr_init(void) { int result; dev_t ndev; result = alloc_chrdev_region(&ndev, 0, 1, "chr_dev"); if(result < 0 ) { return result; } printk("chr_init(): major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev)); chr_major = MAJOR(ndev); char_devp = kmalloc(sizeof(struct chr_dev), GFP_KERNEL); if(!char_devp) { result = -ENOMEM; goto final; } memset(char_devp, 0, sizeof(struct chr_dev)); chr_setup_cdev(char_devp, 0); return 0; final: unregister_chrdev_region(ndev, 1); return 0; } void chr_exit() { cdev_del(&char_devp->cdev); kfree(char_devp); unregister_chrdev_region(MKDEV(chr_major, 0), 1); } module_init(chr_init); module_exit(chr_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("feixiaoxing!163.com"); MODULE_DESCRIPTION("A simple device example!");不可否认,我们的代码出现了更多的内容,但是基本框架还是一致的。要是说区别,无非就是我们在原来的基础上添加了新的处理函数而已。说起来,我们对于设备的主要操作也就是这么几种,大家如果对此的概念已经非常成熟了,那么后面的学习就会轻松很多。当然和之前的驱动一样,我们也需要make & insmod char.ko & mknod /dev/chr_dev c 249 0。接下来,为了验证上述的内容是否正确,编写一段简单的测试代码是必不可少的。
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #define MEM_CLEAR 0x01 #define CHAR_DEV_NAME "/dev/chr_dev" int main() { int ret; int fd; int index; char buf[32]; /* open device */ fd = open(CHAR_DEV_NAME, O_RDWR | O_NONBLOCK); if(fd < 0) { printf("open failed!\n"); return -1; } /* set buffer data, which will be stored into device */ for(index = 0; index < 32; index ++) { buf[index] = index; } /* write data */ write(fd, buf, 32); memset(buf, 0, 32); /* read data */ lseek(fd, 0, SEEK_SET); read(fd, buf, 32); for(index = 0; index < 32; index ++) { printf("data[%d] = %d\n", index, buf[index]); } /* reset all data to zero, read it and check whether it is ok */ ioctl(fd, MEM_CLEAR, NULL); lseek(fd, 0, SEEK_SET); read(fd, buf, 32); for(index = 0; index < 32; index ++) { printf("data[%d] = %d\n", index, buf[index]); } close(fd); return 0; }细心的朋友可能发现了,我们在用户侧代码中使用了很多的处理函数,基本上从open、release、read、write、lseek、ioctl全部包括了。测试代码处理的流程也非常简单,首先打开设备,接着写数据,后面就是读取数据,最后利用ioctl清除数据,程序返回。因为代码中包含了注释的内容,在此我们就不过多赘述了。大家慢慢看代码,应该都会了解和明白的。注意,用户测的代码也要在sudo模式下执行。
希望以上的这段内容对大家有所帮助。
相关文章推荐
- Linux字符设备驱动程序的编写框架
- 一步步理解linux字符设备驱动框架(转)
- Linux字符设备驱动框架
- Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动
- Linux字符设备驱动程序的编写框架
- linux驱动学习记录(一)-字符设备框架
- Linux字符设备驱动程序的编写框架
- linux设备驱动--globalmem字符设备框架分析
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
- 字符设备驱动编写流程以及大概框架
- Linux驱动(三)字符设备驱动框架
- Linux字符设备驱动框架
- linux 字符设备驱动框架
- Linux 字符设备驱动框架详细介绍
- Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
- Linux字符设备驱动框架
- Linux字符设备驱动程序的编写框架
- Linux字符设备驱动程序的编写框架
- Linux字符设备驱动编写基本流程