📄 linux设备驱动程序学习(4)-高级字符驱动程序操作[(1)ioctl and llseek] - linux设备驱动程序 - tekkaman ninja.htm
字号:
style="COLOR: rgb(0,1,255)">《Linux设备驱动程序(第3版)》</SPAN><SPAN
style="FONT-WEIGHT: normal; COLOR: rgb(0,1,2)">第六章高级字符驱动程序操作的学习。</SPAN></DIV>
<DIV>
<HR id=null>
</DIV>
<DIV><FONT color=#0000ff
size=4><STRONG>一、ioctl</STRONG></FONT></DIV>
<DIV>大部分设备除了读写能力,还可进行超出简单的数据传输之外的操作,所以设备驱动也必须具备进行各种硬件控制操作的能力.
这些操作常常通过 ioctl 方法来支持,它有和用户空间版本不同的原型:</DIV>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><SPAN
style="COLOR: rgb(0,0,255)">int</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>ioctl<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN> inode
<SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>inode<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> <SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN> <SPAN
style="COLOR: rgb(255,0,0)">file</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>filp<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN><BR> <SPAN
style="COLOR: rgb(0,0,255)">unsigned</SPAN>
<SPAN style="COLOR: rgb(0,0,255)">int</SPAN>
cmd<SPAN style="COLOR: rgb(0,0,204)">,</SPAN>
<SPAN
style="COLOR: rgb(0,0,255)">unsigned</SPAN>
<SPAN style="COLOR: rgb(0,0,255)">long</SPAN>
<SPAN
style="COLOR: rgb(255,0,0)">arg</SPAN><SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;</SPAN></SPAN></CODE></P></TD></TR></TBODY></TABLE>
<P><FONT
color=#0000ff>需要注意的是:不管可选的参数arg是否由用户给定为一个整数或一个指针,它都以一个unsigned
long的形式传递。如果调用程序不传递arg参数, 被驱动收到的 arg
值是未定义的。因为在arg参数上的类型检查被关闭了,所以若一个非法参数传递给
ioctl,编译器是无法报警的,且任何关联的错误难以查找.</FONT></P>
<P><FONT color=#0000ff size=3><STRONG>选择<FONT
face=新宋体>ioctl</FONT>命令</STRONG></FONT></P>
<P>为了防止向错误的设备使用正确的命令,命令号应该在系统范围内唯一。为方便程序员创建唯一的
ioctl 命令代号, 每个命令号被划分为多个位字段。要按 Linux 内核的约定方法为驱动选择
ioctl 的命令号, 应该首先看看 include/asm/ioctl.h 和
Documentation/ioctl-number.txt。 要使用的位字段符号定义在 <FONT
color=#0000ff><linux/ioctl.h></FONT> :</P>
<P><FONT color=#0000ff>type(幻数):</FONT>8
位宽(_IOC_TYPEBITS),参考ioctl-number.txt选择一个数,并在整个驱动中使用它。</P>
<P><FONT color=#0000ff><SPAN
class=term><SPAN>number(</SPAN></SPAN>序数):</FONT>顺序编号,8
位宽(_IOC_NRBITS)。</P>
<P><FONT color=#0000ff><SPAN
class=term><SPAN>direction(</SPAN></SPAN>数据传送的方向):</FONT>可能的值是
_IOC_NONE(没有数据传输)、_IOC_READ、 _IOC_WRITE和
_IOC_READ|_IOC_WRITE (双向传输数据)。该字段是一个位掩码(两位), 因此可使用
AND 操作来抽取_IOC_READ 和 _IOC_WRITE。</P>
<P><FONT color=#0000ff><SPAN
class=term><SPAN>size(</SPAN></SPAN>数据的大小):</FONT>宽度与体系结构有关,ARM为14位.可在宏
_IOC_SIZEBITS 中找到特定体系的值. </P>
<DT> <linux/ioctl.h> 中包含的
<asm/ioctl.h>定义了一些构造命令编号的宏:
<DIV></DIV>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)">_IO<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>type<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN>nr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(255,153,0)">/*没有参数的命令*/</SPAN><BR>_IOR<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>type<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> nr<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN>
datatype<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(255,153,0)">/*从驱动中读数据*/</SPAN><BR>_IOW<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>type<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN>nr<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN>datatype<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(255,153,0)">/*写数据*/</SPAN><BR>_IOWR<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>type<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN>nr<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN>datatype<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(255,153,0)">/*双向传送*/</SPAN><BR><SPAN
style="COLOR: rgb(255,153,0)">/*type 和 number
成员作为参数被传递, 并且 size 成员通过应用 sizeof 到 datatype
参数而得到*/</SPAN></SPAN></CODE></P></TD></TR></TBODY></TABLE>
<P>这个头文件还定义了用来解开这个字段的宏:</P>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)">_IOC_DIR<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>nr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><BR>_IOC_TYPE<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>nr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><BR>_IOC_NR<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>nr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><BR>_IOC_SIZE<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>nr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN></SPAN></CODE></P></TD></TR></TBODY></TABLE>
<P>具体的使用方法在实验中展示。</P>
<P><FONT color=#0000ff
size=3><STRONG>返回值</STRONG></FONT></P>
<P>POSIX 标准规定:如果使用了不合适的 ioctl 命令号,应当返回-ENOTTY
。这个错误码被 C 库解释为"不合适的设备 ioctl。然而,它返回-EINVAL仍是相当普遍的。
</P>
<P><FONT color=#0000ff
size=3><STRONG>预定义命令</STRONG></FONT></P>
<DIV>
<P>有一些ioctl命令是由内核识别的,当这些命令用于自己的设备时,他们会在我们自己的文件操作被调用之前被解码.
因此,
如果你选择一个ioctl命令编号和系统预定义的相同时,你永远不会看到该命令的请求,而且因为ioctl
号之间的冲突,应用程序的行为将无法预测。预定义命令分为 3 类:</P></DIV>
<DT>
<DIV>
<P>(1)用于任何文件(常规, 设备, FIFO和socket) 的命令</P></DIV>
<DT>
<DIV>
<P>(2)只用于常规文件的命令</P></DIV>
<DT>
<DIV>
<P>(3)特定于文件系统类型的命令 </P></DIV>
<DT>
<DIV>
<P>下列 ioctl 命令是预定义给任何文件,包括设备特定文件:</P>
<DIV class=variablelist>
<DL>
<DT><SPAN class=term><SPAN>FIOCLEX
:</SPAN></SPAN>设置 close-on-exec 标志(File IOctl
Close on EXec)。
<DT><SPAN class=term><SPAN>FIONCLEX
:</SPAN></SPAN>清除 close-no-exec 标志(File IOctl
Not CLose on EXec)。
<DT><SPAN class=term><SPAN>FIOQSIZE
:</SPAN></SPAN>这个命令返回一个文件或者目录的大小; 当用作一个设备文件, 但是,
它返回一个 ENOTTY 错误。
<DT><SPAN
class=term><SPAN>FIONBIO</SPAN></SPAN>:"File
IOctl Non-Blocking I/O"(在"阻塞和非阻塞操作"一节中描述)。
</DT></DL></DIV>
<P><FONT color=#0000ff
size=4><STRONG>使用ioctl参数</STRONG></FONT></P></DIV>
<DT>
<DIV>
<P>在使用ioctl的可选arg参数时,如果传递的是一个整数,它可以直接使用。如果是一个指针,,就必须小心。当用一个指针引用用户空间,
我们必须确保用户地址是有效的,其校验(不传送数据)由函数<FONT color=#ff0000>
access_ok</FONT> 实现,定义在<FONT color=#0000ff>
<asm/uaccess.h></FONT> :
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT face=新宋体><SPAN
style="COLOR: rgb(0,0,255)">int</SPAN>
access_ok<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">int</SPAN> type<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> <SPAN
style="COLOR: rgb(0,0,255)">const</SPAN> <SPAN
style="COLOR: rgb(0,0,255)">void</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>addr<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> <SPAN
style="COLOR: rgb(0,0,255)">unsigned</SPAN>
<SPAN style="COLOR: rgb(0,0,255)">long</SPAN>
size<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;</SPAN>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -