heap_init 函數在SDRAM中指定了一塊1M大小的內存作為heap(起始地址HEAP_BASE = 0x33e00000),并在heap的開頭定義了一個數據結構blockhead。事實上,heap就是使用一系列的blockhead數據結構來描述和操作的。每個blockhead數據結構對應著一塊heap內存,假設一個blockhead數據結構的存放位置為A,則它對應的可分配內存地址為“A + sizeof(blockhead)”到“A + sizeof(blockhead) + size - 1”。blockhead數據結構在lib/heap.c中定義:
1 typedef struct blockhead_t {
2 int32 signature; //固定為BLOCKHEAD_SIGNATURE
3 bool allocated; //此區域是否已經分配出去:0-N,1-Y
4 unsigned long size; //此區域大小
5 struct blockhead_t *next; //鏈表指針
6 struct blockhead_t *prev; //鏈表指針
7 } blockhead;
現在來看看heap是如何運作的(如果您不關心heap實現的細節,這段可以跳過)。vivi對heap的操作比較簡單,vivi中有一個全局變量 static blockhead *gHeapBase,它是heap的鏈表頭指針,通過它可以遍歷所有blockhead數據結構。假設需要動態申請一塊sizeA大小的內存,則 mmalloc函數從gHeapBase開始搜索blockhead數據結構,如果發現某個blockhead滿足:
(1) allocated = 0 //表示未分配
(2) size > sizeA,則找到了合適的blockhead,
滿足上述條件后,進行如下操作:
a.allocated設為1
b.如果size – sizeA > sizeof(blockhead),則將剩下的內存組織成一個新的blockhead,放入鏈表中
c.返回分配的內存的首地址釋放內存的操作更簡單,直接將要釋放的內存對應的blockhead數據結構的allocated設為0即可。
heap_init函數直接調用mmalloc_init函數進行初始化,此函數代碼在lib/heap.c中,比較簡單,初始化gHeapBase即可:
[main(int argc, char *argv[]) > heap_init(void) > mmalloc_init(unsigned char *heap, unsigned long size)]
1 static inline int mmalloc_init(unsigned char *heap, unsigned long size)
2 {
3 if (gHeapBase != NULL) return -1;
4 DPRINTK("malloc_init(): initialize heap area at 0x%08lx, size = 0x%08lx\n", heap, size);
5 gHeapBase = (blockhead *)(heap);
6 gHeapBase->allocated=FALSE;
7 gHeapBase->signature=BLOCKHEAD_SIGNATURE;
8 gHeapBase->next=NULL;
9 gHeapBase->prev=NULL;
10 gHeapBase->size = size - sizeof(blockhead);
11 return 0;
12 }
static blockhead *gHeapBase = NULL; 這個就是上面稱贊的全局變量了,定義在lib/heap.c中。上面就是個鏈表操作,數據結構,看來搞這個也得好好學數據結構啊,不然內存搞的溢出、浪費可就哭都來不及了。
5、Step 5:mtd_dev_init()
所謂MTD(Memory Technology Device)相關的技術。在linux系統中,我們通常會用到不同的存儲設備,特別是FLASH設備。為了在使用新的存儲設備時,我們能更簡便地提供它的驅動程序,在上層應用和硬件驅動的中間,抽象出MTD設備層。驅動層不必關心存儲的數據格式如何,比如是FAT32、ETX2還是FFS2或其它。它僅僅提供一些簡單的接口,比如讀寫、擦除及查詢。如何組織數據,則是上層應用的事情。MTD層將驅動層提供的函數封裝起來,向上層提供統一的接口。這樣,上層即可專注于文件系統的實現,而不必關心存儲設備的具體操作。這段亂七八糟的話也許比較讓人暈,也可以這樣理解在設備驅動(此處指存儲設備)和上層應用之間還存在著一層,共三層,這個中間層就是MTD技術的產物。通常可以將它視為驅動的一部分,叫做上層驅動,而那些實現設備的讀、寫操作的驅動稱為下層驅動,上層驅動將下層驅動封裝,并且留給其上層應用一些更加容易簡單的接口。
在我們即將看到的代碼中,使用mtd_info數據結構表示一個MTD 設備,使用nand_chip數據結構表示一個nand flash芯片。在mtd_info結構中,對nand_flash結構作了封裝,向上層提供統一的接口。比如,它根據nand_flash提供的 read_data(讀一個字節)、read_addr(發送要讀的扇區的地址)等函數,構造了一個通用的讀函數read,將此函數的指針作為自己的一個成員。而上層要讀寫flash時,執行mtd_info中的read、write函數即可。
mtd_dev_init()用來掃描所使用的 NAND Flash的型號,構造MTD設備,即構造一個mtd_info的數據結構。對于S3C2410來說,它直接調用mtd_init(),mtd_init 又調用smc_init(),此函數在drivers/mtd/maps/s3c2410_flash.c中:
[main(int argc,char *argv[])>mtd_dev_init()>mtd_init()]
1 int mtd_init(void)
2 {
3 int ret;
4 #ifdef CONFIG_MTD_CFI /*is not set*/
5 ret = cfi_init();
6 #endif
7 #ifdef CONFIG_MTD_SMC9 /* =y */
8 ret = smc_init();
9 #endif
10 #ifdef CONFIG_S3C2410_AMD_BOOT /*is not set*/
11 ret = amd_init();
12 #endif
13 if (ret) {
14 mymtd = NULL;
15 return ret;
16 }
17 return 0;
18 }
顯而易見,該函數應取第二項,這項在autoconf.h中定義了。
[main(int argc, char *argv[]) > mtd_dev_init() > mtd_init() > smc_init()]
1 static int
2 smc_init(void)
3 {
/*struct mtd_info *mymtd,數據類型在include/mtd/mtd.h*/
/*strcut nand_chip在include/mtd/nand.h中定義*/
4 struct nand_chip *this;
5 u_int16_t nfconf;
/* Allocate memory for MTD device structure and private data */
6 mymtd = mmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip));
7 if (!mymtd) {
8 printk("Unable to allocate S3C2410 NAND MTD device structure.\n");
9 return -ENOMEM;
10 }
/* Get pointer to private data */
11 this = (struct nand_chip *)(&mymtd[1]);
/* Initialize structures */
12 memset((char *)mymtd, 0, sizeof(struct mtd_info));
13 memset((char *)this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
14 mymtd->priv = this;
/* set NAND Flash controller */
15 nfconf = NFCONF;
/* NAND Flash controller enable */
16 nfconf |= NFCONF_FCTRL_EN;
/* Set flash memory timing */
17 nfconf &= ~NFCONF_TWRPH1; /* 0x0 */
18 nfconf |= NFCONF_TWRPH0_3; /* 0x3 */
19 nfconf &= ~NFCONF_TACLS; /* 0x0 */
20 NFCONF = nfconf;
/* Set address of NAND IO lines */
21 this->hwcontrol = smc_hwcontrol;
22 this->write_cmd = write_cmd;
23 this->write_addr = write_addr;
24 this->read_data = read_data;
25 this->write_data = write_data;
26 this->wait_for_ready = wait_for_ready;
/* Chip Enable -> RESET -> Wait for Ready -> Chip Disable */
27 this->hwcontrol(NAND_CTL_SETNCE);
28 this->write_cmd(NAND_CMD_RESET);
29 this->wait_for_ready();
30 this->hwcontrol(NAND_CTL_CLRNCE);
31 smc_insert(this);
32 return 0;
33 }
6 -14行構造了一個mtd_info結構和nand_flash結構,前者對應MTD設備,后者對應nand flash芯片(如果您用的是其他類型的存儲器件,比如nor flash,這里的nand_flash結構應該換為其他類型的數據結構)。MTD設備是具體存儲器件的抽象,那么在這些代碼中這種關系如何體現呢——第 14行的代碼把兩者連結在一起了。事實上,mtd_info結構中各成員的實現(比如read、write函數),正是由priv變量所指向的 nand_flash的各類操作函數(比如read_addr、read_data等)來實現的。
15-20行是初始化S3C2410上的 NAND FLASH控制器。前面分配的nand_flash結構還是空的,現在當然就是填滿它的各類成員了,這正是21-26行做的事情。27-30行對這塊 nand flash作了一下復位操作。最后,也是最復雜的部分,根據剛才填充的nand_flash結構,構造mtd_info結構,這由31行的 smc_insert函數調用smc_scan完成。
這才是VIVI啟動的第5步,還有三步就完成了啟動了,同時我的這篇閱讀筆記也就OVER了。
評論