Linux设备驱动程序学习(4) -高级字符驱动程序操作[(1)ioctl and llseek]

今天进入《Linux设备驱动程序(第3版)》第六章高级字符驱动程序操作的学习。


一、ioctl

大部分设备除了读写能力,还可进行超出简单的数据传输之外的操作,所以设备驱动也必须具备进行各种硬件控制操作的能力. 这些操作常常通过 ioctl 方法来支持,它有和用户空间版本不同的原型:

int (*ioctl) (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);

需要注意的是:不管可选的参数arg是否由用户给定为一个整数或一个指针,它都以一个unsigned long的形式传递。如果调用程序不传递arg参数, 被驱动收到的 arg 值是未定义的。因为在arg参数上的类型检查被关闭了,所以若一个非法参数传递给 ioctl,编译器是无法报警的,且任何关联的错误难以查找.

选择ioctl命令

为了防止向错误的设备使用正确的命令,命令号应该在系统范围内唯一。为方便程序员创建唯一的 ioctl 命令代号, 每个命令号被划分为多个位字段。要按 Linux 内核的约定方法为驱动选择 ioctl 的命令号, 应该首先看看 include/asm/ioctl.h 和 Documentation/ioctl-number.txt。 要使用的位字段符号定义在 <linux/ioctl.h> :

type(幻数):8 位宽(_IOC_TYPEBITS),参考ioctl-number.txt选择一个数,并在整个驱动中使用它。

number(序数):顺序编号,8 位宽(_IOC_NRBITS)。

direction(数据传送的方向):可能的值是 _IOC_NONE(没有数据传输)、_IOC_READ、 _IOC_WRITE和 _IOC_READ|_IOC_WRITE (双向传输数据)。该字段是一个位掩码(两位), 因此可使用 AND 操作来抽取_IOC_READ 和 _IOC_WRITE。

size(数据的大小):宽度与体系结构有关,ARM为14位.可在宏 _IOC_SIZEBITS 中找到特定体系的值. 

<linux/ioctl.h> 中包含的 <asm/ioctl.h>定义了一些构造命令编号的宏:
_IO(type,nr)/*没有参数的命令*/
_IOR(type, nr, datatype)/*从驱动中读数据*/
_IOW(type,nr,datatype)/*写数据*/
_IOWR(type,nr,datatype)/*双向传送*/
/*type 和 number 成员作为参数被传递, 并且 size 成员通过应用 sizeof 到 datatype 参数而得到*/

这个头文件还定义了用来解开这个字段的宏:

_IOC_DIR(nr)
_IOC_TYPE(nr)
_IOC_NR(nr)
_IOC_SIZE(nr)

具体的使用方法在实验中展示。

返回值

POSIX 标准规定:如果使用了不合适的 ioctl 命令号,应当返回-ENOTTY 。这个错误码被 C 库解释为"不合适的设备 ioctl。然而,它返回-EINVAL仍是相当普遍的。

预定义命令

有一些ioctl命令是由内核识别的,当这些命令用于自己的设备时,他们会在我们自己的文件操作被调用之前被解码. 因此, 如果你选择一个ioctl命令编号和系统预定义的相同时,你永远不会看到该命令的请求,而且因为ioctl 号之间的冲突,应用程序的行为将无法预测。预定义命令分为 3 类:

(1)用于任何文件(常规, 设备, FIFO和socket) 的命令

(2)只用于常规文件的命令

(3)特定于文件系统类型的命令 

下列 ioctl 命令是预定义给任何文件,包括设备特定文件:

FIOCLEX :设置 close-on-exec 标志(File IOctl Close on EXec)。 FIONCLEX :清除 close-no-exec 标志(File IOctl Not CLose on EXec)。 FIOQSIZE :这个命令返回一个文件或者目录的大小; 当用作一个设备文件, 但是, 它返回一个 ENOTTY 错误。 FIONBIO:"File IOctl Non-Blocking I/O"(在"阻塞和非阻塞操作"一节中描述)。 

使用ioctl参数

在使用ioctl的可选arg参数时,如果传递的是一个整数,它可以直接使用。如果是一个指针,,就必须小心。当用一个指针引用用户空间, 我们必须确保用户地址是有效的,其校验(不传送数据)由函数 access_ok 实现,定义在 <asm/uaccess.h> :

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

