[U-Boot] [PATCH v5 2/9] dfu: Support larger than memory transfers.

Lukasz Majewski
2013-03-11T10:03:41Z

Hi Tom,

>
> > From: Pantelis Antoniou <panto@antoniou-consulting.com>
> >
> > Previously we didn't support upload/download larger than available
> > memory. This is pretty bad when you have to update your root
> > filesystem for example.
> >
> > This patch removes that limitation (and the crashes when you
> > transfered any file larger than 4MB) by making raw image writes be
> > done in chunks and making file maximum size be configurable.
> >
> > The sequence number is a 16 bit counter; make sure we handle
> > rollover correctly. This fixes the wrong transfers for large (>
> > 256MB) images.
> >
> > Also utilize a variable to handle initialization, so that we don't
> > rely on just the counter sent by the host.
> >
> > Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
> > Signed-off-by: Tom Rini <trini@ti.com>
>
> Acked-by: Lukasz Majewski <l.majewski@samsung.com>
>
> Test-hw: Exynos 4210 (Trats)
>
> Tested-by: Lukasz Majewski <l.majewski@samsung.com>

Sorry but, I've found a regression for reading image from a file
system. It happens with EXT4 mmc read (like uImage).

mmc_file_op: ext4load mmc 0:2 /uImage 0x0 0 0x7f95dc98
** File not found 0x0 **
dfu: Read error!

ext4load params:
ext4load - load binary file from a Ext4 filesystem

Usage:
ext4load <interface> <dev[:part]> [addr] [filename] [bytes]
- load binary file 'filename' from 'dev' on 'interface'
to address 'addr' from ext4 filesystem.
All numeric parameters are assumed to be hex.

Some parameters are wrong (buffer - 0x0) and some are switched (address
and filename).

Please find more details below:

>
> > ---
> > Changes in v5:
> > - Rework Pantelis' "dfu: Support larger than memory transfers" to
> > not break filesystem writing
> >
> > Changes in v4: None
> > Changes in v3: None
> > Changes in v2: None
> >
> > README | 7 ++
> > drivers/dfu/dfu.c | 245
> > ++++++++++++++++++++++++++++++++++++++-----------
> > drivers/dfu/dfu_mmc.c | 116 +++++++++++++++++------
> > include/dfu.h | 26 +++++- 4 files changed, 310
> > insertions(+), 84 deletions(-)
> >

> > +static int dfu_write_buffer_drain(struct dfu_entity *dfu)
> > +{
> > + long w_size;
> > + int ret;
> > +
> > + /* flush size? */
> > + w_size = dfu->i_buf - dfu->i_buf_start;
> > + if (w_size == 0)
> > + return 0;
> > +
> > + /* update CRC32 */
> > + dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size);
> > +
> > + ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start,
> > &w_size);
> > + if (ret)
> > + debug("%s: Write error!\n", __func__);
> > +
> > + /* point back */
> > + dfu->i_buf = dfu->i_buf_start;
> > +
> > + /* update offset */
> > + dfu->offset += w_size;
> > +
> > + puts("#");
> > +
> > + return ret;
> > +}
> > +
> > int dfu_write(struct dfu_entity *dfu, void *buf, int size, int
> > blk_seq_num) {
> > - static unsigned char *i_buf;
> > - static int i_blk_seq_num;
> > - long w_size = 0;
> > int ret = 0;
> > + int tret;
> > +
> > + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x "
> > + "offset: 0x%llx bufoffset: 0x%x\n",
> > + __func__, dfu->name, buf, size, blk_seq_num,
> > dfu->offset,
> > + dfu->i_buf - dfu->i_buf_start);
> > +
> > + if (!dfu->inited) {
> > + /* initial state */
> > + dfu->crc = 0;
> > + dfu->offset = 0;
> > + dfu->i_blk_seq_num = 0;
> > + dfu->i_buf_start = dfu_buf;
> > + dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
> > + dfu->i_buf = dfu->i_buf_start;
> > +
> > + dfu->inited = 1;
> > + }
> >
> > - debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf:
> > 0x%p\n",
> > - __func__, dfu->name, buf, size, blk_seq_num, i_buf);
> > + if (dfu->i_blk_seq_num != blk_seq_num) {
> > + printf("%s: Wrong sequence number! [%d] [%d]\n",
> > + __func__, dfu->i_blk_seq_num, blk_seq_num);
> > + return -1;
> > + }
> >
> > - if (blk_seq_num == 0) {
> > - i_buf = dfu_buf;
> > - i_blk_seq_num = 0;
> > + /* DFU 1.1 standard says:
> > + * The wBlockNum field is a block sequence number. It
> > increments each
> > + * time a block is transferred, wrapping to zero from
> > 65,535. It is used
> > + * to provide useful context to the DFU loader in the
> > device."
> > + *
> > + * This means that it's a 16 bit counter that roll-overs at
> > + * 0xffff -> 0x0000. By having a typical 4K transfer block
> > + * we roll-over at exactly 256MB. Not very fun to debug.
> > + *
> > + * Handling rollover, and having an inited variable,
> > + * makes things work.
> > + */
> > +
> > + /* handle rollover */
> > + dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
> > +
> > + /* flush buffer if overflow */
> > + if ((dfu->i_buf + size) > dfu->i_buf_end) {
> > + tret = dfu_write_buffer_drain(dfu);
> > + if (ret == 0)
> > + ret = tret;
> > }
> >
> > - if (i_blk_seq_num++ != blk_seq_num) {
> > - printf("%s: Wrong sequence number! [%d] [%d]\n",
> > - __func__, i_blk_seq_num, blk_seq_num);
> > + /* we should be in buffer now (if not then size too large)
> > */
> > + if ((dfu->i_buf + size) > dfu->i_buf_end) {
> > + printf("%s: Wrong size! [%d] [%d] - %d\n",
> > + __func__, dfu->i_blk_seq_num, blk_seq_num,
> > size); return -1;
> > }
> >
> > - memcpy(i_buf, buf, size);
> > - i_buf += size;
> > + memcpy(dfu->i_buf, buf, size);
> > + dfu->i_buf += size;
> >
> > + /* if end or if buffer full flush */
> > + if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) {
> > + tret = dfu_write_buffer_drain(dfu);
> > + if (ret == 0)
> > + ret = tret;
> > + }
> > +
> > + /* end? */
> > if (size == 0) {
> > - /* Integrity check (if needed) */
> > - debug("%s: %s %d [B] CRC32: 0x%x\n", __func__,
> > dfu->name,
> > - i_buf - dfu_buf, crc32(0, dfu_buf, i_buf -
> > dfu_buf));
> > + /* Now try and flush to the medium if needed. */
> > + if (dfu->flush_medium)
> > + ret = dfu->flush_medium(dfu);
> > + printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc);
> >
> > - w_size = i_buf - dfu_buf;
> > - ret = dfu->write_medium(dfu, dfu_buf, &w_size);
> > - if (ret)
> > - debug("%s: Write error!\n", __func__);
> > + /* clear everything */
> > + dfu->crc = 0;
> > + dfu->offset = 0;
> > + dfu->i_blk_seq_num = 0;
> > + dfu->i_buf_start = dfu_buf;
> > + dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
> > + dfu->i_buf = dfu->i_buf_start;
> > +
> > + dfu->inited = 0;
> >
> > - i_blk_seq_num = 0;
> > - i_buf = NULL;
> > - return ret;
> > }
> >
> > - return ret;
> > + return ret = 0 ? size : ret;
> > +}
> > +
> > +static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf,
> > int size) +{
> > + long chunk;
> > + int ret, readn;
> > +
> > + readn = 0;
> > + while (size > 0) {
> > +
> > + /* get chunk that can be read */
> > + chunk = min(size, dfu->b_left);
> > + /* consume */
> > + if (chunk > 0) {
> > + memcpy(buf, dfu->i_buf, chunk);
> > + dfu->crc = crc32(dfu->crc, buf, chunk);
> > + dfu->i_buf += chunk;
> > + dfu->b_left -= chunk;
> > + size -= chunk;
> > + buf += chunk;
> > + readn += chunk;
> > + }
> > +
> > + /* all done */
> > + if (size > 0) {
> > + /* no more to read */
> > + if (dfu->r_left == 0)
> > + break;
> > +
> > + dfu->i_buf = dfu->i_buf_start;
> > + dfu->b_left = dfu->i_buf_end -
> > dfu->i_buf_start; +
> > + /* got to read, but buffer is empty */
> > + if (dfu->b_left > dfu->r_left)
> > + dfu->b_left = dfu->r_left;
> > + ret = dfu->read_medium(dfu, dfu->offset,
> > dfu->i_buf,
> > + &dfu->b_left);
> > + if (ret != 0) {
> > + debug("%s: Read error!\n",
> > __func__);
> > + return ret;
> > + }
> > + dfu->offset += dfu->b_left;
> > + dfu->r_left -= dfu->b_left;
> > +
> > + puts("#");
> > + }
> > + }
> > +
> > + return readn;
> > }
> >
> > int dfu_read(struct dfu_entity *dfu, void *buf, int size, int
> > blk_seq_num) {
> > - static unsigned char *i_buf;
> > - static int i_blk_seq_num;
> > - static long r_size;
> > - static u32 crc;
> > int ret = 0;
> >
> > debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf:
> > 0x%p\n",
> > - __func__, dfu->name, buf, size, blk_seq_num, i_buf);
> > -
> > - if (blk_seq_num == 0) {
> > - i_buf = dfu_buf;
> > - ret = dfu->read_medium(dfu, i_buf, &r_size);
> > - debug("%s: %s %ld [B]\n", __func__, dfu->name,
> > r_size);
> > - i_blk_seq_num = 0;
> > - /* Integrity check (if needed) */
> > - crc = crc32(0, dfu_buf, r_size);
> > + __func__, dfu->name, buf, size, blk_seq_num,
> > dfu->i_buf); +
> > + if (!dfu->inited) {
> > + ret = dfu->read_medium(dfu, 0, NULL, &dfu->r_left);
^^^^^^^^^^^^ this call causes read error. I
suppose, that it is an initial "read". Does
it read the whole file at once?

The problem is that the command is fromatted in a
wrong way.

On the other hand write operations works on Trats.

--
Best regards,

Lukasz Majewski

Samsung R&D Poland (SRPOL) | Linux Platform Group