嵌入式驱动开发详解5(ioctl的使用)

文章目录

ioctl介绍

linux内核给用户提供了两类系统调用函数:一类是数据操作函数,比如read、write...。 另外一类函数是非数据操作函数,比如ioctl...,用户程序可以用ioctl给底层设备发送指令。 ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。

==在文件 I/O 中,ioctl 扮演着重要角色,本文将以驱动开发为侧重点,从用户空间到内核空间纵向分析 ioctl 函数。 ==

应用层详解

应用层执行以下函数给底层发送消息,用此函数需要包括头文件#include <sys/ioctl.h>

c 复制代码
int ioctl(int fd, int cmd, ...) ;
参数 描述
fd 文件描述符
cmd 交互协议,设备驱动将根据 cmd 执行对应操作
... 可变参数 arg,依赖 cmd 指定长度以及类型

ioctl() 函数执行成功时返回 0,失败则返回 -1

驱动层详解

在驱动层可以通过自定义的函数操作集来实现,如下所示,unlocked_ioctl就是有关ioctl在驱动层的实现。

c 复制代码
static const struct file_operations timer_fops = {
	.owner	= THIS_MODULE,
	.open	= timer_open,
	.unlocked_ioctl	= timer_unlocked_ioctl,
};

该函数的定义如下:

c 复制代码
static long timer_unlocked_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
参数 描述
fd 文件描述符
cmd 交互协议,设备驱动将根据 cmd 执行对应操作
... 可变参数 arg,依赖 cmd 指定长度以及类型

当应用层执行ioctl函数时,驱动层就会对应的执行unlocked_ioctl函数。

ioctl的cmd

ioctl 方法第二个参数 cmd 为用户与驱动的 "协议",理论上可以为任意 int 型数据,可以为 0、1、2、3......,但是为了确保该 "协议" 的唯一性,ioctl 命令应该使用更科学严谨的方法赋值,在linux中,提供了一种 ioctl 命令的统一格式,将 32 位 int 型数据划分为四个位段,如下图所示:

  • dir(direction),ioctl 命令访问模式(数据传输方向),占据 2 bit,可以为 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
  • size,涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;
  • type(device type),设备类型,占据 8 bit,在一些文献中翻译为 "幻数" 或者 "魔数",可以为任意 char 型字符,如 'a'、'b'、'c' 等等,其主要作用是使 ioctl 命令有唯一的设备标识;
  • nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;
c 复制代码
// include/uapi/asm-generic/ioctl.h
/* 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)))
_IO:       定义不带参数的 ioctl 命令
_IOW:      定义带写参数的 ioctl 命令(copy_from_user)
_IOR:      定义带读参数的ioctl命令(copy_to_user)
_IOWR:     定义带读写参数的 ioctl 命令

实验例程

c 复制代码
#define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */ 
#define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */
#define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令 */
//---------------------------应用层---------------------------------
    while(1){
        printf("Input CMD:");
        ret = scanf("%d",&cmd);
        if(ret != 1){  /* 参数输入错误 */
            fgets(str,100,stdin);   /* 防止卡死 */
        }
        if(cmd == 1)
            cmd = CLOSE_CMD;
        else if(cmd == 2)
            cmd = OPEN_CMD;
        else if(cmd == 3){
            cmd = SETPERIOD_CMD;
            printf("Input Timer Period:");
            ret = scanf("%d",&arg);
            if(ret != 1){
                fgets(str,100,stdin); 
            }
        }
        ioctl(fd,cmd,arg);
    }

//--------------------------------驱动层----------------------------------------

static long timer_unlocked_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
	unsigned long flags;
	int timerperiod;
	struct timer_dev *dev = filep->private_data;
	switch (cmd) {
	case CLOSE_CMD:
		del_timer_sync(&dev->timer);
		break;
	case OPEN_CMD:
		spin_lock_irqsave(&dev->lock, flags);
		timerperiod = dev->timeperiod;
		spin_unlock_irqrestore(&dev->lock, flags);
		mod_timer(&dev->timer,jiffies+msecs_to_jiffies(timerperiod));
		break;
	case SETPERIOD_CMD:
		spin_lock_irqsave(&dev->lock, flags);
		dev->timeperiod = arg;
		spin_unlock_irqrestore(&dev->lock, flags);
		mod_timer(&dev->timer,jiffies+msecs_to_jiffies(dev->timeperiod));
		break;
	default:
		break;
	}
	return 0;
}

static const struct file_operations timer_fops = {
	.owner	= THIS_MODULE,
	.open	= timer_open,
	.unlocked_ioctl	= timer_unlocked_ioctl,
};

这里有一个疑问,SETPERIOD_CMD是带有传参功能的,为啥也是定义为_IO而不是_IOW。

到这里对pinctrl和gpio子系统的大致说明就结束了,后面有新的相关的重要的内容会继续进行更新。

对代码有兴趣的同学可以查看链接https://github.com/NUAATRY/imx6ull_dev

参考文献linux 内核 - ioctl 函数详解

相关推荐
程序员JerrySUN2 天前
Linux UART 驱动开发全解析:从原理到实战
linux·运维·驱动开发
林政硕(Cohen0415)2 天前
Linux驱动开发进阶(三)- 热插拔机制
linux·驱动开发·热插拔
sukalot2 天前
Windows 图形显示驱动开发-WDDM 2.4功能-GPU 半虚拟化(十一)
windows·驱动开发
小麦嵌入式2 天前
Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践
linux·c语言·驱动开发·stm32·嵌入式硬件·物联网·ubuntu
触角010100012 天前
STM32F103低功耗模式深度解析:从理论到应用实践(上) | 零基础入门STM32第九十二步
驱动开发·stm32·单片机·嵌入式硬件·物联网
sukalot2 天前
Windows 图形显示驱动开发-WDDM 2.4功能-GPU 半虚拟化(三)
windows·驱动开发
星星点灯5083 天前
盛铂科技FlexDDS-NG:12通道相位连续DDS信号发生器,400MHz高频输出赋能量子光学与超冷原子研究
驱动开发·科技·测试工具·量子计算·模块测试·射频工程
小麦嵌入式4 天前
Linux驱动开发实战(九):Linux内核pinctrl_map详解与优势分析
linux·c语言·汇编·驱动开发·stm32·嵌入式硬件·硬件工程
触角010100014 天前
MPU6050模块详解:从原理到STM32驱动指南(上) | 零基础入门STM32第八十九步
驱动开发·stm32·单片机·嵌入式硬件·物联网
VermouthSp5 天前
Linux驱动开发 块设备
linux·驱动开发