① 如何編寫linux下Nand Flash驅動
1. 對於驅動框架部分
其實,要了解,關於驅動框架部分,你所要做的事情的話,只要看看三星的整個 flash驅動中的這個結構體,就差不多了:
static struct platform_driver s3c2410_nand_driver = {
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2410-nand",
.owner = THIS_MODULE,
},
};
對於上面這個結構體,沒多少要解釋的。從名字,就能看出來:
(1)probe就是系統「探測」,就是前面解釋的整個過程,這個過程中的多數步驟,都是和你自己的nand flash相關的,尤其是那些硬體初始化部分,是你必須要自己實現的。
(2)remove,就是和probe對應的,「反初始化」相關的動作。主要是釋放系統相關資源和關閉硬體的時鍾等常見操作了。
(3)suspend和resume,對於很多沒用到電源管理的情況下,至少對於我們剛開始寫基本的驅動的時候,可以不用關心,放個空函數即可。
2. 對於nand flash底層操作實現部分
而對於底層硬體操作的有些函數,總體上說,都可以在上面提到的s3c2410_nand_init_chip中找到:
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd,
struct s3c2410_nand_set *set)
{
struct nand_chip *chip = &nmtd->chip;
void __iomem *regs = info->regs;
chip->write_buf = s3c2410_nand_write_buf;
chip->read_buf = s3c2410_nand_read_buf;
chip->select_chip = s3c2410_nand_select_chip;
chip->chip_delay = 50;
chip->priv = nmtd;
chip->options = 0;
chip->controller = &info->controller;
switch (info->cpu_type) {
case TYPE_S3C2410:
/* nand flash控制器中,一般都有對應的數據寄存器,用於給你往裡面寫數據,表示將要讀取或寫入多少個位元組(byte,u8)/字(word,u32),所以,此處,你要給出地址,以便後面的操作所使用 */
chip->IO_ADDR_W = regs + S3C2410_NFDATA;
info->sel_reg = regs + S3C2410_NFCONF;
info->sel_bit = S3C2410_NFCONF_nFCE;
chip->cmd_ctrl = s3c2410_nand_hwcontrol;
chip->dev_ready = s3c2410_nand_devready;
break;
。。。。。。
}
chip->IO_ADDR_R = chip->IO_ADDR_W;
nmtd->info = info;
nmtd->mtd.priv = chip;
nmtd->mtd.owner = THIS_MODULE;
nmtd->set = set;
if (hardware_ecc) {
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
chip->ecc.correct = s3c2410_nand_correct_data;
/* 此處,多數情況下,你所用的Nand Flash的控制器,都是支持硬體ECC的,所以,此處設置硬體ECC(HW_ECC) ,也是充分利用硬體的特性,而如果此處不用硬體去做的ECC的話,那麼下面也會去設置成NAND_ECC_SOFT,系統會用默認的軟體去做ECC校驗,相比之下,比硬體ECC的效率就低很多,而你的nand flash的讀寫,也會相應地要慢不少*/
chip->ecc.mode = NAND_ECC_HW;
switch (info->cpu_type) {
case TYPE_S3C2410:
chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
break;
。。。。。
}
} else {
chip->ecc.mode = NAND_ECC_SOFT;
}
if (set->ecc_layout != NULL)
chip->ecc.layout = set->ecc_layout;
if (set->disable_ecc)
chip->ecc.mode = NAND_ECC_NONE;
}
而我們要實現的底層函數,也就是上面藍色標出來的一些函數而已:
(1)s3c2410_nand_write_buf 和 s3c2410_nand_read_buf:這是兩個最基本的操作函數,其功能,就是往你的nand flash的控制器中的FIFO讀寫數據。一般情況下,是MTD上層的操作,比如要讀取一頁的數據,那麼在發送完相關的讀命令和等待時間之後,就會調用到你底層的read_buf,去nand Flash的FIFO中,一點點把我們要的數據,讀取出來,放到我們制定的內存的緩存中去。寫操作也是類似,將我們內存中的數據,寫到Nand Flash的FIFO中去。具體的數據流向,參考上面的圖4。
(2)s3c2410_nand_select_chip : 實現Nand Flash的片選。
(3)s3c2410_nand_hwcontrol:給底層發送命令或地址,或者設置具體操作的模式,都是通過此函數。
(4)s3c2410_nand_devready:Nand Flash的一些操作,比如讀一頁數據,寫入(編程)一頁數據,擦除一個塊,都是需要一定時間的,在命發送完成後,就是硬體開始忙著工作的時候了,而硬體什麼時候完成這些操作,什麼時候不忙了,變就緒了,就是通過這個函數去檢查狀態的。一般具體實現都是去讀硬體的一個狀態寄存器,其中某一位是否是1,對應著是出於「就緒/不忙」還是「忙」的狀態。這個寄存器,也就是我們前面分析時序圖中的R/B#。
(5)s3c2410_nand_enable_hwecc: 在硬體支持的前提下,前面設置了硬體ECC的話,要實現這個函數,用於每次在讀寫操作前,通過設置對應的硬體寄存器的某些位,使得啟用硬體ECC,這樣在讀寫操作完成後,就可以去讀取硬體校驗產生出來的ECC數值了。
(6)s3c2410_nand_calculate_ecc:如果是上面提到的硬體ECC的話,就不用我們用軟體去實現校驗演算法了,而是直接去讀取硬體產生的ECC數值就可以了。
(7)s3c2410_nand_correct_data:當實際操作過程中,讀取出來的數據所對應的硬體或軟體計算出來的ECC,和從oob中讀出來的ECC不一樣的時候,就是說明數據有誤了,就需要調用此函數去糾正錯誤。對於現在SLC常見的ECC演算法來說,可以發現2位,糾正1位。如果錯誤大於1位,那麼就無法糾正回來了。一般情況下,出錯超過1位的,好像幾率不大。至少我看到的不是很大。更復雜的情況和更加註重數據安全的情況下,一般是需要另外實現更高效和檢錯和糾錯能力更強的ECC演算法的。
當然,除了這些你必須實現的函數之外,在你更加熟悉整個框架之後,你可以根據你自己的nand flash的特點,去實現其他一些原先用系統默認但是效率不高的函數,而用自己的更高效率的函數替代他們,以提升你的nand flash的整體性能和效率。
② 嵌入式Linux 中,nand flash 和 nor flash ,那個用的多
Nand flash
NAND flash和NOR flash原理
一、存儲數據的原理
兩種快閃記憶體都是用三端器件作為存儲單元,分別為源極、漏極和柵極,與場效應管的工作原理 相同,主要是利用電場的效應來控制源極與漏極之間的通斷,柵極的 電流消耗極小,不同 的是場效應管為單柵極結構,而 FLASH 為雙柵極結構,在柵極與硅襯底之間增加了一個浮 置柵極。[attach]158 [/attach]
浮置柵極是由氮化物夾在兩層二氧化硅材料之間構成的,中間的氮化物就是可以存儲電荷的 電荷勢阱。上下兩層氧化物的厚度大於 50 埃,以避免發生擊穿。
二、浮柵的重放電
向數據單元內寫入數據的過程就是向電荷勢阱注入電荷的過程,寫入數據有兩種技術,熱電 子注入(hot electron injection)和 F-N 隧道效應(Fowler Nordheim tunneling),前一種是通過源 極給浮柵充電,後一種是通過硅基層給浮柵充電。NOR 型 FLASH 通過熱電子注入方式給浮 柵充電,而 NAND 則通過 F-N 隧道效應給浮柵充電。
在寫入新數據之前,必須先將原來的數據擦除,這點跟硬碟不同,也就是將浮柵的電荷放掉, 兩種 FLASH 都是通過 F-N 隧道效應放電。
三、0 和 1
這方面兩種 FLASH 一樣,向浮柵中注入電荷表示寫入了'0',沒有注入電荷表示'1',所以對 FLASH 清除數據是寫 1 的,這與硬碟正好相反;
對於浮柵中有電荷的單元來說,由於浮柵的感應作用,在源極和漏極之間將形成帶正電的空 間電荷區,這時無論控制極上有沒有施加偏置電壓,晶體管都將處於 導通狀態。而對於浮 柵中沒有電荷的晶體管來說只有當控制極上施加有適當的偏置電壓,在硅基層上感應出電 荷,源極和漏極才能導通,也就是說在沒有給控制極施 加偏置電壓時,晶體管是截止的。 如果晶體管的源極接地而漏極接位線,在無偏置電壓的情況下,檢測晶體管的導通狀態就可 以獲得存儲單元中的數據,如果位線上的電平為低,說明晶體管處於 導通狀態,讀取的數 據為 0,如果位線上為高電平,則說明晶體管處於截止狀態,讀取的數據為 1。由於控制柵 極在讀取數據的過程中施加的電壓較小或根本不施加 電壓,不足以改變浮置柵極中原有的 電荷量,所以讀取操作不會改變 FLASH 中原有的數據。
四、連接和編址方式
兩種 FLASH 具有相同的存儲單元,工作原理也一樣,為了縮短存取時間並不是對每個單元 進行單獨的存取操作,而是對一定數量的存取單元進行集體操作, NAND 型 FLASH 各存 儲單元之間是串聯的,而 NOR 型 FLASH 各單元之間是並聯的;為了對全部的存儲單元有 效管理,必須對存儲單元進行統一編址。
NAND 的全部存儲單元分為若干個塊,每個塊又分為若干個頁,每個頁是 512byte,就是 512 個 8 位數,就是說每個頁有 512 條位線,每條位線下 有 8 個存儲單元;那麼每頁存儲的數 據正好跟硬碟的一個扇區存儲的數據相同,這是設計時為了方便與磁碟進行數據交換而特意 安排的,那麼塊就類似硬碟的簇;容 量不同,塊的數量不同,組成塊的頁的數量也不同。 在讀取數據時,當字線和位線鎖定某個晶體管時,該晶體管的控制極不加偏置電壓,其它的 7 個都加上偏置電壓 而導通,如果這個晶體管的浮柵中有電荷就會導通使位線為低電平, 讀出的數就是 0,反之就是 1。
NOR 的每個存儲單元以並聯的方式連接到位線,方便對每一位進行隨機存取;具有專用的 地址線,可以實現一次性的直接定址;縮短了 FLASH 對處理器指令的執行時間。 五、性能
NAND flash和NOR flash的區別
一、NAND flash和NOR flash的性能比較
flash快閃記憶體是非易失存儲器,可以對稱為塊的存儲器單元塊進行擦寫和再編程。任何flash器件的寫入操作只能在空或已擦除的單元內進行,所以大多數情況下,在進行寫入操作之前必須先執行擦除。NAND器件執行擦除操作是十分簡單的,而NOR則要求在進行擦除前先要將目標塊內所有的位都寫為0。由於擦除NOR器件時是以64~128KB的塊進行的,執行一個寫入/擦除操作的時間為5s,與此相反,擦除NAND器件是以8~32KB的塊進行的,執行相同的操作最多隻需要4ms。執行擦除時塊尺寸的不同進一步拉大了NOR和NADN之間的性能差距,統計表明,對於給定的一套寫入操作(尤其是更新小文件時),更多的擦除操作必須在基於NOR的單元中進行。這樣,當選擇存儲解決方案時,設計師必須權衡以下的各項因素。
1、NOR的讀速度比NAND稍快一些。
2、NAND的寫入速度比NOR快很多。
3、NAND的4ms擦除速度遠比NOR的5s快。
4、大多數寫入操作需要先進行擦除操作。
5、NAND的擦除單元更小,相應的擦除電路更少。
二、NAND flash和NOR flash的介面差別
NOR flash帶有SRAM介面,有足夠的地址引腳來定址,可以很容易地存取其內部的每一個位元組。
NAND器件使用復雜的I/O口來串列地存取數據,各個產品或廠商的方法可能各不相同。8個引腳用來傳送控制、地址和數據信息。NAND讀和寫操作採用512位元組的塊,這一點有點像硬碟管理此類操作,很自然地,基於NAND的存儲器就可以取代硬碟或其他塊設備。
三、NAND flash和NOR flash的容量和成本
NAND flash的單元尺寸幾乎是NOR器件的一半,由於生產過程更為簡單,NAND結構可以在給定的模具尺寸內提供更高的容量,也就相應地降低了價格。
NOR flash占據了容量為1~16MB快閃記憶體市場的大部分,而NAND flash只是用在8~128MB的產品當中,這也說明NOR主要應用在代碼存儲介質中,NAND適合於數據存儲,NAND在CompactFlash、Secure Digital、PC Cards和MMC存儲卡市場上所佔份額最大。
四、NAND flash和NOR flash的可靠性和耐用性
採用flahs介質時一個需要重點考慮的問題是可靠性。對於需要擴展MTBF的系統來說,Flash是非常合適的存儲方案。可以從壽命(耐用性)、位交換和壞塊處理三個方面來比較NOR和NAND的可靠性。
五、NAND flash和NOR flash的壽命(耐用性)
在NAND快閃記憶體中每個塊的最大擦寫次數是一百萬次,而NOR的擦寫次數是十萬次。NAND存儲器除了具有10比1的塊擦除周期優勢,典型的NAND塊尺寸要比NOR器件小8倍,每個NAND存儲器塊在給定的時間內的刪除次數要少一些。
六、位交換
所有flash器件都受位交換現象的困擾。在某些情況下(很少見,NAND發生的次數要比NOR多),一個比特位會發生反轉或被報告反轉了。一位的變化可能不很明顯,但是如果發生在一個關鍵文件上,這個小小的故障可能導致系統停機。如果只是報告有問題,多讀幾次就可能解決了。當然,如果這個位真的改變了,就必須採用錯誤探測/錯誤更正(EDC/ECC)演算法。位反轉的問題更多見於NAND快閃記憶體,NAND的供應商建議使用NAND快閃記憶體的時候,同時使用
七、EDC/ECC演算法
這個問題對於用NAND存儲多媒體信息時倒不是致命的。當然,如果用本地存儲設備來存儲操作系統、配置文件或其他敏感信息時,必須使用EDC/ECC系統以確保可靠性。
八、壞塊處理
NAND器件中的壞塊是隨機分布的。以前也曾有過消除壞塊的努力,但發現成品率太低,代價太高,根本不劃算。
NAND器件需要對介質進行初始化掃描以發現壞塊,並將壞塊標記為不可用。在已製成的器件中,如果通過可靠的方法不能進行這項處理,將導致高故障率。
九、易於使用
可以非常直接地使用基於NOR的快閃記憶體,可以像其他存儲器那樣連接,並可以在上面直接運行代碼。
由於需要I/O介面,NAND要復雜得多。各種NAND器件的存取方法因廠家而異。在使用NAND器件時,必須先寫入驅動程序,才能繼續執行其他操作。向NAND器件寫入信息需要相當的技巧,因為設計師絕不能向壞塊寫入,這就意味著在NAND器件上自始至終都必須進行虛擬映射。
十、軟體支持
當討論軟體支持的時候,應該區別基本的讀/寫/擦操作和高一級的用於磁碟模擬和快閃記憶體管理演算法的軟體,包括性能優化。
在NOR器件上運行代碼不需要任何的軟體支持,在NAND器件上進行同樣操作時,通常需要驅動程序,也就是內存技術驅動程序(MTD),NAND和NOR器件在進行寫入和擦除操作時都需要MTD。
使用NOR器件時所需要的MTD要相對少一些,許多廠商都提供用於NOR器件的更高級軟體,這其中包括M-System的TrueFFS驅動,該驅動被Wind River System、Microsoft、QNX Software System、Symbian和Intel等廠商所採用。
驅動還用於對DiskOnChip產品進行模擬和NAND快閃記憶體的管理,包括糾錯、壞塊處理和損耗平衡。
③ 解釋一下linux驅動程序結構框架及工作原理
一、Linux device driver 的概念
系統調用是操作系統內核和應用程序之間的介面,設備驅動程序是操作系統內核和機器硬體之間的介面。設備驅動程序為應用程序屏蔽了硬體的細節,這樣在應用程序看來,硬體設備只是一個設備文件,應用程序可以象操作普通文件一樣對硬體設備進行操作。設備驅動程序是內核的一部分,它完成以下的功能:
1、對設備初始化和釋放;
2、把數據從內核傳送到硬體和從硬體讀取數據;
3、讀取應用程序傳送給設備文件的數據和回送應用程序請求的數據;
4、檢測和處理設備出現的錯誤。
在Linux操作系統下有三類主要的設備文件類型,一是字元設備,二是塊設備,三是網路設備。字元設備和塊設備的主要區別是:在對字元設備發出讀/寫請求時,實際的硬體I/O一般就緊接著發生了,塊設備則不然,它利用一塊系統內存作緩沖區,當用戶進程對設備請求能滿足用戶的要求,就返回請求的數據,如果不能,就調用請求函數來進行實際的I/O操作。塊設備是主要針對磁碟等慢速設備設計的,以免耗費過多的CPU時間來等待。
已經提到,用戶進程是通過設備文件來與實際的硬體打交道。每個設備文件都都有其文件屬性(c/b),表示是字元設備還是塊設備?另外每個文件都有兩個設備號,第一個是主設備號,標識驅動程序,第二個是從設備號,標識使用同一個設備驅動程序的不同的硬體設備,比如有兩個軟盤,就可以用從設備號來區分他們。設備文件的的主設備號必須與設備驅動程序在登記時申請的主設備號一致,否則用戶進程將無法訪問到驅動程序。
最後必須提到的是,在用戶進程調用驅動程序時,系統進入核心態,這時不再是搶先式調度。也就是說,系統必須在你的驅動程序的子函數返回後才能進行其他的工作。如果你的驅動程序陷入死循環,不幸的是你只有重新啟動機器了,然後就是漫長的fsck。
二、實例剖析
我們來寫一個最簡單的字元設備驅動程序。雖然它什麼也不做,但是通過它可以了解Linux的設備驅動程序的工作原理。把下面的C代碼輸入機器,你就會獲得一個真正的設備驅動程序。
由於用戶進程是通過設備文件同硬體打交道,對設備文件的操作方式不外乎就是一些系統調用,如 open,read,write,close…, 注意,不是fopen, fread,但是如何把系統調用和驅動程序關聯起來呢?這需要了解一個非常關鍵的數據結構:
STruct file_operatiONs {
int (*seek) (struct inode * ,struct file *, off_t ,int);
int (*read) (struct inode * ,struct file *, char ,int);
int (*write) (struct inode * ,struct file *, off_t ,int);
int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
int (*select) (struct inode * ,struct file *, int ,select_table *);
int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);
int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
int (*open) (struct inode * ,struct file *);
int (*release) (struct inode * ,struct file *);
int (*fsync) (struct inode * ,struct file *);
int (*fasync) (struct inode * ,struct file *,int);
int (*check_media_change) (struct inode * ,struct file *);
int (*revalidate) (dev_t dev);
}
這個結構的每一個成員的名字都對應著一個系統調用。用戶進程利用系統調用在對設備文件進行諸如read/write操作時,系統調用通過設備文件的主設備號找到相應的設備驅動程序,然後讀取這個數據結構相應的函數指針,接著把控制權交給該函數。這是linux的設備驅動程序工作的基本原理。既然是這樣,則編寫設備驅動程序的主要工作就是編寫子函數,並填充file_operations的各個域。
下面就開始寫子程序。
#include <linux/types.h> 基本的類型定義
#include <linux/fs.h> 文件系統使用相關的頭文件
#include <linux/mm.h>
#include <linux/errno.h>
#include <asm/segment.h>
unsigned int test_major = 0;
static int read_test(struct inode *inode,struct file *file,char *buf,int count)
{
int left; 用戶空間和內核空間
if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )
return -EFAULT;
for(left = count ; left > 0 ; left--)
{
__put_user(1,buf,1);
buf++;
}
return count;
}
這個函數是為read調用准備的。當調用read時,read_test()被調用,它把用戶的緩沖區全部寫1。buf 是read調用的一個參數。它是用戶進程空間的一個地址。但是在read_test被調用時,系統進入核心態。所以不能使用buf這個地址,必須用__put_user(),這是kernel提供的一個函數,用於向用戶傳送數據。另外還有很多類似功能的函數。請參考,在向用戶空間拷貝數據之前,必須驗證buf是否可用。這就用到函數verify_area。為了驗證BUF是否可以用。
static int write_test(struct inode *inode,struct file *file,const char *buf,int count)
{
return count;
}
static int open_test(struct inode *inode,struct file *file )
{
MOD_INC_USE_COUNT; 模塊計數加以,表示當前內核有個設備載入內核當中去
return 0;
}
static void release_test(struct inode *inode,struct file *file )
{
MOD_DEC_USE_COUNT;
}
這幾個函數都是空操作。實際調用發生時什麼也不做,他們僅僅為下面的結構提供函數指針。
struct file_operations test_fops = {?
read_test,
write_test,
open_test,
release_test,
};
設備驅動程序的主體可以說是寫好了。現在要把驅動程序嵌入內核。驅動程序可以按照兩種方式編譯。一種是編譯進kernel,另一種是編譯成模塊(moles),如果編譯進內核的話,會增加內核的大小,還要改動內核的源文件,而且不能動態的卸載,不利於調試,所以推薦使用模塊方式。
int init_mole(void)
{
int result;
result = register_chrdev(0, "test", &test_fops); 對設備操作的整個介面
if (result < 0) {
printk(KERN_INFO "test: can't get major number\n");
return result;
}
if (test_major == 0) test_major = result; /* dynamic */
return 0;
}
在用insmod命令將編譯好的模塊調入內存時,init_mole 函數被調用。在這里,init_mole只做了一件事,就是向系統的字元設備表登記了一個字元設備。register_chrdev需要三個參數,參數一是希望獲得的設備號,如果是零的話,系統將選擇一個沒有被佔用的設備號返回。參數二是設備文件名,參數三用來登記驅動程序實際執行操作的函數的指針。
如果登記成功,返回設備的主設備號,不成功,返回一個負值。
void cleanup_mole(void)
{
unregister_chrdev(test_major,"test");
}
在用rmmod卸載模塊時,cleanup_mole函數被調用,它釋放字元設備test在系統字元設備表中佔有的表項。
一個極其簡單的字元設備可以說寫好了,文件名就叫test.c吧。
下面編譯 :
$ gcc -O2 -DMODULE -D__KERNEL__ -c test.c –c表示輸出制定名,自動生成.o文件
得到文件test.o就是一個設備驅動程序。
如果設備驅動程序有多個文件,把每個文件按上面的命令行編譯,然後
ld ?-r ?file1.o ?file2.o ?-o ?molename。
驅動程序已經編譯好了,現在把它安裝到系統中去。
$ insmod ?–f ?test.o
如果安裝成功,在/proc/devices文件中就可以看到設備test,並可以看到它的主設備號。要卸載的話,運行 :
$ rmmod test
下一步要創建設備文件。
mknod /dev/test c major minor
c 是指字元設備,major是主設備號,就是在/proc/devices里看到的。
用shell命令
$ cat /proc/devices
就可以獲得主設備號,可以把上面的命令行加入你的shell script中去。
minor是從設備號,設置成0就可以了。
我們現在可以通過設備文件來訪問我們的驅動程序。寫一個小小的測試程序。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main()
{
int testdev;
int i;
char buf[10];
testdev = open("/dev/test",O_RDWR);
if ( testdev == -1 )
{
printf("Cann't open file \n");
exit(0);
}
read(testdev,buf,10);
for (i = 0; i < 10;i++)
printf("%d\n",buf[i]);
close(testdev);
}
編譯運行,看看是不是列印出全1
以上只是一個簡單的演示。真正實用的驅動程序要復雜的多,要處理如中斷,DMA,I/O port等問題。這些才是真正的難點。上述給出了一個簡單的字元設備驅動編寫的框架和原理,更為復雜的編寫需要去認真研究LINUX內核的運行機制和具體的設備運行的機制等等。希望大家好好掌握LINUX設備驅動程序編寫的方法。
④ linux 內核支持 spi flash 和 nand flash同時使用嗎
在<linux/spi/spi.h>頭文件中包含有內核文檔,做為主要的源碼,你應該詳讀內核API文檔的相關章節.本文只是概覽,在了解細節前有個大致的圖景是好的.
SPI請求會進入到I/O隊列中.請求給定的SPI設備也是按照FIFO順序進行的,通過完成機制非同步通知.也同簡單的同步措施:先寫在讀出來.
有倆類SPI驅動:
控制器驅動(Controller drivers)...集成在SOC中的控制器,經常扮演Master和Slave雙角色.這類驅動直接接觸到硬體層的寄存器甚至使用DMA.亦或者扮演bitbanger,僅需要GPIO腳;
協議驅動(Protocoldrivers)...在控制器和slave或者控制器和另外一條SPI鏈路上的Master傳遞消息.協議驅動是將控制器讀到的數據,比如是一堆0,1代碼,解析成有意義的協議數據;
對於協議驅動應該是我們要寫的,spi在linux內核中有spi子系統分為spi核心層,就類似USBcore一樣是主控制器部分,另一個就是spi設備層了.前者內核幫咱寫好了,為了讓你的spi設備能工作,就得藉助spicontroller driver導出的一些設施來編寫protocoldrivers了.
struct spi_device結構封裝了倆類驅動間的master-side介面.
有一個最小化SPI編程介面的core,專注於使用板級初始化代碼提供的設備表並藉助於驅動模型來連接controller和protocol驅動.在sysfs文件系統中,SPI視圖:
1 /sys/devices/.../CTLR ... physical node for a given SPI controller
2
3 /sys/devices/.../CTLR/spiB.C ... spi_device on bus "B",
4 chipselect C, accessed through CTLR.
5
6 /sys/bus/spi/devices/spiB.C ... symlink to that physical
7 .../CTLR/spiB.C device
8
9 /sys/devices/.../CTLR/spiB.C/modalias ... identifies the driver
10 that should be used with this device (for hotplug/coldplug)
11
12 /sys/bus/spi/drivers/D ... driver for one or more spi*.* devices
13
14 /sys/class/spi_master/spiB ... symlink (or actual device node) to
15 a logical node which could hold class related state for the
16 controller managing bus "B". All spiB.* devices share one
17 physical SPI bus segment, with SCLK, MOSI, and MISO.
需要注意的是控制器類狀態的實際位置取決於您是否開啟CONFIG_SYSFS_DEPRECATED標志.此時,唯一的特定類狀態是匯流排編號("B" in "spiB"),所以/sys/class下的那些入口項是唯一的識別匯流排的標志.