这篇文章给大家讲解一下ioctl的简单使用,关于ioctl更详细的教程后面有机会单独写出来

(一)什么是ioctl

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速、网卡速率等等。它的调用方式如下:

//在应用调用
int ioctl(int fd, int cmd, …);

其中fd是用户程序打开设备时使用open函数返回的文件标示符,cmd是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,这个参数的有无和cmd的意义相关。

(二)ioctl的作用

虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数,但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动,想要一个弹出光驱的操作,这种操作并不是所有的字符设备都需要的,所以文件操作结构体也不会有对应的函数操作。出于这样的原因,ioctl就有它的用处了————一些没办法归类的函数就统一放在ioctl这个函数操作中,通过指定的命令来实现对应的操作。所以,ioctl函数里面都实现了多个的对硬件的操作,通过应用层传入的命令来调用相应的操作。

(3)ioctl的优点

控制I/O设备 ,提供了一种获得设备信息和向设备发送控制参数的手段。用于向设备发控制和配置命令 ,read/write函数处理的是数据,ioctl是控制命令
这样用的可以分工明确:数据是数据,命令是命令

(四)ioctl相关接口介绍

(1)系统层

   #include <sys/ioctl.h>int ioctl(int fd, unsigned long request, ...);int fd:设备对应的打开的文件描述符
unsigned long request:请求码
若有第三个参数,必须为无类型指针

请求码cmd是 unsigned int 是 32 位。下图给出了每位的含义


系统中给定了一些请求码,下面列出一小部分

    // <include/asm-i386/socket.h>地址          宏名称       采用对应宏名字的时候所传递的第三个参数值0x00008901   FIOSETOWN    const int *0x00008902   SIOCSPGRP    const int *0x00008903   FIOGETOWN    int *0x00008904   SIOCGPGRP    int *0x00008905   SIOCATMAR    int *0x00008906   SIOCGSTAMP   timeval *

除了系统给定的,内核中也提供了创建请求码的宏:

/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
type: cmd 中魔数,用来代表一个驱动 8~15位。
nr  : cmd 中的序列号,0-7位
size: cmd 中的数据尺寸,16-29位

内核中提供了相应的解码请求码的宏:

/* used to decode ioctl numbers.. */
获取请求码是从内核获取数据或者写入数据到内核-----判定读写功能
#define _IOC_DIR(nr)    (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)获取请求码对应的操作设备类型
#define _IOC_TYPE(nr)   (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
获取具体的请求码的功能值(传递的是1还是0,或者其他数据)
#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
获取请求码的代销
#define _IOC_SIZE(nr)   (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

注意:
unlocked_ioctl 用来替换 ioctl 接口的, 在 Linux 3.0 以后 ioctl 已经在内核中消失。其对应系统调用接口不变。

(五)实例代码

