mmc讀寫中scatterlist用法

2021-09-12 17:25:09 字數 3516 閱讀 7806

在閱讀核心**下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 的沒有問題,可...