這兩個函數(shù)是字符設(shè)備初始化相關(guān)的內(nèi)核函數(shù)。nice編輯器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;">一、字符設(shè)備架構(gòu)
下面我們以兩個設(shè)備:LED、MPU6050為例來講解字符設(shè)備的架構(gòu)
由上圖所示:
1、硬件
外設(shè)有MPU6050、LED兩個設(shè)備,他們通過外設(shè)電路連接到SOC的對應(yīng)的引腳上。程序要操作外設(shè),就要通過設(shè)置soc中對應(yīng)的SFR來與外設(shè)交互。
2、驅(qū)動層
- 每一個字符設(shè)備都必須首先定義一個結(jié)構(gòu)體變量struct cdev,并注冊到內(nèi)核中
- 所有的該變量在內(nèi)核中會通過鏈表進程管理,其中成員list用于將所有鏈表串接起來
- 用于操作外設(shè)的功能函數(shù)全部被封裝在struct file_operations中,包括read、write等
- 每一個字符設(shè)備都必須要有一個設(shè)備號,保存在成員dev中,
- 主、次設(shè)備號只能被分配一次
- 所有的字符設(shè)備號,都由數(shù)組chrdevs統(tǒng)一管理
- chrdevs是一個指針數(shù)組,成員類型為**struct char_device_struct ***,下標與字符設(shè)備號有一定的對應(yīng)關(guān)系,
- **struct char_device_struct **中有成員:
unsigned?int?major;
struct?cdev?*cdev;?
major : 是主設(shè)備號 cdev ?: 指向該字符設(shè)備號對應(yīng)的cdev結(jié)構(gòu)體
3、應(yīng)用層、VFS層
- 用戶如果想操作硬件,必須調(diào)用內(nèi)核中的struct file_operations中的操作函數(shù),
- 那么如何才能找到該結(jié)構(gòu)體呢?必須要依賴文件節(jié)點來查找,可以通過以下命令來創(chuàng)建
mknod??/dev/led?c?250?0
?mknod?創(chuàng)建設(shè)備文件,可以使字符設(shè)備,也可以是塊設(shè)備
?/dev/led?設(shè)備文件名
?c??字符設(shè)備
?250??主設(shè)備號
?0????次設(shè)備號
字符設(shè)備文件屬性中最重要的屬性就是字符設(shè)備號,該設(shè)備號和chedevs的下標有一定對應(yīng)關(guān)系
- 通過mknod創(chuàng)建的文件,VFS層會分配一個結(jié)構(gòu)體變量來維護該文件,類型為struct inode
- 每新建1個文件內(nèi)核都會創(chuàng)建不同的結(jié)構(gòu)體變量與之對應(yīng)
- 應(yīng)用程序要操作某個字符設(shè)備,那么必須先通過系統(tǒng)調(diào)用open()來打開該字符設(shè)備
- 該函數(shù)會返回一個唯一的整型文件描述符,同時內(nèi)核中會分配結(jié)構(gòu)體變量,類型為struct file,并與文件描述符一一對應(yīng),該結(jié)構(gòu)體維護在struct task_struct中
- 每次打開某個文件,都會分配不同的文件描述符,所以需要用不同的變量來保存文件描述符
二、字符設(shè)備創(chuàng)建的流程
了解了架構(gòu)之后,那么我們來看一下內(nèi)核中完整的創(chuàng)建字符設(shè)備的流程及對應(yīng)的函數(shù)調(diào)用關(guān)系:如下圖所示,字符設(shè)備的創(chuàng)建主要包括以下三個步驟:
- 申請設(shè)備號
- 初始化cdev
- 注冊cdev 調(diào)用的函數(shù)見右側(cè)
下面是一個最簡單的額字符設(shè)備創(chuàng)建的實例
/*??
?*一口Linux
?*2021.6.21
?*version:?1.0.0
*/
#include?
#include?
#include?
#include?
#include?
static?int?major?=?237;
static?int?minor?=?0;
static?dev_t?devno;
static?struct?cdev?cdev;
static?int?hello_open?(struct?inode?*inode,?struct?file?*filep)
{
?printk("hello_open()\n");
?return?0;
}
static?struct?file_operations?hello_ops?=?
{
?.open?=?hello_open,
};
static?int?hello_init(void)
{
?int?result;
?int?error;?
?printk("hello_init?\n");
?devno?=?MKDEV(major,minor);?
?result?=?register_chrdev_region(devno,?1,?"test");
?if(result<0)
?{
??printk("register_chrdev_region?fail?\n");
??return?result;
?}
?cdev_init(