cdev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>int i=0;
dev_t dev=0;
char stat[4]={-1,-1,-1,-1};
#define CDEVCOUNT 5
#define CDEVNAME "cdevdevice"
#define CDEVCLASS "myclass"
#define INODENAME "mycdev"
#define ADDRSIZE 8
unsigned int phy_dadr=0x110002E0;//映射的起始地址,为GPM4CON的偏移地址
unsigned  int *virt_addr=NULL; //用来接收映射后首地址
struct cdev * cdev=NULL;
struct class * cdevclass=NULL;
#define GPM4CON  (*(volatile unsigned int *)virt_addr)
#define GPM4DAT  (*(volatile unsigned int *)(virt_addr+1))int cdev_open (struct inode *node, struct file *file)
{//先将配置寄存器清空GPM4CON &=~(0XFFFF<<0);//设置每个引脚为输出状态GPM4CON |=(0X1111<<0);//给指定的数据寄存器初始化GPM4DAT |=(0XF<<0);return 0;
}
ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{int i=0,ret=0;if(size<=0 || *offset>4 )return -EINVAL;if(size>4-*offset)size=4-*offset;for(i=*offset;i<*offset+size;i++){if(GPM4DAT&(1<<i)){stat[i]='0';}else if(!(GPM4DAT&(1<<i))){stat[i]='1';}}ret=copy_to_user(&buf[*offset],&stat[*offset],size);printk("%s\n",stat);printk("cdev_read is install\n");return 0;
}
ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset)
{int i=0;long ret=0;char str[4]={-1,-1,-1,-1};if(size<=0 || *offset>4 )return -EINVAL;if(size>4-*offset)size=4-*offset;ret=copy_from_user(&str[*offset],&buf[*offset],size);if(ret<0){return -1;}for(i=*offset;i<*offset+size;i++){if(str[i]=='0')GPM4DAT |=(1<<i);else if(str[i]=='1')GPM4DAT &=~(1<<i);}printk("cdev_write is install\n");return 0;
}
loff_t cdev_llseek (struct file *fp, loff_t off, int whence)
{//获取偏移量的操作需要whence和off结合loff_t newoff=0;switch(whence){case SEEK_SET:newoff=off;break;case SEEK_CUR:newoff=fp->f_pos+off;break;case SEEK_END:newoff=off+4;break;default: break;}//上述操作newoff 的值可能为负数,也可能为大于4的数,大于4和小于0没有意义if(newoff<0){newoff=0;}if(newoff>4){newoff=4;}fp->f_pos=newoff;return fp->f_pos;
}
long cdev_ioctl (struct file *fp, unsigned int cmd,  unsigned long tmp)
{int  i=0,ret=0;if(_IOC_TYPE(cmd)!='D')return -EINVAL;//提取cmd中定义的读写方向if(_IOC_DIR(cmd)==_IOC_WRITE){if(_IOC_NR(cmd)==1){GPM4DAT &=~(1<<tmp);}if(_IOC_NR(cmd)==0){GPM4DAT |= (1<<tmp);}}else if(_IOC_DIR(cmd)==_IOC_READ){for(i=0;i<4;i++){if(GPM4DAT&(1<<i)){stat[i]='0';}else if(!(GPM4DAT&(1<<i))){stat[i]='1';}}if((ret=copy_to_user((unsigned long *)tmp, stat, 4))<0)return -EFAULT;}return 0;
}int cdev_release (struct inode *node, struct file *fp)
{printk("cdev_release is install\n");return 0;
}
struct file_operations fop={.open=cdev_open,//.read=cdev_read,//.write=cdev_write,.release=cdev_release,//.llseek=cdev_llseek,.unlocked_ioctl=cdev_ioctl,
};
static int __init cdev_module_init(void)
{int ret=alloc_chrdev_region(&dev, 0, CDEVCOUNT, CDEVNAME);if(ret){return -1;}cdev=cdev_alloc();if (!cdev)goto out;cdev_init(cdev, &fop);if(cdev_add(cdev, dev, CDEVCOUNT)){goto out1;}printk("cdev_add success\n");cdevclass=class_create(THIS_MODULE,CDEVCLASS);if (IS_ERR(cdevclass)){goto out2;}printk("class_create success\n");for(i=0;i<5;i++)device_create(cdevclass,NULL, dev+i, NULL, "mycdev%d",i );virt_addr=ioremap(phy_dadr,ADDRSIZE);printk("device_create success\n");return 0;
out:unregister_chrdev_region(dev,CDEVCOUNT);return -2;
out1:unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);
out2:cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return PTR_ERR(cdevclass);
}
static void __exit cdev_module_cleanup(void)
{iounmap(virt_addr);for(--i;i>=0;i--)device_destroy(cdevclass,dev+i);printk("device_destroy success\n");class_destroy(cdevclass);cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);printk("kfree success\n");
}
module_init(cdev_module_init);
module_exit(cdev_module_cleanup);
MODULE_LICENSE("GPL");

cdev_app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include "cmd.h"
void display(int fd)
{char buf[4]={0};int i=0;//read(fd,buf,4);ioctl(fd,LED_R,buf);printf("%s\n",buf);for(i=0;i<4;i++){printf("led%d is %s\n",i,(buf[i]-'0')?"on":"off");}
}
int main(int argc, char const* argv[])
{int i=0;int fd=open(argv[1],O_RDWR);if(fd==-1){perror("open");return -1;}for(i=0;i<4;i++){ioctl(fd,LED_ON,i);sleep(1);display(fd);ioctl(fd,LED_OFF,i);sleep(1);}printf("%s\n",stat);close(fd);return 0;
}

cmd.h

#ifndef __CMD_H_
#define __CMD_H_#define  LED_OFF _IOW('D',0,sizeof(int))
#define  LED_ON _IOW('D',1,sizeof(int))
#define  LED_R  _IOR('D',2,sizeof(int))
#endif

Makefile

CFLAG=-C
TARGET=cdev
APP=cdev_app
KERNEL=/driver/linux-3.5
obj-m +=$(TARGET).o
all:make $(CFLAG) $(KERNEL) M=$(PWD)arm-linux-gcc -o $(APP) $(APP).c
clean:make $(CFLAG) $(KERNEL) M=$(PWD) cleanrm $(APP)

1.首先使用_IOW或_IOR宏来合成32位的请求码,这个请求码可以单独定义在一个头文件里
2.在字符驱动模型的file_operation结构体里.unlocked_ioctl指向的函数中编写与ioctl相关的控制函数
(1)判断设备类型 _IOC_TYPE(nr)
(2)判断读写方向 _IOC_DIR(nr)
如果是读的话将数据拷贝到用户空间
3.在应用程序中调用ioctr(fd,请求码,参数)来控制设备
这里的请求码可以是我们第一步合成的


