ao-tools: Add ao-makebin
[fw/altos] / ao-tools / lib / ao-dfu.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <stdarg.h>
19 #include <ctype.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <stdint.h>
25 #include <errno.h>
26 #include "ao-hex.h"
27 #include "ao-dfu.h"
28
29 static uint32_t dfu_crc;
30 static FILE     *dfu_file;
31 static int      dfu_failed;
32 static int      dfu_error;
33
34 static uint32_t update_crc(uint32_t crc, uint8_t byte)
35 {
36         int j;
37         uint32_t mask;
38
39         crc = crc ^ byte;
40         for (j = 0; j < 8; j++) {
41                 mask = -(crc & 1);
42                 crc = (crc >> 1) ^ (0xEDB88320 & mask);
43         }
44         return crc;
45 }
46
47 static void dfu_init(FILE *file)
48 {
49         dfu_crc = 0xffffffff;
50         dfu_file = file;
51         dfu_failed = 0;
52         dfu_error = 0;
53 }
54
55 static int dfu_fini(void)
56 {
57         if (fflush(dfu_file) == EOF) {
58                 if (!dfu_failed) {
59                         dfu_failed = 1;
60                         dfu_error = errno;
61                 }
62         }
63         if (dfu_failed)
64                 errno = dfu_error;
65         return !dfu_failed;
66 }
67
68 static void dfu_8(uint8_t byte) {
69         if (putc(byte, dfu_file) == EOF) {
70                 if (!dfu_failed) {
71                         dfu_failed = 1;
72                         dfu_error = errno;
73                 }
74         }
75         dfu_crc = update_crc(dfu_crc, byte);
76 }
77
78 static void dfu_pad(int len) {
79         while (len--)
80                 dfu_8(0);
81 }
82
83 static void dfu_string(char *string) {
84         char    c;
85
86         while ((c = *string++))
87                 dfu_8((uint8_t) c);
88 }
89
90 static void dfu_string_pad(char *string, int len) {
91         char    c;
92
93         while ((c = *string++)) {
94                 dfu_8((uint8_t) c);
95                 len--;
96         }
97         dfu_pad(len);
98 }
99
100 static void dfu_block(uint8_t *bytes, int len) {
101         while (len--)
102                 dfu_8(*bytes++);
103 }
104
105 static void dfu_lsb16(uint16_t value) {
106         dfu_8(value);
107         dfu_8(value>>8);
108 }
109
110 static void dfu_lsb32(uint32_t value) {
111         dfu_8(value);
112         dfu_8(value >> 8);
113         dfu_8(value >> 16);
114         dfu_8(value >> 24);
115 }
116
117 static uint32_t dfu_image_size(struct ao_hex_image *image) {
118         return 8 + image->length;
119 }
120
121 static uint32_t dfu_images_size(int num_image, struct ao_hex_image images[])
122 {
123         uint32_t        size = 0;
124         int             i;
125
126         for (i = 0; i < num_image; i++)
127                 size += dfu_image_size(&images[i]);
128         return size;
129 }
130
131 static void dfu_image(struct ao_hex_image *image)
132 {
133         dfu_lsb32(image->address);
134         dfu_lsb32(image->length);
135         dfu_block(image->data, image->length);
136 }
137
138 static void dfu_target(char *name, int num_image, struct ao_hex_image images[])
139 {
140         uint32_t        images_size = dfu_images_size(num_image, images);
141         int             i;
142
143         dfu_string("Target");
144         dfu_8(0);
145         if (name) {
146                 dfu_8(1);
147                 dfu_pad(3);
148                 dfu_string_pad(name, 255);
149         } else {
150                 dfu_8(0);
151                 dfu_pad(3);
152                 dfu_pad(255);
153         }
154         dfu_lsb32(images_size);
155         dfu_lsb32(num_image);
156         for (i = 0; i < num_image; i++)
157                 dfu_image(&images[i]);
158 }
159
160 static uint32_t dfu_target_size(int num_image, struct ao_hex_image images[])
161 {
162         return 274 + dfu_images_size(num_image, images);
163 }
164
165 static uint32_t
166 dfu_size(int num_image, struct ao_hex_image images[])
167 {
168         uint32_t        size = 0;
169         size += 11;     /* DFU Prefix */
170
171         size += dfu_target_size(num_image, images);
172
173         return size;
174 }
175
176 int
177 ao_dfu_write(FILE *file, struct ao_dfu_info *info, int num_image, struct ao_hex_image images[])
178 {
179         uint32_t        total_size;
180
181         total_size = dfu_size(num_image, images);
182
183         dfu_init(file);
184         /* DFU Prefix */
185         dfu_string(DFU_SIGNATURE);
186         dfu_8(0x01);
187         dfu_lsb32(total_size);
188         dfu_8(0x01);
189
190         dfu_target("ST...", num_image, images);
191
192         /* DFU Suffix */
193         dfu_lsb16(info->bcdDevice);
194         dfu_lsb16(info->idProduct);
195         dfu_lsb16(info->idVendor);
196         dfu_lsb16(DFU_SPEC_VERSION);
197         dfu_string("UFD");
198         dfu_8(16);
199         dfu_lsb32(dfu_crc);
200         return dfu_fini();
201 }
202