老程序员分享:linux驱动开发笔记_ioctl函数

简介: 老程序员分享:linux驱动开发笔记_ioctl函数

1.相关概念


ioctl 是设备驱动程序中设备控制接口函数。某些设备除了打开、关闭、读出和写入功能外,可能还有其它的功能,比如说设置串口波特率、设置马达的转速等等。


1.用户空间函数


#include


int ioctl (int fd, unsigned int cmd, ...)


1


2


参数 描述


fd 打开文件描述符


cmd 交互协议,设备驱动将根据cmd执行相应的操作


… 可变参数arg,依赖cmd中指定的长度以及类型


ioctl()函数执行成功之后则会返回0;失败的话就会返回 -1;


2.驱动程序函数


long (unlocked_ioctl) (struct file , unsigned int, unsigned long);


long (compat_ioctl) (struct file , unsigned int, unsigned long);


1


2


unlocked_ioctl,顾名思义,应该在无大内核锁(BKL)的情况下调用;


compat 全称 compatible(兼容的),主要目的是为 64 位系统提供 32 位 ioctl 的兼容方法,也是在无大内核锁的情况下调用。


其中第二个参数是 cmd 从用户空间无改变的传过来的,第三个参数是传过来的用户空间的可寻址地址;


3. ioctl中的cmd(交互协议)


交互协议,就是用户空间和驱动程序之间达成的一致性协议。需要完成什么功能都在这个协议中得到包含。cmd是一个32位的数据,其中不同的位置代表了不同的段。


在内核当中,有对于生成ioctl命令相关的宏,可以很方便直接生成cmd:


#define _IOC(dir,type,nr,size) \


