ref: a28c64afe2d7329cb68a41b277fa6645b81f312d
author: Alex Musolino <musolinoa@gmail.com>
date: Sat Nov 28 23:26:52 EST 2020
initial commit
--- /dev/null
+++ b/bin2txt.c
@@ -1,0 +1,188 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+enum{
+ SecSz = 0x10000,
+ SecHdrSz = 0x200,
+};
+
+enum{
+ LogFmtUTC,
+ LogFmtValid,
+ LogFmtLatitude,
+ LogFmtLongitude,
+ LogFmtHeight,
+ LogFmtSpeed,
+ LogFmtHeading,
+ LogFmtDSta,
+ LogFmtDAge,
+ LogFmtPDOP,
+ LogFmtHDOP,
+ LogFmtVDOP,
+ LogFmtNSat,
+ LogFmtSId,
+ LogFmtElevation,
+ LogFmtAzimuth,
+ LogFmtSNR,
+ LogFmtRecReason,
+ LogFmtMillis,
+ LogFmtDistance,
+
+ NumLogFmtBits
+};
+
+typedef struct LogRecord LogRecord;
+struct LogRecord
+{
+ uvlong millis;
+ double latitude;
+ double longitude;
+ ulong speed;
+};
+
+typedef struct LogFmtBit LogFmtBit;
+struct LogFmtBit
+{
+ char *name;
+ uchar size;
+};
+
+static LogFmtBit logfmtbits[] = {
+ [LogFmtUTC] {"UTC", 4},
+ [LogFmtValid] {"VALID", 2},
+ [LogFmtLatitude] {"LATITUDE", 8},
+ [LogFmtLongitude] {"LONGITUDE", 8},
+ [LogFmtHeight] {"HEIGHT", 4},
+ [LogFmtSpeed] {"SPEED", 4},
+ [LogFmtHeading] {"HEADING", 4},
+ [LogFmtDSta] {"DSTA", 2},
+ [LogFmtDAge] {"DAGE", 4},
+ [LogFmtPDOP] {"PDOP", 2},
+ [LogFmtHDOP] {"HDOP", 2},
+ [LogFmtVDOP] {"VDOP", 2},
+ [LogFmtNSat] {"NSAT", 2},
+ [LogFmtSId] {"SID", 4},
+ [LogFmtElevation] {"ELEVATION", 2},
+ [LogFmtAzimuth] {"AZIMUTH", 2},
+ [LogFmtSNR] {"SNR", 2},
+ [LogFmtRecReason] {"RCR", 2},
+ [LogFmtMillis] {"MILLISECOND", 2},
+ [LogFmtDistance] {"DISTANCE", 8},
+};
+
+typedef struct SecHdr SecHdr;
+struct SecHdr
+{
+ ushort cnt;
+ ushort mode;
+ ulong fmt;
+ ulong period;
+ ulong distance;
+ ulong speed;
+ uchar failmask[32];
+};
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s\n", argv0);
+ exits("usage");
+}
+
+ushort
+getu16le(uchar *buf)
+{
+ ushort h;
+
+ h = buf[1];
+ h <<= 8;
+ h |= buf[0];
+ return h;
+}
+
+ulong
+getu32le(uchar *buf)
+{
+ ulong w;
+
+ w = buf[3];
+ w <<= 8;
+ w |= buf[2];
+ w <<= 8;
+ w |= buf[1];
+ w <<= 8;
+ w |= buf[0];
+ return w;
+}
+
+static char*
+logfmtstr(ulong fmt)
+{
+ static char buf[256];
+ int i;
+ char *s, *e;
+
+ s = buf;
+ e = &buf[255];
+ buf[1] = '\0';
+
+ for(i = 0; i < NumLogFmtBits; i++){
+ if((fmt & ((ulong)1<<i)) != 0)
+ s = seprint(s, e, ",%s", logfmtbits[i].name);
+ }
+ return buf+1;
+}
+
+int
+parsesector(Biobufhdr *buf)
+{
+ uchar hdr[SecHdrSz];
+ int i;
+ long n;
+
+ n = Bread(buf, hdr, SecHdrSz);
+
+ if(n == 0)
+ return 0;
+
+ if(n != SecHdrSz)
+ return -1;
+
+ if(hdr[SecHdrSz - 6] != '*')
+ return -1;
+
+ for(i = SecHdrSz - 1; i >= SecHdrSz - 4; i--){
+ if(hdr[i] != 0xbb)
+ return -1;
+ }
+
+ print("count=%uhd\n", getu16le(hdr+0));
+ print("fmt=%08ulx (%s)\n", getu32le(hdr+2), logfmtstr(getu32le(hdr+2)));
+ print("\n");
+ return i;
+}
+
+void
+main(int argc, char **argv)
+{
+ Biobuf *buf;
+ int i;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND;
+ if(argc != 0)
+ usage();
+ buf = Bfdopen(0, OREAD);
+ if(buf == nil)
+ sysfatal("Bfdopen: %r");
+ i = 0;
+ for(;;){
+ Bseek(buf, i*SecSz, 0);
+ if(parsesector(buf) == 0)
+ break;
+ i++;
+ }
+}
--- /dev/null
+++ b/cmd.c
@@ -1,0 +1,3 @@
+#include <u.h>
+#include <libc.h>
+
--- /dev/null
+++ b/config.c
@@ -1,0 +1,104 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "fns.h"
+
+typedef struct Dev Dev;
+
+struct Dev
+{
+ Biobuf *in;
+ Biobuf *out;
+};
+
+int debug;
+Dev dev;
+
+int
+dprint(char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ if(!debug)
+ return 0;
+ va_start(args, fmt);
+ n = vfprint(2, fmt, args);
+ va_end(args);
+ return n;
+}
+
+static char*
+docmd(char *cmd, char *expect, int timeout)
+{
+ Bprint(dev.out, "%s*%02x\r\n", cmd, crc(cmd));
+ Bflush(dev.out);
+ return readline(dev.in, expect, timeout);
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s dev\n", argv0);
+ exits("usage");
+}
+
+static void
+identify(void)
+{
+ char *line;
+
+ line = docmd("$PMTK605", "$PMTK705,", 2);
+ if(strstr(line, ",BT-Q1000EX2,") == nil){
+ fprint(2, "mtk: unsupported device\n");
+ exits("unsupported");
+ }
+}
+
+static void
+setup(void)
+{
+ dprint("setting log interval to 0.1s\n");
+ docmd("$PMTK182,1,3,1", "$PMTK001,182,1,3", 2);
+
+ dprint("enabling logging\n");
+ docmd("$PMTK182,4", "$PMTK001,182,4,3", 2);
+}
+
+static Dev*
+devopen(Dev *dev, char *path)
+{
+ dev->in = Bopen(path, OREAD);
+ if(dev->in == nil)
+ goto Error;
+ dev->out = Bopen(path, OWRITE);
+ if(dev->out == nil)
+ goto Error;
+ return dev;
+Error:
+ if(dev->in)
+ Bterm(dev->in);
+ if(dev->out)
+ Bterm(dev->out);
+ return nil;
+}
+
+void
+main(int argc, char **argv)
+{
+ ARGBEGIN{
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage();
+ }ARGEND;
+ if(argc != 1)
+ usage();
+
+ if(!devopen(&dev, argv[0]))
+ sysfatal("devopen: %r");
+ identify();
+ setup();
+ exits(nil);
+}
--- /dev/null
+++ b/crc.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+uchar
+crc(char *s)
+{
+ uchar crc;
+ int i, len;
+
+ crc = 0;
+ len = strlen(s);
+ for(i = 1; i < len; i++)
+ crc ^= s[i];
+ return crc;
+}
--- /dev/null
+++ b/dump.c
@@ -1,0 +1,336 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "fns.h"
+
+int debug;
+
+int
+dprint(char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ if(!debug)
+ return 0;
+ va_start(args, fmt);
+ n = vfprint(2, fmt, args);
+ va_end(args);
+ return n;
+}
+
+typedef struct Dev Dev;
+typedef struct Nibbler Nibbler;
+
+struct Dev
+{
+ Biobuf *in;
+ Biobuf *out;
+};
+
+struct Nibbler
+{
+ uint len;
+ uint cap;
+ uchar *buf;
+ uchar tmp;
+ uchar idx;
+};
+
+static void
+initnibbler(Nibbler *n, uint cap)
+{
+ n->len = 0;
+ n->cap = cap;
+ n->buf = malloc(n->cap);
+ if(n->buf == nil)
+ sysfatal("out of memory");
+ n->idx = 0;
+}
+
+static void
+putnibble(Nibbler *n, uchar nibble)
+{
+ if(n->idx == 0)
+ n->tmp = nibble;
+ else{
+ if(n->len == n->cap){
+ n->cap *= 2;
+ n->buf = realloc(n->buf, n->cap);
+ }
+ n->buf[n->len++] = n->tmp << 4 | nibble;
+ }
+ n->idx = !n->idx;
+}
+
+static void
+resetnibbler(Nibbler *n)
+{
+ n->len = 0;
+ n->idx = 0;
+}
+
+static void
+freenibbler(Nibbler *n)
+{
+ free(n->buf);
+}
+
+Dev dev;
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s dev\n", argv0);
+ exits("usage");
+}
+
+static char*
+docmd(char *cmd, char *expect)
+{
+ Bprint(dev.out, "%s*%02x\r\n", cmd, crc(cmd));
+ Bflush(dev.out);
+ return readline(dev.in, expect, 5000);
+}
+
+static void
+identify(void)
+{
+ char *line;
+
+ line = docmd("$PMTK605", "$PMTK705,");
+ if(strstr(line, ",BT-Q1000EX2,") == nil){
+ fprint(2, "mtk: unsupported device\n");
+ exits("unsupported");
+ }
+}
+
+static char*
+strnchr(char *s, int n, char c)
+{
+ if(n <= 0)
+ return nil;
+ while(n-- > 0){
+ s = strchr(s, c);
+ if(s == nil)
+ break;
+ s++;
+ }
+ return s;
+}
+
+static uchar*
+parsebyte(char *s, uchar *buf)
+{
+ if(!isxdigit(*s)){
+ fprint(2, "parsebyte: expected hex digit, got %c\n", *s);
+ return nil;
+ }
+ if(isalpha(*s))
+ *buf = tolower(*s) - 'a' + 10;
+ else
+ *buf = *s - '0';
+ *buf <<= 4;
+ s++;
+ if(!isxdigit(*s)){
+ fprint(2, "parsebyte: expected hex digit, got %c\n", *s);
+ return nil;
+ }
+ if(isalpha(*s))
+ *buf |= tolower(*s) - 'a' + 10;
+ else
+ *buf |= *s - '0';
+ return ++buf;
+}
+
+static uchar*
+dordlogcmd(uchar *buf, ulong off, ulong len)
+{
+ ulong n;
+ uchar *ebuf;
+ char *f, *line;
+ char cmd[32];
+
+ snprint(cmd, sizeof(cmd), "$PMTK182,7,%08ulx,%08ulx", off, len);
+ line = docmd(cmd, "$PMTK182,8,");
+ //fprint(2, "log output: %s", line);
+ f = strnchr(line, 2, ',');
+ if(f == nil)
+ sysfatal("bad data log response");
+ f++;
+ n = strtoul(f, nil, 16);
+ if(n != off)
+ sysfatal("data log response for wrong offset");
+ f = strchr(f, ',');
+ if(f == nil)
+ sysfatal("empty data log response");
+ f++;
+ ebuf = buf + len;
+ while(buf < ebuf && *f != '*'){
+ if(parsebyte(f, buf) == nil)
+ sysfatal("parsebyte failed");
+ buf++;
+ f += 2;
+ }
+ return buf;
+}
+
+static void
+download(void)
+{
+ uchar *buf, *bp, *nbp;
+ ulong addr, maxaddr;
+ char *f, *line;
+
+ line = docmd("$PMTK182,2,7", "$PMTK182,3,7,");
+ dprint("got log status response! %s", line);
+
+ sleep(10);
+
+ line = docmd("$PMTK182,5", "$PMTK");
+ dprint("got log disable response! %s", line);
+
+ sleep(100);
+
+ line = docmd("$PMTK182,2,8", "$PMTK182,3,8,");
+ dprint("got flash usage response! %s", line);
+
+ f = strnchr(line, 3, ',');
+ if(f == nil)
+ sysfatal("unexpected response");
+ f++;
+ maxaddr = strtoul(f, nil, 16);
+ if(maxaddr == 0)
+ sysfatal("could not determine flash usage");
+ dprint("downloading %uldKB from device\n", (maxaddr+1)>>10);
+ buf = malloc(maxaddr + 1);
+ if(buf == nil)
+ sysfatal("could not allocate data buffer");
+ bp = buf;
+ addr = 0;
+ while(addr < maxaddr){
+ nbp = dordlogcmd(bp, addr, 0x100);
+ write(1, bp, nbp - bp);
+ addr += nbp - bp;
+ bp = nbp;
+ dprint("progess: %uld/%uld\n", addr, maxaddr);
+ }
+}
+
+static void
+dordlogcmd2(Nibbler *nblr, ulong off, ulong len)
+{
+ ulong n, chklen;
+ char *f, *line;
+ char cmd[32];
+
+ snprint(cmd, sizeof(cmd), "$PMTK182,7,%.8ulX,%.8ulX", off, len);
+dprint("cmd=%s\n", cmd);
+ line = docmd(cmd, "$PMTK182,8,");
+ goto Seek;
+ while(len > 0){
+ chklen = nblr->len;
+ while(*f != '*'){
+ if(!isxdigit(*f))
+ sysfatal("bad character in response");
+ if(isdigit(*f))
+ putnibble(nblr, *f - '0');
+ else
+ putnibble(nblr, toupper(*f) - 'A');
+ f++;
+ }
+ chklen = nblr->len - chklen;
+dprint("got %uld byte chunk\n", chklen);
+ len -= chklen;
+ off += chklen;
+ line = readline(dev.in, "$PMTK", 5000);
+ Seek:
+ if(strncmp(line, "$PMTK001,182,7,", 15) == 0){
+ dprint("@@@@ got ack!\n");
+ return;
+ }
+ if(strncmp(line, "$PMTK182,8", 10) != 0)
+ sysfatal("unexpected data log response");
+ dprint("log output: %s", line);
+ f = strnchr(line, 2, ',');
+ if(f == nil)
+ sysfatal("bad data log response");
+ f++;
+ n = strtoul(f, nil, 16);
+ //if(n != off)
+ // sysfatal("data log response for wrong offset");
+ f = strchr(f, ',');
+ if(f == nil)
+ sysfatal("empty data log response");
+ f++;
+ while(n < off){
+ n++;
+ f += 2;
+ }
+ }
+}
+
+static void
+download2(void)
+{
+ Nibbler n;
+ ulong addr, maxaddr;
+ char *f, *line;
+
+ line = docmd("$PMTK182,2,7", "$PMTK182,3,7,");
+ dprint("got log status response! %s", line);
+
+ sleep(10);
+
+ line = docmd("$PMTK182,5", "$PMTK");
+ dprint("got log disable response! %s", line);
+
+ sleep(100);
+
+ line = docmd("$PMTK182,2,8", "$PMTK182,3,8,");
+ dprint("got flash usage response! %s", line);
+
+ f = strnchr(line, 3, ',');
+ if(f == nil)
+ sysfatal("unexpected response");
+ f++;
+ maxaddr = strtoul(f, nil, 16);
+ if(maxaddr == 0)
+ sysfatal("could not determine flash usage");
+ dprint("downloading %uld bytes from device\n", maxaddr+1);
+
+ initnibbler(&n, 0x400);
+ addr = 0;
+ maxaddr = 0x400;
+ while(addr < maxaddr){
+ dordlogcmd2(&n, addr, 0x400);
+ write(1, n.buf, n.len);
+ addr += n.len;
+ dprint("progess: %.2f\n", 100.0*addr/maxaddr);
+ resetnibbler(&n);
+ }
+ freenibbler(&n);
+}
+
+void
+main(int argc, char **argv)
+{
+ ARGBEGIN{
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage();
+ }ARGEND;
+ if(argc != 1)
+ usage();
+ dev.in = Bopen(argv[0], OREAD);
+ if(dev.in == nil)
+ sysfatal("open: %r");
+ dev.out = Bopen(argv[0], OWRITE);
+ if(dev.out == nil)
+ sysfatal("open: %r");
+ identify();
+ download2();
+}
--- /dev/null
+++ b/erase.c
@@ -1,0 +1,119 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "fns.h"
+
+typedef struct Dev Dev;
+
+struct Dev
+{
+ Biobuf *in;
+ Biobuf *out;
+};
+
+int debug;
+Dev dev;
+
+int
+dprint(char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ if(!debug)
+ return 0;
+ va_start(args, fmt);
+ n = vfprint(2, fmt, args);
+ va_end(args);
+ return n;
+}
+
+static char*
+docmd(char *cmd, char *expect, int timeout)
+{
+ Bprint(dev.out, "%s*%02x\r\n", cmd, crc(cmd));
+ Bflush(dev.out);
+ return readline(dev.in, expect, timeout);
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s dev\n", argv0);
+ exits("usage");
+}
+
+static void
+identify(void)
+{
+ char *line;
+
+ line = docmd("$PMTK605", "$PMTK705,", 2);
+ if(strstr(line, ",BT-Q1000EX2,") == nil){
+ fprint(2, "mtk: unsupported device\n");
+ exits("unsupported");
+ }
+}
+
+static void
+erase(void)
+{
+ char *line;
+
+ line = docmd("$PMTK182,2,7", "$PMTK182,3,7,", 2);
+ dprint("got log status response! %s", line);
+
+ line = docmd("$PMTK182,2,2", "$PMTK182,3,2,", 2);
+ dprint("got log format response! %s", line);
+
+ line = docmd("$PMTK182,5", "$PMTK001,182,5,3", 2);
+ dprint("got log disable response! %s", line);
+
+ sleep(10);
+
+ line = docmd("$PMTK182,6,1", "$PMTK001,182,6", 30);
+ dprint("got flash erase response! %s", line);
+
+ sleep(100);
+
+ line = docmd("$PMTK182,4", "$PMTK001,182,4,3", 2);
+ dprint("got log enable response! %s", line);
+}
+
+static Dev*
+devopen(Dev *dev, char *path)
+{
+ dev->in = Bopen(path, OREAD);
+ if(dev->in == nil)
+ goto Error;
+ dev->out = Bopen(path, OWRITE);
+ if(dev->out == nil)
+ goto Error;
+ return dev;
+Error:
+ if(dev->in)
+ Bterm(dev->in);
+ if(dev->out)
+ Bterm(dev->out);
+ return nil;
+}
+
+void
+main(int argc, char **argv)
+{
+ ARGBEGIN{
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage();
+ }ARGEND;
+ if(argc != 1)
+ usage();
+
+ if(!devopen(&dev, argv[0]))
+ sysfatal("devopen: %r");
+ identify();
+ erase();
+ exits(nil);
+}
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,2 @@
+uchar crc(char*);
+char *readline(Biobufhdr*, char*, int);
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,31 @@
+</$objtype/mkfile
+
+LIB=\
+ common.a$O\
+
+LIBOFILES=\
+ crc.$O\
+ cmd.$O\
+ read.$O\
+
+HFILES=\
+ dat.h\
+ fns.h\
+
+TARG=\
+ bin2txt\
+ config\
+ dump\
+ erase
+
+BIN=$home/bin/$objtype/mtk
+
+</sys/src/cmd/mkmany
+
+$BIN:
+ mkdir -p $BIN
+
+$BIN/%: $BIN
+
+common.a$O: $LIBOFILES
+ ar vu $LIB $newprereq
--- /dev/null
+++ b/read.c
@@ -1,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+char*
+readline(Biobufhdr *in, char *expect, int timeout)
+{
+ static char *line = nil;
+
+ alarm(timeout * 1000);
+ for(;;){
+ if(line != nil)
+ free(line);
+ line = Brdstr(in, '\n', 0);
+ if(line != nil)
+ if(strncmp(line, expect, strlen(expect)) == 0)
+ break;
+ }
+ alarm(0);
+ return line;
+}