X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=ao-tools%2Flib%2Fao-dfu.c;fp=ao-tools%2Flib%2Fao-dfu.c;h=b6778495614d50cd5a77dbec1e1cba503a5a1b2e;hp=0000000000000000000000000000000000000000;hb=f393482ec47e857db654fa071d4df95e98dab381;hpb=1d7f88bf7521fa6d301da072f95f97fa42d9d247 diff --git a/ao-tools/lib/ao-dfu.c b/ao-tools/lib/ao-dfu.c new file mode 100644 index 00000000..b6778495 --- /dev/null +++ b/ao-tools/lib/ao-dfu.c @@ -0,0 +1,202 @@ +/* + * Copyright © 2016 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ao-hex.h" +#include "ao-dfu.h" + +static uint32_t dfu_crc; +static FILE *dfu_file; +static int dfu_failed; +static int dfu_error; + +static uint32_t update_crc(uint32_t crc, uint8_t byte) +{ + int j; + uint32_t mask; + + crc = crc ^ byte; + for (j = 0; j < 8; j++) { + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + return crc; +} + +static void dfu_init(FILE *file) +{ + dfu_crc = 0xffffffff; + dfu_file = file; + dfu_failed = 0; + dfu_error = 0; +} + +static int dfu_fini(void) +{ + if (fflush(dfu_file) == EOF) { + if (!dfu_failed) { + dfu_failed = 1; + dfu_error = errno; + } + } + if (dfu_failed) + errno = dfu_error; + return !dfu_failed; +} + +static void dfu_8(uint8_t byte) { + if (putc(byte, dfu_file) == EOF) { + if (!dfu_failed) { + dfu_failed = 1; + dfu_error = errno; + } + } + dfu_crc = update_crc(dfu_crc, byte); +} + +static void dfu_pad(int len) { + while (len--) + dfu_8(0); +} + +static void dfu_string(char *string) { + char c; + + while ((c = *string++)) + dfu_8((uint8_t) c); +} + +static void dfu_string_pad(char *string, int len) { + char c; + + while ((c = *string++)) { + dfu_8((uint8_t) c); + len--; + } + dfu_pad(len); +} + +static void dfu_block(uint8_t *bytes, int len) { + while (len--) + dfu_8(*bytes++); +} + +static void dfu_lsb16(uint16_t value) { + dfu_8(value); + dfu_8(value>>8); +} + +static void dfu_lsb32(uint32_t value) { + dfu_8(value); + dfu_8(value >> 8); + dfu_8(value >> 16); + dfu_8(value >> 24); +} + +static uint32_t dfu_image_size(struct ao_hex_image *image) { + return 8 + image->length; +} + +static uint32_t dfu_images_size(int num_image, struct ao_hex_image images[]) +{ + uint32_t size = 0; + int i; + + for (i = 0; i < num_image; i++) + size += dfu_image_size(&images[i]); + return size; +} + +static void dfu_image(struct ao_hex_image *image) +{ + dfu_lsb32(image->address); + dfu_lsb32(image->length); + dfu_block(image->data, image->length); +} + +static void dfu_target(char *name, int num_image, struct ao_hex_image images[]) +{ + uint32_t images_size = dfu_images_size(num_image, images); + int i; + + dfu_string("Target"); + dfu_8(0); + if (name) { + dfu_8(1); + dfu_pad(3); + dfu_string_pad(name, 255); + } else { + dfu_8(0); + dfu_pad(3); + dfu_pad(255); + } + dfu_lsb32(images_size); + dfu_lsb32(num_image); + for (i = 0; i < num_image; i++) + dfu_image(&images[i]); +} + +static uint32_t dfu_target_size(int num_image, struct ao_hex_image images[]) +{ + return 274 + dfu_images_size(num_image, images); +} + +static uint32_t +dfu_size(int num_image, struct ao_hex_image images[]) +{ + uint32_t size = 0; + size += 11; /* DFU Prefix */ + + size += dfu_target_size(num_image, images); + + return size; +} + +int +ao_dfu_write(FILE *file, struct ao_dfu_info *info, int num_image, struct ao_hex_image images[]) +{ + uint32_t total_size; + + total_size = dfu_size(num_image, images); + + dfu_init(file); + /* DFU Prefix */ + dfu_string(DFU_SIGNATURE); + dfu_8(0x01); + dfu_lsb32(total_size); + dfu_8(0x01); + + dfu_target("ST...", num_image, images); + + /* DFU Suffix */ + dfu_lsb16(info->bcdDevice); + dfu_lsb16(info->idProduct); + dfu_lsb16(info->idVendor); + dfu_lsb16(DFU_SPEC_VERSION); + dfu_string("UFD"); + dfu_8(16); + dfu_lsb32(dfu_crc); + return dfu_fini(); +} +