Linux驅動開發入門與實踐(一)
最近這一段時間,把之前學的linux基礎的東西撿了回來,正式開始接觸驅動了。接觸後發現底層的東西還是相當的繁瑣,核心裡的巨集定義數不勝數,結構體,指標更是撲面而來。驅動主要分為三部分,分別是字元裝置、塊裝置以及網路介面裝置。這裡先總結下簡單字元裝置驅動相關的東西。
參考資料:1.《Linux驅動開發入門與實踐》 鄭強
2.《國嵌培訓資料以及視訊》
參考資料1個人覺得不是很適合拿來上手,雖然講的點多,但是卻不細緻。而2講的點少,但比較細也比較淺顯。
字元裝置驅動:
字元裝置理解起來可以和塊裝置進行區分理解。
字元=只能一個一個位元組讀寫
塊=可以從任意位置讀取一定長度資料的裝置,不必按照先後順序。
如果對應上實物的話:字元裝置有:滑鼠、鍵盤、串列埠、控制檯···
塊裝置有:SD卡、硬碟、磁碟、U盤···
Linux具體是怎樣去操作裝置驅動的?
在Linux系統中,每個字元裝置或者塊裝置都在 /dev目錄下有對應一個裝置檔案。Linux對裝置進行操控,本質上就是通過這些檔案來操作的。這樣相當於有了一套標準,程式設計師便可以撇開裝置的差異化從而按照標準進行程式設計。
區分一個現成的Linux系統當前的裝置檔案的屬性,只需要cd /dev 然後 ls -l
出現以下條目:
crw-rw----+ 1 root root 14, 12 12-21 22:56 adsp
第一個字元c 就表示char 字元型裝置 ,b則表示block 塊裝置。而5、6欄位分別表示主裝置號和次裝置號。
為什麼需要主裝置號和次裝置號呢?
主裝置號用來區分不同型別的裝置,如USB裝置和串列埠裝置。次裝置號則是用來區分某一型別裝置中不同的子裝置。如串列埠裝置不止一種,所以通過此裝置號進行區分。
主裝置號和次裝置號的表示(具體程式碼實現)
Linux中用dev_t 型別來表示裝置號。其實它本質上就是一個無符號長整型,
typedef u_long dev_t
u_long 在32位機裡為4個位元組 ,在64位裡為8位元組。由於自己學的是ARM,以32位機為例。其中高12位表示主裝置號,低20位表示次裝置號。
裝置號的獲得與申請方式:
主要有2種,第一種為靜態申請,第二種自然就是動態申請。採用的方法不同,前期的基礎工作也就不同。
先說說靜態申請:靜態申請毫無疑問需要程式設計師自己給分配一個裝置號,那麼怎樣判斷一個裝置號是否可用呢?或者說當前系統裡沒有被其他裝置佔用呢?
方法:可以讀取 /proc/devices 檔案來獲得裝置號。
指令如下:cat /proc/devices
知道當前系統佔用的裝置號後,比如要需要設定的裝置號為200,那麼怎麼構建該裝置號呢?
Linux系統採用MKDEV來實現,其中
dev_t devno=MKDEV(ma,mi);其中ma為主裝置號,mi為次裝置號。
構建完裝置號之後,還需要進行申請,靜態申請的方法為:
int register_chrdev_region(dev_t from, unsigned count , const char *name); 標頭檔案:<fs/char_dev.c>
from 是要分配裝置號範圍起始值,count表示需要申請裝置號的個數。 name則是裝置名稱,注意不能超過64位元組。
動態分配:靜態分配由於人為因素,很可能導致衝突,所以Linux自己給自動分配一個未使用的裝置號則更加有利。
動態申請不需要自己構建裝置號,呼叫函式:
int alloc_chrdev_region(&dev_t *dev,unsigned baseminor,unsigned count, const char *name)
成功後返回的裝置號儲存在dev指向的dev_t 型別的變數裡。baseminor 為次裝置號的起始號,count為子裝置數 name為裝置名稱。
申請完後,要將字元裝置註冊到系統中,才能使用。
cdev 用來描述字元裝置。
struct cdev {
struct kobject kobj;
struct module *owner;/*指向包含該結構的模組的指標,用於引用計數*/
const struct file_operations *ops;/*指向字元裝置操作函式集的指標*/
struct list_head list; /*該結構將使用該驅動的字元裝置連成一個連結串列*/
dev_t dev; /*該字元裝置的起始裝置號*/
unsigned int count;/*使用該字元裝置驅動的裝置數量*/
};
kobj結構用於核心管理字元裝置,驅動開發人員一般不使用。
ops是指向file_operations 操作函式結構體指標。
list為雙向連結串列,用於將其他結構體連線成一個雙向連結串列,其連線到inode結構體i_devices成員。而i_devices也是一個list_head結構。這樣使得cdev結構與inode結點組成一個雙向連結串列。
檔案系統中對字元裝置檔案的訪問
對於一個字元裝置檔案, 其inode->i_cdev 指向字元驅動物件cdev, 如果i_cdev為 NULL ,則說明該裝置檔案沒有被開啟.
由於多個裝置可以共用同一個驅動程式.所以,通過字元裝置的inode 中的i_devices 和 cdev中的list組成一個雙向連結串列。inode表示/dev下的裝置檔案。每一個字元裝置在/dev下都有一個裝置檔案,開啟裝置檔案就相當於開啟相應的字元裝置。例如應用程式開啟裝置檔案A,那麼系統就會產生一個inode結點,這樣可以通過inode結點的i_cdev欄位找到cdev字元結構體。
首先,系統呼叫open開啟一個字元裝置的時候, 通過一系列呼叫,最終會執行到 chrdev_open.
(最終是通過呼叫到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 這一系列的呼叫過程,本文暫不討論)
int chrdev_open(struct inode * inode, struct file * filp)
chrdev_open()所做的事情可以概括如下:
1. 根據裝置號(inode->i_rdev), 在字元裝置驅動模型中查詢對應的驅動程式, 這通過kobj_lookup() 來實現, kobj_lookup()會返回對應驅動程式cdev的kobject.
2. 設定inode->i_cdev , 指向找到的cdev.
3. 將inode新增到cdev->list的連結串列中.
4. 使用cdev的ops 設定file物件的f_op
5. 如果ops中定義了open方法,則呼叫該open方法
6. 返回.
執行完 chrdev_open()之後,file物件的f_op指向cdev的ops,因而之後對裝置進行的read, write等操作,就會執行cdev的相應操作.
相關文章
- Linux裝置驅動開發詳解:入門與程式設計實踐Linux程式設計
- 驅動開發入門
- Windows驅動開發入門Windows
- 微信小程式開發入門與實踐微信小程式
- Windows驅動開發入門 -- 理清基本概念Windows
- linux驅動開發總結(一)Linux
- 文件驅動開發模式在 AIMS 中的應用與實踐模式AI
- TypeScript入門與實踐TypeScript
- locsut 入門與實踐
- Kafka 入門與實踐Kafka
- Docker 入門與實踐Docker
- SASS入門與實踐
- 《微信小程式開發 入門與實踐》知識點整理微信小程式
- C語言開發入門與程式設計實踐pdfC語言程式設計
- 第一個linux驅動開發包Linux
- Linux驅動實踐:帶你一步一步編譯核心驅動程式Linux編譯
- linux裝置驅動編寫入門Linux
- 測試驅動開發在專案中的實踐
- 技術團隊運用度量驅動開發提升質量:策略與實踐
- GitOps快速入門與實踐Git
- Git與Github入門實踐(上)Github
- 微信小程式入門與實踐微信小程式
- Linux驅動實踐:如何編寫【 GPIO 】裝置的驅動程式?Linux
- Git入門與開發Git
- Scrum敏捷軟體開發之技術實踐——測試驅動開發TDDScrum敏捷
- 領域驅動設計(DDD)實踐之路(二):事件驅動與CQRS事件
- Linux驅動入門基礎基礎知識Linux
- Docker從入門到動手實踐Docker
- Flutter 入門與實戰(九):開發一個常用的登入頁面Flutter
- Linux驅動開發: Ubuntu(PC機)系統上編譯驅動並載入測試LinuxUbuntu編譯
- 領域驅動設計之實踐與反思
- 雲原生推動全雲開發與實踐
- 優酷鴻蒙開發實踐|多屏互動開發實踐鴻蒙
- Linux驅動開發筆記(一):helloworld驅動原始碼編寫、makefile編寫以及驅動編譯Linux筆記原始碼編譯
- Kubeflow實戰: 入門介紹與部署實踐
- (一)TypeScript開發入門TypeScript
- 《Django入門與實踐教程》完整版Django
- 《Kafka入門與實踐》讀書筆記Kafka筆記