在閱讀核心**下mmc模組時,經常會遇到mmc讀寫函式,一般的方式為建立乙個請求佇列,將命令和資料buf新增到請求佇列裡,有mmc塊裝置驅動將請求佇列發下去,但是將資料buf並不是直接帶下去,而是建立了scatterlist結構體,用sg_init_one函式將buf與其繫結,而由這個結構體進行資料的下發或讀取,如下所示,是讀取mmc ext——csd的乙個函式
static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len)
else
mmc_set_data_timeout(&data, card);
mmc_wait_for_req(host, &mrq);
memcpy(buf, data_buf, len);
kfree(data_buf);
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
return 0;
}而核心為什麼要這麼做呢而不是直接將乙個buf指標傳下去呢
接下來介紹scatterlist的用法
使用scatterlist的原因就是系統在執行的時候記憶體會產生很多碎片,比如4k,100k的,1m的,有時候對應磁碟碎片,總之就是碎片。而在網路和磁碟操作中很多時候需要傳送大塊的資料,尤其是使用dma的時候,因為dma操作的實體地址必須是連續的。假設要1m記憶體,此時可以分配乙個整的1m記憶體, 也可以把10個10k的和9個100k的組成一塊1m的記憶體,當然這19個塊可能是不連續的,也可能其中某些或全部是連續的,總之情況不定,為了描述這種情況,就引入了scatterlist,其實看成乙個關於記憶體塊構成的鍊錶就ok了。
在sd/mmc**中,在發起request的時候,都是通過scatterlist來傳送資料的,定義在mmc_data裡面,(mmc core就是這麼設計的,跟具體的s3c2410還是pxa就沒有關係了)
struct mmc_data
其中struct scatterlist *sg;就是指向scatter list的指標,可以理解為陣列的頭指標,這個陣列的作用就是儲存各個scatterlist結構的位址,sg_len表示有幾個 scatterlist結構,相當於陣列元素個數。比如前面提到的那個例子,sg_len就應該是19了,sg組成的記憶體塊就是 sg_mem0--->sg_mem1--->........->sg_mem18這樣的記憶體鏈。所以通過sg就可以遍歷19塊中的任意一塊記憶體的情況,比如位置和大小。以下是scatterlist的定義:
struct scatterlist ;
下面以dw_mmc.c裡面的scatter操作來分析,其實pxamci.c裡面也有,不過pxamci.c裡面只使用了dma模式,相對要簡單一點,dw_mmc.c裡面還使用pio模式(其實就是cpu模式),要複雜一些,所以分析起來更有意義。
1.dma模式下的使用
在使用dma操作這些scatterlist之前,先要對scatterlist進行一下map:
int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir)
其中的nents就是scatterlist的塊數,sg是指標陣列的首位址。返回值是map以後這些位址塊被合併為多少個適合dma搬運的塊的數量,假設其中一塊的結束位址和另一塊的起始位址挨到一起了,這兩塊是會合二為一的,這就是為什麼說返回的值可能會小於 nents的原因。比如上面的例子傳進去的nents=19, 函式的返回值肯定是小於等於19的,當然肯定大於0,同時sg的值也被改寫成了新的塊鍊錶。此時就可以把這些塊放入dma佇列乙個乙個的進行搬運了。 s3cmci.c中的**是這樣的。
dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
rw ? dma_to_device : dma_from_device);
for (i = 0; i < dma_len; i++)
2.cpu方式
cpu方式就不用呼叫map了,這些list本來就是cpu自己產生自己消化。在dw_mmc.c中是通過函式:
static inline int get_data_buffer(struct dw
_host *host,
u32 *bytes, u32 **pointer)
來實現的,它使用了乙個計數host->pio_sgptr 來記錄現在使用的是記憶體鏈中的第幾塊,這個值在每次request後prepare_pio的時候被清零,每次呼叫get_data_buffer就加 一,直到等於sg_len,表示所有的記憶體塊都用過了。對於乙個scatterlist指標sg,是如下這樣獲取它代表的記憶體塊的大小和位置的:
*bytes = sg->length;/*記憶體塊長度*/
*pointer = sg_virt(sg);/*記憶體塊起始位址*/
在dw_mmc.c中使用了一點點小技巧來操作scatterlist和sd/mmc fifo傳送/接收,比如在do_pio_write中
while ((fifo = fifo_free(host)) > 3)
if (fifo >= host->pio_bytes)
fifo = host->pio_bytes;
else
fifo -= fifo & 3;
host->pio_bytes -= fifo;
host->pio_count += fifo;
fifo = (fifo + 3) >> 2;
ptr = host->pio_ptr;
while (fifo--)
writel(*ptr++, to_ptr);
host->pio_ptr = ptr;
}它通過host->pio_bytes來記錄當前的記憶體塊還有多少資料沒有發,如果fifo裡面的空間夠用,那就直接都發了,如果不夠呢,則先把 fifo填滿,然後等著下一次中斷的時候再發。如果這個記憶體塊的資料都發完了,則host->pio_bytes為0,此時呼叫 get_data_buffer來獲取記憶體鏈中的下一塊記憶體資料,在get_data_bu中host->pio_bytes會被置為新塊的長度:
*bytes = sg->length
其中的*bytes就是指向host->pio_bytes的。
fifo-=fifo&3 是因為2410每次必須發四個位元組,所以要把零頭去掉,evb也有這個問題。
其實scatterlist這個東東蠻有意思的,俺們的nucleus上其實也可以借鑑的,由於記憶體太少,在解jpeg檔案時不一定能分到那麼大的一塊連續記憶體,可以通過scatterlist來把檔案分塊讀取,然後解碼的時候dma再一塊一塊的搬,總比分不到記憶體就返回失敗來的強。不過對於應用來講如果沒有mmu支援,還是有點杯具的,如果有mmu支援,讓應用層看到的是一整塊的記憶體,估計要爽的多,甚至在檔案系統層也是這樣的,只要到dma搬數之前把scatterlist的記憶體鏈分清楚就ok了
原文:
mmc讀寫中scatterlist用法
在閱讀核心 下mmc模組時,經常會遇到mmc讀寫函式,一般的方式為建立乙個請求佇列,將命令和資料buf新增到請求佇列裡,有mmc塊裝置驅動將請求佇列發下去,但是將資料buf並不是直接帶下去,而是建立了scatterlist結構體,用sg init one函式將buf與其繫結,而由這個結構體進行資料的...
讀寫SharedPreferences中的資料
很多時候我們開發的軟體需要向使用者提供軟體引數設定功能,例如我們常用的 qq,使用者可以設定是否允許陌生人新增自己為好友。對於軟體配置引數的儲存,如果是 window軟體通常我們會採用 ini檔案進行儲存,如果是 j2se應用,我們會採用 properties屬性檔案進行儲存。如果是 android...
asp 中讀寫檔案
維護專案中看到用到生成檔案時竟然出現錯誤了,以前沒有出現過錯誤的 不過上次生成檔案是英文版本的,這次生成的檔案是日文版的,一共生成了三個檔案,前兩個檔案通過 adodb.stream 儲存檔案的一點檔案也沒有 其實生成的三個檔案有兩個的內容來子同一張 的,用 adodb.stream 的沒有問題,可...