本文目录
- 一、系统层和内核层接口
- 1. ioctl系统层接口
- 2. ioctl内核层接口
- 二、标准 unlocked_ioctl 接口的命令合成
- 三、代码编程
ioctl
主要用于实现对硬件设备控制类操作,实现 write 和 read 不太好实现的功能。
ioctl
是一个强大的工具,可以用于实现复杂的设备控制和状态查询。尽管它的使用有时被认为不太优雅,因为它打破了标准的读写接口模型,但在许多情况下,它提供了一种有效的方法来与设备驱动程序进行高级交互。开发者在使用 ioctl 时需要小心设计和实现命令码,以确保与设备的交互是清晰且安全的。
一、系统层和内核层接口
1. ioctl系统层接口
头文件:#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
//int fd :要操作的文件描述符
//int request :命令,可以是系统预定义的,或者自己定义的。
//...:可变参。
2. ioctl内核层接口
long xxx_unlocked_ioctl(struct file *pfile, unsigned int cmd, unsigned long args)
//struct file *pfile:对应于系统调用的 fd 参数。
//unsigned int cmd :对应系统层的命令。
//unsigned long args :对应系统层的可变参。
二、标准 unlocked_ioctl 接口的命令合成
这个命令接口有系统预定义的命令,用户也可以自己定义命令,但是为了防止用户定义的命令和系统预定义的命令发生冲突,则定义了一套命名规则。即通过多个参数来合成命令,这样就保证了命令的唯一性。
命令由32个bit位来构成,如下所示。
bit位 | 功能 |
---|---|
31 ~ 30 | 00 (用户程序和驱动没有数据传递 uses _IO macro) 、10 (用户程序从驱动读取数据: _IOR) 、 01 (用户程序向驱动写数据: _IOW)、11 (先用户程序写数据到驱动,再从驱动中读取数据) |
29 ~ 16 | 当用户程序和驱动程序有数据传递时候才有效,表示要传递的数据大小。 |
15 ~ 8 | 魔数,幻数,给每一个驱动分配一个惟一的 ASCII 值,用于区分一个驱动的 cmd 和别的不一样。 |
7 ~ 0 | 同一个设备驱动中所有 cmd 命令的编号,范围 0~255,一般情况同一个驱动值都是连续的。 |
假设我们定义一个BEEP_ON
的命令,这时不需要传递参数。我们就可以使用32个bit位来合成该命令,如:0<<30 | 0<<16 | 'B'<<8 |1
。使用32位来逐个合成会有些繁琐,所以系统给我们提供了更便捷的接口,其原理也是通过位来合成,只不过已经给我们封装好了,我们直接使用即可。
接口 | 功能 |
---|---|
_IO(type, nr) | 定义没有数据传递的命令 |
_IOR(type, nr ,size) | 定义从驱动中读取数据的命令 |
_IOW(type, nr, size) | 定义向驱动写入数据的命令 |
_IOWR(type, nr, size) | 定义数据交换类型的命令,先写入数据,再读取数据这类命令。 |
其中type:魔数,nr:命令编号,size:参数的数据类型,如要传递 4 字节,可以写 int。
具体使用如下,为了内核层和系图层都可以使用,命令的定义需要写在一个单独的头文件中。
//不带参数的命令
#define BEEP_ON _IO('B', 0)
#define BEEP_OFF _IO('B', 1)
//带参数的写命令
#define LEDX_ON _IOW('L', 0, void *)
#define LEDX_OFF _IOW('L', 1, void *)
三、代码编程
common.h
#ifndef __COMMON_H
#define __COMMON_H
#define BEEP 36 //GPIO序号为36,低电平触发。
#define LED1 39
#define KEY1 40
//无参的命令
#define BEEP_ON _IO('B', 0)
#define BEEP_OFF _IO('B', 1)
//向内核写的命令
#define LEDX_ON _IOW('L', 0, void *)
#define LEDX_OFF _IOW('L', 1, void *)
//读内核的命令
#define KEY1_Read _IOR('K', 0, void *)
//先向内核写数据,再读取数据的命令
#define KEYX_Read _IOWR('K', 1, void *)
#endif
my_ioctl.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include "common.h"
int ioctl_open(struct inode *node, struct file *fd)
{
gpio_set_value(BEEP, 1);//设置无效电平
return 0;
}
int ioctl_release(struct inode *node, struct file *fd)
{
gpio_set_value(BEEP, 1); //设置无效电平
return 0;
}
long ioctl_unlocked_ioctl(struct file *fd, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case BEEP_ON:
gpio_set_value(BEEP, 0);
break;
case BEEP_OFF:
gpio_set_value(BEEP, 1);
break;
case LEDX_ON:
gpio_set_value(arg, 0);
break;
case LEDX_OFF:
gpio_set_value(arg, 1);
break;
case KEY1_Read:{
int value, ret;
value=gpio_get_value(KEY1);
ret = copy_to_user((void *)arg, &value, sizeof(value));
if(ret < 0) {
pr_err("copy_to_user error\r\n");
return -EINVAL;
}
break;
}
case KEYX_Read:{
break;
}
}
return 0;
}
const struct file_operations ioctl_fops={
.owner = THIS_MODULE,
.open=ioctl_open,
.release=ioctl_release,
.unlocked_ioctl=ioctl_unlocked_ioctl,
};
struct miscdevice misc_ioctl={
.minor=MISC_DYNAMIC_MINOR,
.name="my_ioctl",
.fops=&ioctl_fops,
};
const struct gpio gpio_array[]={
{ BEEP, GPIOF_OUT_INIT_HIGH, "beep"},
{ LED1, GPIOF_OUT_INIT_HIGH, "led1"},
{ KEY1, GPIOF_IN, "key1"},
};
static int __init ioctl_init(void)
{
int ret;
ret= misc_register(&misc_ioctl); //注册杂项设备
if(ret < 0) {
pr_err("misc_register error\r\n");
return -1;
}
ret=gpio_request_array(gpio_array, sizeof(gpio_array)/sizeof(gpio_array[0])); // //申请一组GPIO
if(ret < 0) {
pr_err("gpio_request error\r\n");
goto gpio_error;
}
return 0;
gpio_error: //防止GPIO注册失败后,无法注销杂项设备。
misc_deregister(&misc_ioctl);
return -1;
}
static void __exit ioctl_exit(void)
{
misc_deregister(&misc_ioctl);
gpio_free_array(gpio_array, sizeof(gpio_array)/sizeof(gpio_array[0])); //释放一组GPIO
}
module_init(ioctl_init);
module_exit(ioctl_exit);
MODULE_LICENSE("GPL");
app.c
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include <sys/ioctl.h>
int main()
{
int fd;
int read_value;
fd=open("/dev/my_ioctl",O_RDWR);
if(fd<0){
return -1;
}
/* 无参命令
ioctl(fd,BEEP_ON);
sleep(2);
ioctl(fd,BEEP_OFF);
*/
/*向内核写的命令
ioctl(fd, LEDX_ON, LED1);
sleep(2);
ioctl(fd, LEDX_OFF, LED1);
*/
//读取内核的命令
ioctl(fd, KEY1_Read, &read_value);
printf("%d\n", read_value);
return 0;
}