IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> linux字符驱动之ioctl部分 -> 正文阅读

[系统运维]linux字符驱动之ioctl部分

本期主题:
linux字符驱动之ioctl部分


往期链接:



1.为什么要用ioctl

前面已经讲了怎么对驱动设备进行读写,之所以还需要ioctl的原因是因为 工程师需要对设备进行控制,因此开出了这样的一些接口
因此,可以说ioctl是设备驱动程序中对设备的I/O通道进行管理的函数

2.一个简单的例子

ioctl应用层的使用方式:
int ioctl(int fd, unsigned long request, …)
fd是文件描述符,request是对应的cmd命令

在这里插入图片描述
例子中添加了一个0x01的命令作为ioctl的命令,在应用程序中也调用ioctl,并且往下传了1这个参数

驱动代码


#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>

MODULE_LICENSE("GPL");

#define TEST_CMD 0x01

static int hello_open (struct inode *inode, struct file *filep)
{
	printk("hello_open()\n");
	return 0;
}

long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	switch (cmd)
	{
	case TEST_CMD:
		printk("for test ioctl\r\n");
		printk("arg is %d\r\n", arg);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static struct file_operations hello_ops = 
{
	.open = hello_open,
	.unlocked_ioctl = hello_ioctl,
};

static int major = 230;
static int minor = 0;
static dev_t dev_num = 0;
static struct cdev cdev;

static int hello_init(void)
{
	int result;
	int error;

	dev_num = MKDEV(major, minor);
	
	result = register_chrdev_region(dev_num, 1, "test");

	if (result < 0)
	{
		printk("register_chrdev_region fail \n");
		return result;
	}
	printk("hello_init, register_chrdev_region OK \n");

	cdev_init(&cdev,&hello_ops);
	error = cdev_add(&cdev,dev_num,1);
	if(error < 0)
	{
		printk("cdev_add fail \n");
		unregister_chrdev_region(dev_num,1);
		return error;
	}
	return 0;
}


static void hello_exit(void)
{
	printk("hello_exit \n");
	cdev_del(&cdev);
	unregister_chrdev_region(dev_num, 1);
	return;
}

module_init(hello_init); //insmod
module_exit(hello_exit);//rmmod

应用程序代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define TEST_CMD 0x01

int main()
{
	int fd;
	int ret;

	fd = open("/dev/test",O_RDWR);
	if(fd<0)
	{
		perror("open fail \n");
		return;
	}
	printf("open ok \n ");

	ret = ioctl(fd, TEST_CMD, 1);
	if (ret < 0)
	{
		perror("ioctl fail \n");
	};
	return ret;
}

实验结果:
在这里插入图片描述

3.思考

上面例子的问题在哪?

在上面的例子中,我们将TEST_CMD命令设置为0x01,但实际上这并不是一种好方法,因为如果每个模块都对命令设置成0x00,0x01…等等,内核将会十分混乱。

因此Linux内核采用了一套统一的ioctl()命令生成方式,详情看 linux内核中的 ioctl-number.txt文档
在这里插入图片描述
ioctl()的命令方式为:
在这里插入图片描述
内核中用来辅助ioctl的宏定义,文件位置:linux-linux-4.4.y\include\uapi\asm-generic\ioctl.h,其中:

type表示设备类型
nr表示序列号
size表示数据尺寸

/* 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)

/* ...and for the drivers/sound files... */

#define IOC_IN		(_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT		(_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT	((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK	(_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT	(_IOC_SIZESHIFT)

其实这个ioctl的cmd命令也不是很重要,只是如果想看起来更规范一点,可以参考ioctl-number.txt文档,我们参考他的来设计一下:
1.type类型
定义为"g"
2.序列号
我们自己的程序从1开始,定义为1
3.传输方向
这个是从应用程序的角度来看的,我们现在想传输参数给内核,所以对应用程序来说是写
4.数据尺寸
直接填需要传输的参数的类型

因此,上面例子的变化如下:
在这里插入图片描述
经过实验,与原来现象一致。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:35:01  更:2022-05-24 18:36:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:53:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码