linux驱动学习笔记1(简单实现open,read,write,ioctl)
2017-08-02 18:33
645 查看
以前开发过程中用过无数次的ioctl函数,一直不知道其内部如何实现的,最近正好在看这方面的资料,并结合网上的代码做了实践,这里记录下。
首先编辑一个驱动模块,取名demo.c
然后编辑一个Makefile,如下:
至于原理,我觉得主要是创建的设备的主次设备号在驱动中被注册到文件系统中的缘故。
另外,实际测试中发现,linux中有默认的open函数,如果demo_fops定义如下,即去掉open,那么,上面的测试程序将会用linux默认的open函数打开。
首先编辑一个驱动模块,取名demo.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/cdev.h> #include <linux/version.h> #include <linux/vmalloc.h> #include <linux/ctype.h> #include <linux/pagemap.h> #include <linux/slab.h> #include "demo.h" MODULE_AUTHOR("Sunny"); MODULE_LICENSE("Dual BSD/GPL"); struct demo_dev *demo_devices; static unsigned char demo_inc = 0; //全局变量,每次只能打开一个设备 static u8 demo_buffer[256]; int demo_open(struct inode *inode, struct file *filp) { struct demo_dev *dev; if (demo_inc > 0) return -ERESTARTSYS; demo_inc++; dev = container_of(inode->i_cdev, struct demo_dev, cdev); filp->private_data = dev; printk("demo_open successfully\n"); return 0; } int demo_release(struct inode *inode, struct file *filp) { demo_inc--; return 0; } ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int result; loff_t pos = *f_pos; //pos: offset if (pos >= 256){ result = 0; goto out; } if (count > (256 - pos)) count = 256 - pos; pos += count; if (copy_to_user(buf, demo_buffer + *f_pos, count)){ count = -EFAULT; goto out; } *f_pos = pos; printk("demo_read successfully\n"); out: return count; } ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval = -ENOMEM; loff_t pos = *f_pos; if (pos > 256) goto out; if (count > (256 - pos)) count = 256 - pos; pos += count; if (copy_from_user(demo_buffer + *f_pos, buf, count)) { retval = -EFAULT; goto out; } *f_pos = pos; retval = count; printk("demo_write successfully\n"); out: return retval; } long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { if (cmd == COMMAND1) { printk("ioctl command 1 successfully\n"); return 0; } if (cmd == COMMAND2) { printk("ioctl command 2 successfully\n"); return 0; } printk("demo_ioctl error\n"); return -EFAULT; } loff_t demo_llseek(struct file *filp, loff_t off, int whence) { loff_t pos; pos = filp->f_pos; switch (whence) { case 0: pos = off; break; case 1: pos += off; break; case 2: default: return -EINVAL; } if ((pos > 256) || (pos < 0)) return -EINVAL; printk("demo_llseek successfully\n"); return filp->f_pos = pos; } struct file_operations demo_fops = { .owner = THIS_MODULE, .llseek = demo_llseek, .read = demo_read, .write = demo_write, .unlocked_ioctl = demo_ioctl, .open = demo_open, .release = demo_release }; void demo_cleanup_module(void) { dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINOR); if (demo_devices) { cdev_del(&demo_devices->cdev); kfree(demo_devices); } unregister_chrdev_region(devno, 1); } /* Init module流程: 1)注册设备号MKDEV; 2)注册设备驱动程序,即初始化cdev结构(嵌入到demo_devices结构中) */ int demo_init_module(void) { int result; dev_t dev = 0; dev = MKDEV(DEMO_MAJOR, DEMO_MINOR); result = register_chrdev_region(dev, 1, "DEMO"); if (result < 0) { printk(KERN_WARNING "DEMO: can't get major %d\n", DEMO_MAJOR); return result; } demo_devices = kmalloc(sizeof(struct demo_dev), GFP_KERNEL); if (!demo_devices) { result = -ENOMEM; goto fail; } memset(demo_devices, 0, sizeof(struct demo_dev)); cdev_init(&demo_devices->cdev, &demo_fops); demo_devices->cdev.owner = THIS_MODULE; demo_devices->cdev.ops = &demo_fops; //将创建的字符设备与file_operations中各函数操作连接起来 result = cdev_add(&demo_devices->cdev, dev, 1); if (result) { printk(KERN_NOTICE "error %d adding demo\n", result); goto fail; } return 0; fail: demo_cleanup_module(); return result; } module_init(demo_init_module); module_exit(demo_cleanup_module);其中demo.h文件如下:
#ifndef _DEMO_H_ #define _DEMO_H_ #include <linux/ioctl.h> /*Macros to help debuging*/ #undef PDEBUG #ifdef DEMO_DEBUG #ifdef __KERNEL__ #define PDEBUG(fmt, args...) printk(KERN_DEBUG "DEMO:" fmt,## args) #else #define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) #endif #else #define PDEBUG(fmt, args...) #endif #define DEMO_MAJOR 224 #define DEMO_MINOR 0 #define COMMAND1 1 #define COMMAND2 2 struct demo_dev { struct cdev cdev; }; ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos); ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); loff_t demo_llseek(struct file *filp, loff_t off, int whence); long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); #endif
然后编辑一个Makefile,如下:
obj-m := demo.o #要生成的模块名 modules-objs:= demo.o #生成这个模块名所需要的目标文件 KDIR := /lib/modules/`uname -r`/build PWD := $(shell pwd) default: make -C $(KDIR) M=$(PWD) modules clean: rm -rf *.o *.cmd *.ko *.mod.c *.order *.symvers运行命令make,生成demo.ko,然后insmod demo.ko,接着创建设备节点mknod /dev/sunny c 224 0,之后便是创建应用测试程序进行验证了,如下:
#include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <linux/rtc.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #define COMMAND1 1 #define COMMAND2 2 int main(int argc, char **argv) { int fd; int i; char data[256] = {0}; int retval; char *name = "/dev/sunny"; if(argc == 2) { name = argv[1]; } fd = open(name, O_RDWR | O_CREAT); if (fd == -1) { perror("open error\n"); exit(-1); } printf("open %s successfully\n", name); retval = write(fd, "yangjin", 7); if (retval == -1) { perror("write error\n"); close(fd); exit(-1); } retval = lseek(fd, 0, 0); if (retval == -1) { perror("lseek error\n"); close(fd); exit(-1); } retval = read(fd, data, 10); if (retval == -1) { perror("read error\n"); close(fd); exit(-1); } printf("read successfully: %s\n", data); retval = ioctl(fd, COMMAND1, 0); if (retval == -1) { perror("ioctl error\n"); close(fd); exit(-1); } printf("ioctl command 1 successfully\n"); close(fd); return 0; }
至于原理,我觉得主要是创建的设备的主次设备号在驱动中被注册到文件系统中的缘故。
另外,实际测试中发现,linux中有默认的open函数,如果demo_fops定义如下,即去掉open,那么,上面的测试程序将会用linux默认的open函数打开。
struct file_operations demo_fops = { .owner = THIS_MODULE, .llseek = demo_llseek, .read = demo_read, .write = demo_write, .unlocked_ioctl = demo_ioctl, //.open = demo_open, .release = demo_release };或者,如果操作的不是/dev/sunny文件,那所有的打开/读/写等操作都是用的linux默认的那套函数。
相关文章推荐
- linux驱动学习3:实现一简单完整驱动(包括open,read,write,ioctl)
- Linux简单设备驱动(2): file_operations的write、read、ioctl驱动及Android应用层开发验证
- linux 系统编程-学习笔记2-文件I/O-open-read
- Linux驱动学习6(ioctl的实现)
- linux 驱动学习笔记04--简单驱动
- linux 设备驱动开发学习笔记(一):最简单的内核模块
- linux字符设备驱动学习笔记(一):简单的字符设备驱动
- linux驱动学习笔记:USB README
- 【Linux开发】linux设备驱动归纳总结(三):2.字符型设备的操作open、close、read、write
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(1)-实现一个简单的bootloader
- Linux 系统函数open、close、read、write、fcntl 简单应用
- 【学习笔记】DM9000裸机驱动(三)之简单ARP协议的实现
- 驱动学习第一讲附(read,write的实现)
- Linux2.6.32驱动笔记(4)ioctl方法解析及mini2440-led驱动实现
- linux设备驱动归纳总结(三):2open.close.read.write
- Linux字符设备驱动程序(二)---------实现open,read,write,llseek函数
- 学习笔记 --- LINUX I2C设备驱动的实现
- Linux 驱动学习笔记05--字符驱动实例,实现一个共享内存设备的驱动
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(5)-实现三个任务切换
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(1)-实现一个简单的bootloader