Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e8d32937 authored by Alexander Belyakov's avatar Alexander Belyakov Committed by David Woodhouse
Browse files

MTD: mtdconcat NAND/Sibley support (rev.2)



There is a second revision of "mtdconcat NAND/Sibley" patch. I hope
the patch will not get damaged as I'm posting it from gmail account,
thanks to Jorn.

The patch adds previously missing concat_writev(),
concat_writev_ecc(), concat_block_isbad(), concat_block_markbad()
functions to make concatenation layer compatible with Sibley and NAND
chips.

Patch has been cleared from whitespaces, fixed some lines of code as
requested. Also I have added code for alignment check that should
support Jorn's "writesize" patch.

Signed-off-by: default avatarAlexander Belyakov <alexander.belyakov@intel.com>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent ceb31db1
Loading
Loading
Loading
Loading
+164 −5
Original line number Diff line number Diff line
@@ -250,6 +250,106 @@ concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
	return err;
}

static int
concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
		unsigned long count, loff_t to, size_t * retlen,
		u_char *eccbuf, struct nand_oobinfo *oobsel)
{
	struct mtd_concat *concat = CONCAT(mtd);
	struct kvec *vecs_copy;
	unsigned long entry_low, entry_high;
	size_t total_len = 0;
	int i;
	int err = -EINVAL;

	if (!(mtd->flags & MTD_WRITEABLE))
		return -EROFS;

	*retlen = 0;

	/* Calculate total length of data */
	for (i = 0; i < count; i++)
		total_len += vecs[i].iov_len;

	/* Do not allow write past end of device */
	if ((to + total_len) > mtd->size)
		return -EINVAL;

	/* Check alignment */
	if (mtd->writesize > 1)
		if ((to % mtd->writesize) || (total_len % mtd->writesize))
			return -EINVAL;

	/* make a copy of vecs */
	vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL);
	if (!vecs_copy)
		return -ENOMEM;
	memcpy(vecs_copy, vecs, sizeof(struct kvec) * count);

	entry_low = 0;
	for (i = 0; i < concat->num_subdev; i++) {
		struct mtd_info *subdev = concat->subdev[i];
		size_t size, wsize, retsize, old_iov_len;

		if (to >= subdev->size) {
			to -= subdev->size;
			continue;
		}

		size = min(total_len, (size_t)(subdev->size - to));
		wsize = size; /* store for future use */

		entry_high = entry_low;
		while (entry_high < count) {
			if (size <= vecs_copy[entry_high].iov_len)
				break;
			size -= vecs_copy[entry_high++].iov_len;
		}

		old_iov_len = vecs_copy[entry_high].iov_len;
		vecs_copy[entry_high].iov_len = size;

		if (!(subdev->flags & MTD_WRITEABLE))
			err = -EROFS;
		else if (eccbuf)
			err = subdev->writev_ecc(subdev, &vecs_copy[entry_low],
				entry_high - entry_low + 1, to, &retsize,
				eccbuf, oobsel);
		else
			err = subdev->writev(subdev, &vecs_copy[entry_low],
				entry_high - entry_low + 1, to, &retsize);

		vecs_copy[entry_high].iov_len = old_iov_len - size;
		vecs_copy[entry_high].iov_base += size;

		entry_low = entry_high;

		if (err)
			break;

		*retlen += retsize;
		total_len -= wsize;
		if (concat->mtd.type == MTD_NANDFLASH && eccbuf)
			eccbuf += mtd->oobavail * (wsize / mtd->oobblock);

		if (total_len == 0)
			break;

		err = -EINVAL;
		to = 0;
	}

	kfree(vecs_copy);
	return err;
}

static int
concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
		unsigned long count, loff_t to, size_t * retlen)
{
	return concat_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL);
}

static int
concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
		size_t * retlen, u_char * buf)
@@ -636,6 +736,58 @@ static void concat_resume(struct mtd_info *mtd)
	}
}

static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
	struct mtd_concat *concat = CONCAT(mtd);
	int i, res = 0;

	if (!concat->subdev[0]->block_isbad)
		return res;

	if (ofs > mtd->size)
		return -EINVAL;

	for (i = 0; i < concat->num_subdev; i++) {
		struct mtd_info *subdev = concat->subdev[i];

		if (ofs >= subdev->size) {
			ofs -= subdev->size;
			continue;
		}

		res = subdev->block_isbad(subdev, ofs);
		break;
	}

	return res;
}

static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
	struct mtd_concat *concat = CONCAT(mtd);
	int i, err = -EINVAL;

	if (!concat->subdev[0]->block_markbad)
		return 0;

	if (ofs > mtd->size)
		return -EINVAL;

	for (i = 0; i < concat->num_subdev; i++) {
		struct mtd_info *subdev = concat->subdev[i];

		if (ofs >= subdev->size) {
			ofs -= subdev->size;
			continue;
		}

		err = subdev->block_markbad(subdev, ofs);
		break;
	}

	return err;
}

/*
 * This function constructs a virtual MTD device by concatenating
 * num_devs MTD devices. A pointer to the new device object is
@@ -685,10 +837,18 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
		concat->mtd.read_ecc = concat_read_ecc;
	if (subdev[0]->write_ecc)
		concat->mtd.write_ecc = concat_write_ecc;
	if (subdev[0]->writev)
		concat->mtd.writev = concat_writev;
	if (subdev[0]->writev_ecc)
		concat->mtd.writev_ecc = concat_writev_ecc;
	if (subdev[0]->read_oob)
		concat->mtd.read_oob = concat_read_oob;
	if (subdev[0]->write_oob)
		concat->mtd.write_oob = concat_write_oob;
	if (subdev[0]->block_isbad)
		concat->mtd.block_isbad = concat_block_isbad;
	if (subdev[0]->block_markbad)
		concat->mtd.block_markbad = concat_block_markbad;

	concat->subdev[0] = subdev[0];

@@ -734,14 +894,13 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c

	}

	if(concat->mtd.type == MTD_NANDFLASH)
		memcpy(&concat->mtd.oobinfo, &subdev[0]->oobinfo,
			sizeof(struct nand_oobinfo));

	concat->num_subdev = num_devs;
	concat->mtd.name = name;

	/*
	 * NOTE: for now, we do not provide any readv()/writev() methods
	 *       because they are messy to implement and they are not
	 *       used to a great extent anyway.
	 */
	concat->mtd.erase = concat_erase;
	concat->mtd.read = concat_read;
	concat->mtd.write = concat_write;