bbcr_test.c 6.91 KB
/* bbcr_test.c v1 Frank Berndt
 * bb depot flash interface tests;
 * :set tabstop=4
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>

#include "bbcr.h"

char *cmd;			/* argv[0] */

/*
 * check for block marked bad;
 * more than one 0 in byte 517;
 */
int
marked_bad(u_char x)
{
	int n, zeros;

	zeros = 0;
	for(n = 1; n < 0x100; n <<= 1) {
		if(x & n)
			continue;
		zeros++;
	}
	return(zeros > 1);
}

/*
 * print bad block list;
 */
void
print_list(bbcr_dev_t *dev, u_char *list, u_char mask)
{
	int pos, n;

	printf("%s: bad block list\n", cmd);
	pos = 0;
	for(n = 0; n < dev->nblocks; n++) {
		if(*list++ & mask) {
			if( !pos)
				printf("\t");
			printf("%d, ", n);
			if(++pos >= 8) {
				printf("\n");
				pos = 0;
			}
		}
	}
	if(pos)
		printf("\n");
}

/*
 * exit on error;
 */
void
err_exit(bbcr_t *crp, int err)
{
	printf("%s: error: %s\n", cmd, strerror(err));
	bbcr_ctrl(crp, BBCR_CLR_RED | BBCR_SET_GRN);
	bbcr_close(crp);
	exit(1);
}

/*
 * create data pattern and write block;
 */
static int
write_blk(bbcr_t *crp, u_int baddr, u_int nblks, u_char *buf, u_char *spare, u_char *list)
{
	u_int addr;
	u_short *d, rval;
	u_char *s;
	int n, i, err, nerr;

	/*
	 * first word is block address;
	 * second ... last is increasing random number;
	 * leave spare area all 1s;
	 */
	addr = baddr;
	d = (u_short *)buf;
	s = spare;
	for(n = nblks; n--;) {
		*d++ = addr++;
		rval = random();
		for(i = 16*1024/2-1; i--; rval++)
			*d++ = rval;
		for(i = 16; i--; *s++ = 0xff);
	}
	/*
	 * write block;
	 * EIOs are returned for programming errors;
	 * try programming single blocks then;
	 */
	err = bbcr_write(crp, baddr, nblks, buf, spare);
	nerr = 0;
	if(err) {
		for(n = 0; n < nblks; n++) {
			err = bbcr_write(crp, baddr, 1, buf, spare);
			if(err == EIO) {
				list[baddr + n] |= 2;
				nerr++;
			} else if(err)
				err_exit(crp, err);
			baddr++;
			buf += 16*1024;
			spare += 16;
		}
	}
	return(nerr);
}

/*
 * verify single block;
 * returns 0 if block is ok, else # of miscompares;
 */
int
verify_blk(u_int baddr, u_char *buf, u_char *spare)
{
	u_short *d, rval;
	int nerr, i;

	d = (u_short *)buf;
	nerr = 0;
	if(*d++ != baddr)
		nerr++;
	rval = *d++;
	for(i = 16*1024/2-2; i--;) {
		if(*d++ != ++rval)
			nerr++;
	}
	return(nerr);
}

/*
 * test program;
 */
