字符設備的驅動程序開發步驟大緻都是差不多的,這裡繪制了一張圖來形象的反應字符設備驅動程序的關鍵步驟:
我們創建一個字符設備的時候,首先要得到一個設備号,分配設備号的途徑有靜态分配和動态分配;拿到設備的唯一 ID,我們需要實現 file_operation 并保存到 cdev 中,實現 cdev 的初始化;然後我們需要将我們所做的工作告訴内核,使用 cdev_add() 注冊 cdev;最後我們還需要創建設備節點,以便我們後面調用 file_operation接口。 注銷設備時我們需釋放内核中的 cdev,歸還申請的設備号,删除創建的設備節點。
1.字符設備的定義Linux 内核提供了兩種方式來定義字符設備:
//第一種方式
static struct cdev chrdev;
//第二種方式
struct cdev *cdev_alloc(void);
struct cdev *p_cdev = cdev_alloc();
第一種方式,就是我們常見的變量定義;第二種方式,是内核提供的動态分配方式,調用該函數之後,會返回一個 struct cdev 類型的指針,用于描述字符設備。
第二種方式定義的字符設備,可以通過cdev_del函數來釋放占用的内存。
2.設備号的申請和歸還2.1.設備号的靜态申請register_chrdev_region 函數用于靜态地為一個字符設備申請一個或多個設備編号。
int register_chrdev_region(dev_t from, unsigned count, const char *name);
參數:
返回值:返回 0 表示申請成功,失敗則返回錯誤碼。
2.2.設備号的動态申請使用 register_chrdev_region 函數時,都需要去查閱内核源碼的 Documentation/devices.txt 文件,這就十分不方便。因此,内核又為我們提供了一種能夠動态分配設備編号的方式: alloc_chrdev_region。
調用 alloc_chrdev_region 函數,内核會自動給我們分配一個尚未使用的主設備号。我們可以通過命令“cat /proc/devices”查詢内核分配的主設備号。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
參數:
返回值:返回 0 表示申請成功,失敗則返回錯誤碼。
2.3.設備号的申請(靜态和動态都支持)除了register_chrdev_region函數能夠靜态申請設備号,alloc_chrdev_region函數能夠動态申請設備号之外,内核還提供了register_chrdev 函數用于分配設備号。該函數是一個内聯函數,它不僅支持靜态申請設備号,也支持動态申請設備号,并将主設備号返回。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);
}
參數:
返回值:主設備号。
我們從以上代碼中可以看到,使用 register_chrdev 函數向内核申請設備号,同一類字符設備(即主設備号相同),會在内核中申請了256 個,通常情況下,我們不需要用到這麼多個設備,這就造成了極大的資源浪費。因此通常情況下,并不使用該函數。
2.4.設備号的歸還當我們删除字符設備時候,我們需要把分配的設備編号交還給内核,對于使用 register_chrdev_region 函數以及 alloc_chrdev_region 函數分配得到的設備編号,可以使用 unregister_chrdev_region 函數将分配得到的設備号歸還給内核。
void unregister_chrdev_region(dev_t from, unsigned count);
參數:
cdev_init()函數主要将file_operations結構體和我們的字符設備結構體相關聯。
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
參數:
cdev_add 函數用于向内核的 cdev_map 散列表添加一個新的字符設備。
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
參數:
返回值:0或者錯誤碼。
4.2.字符設備的移除從内核中移除某個字符設備,則需要調用 cdev_del 函數。從系統中删除 cdev, cdev 設備将無法再打開,但任何已經打開的 cdev 将保持不變,即使在 cdev_del 返回後,它們的 fops 仍然可以調用。
void cdev_del(struct cdev *p);
參數:
可以在代碼中使用device_create函數創建設備節點。
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...);
參數:
返回值:成功時返回 struct device 結構體指針, 錯誤時返回 ERR_PTR()。
5.2.設備節點的銷毀可以使用device_destroy函數來删除 device_create 函數創建的設備節點。
void device_destroy(struct class *class, dev_t devt);
參數:
除了使用代碼創建設備節點,還可以使用 mknod 命令創建設備節點。
用法: mknod 設備名 設備類型 主設備号 次設備号
當類型為”p”時可不指定主設備号和次設備号,否則它們是必須指定的。如果主設備号和次設備号以”0x”或”0X”開頭,它們會被視作十六進制數來解析;如果以”0”開頭,則被視作八進制數;其餘情況下被視作十進制數。可用的設備類型包括:
- b:創建 (有緩沖的) 區塊特殊文件;
- c,u:創建 (沒有緩沖的) 字符特殊文件;
- p:創建先進先出 (FIFO) 特殊文件。
如: mkmod /dev/test c 2 0 創建一個字符設備/dev/test,其主設備号為 2,次設備号為 0。
當我們使用上述命令,創建了一個字符設備文件時,實際上就是創建了一個設備節點 inode 結構體,并且将該設備的設備編号記錄在成員i_rdev,将成員 f_op 指針指向了 def_chr_fops 結構體。這就是 mknod 負責的工作内容。
inode 上的 file_operation 并不是自己構造的 file_operation,而是字符設備通用的 def_chr_fops,那麼自己構建的 file_operation 等在應用程序調用 open 函數之後,才會綁定在文件上。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!