ao-tools: Add ao-makebin
[fw/altos] / ao-tools / lib / ao-dfu.c
diff --git a/ao-tools/lib/ao-dfu.c b/ao-tools/lib/ao-dfu.c
new file mode 100644 (file)
index 0000000..b677849
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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 <stdarg.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#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();
+}
+