- 論壇徽章:
- 0
|
2009.7.2
Chapter 9 Communicating with Hardware
I/O Ports and I/O Memory
ISA設(shè)備才影射到io呢, PCI一般影射到內(nèi)存. x86個(gè)變態(tài)?
訪問(wèn)io空間需要防止編譯器和cpu的優(yōu)化帶來(lái)的不確定因素. 硬件cache可以通過(guò)page禁止. 編譯器優(yōu)化就得.. barrier了
*#include
void barrier(void) 禁止編譯器優(yōu)化
*#include
void rmb(void); //保證之前的讀操作,在之后的讀操作之前完成
void read_barrier_depends(void); //rmb的弱化版, rmb保證所有read的order, 這個(gè)只保證不reorder依賴read
void wmb(void);//類rmb
void mb(void); //保證rmb和wmb
不懂read_barrier_depends就別亂用.
![]()
![]()
http://lse.sourceforge.net/locking/wmbdd.html
原因: 一些cpu(alpha)不支持cache同步invalidation.
![]()
注意search中的rmb.
某些cpu的cache語(yǔ)意導(dǎo)致看起來(lái)p->key的讀取可能早于在p=head.next!!!!, 這種cpu順著鏈表的讀取是有問(wèn)題的,如:d = p->next->data;
![]()
比如alpha之流有上面形式的cache. 而wmb只保證invalide小時(shí)是p->key先更新,而head.next=p是后更新的.
如
果cache0,1分別存放偶數(shù)/奇數(shù)地址的地址. 而key在bank0, head在bank1.
雖然key的invalide先到reading cpu的cache, 但是如果bank0的隊(duì)列忙,
將會(huì)導(dǎo)致bank1的head先得到更新而key是垃圾. 而且alpha的cpu 依賴的read
也沒(méi)有等待操作(沒(méi)有把早點(diǎn)到達(dá)的invalid消息處理完), 導(dǎo)致不一致的結(jié)果出現(xiàn).
解決方法1: 吧wmb 換成wmbdd
解決方法2: rmb換成read_barrie_depends (只有alpha是rmb其他體系是空)
另外參考RUC:
http://lse.sourceforge.net/locking/rclock.html
msdn的參考:
http://blogs.msdn.com/itgoestoeleven/archive/2008/03/11/the-joys-of-compiler-and-processor-reordering-why-you-need-the-read-side-barrier.aspx
void smp_rmb(void);
void smp_read_barrier_depends(void);
void smp_wmb(void);
void smp_mb(void);
These versions of the barrier macros insert hardware barriers only when the kernel
is compiled for SMP systems; otherwise, they all expand to a simple barrier
call. (cpu-cpu之間保序,用smp_在UP的時(shí)候會(huì)速度更好, 如果CPU->外設(shè), 永遠(yuǎn)不要用smp_xxx)
典型的驅(qū)動(dòng)使用wmb:
writel(dev->registers.addr, io_destination_address);
writel(dev->registers.size, io_size);
writel(dev->registers.operation, DEV_READ);
wmb( );
writel(dev->registers.control, DEV_GO)
(在x86上寫出cpu外的指令不會(huì)reorder, 但是read會(huì))
atomic操作和spinlock帶有barrier功能.
內(nèi)核實(shí)現(xiàn)的復(fù)制附帶barrier(這是默認(rèn)值, 有的體系結(jié)構(gòu)有專用的優(yōu)化)
#define set_mb(var, value) do {var = value; mb( );} while 0
#define set_wmb(var, value) do {var = value; wmb( );} while 0
#define set_rmb(var, value) do {var = value; rmb( );} while 0
Using I/O Ports
聲明占有i/o region:
#include
struct resource *request_region(unsigned long first, unsigned long n, const char *name); /*失敗返回NULL*
/
/proc/ioports.
void release_region(unsigned long start, unsigned long n);
int check_region(unsigned long first, unsigned long n); //已經(jīng)不再使用,不能保證接下來(lái)的分配成功
Manipulating I/O ports
)
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port); //不同體系結(jié)構(gòu)的數(shù)據(jù)類型會(huì)不同
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);//這4個(gè)s390不能用, s390只支持byte io
unsigned : 代表體系相關(guān). 但是賦值的時(shí)候會(huì)自動(dòng)轉(zhuǎn)換,也沒(méi)有警告.
I/O Port Access from User Space
*
*編譯的時(shí)有用 -O, 確保inline被展開
*調(diào)用ioperm(單個(gè)端口) 或者iopl,(全部), x86特定
*必須有root權(quán)限, 準(zhǔn)確的是CAP_SYS_RAWIO
可以通過(guò)/dev/port來(lái)訪問(wèn), 不過(guò)也只有pc用
String Operations //no endian考慮
同樣不適用于s390.(問(wèn)題不大, 390幾乎不和別的體系共享設(shè)備, 因?yàn)榭偩不同)
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
Pausing I/O
i386比較明顯, 比如超頻的cpu和ISA板子. _p , 要不要_p估計(jì)都是試驗(yàn)得出的?
Platform Dependencies
io操作不可避免的,是特定體系結(jié)構(gòu)相關(guān)的.
IA-32 (x86)
x86_64 :supports all the functions described in this chapter. Port numbers are of type unsigned short.
IA-64 (Itanium) :All functions; ports,unsigned long (memory-mapped).String func implemented in C.
Alpha :All the functions supported(diffrent per cpu), ports memory-mapped. String Ports are unsigned long.
ARM : Ports are memory-mapped,all supported; string in C. Ports are of type unsigned int.
Cris :does not support the I/O port abstraction; the various port operations are defined to do nothing at all.
M68k M68k-nommu : memory-mapped. String functions are supported, unsigned char *.
MIPS MIPS64 :supports all , string ( with tight assembly loops), memory-mapped; unsigned long.
PA-RISC :All func supported; ports is int on PCI-based systems and unsigned short on EISA ,string: unsigned long port numbers.
PowerPC PowerPC64 : All func supported; unsigned char * on 32-bit systems, unsigned long on 64-bit
S390: Similar to the M68k, only byte I/O no string operations. Ports are char pointers memory-mapped.
Super-H : Ports are unsigned int (memory-mapped), and all the functions are supported.
SPARC SPARC64 : memory-mapped. Versions of the port functions are defined to work with unsigned long ports.
除了x86沒(méi)有體系是io單獨(dú)地址空間的. 另外,特別是早期的alpha,不能一次移動(dòng)單個(gè)或兩個(gè)字節(jié), inb, inw實(shí)現(xiàn)為在不同的地址上進(jìn)行兩次32bit read操作.
An I/O Port Example
digital I/O port: 讀寫直接對(duì)應(yīng)于引腳電平.
GPIO: 通用數(shù)字i/o端口. 一般可以被兩個(gè)io地址控制:一個(gè)地方可以選擇哪個(gè)pin是out,哪個(gè)是in, 另外一個(gè)地方可以read/write邏輯電平.
更見單的數(shù)字io可能定死了, 哪些是in, 哪些是out,: 比如 parallel port.
An Overview of the Parallel Port
*3個(gè) 8bit的端口構(gòu)成,就是24個(gè)not so GPIO
*第一個(gè)并口在0x378這個(gè)位置, 第二個(gè)在0x278
*第一個(gè)端口是雙向數(shù)據(jù)端口, 直接鏈接到pin 2-9
*第二個(gè)端口是只讀的狀態(tài)端口,如果鏈接打印機(jī),則能報(bào)告缺紙,忙等狀態(tài)
*第三個(gè)端口是只寫的控制端口, 比如使能中斷等.
*信號(hào): standard transistor-transistor logic (TTL) levels: 0 and 5 volts,the logic threshold at about 1.2 volts
* 至少能滿足the standard TTL LS current ratings (起始許多都能做得更好啦)
*并口電路沒(méi)有和內(nèi)部電路隔離, 方便加接邏輯門,但是容易損害主板. 往電路加接optoisolators或者,買個(gè)plugin的并口吧.
![]()
*共12個(gè)輸出bit(data 8+ control 4),, 5個(gè)輸入bit (5個(gè)狀態(tài)bit)
*bit 4 (0x10) of port 2是irq控制bit, 沒(méi)有pin對(duì)應(yīng)
*一些pin是做了邏輯翻轉(zhuǎn)的.
driver short (Simple Hardware Operations and Raw Tests)
為每個(gè)端口創(chuàng)建一個(gè)設(shè)備節(jié)點(diǎn).查看/proc/ioports 有無(wú)driver使用相應(yīng)端口. 可以為每個(gè)端口接一個(gè)LED,每個(gè)LED有1k的電阻. (ldd3 用pin9 和10做演示, 如果完全按照l(shuí)dd3的玩法, 不要接led到些pin上).
/dev/short0 => 0x378
/dev/short1 => short0+1
....
/dev/short0p => 用outb_p
/dev/short0s => 用 outb_s
while (count--) {
outb(*(ptr++), port);
wmb( ); /*防止被優(yōu)化掉,保障順序*/
}
//點(diǎn)亮led
echo -n "any string" > /dev/short0 //只有最后一個(gè)字符能保持在led帶上,所以用-n,不要加回車,否則最后燈的狀態(tài)不變
//讀取
dd if=/dev/short0 bs=1 count=1 | od -t x1
Using I/O Memory
還是內(nèi)存影射的io操作為主流. 和設(shè)備內(nèi)存一起通稱 i/o memory. (寄存器和內(nèi)存軟件看起來(lái)一樣). 這里主要討論ISA和PCI的設(shè)備內(nèi)存.
設(shè)備內(nèi)存默認(rèn)一般還沒(méi)有建立起頁(yè)表,所以需要先ioremap一下. 最好用io訪問(wèn)宏來(lái)訪問(wèn)io內(nèi)存, 以便于優(yōu)化和良好的可移植性.
*
*struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);
/proc/iomem
void release_mem_region(unsigned long start, unsigned long len);
int check_mem_region(unsigned long start, unsigned long len); //別用,知道就成
*#include
void *ioremap(unsigned long phys_addr, unsigned long size);
void *ioremap_nocache(unsigned long phys_addr, unsigned long size);
void iounmap(void * addr);
Accessing I/O Memory
* , 為了移植, 不要用pointer訪問(wèn), 盡量
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
//在同一個(gè)地址上的rep
void ioread8_rep(void *addr, void *buf, unsigned long count);
void ioread16_rep(void *addr, void *buf, unsigned long count);
void ioread32_rep(void *addr, void *buf, unsigned long count);
void iowrite8_rep(void *addr, const void *buf, unsigned long count);
void iowrite16_rep(void *addr, const void *buf, unsigned long count);
void iowrite32_rep(void *addr, const void *buf, unsigned long count);
void memset_io(void *addr, u8 value, unsigned int count);
void memcpy_fromio(void *dest, void *source, unsigned int count);
void memcpy_toio(void *dest, void *source, unsigned int count);
//一些老的,不鼓勵(lì)使用的宏
unsigned readb(address);
unsigned readw(address);
unsigned readl(address);
void writeb(unsigned value, address);
void writew(unsigned value, address);
void writel(unsigned value, address);
Ports as I/O Memory
一些硬件, 有的版本用i/o 端口, 一些用io memory. linux2.6 實(shí)現(xiàn)了一種port as io memory的手段.
void *ioport_map(unsigned long port, unsigned int count);
void ioport_unmap(void *addr);
request_region仍然是需要的.
ISA Memory Below 1 MB
640 KB (0xA0000) 到1 MB(0x100000).
driver silly, (Simple Tool for Unloading and Printing ISA Data)
訪問(wèn)348k內(nèi)存, 類似/dev/mem.
#define ISA_BASE 0xA0000
#define ISA_MAX 0x100000 /* for general memory access */
/* this line appears in silly_init */
io_base = ioremap(ISA_BASE, ISA_MAX - ISA_BASE);
/dev/sillyb --> iowrite8 ioread8
isa_readb and Friends
不要用, 是為過(guò)渡使用的. 帶有這個(gè)前綴的可以跳過(guò)ioremap的過(guò)程.
本文來(lái)自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u2/79526/showart_1996384.html |
|