嵌入式Linux驅(qū)動程序開發(fā)(一)基本概念和方法

姓名:薛紹宏? ? ?學(xué)號:19020100016? ? 學(xué)院:電子工程學(xué)院

轉(zhuǎn)自:https://blog.csdn.net/iteye_2060/article/details/82089821?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162722233316780264011150%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162722233316780264011150&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-82089821.pc_search_result_control_group&utm_term=%E5%B5%8C%E5%85%A5%E5%BC%8Flinux%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91&spm=1018.2226.3001.4449

【嵌牛導(dǎo)讀】本文介紹了嵌入式Linux驅(qū)動程序開發(fā)的一些概念和方法

【嵌牛鼻子】嵌入式Linux驅(qū)動程序開發(fā)

【嵌牛提問】嵌入式Linux驅(qū)動程序開發(fā)有哪些基本概念和方法

【嵌牛正文】

系統(tǒng)調(diào)用是操作系統(tǒng)內(nèi)核和應(yīng)用程序之間的接口属划,設(shè)備驅(qū)動程序是操作系統(tǒng)內(nèi)核和機器硬件之間的接口。設(shè)備驅(qū)動程序為應(yīng)用程序屏蔽了硬件的細節(jié)防楷,這樣在應(yīng)用程序看來,硬件設(shè)備只是一個設(shè)備文件酵使,應(yīng)用程序可以象操作普通文件一樣對硬件設(shè)備進行操作称鳞。設(shè)備驅(qū)動程序是內(nèi)核的一部分。

Linux將設(shè)備主要分成兩大類:一類是塊設(shè)備吹缔,類似磁盤以記錄塊或扇區(qū)為單位,成塊進行輸入/輸出的設(shè)備豪嚎;另一類是字符設(shè)備搔驼,類似鍵盤以字符為單位,逐個進行輸入/輸出的設(shè)備侈询。網(wǎng)路設(shè)備是介于塊設(shè)備和字符設(shè)備之間的一種特殊設(shè)備舌涨。

塊設(shè)備接口僅支持面向塊的I/O操作,所有I/O操作都通過在內(nèi)核地址空間中的I/O緩沖區(qū)進行妄荔,它可以支持隨機存取的功能泼菌。文件系統(tǒng)通常都建立在塊設(shè)備上。

字符設(shè)備接口支持面向字符的I/O操作啦租,由于它們不經(jīng)過系統(tǒng)的快速緩存,所以它們負責(zé)管理自己的緩沖區(qū)結(jié)構(gòu)荒揣。字符設(shè)備接口只支持順序存取的功能篷角,一般不能進行任意長度的I/O請求,而是限制I/O請求的長度必須是設(shè)備要求的基本塊長的倍數(shù)系任。

1.設(shè)備驅(qū)動程序的概念

設(shè)備驅(qū)動程序?qū)嶋H是處理和操作硬件控制器的軟件恳蹲,從本質(zhì)上講,是內(nèi)核中具有最高特權(quán)級的俩滥、駐留內(nèi)存的嘉蕾、可共享的底層硬件處理例程。驅(qū)動程序是內(nèi)核的一部分霜旧,是操作系統(tǒng)內(nèi)核與硬件設(shè)備的直接接口错忱,驅(qū)動程序屏蔽了硬件的細節(jié),完成以下功能:

對設(shè)備初始化和釋放挂据;

對設(shè)備進行管理以清,包括實時參數(shù)設(shè)置,以及提供對設(shè)備的操作接口崎逃;

讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)或者回送應(yīng)用程序請求的數(shù)據(jù)掷倔;

檢測和處理設(shè)備出現(xiàn)的錯誤。

Linux操作系統(tǒng)將所有的設(shè)備全部看成文件个绍,并通過文件的操作界面進行操作勒葱。對用戶程序而言,設(shè)備驅(qū)動程序隱藏了設(shè)備的具體細節(jié)巴柿,對各種不同設(shè)備提供了一致的接口凛虽,一般來說,是把設(shè)備映射為一個特殊的設(shè)備文件篮洁,用戶程序可以像對其他文件一樣對此設(shè)備文件進行操作涩维。這意味著:

由于每一個設(shè)備至少由文件系統(tǒng)的一個文件代表,因而都有一個“文件名”。

應(yīng)用程序通惩卟可以通過系統(tǒng)調(diào)用open()打開設(shè)備文件蜗侈,建立起與目標設(shè)備的連接。

打開了代表著目標設(shè)備的文件睡蟋,即建立起與設(shè)備的連接后踏幻,可以通過read()、write()戳杀、ioctl()等常規(guī)的文件操作對目標設(shè)備進行操作该面。

設(shè)備文件的屬性由三部分信息組成:第一部分是文件的類型,第二部分是一個主設(shè)備號信卡,第三部分是一個次設(shè)備號隔缀。其中類型和主設(shè)備號結(jié)合在一起惟一地確定了設(shè)備文件驅(qū)動程序及其界面,而次設(shè)備號則說明目標設(shè)備是同類設(shè)備中的第幾個傍菇。

由于Linux 中將設(shè)備當(dāng)做文件處理猾瘸,所以對設(shè)備進行操作的調(diào)用格式與對文件的操作類似,主要包括open()丢习、read()牵触、write()、ioctl()咐低、close()等揽思。應(yīng)用程序發(fā)出系統(tǒng)調(diào)用命令后,會從用戶態(tài)轉(zhuǎn)到核心態(tài)见擦,通過內(nèi)核將open()這樣的系統(tǒng)調(diào)用轉(zhuǎn)換成對物理設(shè)備的操作钉汗。

2.處理器與設(shè)備間數(shù)據(jù)交換方式

處理器與外設(shè)之間傳輸數(shù)據(jù)的控制方式通常有3種:查詢方式、中斷方式和直接內(nèi)存存任巍(DMA)方式儡湾。

21.查詢方式

設(shè)備驅(qū)動程序通過設(shè)備的I/O端口空間,以及存儲器空間完成數(shù)據(jù)的交換执俩。例如徐钠,網(wǎng)卡一般將自己的內(nèi)部寄存器映射為設(shè)備的I/O端口,而顯示卡則利用大量的存儲器空間作為視頻信息的存儲空間役首。利用這些地址空間尝丐,驅(qū)動程序可以向外設(shè)發(fā)送指定的操作指令。通常來講衡奥,由于外設(shè)的操作耗時較長爹袁,因此,當(dāng)處理器實際執(zhí)行了操作指令之后矮固,驅(qū)動程序可采用查詢方式等待外設(shè)完成操作失息。

2.2.中斷方式

查詢方式白白浪費了大量的處理器時間譬淳,而中斷方式才是多任務(wù)操作系統(tǒng)中最有效利用處理器的方式。當(dāng)CPU進行主程序操作時盹兢,外設(shè)的數(shù)據(jù)已存入端口的數(shù)據(jù)輸入寄存器邻梆,或端口的數(shù)據(jù)輸出寄存器已空,此時由外設(shè)通過接口電路向CPU發(fā)出中斷請求信號绎秒。CPU在滿足一定條件下浦妄,暫停執(zhí)行當(dāng)前正在執(zhí)行的主程序,轉(zhuǎn)入執(zhí)行相應(yīng)能夠進行輸入/輸出操作的子程序见芹,待輸入/輸出操作執(zhí)行完畢之后剂娄,CPU再返回并繼續(xù)執(zhí)行原來被中斷的主程序。這樣玄呛,CPU就避免了把大量時間耗費在等待阅懦、查詢外設(shè)狀態(tài)的操作上,使其工作效率得以大大提高把鉴。中斷方式的原理示意圖如圖6.1所示故黑。

2.3.直接訪問內(nèi)存(DMA)方式

利用中斷,系統(tǒng)和設(shè)備之間可以通過設(shè)備驅(qū)動程序傳送數(shù)據(jù)庭砍,但是,當(dāng)傳送的數(shù)據(jù)量很大時混埠,因為中斷處理上的延遲怠缸,利用中斷方式的效率會大大降低。而直接內(nèi)存訪問(DMA)可以解決這一問題钳宪。DMA可允許設(shè)備和系統(tǒng)內(nèi)存間在沒有處理器參與的情況下傳輸大量數(shù)據(jù)揭北。設(shè)備驅(qū)動程序在利用DMA之前,需要選擇DMA通道并定義相關(guān)寄存器吏颖,以及數(shù)據(jù)的傳輸方向搔体,即讀取或?qū)懭耄缓髮⒃O(shè)備設(shè)定為利用該DMA通道傳輸數(shù)據(jù)半醉。設(shè)備完成設(shè)置之后疚俱,可以立即利用該DMA通道在設(shè)備和系統(tǒng)的內(nèi)存之間傳輸數(shù)據(jù),傳輸完畢后產(chǎn)生中斷以便通知驅(qū)動程序進行后續(xù)處理缩多。在利用DMA進行數(shù)據(jù)傳輸?shù)耐瑫r呆奕,處理器仍然可以繼續(xù)執(zhí)行指令。

3.驅(qū)動程序結(jié)構(gòu)

3.1一個設(shè)備驅(qū)動程序模塊的基本框架

設(shè)備驅(qū)動程序流程圖

在系統(tǒng)內(nèi)部衬吆,I/O設(shè)備的存取通過一組固定的入口點來進行梁钾,入口點也可以理解為設(shè)備的句柄,就是對設(shè)備進行操作的基本函數(shù)逊抡。字符型設(shè)備驅(qū)動程序提供如下幾個入口點:

open入口點姆泻。打開設(shè)備準備I/O操作。對字符設(shè)備文件進行打開操作,都會調(diào)用設(shè)備的open入口點拇勃。open子程序必須對將要進行的I/O操作做好必要的準備工作四苇,如清除緩沖區(qū)等。如果設(shè)備是獨占的潜秋,即同一時刻只能有一個程序訪問此設(shè)備蛔琅,則open子程序必須設(shè)置一些標志以表示設(shè)備處于忙狀態(tài)。

close入口點峻呛。關(guān)閉一個設(shè)備罗售。當(dāng)最后一次使用設(shè)備完成后,調(diào)用close子程序钩述。獨占設(shè)備必須標記設(shè)備方可再次使用寨躁。

read入口點。從設(shè)備上讀數(shù)據(jù)牙勘。對于有緩沖區(qū)的I/O操作职恳,一般是從緩沖區(qū)里讀數(shù)據(jù)。對字符設(shè)備文件進行讀操作將調(diào)用read子程序方面。

write入口點放钦。往設(shè)備上寫數(shù)據(jù)。對于有緩沖區(qū)的I/O操作恭金,一般是把數(shù)據(jù)寫入緩沖區(qū)里操禀。對字符設(shè)備文件進行寫操作將調(diào)用write子程序。

ioctl入口點横腿。執(zhí)行讀颓屑、寫之外的操作。

select入口點耿焊。檢查設(shè)備揪惦,看數(shù)據(jù)是否可讀或設(shè)備是否可用于寫數(shù)據(jù)。select系統(tǒng)調(diào)用在檢查與設(shè)備文件相關(guān)的文件描述符時使用select入口點罗侯。

3.1. file_operations結(jié)構(gòu)體

struct file_operations {

structmodule *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);

unsignedint (*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);

unsignedlong (*get_unmapped_area)(

struct file*,

unsignedlong,

unsignedlong,

unsignedlong,

unsignedlong

);

};

lseek器腋,移動文件指針的位置,只能用于可以隨機存取的設(shè)備歇父。

read蒂培,進行讀操作,buf為存放讀取結(jié)果的緩沖區(qū)榜苫,count為所要讀取的數(shù)據(jù)長度护戳。

write,進行寫操作垂睬,與read類似媳荒。

select抗悍,進行選擇操作。

ioctl钳枕,進行讀缴渊、寫以外的其他操作。

mmap鱼炒,用于把設(shè)備的內(nèi)容映射到地址空間衔沼,一般只有塊設(shè)備驅(qū)動程序使用。

open昔瞧,打開設(shè)備進行I/O操作指蚁。返回0表示成功,返回負數(shù)表示失敗自晰。

release凝化,即close操作

3.2.inode{}和file{}結(jié)構(gòu)體

inode數(shù)據(jù)結(jié)構(gòu)體提供關(guān)于特別設(shè)備文件的信息。file結(jié)構(gòu)體主要是與文件系統(tǒng)對應(yīng)的設(shè)備驅(qū)動程序使用酬荞。

struct file主要用于與文件系統(tǒng)相關(guān)的設(shè)備驅(qū)動程序搓劫,可提供關(guān)于被打開的文件的信息,定義如下:

struct file {

struct list_head f_list;

struct dentry *f_dentry;

struct vfsmount*f_vfsmnt;

struct file_operations *f_op;

atomic_t f_count;

unsigned int f_flags;

mode_t f_mode;

loff_t f_pos;

unsigned long f_reada,f_ramax, f_raend, f_ralen, f_rawin;

struct fown_struct f_owner;

unsigned int f_uid,f_gid;

int f_error;

unsigned long f_version;

/* needed for tty driver, and maybe others */

void *private_data;

/* preallocated helper kiobuf to speedup O_DIRECT */

struct kiobuf *f_iobuf;

long f_iobuf_lock;

};

在用戶自己的驅(qū)動程序中混巧,首先要根據(jù)驅(qū)動程序的功能枪向,完成file_operations結(jié)構(gòu)中函數(shù)的實現(xiàn)。不需要的函數(shù)接口可以直接在file_operations結(jié)構(gòu)中初始化為NULL咧党。file_operations中的變量會在驅(qū)動程序初始化時遣疯,注冊到系統(tǒng)內(nèi)部。每個進程對設(shè)備的操作凿傅,都會根據(jù)主次設(shè)備號,轉(zhuǎn)換成對file_operations結(jié)構(gòu)的訪問数苫。

4.設(shè)備注冊和初始化

設(shè)備的驅(qū)動程序在加載的時候首先需要調(diào)用入口函數(shù)init_module()聪舒,該函數(shù)最重要的一個工作就是向內(nèi)核注冊該設(shè)備,對于字符設(shè)備調(diào)用register_chrdev()完成注冊虐急。register_chrdev 的定義為:int register_chrdev(unsignedint major, const char *name, struct file_ operations *fops);

其中箱残,major是為設(shè)備驅(qū)動程序向系統(tǒng)申請的主設(shè)備號,如果為0止吁,則系統(tǒng)為此驅(qū)動程序動態(tài)分配一個主設(shè)備號被辑。name是設(shè)備名,fops是對各個調(diào)用的入口點說明敬惦。此函數(shù)返回0時表示成功盼理;返回-EINVAL,表示申請的主設(shè)備號非法俄删,主要原因是主設(shè)備號大于系統(tǒng)所允許的最大設(shè)備號宏怔;返回-EBUSY奏路,表示所申請的主設(shè)備號正在被其他設(shè)備程序使用。如果動態(tài)分配主設(shè)備號成功臊诊,此函數(shù)將返回所分配的主設(shè)備號鸽粉。如果register_chrdev()操作成功,設(shè)備名就會出現(xiàn)在/proc/dvices文件中抓艳。

Linux在/dev目錄中為每個設(shè)備建立一個文件触机,用ls –l命令列出函數(shù)返回值,若小于0玷或,則表示注冊失斃苁住;返回0或者大于0的值表示注冊成功庐椒。注冊以后椒舵,Linux將設(shè)備名與主、次設(shè)備號聯(lián)系起來约谈。當(dāng)有對此設(shè)備名的訪問時笔宿,Linux通過請求訪問的設(shè)備名得到主、次設(shè)備號棱诱,然后把此訪問分發(fā)到對應(yīng)的設(shè)備驅(qū)動泼橘,設(shè)備驅(qū)動再根據(jù)次設(shè)備號調(diào)用不同的函數(shù)。

當(dāng)設(shè)備驅(qū)動模塊從Linux內(nèi)核中卸載迈勋,對應(yīng)的主設(shè)備號必須被釋放炬灭。字符設(shè)備在cleanup_module()函數(shù)中調(diào)用unregister_chrdev()來完成設(shè)備的注銷。unregister_chrdev()的定義為:int unregister_chrdev(unsignedint major, const char *name);

此函數(shù)的參數(shù)為主設(shè)備號major和設(shè)備名name靡菇。Linux內(nèi)核把name和major在內(nèi)核注冊的名稱對比重归,如果不相等,卸載失敗厦凤,并返回-EINVAL鼻吮;如果major大于最大的設(shè)備號,也返回-EINVAL较鼓。

包括設(shè)備注冊在內(nèi)椎木,設(shè)備驅(qū)動的初始化函數(shù)主要完成的功能是有以下5項。

(1)對驅(qū)動程序管理的硬件進行必要的初始化博烂。

對硬件寄存器進行設(shè)置香椎。比如,設(shè)置中斷掩碼禽篱,設(shè)置串口的工作方式畜伐、并口的數(shù)據(jù)方向等。

(2)初始化設(shè)備驅(qū)動相關(guān)的參數(shù)谆级。

一般說來烤礁,每個設(shè)備都要定義一個設(shè)備變量讼积,用以保存設(shè)備相關(guān)的參數(shù)。在這一步驟里對設(shè)備變量中的項進行初始化脚仔。

(3)在內(nèi)核注冊設(shè)備勤众。

調(diào)用register_chrdev()函數(shù)來注冊設(shè)備。

(4)注冊中斷鲤脏。

如果設(shè)備需要IRQ支持们颜,則要使用request_irq()函數(shù)注冊中斷。

(5)其他初始化工作猎醇。

初始化部分一般還負責(zé)給設(shè)備驅(qū)動程序申請包括內(nèi)存窥突、時鐘、I/O端口等在內(nèi)的系統(tǒng)資源硫嘶,這些資源也可以在open子程序或者其他地方申請阻问。這些資源不用時,應(yīng)該釋放沦疾,以利于資源的共享称近。

若驅(qū)動程序是內(nèi)核的一部分,初始化函數(shù)則要按如下方式聲明:

int __init chr_driver_init(void);

其中__init是必不可少的哮塞,在系統(tǒng)啟動時會由內(nèi)核調(diào)用chr_driver_init刨秆,完成驅(qū)動程序的初始化。

當(dāng)驅(qū)動程序是以模塊的形式編寫時忆畅,則要按照如下方式聲明:

int init_module(void)

當(dāng)運行后面介紹的insmod命令插入模塊時衡未,會調(diào)用init_module函數(shù)完成初始化工作。

5.中斷管理

設(shè)備驅(qū)動程序通過調(diào)用request_irq函數(shù)來申請中斷家凯,通過free_irq來釋放中斷缓醋。它們在linux/sched.h中的定義如下:

int request_irq(

unsigned int irq,

void (*handler)(int irq,void dev_id,structpt_regs *regs),

unsigned long flags,

const char *device,

void *dev_id

);

void free_irq(unsigned int irq, void*dev_id);

通常從request_irq函數(shù)返回的值為0時,表示申請成功绊诲;負值表示出現(xiàn)錯誤改衩。

irq表示所要申請的硬件中斷號。

handler為向系統(tǒng)登記的中斷處理子程序驯镊,中斷產(chǎn)生時由系統(tǒng)來調(diào)用,調(diào)用時所帶參數(shù)irq為中斷號竭鞍,dev_id為申請時告訴系統(tǒng)的設(shè)備標識板惑,regs為中斷發(fā)生時寄存器內(nèi)容。

device為設(shè)備名偎快,將會出現(xiàn)在/proc/interrupts文件里冯乘。

flag是申請時的選項,它決定中斷處理程序的一些特性晒夹,其中最重要的是決定中斷處理程序是快速處理程序(flag里設(shè)置了SA_INTERRUPT)還是慢速處理程序(不設(shè)置SA_INTERRUPT)裆馒。

下面的代碼將在SBC-2410X的Linux中注冊外部中斷2姊氓。

eint_irq = IRQ_EINT2;

set_external_irq (eint_irq, EXT_FALLING_EDGE,GPIO_PULLUP_DIS);