第一个参数应当是 VERIFY_READ(读)或VERIFY_WRITE(读写);addr 参数为用户空间地址,size 为字节数,可使用sizeof()。access_ok 返回一个布尔值: 1 是成功(存取没问题)和 0 是失败(存取有问题)。如果它返回假,驱动应当返回 -EFAULT 给调用者。

注意:首先, access_ok不做校验内存存取的完整工作; 它只检查内存引用是否在这个进程有合理权限的内存范围中,且确保这个地址不指向内核空间内存。其次,大部分驱动代码不需要真正调用 access_ok,而直接使用put_user(datum, ptr)和get_user(local, ptr),它们带有校验的功能,确保进程能够写入给定的内存地址,成功时返回 0, 并且在错误时返回 -EFAULT.。

put_user(datum, ptr)
__put_user(datum, ptr)
get_user(local, ptr)
__get_user(local, ptr) 

这些宏它们相对copy_to_user 和copy_from_user快, 并且这些宏已被编写来允许传递任何类型的指针,只要它是一个用户空间地址. 传送的数据大小依赖 prt 参数的类型, 并且在编译时使用 sizeof 和 typeof 等编译器内建宏确定。他们只传送1、2、4或8 个字节。如果使用以上函数来传送一个大小不适合的值,结果常常是一个来自编译器的奇怪消息,如"coversion to non-scalar type requested". 在这些情况中,必须使用 copy_to_user 或者 copy_from_user。

__put_user和__get_user 进行更少的检查(不调用 access_ok), 但是仍然能够失败如果被指向的内存对用户是不可写的,所以他们应只用在内存区已经用 access_ok 检查过的时候。作为通用的规则:当实现一个 read 方法时,调用 __put_user 来节省几个周期, 或者当你拷贝几个项时,因此,在第一次数据传送之前调用 access_ok 一次。

权能与受限操作

Linux 内核提供了一个更加灵活的系统, 称为权能(capability)。内核专为许可管理上使用权能并导出了两个系统调用 capget 和 capset,这样可以从用户空间管理权能,其定义在 <linux/capability.h> 中。对设备驱动编写者有意义的权能如下:

CAP_DAC_OVERRIDE /*越过在文件和目录上的访问限制(数据访问控制或 DAC)的能力。*/
CAP_NET_ADMIN /*进行网络管理任务的能力, 包括那些能够影响网络接口的任务*/
CAP_SYS_MODULE /*加载或去除内核模块的能力*/
CAP_SYS_RAWIO /*进行 "raw"(裸)I/O 操作的能力. 例子包括存取设备端口或者直接和 USB 设备通讯*/
CAP_SYS_ADMIN /*截获的能力, 提供对许多系统管理操作的途径*/
CAP_SYS_TTY_CONFIG /*执行 tty 配置任务的能力*/

在进行一个特权操作之前, 一个设备驱动应当检查调用进程有合适的能力,检查是通过 capable 函数来进行的(定义在 <linux/sched.h> )范例如下:

if (! capable (CAP_SYS_ADMIN))
return -EPERM;

二、定位设备(llseek实现)

llseek是修改文件中的当前读写位置的系统调用。内核中的缺省的实现进行移位通过修改 filp->f_pos, 这是文件中的当前读写位置。对于 lseek 系统调用要正确工作,读和写方法必须通过更新它们收到的偏移量来配合。

如果设备是不允许移位的,你不能只制止声明 llseek 操作,因为缺省的方法允许移位。应当在你的 open 方法中,通过调用 nonseekable_open 通知内核你的设备不支持 llseek :

int nonseekable_open(struct inode *inode; struct file *filp); 

完整起见, 你也应该在你的 file_operations 结构中设置 llseek 方法到一个特殊的帮助函数 no_llseek(定义在 <linux/fs.h> )。 具体的应用在试验程序中学习.


三、ioctl和llseek实验。

模块程序链接:ioctl_and_llseek

模块测试程序链接:ioctl_and_llseek-test

ARM9实验板的实验现象是:

