⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch09s02.html

📁 真正LDD3中文
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<html xmlns:cf="http://docbook.sourceforge.net/xmlns/chunkfast/1.0"><head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>9.2.&#160;使用 I/O 端口</title><link rel="stylesheet" href="docbook.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.69.0"><link rel="start" href="index.html" title="Linux 设备驱动 Edition 3"><link rel="up" href="ch09.html" title="第&#160;9&#160;章&#160;与硬件通讯"><link rel="prev" href="ch09.html" title="第&#160;9&#160;章&#160;与硬件通讯"><link rel="next" href="ch09s03.html" title="9.3.&#160;一个 I/O 端口例子"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">9.2.&#160;使用 I/O 端口</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch09.html">上一页</a>&#160;</td><th width="60%" align="center">第&#160;9&#160;章&#160;与硬件通讯</th><td width="20%" align="right">&#160;<a accesskey="n" href="ch09s03.html">下一页</a></td></tr></table><hr></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="UsingIOPort.sect"></a>9.2.&#160;使用 I/O 端口</h2></div></div></div><p>I/O 端口是驱动用来和很多设备通讯的方法, 至少部分时间. 这节涉及可用的各种函数来使用 I/O 端口; 我们也触及一些可移植性问题.</p><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="IOPortAllocation.sect"></a>9.2.1.&#160;I/O 端口分配</h3></div></div></div><p>如同你可能希望的, 你不应当离开并开始抨击 I/O 端口而没有首先确认你对这些端口有唯一的权限. 内核提供了一个注册接口以允许你的驱动来声明它需要的端口. 这个接口中的核心的函数是 request_region:</p><pre class="programlisting">#include &lt;linux/ioport.h&gt;struct resource *request_region(unsigned long first, unsigned long n, const char *name);</pre><p>这个函数告诉内核, 你要使用 n 个端口, 从 first 开始. name 参数应当是你的设备的名子. 如果分配成功返回值是非 NULL. 如果你从 request_region 得到 NULL, 你将无法使用需要的端口. </p><p>所有的的端口分配显示在 /proc/ioports 中. 如果你不能分配一个需要的端口组, 这是地方来看看谁先到那里了.</p><p>当你用完一组 I/O 端口(在模块卸载时, 也许), 应当返回它们给系统, 使用:</p><pre class="programlisting">void release_region(unsigned long start, unsigned long n); </pre><p>还有一个函数以允许你的驱动来检查是否一个给定的 I/O 端口组可用:</p><pre class="programlisting">int check_region(unsigned long first, unsigned long n); </pre><p>这里, 如果给定的端口不可用, 返回值是一个负错误码. 这个函数是不推荐的, 因为它的返回值不保证是否一个分配会成功; 检查和后来的分配不是一个原子的操作. 我们列在这里因为几个驱动仍然在使用它, 但是你调用一直使用 request_region, 它进行要求的加锁来保证分配以一个安全的原子的方式完成.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="ManipulatingIOports.sect"></a>9.2.2.&#160;操作 I/O 端口</h3></div></div></div><p>在驱动硬件请求了在它的活动中需要使用的 I/O 端口范围之后, 它必须读且/或写到这些端口. 为此, 大部分硬件区别8-位, 16-位, 和 32-位端口. 常常你无法混合它们, 象你正常使用系统内存存取一样.<sup>[<a name="id455968" href="#ftn.id455968">33</a>]</sup></p><p>一个 C 程序, 因此, 必须调用不同的函数来存取不同大小的端口. 如果在前一节中建议的, 只支持唯一内存映射 I/O 寄存器的计算机体系伪装端口 I/O , 通过重新映射端口地址到内存地址, 并且内核向驱动隐藏了细节以便易于移植. Linux 内核头文件(特别地, 体系依赖的头文件 &lt;asm/io.h&gt;) 定义了下列内联函数来存取 I/O 端口:</p><div class="variablelist"><dl><dt><span class="term"><span>unsigned inb(unsigned port);</span></span></dt><dd></dd><dt><span class="term"><span>void outb(unsigned char byte, unsigned port);</span></span></dt><dd><p>读或写字节端口( 8 位宽 ). port 参数定义为 unsigned long 在某些平台以及 unsigned short 在其他的上. inb 的返回类型也是跨体系而不同的.</p></dd><dt><span class="term"><span>unsigned inw(unsigned port);</span></span></dt><dd></dd><dt><span class="term"><span>void outw(unsigned short word, unsigned port);</span></span></dt><dd><p>这些函数存取 16-位 端口( 一个字宽 ); 在为 S390 平台编译时它们不可用, 它只支持字节 I/O.</p></dd><dt><span class="term"><span>unsigned inl(unsigned port);</span></span></dt><dd></dd><dt><span class="term"><span>void outl(unsigned longword, unsigned port);</span></span></dt><dd><p>这些函数存取 32-位 端口. longword 声明为或者 unsigned long 或者 unsigned int, 根据平台. 如同字 I/O, "Long" I/O 在 S390 上不可用.</p></dd></dl></div><p>从现在开始, 当我们使用 unsigned 没有进一步类型规定时, 我们指的是一个体系相关的定义, 它的确切特性是不相关的. 函数几乎一直是可移植的, 因为编译器自动转换值在赋值时 -- 它们是 unsigned 有助于阻止编译时的警告. 这样的转换不丢失信息, 只要程序员安排明智的值来避免溢出. 我们坚持这个"未完成的类型"传统贯串本章.</p><p>注意, 没有定义 64-位 端口 I/O 操作. 甚至在 64-位 体系中, 端口地址空间使用一个32-位(最大)的数据通路.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="IOPortAccessfromUserSpace.sect"></a>9.2.3.&#160;从用户空间的 I/O 存取</h3></div></div></div><p>刚刚描述的这些函数主要打算被设备驱动使用, 但它们也可从用户空间使用, 至少在 PC-类 的计算机. GNU C 库在 &lt;sys/io.h&gt; 中定义它们. 下列条件应当应用来对于 inb 及其友在用户空间代码中使用:</p><div class="itemizedlist"><ul type="disc"><li><p>程序必须使用 -O 选项编译来强制扩展内联函数.</p></li><li><p>ioperm 和 iopl 系统调用必须用来获得权限来进行对端口的 I/O 操作. ioperm 为单独端口获取许可, 而 iopl 为整个 I/O 空间获取许可. 这 2 个函数都是 x86 特有的.</p></li><li><p>程序必须作为 root 来调用 ioperm 或者 iopl.<sup>[<a name="id456132" href="#ftn.id456132">34</a>]</sup> 可选地, 一个它的祖先必须已赢得作为 root 运行的端口权限.</p></li></ul></div><p>如果主机平台没有 ioperm 和 iopl 系统调用, 用户空间仍然可以存取 I/O 端口, 通过使用 /dev/prot 设备文件. 注意, 但是, 这个文件的含义是非常平台特定的, 并且对任何东西除了 PC 不可能有用.</p><p>例子源码 misc-progs/inp.c 和 misc-progs/outp.c 是一个从命令行读写端口的小工具, 在用户空间. 它们希望被安装在多个名子下(例如, inb, inw, 和 inl 并且操作字节, 字, 或者长端口依赖于用户调用哪个名子). 它们使用 ioperm 或者 iopl 在 x86下, 在其他平台是 /dev/port.</p><p>程序可以做成 setuid root, 如果你想过危险生活并且在不要求明确的权限的情况下使用你的硬件. 但是, 请不要在产品系统上以 set-uid 安装它们; 它们是设计上的安全漏洞.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="StringOperations.sect"></a>9.2.4.&#160;字串操作</h3></div></div></div><p>除了单发地输入和输出操作, 一些处理器实现了特殊的指令来传送一系列字节, 字, 或者 长字 到和自一个单个 I/O 端口或者同样大小. 这是所谓的字串指令, 并且它们完成任务比一个 C 语言循环能做的更快. 下列宏定义实现字串处理的概念或者通过使用一个单个机器指令或者通过执行一个紧凑的循环, 如果目标处理器没有进行字串 I/O 的指令. 当编译为 S390 平台时这些宏定义根本不定义. 这应当不是个移植性问题, 因为这个平台通常不与其他平台共享设备驱动, 因为它的外设总线是不同的.</p><p>字串函数的原型是:</p><div class="variablelist"><dl><dt><span class="term"><span>void insb(unsigned port, void *addr, unsigned long count);</span></span></dt><dd></dd>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -