ioctl

本文详细介绍了Linux中的ioctl函数,包括其原型、应用程序与驱动程序的交互方式,以及如何定义和使用自定义的IOCTL命令。涵盖了LED控制、蜂鸣器操作、按键读取等实例,展示了如何在硬件设备驱动中实现非读写操作。
摘要由CSDN通过智能技术生成

一、概述

一个设备除了能够通过读写操作来收发数据或返回、保存数据、还应该有很多其他的操作。
比如一个串口设备还应该具备波特率获取和设置、帧格式获取与设置的操作。
一个LED设备甚至不应该有读写操作,而应该具有点灯和灭灯的操作。
硬件设备如此众多,各种操作纷繁复杂,所以内核将读写之外的其他I/O操作都委派给了另外一个函数接口ioctl,而且文件I/O还具备多种模型,比如有非阻塞、阻塞、I/O多路复用、异步I/O和异步通知。

二、IOCTL函数的原型

1. 应用程序的ioctl()—>linux的系统IO函数

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);

经常使用的函数原型:

int ioctl(int fd, unsigned int cmd, unsigned long args);
int ioctl(int fd, unsigned int cmd);

应用程序的ioctl()会系统调用驱动程序的unlocked_ioctl()

2. 驱动程序的ioctl()—>file_operations

long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long args);

3. 应用的方法

1)应用程序向驱动程序发送命令cmd,但是不发送参数

int ioctl(int fd, unsigned int cmd);
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long args);

注:此时unsigned long args会被忽略

2)应用程序向驱动程序发送命令,并发送参数

int ioctl(int fd, unsigned int cmd, unsigned long args);
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long args);

unsigned int cmd —>应用程序发下来的命令
unsigned long args —>应用程序发下来的参数

例子:

#include <linux/videodev2.h>
#include <linux/ioctl.h>
struct v4l2_format fmt;  //保存或设置摄像头输出的视频格式
 
 
fmt.fmt.pix.width       = 640;//设置视频输出宽为640
fmt.fmt.pix.height      = 480;//输出高位480
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置输出格式为YUYV格式(必须要摄像头支持YUYV格式设置才有效)
fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

ret = ioctl(fd, VIDIOC_S_FMT, &fmt); //把格式数据设置到驱动中
if (ret < 0)
{
    printf("VIDIOC_S_FMT failed (%d)\n", ret);
    return ret;
}

应用程序向驱动程序发送命令VIDIOC_S_FMT,并发送参数fmt。

3)应用程序向驱动程序发送命令,并获取参数

int ioctl(int fd, unsigned int cmd, unsigned long args);
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long args);

unsigned int cmd —>应用程序发下来的命令
unsigned long args —>发送给应用程序的参数

例子:

#include <linux/ioctl.h>
#include <linux/videodev2.h>
struct v4l2_format fmt;  //保存或设置摄像头输出的视频格式
memset(&fmt, 0, sizeof(struct v4l2_format));
 
ret = ioctl(fd, VIDIOC_G_FMT, &fmt);//从驱动中获取信息
    if (ret < 0) {
        printf("***********************************\nVIDIOC_G_FMT failed (%d)\n", ret);
        return ret;
    }
    printf("Stream Format Informations:\n");
    printf(" type: %d\n", fmt.type);
    printf(" width: %d", fmt.fmt.pix.width);
    printf(" height: %d\n", fmt.fmt.pix.height);

应用程序向驱动程序发送命令VIDIOC_G_FMT,获取驱动的数据fmt

三、IOCTL的命令

头文件:#include <linux/ioctl.h>

IOCTL的命令需要预先定义好,应用程序和驱动程序要使用相同的命令。命令是一个32位无符号的整型值,该整型值是有固定格式要求的。
我们要使用固定的格式标准,定义自己的cmd。
在这里插入图片描述
其实每个命令就是32位的,通过配置该32位的命令来形成_IO、_IOR、_IOW、_IOWR。

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

定义一个命令,但是不需要参数参数

#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_TYPECH

函数的参数说明:
type,命令的类型,一般为一个ASCII码,一般一个驱动程序使用一个type
nr,该命令下序号。一个驱动有多个命令,一般它们的type,序号不同
size,args的类型

#define STK_IOCTL_SET_ENABLE        _IOW(STKDIR, 0x03, char) 
#define STK_IOCTL_SET_OFFSET        _IOW(STKDIR, 0x07, char[3])
#define PHONE_CAPABILITIES_CHECK    _IOW ('q', 0x82, struct phone_capability *)
#define PHONE_RING_CADENCE		_IOW ('q', 0x86, short)