int
main(int argc, char **argv)
{
	int ntests, nt, ntotal;
	int err, n, pl, bpl, npl;
	int nbad, nwr, nwerr, bad, newbad;
	bbcr_dev_t *dev;
	char *arg;
	u_char *buf, *spare;
	char *blist, *dlist, *list;
	bbcr_t cr;
	int tps;
	time_t rtime[2];
	double bw;

	/*
	 * process cmd line;
	 */
	cmd = *argv++;
	arg = *argv++;
	tps = sysconf(_SC_CLK_TCK);
	ntests = ntotal = 0;

	/*
	 * open interface to get the device config;
	 * probe the flash and change timing;
	 */
	if(err = bbcr_open(&cr, arg)) {
		printf("%s: id %02x%02x%02x%02x, open: %s\n", cmd,
			cr.mfgid[0], cr.mfgid[1], cr.mfgid[2], cr.mfgid[3],
			strerror(err));
		return(2);
	}
	/*
	 * allocate data/spare buffer;
	 */
	dev = cr.dev;
	buf = malloc(16*1024 * dev->nplanes);
	spare = malloc(16 * dev->nplanes);
	blist = malloc(dev->nblocks);
	dlist = malloc(dev->nblocks);
	if( !buf || !spare || !blist || !dlist) {
		printf("%s: cannot alloc buffers\n", cmd);
		bbcr_close(&cr);
		return(1);
	}
	/*
	 * loop through a number of these tests;
	 * 0 means loop forever;
	 */
	list = blist;
	for(nt = 0; !ntests || (nt < ntests); nt++) {
		/*
		 * read entire flash;
		 * build bad block list;
		 * bad block if more than one bit 0 in byte 517;
		 */
		printf("%s: size %dMB, planes %d, fast timing %d, id %02x%02x%02x%02x\n",
			cmd, dev->nblocks / 64, dev->nplanes, dev->use_tmg,
			cr.mfgid[0], cr.mfgid[1], cr.mfgid[2], cr.mfgid[3]);
		printf("%s: reading all blocks\n", cmd);
		bbcr_ctrl(&cr, BBCR_SET_RED | BBCR_CLR_GRN);
		nbad = 0;
		bzero(list, dev->nblocks);
		rtime[0] = times(0);
		for(n = 0; n < dev->nblocks; n += dev->nplanes) {
			if(err = bbcr_read(&cr, n, dev->nplanes, buf, spare)) {
				if(err != ENXIO)
					err_exit(&cr, err);
			}
			for(pl = 0; pl < dev->nplanes; pl++) {
				if(marked_bad(spare[pl*16 + 5])) {
					list[n + pl] = 1;
					nbad++;
				}
			}
		}
		/*
		 * report # of blocks, cor and uncorrectable blocks;
		 * measure read bandwidth;
		 */
		rtime[1] = times(0);
		if(nbad)
			print_list(dev, list, 1);
		printf("%s: %d blocks, %d marked bad, %d sbe pages, %d dbe pages\n",
			cmd, dev->nblocks, nbad, cr.cor, cr.unc);
		bw = (double)(dev->nblocks * 16) * (double)tps / (double)(rtime[1] - rtime[0]);
		printf("%s: %d blocks, %.2fkB/sec\n",
			cmd, dev->nblocks, bw);
		/*
		 * write all blocks, that are not marked bad;
		 * first short is block address;
		 * second short is a random number;
		 * rest of buffer is incrementing;
		 */
		printf("%s: writing all good blocks\n", cmd);
		bbcr_ctrl(&cr, BBCR_SET_RED | BBCR_CLR_GRN);
		nwr = nwerr = 0;
		rtime[0] = times(0);
		for(n = 0; n < dev->nblocks; n += dev->nplanes) {
			for(bpl = pl = 0; pl < dev->nplanes; pl++) {
				bad = list[n + pl];
				if( !bad)
					continue;				/* coalesce blocks */
				if(npl = pl - bpl)
					nwerr += write_blk(&cr, n + bpl, npl, buf, spare, list);
				nwr += npl;
				bpl = pl + 1;
			}
			if(npl = pl - bpl)
				nwerr += write_blk(&cr, n + bpl, npl, buf, spare, list);
			nwr += npl;
		}
	
		/*
		 * report # of blocks;
		 * measure write bandwidth;
		 */
		rtime[1] = times(0);
		bw = (double)(nwr * 16) * (double)tps / (double)(rtime[1] - rtime[0]);
		printf("%s: %d blocks, %.2fkB/sec\n", cmd, nwr, bw);
		if(nwerr) {
			printf("%s: %d blocks had programming errors\n", cmd, nwerr);
			print_list(dev, list, 2);
		}
	
		/*
		 * verify all blocks;
		 * recount sbe and bad blocks;
		 */
		printf("%s: verifying all blocks\n", cmd);
		bbcr_ctrl(&cr, BBCR_SET_RED | BBCR_SET_GRN);
		cr.cor = cr.unc = 0;
		newbad = 0;
		for(n = 0; n < dev->nblocks; n += dev->nplanes) {
			if(err = bbcr_read(&cr, n, dev->nplanes, buf, spare)) {
				if(err != ENXIO)
					err_exit(&cr, err);
			}
			for(pl = 0; pl < dev->nplanes; pl++) {
				bad = list[n + pl];
				if(bad)
					continue;				/* do not check bad blocks */
				if(verify_blk(n + pl, buf + 16*1024*pl, spare + 16*pl)) {
					list[n + pl] |= 4;
					newbad++;
				}
			}
		}
		printf("%s: %d blocks, %d marked bad, %d sbe pages, %d dbe pages\n",
			cmd, dev->nblocks, nbad, cr.cor, cr.unc);
		if(newbad) {
			printf("%s: %d new blocks found bad\n", cmd, newbad);
			print_list(dev, list, 4);
		}
		/*
		 * turn LEDs green;
		 * close device, free buffers;
		 */
		bbcr_ctrl(&cr, BBCR_CLR_RED | BBCR_SET_GRN);

		/*
		 * report deviations;
		 */
		if(list == dlist) {
			for(err = n = 0; n < dev->nblocks; n++) {
				if(list[n] != blist[n]) {
					list[n] |= 8;
					err++;
				}
			}
			printf("%s: test %d has %d deviations, total %d\n\n", cmd, nt, err, ntotal);
			if(err) {
				ntotal++;
				print_list(dev, list, 8);
			}
		}
		list = dlist;
	}
	bbcr_close(&cr);
	return(0);
}