linux驱动学习笔记1(简单实现open,read,write,ioctl)
来源:互联网 发布:himall 2.6 完整源码 编辑:程序博客网 时间:2024/06/11 21:18
以前开发过程中用过无数次的ioctl函数,一直不知道其内部如何实现的,最近正好在看这方面的资料,并结合网上的代码做了实践,这里记录下。
首先编辑一个驱动模块,取名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`/buildPWD := $(shell pwd)default:make -C $(KDIR) M=$(PWD) modulesclean: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默认的那套函数。
阅读全文
0 0
- linux驱动学习笔记1(简单实现open,read,write,ioctl)
- linux驱动学习3:实现一简单完整驱动(包括open,read,write,ioctl)
- Linux简单设备驱动(2): file_operations的write、read、ioctl驱动及Android应用层开发验证
- 驱动学习第一讲附(read,write的实现)
- linux设备驱动归纳总结(三):2open.close.read.write
- linux设备驱动归纳总结(三):2open.close.read.write
- linux设备驱动归纳总结(三): 2 open.close.read.write
- linux设备驱动归纳总结(三):2open.close.read.write
- linux设备驱动归纳总结(三):2open.close.read.write
- linux设备驱动归纳总结(三):2open.close.read.write
- linux设备驱动归纳总结(三):2open.close.read.write
- linux编程学习6-文件操作之用open、read、write实现文件复制拷贝功能
- Linux驱动学习6(ioctl的实现)
- 应用层open(read、write、close)如何调用驱动open(read、write、close)函数的?
- 【Linux开发】linux设备驱动归纳总结(三):2.字符型设备的操作open、close、read、write
- Linux 文件IO函数(open/close/read/write/lseek)
- linux下文件的读写操作( open read write)
- linux下文件的读写操作(open read write)
- Android EditText限制输入字符的5种实现方式
- php中pdo扩展安装
- 点云匹配和ICP算法概述
- 【Linux】TCP的四种定时器介绍
- 反演变换
- linux驱动学习笔记1(简单实现open,read,write,ioctl)
- hdu 2112 HDU Today
- 获取拼音首字母
- Qt: windeployqt命令使用
- 理解word2vec的训练过程
- [BZOJ 2160] 拉拉队排练 Manacher+贪心
- More is better
- pandas入门——数据分组
- ibatis打印sql日志的方法