diff options
Diffstat (limited to 'cmd/ubi.c')
-rw-r--r-- | cmd/ubi.c | 100 |
1 files changed, 87 insertions, 13 deletions
diff --git a/cmd/ubi.c b/cmd/ubi.c index 0a6a80bdd10..0e62e449327 100644 --- a/cmd/ubi.c +++ b/cmd/ubi.c @@ -11,7 +11,6 @@ * published by the Free Software Foundation. */ -#include <common.h> #include <command.h> #include <env.h> #include <exports.h> @@ -249,7 +248,7 @@ static int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id, static struct ubi_volume *ubi_find_volume(char *volume) { - struct ubi_volume *vol = NULL; + struct ubi_volume *vol; int i; for (i = 0; i < ubi->vtbl_slots; i++) { @@ -356,13 +355,18 @@ static int ubi_rename_vol(char *oldname, char *newname) static int ubi_volume_continue_write(char *volume, void *buf, size_t size) { - int err = 1; + int err; struct ubi_volume *vol; vol = ubi_find_volume(volume); if (vol == NULL) return ENODEV; + if (!vol->updating) { + printf("UBI volume update was not initiated\n"); + return EINVAL; + } + err = ubi_more_update_data(ubi, vol, buf, size); if (err < 0) { printf("Couldnt or partially wrote data\n"); @@ -392,8 +396,8 @@ static int ubi_volume_continue_write(char *volume, void *buf, size_t size) int ubi_volume_begin_write(char *volume, void *buf, size_t size, size_t full_size) { - int err = 1; - int rsvd_bytes = 0; + int err; + int rsvd_bytes; struct ubi_volume *vol; vol = ubi_find_volume(volume); @@ -412,21 +416,91 @@ int ubi_volume_begin_write(char *volume, void *buf, size_t size, return -err; } + /* The volume is just wiped out */ + if (!full_size) + return 0; + return ubi_volume_continue_write(volume, buf, size); } -int ubi_volume_write(char *volume, void *buf, size_t size) +static int ubi_volume_offset_write(char *volume, void *buf, loff_t offset, + size_t size) +{ + int len, tbuf_size, ret; + u64 lnum; + struct ubi_volume *vol; + loff_t off = offset; + void *tbuf; + + vol = ubi_find_volume(volume); + if (!vol) + return -ENODEV; + + if (size > vol->reserved_pebs * (ubi->leb_size - vol->data_pad)) + return -EINVAL; + + tbuf_size = vol->usable_leb_size; + tbuf = malloc_cache_aligned(tbuf_size); + if (!tbuf) + return -ENOMEM; + + lnum = off; + off = do_div(lnum, vol->usable_leb_size); + + do { + struct ubi_volume_desc desc = { + .vol = vol, + .mode = UBI_READWRITE, + }; + + len = size > tbuf_size ? tbuf_size : size; + if (off + len >= vol->usable_leb_size) + len = vol->usable_leb_size - off; + + ret = ubi_read(&desc, (int)lnum, tbuf, 0, tbuf_size); + if (ret) { + pr_err("Failed to read leb %lld (%d)\n", lnum, ret); + goto exit; + } + + memcpy(tbuf + off, buf, len); + + ret = ubi_leb_change(&desc, (int)lnum, tbuf, tbuf_size); + if (ret) { + pr_err("Failed to write leb %lld (%d)\n", lnum, ret); + goto exit; + } + + off += len; + if (off >= vol->usable_leb_size) { + lnum++; + off -= vol->usable_leb_size; + } + + buf += len; + size -= len; + } while (size); + +exit: + free(tbuf); + return ret; +} + +int ubi_volume_write(char *volume, void *buf, loff_t offset, size_t size) { - return ubi_volume_begin_write(volume, buf, size, size); + if (!offset) + return ubi_volume_begin_write(volume, buf, size, size); + + return ubi_volume_offset_write(volume, buf, offset, size); } -int ubi_volume_read(char *volume, char *buf, size_t size) +int ubi_volume_read(char *volume, char *buf, loff_t offset, size_t size) { int err, lnum, off, len, tbuf_size; void *tbuf; unsigned long long tmp; struct ubi_volume *vol; - loff_t offp = 0; + loff_t offp = offset; size_t len_read; vol = ubi_find_volume(volume); @@ -574,7 +648,7 @@ static int ubi_detach(void) int ubi_part(char *part_name, const char *vid_header_offset) { struct mtd_info *mtd; - int err = 0; + int err; if (ubi && ubi->mtd && !strcmp(ubi->mtd->name, part_name)) { printf("UBI partition '%s' already selected\n", part_name); @@ -605,7 +679,7 @@ int ubi_part(char *part_name, const char *vid_header_offset) static int do_ubi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - int64_t size = 0; + int64_t size; ulong addr = 0; bool skipcheck = false; @@ -761,7 +835,7 @@ static int do_ubi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) (void *)addr, size, full_size); } } else { - ret = ubi_volume_write(argv[3], (void *)addr, size); + ret = ubi_volume_write(argv[3], (void *)addr, 0, size); } if (!ret) { printf("%lld bytes written to volume %s\n", size, @@ -787,7 +861,7 @@ static int do_ubi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } if (argc == 3) { - return ubi_volume_read(argv[3], (char *)addr, size); + return ubi_volume_read(argv[3], (char *)addr, 0, size); } } |