在閱讀核心**下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++)
for迴圈其實就是遍歷記憶體塊了,不過據下面這個**上說的,這種用
for的方式已經
out了,現在要用
for_each_sg,
其實是一樣的:
/* fill in list and pass it to dma_map_sg(). then... */
for_each_sg(i, list, sgentry, nentries)
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 的沒有問題,可...