?? ch09s02.html
字號:
<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. 使用 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="第 9 章 與硬件通訊"><link rel="prev" href="ch09.html" title="第 9 章 與硬件通訊"><link rel="next" href="ch09s03.html" title="9.3. 一個 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. 使用 I/O 端口</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch09.html">上一頁</a> </td><th width="60%" align="center">第 9 章 與硬件通訊</th><td width="20%" align="right"> <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. 使用 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. I/O 端口分配</h3></div></div></div><p>如同你可能希望的, 你不應當離開并開始抨擊 I/O 端口而沒有首先確認你對這些端口有唯一的權限. 內核提供了一個注冊接口以允許你的驅動來聲明它需要的端口. 這個接口中的核心的函數是 request_region:</p><pre class="programlisting">#include <linux/ioport.h>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. 操作 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 內核頭文件(特別地, 體系依賴的頭文件 <asm/io.h>) 定義了下列內聯函數來存取 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. 從用戶空間的 I/O 存取</h3></div></div></div><p>剛剛描述的這些函數主要打算被設備驅動使用, 但它們也可從用戶空間使用, 至少在 PC-類 的計算機. GNU C 庫在 <sys/io.h> 中定義它們. 下列條件應當應用來對于 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. 字串操作</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 + -