設備驅動程序大概可分爲兩種:
內核驅動程序及用戶空間驅動程序。
內核驅動程序
是內核空間實現的驅動程序,它使用內核資源,內核棧。它包括可加載的內核驅動模塊。在這裏我想主要說說用戶空間驅動程序的編寫。
用戶空間驅動程序
就是指在用戶空間實現的驅動程序。可以認爲,它跟普通的用戶程序沒有什麼兩樣,它使用用戶進程空間和棧。這裏說下,我不是討論接到串口,並口上什麼設備!
大家不要以爲在用戶空間寫驅動程序“不過癮!”。其實,作爲設備驅動程序,其主要做的事就是配置設備寄存器(一家之言)。所以也不用把驅動程序看得那麼“神祕”。當然,有些設備是不合適在用戶空間實現驅動的。個人認爲,使用中斷的設備不宜在用戶空間驅動,除此外,都有辦法在用戶空間來驅動。
在用戶空間實現驅動的一個好處就是,方便用GDB調試。
怎麼寫
上面說到,驅動程序主要做的事情就是配置設備寄存器。那麼,只要在用戶空間,我能獲得到寄存器地址(或是映像地址)那就可以驅動這個設備了。
以PCI設備爲例,假設你的PCI設備主芯片的配置寄存器在PCI配置頭的0x14位置:
首先:通過/proc文件系統的pci文件,及/proc/bus/pci目錄下的文件,可以獲取到你的設備PCI配置頭的信息,當然,也可以讀出0x14偏移的值。假設其值爲0xE0080000;這個就是你的PCI設備主芯片的配置寄存器的物理地址。在有MMU的處理器裏,直接使用這個地址是不允許的。我們都知道,在用戶進程空間尋址一個物理地址,首先要把這個物理地址映射到進程空間內。
怎麼映射呢?
我們知道,在Linux的/dev目錄下,有一個mem的設備。我們簡單認爲它就是管理0x00000000-0xFFFFFFFF(32位處理器)物理地址的一個設備(不單指物理內存,它的空間也含蓋了PCI地址空間)。將PCI配置寄存器空間映射到進程空間的邏輯如下:
fd = open("/dev/mem",O_RDWR);
reg_addr = mmap(NULL,0x100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0xE0080000);
之後,你就可以使用reg_addr這個地址,在0x100大小空間範圍內配置你的PCI設備了。
就這麼完了嗎
在用戶空間實現的驅動程序,不用爲設備建立設備文件。當然,在多個進程使用時,要使用IPC機制來實現設備臨界區的保護等。如果你真的感覺寫用戶空間的設備驅動程序“沒什麼水平”,那你可以用它來調試好你的設備(也是就知道怎麼設置寄存器)後,再花一點時間把它移到內核去,加上文件系統接口,使用內核同步機制進行同步,再實現初始化函數,就可以變成內核驅動了。