Linux-设备驱动概述
文章目錄
- Linux設備驅動概述
- 1. 設備驅動的作用
- 2. 無操作系統的設備驅動
- 3. 有操作系統時的設備驅動
- 4. Linux設備驅動
- 4.1 設備的分類及特點
- 4.2 Linux設備驅動與整個軟硬件系統的關系
- 4.3 Linux設備驅動的重難點
- 5. 源代碼閱讀
- 6. 設備驅動:LED驅動
- 6.1 無操作系統的LED驅動
- 6.2 Linux下的LED驅動
Linux設備驅動概述
1. 設備驅動的作用
??對設備驅動的最通俗解釋就是驅動硬件設備行動,驅動和底層硬件設備直接打交道,按照硬件設備的具體工作方式,讀寫設備的寄存器,完成設備的輪詢,中斷處理,DMA通信,進行物理內存向虛擬內存的映射等,最終讓通信設備能夠收發數據,讓顯示設備能夠顯示文字和畫面,讓存儲設備能記錄文件和數據。
??無操作系統的情況下,工程師可以根據硬件設備的特點自信定義接口,有操作系統的情況下,驅動的結構則由相應的操作系統定義,驅動工程師必須按照相應的架構設計驅動,這樣,驅動才能整合入操作系統的內核中。
2. 無操作系統的設備驅動
??并不是任何一個計算機系統都一定要有操作系統,很多情況下,操作系統都不必存在,對于功能比較單一,控制不復雜的系統,ASIC內部,公交車的刷卡機,電冰箱,微波爐等,并不需要多任務調度,文件系統,內部管理等復雜功能,單任務架構完全可以良好的支持它們的工作。一個無限循環中夾雜著對設備中斷的檢測或者對設備的輪詢是這種系統中軟件的典型架構。
int main(int argc, char* argv[]) {while(1){if(serialInt == 1){//有串口中斷ProcessSerialInt()//處理串口中斷serialInt = 0;//中斷標志變量清0}if(keyInt == 1){//有按鍵中斷ProcessKeyInt();//處理按鍵中斷keyInt = 0;//中斷標志變量清0}status = CheckXXX();switch(status){//...}//...} }??在無操作系統的系統中,雖然不存在操作系統,但是設備驅動則無論如何都必須存在,一般情況下,每種設備驅動都會定義一個軟件模塊,包含.h文件和.c文件,前者定義該設備驅動的數據結構并聲明外部函數,后者負責進行驅動的具體實現,如下:
//無操作系統情況下串口的驅動 /**************** *serial.h文件 *****************/extern void SerialInit(void); extern void SerialSend(const char buf*, int count); extern void SerialRecv(char buf*, int count);/**************** *serial.c文件 *****************///初始化串口 void SerialInit(void) {//... } //串口發送 void SerialSend(const char buf*, int count) {//... } //串口接收 void SerialRecv(char buf*, int count) {//... } //串口中斷處理函數 void SerialIsr(void) {//...serialInt = 1; }??其他模塊想要使用這個設備的時候,只需要包含設備驅動的頭文件serial.h,然后調用其中的外部接口函數,如果要從串口上發送“hello world”字符串,使用語句SerialSend(“hello world”, 11)即可。
??無操作系統下硬件,設備驅動與引用軟件的關系:
??兩種不合理的設計:
3. 有操作系統時的設備驅動
??當系統中存在操作系統的時候,驅動變成了連接硬件和內核的橋梁,操作系統的存在勢必要求設備驅動附加更多的代碼和功能,把單一的“驅使設備硬件行動”變成了操作系統內與硬件交互的模塊,對外呈現操作系統的API,不在給應用軟件工程師提供接口。
操作系統的作用:
??1.一個復雜的軟件系統需要處理多個并發的任務,沒有操作系統,想完成多任務并發時很困難的。
??2.操作系統給我們提供了內存管理機制,對于多數含MMU的32位處理器而言,Windows,Linux等操作系統可以讓每個進程都獨立地訪問4GB的內存空間。
??3.操作系統通過給驅動設定統一的接口形式來使得上層的應用程序可以使用統一的系統調用接口來訪問各種設備。
4. Linux設備驅動
4.1 設備的分類及特點
??計算機系統的硬件主要由CPU,存儲器和外設組成,因為IC制作的發展,芯片集成度更高了,往往在CPU內部就集成了存儲器和外設適配器。
??驅動針對的對象事存儲器和外設(包括CPU內部繼承的存儲器和外設),而不是針對CPU內核,Linux將存儲器和外設分為3個基礎大類:
??1.字符設備:指那些必須以串行順序依次進行訪問的設備,如觸摸屏,磁帶驅動器,鼠標等。塊設備可以按任意順序進行訪問,以塊為單位進行操作。
??2.塊設備:字符設備和塊設備的驅動設計有很大的差異,但是對于用戶而言,字符設備和塊設備都只需要使用文件系統的操作接口open(),close(),read(),write()等進行訪問。
??3.網絡設備:主要是為了面向數據包的接收和發送而設計的,網絡設備的通信方式和前兩者完全不同,主要是使用的套接字接口。
4.2 Linux設備驅動與整個軟硬件系統的關系
??Linux的塊設備有兩種訪問的方式:一種是類似dd命令對應的原始塊設備,另一種是在塊設備上建立FAT、EXT4、BTRFS等文件系統,然后以文件路徑的方式訪問。
??在Linux中,針對NOR、NAND等提供了獨立的內存技術設備子系統,其上運行具備擦除和負載均衡能力的文件系統,針對磁盤或者Flash設備上的FAT,EXT4等文件系統定義了文件和目錄在存儲介質上的組織。
4.3 Linux設備驅動的重難點
??1.編寫Linux設備驅動要求工程師有非常好的硬件基礎,懂得SRAM,Flash,SDRAM,磁盤的讀寫方式,UART,I2C,USB等設備的接口以及輪詢,中斷,DMA的原理,PCI總線的工作方式以及CPU的內存管理單元MMU等。
??2.編寫Linux設備驅動要求工程師有非常好的C語言基礎,能靈活運用結構體,指針,函數指針以及內存動態申請和釋放等。
??3.要求有Linux內核基礎,明白驅動與內核的接口,尤其是塊設備,網絡設備,Flash設備,串口設備等。
??4.需要多任務并發控制和同步的基礎,因為驅動中會大量使用自旋鎖,互斥,信號量,等待隊列等并發與同步機制。
5. 源代碼閱讀
??1.window上閱讀Linux源代碼的工具SourceInsight
??2.類似https://elixir.bootlin.com/linux/latest/source/Documentation提供了Linux內核源代碼的交叉索引
??3.Linux上通常是vim+cscope或者vim+ctags,cscope和ctags可建立代碼索引
6. 設備驅動:LED驅動
6.1 無操作系統的LED驅動
??在嵌入式系統的設計中,LED一般直接由CPU和GPIO(通用可編程IO)口控制,GPIO一般是由兩組寄存器控制,一組控制寄存器和一組數據寄存器。控制寄存器設置GPIO工作方式為輸入還是輸出。當設置為輸出時,向寄存器的對應位寫入1和0會分別在引腳上產生高電平和低電平,當設置為輸入時,讀取數據寄存器的對應位可獲得引腳上的電平為高或低。
??屏蔽CPU差異,GPIO_REG_CTRL物理地址中控制寄存器處的第n位寫入1可設置GPIO口為輸出,在地址GPIO_REG_DATA物理地址中數據寄存器的第n位寫入1或者0可以引腳上產生高或低電平。無操作系統下設備驅動清單:
??LigthInit(),LightOn(),LightOff()都是外部的接口函數。程序中ToVirtual()的作用是當系統啟動了硬件MMU之后,根據物理地址和虛擬地址的映射關系,將寄存器的物理地址轉化為虛擬地址。
6.2 Linux下的LED驅動
??位于drivers/leds/leds-gpio.c中,目錄
https://elixir.bootlin.com/linux/latest/source/drivers/leds/leds-gpio.c??內核中實現了一個提供sysfs節點的GPIO LED驅動,操作硬件的LightInit(),LightOn(),Light Off()函數仍然需要,但是,遵循Linux編程命名習慣,改為light_init(),light_on(),light_off(),這些函數將被LED設備驅動中獨立于設備并針對內核的接口進行調用。
#include ...//包含多個頭文件//設備結構體 struct light_dev {struct cdev cdev;//字符設備cdev結構體unsigned char value;//LED亮時為1,熄滅為0,用戶可讀寫該值 };struct ligth_dev* light_devp; int light_major = LIGHT_MAJOR;MODULE_AUTHOR("Barry Song <xxx@gmail.com>"); MODULE_LICENSE("Dual BSD/GPL"); //打開和關閉函數 int light_open(struct inode* inode, struct file* filp) {struct light_dev* dev;//獲得設備結構體指針dev = container_of(inode->i_cdev, struct light_dev, cdev);//讓設備結構體作為設備的私有信息filp->private_data = dev;return 0; }int light_release(struct inode* inode, struct file* filp) {return 0; }//讀寫設備:可以不需要 ssize_t light_read(struct file *filp, char __user *buf, size_t count, loff_t* f_pos) {struct light_dev *dev = filp->private_data;//獲得設備結構體if(copy_to_user(buf, &(dev->value), 1))return -EFAULT;return 1; }ssize_t light_write(struct file *filp, char __user *buf, size_t count, loff_t* f_pos) {struct light_dev* dev = filp->private_data;if(copy_from_user(&(dev->value), buf, 1)return -EFAULT;//根據寫入的值點亮和熄滅LEDif(dev->value == 1)ligth_on();elselight_off();return 1; }//ioctl函數 int light_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, usigned long arg) {struct light_dev* dev = filp->private_data;switch(cmd){case LIGHT_ON:dev->value = 1;light_on();break();case LIGHT_OFF:dev-value = 0;light_off();break;default:return -EFAULT;}return 0; }struct file_operations light_fops = {.owner = THIS_MODULE,.read = light_read,.write = light_write,.ioctl = light_ioctl,.open = light_open,.release = light_release, };//設置字符設備cdev結構體 static void light_setip_cdev(struct light_dev* dev, int index) {int err, devno = MKDEV(light_major, index);cdev_init(&dev->cdev, &light_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &light_fops;err = cdev_add(&dev->cdev, devno, 1);if(err)printk(KERN_NOTICE "Error %d adding LED%d", err, index); }//模塊加載函數 int light_init(void) {int result;dev_t dev = MKDEV(light_major, 0);//申請字符設備號if(light_major)result = register_chrdev_region(dev, 1, "LED");else {result = alloc_chrdev_region(&dev, 0, 1, "LED");light_major = MAJOR(dev);}if(result < 0)return result;//分配設備結構體的內存light_devp = kmalloc(sizeof(struct light_dev), GFP_KERNEL);if(!light_devp){result = -ENOMEM;goto fail_malloc;}memset(light_devp, 0, sizeof(struct light_dev));light_setup_cdev(light_devp, 0);light_gpio_init();return 0;fail_malloc:unregister_chrdev_region(dev, light_devp);return result; }//模塊卸載函數 void light_cleanup(void) {cdev_del(&light_devp->cdev);//刪除字符設備結構體kfree(light_devp);//釋放在light_init中分配的內存unregister_chrdev_region(MKDEV(light_major, 0), 1);//刪除字符設備 }module_init(light_init); module_exit(light_cleanup);??上述暫時陌生的元素都是Linux內核字符設備定義的,以實現驅動與內核的接口而定義的,Linux對各類設備的驅動都定義了類似的數據結構和函數。
總結
以上是生活随笔為你收集整理的Linux-设备驱动概述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第十四届恩智浦智能车竞赛小白四轮硬件总结
- 下一篇: C语言求1000后面有多少个0,c语言题