写点什么

Linux 设备驱动系列(八)——ioctl 系统调用

  • 2024-04-30
    浙江
  • 本文字数:2645 字

    阅读完需:约 9 分钟

Linux设备驱动系列(八)——ioctl系统调用

关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/lbhCd6gri4hQKDT6ezsW7A


Linux 内核将虚拟内存空间划分成相互隔离的两个部分:内核空间和用户空间。内核空间只能用于运行内核代码、内核扩展以及大部分的设备驱动程序。相反,用户空间的内存可以被所有运行的用户态程序使用,并且必要的时候用户空间的内存会被交换到磁盘中。


为了实现内核空间和用户空间之间的通信,Linux 提供了 ioctl、procfs、sysfs、configfs、debugfs、sysctl、UDP sockets 以及 Netlink sockets 等通信机制。本文将重点介绍基于 ioctl 系统调用的通信机制。

1 ioctl 介绍

IOCTL(Input and Output Contol)全称”输入和输出控制“,它通常用来与设备驱动程序通信。


系统调用作为通用的接口,不太可能满足所有的设备操作需求,这时候可以通过实现设备驱动的 ioctl 系统调用,处理一些系统调用没有实现的设备特定操作。

2 ioctl 实现步骤

下面介绍一下,如果要在 Linux 设备驱动程序中实现一个 ioctl 系统调用,通常要涉及的步骤。

2.1 设备驱动创建 ioctl 命令

"linux/ioctl.h"头文件中提供了创建 ioctl 命令的宏:


#define "ioctl name" __IOX("magic number","command number","argument type")
复制代码


其中 IOX 可以是"IO"、"IOW"、"IOR"和"IOWR",分别表示 ioctl 命令不带参数、携带写入参数、携带读取参数以及同时携带读写参数。


Magic number 是新创建 ioctl 命令集合的唯一标识,用于区分其他的 ioctl 命令,通常设置被设备的主设备号,而 command number 则是表示当前 ioctl 命令在命令集合中的唯一标识。


例如:


#include <linux/ioctl.h>
#define WR_VALUE _IOW(1, 1, int32_t *)#define RD_VALUE _IOR(1, 2, int32_t *)
复制代码

2.2 设备驱动实现 ioctl 函数

接下来就是实现设备驱动的 ioctl 调用,其函数原型如下:


int  ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
复制代码


其中 cmd 表示当前 ioctl 调用接收到的 ioctl 命令,即前面创建的 ioctl 命令。


ioctl 调用函数需要实现前面创建的所有 ioctl 命令,通常使用 switch 来处理,最后将实现好的 ioctl 函数设置到设备文件的 fops->unlocked_ioctl 上。


static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg){  switch(cmd) {    case WR_VALUE:      return copy_from_user(&value ,(int32_t*) arg, sizeof(value));    case RD_VALUE:      return copy_to_user((int32_t*) arg, &value, sizeof(value));  }  return 0;}
static struct file_operations fops ={ .owner = THIS_MODULE, ... .unlocked_ioctl = my_ioctl,};
复制代码

2.3 用户程序创建 ioctl 命令

用户程序创建 ioctl 命令和设备驱动中的类似:


#define WR_VALUE _IOW(1, 1, int32_t *)#define RD_VALUE _IOR(1, 2, int32_t *)
复制代码

2.4 用户程序使用 ioctl 系统调用

"sys/ioctl.h"头文件中提供了 ioctl()系统调用函数,可以通过它向设备驱动发送 ioctl 命令:


long ioctl(fd, cmd, args);
复制代码


例如:


ioctl(fd, WR_VALUE, (int32_t *)&value); ioctl(fd, RD_VALUE, (int32_t *)&value);
复制代码

3 设备驱动 ioctl 示例

说明一下:随着知识点的深入,后面的示例代码行数会越来越多,为了节省篇幅突出重点,从本文开始,后续的示例代码将简化异常路径的处理以及其他的一些次要代码,但这种做法在实际代码开发中是非常不推荐的。

3.1 设备驱动代码

kernle_driver.c


#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kdev_t.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/ioctl.h>#include <linux/err.h>
#define WR_VALUE _IOW(1, 1, int32_t *)#define RD_VALUE _IOR(1, 2, int32_t *)
int32_t value = 0;
dev_t dev = 0;static struct class *dev_class;static struct cdev my_cdev;
static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ switch (cmd) { case WR_VALUE: return copy_from_user(&value, (int32_t *)arg, sizeof(value)); case RD_VALUE: return copy_to_user((int32_t *)arg, &value, sizeof(value)); } return 0;}
static struct file_operations fops = { .owner = THIS_MODULE, .unlocked_ioctl = my_ioctl,};
static int __init my_driver_init(void){ if ((alloc_chrdev_region(&dev, 0, 1, "my_dev")) < 0) return -1;
cdev_init(&my_cdev, &fops);
if ((cdev_add(&my_cdev, dev, 1)) < 0) goto r_class;
if (IS_ERR(dev_class = class_create(THIS_MODULE, "my_class"))) goto r_class;
if (IS_ERR(device_create(dev_class, NULL, dev, NULL, "my_device"))) goto r_device;
return 0;
r_device: class_destroy(dev_class);r_class: unregister_chrdev_region(dev, 1); return -1;}
static void __exit my_driver_exit(void){ device_destroy(dev_class, dev); class_destroy(dev_class); cdev_del(&my_cdev); unregister_chrdev_region(dev, 1);}
module_init(my_driver_init);module_exit(my_driver_exit);
MODULE_LICENSE("GPL");MODULE_AUTHOR("feifei <feifei@gmail.com>");MODULE_DESCRIPTION("Simple Linux device driver");MODULE_VERSION("1.5");
复制代码

3.2 用户程序代码

user_prog.c


#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <sys/ioctl.h>
#define WR_VALUE _IOW(1, 1, int32_t *)#define RD_VALUE _IOR(1, 2, int32_t *)
int main(){ int fd; int32_t value, number;
fd = open("/dev/my_device", O_RDWR); if (fd < 0) { printf("Failed to open /dev/my_device\n"); return -1; }
printf("Enter the Value to send: \n"); scanf("%d", &number); ioctl(fd, WR_VALUE, (int32_t *)&number); ioctl(fd, RD_VALUE, (int32_t *)&value); printf("Value read from Driver: %d\n", value);
close(fd); return 0;}
复制代码

3.3 代码演示


关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/lbhCd6gri4hQKDT6ezsW7A


发布于: 刚刚阅读数: 4
用户头像

聚沙成塔 2023-01-12 加入

分享Linux内核开发相关的编程语言、开发调试工具链、计算机组成及操作系统内核知识、Linux社区最新资讯等

评论

发布
暂无评论
Linux设备驱动系列(八)——ioctl系统调用_Linux Kenel_Linux内核拾遗_InfoQ写作社区