(((dir) [ _IOC_DIRSHIFT) | \


((type) [ _IOC_TYPESHIFT) | \


((nr) [ _IOC_NRSHIFT) | \


((size) [ _IOC_SIZESHIFT))


#define _IOC_NRSHIFT 0


#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)


#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)


#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)


#define _IOC_NRBITS 8


#define _IOC_TYPEBITS 8


# define _IOC_SIZEBITS 14


1.dir(direction), ioctl命令的访问模式。定义数据的传输方向。可以为 _IOC_NONE 、 _IOC_READ、 _IOC_WRITE、 //代码效果参考:http://www.jhylw.com.cn/590730185.html

_IOC_READ|_IOC_WRITE。分别代表了无数据、读数据、写数据、读写数据。读写是从用户空间来看的,写是往驱动写,读是往用户读。

2.type(device type),设备类型,有些地方叫做 幻数 。可以为任意的char类型的字符。比如说设置为 ’a‘ , ‘b’ , ’ D’等等。主要作用是使 ioctl 命令有唯一的设备标识;


3. nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;


4. size涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;


为了更加方便的使用,在上述宏定义的基础上又衍生出了更加方便的ioctl命令:


#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),(//代码效果参考:http://www.jhylw.com.cn/010821485.html

_IOC_TYPECHECK(size)))

#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))


1


2


3


4


_IO: 定义不带参数的 ioctl 命令


_IOW: 定义带写参数的 ioctl 命令(copy_from_user)


_IOR: 定义带读参数的ioctl命令(copy_to_user)


_IOWR: 定义带读写参数的 ioctl 命令


在内核驱动代码中,还提供了分离出不同的段的宏定义接口,供内核的驱动代码使用。


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


1


2


3


4


比如:


if( _IOC_TYPE(cmd) != ‘c’) 判断是否设备种类正确


if( _IOC_NR(cmd) > 2) 判断命令的数值是不是大于2


if( _IOC_DIR(cmd) & _IOC_READ) 判断该命令是否为可读命令


4.ioctl例子分析


这个例子用ioctl来实现内核空间和用户空间的数据交互。


1.定义用户程序和驱动程序共同定义的协商文件ioctl_menu.h。


#ifndef IOCTLMENU


#define IOCTLMENU


#include // 内核空间


/定义设备类型/


#define IOC_MAGIC 'c'


/ 初始化设备/


#define IOCINIT _IO(IOC_MAGIC, 0)


/ 写寄存器 /


#define IOCWREG _IOW(IOC_MAGIC, 1, int)


/ 读寄存器 /


#define IOCRREG _IOR(IOC_MAGIC, 2, int)


#define IOC_MAXNR 2


#endif


该文件中定义了三个操作方式: 初始化、读出数据、写入数据。


2.设备驱动程序代码


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include


#include "ioctl_menu.h"


struct ioctl_struct{


dev_t devid; / 设备号/


int major; / 主设备号/


int minor; / 次设备号 /


struct cdev cdev; / cdev /


struct class class; //


struct device device; / 设备 /


int data; /设备的一个储存空间/


};


struct ioctl_struct mytest;


long test_ioctl(struct file file, unsigned int cmd, unsigned long arg)


{


int ret;


//如果设备种类不正确


if(_IOC_TYPE(cmd) != IOC_MAGIC)


{


pr_err("【%s】 command type 【%c】 error!\n", func, _IOC_TYPE(cmd));


return -1;


}


//检查序号是否超限


if(_IOC_NR(cmd) > IOC_MAXNR )


{


pr_err("【%s】 command number 【%d】 exceeded!\n", func, _IOC_NR(cmd));


return -1;


}


//检查访问模式


if(_IOC_DIR(cmd) & _IOC_READ)


{


ret= !access_ok(VERIFY_WRITE, (void __user )arg, _IOC_SIZE(cmd));


}


else if (_IOC_DIR(cmd) & _IOC_WRITE)


{


ret= !access_ok(VERIFY_READ, (void user *)arg, _IOC_SIZE(cmd));


}


if(ret) return -1;


switch(cmd)


{


//初始化


case IOCINIT:


printk(KERN_ALERT "hello ,i am a register!\n");


break;


//写数据


case IOCWREG:


ret = copy_from_user(&mytest.data, (int user )arg, sizeof(int));


break;


//读数据


case IOCRREG:


ret = copy_to_user((int __user )arg, &mytest.data, sizeof(int));


break;


default: break;


}


return 0;


}


static struct file_operations mytest_fops =


{


.owner = THIS_MODULE,


.unlocked_ioctl = test_ioctl,


};


static int init ioctl_init(void)


{


//1.构建设备号


if(mytest.major)


{


mytest.devid = MKDEV(mytest.major, 0);


register_chrdev_region(mytest.devid , 1, "mytest");


}


else


{


alloc_chrdev_region(&mytest.devid, 0, 1, "mytest");


mytest.major = MAJOR(mytest.devid);


mytest.minor = MINOR(mytest.devid);


}


//2.注册字符设备


cdev_init(&mytest.cdev , &mytest_fops);


cdev_add(&mytest.cdev , mytest.devid, 1);


//3.创建类


mytest.class = class_create(THIS_MODULE, "mytest");


//4.创建设备文件


mytest.device = device_create(mytest.class, NULL, mytest.devid, NULL, "mytest");


return 0;


}


static void exit ioctl_exit(void)


{


printk(KERN_ALERT "ioctl Goodbye!\n");


cdev_del(&mytest.cdev);


unregister_chrdev_region(mytest.devid, 1);


device_destroy(mytest.class, mytest.devid);


class_destroy(mytest.class);


}


module_init(ioctl_init);


module_exit(ioctl_exit);


MODULE_LICENSE("GPL");


MODULE_AUTHOR("linyuwang");


其中的access_ok()函数原型如下,用来判断用户空间的地址是否可读或者可以写。


int access_ok (int type, const void addr, unsigned long size);


1


3.用户测试代码


#include


#include


#include


#include


#include


#include


#include


#include


#include "ioctl_menu.h"


#define READ 0


#define WRITE 1


int main(int argc , char argv【】)


{


int fd;


int method;


int data;


int ret;


//判断用法是否正确


if(argc < 3)


{


printf("error usage!\r\n");


return -1;


}


//打开设备文件


fd = open(argv【1】, O_RDWR);


if(fd < 0)


{


printf("file %s open failed!\r\n", argv【1】);


return -2;


}


//判断是读还是写


if(memcmp(argv【2】, "read", sizeof("read")) == 0)


{


method = READ;


}


else if(memcmp(argv【2】, "write", sizeof("write")) == 0)


{


method = WRITE;


}


else


{


printf("methord error!");


return -3;


}


//完成对应的操作


if(method == READ)


{


ret = ioctl(fd, IOCRREG, &data);


if(ret == 0)


{


printf("%d\r\n", data);


}


else


{


printf("Read failed!");


return -4;


}


}


else if(method == WRITE)


{


data = atoi(argv【3】);


ret = ioctl(fd, IOCWREG, &data);


if(ret == 0)


{


printf("Success!");


}


else


{


printf("Write failed!");


return -5;


}


}


return 0;


}


————————————————


版权声明:本文为CSDN博主「一人一城506」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。


原文链接:

相关文章
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
4969 77
|
Ubuntu Linux Python
Tkinter错误笔记(一):tkinter.Button在linux下出现乱码
在Linux系统中,使用Tkinter库时可能会遇到中文显示乱码的问题,这通常是由于字体支持问题导致的,可以通过更换支持中文的字体来解决。
1134 0
Tkinter错误笔记(一):tkinter.Button在linux下出现乱码
|
Ubuntu 搜索推荐 Linux
详解Ubuntu的strings与grep命令:Linux开发的实用工具。
这就是Ubuntu中的strings和grep命令,透明且强大。我希望你喜欢这个神奇的世界,并能在你的Linux开发旅程上,通过它们找到你的方向。记住,你的电脑是你的舞台,在上面你可以做任何你想做的事,只要你敢于尝试。
542 32
|
运维 监控 中间件
Linux运维笔记 - 如何使用WGCLOUD监控交换机的流量
WGCLOUD是一款开源免费的通用主机监控工具,安装使用都非常简单,它可以监控主机、服务器的cpu、内存、磁盘、流量等数据,也可以监控数据库、中间件、网络设备
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
898 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
存储 Linux 开发工具
如何进行Linux内核开发【ChatGPT】
如何进行Linux内核开发【ChatGPT】
|
9月前
|
Linux 应用服务中间件 Shell
二、Linux文本处理与文件操作核心命令
熟悉了Linux的基本“行走”后,就该拿起真正的“工具”干活了。用grep这个“放大镜”在文件里搜索内容,用find这个“探测器”在系统中寻找文件,再用tar把东西打包带走。最关键的是要学会使用管道符|,它像一条流水线,能把这些命令串联起来,让简单工具组合出强大的功能,比如 ps -ef | grep 'nginx' 就能快速找出nginx进程。
981 1
二、Linux文本处理与文件操作核心命令
|
9月前
|
Linux
linux命令—stat
`stat` 是 Linux 系统中用于查看文件或文件系统详细状态信息的命令。相比 `ls -l`,它提供更全面的信息,包括文件大小、权限、所有者、时间戳(最后访问、修改、状态变更时间)、inode 号、设备信息等。其常用选项包括 `-f` 查看文件系统状态、`-t` 以简洁格式输出、`-L` 跟踪符号链接,以及 `-c` 或 `--format` 自定义输出格式。通过这些选项,用户可以灵活获取所需信息,适用于系统调试、权限检查、磁盘管理等场景。
572 137
|
9月前
|
安全 Ubuntu Unix
一、初识 Linux 与基本命令
玩转Linux命令行,就像探索一座新城市。首先要熟悉它的“地图”,也就是/根目录下/etc(放配置)、/home(住家)这些核心区域。然后掌握几个“生存口令”:用ls看周围,cd去别处,mkdir建新房,cp/mv搬东西,再用cat或tail看文件内容。最后,别忘了随时按Tab键,它能帮你自动补全命令和路径,是提高效率的第一神器。
1515 58
|
8月前
|
存储 安全 Linux
Linux卡在emergency mode怎么办?xfs_repair 命令轻松解决
Linux虚拟机遇紧急模式?别慌!多因磁盘挂载失败。本文教你通过日志定位问题,用`xfs_repair`等工具修复文件系统,三步快速恢复。掌握查日志、修磁盘、验重启,轻松应对紧急模式,保障系统稳定运行。
1360 2