initial commit
[saturn.git] / datalink.c
CommitLineData
01698af3 1/*
2 * Tool for Saturn USB DataLink device
3 * Copyright (c) 2014 notaz
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <string.h>
30#include <stdint.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <sys/ioctl.h>
35#include <unistd.h>
36
37// ugh.. avoid conflicting winsize/termio from sys/ioctl.h
38#define winsize winsize_
39#define termio termio_
40#include <linux/termios.h>
41
42#define BAUD_RATE 375000
43#define MAX_DATA_SIZE 191
44
45static int setup(int fd)
46{
47 struct termios2 tty;
48 //struct termios tty;
49 int ret;
50
51 memset(&tty, 0, sizeof(tty));
52
53 //ret = tcgetattr(fd, &tty);
54 ret = ioctl(fd, TCGETS2, &tty);
55 if (ret != 0) {
56 perror("TCGETS2");
57 return ret;
58 }
59
60 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
61 | INLCR | IGNCR | ICRNL | IXON);
62 tty.c_oflag &= ~OPOST;
63 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
64 tty.c_cflag &= ~(CSIZE | PARENB);
65 tty.c_cflag |= CS8 | CSTOPB | CREAD;
66 tty.c_cflag &= ~CBAUD;
67 tty.c_cflag |= BOTHER;
68 tty.c_ispeed =
69 tty.c_ospeed = BAUD_RATE;
70
71 tty.c_cc[VMIN] = 1;
72 tty.c_cc[VTIME] = 0; // read timeout deciseconds
73
74 //ret = tcsetattr(fd, TCSANOW, &tty);
75 ret = ioctl(fd, TCSETS2, &tty);
76 if (ret != 0) {
77 perror("TCSETS2");
78 return ret;
79 }
80
81 memset(&tty, 0, sizeof(tty));
82 ret = ioctl(fd, TCGETS2, &tty);
83 if (ret != 0) {
84 perror("TCGETS2");
85 return ret;
86 }
87
88 if (tty.c_ispeed != BAUD_RATE || tty.c_ospeed != BAUD_RATE) {
89 fprintf(stderr, "warning: could not set %d baud, got:\n",
90 BAUD_RATE);
91 fprintf(stderr, "c_ispeed: %d\n", tty.c_ispeed);
92 fprintf(stderr, "c_ospeed: %d\n", tty.c_ospeed);
93 fprintf(stderr, "c_cflag: 0%o\n", tty.c_cflag);
94 }
95
96 return 0;
97}
98
99static void dump_pkt(const uint8_t *buf, size_t size, const char *msg)
100{
101 size_t i;
102
103 printf("%s\n", msg);
104
105 for (i = 0; i < size; ) {
106 printf("%02zx:", i);
107 while (i < size) {
108 printf(" %02x", buf[i]);
109 i++;
110 if ((i & 15) == 0)
111 break;
112 }
113 printf("\n");
114 }
115}
116
117static uint8_t sum(const uint8_t *buf, size_t size)
118{
119 uint8_t ret = 0;
120 size_t i;
121
122 for (i = 0; i < size; i++)
123 ret += buf[i];
124
125 return ret;
126}
127
128static int send_pkt(int fd, uint8_t cmd, uint32_t addr, uint8_t len,
129 const void *data, size_t data_size)
130{
131 uint8_t pkt[200];
132 int ret;
133
134 pkt[0] = 0x5a;
135 pkt[1] = 9 - 2 + data_size;
136 pkt[2] = cmd;
137 pkt[3] = addr >> 24;
138 pkt[4] = addr >> 16;
139 pkt[5] = addr >> 8;
140 pkt[6] = addr;
141 pkt[7] = len;
142 if (data_size > 0)
143 memcpy(&pkt[8], data, data_size);
144 pkt[8 + data_size] = sum(&pkt[1], 8 + data_size - 1);
145
146 // dump_pkt(pkt, 9 + data_size, "send:");
147
148 ret = write(fd, pkt, 9 + data_size);
149 if (ret != 9 + data_size) {
150 fprintf(stderr, "write: %d/%zd, cmd %02x, addr %08x: ",
151 ret, 9 + data_size, cmd, addr);
152 perror("");
153 dump_pkt(pkt, 9 + data_size, "");
154 return -1;
155 }
156
157 return 0;
158}
159
160static int recv_pkt(int fd, uint8_t cmd, uint32_t addr,
161 void *data, size_t data_size)
162{
163 size_t size = 9 + data_size;
164 size_t got;
165 uint8_t pkt[200];
166 uint8_t csum;
167 int ret;
168
169 for (got = 0; got < size; ) {
170 ret = read(fd, pkt + got, size);
171 if (ret <= 0) {
172 fprintf(stderr, "read: %d, %zd/%zd, "
173 "cmd %02x, addr %08x: ",
174 ret, got, size, cmd, addr);
175 perror("");
176 if (got > 0)
177 dump_pkt(pkt, got, "received data:");
178 return -1;
179 }
180 got += ret;
181 }
182
183 csum = sum(&pkt[1], size - 2);
184 if (csum != pkt[size - 1]) {
185 fprintf(stderr, "read: %zd, cmd %02x, addr %08x: "
186 "checksum incorrect, expected %02x\n",
187 got, cmd, addr, csum);
188 dump_pkt(pkt, got, "");
189 return -2;
190 }
191
192 if (pkt[0] != 0xa5 || pkt[1] != size - 2 || pkt[2] != 0xff) {
193 fprintf(stderr, "read: %zd, cmd %02x, addr %08x: "
194 "unexpected response header\n",
195 got, cmd, addr);
196 dump_pkt(pkt, got, "");
197 return -2;
198 }
199
200 if (data_size != 0)
201 memcpy(data, &pkt[8], data_size);
202
203 // dump_pkt(pkt, got, "recv:");
204
205 return 0;
206}
207
208static int do_rx(int fd, uint32_t addr, void *buf, size_t size)
209{
210 char *cbuf = buf;
211 int ret;
212 int len;
213
214 /* must use at least 2 packets */
215 if (size <= MAX_DATA_SIZE)
216 len = size / 2;
217 else
218 len = MAX_DATA_SIZE;
219
220 ret = send_pkt(fd, 0x01, addr, len, NULL, 0);
221 if (ret != 0)
222 return ret;
223
224 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
225 if (ret != 0)
226 return ret;
227
228 addr += len;
229 cbuf += len;
230 size -= len;
231
232 while (size > 0) {
233 uint8_t cmd = size > MAX_DATA_SIZE ? 0x11 : 0x21;
234
235 len = size;
236 if (len > MAX_DATA_SIZE)
237 len = MAX_DATA_SIZE;
238
239 ret = send_pkt(fd, cmd, addr, len, NULL, 0);
240 if (ret != 0)
241 return ret;
242
243 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
244 if (ret != 0)
245 return ret;
246
247 addr += len;
248 cbuf += len;
249 size -= len;
250 }
251
252 return 0;
253}
254
255static int do_tx(int fd, uint32_t addr, void *buf, size_t size, int exe)
256{
257 size_t size_last = size;
258 uint32_t addr_last = addr;
259 char *cbuf = buf;
260 uint8_t cmd = 0x09;
261 int ret;
262 int len;
263
264 if (size > MAX_DATA_SIZE) {
265 /* skip first packet, we'll send it last */
266 size_last = MAX_DATA_SIZE;
267 addr += size_last;
268 cbuf += size_last;
269 size -= size_last;
270
271 while (size > 0) {
272 len = size;
273 if (len > MAX_DATA_SIZE)
274 len = MAX_DATA_SIZE;
275
276 ret = send_pkt(fd, cmd, addr, len, cbuf, len);
277 if (ret != 0)
278 return ret;
279
280 ret = recv_pkt(fd, cmd, addr, NULL, 0);
281 if (ret != 0)
282 return ret;
283
284 addr += len;
285 cbuf += len;
286 size -= len;
287 }
288 }
289
290 if (exe)
291 cmd = 0x19;
292
293 ret = send_pkt(fd, cmd, addr_last, size_last, buf, size_last);
294 if (ret != 0)
295 return ret;
296
297 ret = recv_pkt(fd, cmd, addr_last, NULL, 0);
298 if (ret != 0)
299 return ret;
300
301 return 0;
302}
303
304static void usage(const char *argv0)
305{
306 printf("usage:\n"
307 "%s [dev <device>] command(s)\n"
308 "commands:\n"
309 " rx <addr> <size> <outfile>\n"
310 " tx <addr> <size> <infile> [x]\n"
311 " <outfile> can be \"/hd/\" for a hexdump\n"
312 " x instructs to execute uploaded data\n"
313 " for tx, size 0 asks to use filesize\n", argv0);
314 exit(1);
315}
316
317static void invarg(int argc, char *argv[], int arg)
318{
319 if (arg < argc)
320 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
321 else
322 fprintf(stderr, "missing required argument %d\n", arg);
323 exit(1);
324}
325
326int main(int argc, char *argv[])
327{
328 const char *portname = "/dev/ttyUSB0";
329 const char *fname;
330 struct stat st;
331 void *buf = NULL;
332 FILE *f = NULL;
333 uint32_t addr;
334 size_t size;
335 char *endp;
336 int arg = 1;
337 int ret;
338 int fd;
339
340 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
341 usage(argv[0]);
342
343 if (!strcmp(argv[arg], "dev")) {
344 arg++;
345 if (argv[arg] != NULL)
346 portname = argv[arg];
347 else
348 invarg(argc, argv, arg);
349 arg++;
350 }
351
352 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
353 if (fd < 0) {
354 fprintf(stderr, "open %s: ", portname);
355 perror("");
356 return 1;
357 }
358
359 ret = setup(fd);
360 if (ret)
361 return ret;
362
363 while (arg < argc)
364 {
365 if (!strcmp(argv[arg], "rx") ||
366 !strcmp(argv[arg], "tx"))
367 {
368 int tx = !strcmp(argv[arg], "tx");
369 int exe = 0;
370
371 if (argc - arg < 4)
372 invarg(argc, argv, argc);
373
374 endp = NULL;
375 addr = strtoul(argv[++arg], &endp, 0);
376 if (endp == NULL || *endp != 0)
377 invarg(argc, argv, arg);
378
379 endp = NULL;
380 size = strtoul(argv[++arg], &endp, 0);
381 if (endp == NULL || *endp != 0)
382 invarg(argc, argv, arg);
383
384 fname = argv[++arg];
385 if (tx || strcmp(fname, "/hd/") != 0) {
386 f = fopen(fname, tx ? "rb" : "wb");
387 if (f == NULL) {
388 fprintf(stderr, "open %s: \n",
389 fname);
390 perror("");
391 return 1;
392 }
393 }
394
395 if (tx && size == 0) {
396 ret = fstat(fileno(f), &st);
397 if (ret != 0) {
398 fprintf(stderr, "fstat %s: \n",
399 fname);
400 perror("stat");
401 return 1;
402 }
403 size = st.st_size;
404 }
405
406 if (size == 0) {
407 fprintf(stderr, "size is 0\n");
408 return 1;
409 }
410
411 buf = realloc(buf, size);
412 if (buf == NULL) {
413 fprintf(stderr, "OOM\n");
414 return 1;
415 }
416
417 if (tx) {
418 ret = fread(buf, 1, size, f);
419 if (ret != size) {
420 fprintf(stderr, "read %s: \n",
421 fname);
422 perror("");
423 return 1;
424 }
425
426 if (arg + 1 < argc
427 && !strcmp(argv[arg + 1], "x"))
428 {
429 exe = 1;
430 arg++;
431 }
432
433 ret = do_tx(fd, addr, buf, size, exe);
434 if (ret != 0)
435 return ret;
436 }
437 else {
438 ret = do_rx(fd, addr, buf, size);
439 if (ret != 0)
440 return ret;
441
442 if (f != NULL) {
443 ret = fwrite(buf, 1, size, f);
444 if (ret != size) {
445 fprintf(stderr,
446 "write %s: \n",
447 fname);
448 perror("");
449 return 1;
450 }
451 }
452 else {
453 dump_pkt(buf, size, "");
454 }
455 }
456
457 if (f != NULL) {
458 fclose(f);
459 f = NULL;
460 }
461 arg++;
462 }
463 else {
464 invarg(argc, argv, arg);
465 }
466 }
467
468 return 0;
469}