ret_val =request_irq(eint_irq,eint2_handler, “S3C2410Xeint2”,0);

if(ret_val < 0){

return ret_val;

}

用來打開和關(guān)閉中斷的函數(shù)如下:

#define cli() _asm_ _volatile_("cli"::)

#define sli() _asm_ _volatile_("sli"::) 。

6.設(shè)備驅(qū)動程序的開發(fā)過程

由于嵌入式設(shè)備由于硬件種類非常豐富喷好,在默認的內(nèi)核發(fā)布版中不一定包括所有驅(qū)動程序翔横。所以進行嵌入式Linux系統(tǒng)的開發(fā),很大的工作量是為各種設(shè)備編寫驅(qū)動程序梗搅。除非系統(tǒng)不使用操作系統(tǒng)禾唁,程序直接操縱硬件。嵌入式Linux系統(tǒng)驅(qū)動程序開發(fā)與普通Linux開發(fā)沒有區(qū)別无切〉炊蹋可以在硬件生產(chǎn)廠家或者Internet上尋找驅(qū)動程序,也可以根據(jù)相近的硬件驅(qū)動程序來改寫哆键,這樣可以加快開發(fā)速度掘托。實現(xiàn)一個嵌入式Linux設(shè)備驅(qū)動的大致流程如下。

(1)查看原理圖籍嘹,理解設(shè)備的工作原理闪盔。一般嵌入式處理器的生產(chǎn)商提供參考電路,也可以根據(jù)需要自行設(shè)計噩峦。

(2)定義設(shè)備號锭沟。設(shè)備由一個主設(shè)備號和一個次設(shè)備號來標識。主設(shè)備號惟一標識了設(shè)備類型识补,即設(shè)備驅(qū)動程序類型乓诽,它是塊設(shè)備表或字符設(shè)備表中設(shè)備表項的索引。次設(shè)備號僅由設(shè)備驅(qū)動程序解釋瘾敢,區(qū)分被一個設(shè)備驅(qū)動控制下的某個獨立的設(shè)備救赐。

(3)實現(xiàn)初始化函數(shù)。在驅(qū)動程序中實現(xiàn)驅(qū)動的注冊和卸載切油。

(4)設(shè)計所要實現(xiàn)的文件操作蝙斜,定義file_operations結(jié)構(gòu)。

(5)實現(xiàn)所需的文件操作調(diào)用澎胡,如read孕荠、write等。

(6)實現(xiàn)中斷服務(wù)攻谁,并用request_irq向內(nèi)核注冊稚伍,中斷并不是每個設(shè)備驅(qū)動所必需的。

(7)編譯該驅(qū)動程序到內(nèi)核中戚宦,或者用insmod命令加載模塊个曙。

(8)測試該設(shè)備,編寫應(yīng)用程序受楼,對驅(qū)動程序進行測試垦搬。

7.設(shè)備驅(qū)動開發(fā)的基本函數(shù)

7.1.I/O口函數(shù)

無論驅(qū)動程序多么復(fù)雜呼寸,歸根結(jié)底,無非還是向某個端口或者某個寄存器位賦值猴贰,這個值只能是0或1对雪。接收值的就是I/O口。與中斷和內(nèi)存不同糟趾,使用一個沒有申請的I/O端口不會使處理器產(chǎn)生異常慌植,也就不會導(dǎo)致諸如“segmentationfault”一類的錯誤發(fā)生。由于任何進程都可以訪問任何一個I/O端口义郑,此時系統(tǒng)無法保證對I/O端口的操作不會發(fā)生沖突蝶柿,甚至因此而使系統(tǒng)崩潰。因此非驮,在使用I/O端口前交汤,也應(yīng)該檢查此I/O端口是否已有別的程序在使用,若沒有劫笙,再把此端口標記為正在使用芙扎,在使用完以后釋放它。

這樣需要用到如下幾個函數(shù):

int check_region(unsigned int from,unsigned int extent);

void request_region(unsigned int from,unsigned int extent,const char *name);

void release_region(unsigned int from, unsignedint extent);

調(diào)用這些函數(shù)時的參數(shù)為:

from表示所申請的I/O端口的起始地址填大;

extent為所要申請的從from開始的端口數(shù)戒洼;

name為設(shè)備名,將會出現(xiàn)在/proc/ioports文件里允华;

check_region返回0表示I/O端口空閑圈浇,否則為正在被使用。

在申請了I/O端口之后靴寂,可以借助asm/io.h中的如下幾個函數(shù)來訪問I/O端口:

inline unsigned int inb(unsigned shortport);

inline unsigned int inb_p(unsigned shortport);

inline void outb(char value, unsigned shortport);

inline void outb_p(charvalue,unsigned short port);

其中inb_p和outb_p插入了一定的延時以適應(yīng)某些低速的I/O端口磷蜀。

7.2.時鐘函數(shù)

在設(shè)備驅(qū)動程序中,一般都需要用到計時機制百炬。在Linux系統(tǒng)中褐隆,時鐘是由系統(tǒng)接管的,設(shè)備驅(qū)動程序可以向系統(tǒng)申請時鐘剖踊。與時鐘有關(guān)的系統(tǒng)調(diào)用有:

#include <asm/param.h>

#include <linux/timer.h>

void add_timer(struct timer_list * timer);

int del_timer(struct timer_list * timer);

inline void init_timer(struct timer_list *timer);

struct timer_list的定義為:

struct timer_list {

struct timer_list *next;

struct timer_list *prev;

unsigned long expires;

unsigned long data;

void (*function)(unsigned long d);

};

其中庶弃,expires是要執(zhí)行function的時間。系統(tǒng)核心有一個全局變量jiffies表示當(dāng)前時間德澈,一般在調(diào)用add_timer時jiffies=JIFFIES+num虫埂,表示在num個系統(tǒng)最小時間間隔后執(zhí)行function函數(shù)。系統(tǒng)最小時間間隔與所用的硬件平臺有關(guān)圃验,在核心里定義了常數(shù)HZ表示一秒內(nèi)最小時間間隔的數(shù)目,則num*HZ表示num秒缝呕。系統(tǒng)計時到預(yù)定時間就調(diào)用function澳窑,并把此子程序從定時隊列里刪除斧散,可見,如果想要每隔一定時間間隔執(zhí)行一次的話摊聋,就必須在function里再一次調(diào)用add_timer鸡捐。function的參數(shù)d即為timer里面的data項。

7.3.內(nèi)存操作函數(shù)

作為系統(tǒng)核心的一部分麻裁,設(shè)備驅(qū)動程序在申請和釋放內(nèi)存時不是調(diào)用malloc和free箍镜,而代之以調(diào)用kmalloc和kfree,它們在linux/kernel.h中被定義為:

void * kmalloc(unsigned int len, intpriority);

void kfree(void * obj);

參數(shù)len為希望申請的字節(jié)數(shù)煎源,obj為要釋放的內(nèi)存指針色迂。priority為分配內(nèi)存操作的優(yōu)先級,即在沒有足夠空閑內(nèi)存時如何操作手销,一般由取值GFP_KERNEL解決即可歇僧。

7.4.復(fù)制函數(shù)

在用戶程序調(diào)用read、write時锋拖,因為進程的運行狀態(tài)由用戶態(tài)變?yōu)楹诵膽B(tài)诈悍,地址空間也變?yōu)楹诵牡刂房臻g。由于read兽埃、write中參數(shù)buf是指向用戶程序的私有地址空間的侥钳,所以不能直接訪問,必須通過下面兩個系統(tǒng)函數(shù)來訪問用戶程序的私有地址空間柄错。

#include <asm/segment.h>

void memcpy_fromfs(void * to,const void *from,unsigned long n);

void memcpy_tofs(void * to,const void *from,unsigned long n);

memcpy_fromfs由用戶程序地址空間往核心地址空間復(fù)制舷夺,memcpy_tofs則反之。參數(shù)to為復(fù)制的目的指針鄙陡,from為源指針冕房,n為要復(fù)制的字節(jié)數(shù)。

在設(shè)備驅(qū)動程序里趁矾,可以調(diào)用printk來打印一些調(diào)試信息耙册,printk的用法與printf類似。printk打印的信息不僅出現(xiàn)在屏幕上毫捣,同時還記錄在文件syslog里详拙。

8.模塊加載與卸載

雖然模塊作為內(nèi)核的一部分,但并未被編譯到內(nèi)核中蔓同,它們被分別編譯和鏈接成目標文件饶辙。Linux中模塊可以用C語言編寫,用gcc命令編譯成模塊*.o斑粱,在命令行里加上-c的參數(shù)和“-D__KERNEL__-DMODULE”參數(shù)弃揽。然后用depmod -a 使此模塊成為可加載模塊。模塊用insmod命令加載,用rmmod命令來卸載矿微,這兩個命令分別調(diào)用init_module()和cleanup_ module()函數(shù)痕慢,還可以用lsmod命令來查看所有已加載的模塊的狀態(tài)。

insmod命令可將編譯好的模塊調(diào)入內(nèi)存涌矢。內(nèi)核模塊與系統(tǒng)中其他程序一樣是已鏈接的目標文件掖举,但不同的是它們被鏈接成可重定位映像。insmod將執(zhí)行一個特權(quán)級系統(tǒng)調(diào)用get_kernel_sysms()函數(shù)以找到內(nèi)核的輸出內(nèi)容娜庇,insmod修改模塊對內(nèi)核符號的引用后塔次,將再次使用特權(quán)級系統(tǒng)調(diào)用create_module()函數(shù)來申請足夠的物理內(nèi)存空間,以保存新的模塊名秀。內(nèi)核將為其分配一個新的module結(jié)構(gòu)励负,以及足夠的內(nèi)核內(nèi)存,并將新模塊添加在內(nèi)核模塊鏈表的尾部泰偿,然后將新模塊標記為uninitialized熄守。

利用rmmod命令可以卸載模塊。如果內(nèi)核中還在使用此模塊耗跛,這個模塊就不能被卸載裕照。原因是如果設(shè)備文件正被一個進程打開就卸載還在使用的內(nèi)核模塊,并導(dǎo)致對內(nèi)核模塊的讀/寫函數(shù)所在內(nèi)存區(qū)域的調(diào)用调塌。如果幸運晋南,沒有其他代碼被加載到那個內(nèi)存區(qū)域,將得到一個錯誤提示羔砾;否則负间,另一個內(nèi)核模塊被加載到同一區(qū)域,這就意味著程序跳到內(nèi)核中另一個函數(shù)的中間姜凄,結(jié)果是不可預(yù)見的政溃。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市态秧,隨后出現(xiàn)的幾起案子董虱,更是在濱河造成了極大的恐慌,老刑警劉巖申鱼,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愤诱,死亡現(xiàn)場離奇詭異,居然都是意外死亡捐友,警方通過查閱死者的電腦和手機淫半,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匣砖,“玉大人科吭,你說我怎么就攤上這事昏滴。” “怎么了对人?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵影涉,是天一觀的道長。 經(jīng)常有香客問我规伐,道長,這世上最難降的妖魔是什么匣缘? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任猖闪,我火速辦了婚禮,結(jié)果婚禮上肌厨,老公的妹妹穿的比我還像新娘培慌。我一直安慰自己,他們只是感情好柑爸,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布吵护。 她就那樣靜靜地躺著,像睡著了一般表鳍。 火紅的嫁衣襯著肌膚如雪馅而。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天譬圣,我揣著相機與錄音瓮恭,去河邊找鬼。 笑死厘熟,一個胖子當(dāng)著我的面吹牛屯蹦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绳姨,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼登澜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了飘庄?” 一聲冷哼從身側(cè)響起脑蠕,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎竭宰,沒想到半個月后空郊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡切揭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年狞甚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片廓旬。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡哼审,死狀恐怖谐腰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涩盾,我是刑警寧澤十气,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站春霍,受9級特大地震影響砸西,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜址儒,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一芹枷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧莲趣,春花似錦鸳慈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至潘鲫,卻和暖如春翁逞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背次舌。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工熄攘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人彼念。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓挪圾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親逐沙。 傳聞我的和親對象是個殘疾皇子哲思,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容