Linux中,根據設備的類型能夠分爲三類:字符設備、塊設備和網絡設備。html
字符設備:應用程序按字節/字符來讀寫數據,一般不支持隨機存取。咱們經常使用的鍵盤、串口都是字符設備。node
塊設備:應用程序能夠隨機訪問設備數據。典型的塊設備有硬盤、SD卡、閃存等,應用程序 能夠尋址磁盤上的任何位置,並由此讀取數據。此外,數據的讀寫只能以塊的倍數進行。linux
網絡設備是一種特殊設備,它並不存在於/dev下面,主要用於網絡數據的收發。網絡
0x01 前置知識
函數
用戶使用open函數打開設備文件,作了些什麼工做:
spa
(引自https://embed-linux-tutorial.readthedocs.io/zh_CN/latest/linux_driver/character_device.html)指針
設備文件一般在開機啓動時自動建立的,不過,咱們仍然可使用命令mknod來建立一個新的設備文件,命令的基本語法以下:code
mknod 設備名 設備類型 主設備號 次設備號
當咱們使用上述命令,建立了一個字符設備文件時,實際上就是建立了一個設備節點inode結構體,而且將該設備的設備編號記錄在成員i_rdev,將成員f_op指針指向了def_chr_fops結構體。這就是mknod負責 的工做內容。htm
(之間的過程有點多,詳細地可照着圖看源碼瞭解)總的來講用戶調用open函數時,最終會調用file結構體中的f_op,即def_chr_fops。
blog
在Linux內核中,使用結構體cdev來描述一個字符設備。函數chrdev_open最終將該文件結構體file的成員f_op替換成了cdev對應的ops成員,並執行ops結構體中的open函數。
咱們使用對該文件描述符fd調用read、write函數,最終都會調用file結構體對應的函數,實際上也就是調用cdev結構體中ops結構體內的相關函數。
總結一下整個過程,當咱們使用open函數,打開設備文件時,會根據該設備的文件的設備號找到相應的設備結構體,從而獲得了操做該設備的方法。也就是說若是咱們要添加一個新設備的話,咱們須要提供一個設備號,一個設備結構體以及操做該設備的方法(file_operations結構體)。接下來,咱們將介紹以上的三個內容。
0x02 設備驅動程序的編寫
(引自哪我也不記得了)
1)定義cdev設備
//第一種方式 static struct cdev chrdev; //第二種方式 struct cdev *cdev_alloc(void);
2)分配/註銷設備號
Linux的各類設備都以文件的形式存放在/dev目錄下,爲了管理這些設備,系統爲各個設備進行編號,每一個設備號又分爲主設備號和次設備號。主設備號用來區分不一樣種類的設備,如USB,tty等,次設備號用來區分同一類型的多個設備,如tty0,tty1……下圖 列出了部分tty設備,他們的主設備號都是4,而不一樣的次設備號分別對應一個tty設備。
內核提供了一種數據類型:dev_t,用於記錄設備編號,該數據類型其實是一個無符號32位整型,其中的12位用於表示主設備號,剩餘的20位則用於表示次設備號。
靜態地爲一個字符設備申請一個或多個設備編號
int register_chrdev_region(dev_t from, unsigned count, const char *name)
參數說明:
動態分配設備編號
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
參數說明以下:
void unregister_chrdev_region(dev_t from, unsigned count)
內核還提供了register_chrdev函數用於分配設備號。該函數是一個內聯函數,它不只支持靜態申請設備號,也支持動態申請設備號,並將主設備號返回,函數原型以下所示。
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops) { return __register_chrdev(major, 0, 256, name, fops); }
參數說明:
同一類字符設備會在內核中申請256個,若用不到,會形成資源浪費
註銷函數:
static inline void unregister_chrdev(unsigned int major, const char *name) { __unregister_chrdev(major, 0, 256, name); }
3)初始化cdev
將cdev結構體與file_operations結構相關聯
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
4)註冊設備
cdev_add函數用於向內核的cdev_map散列表(管理當前系統中的全部字符設備)添加一個新的字符設備
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
5)file_operation *fops
本身編寫一個字符設備驅動:https://tutorial.linux.doc.embedfire.com/zh_CN/latest/linux_driver/character_device.html