本博客转载于:http://www.cnblogs.com/tureno/articles/4445401.html
在我们即将看到的代码中, 使用mtd_info数据结构表示一个MTD设备, 使用nand_chip数据结构表示一个nand flash芯片。 在mtd_info结构中,对nand_flash结构作了封装,向上层提供统一的接口。比如, 它根据nand_flash提供的read_data(读一个字节)、read_addr(发送要读的扇区的地址)等函数,构造了一个通用的读函数read,将此函数的指针作为自己的一个成员。而上层要读写flash
时,执行mtd_info中的read、write函数即可。
|
|
|
u_char type;
u_int32_t flags;
u_int32_t size; // Total size of the MTD
* to be the only erase size available, or may use the more detailed
* information below if they desire
*/
u_int32_t erasesize;
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
u_int32_t ecctype;
u_int32_t eccsize;
char *name;
int index;
* it means that the whole device has erasesize as given above.
*/
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
u_int32_t bank_size;
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);
void (*unpoint) (struct mtd_info *mtd, u_char * addr);
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf);
int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
* Methods to access the protection register area, present in some
* flash devices. The user data is one time programmable but the
* factory data is read only.
*/
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
/* Chip-supported device locking */
int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
};
#ifdef CONFIG_MTD_NANDY
void (*hwcontrol)(int cmd);
void (*write_cmd)(u_char val);
void (*write_addr)(u_char val);
u_char (*read_data)(void);
void (*write_data)(u_char val);
void (*wait_for_ready)(void);
/*spinlock_t chip_lock;*/
/*wait_queue_head_t wq;*/
/*nand_state_t state;*/
int page_shift;
u_char *data_buf;
u_char *data_cache;
int cache_page;
struct nand_smc_dev *dev;
u_char spare[SMC_OOB_SIZE];
#else /* CONFIG_MTD_NANDY */
unsigned long IO_ADDR_R;
unsigned long IO_ADDR_W;
void (*hwcontrol)(int cmd);
int (*dev_ready)(void);
int chip_delay;
/*spinlock_t chip_lock;*/
/*wait_queue_head_t wq;*/
/*nand_state_t state;*/
int page_shift;
u_char *data_buf;
u_char *data_cache;
int cache_page;
#ifdef CONFIG_MTD_NAND_ECC
u_char ecc_code_buf[6];
u_char reserved[2];
#endif
#endif /* CONFIG_MTD_NANDY */
};
|
|
|
|
|
|
{
int i, nand_maf_id, nand_dev_id; //定义flash的厂家ID和设备ID
struct nand_chip *this = mtd->priv; //获得与mtd设备相联系的真正的flash设备结构
nand_select(); //#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); nand_command(mtd, NAND_CMD_RESET, -1, -1); udelay(10);这三句代码和我们之前在nand章节中讲解的片选flash是一个意思,先将使能nand的那位置1也就是片选nand,然后想nand发送reset命令,然后等待一段时间。
nand_command(mtd, NAND_CMD_READID, 0x00, -1);//向nand发送读ID的命令
nand_maf_id = this->read_data();//nand数据准备好后,通过read_data可以相继的读出厂家ID和设备ID
nand_dev_id = this->read_data();
for (i = 0; nand_flash_ids[i].name != NULL; i++) { //在数组nand_flash_ids中查找与ID相符合的项,可以看到下面对数组说明
if (nand_maf_id == nand_flash_ids[i].manufacture_id &&
nand_dev_id == nand_flash_ids[i].model_id) {
#ifdef USE_256BYTE_NAND_FLASH
if (!mtd->size) { //下面都是根据在nand_flash_ids数组中找到相符的项,然后从对应的nand_flash_dev结构体中取出对应的属性值来填充mtd_info结构
mtd->name = nand_flash_ids[i].name;
mtd->erasesize = nand_flash_ids[i].erasesize;
mtd->size = (1 << nand_flash_ids[i].chipshift); //
mtd->eccsize = 256;
if (nand_flash_ids[i].page256) {
mtd->oobblock = 256;
mtd->oobsize = 8;
this->page_shift = 8;
} else {
mtd->oobblock = 512;
mtd->oobsize = 16;
this->page_shift = 9;
}
this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)];
}
#else
if (!(mtd->size) && !(nand_flash_ids[i].page256)) {
mtd->name = nand_flash_ids[i].name;
mtd->erasesize = nand_flash_ids[i].erasesize;
mtd->size = (1 << nand_flash_ids[i].chipshift);
mtd->eccsize = 256;
mtd->oobblock = 512;
mtd->oobsize = 16;
this->page_shift = 9;
this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)];
}
#endif
printk("NAND device: Manufacture ID:" /
" 0x%02x, Chip ID: 0x%02x (%s)/n",
nand_maf_id, nand_dev_id, mtd->name);
break;
}
}
nand_deselect(); //在对nand flash操作完后,需要禁止nand flash#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);也就是将对应的位置0
if (!mtd->size) {
printk("No NAND device found!!!/n");
return 1;
}
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; //填充mtd_info结构体中的函数指针,这些函数大多都是在smc_core.c定义
mtd->module = NULL;
mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_ecc = nand_read_ecc;
mtd->write_ecc = nand_write_ecc;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->lock = NULL;
mtd->unlock = NULL;
return 0;
}
|
|
|
|
a.vivi自身使用的一些参数,比如传输文件时的使用的协议等
b.linux启动命令
c.nand flash的分区参数
|
|
|
|
|
{
char *src = linux_cmd;
char *dst = (char *)(VIVI_PRIV_RAM_BASE + LINUX_CMD_OFFSET);
dst += 8;
memcpy(dst, src, (strlen(src) + 1));
}
mtd_partition_t default_mtd_partitions[] = {
{
name: "vivi",
offset: 0,
size: 0x00020000,
flag: 0
}, {
name: "param",
offset: 0x00020000,
size: 0x00010000,
flag: 0
}, {
name: "kernel",
offset: 0x00030000,
size: 0x001d0000, // 2M sector
flag: 0
}, {
name: "root",
offset: 0x00200000,
size: 0x03000000,
flag: MF_BONFS
}
};在这里在flash分为vivi区,参数区,内核区,文件系统区,我们在vivi期间会将对应的内核,文件系统都会放到指定的区域,然后将信息传递给内核,这样内核在启动时会知道从哪地方加载内核,和文件系统。
{
char *buf = (char *)(DRAM_BASE);
char *dst = (char *)(VIVI_PRIV_RAM_BASE);
printk("invalid (saved) parameter block/n");
return -1;
}
if (strncmp((buf + PARAMETER_TLB_OFFSET), vivi_param_magic, 8) != 0)
return WRONG_MAGIC_PARAM;
memcpy(dst + PARAMETER_TLB_OFFSET, buf + PARAMETER_TLB_OFFSET,
PARAMETER_TLB_SIZE);
/* load linux command line */
if (strncmp((buf + LINUX_CMD_OFFSET), linux_cmd_magic, 8) != 0)
return WRONG_MAGIC_LINUXCMD;
memcpy((dst + LINUX_CMD_OFFSET), buf + LINUX_CMD_OFFSET, LINUX_CMD_SIZE);
/* load mtd partition table */
if (strncmp(buf + MTD_PART_OFFSET, mtd_part_magic, 8) != 0)
return WRONG_MAGIC_MTDPART;
memcpy(dst + MTD_PART_OFFSET, buf + MTD_PART_OFFSET, MTD_PART_SIZE);
}
{
char *src = (char *)(VIVI_PRIV_ROM_BASE); //VIVI_PRIV_ROM_BASE为flash上参数区域的起始地址
size_t size = (size_t)(VIVI_PRIV_SIZE); //VIVI_PRIV_SIZE也就是这三种类型的参数大小也就是48KB
{ //如果是在nand flash上定义了param分区,我们则直接去得到param分区的mtd_partition_t结构体
mtd_partition_t *part = get_mtd_partition("param");
if (part == NULL) {
printk("Could not found 'param' partition/n");
return -1;
}
src = (char *)part->offset; //然后获取到该结构体中定义的该分区的偏移地址,也真正就是存放参数的地址
}
#endif
return read_mem(buf, src, size); //然后从参数的源地址中读取参数先存放到sdram的起始地址也就是0x30000000,然后再拷贝到sdram的参数区,也就是load_saved_priv_data函数中的操作。read_mem函数里面也就是nand_read_ll函数,读nand flash之前已经讲过。
}
|
|
|
|
{
char c;
int ret;
ulong boot_delay;
boot_delay = get_param_value("boot_delay", &ret); //从vivi的环境参数boot_delay中获取到等待的时间
if (ret) boot_delay = DEFAULT_BOOT_DELAY; //若没有设置该参数的值,则利用默认的等待时间
* unconditionally call vivi shell */
/*
* wait for a keystroke (or a button press if you want.)
*/
printk("Press Return to start the LINUX now, any other key for vivi/n");
c = awaitkey(boot_delay, NULL); //这句话也很重要,在等待时间内获取到用户按下去的键盘码值
if (((c != '/r') && (c != '/n') && (c != '/0'))) {
printk("type /"help/" for help./n");
vivi_shell();
}
run_autoboot(); //启动内核
}
{
#ifdef CONFIG_SERIAL_TERM
serial_term();
#else
#error there is no terminal.
#endif
}
{
char cmd_buf[MAX_CMDBUF_SIZE];
printk("%s> ", prompt);
if (cmd_buf[0])
exec_string(cmd_buf);//最终还是调用exec_string来进行命令的处理函数分配
}
}
{
while (1) {
exec_string("boot");
printk("Failed 'boot' command. reentering vivi shell/n");
/* if default boot fails, drop into the shell */
vivi_shell();
}
}
{
int argc;
char *argv[128];
char *resid;
memset(argv, 0, sizeof(argv));
parseargs(buf, &argc, argv, &resid); //从得到的命令字符串解析出命令
if (argc > 0)
execcmd(argc, (const char **)argv);//然后执行命令,在这里其实也是查表,找到命令名和argc相同的user_command_t结构体,然后调用user_command_t结构体中对应的命令处理函数来执行。
buf = resid;
}
}