本文章仅供学习交流用禁止用作商业用途,文中内容来水枂编辑,如需转载请告知,谢谢合作

微信公众号:zhjj0729

微博:文艺to青年

(八)linux驱动之ioctl的使用相关推荐

  1. Linux驱动设计ioctl函数的cmd参数不能为2

    Linux驱动程序设计的时候偶然发现的ioctl()函数的cmd参数不能为2,如果为2,ioctl()函数返回-1,网上说就是这样的,正常,不知道为什么,stack overflow上有一个外国学友的 ...

  2. linux驱动之ioctl

    大部分驱动除了需要具备读写设备的能力之外,还需要具备对硬件控制的能力. 一.在用户空间,使用ioctl系统调用来控制设备,原型如下: int ioctl(int fd,unsigned long cm ...

  3. 嵌入式Linux驱动笔记(十八)------浅析V4L2框架之ioctl【转】

    转自:https://blog.csdn.net/Guet_Kite/article/details/78574781 权声明:本文为 风筝 博主原创文章,未经博主允许不得转载!!!!!!谢谢合作 h ...

  4. Linux驱动之 字符设备 ioctl接口使用

    字符设备ioctl接口使用: Linux驱动编写除了对设备进行读写数据之外,通常还希望可以对设备进行控制. 从应用层传递一些命令参数,并在驱动层实现相应设备操作,这时候就用到了 ioctl函数: 应用 ...

  5. Linux驱动开发(十八)---网络(网卡)驱动学习

    前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...

  6. [Linux驱动开发五]实现ioctl函数

    目录 一.前言 1.1 ioctl用来做什么? 1.2 ioctl和unlock_ioctl区别? (1)ioctl()主要用于应用层系统调用 (2)unlock_ioctl主要用于驱动层系统调用 1 ...

  7. linux驱动---ioctl函数解析

    转自(39条消息) linux驱动---ioctl函数解析_那可真是太开心了的博客-CSDN博客_linux驱动ioctl 参考:ioctl,unlocked_ioctl 处理方法-阿里云开发者社区 ...

  8. Linux驱动——sdio type card(八)

    Linux驱动--sdio type card(八) 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0 文章目录 Linux驱动--sdio typ ...

  9. Linux驱动--ioctl

    ioctl系统框图 用户空间的ioctl #include <sys/ioctl.h> int ioctl(int fd, int cmd, ...) ; 参数 描述 fd 文件描述符 c ...

最新文章

  1. redis 备份导出rdb_redis简单安装学习
  2. 《Java从入门到放弃》框架入门篇:hibernate基本用法
  3. timeit统计运行时间
  4. html属性wmode代码,html – wmode – 浏览器的渲染行为意味着什么?
  5. 漫步微积分二十四——定积分引言
  6. 蔺永华:虚拟化你的大数据应用
  7. java case用法_Go语言 | goroutine不只有基础的用法,还有这些你不知道的操作
  8. 浅析GDAL库C#版本支持中文路径问题
  9. redis、kafka、rabittMQ对比 (转)
  10. PHP开源软件《个人管理系统》-希望大家一起来开发
  11. php内置函数什么语言,语言构造和PHP中的“内置”函数有什么区别?
  12. 谷歌地图的标记样式_如何为Google地图设置样式
  13. Oracle中rank函数详解
  14. 微信小程序 —— 自定义picker选择器弹窗内容
  15. FreeRTOS 遇坑(1)
  16. FTT 海面模拟(DirectX11)
  17. C#多线程顺序依赖执行控制
  18. webpack css中字体文件路径问题
  19. 浅谈二层交换安全攻击与防御
  20. C++程序中如何执行cmd命令

热门文章

  1. 2021计算机应用基础形考答案模块2,国家开放大学计算机应用基础模块2形考答案-20210603091431.docx-原创力文档...
  2. 平果手机桌面计算机,苹果手机便签记事本怎么在Windows电脑桌面上使用?
  3. Docker JFrog Artifactory 7.27.10 maven私服(搭建篇)
  4. SpringBoot/Cloud 统一返回优雅设计+自定义异常
  5. 黑马商城项目级联选择占满全屏解决方案
  6. Spring Boot2 整合 Ehcache
  7. Linux 主机信息 总览
  8. 大事件后台管理系统开发实战(中)
  9. ajax respon有数据,Ajax怎么通过response在后台传递数据?(附代码)
  10. python读取excel列数据库_python中读取excel数据库