例子:
应用程序定义的cmd:/usr/include/linux/videodev2.h

#define VIDIOC_G_FMT _IOWR('V',  4, struct v4l2_format)
#define VIDIOC_S_FMT _IOWR('V',  5, struct v4l2_format)

驱动程序定义的cmd:内核源码/include/linux/videodev2.h

#define VIDIOC_G_FMT _IOWR('V',  4, struct v4l2_format)
#define VIDIOC_S_FMT _IOWR('V',  5, struct v4l2_format)

四、命令示例

1.定义LED灯的命令
应用程序发命令:灯亮和灯灭
应用程序发参数:灯号:8/9/10/11

由于开发板有4盏LED灯,最好使用参数来指定控制哪一盏LED灯

#define GEC6818_LED_ON _IOW('L',  1, unsigned long)
#define GEC6818_LED_OFF _IOW('L',  2, unsigned long)

2.定义蜂鸣器的命令
由于开发板只有1个蜂鸣器,可以只用单独一个命令来控制蜂鸣器,不用附带参数

#define GEC6818_BEEP_ON _IO('B',  1)
#define GEC6818_BEEP_OFF _IO('B',  2)

3.定义按键的命令
读取单个按键

#define GEC6818_K2_STA		_IOR('K',  1, unsigned long)
#define GEC6818_K3_STA		_IOR('K',  2, unsigned long)
#define GEC6818_K4_STA		_IOR('K',  3, unsigned long)
#define GEC6818_K6_STA		_IOR('K',  4, unsigned long)

读取所有按键

#define GEC6818_KALL_STA	_IOR('K',  5, unsigned long)

五、参数内容多字节传递

如果实现参数内容多字节传递,应用层要传递地址值。驱动层得使用copy_from_user或copy_to_user,实现数据内容的交换,参考源码:

应用层:

#define CMD_BUF_W	 _IOW('B',0,unsigned char[4])
#define CMD_BUF_R	 _IOR('B',1,unsigned char[4])

unsigned char buf[4]={1,2,3,4};

ioctl(fd,CMD_BUF_W,buf);
ioctl(fd,CMD_BUF_R,buf);

驱动层:

#define CMD_BUF_W	 _IOW('B',0,unsigned char[4])
#define CMD_BUF_R	 _IOW('B',1,unsigned char[4])


long key_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	void __user *argp = (void __user *) arg;
    unsigned int value=0x12345678;

	switch (cmd)
	{
         case CMD_BUF_W:
			{
                       copy_from_user((void *)&value, argp, sizeof(unsigned int))
			}break;

        case CMD_BUF_R:
			{
                        copy_to_user(argp, (const void *)&value, sizeof(unsigned int))
			}break;
        default:return -ENOIOCTLCMD;
    }
    return 0;
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux ioctl(Input Output Control)是一组特殊的系统调用,用于控制设备驱动程序的状态或功能,以便进行低级别的I/O操作。这些操作对于直接与硬件交互非常有用,例如改变USB设备的配置、调整串口通信参数或是对网络适配器进行更细致的控制。 ioctl的基本语法形式是一个整数值,这个值包含两部分信息: 1. **主要编号**(通常是大写字母M):标识 ioctl 的用途类别。 2. **次要编号**(通常是小写字母m):进一步细分具体的 ioctl 操作。 例如 `IOCTL_MDEV` 可能表示某一种设备驱动的相关指令集,`IOCTL_MDEV_SET_SPEED` 则是一个具体的指令,用于设置某个设备的速度。 典型的 ioctl 调用看起来像这样: ```c int ioctl(int fd, unsigned long request, ...); ``` 这里的参数解释如下: - `fd`:文件描述符,关联到设备或文件的一个索引,通常由 open 或其他 I/O 函数返回。 - `request`:一个由驱动程序特定的长整型值,包含指令和可能的数据。 - `...`:额外的数据,取决于请求的具体内容。 ioctl 的应用例子很多,比如在网络编程中调整套接字选项,或者在音频硬件编程中控制混音器的状态。它们之所以重要是因为它们让应用程序能够以比标准 I/O 更底层的方式控制设备,从而提高性能或实现特定功能。 了解 ioctl 的使用可以帮助开发者深入理解系统级别的细节,以及更好地优化他们的程序与硬件的交互。 --- 相关问题 - : 1. ioctl 与其他 I/O 控制方式的区别是什么? 2. 在实际编程中如何正确地使用 ioctl 进行设备控制? 3. 对于不同的硬件设备(如 USB 设备、串口、网络设备),常见的 ioctl 请求有哪些?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值