⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nandsim.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
			ns->geom.pgaddrbytes  = 4;			ns->geom.secaddrbytes = 3;		}	} else {		if (ns->geom.totsz <= (128 << 20)) {			ns->geom.pgaddrbytes  = 4;			ns->geom.secaddrbytes = 2;		} else {			ns->geom.pgaddrbytes  = 5;			ns->geom.secaddrbytes = 3;		}	}	/* Fill the partition_info structure */	if (parts_num > ARRAY_SIZE(ns->partitions)) {		NS_ERR("too many partitions.\n");		ret = -EINVAL;		goto error;	}	remains = ns->geom.totsz;	next_offset = 0;	for (i = 0; i < parts_num; ++i) {		u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz;		if (!part_sz || part_sz > remains) {			NS_ERR("bad partition size.\n");			ret = -EINVAL;			goto error;		}		ns->partitions[i].name   = get_partition_name(i);		ns->partitions[i].offset = next_offset;		ns->partitions[i].size   = part_sz;		next_offset += ns->partitions[i].size;		remains -= ns->partitions[i].size;	}	ns->nbparts = parts_num;	if (remains) {		if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {			NS_ERR("too many partitions.\n");			ret = -EINVAL;			goto error;		}		ns->partitions[i].name   = get_partition_name(i);		ns->partitions[i].offset = next_offset;		ns->partitions[i].size   = remains;		ns->nbparts += 1;	}	/* Detect how many ID bytes the NAND chip outputs */        for (i = 0; nand_flash_ids[i].name != NULL; i++) {                if (second_id_byte != nand_flash_ids[i].id)                        continue;		if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR))			ns->options |= OPT_AUTOINCR;	}	if (ns->busw == 16)		NS_WARN("16-bit flashes support wasn't tested\n");	printk("flash size: %llu MiB\n",			(unsigned long long)ns->geom.totsz >> 20);	printk("page size: %u bytes\n",         ns->geom.pgsz);	printk("OOB area size: %u bytes\n",     ns->geom.oobsz);	printk("sector size: %u KiB\n",         ns->geom.secsz >> 10);	printk("pages number: %u\n",            ns->geom.pgnum);	printk("pages per sector: %u\n",        ns->geom.pgsec);	printk("bus width: %u\n",               ns->busw);	printk("bits in sector size: %u\n",     ns->geom.secshift);	printk("bits in page size: %u\n",       ns->geom.pgshift);	printk("bits in OOB size: %u\n",	ns->geom.oobshift);	printk("flash size with OOB: %llu KiB\n",			(unsigned long long)ns->geom.totszoob >> 10);	printk("page address bytes: %u\n",      ns->geom.pgaddrbytes);	printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);	printk("options: %#x\n",                ns->options);	if ((ret = alloc_device(ns)) != 0)		goto error;	/* Allocate / initialize the internal buffer */	ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);	if (!ns->buf.byte) {		NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",			ns->geom.pgszoob);		ret = -ENOMEM;		goto error;	}	memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);	return 0;error:	free_device(ns);	return ret;}/* * Free the nandsim structure. */static void free_nandsim(struct nandsim *ns){	kfree(ns->buf.byte);	free_device(ns);	return;}static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd){	char *w;	int zero_ok;	unsigned int erase_block_no;	loff_t offset;	if (!badblocks)		return 0;	w = badblocks;	do {		zero_ok = (*w == '0' ? 1 : 0);		erase_block_no = simple_strtoul(w, &w, 0);		if (!zero_ok && !erase_block_no) {			NS_ERR("invalid badblocks.\n");			return -EINVAL;		}		offset = erase_block_no * ns->geom.secsz;		if (mtd->block_markbad(mtd, offset)) {			NS_ERR("invalid badblocks.\n");			return -EINVAL;		}		if (*w == ',')			w += 1;	} while (*w);	return 0;}static int parse_weakblocks(void){	char *w;	int zero_ok;	unsigned int erase_block_no;	unsigned int max_erases;	struct weak_block *wb;	if (!weakblocks)		return 0;	w = weakblocks;	do {		zero_ok = (*w == '0' ? 1 : 0);		erase_block_no = simple_strtoul(w, &w, 0);		if (!zero_ok && !erase_block_no) {			NS_ERR("invalid weakblocks.\n");			return -EINVAL;		}		max_erases = 3;		if (*w == ':') {			w += 1;			max_erases = simple_strtoul(w, &w, 0);		}		if (*w == ',')			w += 1;		wb = kzalloc(sizeof(*wb), GFP_KERNEL);		if (!wb) {			NS_ERR("unable to allocate memory.\n");			return -ENOMEM;		}		wb->erase_block_no = erase_block_no;		wb->max_erases = max_erases;		list_add(&wb->list, &weak_blocks);	} while (*w);	return 0;}static int erase_error(unsigned int erase_block_no){	struct weak_block *wb;	list_for_each_entry(wb, &weak_blocks, list)		if (wb->erase_block_no == erase_block_no) {			if (wb->erases_done >= wb->max_erases)				return 1;			wb->erases_done += 1;			return 0;		}	return 0;}static int parse_weakpages(void){	char *w;	int zero_ok;	unsigned int page_no;	unsigned int max_writes;	struct weak_page *wp;	if (!weakpages)		return 0;	w = weakpages;	do {		zero_ok = (*w == '0' ? 1 : 0);		page_no = simple_strtoul(w, &w, 0);		if (!zero_ok && !page_no) {			NS_ERR("invalid weakpagess.\n");			return -EINVAL;		}		max_writes = 3;		if (*w == ':') {			w += 1;			max_writes = simple_strtoul(w, &w, 0);		}		if (*w == ',')			w += 1;		wp = kzalloc(sizeof(*wp), GFP_KERNEL);		if (!wp) {			NS_ERR("unable to allocate memory.\n");			return -ENOMEM;		}		wp->page_no = page_no;		wp->max_writes = max_writes;		list_add(&wp->list, &weak_pages);	} while (*w);	return 0;}static int write_error(unsigned int page_no){	struct weak_page *wp;	list_for_each_entry(wp, &weak_pages, list)		if (wp->page_no == page_no) {			if (wp->writes_done >= wp->max_writes)				return 1;			wp->writes_done += 1;			return 0;		}	return 0;}static int parse_gravepages(void){	char *g;	int zero_ok;	unsigned int page_no;	unsigned int max_reads;	struct grave_page *gp;	if (!gravepages)		return 0;	g = gravepages;	do {		zero_ok = (*g == '0' ? 1 : 0);		page_no = simple_strtoul(g, &g, 0);		if (!zero_ok && !page_no) {			NS_ERR("invalid gravepagess.\n");			return -EINVAL;		}		max_reads = 3;		if (*g == ':') {			g += 1;			max_reads = simple_strtoul(g, &g, 0);		}		if (*g == ',')			g += 1;		gp = kzalloc(sizeof(*gp), GFP_KERNEL);		if (!gp) {			NS_ERR("unable to allocate memory.\n");			return -ENOMEM;		}		gp->page_no = page_no;		gp->max_reads = max_reads;		list_add(&gp->list, &grave_pages);	} while (*g);	return 0;}static int read_error(unsigned int page_no){	struct grave_page *gp;	list_for_each_entry(gp, &grave_pages, list)		if (gp->page_no == page_no) {			if (gp->reads_done >= gp->max_reads)				return 1;			gp->reads_done += 1;			return 0;		}	return 0;}static void free_lists(void){	struct list_head *pos, *n;	list_for_each_safe(pos, n, &weak_blocks) {		list_del(pos);		kfree(list_entry(pos, struct weak_block, list));	}	list_for_each_safe(pos, n, &weak_pages) {		list_del(pos);		kfree(list_entry(pos, struct weak_page, list));	}	list_for_each_safe(pos, n, &grave_pages) {		list_del(pos);		kfree(list_entry(pos, struct grave_page, list));	}	kfree(erase_block_wear);}static int setup_wear_reporting(struct mtd_info *mtd){	size_t mem;	if (!rptwear)		return 0;	wear_eb_count = divide(mtd->size, mtd->erasesize);	mem = wear_eb_count * sizeof(unsigned long);	if (mem / sizeof(unsigned long) != wear_eb_count) {		NS_ERR("Too many erase blocks for wear reporting\n");		return -ENOMEM;	}	erase_block_wear = kzalloc(mem, GFP_KERNEL);	if (!erase_block_wear) {		NS_ERR("Too many erase blocks for wear reporting\n");		return -ENOMEM;	}	return 0;}static void update_wear(unsigned int erase_block_no){	unsigned long wmin = -1, wmax = 0, avg;	unsigned long deciles[10], decile_max[10], tot = 0;	unsigned int i;	if (!erase_block_wear)		return;	total_wear += 1;	if (total_wear == 0)		NS_ERR("Erase counter total overflow\n");	erase_block_wear[erase_block_no] += 1;	if (erase_block_wear[erase_block_no] == 0)		NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);	rptwear_cnt += 1;	if (rptwear_cnt < rptwear)		return;	rptwear_cnt = 0;	/* Calc wear stats */	for (i = 0; i < wear_eb_count; ++i) {		unsigned long wear = erase_block_wear[i];		if (wear < wmin)			wmin = wear;		if (wear > wmax)			wmax = wear;		tot += wear;	}	for (i = 0; i < 9; ++i) {		deciles[i] = 0;		decile_max[i] = (wmax * (i + 1) + 5) / 10;	}	deciles[9] = 0;	decile_max[9] = wmax;	for (i = 0; i < wear_eb_count; ++i) {		int d;		unsigned long wear = erase_block_wear[i];		for (d = 0; d < 10; ++d)			if (wear <= decile_max[d]) {				deciles[d] += 1;				break;			}	}	avg = tot / wear_eb_count;	/* Output wear report */	NS_INFO("*** Wear Report ***\n");	NS_INFO("Total numbers of erases:  %lu\n", tot);	NS_INFO("Number of erase blocks:   %u\n", wear_eb_count);	NS_INFO("Average number of erases: %lu\n", avg);	NS_INFO("Maximum number of erases: %lu\n", wmax);	NS_INFO("Minimum number of erases: %lu\n", wmin);	for (i = 0; i < 10; ++i) {		unsigned long from = (i ? decile_max[i - 1] + 1 : 0);		if (from > decile_max[i])			continue;		NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n",			from,			decile_max[i],			deciles[i]);	}	NS_INFO("*** End of Wear Report ***\n");}/* * Returns the string representation of 'state' state. */static char *get_state_name(uint32_t state){	switch (NS_STATE(state)) {		case STATE_CMD_READ0:			return "STATE_CMD_READ0";		case STATE_CMD_READ1:			return "STATE_CMD_READ1";		case STATE_CMD_PAGEPROG:			return "STATE_CMD_PAGEPROG";		case STATE_CMD_READOOB:			return "STATE_CMD_READOOB";		case STATE_CMD_READSTART:			return "STATE_CMD_READSTART";		case STATE_CMD_ERASE1:			return "STATE_CMD_ERASE1";		case STATE_CMD_STATUS:			return "STATE_CMD_STATUS";		case STATE_CMD_STATUS_M:			return "STATE_CMD_STATUS_M";		case STATE_CMD_SEQIN:			return "STATE_CMD_SEQIN";		case STATE_CMD_READID:			return "STATE_CMD_READID";		case STATE_CMD_ERASE2:			return "STATE_CMD_ERASE2";		case STATE_CMD_RESET:			return "STATE_CMD_RESET";		case STATE_CMD_RNDOUT:			return "STATE_CMD_RNDOUT";		case STATE_CMD_RNDOUTSTART:			return "STATE_CMD_RNDOUTSTART";		case STATE_ADDR_PAGE:			return "STATE_ADDR_PAGE";		case STATE_ADDR_SEC:			return "STATE_ADDR_SEC";		case STATE_ADDR_ZERO:			return "STATE_ADDR_ZERO";		case STATE_ADDR_COLUMN:			return "STATE_ADDR_COLUMN";		case STATE_DATAIN:			return "STATE_DATAIN";		case STATE_DATAOUT:			return "STATE_DATAOUT";		case STATE_DATAOUT_ID:			return "STATE_DATAOUT_ID";		case STATE_DATAOUT_STATUS:			return "STATE_DATAOUT_STATUS";		case STATE_DATAOUT_STATUS_M:			return "STATE_DATAOUT_STATUS_M";		case STATE_READY:			return "STATE_READY";		case STATE_UNKNOWN:			return "STATE_UNKNOWN";	}	NS_ERR("get_state_name: unknown state, BUG\n");	return NULL;}/* * Check if command is valid. * * RETURNS: 1 if wrong command, 0 if right. */static int check_command(int cmd){	switch (cmd) {	case NAND_CMD_READ0:	case NAND_CMD_READ1:	case NAND_CMD_READSTART:	case NAND_CMD_PAGEPROG:	case NAND_CMD_READOOB:	case NAND_CMD_ERASE1:	case NAND_CMD_STATUS:	case NAND_CMD_SEQIN:	case NAND_CMD_READID:	case NAND_CMD_ERASE2:	case NAND_CMD_RESET:	case NAND_CMD_RNDOUT:	case NAND_CMD_RNDOUTSTART:		return 0;	case NAND_CMD_STATUS_MULTI:	default:		return 1;	}}/* * Returns state after command is accepted by command number. */static uint32_t get_state_by_command(unsigned command){	switch (command) {		case NAND_CMD_READ0:			return STATE_CMD_READ0;		case NAND_CMD_READ1:			return STATE_CMD_READ1;		case NAND_CMD_PAGEPROG:			return STATE_CMD_PAGEPROG;		case NAND_CMD_READSTART:			return STATE_CMD_READSTART;		case NAND_CMD_READOOB:			return STATE_CMD_READOOB;		case NAND_CMD_ERASE1:			return STATE_CMD_ERASE1;		case NAND_CMD_STATUS:			return STATE_CMD_STATUS;		case NAND_CMD_STATUS_MULTI:			return STATE_CMD_STATUS_M;		case NAND_CMD_SEQIN:			return STATE_CMD_SEQIN;		case NAND_CMD_READID:			return STATE_CMD_READID;		case NAND_CMD_ERASE2:			return STATE_CMD_ERASE2;		case NAND_CMD_RESET:			return STATE_CMD_RESET;		case NAND_CMD_RNDOUT:			return STATE_CMD_RNDOUT;		case NAND_CMD_RNDOUTSTART:			return STATE_CMD_RNDOUTSTART;	}	NS_ERR("get_state_by_command: unknown command, BUG\n");	return 0;}/* * Move an address byte to the correspondent internal register. */static inline void accept_addr_byte(struct nandsim *ns, u_char bt){	uint byte = (uint)bt;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -