首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

ok6410学习笔记(4.ioctl字符驱动)

ok6410学习笔记(4.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
[cpp] view plaincopy


  • /**************************************************************************
  • 文件名:            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[cpp] view plaincopy


  • /**************************************************************************
  • 文件名:            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
[cpp] view plaincopy


  • #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;  
  • }  


返回列表