[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod scull.ko scull_nr_devs=1
[Tekkaman2440@SBC2440V4]#cd /tmp/
[Tekkaman2440@SBC2440V4]#./scull_test2
open scull !
SCULL_IOCSQUANTUM-SCULL_IOCQQUANTUM : scull_quantum=10
SCULL_IOCTQUANTUM-SCULL_IOCGQUANTUM : scull_quantum=6
SCULL_IOCXQUANTUM : scull_quantum=6 --> 10
SCULL_IOCHQUANTUM : scull_quantum=10 --> 6
SCULL_IOCSQSET-SCULL_IOCQQSET : scull_qset=2
SCULL_IOCTQSET-SCULL_IOCGQSET : scull_qset=4
SCULL_IOCXQSET : scull_qset=4 --> 2
SCULL_IOCHQSET : scull_qset=2 --> 4
before reset : scull_quantum=6 scull_qset=4
close scull !
reopen scull !
reopen : scull_quantum=6 scull_qset=4
write code=6 i=20
write code=6 i=14
write code=6 i=8
write code=2
lseek scull SEEK_SET-->0 !
read code=6 i=20
read code=6 i=14
read code=6 i=8
read code=2
[0]=0 [1]=1 [2]=2 [3]=3 [4]=4
[5]=5 [6]=6 [7]=7 [8]=8 [9]=9
[10]=10 [11]=11 [12]=12 [13]=13 [14]=14
[15]=15 [16]=16 [17]=17 [18]=18 [19]=19
SCULL_IOCRESET
after reset : scull_quantum=4000 scull_qset=1000
close scull !
reopen scull !
write code=20
lseek scull SEEK_CUR-10-->10 !
read code=10
[0]=10 [1]=11 [2]=12 [3]=13 [4]=14
[5]=15 [6]=16 [7]=17 [8]=18 [9]=19
lseek scull SEEK_END-20-->0 !
read code=20
[0]=0 [1]=1 [2]=2 [3]=3 [4]=4
[5]=5 [6]=6 [7]=7 [8]=8 [9]=9
[10]=10 [11]=11 [12]=12 [13]=13 [14]=14
[15]=15 [16]=16 [17]=17 [18]=18 [19]=19
close scull !
[Tekkaman2440@SBC2440V4]#cat /proc/scullseq
Device 0: qset 1000, q 4000, sz 20item at c3dd3d74, qset at c3f540000: c3e71000
[Tekkaman2440@SBC2440V4]#  

转载于:https://www.cnblogs.com/shenhaocn/archive/2011/03/27/1996874.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

markdown文本居中方法(CSDN博客编辑器文本居中方法)

居中居中使用html添加&#xff1a;   <center>这一行需要居中</center> 比如: <center>居中</cenetr>对应下面&#xff1a; 居中

【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁

上两篇文章我们初步接触了ABI-应用程序二进制接口的概念&#xff0c;点击链接查看上一篇文章&#xff1a;【软件开发底层知识修炼】二十二 ABI-应用程序二进制接口 二。了解了为什么会有ABI的存在。本篇文章继续学习ABI 的内容。学习在ABI规范下&#xff0c;函数栈帧的结构与函…

【转】电脑GPS导航软件下载,教你把笔记本做成GPS

在开始之前&#xff0c;先说一下&#xff0c;相信很多朋友在谷哥搜索“电脑GPS导航软件”时&#xff0c;都很难找到真正的下载地址&#xff0c;多数是只能下载到灵图的破解文件&#xff0c;那么&#xff0c;本文不同&#xff0c;本文不但教你怎么打造电脑GPS&#xff0c;而且提…

【软件开发底层知识修炼】二十四 ABI之函数调用约定

上一篇文章学习了Linux环境下的函数栈帧的形成与摧毁。点击链接查看相关文章&#xff1a;软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁本篇文章继续学习ABI接口相关的内容。函数调用约定 文章目录1 函数参数如何入栈&#xff0c;返回…

深入了解 Python 的 import 语句

在 Python 中&#xff0c;import 语句是一个关键的功能&#xff0c;用于在程序中引入模块和包。本文将深入讨论 import 语句的各种用法、注意事项以及一些高级技巧&#xff0c;以帮助你更好地理解和使用这一功能。 概念介绍 package 通常对应一个文件夹&#xff0c;下面可以有…

javascript 获取上一页的url

在js中&#xff0c;如果通过连接或者表单提交里&#xff0c;可以用以下三种方式获取上一页的url&#xff1a; 1、document.referrer 2、top.document.referrer 3、window.parent.document.referrer 这在ie和firefox里都可以实现 但如果在IE中用js函数跳转的话&#xff0c;以上三…

【软件开发底层知识修炼】二十五 ABI之函数调用约定二之函数返回值为结构体时的约定

上一篇文章学习了几种函数调用约定的区别&#xff0c;点击链接查看上一篇文章&#xff1a;【软件开发底层知识修炼】二十四 ABI之函数调用约定本篇文章继续学习函数调用约定中&#xff0c;关于函数返回值的问题。当函数返回值为结构体时&#xff0c;函数返回值是如何来传给调用…

说说我最近比较迷的Criminal Minds

喜欢《犯罪心理》这部美剧很久了&#xff0c;从第四季一直到现在还在追的第六季&#xff0c;觉得集集精彩。简练而不失悬念。该片的一些情节上的特质让我总是与工作上的某些场景产生对比联想。管理模式。BAU是一个典型的家庭式的管理方式。Hotch作为大家长&#xff0c;不辞辛劳…

【软件开发底层知识修炼】二十六 ABI-应用程序二进制接口 学习总结文章目录

前面学习了ABI的知识&#xff0c;感觉受益良多。对底层与编译器有更加深刻的认识&#xff0c;为此这里将前面写过的关于ABI 的文章给列出来&#xff0c;方便学习与翻阅。 【软件开发底层知识修炼】二十一 ABI-应用程序二进制接口一【软件开发底层知识修炼】二十二 ABI-应用程序…

关于《高性能JavaScript》制表(Memoization)笔记

减少工作量是最好的性能优化技术。代码所做的事情越少&#xff0c;他的速度就越快。 书中在关于用制表优化递归的下一段代码中&#xff0c;存在问题。 function memfactorial(n){ if (!memfactorial.cache){ memfactorial.cache { "0": 1, …

CSDN-Markdown-图片设置(大小,居中)

利用markdown在编写文档时插入图片是默认靠左&#xff0c;有些时候将图片设置为居中时可以更加的美观&#xff0c;这时就需要在图片的信息前边添加如下程序 <div aligncenter>![这里写图片描述](http:...如果想将图片位于右侧&#xff0c;只需要将center改为right<di…

查询指定范围内数据记录(适用于sqlserver2005以上)

1 WITHOrderedOrders AS2 (SELECTROW_NUMBER()over(orderbyg_Id asc) asRowNumber,g_Id FROMguanxi) 3 SELECT*4 FROMOrderedOrders 5 WHERERowNumber BETWEEN50AND60;转载于:https://www.cnblogs.com/prolion/archive/2011/04/02/2003277.html

CSDN中markdown字体颜色,大小,首行缩进,居中排布

一、下面是首行缩进的两种方法 1.这里实用空格去替代缩进的字符&#xff0c;下面讲的替代包括分号 2.把输入法由半角改为全角。 两次空格之后就能够有两个汉字的缩进。 半方大的空白用&ensp;或 全方大的空白用&emsp;或 不断行的空白格用 或 示例&#xff1a; 略略略…

SQL查询中的转义字符

如果想查找“_cs”结尾的的账户select * from [user] where loginname like %_cs是不行的&#xff0c;_ 被认为是任意的字符&#xff0c;所以需要转义字符&#xff0c;有两种写法&#xff1a;select * from [user] where loginname like %[_]csselect * from [user] where logi…

【Git、GitHub、GitLab】一 Git安装与Git最小配置

Git学习开始&#xff0c;虽然只是工具。 文章目录1 Git安装2 检查Git安装版本3 Git的最小配置4 git config的其它作用域5 总结1 Git安装 下面是安装Git的的官网链接&#xff1a; Git安装 分别有Linux安装、Mac安装与Windows安装的方法。我个人安装Windows与Linux。方法在上面…

C++类构造函数初始化列表

//以前学习中很模糊的一些概念  结构函数初始化列表以一个冒号开始&#xff0c; 接着是以逗号分隔的数据成员列表&#xff0c; 上面的结构函数(运用初始化列表的结构函数)显式的初始化类的成员;而没运用初始化列表的结构函数是对类的成员赋值&#xff0c; 并没有进行显式的初…

【Git、GitHub、GitLab】二 Git基本命令之建立Git仓库

上一篇文章学习了Git的安装与最小配置&#xff1a;【Git、GitHub、GitLab】一 Git安装与Git最小配置 文章目录建立Git仓库建立Git仓库 有两种方式可以建立Git仓库&#xff1a; 用Git之前已经有项目代码&#xff0c;则使用以下两条命令建立Git仓库 $ cd 项目代码所在的文件夹 $…