Linux 下的ioctl函数

1. linux设备及相关概念

在了解ioctl函数之前,熟悉下linux的一些概念。

设备:即指linux中的硬件设备,如打印机、鼠标...

设备文件:是出现在文件系统中的设备驱动程序的接口。 在类似Unix的操作系统中,它们通常位于/dev目录下,也称为设备节点。

设备驱动程序:是一块其操作或控制特定类型的设备的软件。在现代的单片内核操作系统上,这些通常是内核的一部分。

在Linux系统中,每个设备都有对应的驱动程序,用于与设备进行通信和控制。驱动程序通常由设备的厂商或社区开发,通过内核模块的形式加载到操作系统中。驱动程序提供了一组接口,即设备文件,供用户程序或其他内核模块调用,以实现对设备的控制和管理。

  • 用户空间不能直接对内核进行操作,因此必须使用一个叫做 “系统调用”的方法 来实现从用户空间“陷入” 到内核空间,这样才能实现对底层驱动的操作。
  • 系统调用的方法:glibc库函数和syscall函数。open、read、write、ioctl等函数都属于系统调用。

1.1 设备文件分类

  • 字符设备:以串行顺序进行数据访问

常见的字符设备:鼠标、键盘(IO设备),LCD、Camera(帧缓冲设备)等

  • 块设备:可以任意顺序进行访问,以块为单位进行操作。块设备经过系统的快速缓冲

常见的块设备:硬盘、光盘、SD卡、emmc、flash等存储设备

  • 网络设备:网络设备面向数据包的接收和发送设计,它并不对应于文件系统的节点。

说明:

  • 块设备以块为最小传输单位,不能按字节处理数据。而Linux则允许块设备传送任意数量的字节,因此块 & 字符设备的区别仅在于内核内部管理数据的方式不同,即内核和驱动之间的软件接口不同。
  • 字符设备和块设备都被映射为Linux文件系统中的文件,可以通过文件系统的系统调用接口(open / close / read / write)访问。

1.2. 访问设备文件

1.2.1 字符设备和块设备

在用户空间的/dev目录下,都有一个对应的设备文件(一切皆文件),应用程序通过系统函数调用open、read、write等来操作设备文件从而访问设备驱动来操作硬件设备。

在终端输入ls /dev -l 可以看到设备文件:

字符设备文件-例:鼠标:

crw-r----- 1 root root 13, 33 mouse1

块设备文件-例:硬盘:

brw-rw---- 1 root disk 8, 1 sda1

首字符c表示char,字符设备,首字符d表示block ,块设备。

13和8表示这两个设备的主设备号,用于标识设备的类型;

33和1表示这两个设备的次设备号,用于标识同类设备的不同设备个体

应用程序访问设备流程是,根据用户空间的设备文件,找到对应的设备号,根据设备号去内核找到对应的设备驱动,然后通过设备驱动操作硬件设备。

1.2.2 网络设备

网络设备没有对应设备节点和设备号,网络设备使用套接字来实现网络数据的接收和发送

最常见的网络设备:网卡。

4. ioctl函数

ioctl 是设备驱动程序中设备控制接口函数。一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。

如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数来控制设备的I/O通道。

原型:

#include <sys/ioctl.h>

int ioctl(int fd, unsigned long cmd, ...);
  •  fd:文件描述符,是通过open打开设备返回的值。
  • cmd:交互协议,设备驱动将根据 cmd 执行对应操作
  • ...:可变参数 arg,依赖 cmd 指定长度以及类型

4.1. cmd参数

命令码(cmd)是唯一联系用户程序命令驱动程序支持的途径。cmd的大小为 32位,共分 4 个域:

dir(读/写)size(数据大小)type(幻数)nr(序列号)
2bit14bit8bit8bit
  • dir:表示读或写:

        (1)00 - 命令不带参数。_IOC_NONE

        (2)01 - 命令需要把数据写入驱动,写方向。_IOC_WRITE

        (3)10 - 命令需要从驱动中获取数据,读方向。_IOC_READ

        (4)11 - 命令既要写入数据又要获取数据,读写方向。_IOC_READ | _IOC_WRITE

  • size:如果命令带参数,则指定参数所占用的内存空间大小
  • type:每个驱动全局唯一的幻数(魔数),用英文字符 "A" ~ "Z" 或者 "a" ~ "z" 来表示,主要作用是使 ioctl 命令有唯一的设备标识;(一般魔数定义在驱动程序的头文件中)
  • nr:命令编号/序数,是区分命令的命令顺序序号,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增。

在内核include/uapi/asm-generic/ioctl.h头文件中提供了一组宏来定义、提取命令中的字符信息:

#define _IOC(dir,type,nr,size) \
	(((dir)  << _IOC_DIRSHIFT) | \
	 ((type) << _IOC_TYPESHIFT) | \
	 ((nr)   << _IOC_NRSHIFT) | \
	 ((size) << _IOC_SIZESHIFT))

#ifndef __KERNEL__
#define _IOC_TYPECHECK(t) (sizeof(t))
#endif

/* 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)))
#define _IOR_BAD(type,nr,size)	_IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW_BAD(type,nr,size)	_IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR_BAD(type,nr,size)	_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

/* used to decode ioctl numbers.. */
#define _IOC_DIR(nr)		(((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)		(((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)		(((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)		(((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

构造ioctl命令:

_IO(type,nr):用于构造无参数的命令号
_IOR(type,nr,datetype):用于构造从驱动程序中读取数据的命令号
_IOW(type,nr,datatype):用于构造向驱动程序写入数据的命令号
_IORW(type,nr,datatype):用于构造双向传输的命令号

ioctl的使用可看看这里 :SPI通信接口(c/c++)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值