姓名:李萌怡? 學(xué)號(hào):19020100103? 學(xué)院:電子工程學(xué)院
轉(zhuǎn)自:https://blog.csdn.net/light_in_dark/article/details/73321105
【嵌牛導(dǎo)讀】:對(duì)于嵌入式系統(tǒng)的學(xué)習(xí)來(lái)說(shuō)捉兴,學(xué)習(xí)嵌入式Linux驅(qū)動(dòng)開(kāi)發(fā)是十分重要的環(huán)節(jié)拆檬。本文對(duì)嵌入式linux設(shè)備系統(tǒng)開(kāi)發(fā)作以簡(jiǎn)要總結(jié)袜爪。
【嵌牛鼻子】:寄存器? 結(jié)構(gòu)體
【嵌牛提問(wèn)】:字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)流程有哪些映琳?
【嵌牛正文】
一、Linux設(shè)備的分類(lèi)
字符設(shè)備、塊設(shè)備寥假、網(wǎng)絡(luò)設(shè)備,三種設(shè)備之間的區(qū)別是數(shù)據(jù)的交互模式霞扬,分別為:
字節(jié)流糕韧、數(shù)據(jù)塊、數(shù)據(jù)包喻圃。
二萤彩、VFS核心結(jié)構(gòu)體
VFS核心結(jié)構(gòu)體定義在”linux/fs.h”頭文件中。
1斧拍、struct inode結(jié)構(gòu)體
記錄文件的屬主乒疏、訪(fǎng)問(wèn)時(shí)間等信息。當(dāng)?shù)谝淮未蜷_(kāi)文件的時(shí)候由VFS創(chuàng)建并初始化饮焦。當(dāng)文件的所有引用都退出后,釋放inode; 如果用戶(hù)態(tài)有多個(gè)人同時(shí)打開(kāi)一個(gè)文件窍侧,則VFS只需要分配一個(gè)inode县踢。
2、struct file結(jié)構(gòu)體
對(duì)應(yīng)用戶(hù)態(tài)的open操作伟件。如果多次打開(kāi)同一個(gè)文件硼啤,內(nèi)核會(huì)生成多個(gè)file。file中記錄文件的打開(kāi)方式斧账,文件內(nèi)部指針等谴返。當(dāng)文件徹底關(guān)閉時(shí),釋放file咧织。
3嗓袱、struct file_operations結(jié)構(gòu)體
該結(jié)構(gòu)體包含若干函數(shù)指針,這些函數(shù)由驅(qū)動(dòng)來(lái)實(shí)現(xiàn)习绢,并集中到file_operations中渠抹,注冊(cè)到VFS。
驅(qū)動(dòng)一般要實(shí)現(xiàn)的函數(shù)有:
open
release
read
write
unlocked_ioctl
驅(qū)動(dòng)可能會(huì)實(shí)現(xiàn)的有:
poll
mmap
fasync
flush
llseek
三闪萄、字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)流程
(1)確定硬件信息
要確定硬件的數(shù)量梧却,物理地址,中斷號(hào)等信息败去;
(2)為要支持的設(shè)備準(zhǔn)備一個(gè)私有結(jié)構(gòu)體
內(nèi)核并不要求必須有私有結(jié)構(gòu)體放航,但如果驅(qū)動(dòng)支持多個(gè)設(shè)備,最好設(shè)計(jì)一個(gè)圆裕。私有結(jié)構(gòu)體完全由驅(qū)動(dòng)人員自行設(shè)計(jì)广鳍,一般來(lái)說(shuō)荆几,會(huì)把和設(shè)備相關(guān)的信息寫(xiě)入該結(jié)構(gòu)體,比如設(shè)備的地址等搜锰。
(3)為要支持的每個(gè)設(shè)備分配對(duì)應(yīng)的設(shè)備號(hào)
設(shè)備號(hào)由char驅(qū)動(dòng)分配伴郁,要求唯一。一般來(lái)說(shuō)蛋叼,如果char驅(qū)動(dòng)可支持多個(gè)類(lèi)似的設(shè)備焊傅,則應(yīng)該為這些設(shè)備選擇一個(gè)主設(shè)備號(hào),然后為每個(gè)設(shè)備選擇一個(gè)特定的次設(shè)備號(hào)狈涮。盡量挑選和其他驅(qū)動(dòng)不一樣的主設(shè)備號(hào)狐胎。可以看/proc/devices歌馍,文件中記錄了其他驅(qū)動(dòng)選擇的主設(shè)備號(hào)握巢;也可以向內(nèi)核申請(qǐng),由內(nèi)核分配一個(gè)主設(shè)備號(hào)松却。
#define DEV_MAJOR? 50
...
dev_id = MKDEV(DEV_MAJOR, 0);
(4)準(zhǔn)備file_operations結(jié)構(gòu)體
函數(shù)集中包括open/release/read/write/unlocked_ioctl等函數(shù)暴浦,如果驅(qū)動(dòng)支持多個(gè)設(shè)備,在函數(shù)中必須能區(qū)分自己訪(fǎng)問(wèn)的是哪個(gè)設(shè)備晓锻。
static struct file_operations mem_fops = {
? ? .owner = THIS_MODULE,
? ? .open = mem_open,
? ? .release = mem_release,
? ? .read = mem_read,
? ? .write = mem_write,
? ? .unlocked_ioctl = mem_ioctl,
};
(5)注冊(cè)設(shè)備
方法一:
利用cdev結(jié)構(gòu)體歌焦,將設(shè)備號(hào)和file_operations注冊(cè)到VFS。一般來(lái)說(shuō)砚哆,將cdev結(jié)構(gòu)體包含到私有結(jié)構(gòu)體中独撇。采用cdev注冊(cè)的設(shè)備,不會(huì)自動(dòng)創(chuàng)建設(shè)備文件躁锁。
cdev_init(&mem_cdev, &mem_fops);
cdev_add(&mem_cdev, dev_id, 1);
cdev_del(&mem_cdev);? //注銷(xiāo)cdev?
方法二:
注冊(cè)miscdevice(用misc代替cdev注冊(cè)纷铣,可以自動(dòng)創(chuàng)建設(shè)備文件)
這種情況下不需要定義主設(shè)備號(hào),即省去#define DEV_MAJOR 50战转,同時(shí)需要用頭文件”linux/miscdevice.h”代替”linux/cdev.h”頭文件搜立。
static struct miscdevice mem_miscdev = {
? ? .minor? = MISC_DYNAMIC_MINOR,
? ? .name? = "mem",? ? //對(duì)應(yīng)/dev/mem設(shè)備文件
? ? .fops? = &mem_fops,
};
ret = misc_register(&mem_miscdev); //注冊(cè)miscdevice
misc_deregister(&mem_miscdev);? ? //注銷(xiāo)miscdevice
cdev和miscdevice比較:
(1)cdev可以實(shí)現(xiàn)同一個(gè)驅(qū)動(dòng)對(duì)應(yīng)多個(gè)設(shè)備,而miscdevice只能實(shí)現(xiàn)一個(gè)驅(qū)動(dòng)對(duì)應(yīng)一個(gè)設(shè)備匣吊;
(2)cdev不能實(shí)現(xiàn)設(shè)備文件的自動(dòng)創(chuàng)建儒拂,而miscdevice可以實(shí)現(xiàn)設(shè)備文件的自動(dòng)創(chuàng)建。
上述的過(guò)程比較適合較簡(jiǎn)單的設(shè)備色鸳,比如看門(mén)狗社痛,led燈,各種傳感器等命雀。較復(fù)雜設(shè)備的char驅(qū)動(dòng)蒜哀,常常要利用內(nèi)核提供的驅(qū)動(dòng)子系統(tǒng)代碼進(jìn)行設(shè)計(jì)。
四、如何在linux驅(qū)動(dòng)中訪(fǎng)問(wèn)寄存器(SFR)
1撵儿、片內(nèi)外設(shè)(pripheral)
(1)基于三總線(xiàn)訪(fǎng)問(wèn)
(2)用寄存器控制
(3)寄存器有物理地址乘客,可通過(guò)手冊(cè)查到,不可更改
2淀歇、片外外設(shè)
(1)很少通過(guò)三總線(xiàn)相連易核,一般是通過(guò)UART,CAN,I2C,USB,SPI,MIPI,I2S,AC97等總線(xiàn)相連。主芯片內(nèi)部必須提供對(duì)應(yīng)的控制器:內(nèi)部的控制器 <–> 外部的外設(shè)
(2)片外外設(shè)基本都是智能設(shè)備(不但有硬件浪默,還有軟件)
(3)有些片外外設(shè)通過(guò)寄存器控制牡直,有些則通過(guò)命令控制
(4)如果用寄存器控制,寄存器沒(méi)有物理地址纳决,只有偏移碰逸。
(5)要控制片外外設(shè),需要首先了解對(duì)應(yīng)的總線(xiàn)
3阔加、訪(fǎng)問(wèn)寄存器的流程
由于linux使能了MMU饵史,因此對(duì)于驅(qū)動(dòng)來(lái)說(shuō),不能直接使用寄存器的物理地址胜榔,必須將其映射為虛擬地址才可以使用胳喷。
(1)定義寄存器物理基地址以及寄存器的偏移
#define GPIO_BASE? 0x11000000
#define GPIO_SIZE? 0x1000? //0x8
#define GPM4CON? ? 0x2E0? //偏移地址
#define GPM4DAT? ? 0x2E4? //偏移地址
GPIO_SIZE為寄存器的范圍,可以按照使用的寄存器的總大小進(jìn)行計(jì)算夭织,比如用了兩個(gè)寄存器厌蔽,范圍是0x8;但由于地址映射的最小單位是4K摔癣,因此小于4K的值都是可以的。
(2)將寄存器物理地址映射到虛擬地址纬向,如果映射不成功择浊,則無(wú)法訪(fǎng)問(wèn)寄存器
static void __iomem *vir_base;
vir_base = ioremap(GPIO_BASE, GPIO_SIZE);
if (!vir_base) {
? ? printk("Cannot ioremap\n");
? ? return -EIO;
(3)訪(fǎng)問(wèn)寄存器,一般采用基地址加偏移的模式逾条,內(nèi)核根據(jù)寄存器的大小琢岩,提供了一系列函數(shù)
8位寄存器的訪(fǎng)問(wèn)
char value;
value = readb(vir_base + offset);
writeb(value, (vir_base + offset));
16位寄存器的訪(fǎng)問(wèn)
short value;
value = readw(vir_base + offset);
writew(value, (vir_base + offset));
32位寄存器的訪(fǎng)問(wèn)
int value;
value = readl(vir_base + offset);
writel(value, (vir_base + offset));
64位寄存器的訪(fǎng)問(wèn)
u64 value;
value = readq(vir_base + offset);
writeq(value, (vir_base + offset));
(4)取消寄存器的映射
iounmap(vir_base);
————————————————
版權(quán)聲明:本文為CSDN博主「light_in_dark」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議师脂,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明担孔。
原文鏈接:https://blog.csdn.net/light_in_dark/article/details/73321105