---
# System prepended metadata

title: 'Linux device driver -- Comunicating with Hardware, CH9'

---

# 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)

