字符設備驅動程序

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)

參數說明:

  • from:dev_t類型的變量,用於指定字符設備的起始設備號,若是要註冊的設備號已經被其餘的設備註冊了,那麼就會致使註冊失敗。
  • count:指定要申請的設備號個數,count的值不能夠太大,不然會與下一個主設備號重疊。
  • name:用於指定該設備的名稱,咱們能夠在/proc/devices中看到該設備。

動態分配設備編號

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

參數說明以下:

  • dev:指向dev_t類型數據的指針變量,用於存放分配到的設備編號的起始值;
  • baseminor:次設備號的起始值,一般狀況下,設置爲0;
  • count、name:同register_chrdev_region類型,用於指定須要分配的設備編號的個數以及設備的名稱。
可經過cat /proc/devices查看內核分配的主設備號。
使用上兩個函數分配設備號對應地註銷函數(將設備編號還給內核)爲:
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);
}

參數說明:

  • major:用於指定要申請的字符設備的主設備號,等價於register_chrdev_region函數,當設置爲0時,內核會自動分配一個未使用的主設備號。
  • 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)
  • cdev:struct cdev類型的指針變量,指向須要關聯的字符設備結構體;
  • fops:file_operations類型的結構體指針變量,通常將實現操做該設備的結構體file_operations結構體做爲實參。

4)註冊設備

cdev_add函數用於向內核的cdev_map散列表(管理當前系統中的全部字符設備)添加一個新的字符設備

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
  • p:struct cdev類型的指針,用於指定須要添加的字符設備;
  • dev:dev_t類型變量,用於指定設備的起始編號;
  • count:指定註冊多少個設備。

5)file_operation *fops

本身編寫一個字符設備驅動:https://tutorial.linux.doc.embedfire.com/zh_CN/latest/linux_driver/character_device.html

相關文章
相關標籤/搜索