一、linux的設備驅動程序與外界的接口可以分為三個部分:
1.驅動程序與操作系統內核的接口。通過file_operations(include/linux/fs.h)數據結構來完成的。
2.驅動程序與系統引導的接口。這部分利用驅動程序對設備進行初始化。
3.驅動程序與設備的接口。這部分描述了驅動程序如何與設備進行交互,與具體的設備密切相關。
二、根據功能劃分,設備驅動程序的代碼有以下幾部分:
1.驅動程序的注冊和注銷。
2.設備的打開和釋放。
3.設備的讀寫操作。
4.設備的控制操作。
5.設備的中斷和輪詢處理。
三、驅動程序的注冊和注銷:
設備驅動程序可以在系統啟動的時候初始化,也可以在需要的時候動態加載。字符設備的初始化由chr_dev_init()完成,包括對內存(devfs_register_chrdev(MEM_MAJOR,"mem",&memory_fops)),終端(tty_init()),打印機(lp_init()),鼠標(misc_init())等字符設備的初始化。
塊設備初始化由blk_dev_init()完成,這包括對IDE硬盤(ide_init()),軟盤(floppy_init()),光驅等塊設備的初始化。
每個字符設備或是塊設備的初始化都是通過devfs_register_chrdev()或是devfs_register_blkdev()向內核注冊。在關閉字符設備或是塊設備時,還需要通過devfs_unregister_chrdev()或是devfs_unregister_blkdev()從內核中注銷設備。
四、設備的打開和釋放:
打開設備是由open()來完成的。例如,打印機是用lp_open()打開的,而硬盤是用hd_open()打開的。在大部分設備驅動程序中,open完成如下工作:
1.增加設備的是用計數。
2.檢查設備的相關錯誤,如設備尚未准備好或是類似硬件的問題。
3.檢查是首次打開,則初始化設備。
4.識別次設備號,如有必要則更新f_op指針。
5.如果需要,分配且設置要放在filp->private_data裡的數據結構。
釋放設備由release()來完成,例如釋放打印機是用lp_release(),而釋放終端設備是用tty_release()。釋放設備的一般步驟包括:
1.釋放在filp->private_data中的open分配的內存。
2.如果是最後一次釋放,則關閉設備。
3.遞減設別的使用計數。
五、設備的讀寫操作:
字符設備使用各自的read()和write()來進行數據讀寫。例如,對虛擬終端的讀寫是通過vcs_read()和vcs_write()來進行數據讀寫的。
塊設備使用通用的generic_file_read()和generic_file_write()來進行數據讀寫。這兩個通用函數向請求表添加讀寫請求,內核可以通過ll_rw_block()優化請求順序。由於是對內存緩沖區而不是設備進行操作的,因而可以加快讀寫請求。如果內存緩沖區內沒有要讀入的數據或是要將寫請求寫入設備,那麼就要真正的執行數據傳輸。這是通過數據結構request_queue和request_fn()來完成(include/linux/blkdev.h)。
六、設備的控制操作:
除了讀寫操作,有時還要控制設備。這可以通過設備驅動程序中的ioctl()來完成。例如IDE硬盤的控制可以通過hd_ioctl(),對光驅的控制可以通過cdrom_ioctl()。
與讀寫操作不同,ioctl()的用法與具體設備密切相關。以軟驅的floppy_ioctl()為例(drivers/block/floppy.c):
static int fd_ioctl(struct inode *inode,
struct file *filp,
unsigned int cmd,
unsigned long param);
其中,cmd的取值及含義都是與軟驅有關的,比如,FDEJECT表示彈出軟盤。
除了ioctl(),設備驅動程序還可能有其他控制函數,比如llseek()等。
七、設備的輪詢和中斷處理:
對於不支持中斷的設備,讀寫時需要輪詢設備狀態,以及是否需要繼續進行數據傳輸。例如,打印機。如果設備支持中斷,則可按照中斷方式進行