# Linux device driver -- Comunicating with Hardware, CH9 這個章節在討論driver怎麼access I/O ports and I/O memory # I/O Ports and I/O memory Every peripheral device is controlled by writing and reading its registers. Most of thetime a device has several registers, and they are accessed at consecutive addresses, either * in the memory address space or * in the I/O address space. 從hardware的角度來看,memory regions跟I/O regions是沒有分別的,他們都是透過送出電子訊號到address bus跟control bus(i.e. the read and write signals) 跟從data bus讀或寫東西 CPU設計上(X86)透過read/write I/O ports上的電子訊號跟CPU指令來存取ports,大多數的PCI devices會把register mapping到記憶體來讓使用者讀寫。(這樣就不需要其他特別的CPU指令來讀寫了) # I/O Registers and Conventional Memory I/O registers跟RAM有個主要區別是I/O operations有side effects, memory operations則無。(工三小) driver必須確保讀register的時候,cpu必須不作caching且不能自己亂調read/write指令順序(volatile or memory barrier),linux有提供四個barrier機制來讓hardware自己作保護 * void rmb(void); * void read_barrier_depends(void); * void wmb(void); * void mb(void); kernel是SMP systems時 * void smp_rmb(void); * void smp_read_barrier_depends(void); * void smp_wmb(void); * void smp_mb(void); 典型的用法在device driver ![](https://i.imgur.com/nXkx8sZ.png) 這個case代表你在開始control之前,要先確保資料全部都設定對了,所以中間插了一個barrier,但用這個會影響performance喔,所以不要亂用,有些architecture有提供進階版的memory barrier. * #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 ports就是drivers跟devices溝通的橋樑 # I/O Port Allocation first, 你一定要有存取這個I/O ports的權限,你要先註冊 ![](https://i.imgur.com/FvFBdkc.png) 這個註冊告訴kernel你想用 n ports, 從first開始(怎麼有點像挑小姐),name就是你的device name. 成功回傳非0,失敗回傳NULL。 所有的port配置可以去/proc/ioports去看,你如果被拒絕就先來這看看 ![](https://i.imgur.com/3brYnGq.png) driver被拔掉時(module unload time),應該要把region放掉 ![](https://i.imgur.com/uMoFnGU.png) 你還可以先去check到底能不能用這組I/O ports ![](https://i.imgur.com/TxHyxsS.png) 但這個不推薦使用,因為他不是atomic的XDDD哪招 也就是說他跟你說你能用但你也不一定能用(check跟之後allocate的動作是分開的),但你永遠就是要用request_region就對了 # Manipulating I/O ports 你要求存取 I/O ports之後,就可以進去read/write了 大部分的hardware會區分出 * 8-bits ports * 16-bits ports * 32-bits ports 你不能混和的去存取這些,你只能用不同的functions去存取不同size的ports kernel用 memory-mapped I/O registers來假裝 port I/O,用remapping port addresses to memory addresses的方式來達成。(這邊是抽象化去作的,driver不會察覺這件事(portability issue)),Linux把這些抽象化的header(architecture-dependent)放在<asm/io.h>,定義了以下存取 I/O ports的方式 * 8bits * ![](https://i.imgur.com/DTACn7o.png) * 16 bits * ![](https://i.imgur.com/jZjqasW.png) * 32 bits * ![](https://i.imgur.com/d8IRbKz.png) 沒有64bits的版本,就算在64-bits architectures上,最多就是32-bits 還是有例外 ![](https://i.imgur.com/oIlN3rI.png) # I/O Port Access from User Space 不只是device drivers可以access I/O Ports, user space也可以唷~~~~~ The GNU C library defines them in <sys/io.h> 但必須遵守下面的規定 ![](https://i.imgur.com/wMH7kB0.png) 如果host上沒有這些system calls * ioperm * iopl user space還能透過/dev/port device node去access I/O ports,有兩隻sample code可以參考 * misc-progs/inp.c * misc-progs/outp.c # String Operations 除了簡單的 in and out operations. 還有transfer a sequence of bytes, words, or longs to and from a single I/O port or the same size. * Read or write count bytes starting at the memory address addr. Data is read from or written to the single port port ![](https://i.imgur.com/N1xerGh.png) * Read or write 16-bit values to a single 16-bit port ![](https://i.imgur.com/D6ojFjR.png) * Read or write 32-bit values to a single 32-bit port ![](https://i.imgur.com/dtBLDhE.png) # Pausing I/O 當processor送資料給bus速度太快時,需要溫柔一點,解法是insert a small delay在每個I/O指令之後。如果你怕device掉資料,用用看這些 ![](https://i.imgur.com/ymmcGGX.png) # Platform Dependencies I/O instructions天生的就是高度processor dependent. 每個systems之間的實作是天差地遠的,所以,幾乎所有的 port I/O都是 platform-dependent. ![](https://i.imgur.com/8B6dDVz.png) ![](https://i.imgur.com/pgnJxYh.png) ![](https://i.imgur.com/7nePdIA.png) # An I/O Port Example A digital I/O port, in its most common incarnation,最常見的是 byte-wide I/O location, either memory-mapped or port-mapped. # An Overview of the PC Parallel Port ![](https://i.imgur.com/cW9z0nz.png) ![](https://i.imgur.com/VrYXJo0.png) # A Sample Driver short(Simple Hardware Operations and Raw Tests).範例教學,簡單read/write 幾個8bits的 ports,By default, 他使用assigned給parallel interface(一種hardware)的port range. * Each device node(with a unique minor number) accesses different port. 第一個要讓hardware把access權限給short driver. 如果有衝突的話就去查看 /proc/ioports看看是誰佔用了hardware. # Using I/O Memory 盡管 I/O ports在X86的世界很受歡迎,但主要跟硬體溝通的機制還是透過 memory-mapped registers and device memory. 兩者都叫做 I/O memory. I/O memory是什麼 * 因為是被抽象出來的東西所以理解邏輯上他是什麼就好 * 邏輯上就像是一塊RAM讓device跟cpu透過bus溝通 * can hoding video data or network packets * or implementing device registers that behave just like I/O ports * access I/O memory的方法是architecture dependent的, bus, deivce怎麼被使用等等 * 這章節主要討論ISA and PCI memory. * 根據不同平台,I/O memory可能不能透過page tables access. * 要讓page table能work要讓driver直接摸physical address. * 代表做I/O之前要做ioremap * 如果不需要page tables, I/O memory locations跟I/O ports基本一樣,直接讀寫I/O ports就好 * 不管需不需要用到ioremap來access I/O memory,都不鼓勵直接用Pointers去指向IO memory,就算 I/O memory在HW level被定址成就像RAM一樣,一定要用wrapper function來安全的存取(直接用指標可能還是可以work,但很危險(應該是driver在不同平台的portability的考量QQ)) # I/O Memory Allocation and Mapping I/O memory regions一樣用之前要先配置 ![](https://i.imgur.com/8kqaxYD.png) 所有的I/O memory allocations在 /proc/iomem 不用要free掉 ![](https://i.imgur.com/GdFj6xR.png) ![](https://i.imgur.com/kWg8g75.png) # ioremap在搞什麼? 配置完 I/O memory之後不代表就可以直接用了,你還要確定這塊 I/O memory在kernel裡面是合法使用的,在很多系統上,你不能直接dereferencing a pointer from剛拿到的這塊記憶體的Pointer. 你還需要建立mapping先。就是ioremap的工作。 回頭看 ![](https://i.imgur.com/V2NjDqm.png) 只要裝上了ioremap and iounmap, device driver就可以存取任何 I/O memory address,不管是不是直接mapped到virtual address space. * 不過從ioremap return回來的addresses不能直接被dereferenced! 必須要用一個特別的kernel functions(雖然有些platform上你這樣做還是能work.但還是一定會遭遇portability的問題).等等會談,先來看看ioremap的interface ![](https://i.imgur.com/WgANMfK.png) # Accessing I/O Memory 要正確的access ioremap後的pointer要用 這邊*addr就是ioremap後得到的address pointer return value會是從I/O memory讀出來的data ![](https://i.imgur.com/KLsjRD2.png) write ![](https://i.imgur.com/CZc13un.png) read/write ![](https://i.imgur.com/tyX0Gko.png) ![](https://i.imgur.com/CVjx800.png) These functions read or write *count* values from the given *buf* to the given *addr* 如果你需要存取block of I/O memory, 可以用這些 ![](https://i.imgur.com/BSUkpP4.png) 如果去看kernel舊一點的code,可以看到許多比較老的操作,還是能work,但不推薦使用。 read 8-bit, 16-bit, and 32-bit data values from I/O memory ![](https://i.imgur.com/WlvzkZ7.png) write 8-bit, 16-bit, and 32-bit data items ![](https://i.imgur.com/W2K9g5L.png) 64bits系統可能有 * readq (quad-word = 8bytes) * writeq # Ports as I/O memory 有些HW很智障,有些版本用I/O ports, 其他用I/O memory. 從CPU的角度來看沒差,都是吐registers給我,但access method會有差別。為了讓drivers可以好好過生活,不要一直加班,2.6 kernel提供了一個機制 * ioport_map ![](https://i.imgur.com/amaZuGj.png) 這個function把count I/O ports轉換成 I/O memory. 這樣driver以後就直接跟I/O memory打交道就好 這個東西沒用到要free掉喔 ![](https://i.imgur.com/CySIU3h.png) note: I/O ports還是要先allocated by request_region後才能使用喔 # ISA memory的例子 ![](https://i.imgur.com/mp8QFzq.png) # sample module ![](https://i.imgur.com/ZdHFqkZ.png) ![](https://i.imgur.com/rsVayrs.png) ![](https://i.imgur.com/OVR1wNp.png)