Linux 驅動程式觀念解析, #5: 依流程來實作 -- Virtual Device Driver

jollen 發表於 May 4, 2006 9:53 AM

根據Linux驅動程式的一般化設計流程,我們來設計一個真正可以動的驅動程式。

作者/陳俊宏
www.jollen.org

根據流程寫程式

定義 file_operations

struct file_operations card_fops = {
	open:		card_open,
	write:		card_write,
	release:	card_release,
	ioctl:		card_ioctl,
};

由此定義可以,我們所計的驅動程式將提供 open/write/close(release)/ioctl 4 個 system call 介面給 user application。

實作 System Call

接著要實作我們所提供的4個 system call。open/close(即 release)/read/write/ ioctl 是初學 Linux 驅動程式最重要的 5 個 system call,了解如何實作不同的 system call,是學好 Linux 驅動程式的重要工作。

本文先介紹 open/close(release)/write 的實作。此部份說明如後。

註冊 Driver

將driver自己「註冊」到kernel的VFS層,註冊時所要呼叫的函數根據裝置類型的不同而不同。

將驅動程式「註冊」(registration)至kernel的動作必須在init_module()函數裡實作。根據裝置類型的不同,所呼叫的函數也不同,以下是幾個基本的裝置註冊函數:

˙ int register_chrdev(unsigned int major, const char * name, struct file_operations *fops):註冊字元型驅動程式。
˙ int register_blkdev(unsigned int major, const char *name, struct file_operations *fops):註冊區塊型驅動程式。
˙ int usb_register(struct usb_driver *new_driver):註冊USB驅動程式。
˙ int pci_register_driver(struct pci_driver *):註冊PCI驅動程式。

本文範例註冊驅動程式的程式片斷如下:

#define	DEV_MAJOR	121
#define	DEV_NAME	"debug"	
#define MSG(format, arg...) printk(KERN_INFO "DEBUG CARD: " format "\n", ## arg)

int init_module(void)
{
MSG("DEBUG CARD v0.1.1");
MSG(" Copyright (C) 2004 www.jollen.org");

if (register_chrdev(DEV_MAJOR, DEV_NAME, &card_fops) < 0) {
MSG("Couldn't register a device.");
return -1;
}

return 0;
}



register_chrdev()參數說明如下:



˙ 第1個參數:為device file的major number。該device file應在Linux系統底下以root身份手動建立。
˙ 第2個參數:
˙ 第3個參數:為驅動程式的fops。



註冊的動作是寫在init_module()裡,因此當使用者執行insmod載入驅動程式時,register_chrdev()便會執行。由此可知,註冊驅動程式的時機為insmod時。相對的,在rmmod時,必須執行解除註冊的動作,此動作必須實作在cleanup_module()函數裡。


前面所介紹的4個註冊函數,其相對應的解除註冊函數如下:



˙ int unregister_chrdev(unsigned int major, const char * name) :解除註冊字元型驅動程式。
˙ int unregister_blkdev(unsigned int major, const char *name) :解除註冊區塊型驅動程式。
˙ void usb_deregister(struct usb_driver *driver):解除註冊USB驅動程式。
˙ pci_unregister_driver(struct pci_driver *drv) :解除註冊PCI驅動程式。



範例debug card 0.1.0解除註冊的程式片斷如下:



void cleanup_module(void)
{
if (unregister_chrdev(DEV_MAJOR, DEV_NAME))
MSG("failed to unregister driver");
else
MSG("driver un-installed\n");
}


Linux驅動程式的「註冊」是一個非常重要的動作,這個動作代表 Linux 驅動程式是一個嚴謹的分層式架構;換句話說,
Linux驅動程式的分層(layered)關係可透過「註冊」的程序來分析。


定義chipset標頭檔


我們所要設計的 Port 80H 除錯卡驅動程式,不需要定義標頭檔;此部份可參考 kernel 裡的 BTTV 驅動程式。


定義I/O wrapper function


我們所要設計的 Port 80H 除錯卡驅動程式,不需要定義 I/O wrapper function;此部份可參考 kernel 裡的 BTTV
驅動程式。


實作chipset控制函數


在我們所要設計的 Port 80H 除錯卡驅動程式中,我們是直接使用 kernel 的 I/O 介面來控制除錯卡,physiacl device
driver 的部份將在下一篇文章再做說明。


open/release實作


open與release是Linux驅動程式最基本的2個system call。驅動程式應先實作此2個system call。








為了方便說明起見,本文後文將以「fops->open」表示實作open system call的driver function。其它system call亦同。



open與release system call的執行時機如下:



1. 當user application執行open()函數時,便呼叫Linux kernel的open system call,即執行fops->open。


2. 當user application執行close()函數時,便呼叫Linux kernel的close system
call,即執行fops->release。



Linux驅動程式註冊至kernel時會指定device file的major number,user application便可以透過此符合此major
number的device file與硬體溝通,即Linux驅動程式是透過VFS架構層與user
application溝通。


file_operation是Linux驅動程式支援VFS的重要結構。學習file_operation的重要目的如下:



1. 了解每一個system call的用途。


2. 了解每一個system call的實作「原則」。



System call的實作原則即driver function所要負責處理的基本工作。學習Linux device driver的重要工作之一,便是一一了解fops裡每一個system call的實作原則,並依照實際需求來實作不同的 system call。


open System Call


以open system call為例,fops->open是在user呼叫open()函數時執行,即當user開啟driver所指定的device file時呼叫fops->open。


fops->open實作原則如下:



  1. 將usage count加一(increment)

  2. 檢查inode->i_rdev

  3. 檢查裝置是否錯誤

  4. 初始化裝置

  5. 將驅動程式自己的資料結構放到filp->private_data


以下是本範例的fops->open實作:



int card_open(struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
return 0;
};


release System Call


當user application 呼叫close() 函數後,便執行fops->release。


有些驅動程式會將release method函數名稱命名為 XXX_close(),但建議以XXX_release()名稱為主,以避免混淆。


fops->release實作原則如下:



  • 將 usage count 減一。



以下是本範例的fops->release實作:



int card_release(struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
kfree(filp->private_data); //reentrant code 觀念 (本文尚未說明)

return 0;
};


Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue

您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw