ok6410学习笔记(4.ioctl字符驱动)
2013-05-23 11:11
246 查看
写了几篇文章发现,自己的总结方式有些问题进行了如下调整:
1.对于驱动的学习应该注重的是驱动的结构,module_init里面的结构,read,write,ioctl里面的结构,日后的文章会详细结构部分。
2.对于知识点的总结,应该加强对函数的总结,函数是一个很容易忘记的东西(因为不常用)。
2.ioctl和read,write在用途上有什么本质区别,等到学到硬件控制再回头看看。
_IOW(type,nr.datatype) _IOWR(type,nr.datatype) type是幻数
2.register_chrdev_region静态申请设备号 alloc_chrdev_region动态申请设备号 unregister_chrdev_region注销设备号
3.cdev_init()设备注册初始化 cdev_add()设备注册添加 cdev_del()设备注销
4.在2.6.36以后的内核中没有了int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 只剩下long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);记住参数少了struct inode *
5.判断命令号的宏 _IOC_TYPE()获得命令的type幻数 _IOC_NR()获得命令的num个数 _IOC_DIR()获得命令的数据传送方向 _IOC_SIZE获得传送数据的类型
6.access_ok() 是用来判断用户空间传来的arg是否有效 copy_from_user,copy_to_user,get_user和put_user是不需要用access_ok因为,函数内部带有这个检测函数,而__get_user和__put_user必须在前面进行检查。copy_from_user可以按照一定的大小传递数据,get_user不能按照一定大小传递数据。
7.ret=__get_user(kernel_num, (int *)arg);这类问题 必须使用在内核空间和用户空间之间传递数据的函数 直接赋值是不行的(后面想想貌似直接赋值也行,只要做好access_ok)。
8.最后一点,说说arg,也就是ioctl的数据传递方式,arg是一个unsigned long型的,可以当做一个指针地址传递各种数据,前提是用户空间有效。
2.判断用户态传递的空间是否有效针对 __get_user和__put_user 貌似还有直接赋值
3.通过switch case实现命令
memdev.h
appmem_ioctl
1.对于驱动的学习应该注重的是驱动的结构,module_init里面的结构,read,write,ioctl里面的结构,日后的文章会详细结构部分。
2.对于知识点的总结,应该加强对函数的总结,函数是一个很容易忘记的东西(因为不常用)。
本节难点:
1.ioctl的参数传递,和cmd命令的理解。2.ioctl和read,write在用途上有什么本质区别,等到学到硬件控制再回头看看。
本节知识:
1. 定义命令用的宏: _IO(type,nr) _IOR(type,nr,datatype)_IOW(type,nr.datatype) _IOWR(type,nr.datatype) type是幻数
2.register_chrdev_region静态申请设备号 alloc_chrdev_region动态申请设备号 unregister_chrdev_region注销设备号
MAJOR()获得主设备号 MKDEV()获得设备号 MINOR()获得次设备号
3.cdev_init()设备注册初始化 cdev_add()设备注册添加 cdev_del()设备注销 4.在2.6.36以后的内核中没有了int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 只剩下long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);记住参数少了struct inode *
5.判断命令号的宏 _IOC_TYPE()获得命令的type幻数 _IOC_NR()获得命令的num个数 _IOC_DIR()获得命令的数据传送方向 _IOC_SIZE获得传送数据的类型
6.access_ok() 是用来判断用户空间传来的arg是否有效 copy_from_user,copy_to_user,get_user和put_user是不需要用access_ok因为,函数内部带有这个检测函数,而__get_user和__put_user必须在前面进行检查。copy_from_user可以按照一定的大小传递数据,get_user不能按照一定大小传递数据。
7.ret=__get_user(kernel_num, (int *)arg);这类问题 必须使用在内核空间和用户空间之间传递数据的函数 直接赋值是不行的(后面想想貌似直接赋值也行,只要做好access_ok)。
8.最后一点,说说arg,也就是ioctl的数据传递方式,arg是一个unsigned long型的,可以当做一个指针地址传递各种数据,前提是用户空间有效。
ioctl驱动函数的结构
1.先判断命令号是否正确2.判断用户态传递的空间是否有效针对 __get_user和__put_user 貌似还有直接赋值
3.通过switch case实现命令
代码
memdev.c/************************************************************************** 文件名: memdev.c 日期: 2013/05/22 15:58 头文件: memdev.h 功能: ioctl字符驱动 (通过三个命令进行ioctl的测试) 此为驱动部分 环境: Redhat企业版5 内核版本2.6.18-53.el5 作者: Hao 流程: 1.分配设备号(a.静态申请 b.动态分配) 2.创建设备文件(a.手动创建mknod(需要设备号)注“设备名和设备文件名” b.自动创建) 3.设备注册(a.设备注册分配 b.设备注册初始化 c.设备注册添加) 4.实现file_operation中的函数 ioctl驱动 a.初始化cmd b.无参数的命令 c.读方向的命令 d.写方向的命令 e.在ioctl函数中命令的实现 5.设备注销(cdev_del) 6.设备号注销 ***************************************************************************/ #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 <linux/slab.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include "memdev.h" static int mem_major = MEMDEV_MAJOR; module_param(mem_major, int, S_IRUGO); struct cdev c_dev; int kernel_num=1991;//用一个全局变量吧 要不不能把先写入的数据保存下来 char kernel_buf[40]="hello kernel!!!"; MODULE_AUTHOR("Hao"); MODULE_LICENSE("GPL"); /************************************************************************** 函数名: memdev_ioctl 函数功能: ioctl实现函数 命令实习函数 函数参数: 无 函数返回值: 返回ret为正常执行 返回-EINVAL命令号不正确 ***************************************************************************/ static long memdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret=0; int err=0; //int kernel_num=1991; //char kernel_buf[20]="hello kernel!!!"; /*先判断命令号是否正确*/ if (_IOC_TYPE(cmd) != CMD_KTYPE) //获得命令的type类型是否正确 return -EINVAL; if (_IOC_NR(cmd) > NUM_KCMD) //获得命令的num类型 是否小于命令个数 return -EINVAL; /*获命令的数据传输方向 根据各自的方向判断*/ if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));/*此函数是根据 内核空间写的 是用来判断 arg应用程序传来的用户空间 是否有效的 所以对于用户空间来说是写*/ else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));//对于用户空间来说是读 成功返回1 失败返回0 if (err) return -EFAULT; /*实现CMD的用法*/ switch(cmd) { case NONE_KCMD: printk(KERN_EMERG "NONE_KCMD is in kernel!!!\n"); break; case READ_KCMD: ret=__put_user(kernel_num, (int *)arg); //把内核中的int型数据读入用户空间 unsigned long arg就是一个地址值 kernel->arg break; case WRITE_KCMD: ret=__get_user(kernel_num, (int *)arg); //arg->kernel_num 把用户空间的数据传递给kernel_num printk(KERN_EMERG "WRITE_KCMD is in kernel!!! kernel_num:%d \n",kernel_num); break; case READ_STR_KCMD: if(copy_to_user((char *)arg,kernel_buf,40))//这个实验 是从内核空间到用户空间 传递固定大小count的数据 ret = - EFAULT; break; case WRITE_STR_KCMD: if(copy_from_user(kernel_buf,(char *)arg,40));//不太适合是因为 只能传递规定大小的数据 这里不能超过40的 ret = - EFAULT; break; default: return -EINVAL; break; } } int mem_release(struct inode *inode, struct file *filp) { return 0; } int mem_open(struct inode *inode,struct file *filp) { return 0; } static const struct file_operations mem_fops = //定义此字符设备的file_operations { //这里是对结构体整体赋值的方式 .owner = THIS_MODULE, //函数名都可以自己定义 都是函数指针 .open = mem_open, .release = mem_release, .unlocked_ioctl=memdev_ioctl, }; static int memdev_init(void) { int result; dev_t dev_num=MKDEV(mem_major,0);//将主设备号和次设备号转换成32位的设备号给 静态申请设备号用的 if(mem_major) result=register_chrdev_region(dev_num,2,"newmemdev");//静态申请设备号为dev_num 2个设备 设备名为“newmemdev” else { result=alloc_chrdev_region(&dev_num,0,2,"newmemdev");//动态分配设备号 设备名可以在/proc/devices文件中找 与/dev路径找文件不同 因为一个设备文件 一个次设备号 mem_major = MAJOR(dev_num);//获得主设备号 } if (result < 0) { return result; //判断动态分配是否成功 不成功则退出函数 } /*设备注册 分配已经在前面完成了 为全局变量*/ cdev_init(&c_dev,&mem_fops);//设备注册初始化 将设备号dev_num和file_operation建立联系 c_dev.owner = THIS_MODULE;//*****************貌似可以屏蔽吧********************* cdev_add(&c_dev,dev_num,2);//设备注册 添加 设备号为dev_num 设备数为2个 return 0; } static int memdev_exit(void) { cdev_del(&c_dev);//设备注销 unregister_chrdev_region(MKDEV(mem_major, 0), 2);//设备号注销 return 0; } module_init(memdev_init); module_exit(memdev_exit);
memdev.h
/************************************************************************** 文件名: memdev.h 日期: 2013/05/22 15:10 头文件: memdev.h 功能: ioctl字符驱动 (通过三个命令进行ioctl的测试) 此为驱动部分头文件 定义三个命令 环境: Redhat企业版5 内核版本2.6.18-53.el5 作者: Hao 流程: 1.分配设备号(a.静态申请 b.动态分配) 2.创建设备文件(a.手动创建mknod(需要设备号)注“设备名和设备文件名” b.自动创建) 3.设备注册(a.设备注册分配 b.设备注册初始化 c.设备注册添加) 4.实现file_operation中的函数 ioctl驱动 a.初始化cmd b.无参数的命令 c.读方向的命令 d.写方向的命令 5.设备注销(cdev_del) 6.设备号注销 ***************************************************************************/ #ifndef _MEMDEV_H_ #define _MEMDEV_H_ #include <linux/ioctl.h> #ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 251 /*预设的mem的主设备号*/ #endif #ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2 /*设备数*/ #endif #ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 /*开辟的内存大小*/ #endif /*mem设备描述结构体*/ struct mem_dev { char *data; unsigned long size; }; #define CMD_KTYPE 'k' //定义命令幻数 也叫命令类型 #define NONE_KCMD _IO(CMD_KTYPE,1) //定义无参数的命令 #define READ_KCMD _IOR(CMD_KTYPE,2,int) //定义读方向的命令 #define WRITE_KCMD _IOW(CMD_KTYPE,3,int) //定义写方向的命令 #define READ_STR_KCMD _IOR(CMD_KTYPE,4,int) //定义读方向的命令 读字符串 #define WRITE_STR_KCMD _IOW(CMD_KTYPE,5,int) //定义写方向的命令 写字符串 #define NUM_KCMD 5 //命令个数 后面判断命令是否有效 用的 #endif /* _MEMDEV_H_ */
appmem_ioctl
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include "memdev.h" int main(void) { int fd=0; int arg=0; char buf[40]="WRITE_STR_KCMD is in kernel"; if(-1==(fd=open("/dev/memdev0",O_RDWR))) //设备节点名称为memdev0 { printf("Open Dev Mem0 Error!\n"); _exit(EXIT_FAILURE); } printf("begin NONE_KCMD!!!\n"); //一个无参数的命令 ioctl(fd,NONE_KCMD,&arg); printf("begin WRITE_KCMD!!!\n"); //写入一个int型arg arg=2013; ioctl(fd,WRITE_KCMD,&arg); printf("begin READ_KCMD!!!\n"); //把上面写入的arg或者是驱动程序中的arg读出来 arg=0; //排除误会 ioctl(fd,READ_KCMD,&arg); printf("now arg is %d\n",arg); printf("begin WRITE_STR_KCMD!!!\n"); //写入一个固定大小20字节的字符串 ioctl(fd,WRITE_STR_KCMD,buf); printf("begin READ_STR_KCMD!!!\n"); memset(buf, 0, 40);//排除误会 ioctl(fd,READ_STR_KCMD,buf); printf("now buf is %s\n",buf); close(fd); return 0; }
相关文章推荐
- 新路程------ 一个字符驱动(ioctl)
- Linux字符设备驱动入门(二)——加入ioctl功能
- Linux设备驱动程式学习(4)-高级字符驱动程式操作[(1)ioctl and llseek]
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现
- Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析
- 【Linux 驱动】第六章 高级字符驱动程序操作 ----ioctl
- Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析
- Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析
- 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl
- linux字符设备驱动的 ioctl 幻数
- ok6410学习笔记(2.简单字符驱动)
- linux字符设备驱动的 ioctl 幻数
- 高级字符设备驱动-Ioctl设备控制笔记
- linux驱动:[3]高级字符设备驱动之ioctl
- linux驱动开发之字符设备--内核和用户空间数据的交换(ioctl)
- 《Linux设备驱动程序》学习2—高级字符设备驱动ioctl
- 具有IOctl的简单字符驱动
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】
- 字符设备驱动第六课---ioctl
- 高级字符设备驱动-Ioctl设备控制笔记