linux驅動程式設計(轉)
linux驅動程式設計(轉)[@more@]PROGRAM FOR BLOCK DEVICE DRIVER OF DEVFS TYPE對linux的devfs型別的驅動程式的編寫可以從以下幾大內容理解和入手:透過分析驅動程式原始碼可以發現驅動程式一般可分三部分:核心資料結構;核心資料和資源的初始化,註冊以及注消,釋放;底層裝置操作函式;A.核心資料結構struct file_operations fops 裝置驅動程式介面struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char *, size_t, loff_t *);ssize_t (*write) (struct file *, const char *, size_t, loff_t *);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);};block_device_operations 塊裝置驅動程式介面{ int (*open) (struct inode *, struct file *);int (*release) (struct inode *, struct file *);int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);int (*check_media_change) (kdev_t);int (*revalidate) (kdev_t);struct module *owner;};塊裝置的READ().WRITE()不在這裡註冊,而是在裝置的讀寫請求佇列裡註冊,核心在這裡將呼叫通用的blk_read(),blk_write().向讀寫佇列發出讀寫請求.Linux 利用這些資料結構向核心註冊open(),release(),ioctl(),check_media_change(),rvalidate()等函式的入口控制程式碼.我們將要編寫的open(),release(),ioctl(),check_media_change(),revalidate()等函式,將在驅動初始化的時候,透過一個此結構型別的變數向核心提供函式的 入口.struct request_queue_t 裝置請求佇列的資料結構struct request_list {unsigned int count;unsigned int pending[2];struct list_head free;};struct request {struct list_head queue;int elevator_sequence;kdev_t rq_dev;int cmd; /* READ or WRITE */int errors;unsigned long start_time;unsigned long sector;unsigned long nr_sectors;unsigned long hard_sector, hard_nr_sectors;unsigned int nr_segments;unsigned int nr_hw_segments;unsigned long current_nr_sectors, hard_cur_sectors;void * special;char * buffer;struct completion * waiting;struct buffer_head * bh;struct buffer_head * bhtail;request_queue_t *q;};struct request_queue{/** the queue request freelist, one for reads and one for writes*/struct request_list rq;/** The total number of requests on each queue*/int nr_requests;/** Batching threshold for sleep/wakeup decisions*/int batch_requests;/** The total number of 512byte blocks on each queue*/atomic_t nr_sectors;/** Batching threshold for sleep/wakeup decisions*/int batch_sectors;/** The max number of 512byte blocks on each queue*/int max_queue_sectors;/** Together with queue_head for cacheline sharing*/struct list_head queue_head;elevator_t elevator;request_fn_proc * request_fn;merge_request_fn * back_merge_fn;merge_request_fn * front_merge_fn;merge_requests_fn * merge_requests_fn;make_request_fn * make_request_fn;plug_device_fn * plug_device_fn;/** The queue owner gets to use this for whatever they like.* ll_rw_blk doesn't touch it.*/void * queuedata;/** This is used to remove the plug when tq_disk runs.*/struct tq_struct plug_tq;/** Boolean that indicates whether this queue is plugged or not.*/int plugged:1;/** Boolean that indicates whether current_request is active or* not.*/int head_active:1;/** Boolean that indicates you will use blk_started_sectors* and blk_finished_sectors in addition to blk_started_io* and blk_finished_io. It enables the throttling code to* help keep the sectors in flight to a reasonable value*/int can_throttle:1;unsigned long bounce_pfn;/** Is meant to protect the queue in the future instead of* io_request_lock*/spinlock_t queue_lock;/** Tasks wait here for free read and write requests*/wait_queue_head_t wait_for_requests;struct request *last_request;};緩衝區和對緩衝區相應的I/O操作在此任務佇列中相關聯,等待核心的排程.如果是字元裝置就不需要此資料結構.而塊裝置的read(),write()函式則在buffer_queue的initize和裝置請求佇列進行處理請求時候傳遞給request_fn().struct request_queue_t{}裝置請求佇列的變數型別,驅動程式在初始化的時候需要填寫request_fn().其他的資料結構還有 I/O port,Irq,DMA 資源分配,符合POSIX標準的ioctl的cmd的構造和定義,以及描述裝置自身的相關資料結構定義-如裝置的控制暫存器的相關資料結構定義,BIOS裡的引數定義,裝置型別定義等.B.初始化和註冊和注消,模組方式驅動程式的載入和解除安裝.裝置驅動程式在定義了資料結構後 ,首先開始初始化:如I/O 埠的檢查和登記,核心對 I/O PORT的檢查和登記提供了兩個 函式check_region(int io_port, int off_set)和request_region(int io_port, int off_set,char *devname).I/O Port登記後,就可以用inb()和outb()進行操作了 .還有DMA和Irq的初始化檢查和 登記,int request_irq(unsigned int irq ,void(*handle)(int,void *,struct pt_regs *),unsigned int long flags,const char *device);irq: 是要申請的中斷。handle:中斷處理函式指標。flags:SA_INTERRUPT 請求一個快速中斷,0 正常中斷。device:裝置名。如果登記成功,返回0,這時在/proc/interrupts檔案中可以看你請求的中斷。DMA主要是在記憶體中分配交換記憶體空間.還有緩衝區,裝置請求佇列的初始化.還有裝置控制暫存器的檢查和初始化,還有對裝置自身相關的資料結構的初始化,填寫一些裝置特定的資料等.然後,開始註冊devfs_register()向VFS註冊統一的裝置操作函式.static struct file_operations XXX_fops = {owner: THIS_MODULE, XXX_fops所屬的裝置模組read: XXX_read, 讀裝置操作write: XXX_write, 寫裝置操作ioctl: XXX_ioctl, 控制裝置操作mmap: XXX_mmap, 記憶體重對映操作open: XXX_open, 開啟裝置操作release: XXX_release 釋放裝置操作/* ... */};blk_init_queue()佇列初始化函式.request_irq()中斷註冊函式相應的注消函式:devfs_unregister (devfs_handle_t de){};free_irq()釋放中斷,I/O資源,釋放緩衝區,釋放裝置,請求佇列,VFS節點等.模組方式驅動程式的載入和解除安裝.static int __init _init_module (void){/* ... */}static void __exit _cleanup_module (void){}/* 載入驅動程式模組入口 */module_init(_init_module);/* 解除安裝驅動程式模組入口 */module_exit(_cleanup_module);_intrrupt()裝置發生中斷時的處理程式.{1.對共享中斷的處理;2.對spinlock以及其他的事務的處理;}C. 底層裝置操作函式的編寫read().write(),open(),release(),check_media_change(),revalidate()等.open()和release()開啟裝置是透過呼叫file_operations結構中的函式open( )來完成的,它是驅動程式用來為今後的操作完成初始化準備工作的。在大部分驅動程式中,open( )通常需要完成下列工作:1. 檢查裝置相關錯誤,如裝置尚未準備好等。2. 如果是第一次開啟,則初始化硬體裝置。3. 識別次裝置號,如果有必要則更新讀寫操作的當前位置指標f_ops。4. 分配和填寫要放在file->private_data裡的資料結構。5. 使用計數增1。釋放裝置是透過呼叫file_operations結構中的函式release( )來完成的,這個裝置方法有時也被稱為close( ),它的作用正好與open( )相反,通常要完成下列工作:1. 使用計數減1。2. 釋放在file->private_data中分配的記憶體。3. 如果使用計算為0,則關閉裝置。read()和 write()字元裝置的讀寫操作相對比較簡單,直接使用函式read( )和write( )就可以了。但如果是塊裝置的話,則需要呼叫函式block_read( )和block_write( )來進行資料讀寫,這兩個函式將向裝置請求表中增加讀寫請求,以便Linux核心可以對請求順序進行最佳化。由於是對記憶體緩衝區而不是直接對裝置進行操作的,因此能很大程度上加快讀寫速度。如果記憶體緩衝區中沒有所要讀入的資料,或者需要執行寫操作將資料寫入裝置,那麼就要執行真正的資料傳輸,這是透過呼叫資料結構blk_dev_struct中的函式request_fn( )來完成的。ioctl()--將cmd進行解釋,並送到裝置的控制暫存器.事實上,read()和write()也要透過ioctl()來完成操作的 .ioctl(){CASE CMD{SWITCH CASE1:{...};SWITCH CASE2:{...};SWITCH CASE N:{...};..DEFAULT : {...};}END CASE總結:我們可以看出一個linux的驅動程式通常包含如下:初始化裝置模組、{I/O port ,DMA.Irq,記憶體 buffer,初始化並且填寫具體裝置資料結構,註冊 fops的具體函式等等 }中斷處理模組、裝置釋放模組、裝置解除安裝模組裝置開啟模組、資料讀寫和控制模組、驅動裝載模組、驅動釋放模組.浙江省城鄉規劃設計研究院計算機中心陳剛 2004.07.2
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-944765/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 程式設計模式-表驅動程式設計程式設計設計模式
- 驅動篇——核心程式設計基礎程式設計
- 程式設計技巧之-表驅動法程式設計
- Laravel最佳實踐–事件驅動程式設計Laravel事件程式設計
- Laravel 最佳實踐 -- 事件驅動程式設計Laravel事件程式設計
- Laravel最佳實踐 -- 事件驅動程式設計Laravel事件程式設計
- 事件驅動的微服務-事件驅動設計事件微服務
- linux驅動之LED驅動Linux
- 問題驅動設計與領域驅動設計的區別 - abdullin
- Linux裝置驅動程式學習----1.裝置驅動程式簡介Linux
- 領域驅動設計最佳實踐--程式碼篇
- Linux驅動實踐:如何編寫【 GPIO 】裝置的驅動程式?Linux
- 關於領域驅動設計的函式程式設計思考 - Naveen Negi函式程式設計
- MasaFramework -- 領域驅動設計Framework
- 領域驅動設計示例
- 事件驅動及其設計模式事件設計模式
- 理解領域驅動設計
- 事件驅動架構設計事件架構
- 基於函數語言程式設計的領域驅動設計 - Scott Wlaschin函數程式設計
- linux3.4.2核心-LCD驅動程式的移植Linux
- 【Linux】 Linux網路程式設計Linux程式設計
- 領域驅動設計核心概念
- 領域驅動設計簡介
- DDD領域驅動設計pdf
- 再談領域驅動設計
- 實現領域驅動設計
- Linux jpeg程式設計Linux程式設計
- Linux Bash程式設計Linux程式設計
- 全志A33linuxled驅動程式設計(附實測參考程式碼)Linux程式設計
- PostgreSQL中利用驅動程式實現故障轉移SQL
- 戲說領域驅動設計(廿七)——Saga設計模型模型
- Linux 驅動之IoctlLinux
- 從程式碼戰術角度解釋領域驅動設計 - Cyrille
- DDD領域驅動設計總結和C#程式碼示例C#
- 系統設計思想之Domain驅動AI
- DDD-領域驅動設計示例
- 微服務領域驅動設計 - semaphoreci微服務
- 淺談DDD(領域驅動設計)
- 淺談 DDD 領域驅動設計