Commit 5d3c500b authored by Russell King's avatar Russell King Committed by Linus Torvalds

[PATCH] Update MTD concatenating driver

This patch updates the MTD concatenating driver from MTD CVS, which
fixes issues found with this driver which concatenates multiple MTD
devices into one MTD device.

From David Woodhouse, through CVS:

	revision 1.8
	date: 2003/06/30 11:01:26;  author: dwmw2;  state: Exp;  lines: +5 -5
	I will not commit stuff whilst pissed
	I will not commit stuff whilst pissed

	revision 1.7
	date: 2003/06/29 21:26:34;  author: dwmw2;  state: Exp;  lines: +9 -9
	Fix ecc/oob subdev comparisions

	revision 1.6
	date: 2003/06/25 12:37:50;  author: dwmw2;  state: Exp;  lines: +14 -6
	Don't pretend to have {read,write}_{oob,ecc} functions if subdevices don't

	revision 1.5
	date: 2003/06/25 12:21:16;  author: dwmw2;  state: Exp;  lines: +390 -397
	coding style cleanup
parent 791c0db5
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* This code is GPL * This code is GPL
* *
* $Id: mtdconcat.c,v 1.4 2003/03/07 17:44:59 rkaiser Exp $ * $Id: mtdconcat.c,v 1.8 2003/06/30 11:01:26 dwmw2 Exp $
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
*/ */
struct mtd_concat { struct mtd_concat {
struct mtd_info mtd; struct mtd_info mtd;
int num_subdev; int num_subdev;
struct mtd_info **subdev; struct mtd_info **subdev;
}; };
...@@ -37,21 +37,20 @@ struct mtd_concat { ...@@ -37,21 +37,20 @@ struct mtd_concat {
#define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \
((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))
/* /*
* Given a pointer to the MTD object in the mtd_concat structure, * Given a pointer to the MTD object in the mtd_concat structure,
* we can retrieve the pointer to that structure with this macro. * we can retrieve the pointer to that structure with this macro.
*/ */
#define CONCAT(x) ((struct mtd_concat *)(x)) #define CONCAT(x) ((struct mtd_concat *)(x))
/* /*
* MTD methods which look up the relevant subdevice, translate the * MTD methods which look up the relevant subdevice, translate the
* effective address and pass through to the subdevice. * effective address and pass through to the subdevice.
*/ */
static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, static int
size_t *retlen, u_char *buf) concat_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int err = -EINVAL; int err = -EINVAL;
...@@ -59,43 +58,43 @@ static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -59,43 +58,43 @@ static int concat_read (struct mtd_info *mtd, loff_t from, size_t len,
*retlen = 0; *retlen = 0;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize; size_t size, retsize;
if (from >= subdev->size) if (from >= subdev->size) {
{ /* Not destined for this subdev */ /* Not destined for this subdev */
size = 0; size = 0;
from -= subdev->size; from -= subdev->size;
continue;
} }
if (from + len > subdev->size)
/* First part goes into this subdev */
size = subdev->size - from;
else else
{ /* Entire transaction goes into this subdev */
if (from + len > subdev->size) size = len;
size = subdev->size - from; /* First part goes into this subdev */
else
size = len; /* Entire transaction goes into this subdev */
err = subdev->read(subdev, from, size, &retsize, buf); err = subdev->read(subdev, from, size, &retsize, buf);
if(err) if (err)
break; break;
*retlen += retsize; *retlen += retsize;
len -= size; len -= size;
if(len == 0) if (len == 0)
break; break;
err = -EINVAL; err = -EINVAL;
buf += size; buf += size;
from = 0; from = 0;
}
} }
return err; return err;
} }
static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, static int
size_t *retlen, const u_char *buf) concat_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t * retlen, const u_char * buf)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int err = -EINVAL; int err = -EINVAL;
...@@ -106,46 +105,44 @@ static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, ...@@ -106,46 +105,44 @@ static int concat_write (struct mtd_info *mtd, loff_t to, size_t len,
*retlen = 0; *retlen = 0;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize; size_t size, retsize;
if (to >= subdev->size) if (to >= subdev->size) {
{ size = 0;
size = 0;
to -= subdev->size; to -= subdev->size;
continue;
} }
if (to + len > subdev->size)
size = subdev->size - to;
else else
{ size = len;
if (to + len > subdev->size)
size = subdev->size - to;
else
size = len;
if (!(subdev->flags & MTD_WRITEABLE)) if (!(subdev->flags & MTD_WRITEABLE))
err = -EROFS; err = -EROFS;
else else
err = subdev->write(subdev, to, size, &retsize, buf); err = subdev->write(subdev, to, size, &retsize, buf);
if(err) if (err)
break; break;
*retlen += retsize; *retlen += retsize;
len -= size; len -= size;
if(len == 0) if (len == 0)
break; break;
err = -EINVAL; err = -EINVAL;
buf += size; buf += size;
to = 0; to = 0;
}
} }
return err; return err;
} }
static int concat_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, static int
size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel) concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf, u_char * eccbuf,
struct nand_oobinfo *oobsel)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int err = -EINVAL; int err = -EINVAL;
...@@ -153,53 +150,56 @@ static int concat_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -153,53 +150,56 @@ static int concat_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
*retlen = 0; *retlen = 0;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize; size_t size, retsize;
if (from >= subdev->size) if (from >= subdev->size) {
{ /* Not destined for this subdev */ /* Not destined for this subdev */
size = 0; size = 0;
from -= subdev->size; from -= subdev->size;
continue;
} }
if (from + len > subdev->size)
/* First part goes into this subdev */
size = subdev->size - from;
else else
{ /* Entire transaction goes into this subdev */
if (from + len > subdev->size) size = len;
size = subdev->size - from; /* First part goes into this subdev */
else
size = len; /* Entire transaction goes into this subdev */
if (subdev->read_ecc)
err = subdev->read_ecc(subdev, from, size, &retsize, buf, eccbuf, oobsel);
else
err = -EINVAL;
if(err)
break;
*retlen += retsize;
len -= size;
if(len == 0)
break;
if (subdev->read_ecc)
err = subdev->read_ecc(subdev, from, size,
&retsize, buf, eccbuf, oobsel);
else
err = -EINVAL; err = -EINVAL;
buf += size;
if (eccbuf) if (err)
{ break;
eccbuf += subdev->oobsize;
/* in nand.c at least, eccbufs are tagged with 2 (int)eccstatus', *retlen += retsize;
we must account for these */ len -= size;
eccbuf += 2 * (sizeof(int)); if (len == 0)
} break;
from = 0;
err = -EINVAL;
buf += size;
if (eccbuf) {
eccbuf += subdev->oobsize;
/* in nand.c at least, eccbufs are
tagged with 2 (int)eccstatus'; we
must account for these */
eccbuf += 2 * (sizeof (int));
} }
from = 0;
} }
return err; return err;
} }
static int concat_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, static int
size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel) concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
size_t * retlen, const u_char * buf, u_char * eccbuf,
struct nand_oobinfo *oobsel)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int err = -EINVAL; int err = -EINVAL;
...@@ -210,50 +210,48 @@ static int concat_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, ...@@ -210,50 +210,48 @@ static int concat_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
*retlen = 0; *retlen = 0;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize; size_t size, retsize;
if (to >= subdev->size) if (to >= subdev->size) {
{ size = 0;
size = 0;
to -= subdev->size; to -= subdev->size;
continue;
} }
if (to + len > subdev->size)
size = subdev->size - to;
else else
{ size = len;
if (to + len > subdev->size)
size = subdev->size - to;
else
size = len;
if (!(subdev->flags & MTD_WRITEABLE))
err = -EROFS;
else if (subdev->write_ecc)
err = subdev->write_ecc(subdev, to, size, &retsize, buf, eccbuf, oobsel);
else
err = -EINVAL;
if(err)
break;
*retlen += retsize;
len -= size;
if(len == 0)
break;
if (!(subdev->flags & MTD_WRITEABLE))
err = -EROFS;
else if (subdev->write_ecc)
err = subdev->write_ecc(subdev, to, size,
&retsize, buf, eccbuf, oobsel);
else
err = -EINVAL; err = -EINVAL;
buf += size;
if (eccbuf) if (err)
eccbuf += subdev->oobsize; break;
to = 0;
} *retlen += retsize;
len -= size;
if (len == 0)
break;
err = -EINVAL;
buf += size;
if (eccbuf)
eccbuf += subdev->oobsize;
to = 0;
} }
return err; return err;
} }
static int concat_read_oob (struct mtd_info *mtd, loff_t from, size_t len, static int
size_t *retlen, u_char *buf) concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int err = -EINVAL; int err = -EINVAL;
...@@ -261,46 +259,47 @@ static int concat_read_oob (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -261,46 +259,47 @@ static int concat_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
*retlen = 0; *retlen = 0;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize; size_t size, retsize;
if (from >= subdev->size) if (from >= subdev->size) {
{ /* Not destined for this subdev */ /* Not destined for this subdev */
size = 0; size = 0;
from -= subdev->size; from -= subdev->size;
continue;
} }
if (from + len > subdev->size)
/* First part goes into this subdev */
size = subdev->size - from;
else else
{ /* Entire transaction goes into this subdev */
if (from + len > subdev->size) size = len;
size = subdev->size - from; /* First part goes into this subdev */
else
size = len; /* Entire transaction goes into this subdev */
if (subdev->read_oob)
err = subdev->read_oob(subdev, from, size, &retsize, buf);
else
err = -EINVAL;
if(err)
break;
*retlen += retsize;
len -= size;
if(len == 0)
break;
if (subdev->read_oob)
err = subdev->read_oob(subdev, from, size,
&retsize, buf);
else
err = -EINVAL; err = -EINVAL;
buf += size;
from = 0; if (err)
} break;
*retlen += retsize;
len -= size;
if (len == 0)
break;
err = -EINVAL;
buf += size;
from = 0;
} }
return err; return err;
} }
static int concat_write_oob (struct mtd_info *mtd, loff_t to, size_t len, static int
size_t *retlen, const u_char *buf) concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
size_t * retlen, const u_char * buf)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int err = -EINVAL; int err = -EINVAL;
...@@ -311,50 +310,46 @@ static int concat_write_oob (struct mtd_info *mtd, loff_t to, size_t len, ...@@ -311,50 +310,46 @@ static int concat_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
*retlen = 0; *retlen = 0;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize; size_t size, retsize;
if (to >= subdev->size) if (to >= subdev->size) {
{ size = 0;
size = 0;
to -= subdev->size; to -= subdev->size;
continue;
} }
if (to + len > subdev->size)
size = subdev->size - to;
else else
{ size = len;
if (to + len > subdev->size)
size = subdev->size - to;
else
size = len;
if (!(subdev->flags & MTD_WRITEABLE))
err = -EROFS;
else if (subdev->write_oob)
err = subdev->write_oob(subdev, to, size, &retsize, buf);
else
err = -EINVAL;
if(err)
break;
*retlen += retsize;
len -= size;
if(len == 0)
break;
if (!(subdev->flags & MTD_WRITEABLE))
err = -EROFS;
else if (subdev->write_oob)
err = subdev->write_oob(subdev, to, size, &retsize,
buf);
else
err = -EINVAL; err = -EINVAL;
buf += size;
to = 0; if (err)
} break;
*retlen += retsize;
len -= size;
if (len == 0)
break;
err = -EINVAL;
buf += size;
to = 0;
} }
return err; return err;
} }
static void concat_erase_callback(struct erase_info *instr)
static void concat_erase_callback (struct erase_info *instr)
{ {
wake_up((wait_queue_head_t *)instr->priv); wake_up((wait_queue_head_t *) instr->priv);
} }
static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
...@@ -370,18 +365,18 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) ...@@ -370,18 +365,18 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
erase->mtd = mtd; erase->mtd = mtd;
erase->callback = concat_erase_callback; erase->callback = concat_erase_callback;
erase->priv = (unsigned long)&waitq; erase->priv = (unsigned long) &waitq;
/* /*
* FIXME: Allow INTERRUPTIBLE. Which means * FIXME: Allow INTERRUPTIBLE. Which means
* not having the wait_queue head on the stack. * not having the wait_queue head on the stack.
*/ */
err = mtd->erase(mtd, erase); err = mtd->erase(mtd, erase);
if (!err) if (!err) {
{
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&waitq, &wait); add_wait_queue(&waitq, &wait);
if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) if (erase->state != MTD_ERASE_DONE
&& erase->state != MTD_ERASE_FAILED)
schedule(); schedule();
remove_wait_queue(&waitq, &wait); remove_wait_queue(&waitq, &wait);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
...@@ -391,7 +386,7 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) ...@@ -391,7 +386,7 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
return err; return err;
} }
static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
struct mtd_info *subdev; struct mtd_info *subdev;
...@@ -402,10 +397,10 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) ...@@ -402,10 +397,10 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
if (!(mtd->flags & MTD_WRITEABLE)) if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS; return -EROFS;
if(instr->addr > concat->mtd.size) if (instr->addr > concat->mtd.size)
return -EINVAL; return -EINVAL;
if(instr->len + instr->addr > concat->mtd.size) if (instr->len + instr->addr > concat->mtd.size)
return -EINVAL; return -EINVAL;
/* /*
...@@ -414,23 +409,22 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) ...@@ -414,23 +409,22 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
* region info rather than looking at each particular sub-device * region info rather than looking at each particular sub-device
* in turn. * in turn.
*/ */
if (!concat->mtd.numeraseregions) if (!concat->mtd.numeraseregions) {
{ /* the easy case: device has uniform erase block size */ /* the easy case: device has uniform erase block size */
if(instr->addr & (concat->mtd.erasesize - 1)) if (instr->addr & (concat->mtd.erasesize - 1))
return -EINVAL; return -EINVAL;
if(instr->len & (concat->mtd.erasesize - 1)) if (instr->len & (concat->mtd.erasesize - 1))
return -EINVAL; return -EINVAL;
} } else {
else /* device has variable erase size */
{ /* device has variable erase size */ struct mtd_erase_region_info *erase_regions =
struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; concat->mtd.eraseregions;
/* /*
* Find the erase region where the to-be-erased area begins: * Find the erase region where the to-be-erased area begins:
*/ */
for(i = 0; i < concat->mtd.numeraseregions && for (i = 0; i < concat->mtd.numeraseregions &&
instr->addr >= erase_regions[i].offset; i++) instr->addr >= erase_regions[i].offset; i++) ;
;
--i; --i;
/* /*
...@@ -438,25 +432,26 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) ...@@ -438,25 +432,26 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
* to-be-erased area begins. Verify that the starting * to-be-erased area begins. Verify that the starting
* offset is aligned to this region's erase size: * offset is aligned to this region's erase size:
*/ */
if (instr->addr & (erase_regions[i].erasesize-1)) if (instr->addr & (erase_regions[i].erasesize - 1))
return -EINVAL; return -EINVAL;
/* /*
* now find the erase region where the to-be-erased area ends: * now find the erase region where the to-be-erased area ends:
*/ */
for(; i < concat->mtd.numeraseregions && for (; i < concat->mtd.numeraseregions &&
(instr->addr + instr->len) >= erase_regions[i].offset ; ++i) (instr->addr + instr->len) >= erase_regions[i].offset;
; ++i) ;
--i; --i;
/* /*
* check if the ending offset is aligned to this region's erase size * check if the ending offset is aligned to this region's erase size
*/ */
if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1)) if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
1))
return -EINVAL; return -EINVAL;
} }
/* make a local copy of instr to avoid modifying the caller's struct */ /* make a local copy of instr to avoid modifying the caller's struct */
erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL); erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
if (!erase) if (!erase)
return -ENOMEM; return -ENOMEM;
...@@ -468,39 +463,40 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) ...@@ -468,39 +463,40 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
* find the subdevice where the to-be-erased area begins, adjust * find the subdevice where the to-be-erased area begins, adjust
* starting offset to be relative to the subdevice start * starting offset to be relative to the subdevice start
*/ */
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
subdev = concat->subdev[i]; subdev = concat->subdev[i];
if(subdev->size <= erase->addr) if (subdev->size <= erase->addr)
erase->addr -= subdev->size; erase->addr -= subdev->size;
else else
break; break;
} }
if(i >= concat->num_subdev) /* must never happen since size */
BUG(); /* limit has been verified above */ /* must never happen since size limit has been verified above */
if (i >= concat->num_subdev)
BUG();
/* now do the erase: */ /* now do the erase: */
err = 0; err = 0;
for(;length > 0; i++) /* loop for all subevices affected by this request */ for (; length > 0; i++) {
{ /* loop for all subdevices affected by this request */
subdev = concat->subdev[i]; /* get current subdevice */ subdev = concat->subdev[i]; /* get current subdevice */
/* limit length to subdevice's size: */ /* limit length to subdevice's size: */
if(erase->addr + length > subdev->size) if (erase->addr + length > subdev->size)
erase->len = subdev->size - erase->addr; erase->len = subdev->size - erase->addr;
else else
erase->len = length; erase->len = length;
if (!(subdev->flags & MTD_WRITEABLE)) if (!(subdev->flags & MTD_WRITEABLE)) {
{
err = -EROFS; err = -EROFS;
break; break;
} }
length -= erase->len; length -= erase->len;
if ((err = concat_dev_erase(subdev, erase))) if ((err = concat_dev_erase(subdev, erase))) {
{ /* sanity check: should never happen since
if(err == -EINVAL) /* sanity check: must never happen since */ * block alignment has been checked above */
BUG(); /* block alignment has been checked above */ if (err == -EINVAL)
BUG();
break; break;
} }
/* /*
...@@ -523,85 +519,79 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) ...@@ -523,85 +519,79 @@ static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
return 0; return 0;
} }
static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len) static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL; int i, err = -EINVAL;
if ((len + ofs) > mtd->size) if ((len + ofs) > mtd->size)
return -EINVAL; return -EINVAL;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
size_t size; size_t size;
if (ofs >= subdev->size) if (ofs >= subdev->size) {
{ size = 0;
size = 0;
ofs -= subdev->size; ofs -= subdev->size;
continue;
} }
if (ofs + len > subdev->size)
size = subdev->size - ofs;
else else
{ size = len;
if (ofs + len > subdev->size)
size = subdev->size - ofs;
else
size = len;
err = subdev->lock(subdev, ofs, size); err = subdev->lock(subdev, ofs, size);
if(err) if (err)
break; break;
len -= size; len -= size;
if(len == 0) if (len == 0)
break; break;
err = -EINVAL; err = -EINVAL;
ofs = 0; ofs = 0;
}
} }
return err; return err;
} }
static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int i, err = 0; int i, err = 0;
if ((len + ofs) > mtd->size) if ((len + ofs) > mtd->size)
return -EINVAL; return -EINVAL;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
size_t size; size_t size;
if (ofs >= subdev->size) if (ofs >= subdev->size) {
{ size = 0;
size = 0;
ofs -= subdev->size; ofs -= subdev->size;
continue;
} }
if (ofs + len > subdev->size)
size = subdev->size - ofs;
else else
{ size = len;
if (ofs + len > subdev->size)
size = subdev->size - ofs;
else
size = len;
err = subdev->unlock(subdev, ofs, size); err = subdev->unlock(subdev, ofs, size);
if(err) if (err)
break; break;
len -= size; len -= size;
if(len == 0) if (len == 0)
break; break;
err = -EINVAL; err = -EINVAL;
ofs = 0; ofs = 0;
}
} }
return err; return err;
} }
...@@ -610,8 +600,7 @@ static void concat_sync(struct mtd_info *mtd) ...@@ -610,8 +600,7 @@ static void concat_sync(struct mtd_info *mtd)
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int i; int i;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
subdev->sync(subdev); subdev->sync(subdev);
} }
...@@ -622,10 +611,9 @@ static int concat_suspend(struct mtd_info *mtd) ...@@ -622,10 +611,9 @@ static int concat_suspend(struct mtd_info *mtd)
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int i, rc = 0; int i, rc = 0;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
if((rc = subdev->suspend(subdev)) < 0) if ((rc = subdev->suspend(subdev)) < 0)
return rc; return rc;
} }
return rc; return rc;
...@@ -636,8 +624,7 @@ static void concat_resume(struct mtd_info *mtd) ...@@ -636,8 +624,7 @@ static void concat_resume(struct mtd_info *mtd)
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int i; int i;
for(i = 0; i < concat->num_subdev; i++) for (i = 0; i < concat->num_subdev; i++) {
{
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
subdev->resume(subdev); subdev->resume(subdev);
} }
...@@ -649,11 +636,10 @@ static void concat_resume(struct mtd_info *mtd) ...@@ -649,11 +636,10 @@ static void concat_resume(struct mtd_info *mtd)
* stored to *new_dev upon success. This function does _not_ * stored to *new_dev upon success. This function does _not_
* register any devices: this is the caller's responsibility. * register any devices: this is the caller's responsibility.
*/ */
struct mtd_info *mtd_concat_create( struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */
struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */
int num_devs, /* number of subdevices */ char *name)
char *name) /* name for the new device */ { /* name for the new device */
{
int i; int i;
size_t size; size_t size;
struct mtd_concat *concat; struct mtd_concat *concat;
...@@ -661,94 +647,103 @@ struct mtd_info *mtd_concat_create( ...@@ -661,94 +647,103 @@ struct mtd_info *mtd_concat_create(
int num_erase_region; int num_erase_region;
printk(KERN_NOTICE "Concatenating MTD devices:\n"); printk(KERN_NOTICE "Concatenating MTD devices:\n");
for(i = 0; i < num_devs; i++) for (i = 0; i < num_devs; i++)
printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
printk(KERN_NOTICE "into device \"%s\"\n", name); printk(KERN_NOTICE "into device \"%s\"\n", name);
/* allocate the device structure */ /* allocate the device structure */
size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
concat = kmalloc (size, GFP_KERNEL); concat = kmalloc(size, GFP_KERNEL);
if(!concat) if (!concat) {
{ printk
printk ("memory allocation error while creating concatenated device \"%s\"\n", ("memory allocation error while creating concatenated device \"%s\"\n",
name); name);
return NULL; return NULL;
} }
memset(concat, 0, size); memset(concat, 0, size);
concat->subdev = (struct mtd_info **)(concat + 1); concat->subdev = (struct mtd_info **) (concat + 1);
/* /*
* Set up the new "super" device's MTD object structure, check for * Set up the new "super" device's MTD object structure, check for
* incompatibilites between the subdevices. * incompatibilites between the subdevices.
*/ */
concat->mtd.type = subdev[0]->type; concat->mtd.type = subdev[0]->type;
concat->mtd.flags = subdev[0]->flags; concat->mtd.flags = subdev[0]->flags;
concat->mtd.size = subdev[0]->size; concat->mtd.size = subdev[0]->size;
concat->mtd.erasesize = subdev[0]->erasesize; concat->mtd.erasesize = subdev[0]->erasesize;
concat->mtd.oobblock = subdev[0]->oobblock; concat->mtd.oobblock = subdev[0]->oobblock;
concat->mtd.oobsize = subdev[0]->oobsize; concat->mtd.oobsize = subdev[0]->oobsize;
concat->mtd.ecctype = subdev[0]->ecctype; concat->mtd.ecctype = subdev[0]->ecctype;
concat->mtd.eccsize = subdev[0]->eccsize; concat->mtd.eccsize = subdev[0]->eccsize;
if (subdev[0]->read_ecc)
concat->subdev[0] = subdev[0]; concat->mtd.read_ecc = concat_read_ecc;
if (subdev[0]->write_ecc)
for(i = 1; i < num_devs; i++) concat->mtd.write_ecc = concat_write_ecc;
{ if (subdev[0]->read_oob)
if(concat->mtd.type != subdev[i]->type) concat->mtd.read_oob = concat_read_oob;
{ if (subdev[0]->write_oob)
concat->mtd.write_oob = concat_write_oob;
concat->subdev[0] = subdev[0];
for (i = 1; i < num_devs; i++) {
if (concat->mtd.type != subdev[i]->type) {
kfree(concat); kfree(concat);
printk ("Incompatible device type on \"%s\"\n", subdev[i]->name); printk("Incompatible device type on \"%s\"\n",
subdev[i]->name);
return NULL; return NULL;
} }
if(concat->mtd.flags != subdev[i]->flags) if (concat->mtd.flags != subdev[i]->flags) {
{ /* /*
* Expect all flags except MTD_WRITEABLE to be equal on * Expect all flags except MTD_WRITEABLE to be
* all subdevices. * equal on all subdevices.
*/ */
if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE) if ((concat->mtd.flags ^ subdev[i]->
{ flags) & ~MTD_WRITEABLE) {
kfree(concat); kfree(concat);
printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name); printk("Incompatible device flags on \"%s\"\n",
subdev[i]->name);
return NULL; return NULL;
} } else
else /* if writeable attribute differs, make super device writeable */ /* if writeable attribute differs,
concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE; make super device writeable */
concat->mtd.flags |=
subdev[i]->flags & MTD_WRITEABLE;
} }
concat->mtd.size += subdev[i]->size; concat->mtd.size += subdev[i]->size;
if(concat->mtd.oobblock != subdev[i]->oobblock || if (concat->mtd.oobblock != subdev[i]->oobblock ||
concat->mtd.oobsize != subdev[i]->oobsize || concat->mtd.oobsize != subdev[i]->oobsize ||
concat->mtd.ecctype != subdev[i]->ecctype || concat->mtd.ecctype != subdev[i]->ecctype ||
concat->mtd.eccsize != subdev[i]->eccsize) concat->mtd.eccsize != subdev[i]->eccsize ||
{ !concat->mtd.read_ecc != !subdev[i]->read_ecc ||
!concat->mtd.write_ecc != !subdev[i]->write_ecc ||
!concat->mtd.read_oob != !subdev[i]->read_oob ||
!concat->mtd.write_oob != !subdev[i]->write_oob) {
kfree(concat); kfree(concat);
printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); printk("Incompatible OOB or ECC data on \"%s\"\n",
subdev[i]->name);
return NULL; return NULL;
} }
concat->subdev[i] = subdev[i]; concat->subdev[i] = subdev[i];
} }
concat->num_subdev = num_devs; concat->num_subdev = num_devs;
concat->mtd.name = name; concat->mtd.name = name;
/* /*
* NOTE: for now, we do not provide any readv()/writev() methods * NOTE: for now, we do not provide any readv()/writev() methods
* because they are messy to implement and they are not * because they are messy to implement and they are not
* used to a great extent anyway. * used to a great extent anyway.
*/ */
concat->mtd.erase = concat_erase; concat->mtd.erase = concat_erase;
concat->mtd.read = concat_read; concat->mtd.read = concat_read;
concat->mtd.write = concat_write; concat->mtd.write = concat_write;
concat->mtd.read_ecc = concat_read_ecc; concat->mtd.sync = concat_sync;
concat->mtd.write_ecc = concat_write_ecc; concat->mtd.lock = concat_lock;
concat->mtd.read_oob = concat_read_oob; concat->mtd.unlock = concat_unlock;
concat->mtd.write_oob = concat_write_oob; concat->mtd.suspend = concat_suspend;
concat->mtd.sync = concat_sync; concat->mtd.resume = concat_resume;
concat->mtd.lock = concat_lock;
concat->mtd.unlock = concat_unlock;
concat->mtd.suspend = concat_suspend;
concat->mtd.resume = concat_resume;
/* /*
* Combine the erase block size info of the subdevices: * Combine the erase block size info of the subdevices:
...@@ -758,44 +753,44 @@ struct mtd_info *mtd_concat_create( ...@@ -758,44 +753,44 @@ struct mtd_info *mtd_concat_create(
*/ */
max_erasesize = curr_erasesize = subdev[0]->erasesize; max_erasesize = curr_erasesize = subdev[0]->erasesize;
num_erase_region = 1; num_erase_region = 1;
for(i = 0; i < num_devs; i++) for (i = 0; i < num_devs; i++) {
{ if (subdev[i]->numeraseregions == 0) {
if(subdev[i]->numeraseregions == 0) /* current subdevice has uniform erase size */
{ /* current subdevice has uniform erase size */ if (subdev[i]->erasesize != curr_erasesize) {
if(subdev[i]->erasesize != curr_erasesize) /* if it differs from the last subdevice's erase size, count it */
{ /* if it differs from the last subdevice's erase size, count it */
++num_erase_region; ++num_erase_region;
curr_erasesize = subdev[i]->erasesize; curr_erasesize = subdev[i]->erasesize;
if(curr_erasesize > max_erasesize) if (curr_erasesize > max_erasesize)
max_erasesize = curr_erasesize; max_erasesize = curr_erasesize;
} }
} } else {
else /* current subdevice has variable erase size */
{ /* current subdevice has variable erase size */
int j; int j;
for(j = 0; j < subdev[i]->numeraseregions; j++) for (j = 0; j < subdev[i]->numeraseregions; j++) {
{ /* walk the list of erase regions, count any changes */
if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) /* walk the list of erase regions, count any changes */
{ if (subdev[i]->eraseregions[j].erasesize !=
curr_erasesize) {
++num_erase_region; ++num_erase_region;
curr_erasesize = subdev[i]->eraseregions[j].erasesize; curr_erasesize =
if(curr_erasesize > max_erasesize) subdev[i]->eraseregions[j].
erasesize;
if (curr_erasesize > max_erasesize)
max_erasesize = curr_erasesize; max_erasesize = curr_erasesize;
} }
} }
} }
} }
if(num_erase_region == 1) if (num_erase_region == 1) {
{ /* /*
* All subdevices have the same uniform erase size. * All subdevices have the same uniform erase size.
* This is easy: * This is easy:
*/ */
concat->mtd.erasesize = curr_erasesize; concat->mtd.erasesize = curr_erasesize;
concat->mtd.numeraseregions = 0; concat->mtd.numeraseregions = 0;
} } else {
else /*
{ /*
* erase block size varies across the subdevices: allocate * erase block size varies across the subdevices: allocate
* space to store the data describing the variable erase regions * space to store the data describing the variable erase regions
*/ */
...@@ -804,13 +799,14 @@ struct mtd_info *mtd_concat_create( ...@@ -804,13 +799,14 @@ struct mtd_info *mtd_concat_create(
concat->mtd.erasesize = max_erasesize; concat->mtd.erasesize = max_erasesize;
concat->mtd.numeraseregions = num_erase_region; concat->mtd.numeraseregions = num_erase_region;
concat->mtd.eraseregions = erase_region_p = kmalloc ( concat->mtd.eraseregions = erase_region_p =
num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL); kmalloc(num_erase_region *
if(!erase_region_p) sizeof (struct mtd_erase_region_info), GFP_KERNEL);
{ if (!erase_region_p) {
kfree(concat); kfree(concat);
printk ("memory allocation error while creating erase region list" printk
" for device \"%s\"\n", name); ("memory allocation error while creating erase region list"
" for device \"%s\"\n", name);
return NULL; return NULL;
} }
...@@ -820,46 +816,53 @@ struct mtd_info *mtd_concat_create( ...@@ -820,46 +816,53 @@ struct mtd_info *mtd_concat_create(
*/ */
curr_erasesize = subdev[0]->erasesize; curr_erasesize = subdev[0]->erasesize;
begin = position = 0; begin = position = 0;
for(i = 0; i < num_devs; i++) for (i = 0; i < num_devs; i++) {
{ if (subdev[i]->numeraseregions == 0) {
if(subdev[i]->numeraseregions == 0) /* current subdevice has uniform erase size */
{ /* current subdevice has uniform erase size */ if (subdev[i]->erasesize != curr_erasesize) {
if(subdev[i]->erasesize != curr_erasesize) /*
{ /*
* fill in an mtd_erase_region_info structure for the area * fill in an mtd_erase_region_info structure for the area
* we have walked so far: * we have walked so far:
*/ */
erase_region_p->offset = begin; erase_region_p->offset = begin;
erase_region_p->erasesize = curr_erasesize; erase_region_p->erasesize =
erase_region_p->numblocks = (position - begin) / curr_erasesize; curr_erasesize;
erase_region_p->numblocks =
(position - begin) / curr_erasesize;
begin = position; begin = position;
curr_erasesize = subdev[i]->erasesize; curr_erasesize = subdev[i]->erasesize;
++erase_region_p; ++erase_region_p;
} }
position += subdev[i]->size; position += subdev[i]->size;
} } else {
else /* current subdevice has variable erase size */
{ /* current subdevice has variable erase size */
int j; int j;
for(j = 0; j < subdev[i]->numeraseregions; j++) for (j = 0; j < subdev[i]->numeraseregions; j++) {
{ /* walk the list of erase regions, count any changes */ /* walk the list of erase regions, count any changes */
if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) if (subdev[i]->eraseregions[j].
{ erasesize != curr_erasesize) {
erase_region_p->offset = begin; erase_region_p->offset = begin;
erase_region_p->erasesize = curr_erasesize; erase_region_p->erasesize =
erase_region_p->numblocks = (position - begin) / curr_erasesize; curr_erasesize;
erase_region_p->numblocks =
(position -
begin) / curr_erasesize;
begin = position; begin = position;
curr_erasesize = subdev[i]->eraseregions[j].erasesize; curr_erasesize =
subdev[i]->eraseregions[j].
erasesize;
++erase_region_p; ++erase_region_p;
} }
position += subdev[i]->eraseregions[j].numblocks * curr_erasesize; position +=
subdev[i]->eraseregions[j].
numblocks * curr_erasesize;
} }
} }
} }
/* Now write the final entry */ /* Now write the final entry */
erase_region_p->offset = begin; erase_region_p->offset = begin;
erase_region_p->erasesize = curr_erasesize; erase_region_p->erasesize = curr_erasesize;
erase_region_p->numblocks = (position - begin) / curr_erasesize; erase_region_p->numblocks = (position - begin) / curr_erasesize;
} }
...@@ -874,16 +877,14 @@ struct mtd_info *mtd_concat_create( ...@@ -874,16 +877,14 @@ struct mtd_info *mtd_concat_create(
void mtd_concat_destroy(struct mtd_info *mtd) void mtd_concat_destroy(struct mtd_info *mtd)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
if(concat->mtd.numeraseregions) if (concat->mtd.numeraseregions)
kfree(concat->mtd.eraseregions); kfree(concat->mtd.eraseregions);
kfree(concat); kfree(concat);
} }
EXPORT_SYMBOL(mtd_concat_create); EXPORT_SYMBOL(mtd_concat_create);
EXPORT_SYMBOL(mtd_concat_destroy); EXPORT_SYMBOL(mtd_concat_destroy);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>"); MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment