字符設(shè)備驅(qū)動代碼學(xué)習(xí)
注冊字符設(shè)備cdev結(jié)構(gòu)體
當(dāng)我們在用戶空間打開字符設(shè)備文件時,首先調(diào)用def_chr_fops->chrdev_open函數(shù)(字符設(shè)備都是調(diào)用這個函數(shù)),def_chr_fops->chrdev_open函數(shù)會調(diào)用kobj_lookup通過設(shè)備號找到cdev->kobject,從而得到設(shè)備的cdev,進(jìn)而獲得file_operations。
register_chrdev:大致作用為向內(nèi)核注冊cdev結(jié)構(gòu)體,當(dāng)在用戶空間打開設(shè)備文件時,內(nèi)核會根據(jù)此設(shè)備文件的主設(shè)備號快速定位cdev->file_operation結(jié)構(gòu)體,從而調(diào)用底層驅(qū)動的open、read、write、ioctl等函數(shù)。如果有指定的主設(shè)備號傳入,函數(shù)會去努力向內(nèi)核申請,成功會返回0。如果傳輸?shù)闹髟O(shè)備好是0,則表示主設(shè)備號由內(nèi)核自動分配。
老接口:register_chrdev:一個函數(shù)完成字符設(shè)備的注冊。
新接口:register_chrdev_region/alloc_chrdev_region+cdev結(jié)構(gòu)體
register_chrdev_region/alloc_chrdev_region只是申請了設(shè)備號,后續(xù)增加了cdev_init和cdev_add步驟來共同完成字符設(shè)備驅(qū)動注冊。我猜測啟用新方法的原因,可能是為了在設(shè)備驅(qū)動中引入設(shè)備類的概念,配合udev/mdev進(jìn)行設(shè)備驅(qū)動文件的管理,這樣的話只有真正在運(yùn)行的驅(qū)動文件才會出現(xiàn)在內(nèi)核的文件列表里。
新接口完成設(shè)備驅(qū)動注冊,必須有以下步驟:
第一、使用alloc_chrdev_region/register_chrdev_region保留一個主設(shè)備號和若干此設(shè)備號
第二、使用class_create()創(chuàng)建自己的設(shè)備類,該函數(shù)在/sys/class 中定義
第三,創(chuàng)建一個struct file_operation(傳遞給cdev_init),每一個設(shè)備都需要創(chuàng)建,并調(diào)用cdev_init和cdev_add將這個設(shè)備文件注冊到內(nèi)核中。
第四、調(diào)用device_create()創(chuàng)建每個設(shè)備,并給一個合適的名字,這樣就能再/dev下創(chuàng)建出設(shè)備文件。
示例代碼:
RSIC處理器的IO內(nèi)存映射
在linux內(nèi)核中,I/O內(nèi)存的映射分為動態(tài)映射和靜態(tài)映射兩種,都是將物理地址映射成為內(nèi)核中的虛擬地址。
靜態(tài)映射:linux移植時會建立一個I/O內(nèi)存靜態(tài)映射表,驅(qū)動程序員只能使用
動態(tài)映射:調(diào)用內(nèi)核提供的映射機(jī)制實(shí)現(xiàn),驅(qū)動程序員可以控制
將外設(shè)的IO端口(寄存器)動態(tài)映射到內(nèi)存中,分為兩步:
1、向內(nèi)核申請?zhí)摂M地址空間
此函數(shù)的功能就是為I/O端口內(nèi)存映射向內(nèi)核從3G-4G的虛擬地址空間申請一段虛擬地址,成功返回非NULL,失敗返回NULL;釋放時使用release_mem_region();
2、I/O端口(寄存器)地址映射
ioremap函數(shù)通過__ioremap實(shí)現(xiàn),只不過傳給__ioremap的第三個參數(shù)為0.
ioremap函數(shù)時內(nèi)核提供的用于將外設(shè)寄存器映射到主存的工具。
mmap:則是將物理內(nèi)存映射到用戶空間。
當(dāng)然,驅(qū)動程序中的I/O內(nèi)存的動態(tài)映射可以放在模塊初始化函數(shù)或者open函數(shù)中。
如果一個物理地址已經(jīng)進(jìn)行了靜態(tài)映射,再進(jìn)行動態(tài)映射,這樣的話,通過這兩個虛擬地址都可以實(shí)現(xiàn)對該物理地址的訪問。
使用案例: