/* * Copyright 2005 paul@t6tr2.sbrk.co.uk * * Released under terms of GPL version 2. See COPYING. * * v0.5a */ #include #include #include #include #include #include #include #include #include #define S(e) if (-1 == (e)) {perror(#e);exit(1);} else #define A(e) if (!(e)) {perror(#e);exit(1);} else #define WHERE if (d) fprintf(stderr, "%s %s %d\n", \ __FILE__, __FUNCTION__, __LINE__); #define DUMP(m,b,n) if (d) {fprintf(stderr, "%s ", m); \ for (p = (b); p < (b)+n; p++) \ fprintf(stderr, "%02x ", *p); fprintf(stderr, "\n");} #define PDUMP(m,b,n) {printf("@@%s ", m); \ for (p = (b); p < (b)+n; p++) \ printf("%02x", *p); printf("\n");} #define HEX(v) (((v) >= '0' && (v) <= '9') ? (v)-'0' : \ ((v) >= 'a' && (v) <= 'z') ? (v) - 'a' + 10 : (v) - 'A' + 10) #define uchar unsigned char #define uint unsigned int #define ulong unsigned long #define PADDR(p) ((p)*0x200+0xE00) /* absolute address of page no. */ #define PAGESZ 512 /* size of a page */ #define PGDATA 510 /* usable data in a page */ #define HDRSIZE 46 /* size of a log header */ #define LAP1OFF 0x3c /* offset of lap data in log header page */ #define LAP2OFF 0 /* offset of lap data in subsequent pages */ #define LAPSIZE 15 /* size of a lap record */ #define RWMAX 200 /* max amount of data per read/write. < 256 */ #define MAXPAGE 248 /* number of pages of memory for logs */ #define LOCSER 0x005a /* location of serial number */ #define LOCCF 0x0080 /* location of calibration factors */ #define LOCVER 0x00f0 /* location of versions of paired devices */ #define LOCHIST 0x0d48 /* location of device history */ #define LOCLOGH 0x0fb4 /* location of log headers */ /* t6 commands */ #define MREAD 0x05 /* Reads first 64k memory bank only */ #define MWRITE 0x06 /* write mem */ #define RVER 0x0f /* read t6 firmware version */ #define CMD10 0x10 /* TODO don't know what this does */ /* I think it's just a test to see */ /* if higher command numbers implemented */ /* 10 0000 -> 0001 00 */ #define CMD7 0x07 /* ditto - used on dive computer */ /* 07 0000 -> 0000 */ #define CMDB 0x0b /* ditto */ /* 0b 0000 -> 0000 */ #define CMDE 0x0e /* ditto */ /* 0e 0000 -> 0001 01 */ /* (Not) implemented commands * (0,1,2,3,4) * 5,6 * 7->0 (8,9,a) * b->0 (c,d) * e->1 f * 10->0 (11,12,13,14) * 15,16 * (17,18,19,1a,1b,1c,1d,1e,1f) * 20,21,22,23,24,25,26,27,28 * (29->ff) */ #define MREAD2 0x15 /* extended READ. Reads any 64k bank */ #define MWRITE2 0x16 /* extended write */ #define RSTHIST 0x20 /* reset device history */ #define DLOG 0x21 /* delete log(s) */ #define RSET 0x22 /* read general settings */ #define WSET 0x23 /* write general settings */ #define RHR 0x24 /* read HR settings */ #define WHR 0x25 /* write HR settings */ #define GETCON 0x26 /* get connected devices */ #define GETCF 0x27 /* read calibration factors */ #define SETCF 0x28 /* write calibration factors */ /* commands 0x29 and above appear to be unimplemented */ #define VERBOSE 1 #define BRIEF 2 #define CSV 3 #define XML 4 #define IMULT 2 #define SNONE 99 #define SMAX 0 #define SMAX1 1 #define SMAX2 2 #define SMAXM1 11 #define SMAXM2 12 void usage(void); uchar *readpages(uchar, uint, uint *); void decode2(ushort *, uint, int, int); int readlaps(uchar, uchar); void readlog(uchar, uint, uint); uchar *mread(uint, int); void mwrite(uint, uchar *, int); uchar *cmd2(uint, uint, int *); uchar *cmdn(uint, uchar *, uint, int *); uchar *cmd221(uint, uint, uint, uint, int *); uchar *cmd2221(uint, uint, uint, uint, uint, int *); uchar *cmd221n(uint, uint, uint, uint, uchar *, uint, int *); uchar *cmd2221n(uint, uint, uint, uint, uint, uchar *, uint, int *); void addck(uchar *, uint); int chkck(uchar *, uint); void msend(uchar *, uint); void resend(int); void flushfd(void); int fd; int indent; char indents[100]; int opmode = VERBOSE; char *progname; int d = 0; /* debugging on off */ int repeats = 1; /* # times to repeat each command */ struct { long npages; long nbeats; long beatbits; long buffbits; ulong hrtime; } s; main(int ac, char *av[]) { struct termios tp; uchar *b, *b2, *b3; uchar *p, q; int save = 0; char *file = 0; char *mdump = 0; char *memwrite = 0; int mstart, mcount; int readhr = 0; char *writehr = 0; int readser = 0; int readver = 0; int readlogdata = 0; int readloghdrs = 0; char *deletelog = 0; int deleteall = 0; int readsettings = 0; char *writesettings = 0; uchar settings[30]; int readhist = 0; int resethist = 0; int readcf = 0; char *writecf = 0; int stats = 0; extern char *optarg; extern int optind, opterr, optopt; int c; int i, j; int l; long v; char *ep; char *sep; char *condev; ushort cfs[3]; uint addr; uchar *membuf; uchar setcfs[6] = {0x03,0xE7,0x03,0xCF,0x03,0xEe}; int testcmd = CMD10; uchar altpage, hrpage, dstpage; ushort *alogdata, *dlogdata, *hrlogdata; uint aread, dread, hrread; uchar inuse[MAXPAGE+1]; int serport; WHERE progname = av[0]; while ((c = getopt(ac, av, "Vt:R:f:rd:soO:hHD:alLvcn:m:M:w:SBXC")) != -1) { switch(c) { case 't': testcmd = atoi(optarg); break; case 'R': repeats = atoi(optarg); break; case 'd': d = atoi(optarg); break; case 'm': mdump = optarg; break; case 'M': memwrite = optarg; break; case 'f': file = optarg; break; case 'V': save = 1; break; case 'r': readsettings = 1; break; case 'w': writesettings = optarg; break; case 'h': readhist = 1; break; case 'H': resethist = 1; break; case 'O': writehr = optarg; break; case 'o': readhr = 1; break; case 'S': stats = 1; break; case 's': readser = 1; break; case 'n': writecf = optarg; break; case 'c': readcf = 1; break; case 'D': deletelog = optarg; break; case 'l': readlogdata = 1; break; case 'L': readloghdrs = 1; break; case 'v': readver = 1; break; case 'X': opmode = XML; break; case 'C': opmode = CSV; break; case 'B': opmode = BRIEF; break; case 'a': readhist = readsettings = readhr = readser = readcf = readver = 1; break; default: fprintf(stderr, "unknown option %s\n", optarg); usage(); } } ac -= optind; av += optind; /* any remaining arguments refer to specific log numbers */ if (!file) usage(); S(fd = open(file, O_RDWR/*|O_BINARY*/)); S(tcgetattr(fd, &tp)); /*cfmakeraw(&tp); equivalent*/ tp.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|INPCK #ifdef IUCLC |IUCLC #endif ); tp.c_oflag &= ~OPOST; tp.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN|ECHOE); tp.c_cflag &= ~(CSIZE|PARENB); tp.c_cflag = CS8 | CREAD | CLOCAL; /* it seems the usb-serial adapter requires RTS but doesn't provide CTS */ /* so we must ignore modem control lines (CLOCAL) ... */ S(cfsetispeed(&tp, B115200)); S(cfsetospeed(&tp, B115200)); tp.c_cc[VMIN] = 0; tp.c_cc[VTIME] = 10; S(tcsetattr(fd, TCSANOW, &tp)); /* ... but clear RTS (TIOCM_RTS) */ serport = TIOCM_RTS; S(ioctl(fd, TIOCMBIC, &serport)); flushfd(); /* no output modes for this */ if (save) { /* save all settings */ printf("settings\n"); b = cmd2(CMD10, 0, &l); PDUMP("cmd10", b, l) free(b); b = cmd2(CMD7, 0, &l); /* something dodgy here PDUMP("cmd7", b, l) free(b); b = cmd2(CMDB, 0, &l); PDUMP("cmdb", b, l) free(b); b = cmd2(CMDE, 0, &l); PDUMP("cmde", b, l) free(b); */ b = cmd2(RVER, 0, &l); PDUMP("ver", b, l); free(b); b = cmd2(RSET, 0, &l); PDUMP("set", b, l) free(b); b = cmd2(RHR, 0, &l); PDUMP("hr", b, l) free(b); b = cmd2(GETCON, 0, &l); PDUMP("condev", b, l) free(b); b = cmd2(GETCF, 0, &l); PDUMP("cf", b, l) free(b); /* lower 4K of memory */ printf("\nlower4k\n"); b = mread(0, 4096); for (i=0; i < 4096; i++) { if (!(i % 32)) printf("%06x ", i); printf("%02x", b[i]); if ((i % 32) == 31) printf("\n"); } free(b); /* all in use memory */ printf("\nin use\n"); memset(inuse, 0, sizeof inuse); b = mread(LOCLOGH, 30); for (p = b; *p && p < b+30; p++) { printf("log %d\n", p-b+1); q = *p; while (q) { inuse[q] = 1; b3 = mread(PADDR(q), 512); for (i=0; i < 512; i++) { if (!(i % 32)) printf("%06x ", PADDR(q)+i); printf("%02x", b3[i]); if ((i % 32) == 31) printf("\n"); } q = b3[511]; free(b3); } b2 = mread(PADDR(*p), HDRSIZE); altpage = *(b2+7); hrpage = *(b2+8); dstpage = *(b2+9); for (j = 7; j <= 9; j++) { switch (j) { case 7: printf("altitude data\n"); break; case 8: printf("hr data\n"); break; case 9: printf("distance data\n"); } q = *(b2+j); while (q) { inuse[q] = 1; b3 = mread(PADDR(q), 512); for (i=0; i < 512; i++) { if (!(i % 32)) printf("%06x ", PADDR(q)+i); printf("%02x", b3[i]); if ((i % 32) == 31) printf("\n"); } q = b3[511]; free(b3); } } free(b2); } /* all page forward and back pointers of not in-use pages */ printf("\nfree pages\n"); for (i = 1; i <= MAXPAGE; i++) { if (!inuse[i]) { b = mread(PADDR(i), 1); printf("%06x %02x\n", PADDR(i), *b); free(b); b = mread(PADDR(i)+511, 1); printf("%06x %02x\n", PADDR(i)+511, *b); free(b); } } } if (opmode == XML) { indent = 0; printf("\n"); printf("\n"); sprintf(indents, "%*s", ++indent*IMULT, ""); printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); } if (memwrite) { p = strtok(memwrite, " ,"); if (p) { addr = strtol(p, &ep, 16); if (d) fprintf(stderr, "memwrite addr %lx\n", addr); } else { fprintf(stderr, "error: -M \"addr data\"\n"); exit(1); } p = strtok(0, " ,"); if (p) { if (d) fprintf(stderr, "memwrite data %s\n", p); membuf = malloc((strlen(p)+1)/2); for (i = 0; i < strlen(p)/2; i++) membuf[i] = HEX(*(p+i*2))*16 + HEX(*(p+i*2+1)); } else { fprintf(stderr, "error: -M \"addr data\"\n"); exit(1); } /* should be no more tokens */ p = strtok(0, " ,"); if (p) { fprintf(stderr, "extraneous data\n"); exit(1); } if (d) fprintf(stderr, "memwrite %lx %d\n", addr, i); DUMP("membuf", (uchar *)membuf, i) mwrite(addr, (uchar *)membuf, i); free(membuf); } if (mdump) { WHERE /* TODO do this in CSV, XML maybe... */ sscanf(mdump, "%x %d", &mstart, &mcount); printf("dumping %x %d\n", mstart, mcount); b = mread(mstart, mcount); for (i=0; i < mcount; i++) { if (!(i % 32)) printf("%08x ", mstart+i); printf("%02x", b[i]); if ((i % 32) == 31) printf("\n"); } if ((i % 64) != 0) printf("\n"); } if (readser) { WHERE b = mread(LOCSER, 4); DUMP("serial", b, 4); switch (opmode) { case CSV: printf("serial,"); case BRIEF: printf("%d\n\n", ntohl(*(int *)b)); break; case VERBOSE: printf("Serial no.: %d %d %08x %08x\n\n", *(int *)b, ntohl(*(int *)b), *(int *)b, ntohl(*(int *)b)); break; case XML: printf("%s%d\n", indents, ntohl(*(int *)b)); } free(b); } if (writecf) { /* input should look like this: */ /* 1063 1050 1090 or /* 1063 */ b = mread(LOCCF, 6); DUMP("CF", b, 6); memmove(cfs, b, 6); free(b); for (i = 0; i < 3; i++) { p = strtok(i ? 0 : writecf, " ,"); if (p) { v = strtol(p, &ep, 10); if (ep && !*ep) cfs[i] = htons(v); else { fprintf(stderr, "bad cf #%d\n", i); exit(1); } } } /* should be no more tokens */ p = strtok(0, " ,"); if (p) { fprintf(stderr, "extraneous cfs\n"); exit(1); } DUMP("cfs", (uchar *)cfs, 6) /*mwrite(LOCCF, (uchar *)cfs, 6);*/ b = cmdn(SETCF, (uchar *)cfs, 6, &l); readcf = 1; } if (readcf) { WHERE /*b = mread(LOCCF, 6);*/ b = cmd2(GETCF, 0, &l); DUMP("CF", b, 6); sep=" "; switch (opmode) { case CSV: printf("cf,"); sep=","; case BRIEF: printf("%d%s%d%s%d\n\n", ntohs(*(ushort *)b), sep, ntohs(*(ushort *)(b+2)), sep, ntohs(*(ushort *)(b+4))); break; case VERBOSE: printf("CF: foot %d bike %d speed %d\n\n", ntohs(*(ushort *)b), ntohs(*(ushort *)(b+2)), ntohs(*(ushort *)(b+4))); break; case XML: printf("%s\n", indents, ntohs(*(ushort *)b), ntohs(*(ushort *)(b+2)), ntohs(*(ushort *)(b+4))); } free(b); } if (resethist) { b = cmd2(RSTHIST, 0, &l); DUMP("reset hist", b, l) readhist = 1; } if (readhist) { WHERE b = mread(LOCHIST, 30); DUMP("history", b, 30); sep = " "; switch (opmode) { case CSV: sep = ","; printf("total time,last reset date,last reset time," "highest point,highest point date," "highest point time," "total ascent,total descent," "hr above high,hr middle-high,hr low-middle," "total distance\n"); case BRIEF: printf("%d%s%02d.%02d.%02d%s%02d:%02d%s" "%d%s%02d.%02d.%02d%s" "%02d:%02d%s" "%ld%s%ld%s" "%02d:%02d%s" "%02d:%02d%s" "%02d:%02d%s" "%.1f\n\n", ntohs(*(ushort *)b), sep, b[4], b[3], b[2], sep, b[5], b[6], sep, ntohs(*(ushort *)(b+7)), sep, b[11], b[10], b[9], sep, b[12], b[13], sep, ntohl(*(ulong *)(b+14)), sep, ntohl(*(ulong *)(b+18)), sep, ntohs(*(ushort *)(b+22))/60, ntohs(*(ushort *)(b+22))%60, sep, ntohs(*(ushort *)(b+24))/60, ntohs(*(ushort *)(b+24))%60, sep, ntohs(*(ushort *)(b+26))/60, ntohs(*(ushort *)(b+26))%60, sep, (float)ntohs(*(ushort *)(b+28))/10); break; case VERBOSE: printf("total time: %d mins, " "last reset: %02d.%02d.%02d %02d:%02d\n" "highest point: %d " "highest point date: %02d.%02d.%02d " "time: %02d:%02d\n" "total ascent: %ld total descent: %ld\n" "hr above high: %02d:%02d\n" "hr middle-high: %02d:%02d\n" "hr low-middle: %02d:%02d\n" "total distance: %.1f km\n\n", ntohs(*(ushort *)b), b[4], b[3], b[2], b[5], b[6], ntohs(*(ushort *)(b+7)), b[11], b[10], b[9], b[12], b[13], ntohl(*(ulong *)(b+14)), ntohl(*(ulong *)(b+18)), ntohs(*(ushort *)(b+22))/60, ntohs(*(ushort *)(b+22))%60, ntohs(*(ushort *)(b+24))/60, ntohs(*(ushort *)(b+24))%60, ntohs(*(ushort *)(b+26))/60, ntohs(*(ushort *)(b+26))%60, (float)ntohs(*(ushort *)(b+28))/10 ); break; case XML: printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); printf("%s%d\n", indents, ntohs(*(ushort *)b)); printf("%s\n", indents, b[4], b[3], b[2], b[5], b[6]); printf("%s%d" "\n", indents, b[11], b[10], b[9], b[12], b[13], ntohs(*(ushort *)(b+7))); printf("%s%04x\n", indents, ntohs(*(ushort *)(b+12))); printf("%s%ld\n", indents, ntohl(*(ulong *)(b+14))); printf("%s%ld\n", indents, ntohl(*(ulong *)(b+18))); printf("%s
\n", indents, ntohs(*(ushort *)(b+22))/60, ntohs(*(ushort *)(b+22))%60, ntohs(*(ushort *)(b+24))/60, ntohs(*(ushort *)(b+24))%60, ntohs(*(ushort *)(b+26))/60, ntohs(*(ushort *)(b+26))%60); printf("%s%.1f\n", indents, (float)ntohs(*(ushort *)(b+28))/10); sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s
\n", indents); break; } free(b); } if (readver) { WHERE b = cmd2(testcmd, 0, &l); DUMP("testcmd", b, l) free(b); b = cmd2(GETCON, 0, &l); DUMP("getcon", b, l) condev = malloc(l*2+1); for (i = 0; i < l; i++) sprintf(condev+i*2, "%02x", (int)b[i]); free(b); b = cmd2(RVER, 0, &l); if (!b || l != 4) { fprintf(stderr, "cmd %d failed %lx %d\n", RVER, b, l); exit(1); } DUMP("version", b, l); b2 = mread(LOCVER, 6); DUMP("versions", b, 6); sep = " "; switch (opmode) { case CSV: printf("ver,"); sep=","; case BRIEF: printf("%d.%d.%d%s%d.%d.%d%s%d.%d.%d%s%s\n\n", b[4],b[5],b[6], sep, b2[0],b2[1],b2[2], sep, b2[3],b2[4],b2[5], sep, condev); break; case VERBOSE: printf("epson (t6 firmware): %d.%d.%d\n", b[4],b[5],b[6]); printf("texas (foot pod): %d.%d.%d hrbelt: %d.%d.%d\n", b2[0],b2[1],b2[2],b2[3],b2[4],b2[5]); printf("connected devices: %s\n\n", condev); break; case XML: printf("%s\n", indents, b[4],b[5],b[6], b2[0],b2[1],b2[2], b2[3],b2[4],b2[5], condev); break; } free(b); free(b2); free(condev); WHERE } if (writesettings) { /* input should look like this: */ /* 1 0 0 2 1 1 1 1 1 1 167 1 1 1 or */ /* 1,0,0,2,1,1,1,1,1,1,167,1,1,1 */ for (i = 0; i < 14; i++) settings[i] = (uchar)99; for (i = 0; i < 14; i++) { p = strtok(i ? 0 : writesettings, " ,"); if (p) { v = strtol(p, &ep, 10); if (ep && !*ep) settings[i] = (uchar)v; else { fprintf(stderr, "bad setting #%d\n", i); exit(1); } } else { fprintf(stderr, "insufficient settings %d\n", i); exit(1); } } /* should be no more tokens */ p = strtok(0, " ,"); if (p) { fprintf(stderr, "extraneous settings\n"); exit(1); } DUMP("wrsettings", settings, 14) b = cmdn(WSET, settings, 14, &l); DUMP("wrset rtn", b, l) readsettings = 1; free(b); } if (readsettings) { WHERE b = cmd2(RSET, 0, &l); if (!b || l != 14) { fprintf(stderr, "cmd %d failed %lx %d\n", RVER, b, l); exit(1); } DUMP("settings", b, l) sep = " "; switch (opmode) { case CSV: sep = ","; printf("tones,icons,lights,interval,clock,date," "altitude,vspeed,pressure,temp," "unknown,hr,distance,speed\n"); printf("%s%s%s%s%s%s%d%s%s%s%s%s%s%s" "%s%s%s%s%s%s%d%s%s%s%s%s%s\n", b[0] ? "on": "off", sep, b[1] ? "on": "off", sep, b[2] ? b[2] == 1 ? "off": "night": "normal", sep, b[3], sep, b[4] ? "24hr": "12hr", sep, b[5] ? (b[5] == 1 ? "dd.mm": "day"): "mm.dd", sep, b[6] ? "m": "ft", sep, b[7] ? b[7] == 1 ? "m/min" : b[7] == 2 ? "m/h": b[7] == 3 ? "ft/s" : "ft/min": "m/s", sep, b[8] ? "hPa": "inHG", sep, b[9] ? "C": "F", sep, b[10], sep, b[11] ? "bpm": "%", sep, b[12] ? "km": "mi", sep, b[13] ? b[13] == 1 ? "km/h": "mph": "min/mi"); case BRIEF: printf("%d%s%d%s%d%s%d%s%d%s%d%s%d%s" "%d%s%d%s%d%s%d%s%d%s%d%s%d\n\n", b[0], sep, b[1], sep, b[2], sep, b[3], sep, b[4], sep, b[5], sep, b[6], sep, b[7], sep, b[8], sep, b[9], sep, b[10], sep, b[11], sep, b[12], sep, b[13]); break; case VERBOSE: printf("tones: %s(%d) icons: %s(%d) " "lights: %s(%d) interval: %d\n" "clock: %s(%d) date: %s(%d) " "altitude: %s(%d) vspeed: %s(%d)\n" "pressure: %s(%d) temp: %s(%d) " "unknown: %x HR: %s(%d) " "distance: %s(%d) speed: %s(%d)" "\n\n", b[0] ? "on": "off", b[0], b[1] ? "on": "off", b[1], b[2] ? b[2] == 1 ? "off": "night": "normal", b[2], b[3], b[4] ? "24hr": "12hr", b[4], b[5] ? (b[5] == 1 ? "dd.mm": "day"): "mm.dd", b[5], b[6] ? "m": "ft", b[6], b[7] ? b[7] == 1 ? "m/min" : b[7] == 2 ? "m/h": b[7] == 3 ? "ft/s" : "ft/min": "m/s", b[7], b[8] ? "hPa": "inHG", b[8], b[9] ? "C": "F", b[9], b[10], b[11] ? "bpm": "%", b[11], b[12] ? "km": "mi", b[12], b[13] ? b[13] == 1 ? "km/h": "mph": "min/mi", b[13] ); break; case XML: printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); printf("%s%s\n", indents, b[0], b[0] ? "on": "off"); printf("%s%s\n", indents, b[1], b[1] ? "on": "off"); printf("%s%s\n", indents, b[2], b[2] ? b[2] == 1 ? "off": "night": "normal"); printf("%s%d\n", indents, b[3], b[3]); printf("%s%s\n", indents, b[4], b[4] ? "24hr": "12hr"); printf("%s%s\n", indents, b[5], b[5] ? (b[5] == 1 ? "dd.mm": "day"): "mm.dd"); printf("%s%s\n", indents, b[6], b[6] ? "m": "ft"); printf("%s%s\n", indents, b[7], b[7] ? b[7] == 1 ? "m/min" : b[7] == 2 ? "m/h": b[7] == 3 ? "ft/s" : "ft/min": "m/s"); printf("%s%s\n", indents, b[8], b[8] ? "hPa": "inHG"); printf("%s%s\n", indents, b[9], b[9] ? "C": "F"); printf("%s%d\n", indents, b[10], b[10]); printf("%s
%s\n", indents, b[11], b[11] ? "bpm": "%"); printf("%s%s\n", indents, b[12], b[12] ? "km": "mi"); printf("%s%s\n", indents, b[13], b[13] ? b[13] == 1 ? "km/h": "mph": "min/mi"); sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s
\n", indents); } free(b); } if (writehr) { /* input should look like this: */ /* 140 160 60 191 100 125 185 or */ /* 140,160,60,191,100,125,185 */ for (i = 0; i < 7; i++) settings[i] = (uchar)99; for (i = 0; i < 7; i++) { p = strtok(i ? 0 : writehr, " ,"); if (p) { v = strtol(p, &ep, 10); if (ep && !*ep) settings[i] = (uchar)v; else { fprintf(stderr, "bad setting #%d\n", i); exit(1); } } else { fprintf(stderr, "insufficient settings %d\n", i); exit(1); } } /* should be no more tokens */ p = strtok(0, " ,"); if (p) { fprintf(stderr, "extraneous settings\n"); exit(1); } DUMP("wrhr", settings, 7) b = cmdn(WHR, settings, 7, &l); DUMP("wrhr rtn", b, l) readhr = 1; free(b); } if (readhr) { WHERE b = cmd2(RHR, 0, &l); if (!b || l != 7) { fprintf(stderr, "cmd %d failed %lx %d\n", RVER, b, l); exit(1); } sep = " "; switch (opmode) { case CSV: printf("hrlow,hrhigh," "pers hrmin,pers hrmax," "hist hrlow,hist hrmid,hist hrmax\n"); sep = ","; case BRIEF: printf("%d%s%d%s" "%d%s%d%s" "%d%s%d%s%d\n\n", b[0], sep, b[1], sep, b[2], sep, b[3], sep, b[4], sep, b[5], sep, b[6]); break; case VERBOSE: printf("hrlow: %d hrhigh: %d\n" "pers hrmin: %d pers hrmax: %d\n" "hist hrlow: %d hist hrmid: %d hist hrmax: %d\n\n", b[0], b[1], b[2], b[3], b[4], b[5], b[6]); break; case XML: printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); printf("%s%d\n", indents, b[0]); printf("%s%d\n", indents, b[1]); printf("%s%d\n", indents, b[2]); printf("%s%d\n", indents, b[3]); printf("%s%d\n", indents, b[4]); printf("%s%d\n", indents, b[5]); printf("%s%d\n", indents, b[6]); sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s\n", indents); } free(b); } if (opmode == XML) { sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s
\n", indents); printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); } if (deletelog) { /* input should look like this: */ /* all or */ /* 1 5 6 or */ /* 1,2,3 */ for (i = 0; i < 30; i++) settings[i] = (uchar)0; for (i = 0; i < 30; i++) { p = strtok(i ? 0 : deletelog, " ,"); if (p) { /* TODO not implemented if (!strcmp(p, "all")) { deleteall = 1; break; } */ v = strtol(p, &ep, 10); if (ep && !*ep) settings[i] = (uchar)v; else { fprintf(stderr, "bad setting #%d\n", i); exit(1); } } else break; } /* should be no more tokens */ p = strtok(0, " ,"); if (p) { fprintf(stderr, "extraneous settings\n"); exit(1); } if (deleteall) { b = cmd2(DLOG, 0, &l); DUMP("dlogall rtn", b, l) free(b); } else { DUMP("deletelog", settings, i) b = cmdn(DLOG, settings, i, &l); DUMP("dlog rtn", b, l) free(b); } } if (readloghdrs) { WHERE b = mread(LOCLOGH, 30); DUMP("logptrs", b, 30) for (p = b; *p && p < b+30; p++) { i = 999; if (ac > 0) { for (i = 0; i < ac; i++) if (atoi(av[i]) == (p-b)+1) break; } if (i == ac) continue; if (opmode == XML) { printf("%s\n", indents, p-b+1); sprintf(indents, "%*s", ++indent*IMULT, ""); } readlog(*p, p-b+1, 0); if (opmode == XML) { sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s\n", indents); } } free(b); } if (readlogdata) { WHERE b = mread(LOCLOGH, 30); DUMP("logptrs", b, 30) for (p = b; *p && p < b+30; p++) { i = 999; if (ac > 0) { for (i = 0; i < ac; i++) if (atoi(av[i]) == (p-b)+1) break; } if (i == ac) continue; s.npages = 1; /* 1 page for header */ s.nbeats = 0; s.beatbits = 0; s.buffbits = 0; s.hrtime = 0; if (opmode == XML) { printf("%s\n", indents, p-b+1); sprintf(indents, "%*s", ++indent*IMULT, ""); } readlog(*p, p-b+1, 1); if (stats) { sep = " "; switch (opmode) { case CSV: printf("pages,beats,buffbits," "beatbits,hrtime,bits/beat,totbits/beat,avgHR\n"); sep = ","; case BRIEF: printf("%ld%s%ld%s%ld%s%ld%s%ld%s%.1f%s%.1f%s%d\n\n", s.npages, sep, s.nbeats, sep, s.buffbits, sep, s.beatbits, sep, s.hrtime/1000, sep, s.nbeats > 0 ? (float)s.beatbits/ (float)s.nbeats : 0.0, sep, s.nbeats > 0 ? (float)s.buffbits/ (float)s.nbeats : 0.0, sep, s.nbeats > 0 ? s.nbeats*60*1000/s.hrtime : 0); break; case VERBOSE: printf("stats\npages: %ld beats: %ld\n" "buffbits: %ld beatbits: %d hrtime: %ld\n" "bits/beat: %.1f totbits/beat: %.1f avgHR: %d\n\n", s.npages, s.nbeats, s.buffbits, s.beatbits, s.hrtime/1000, s.nbeats > 0 ? (float)s.beatbits/ (float)s.nbeats : 0.0, s.nbeats > 0 ? (float)s.buffbits/ (float)s.nbeats : 0.0, s.nbeats > 0 ? s.nbeats*60*1000/s.hrtime : 0); break; case XML: printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); printf("%s%ld\n", indents, s.npages); printf("%s%ld\n", indents, s.nbeats); printf("%s%ld\n", indents, s.buffbits); printf("%s%ld\n", indents, s.beatbits); printf("%s%ld\n", indents, s.beatbits); if (s.nbeats > 0) printf("%s%.1f" "\n", indents, (float)s.beatbits/ (float)s.nbeats); printf("%s%.1f" "\n", indents, (float)s.buffbits/ (float)s.nbeats); printf("%s%d\n", indents, s.nbeats*60*1000/s.hrtime); sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s\n", indents); } } if (opmode == XML) { sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s\n", indents); } } free(b); } if (opmode == XML) { sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s\n", indents); printf("
\n"); } } /* read log data in a given page */ void readlog(uchar page, uint logno, uint readdata) { uchar *p; uchar *b; ulong date, time; uint interval; ulong duration; uchar altpage, hrpage, dstpage; uchar laps; ushort asc,asctime,dsc,dsctime; short minalt,maxalt; ulong mintime, maxtime; uchar hrmin,hrmax,hrav,hrhigh,hrlow; ushort tlow,tin,thigh,dist; ulong durtime; ushort o = 0; long alt, dst; char *sep; int ends; ushort i, count; ushort *alogdata, *dlogdata, *hrlogdata; uint aread, dread, hrread; if (d) fprintf(stderr, "readlog %d\n", (int)page); WHERE if (d) fprintf(stderr, "rlmread %lx %d\n", PADDR(page), HDRSIZE); b = mread(PADDR(page), HDRSIZE); DUMP("loghdr", b, HDRSIZE) date = *(b+o+2)+*(b+o+1)*100+*(b+o)*10000+20000000; o += 3; time = *(b+o+2)+*(b+o+1)*100+*(b+o)*10000; o += 3; interval = *(b+o++); altpage = *(b+o++); hrpage = *(b+o++); dstpage = *(b+o++); duration = *(b+o+3)*10+*(b+o+2)*100+*(b+o+1)*10000+*(b+o)*1000000; durtime = *(b+o+3)*10+*(b+o+2)*100+*(b+o+1)*6000+*(b+o)*360000; o += 4; laps = *(b+o++); asc = ntohs(*(ushort *)(b+o)); o += 2; asctime = ntohs(*(ushort *)(b+o)); o += 2; dsc = ntohs(*(ushort *)(b+o)); o += 2; dsctime = ntohs(*(ushort *)(b+o)); o += 2; maxalt = ntohs(*(ushort *)(b+o)); o += 2; maxtime = *(b+o+2)+*(b+o+1)*100+*(b+o)*10000; o += 3; minalt = ntohs(*(ushort *)(b+o)); o += 2; mintime = *(b+o+2)+*(b+o+1)*100+*(b+o)*10000; o += 3; hrmin = *(b+o++); hrmax = *(b+o++); hrav = *(b+o++); hrhigh = *(b+o++); hrlow = *(b+o++); tlow = ntohs(*(ushort *)(b+o)); o += 2; tin = ntohs(*(ushort *)(b+o)); o += 2; thigh = ntohs(*(ushort *)(b+o)); o += 2; dist = ntohs(*(ushort *)(b+o)); o += 2; sep = " "; switch (opmode) { case CSV: sep = ","; printf("log,date,time,interval,duration,laps,asc,asctime," "dsc,dsctime,minalt," "mintime,maxalt,maxtime,hrmin,hrmax," "hrav,hrhigh,hrlow,tlow,tin,thigh,dist\n"); case BRIEF: printf("%d%s%ld%s%06d%s%d%s%g%s%d%s%d%s%d%s%d%s%d%s%d%s", logno, sep, date, sep, time, sep, interval, sep, (double)durtime/100, sep, laps, sep, asc, sep, asctime, sep, dsc, sep, dsctime, sep, minalt, sep); printf("%06d%s%d%s%06d%s%d%s%d%s%d%s%d%s%d%s%d%s%d%s%d%s%.1f\n\n", mintime, sep, maxalt, sep, maxtime, sep, hrmin, sep, hrmax, sep, hrav, sep, hrhigh, sep, hrlow, sep, tlow, sep, tin, sep, thigh, sep, (float)dist/100, sep); break; case VERBOSE: printf("log#: %d\n", logno); printf("date: %ld\n", date); printf("time: %06d\n", time); printf("interval: %d\n", interval); printf("duration: %06d.%d\n", duration/100, (duration%100)/10); printf("laps: %d\n", laps); printf("asc: %d\n", asc); printf("asctime: %02d:%02d:%02d\n", asctime*10/3600, asctime*10/60-(asctime*10/3600)*60, (asctime*10) % 60); printf("dsc: %d\n", dsc); printf("dsctime: %02d:%02d:%02d\n", dsctime*10/3600, dsctime*10/60-(dsctime*10/3600)*60, (dsctime*10) % 60); printf("minalt: %d\n", minalt); printf("maxalt: %d\n", maxalt); printf("hrmin: %d\n", hrmin); printf("hrmax: %d\n", hrmax); printf("hrav: %d\n", hrav); printf("hrhigh: %d\n", hrhigh); printf("hrlow: %d\n", hrlow); printf("tlow: %d\n", tlow); printf("tin: %d\n", tin); printf("thigh: %d\n", thigh); printf("dist: %.1f\n\n", (float)dist/100); break; case XML: printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); printf("%s%d\n", indents, date); printf("%s\n", indents, time); printf("%s%d\n", indents, interval); printf("%s%g\n", indents, (double)durtime/100); printf("%s%d\n", indents, laps); printf("%s%d\n", indents, asc); printf("%s%d\n", indents, asctime); printf("%s%d\n", indents, dsc); printf("%s%d\n", indents, dsctime); printf("%s%d\n", indents, minalt); printf("%s%d\n", indents, maxalt); printf("%s%d\n", indents, hrmin); printf("%s%d\n", indents, hrmax); printf("%s%d\n", indents, hrav); printf("%s%d\n", indents, hrhigh); printf("%s%d\n", indents, hrlow); printf("%s%d\n", indents, tlow); printf("%s%d\n", indents, tin); printf("%s%d\n", indents, thigh); printf("%s%.1f\n", indents, (float)dist/100); sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s\n", indents); break; } if (d) fprintf(stderr, "PAGES %x, %x, %x\n", altpage, hrpage, dstpage); if (!readdata) return; ends = readlaps(page, laps); count = 2+(durtime/100/interval); if (altpage > 0) alogdata = (ushort *)readpages(altpage, 2*count, &aread); if (dstpage > 0) dlogdata = (ushort *)readpages(dstpage, 2*count, &dread); if (hrpage > 0) { WHERE hrlogdata = (ushort *)readpages(hrpage, 0, &hrread); DUMP("hrlogdata1", (uchar *)hrlogdata, hrread) } WHERE if (d) fprintf(stderr, "xread %d %d %d %d\n", count, aread, dread, hrread); if (opmode == XML) { printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); } sep = " "; switch (opmode) { case CSV: sep = ","; if (altpage || dstpage) printf("time%s%s\n", altpage ? ",altitude" : "", dstpage ? ",distance" : ""); case BRIEF: if (altpage || dstpage) for (i=0; i < count-1; i++) { printf("%d", i*interval); if (altpage) { alt = ntohs(alogdata[i]); if (alt & 0x8000) alt -= 0x10000; printf("%s%d", sep, alt); } if (dstpage) { dst = ntohs(dlogdata[i]); if (dst & 0x8000) dst -= 0x10000; printf("%s%d", sep, dst); } printf("\n"); } printf("\n"); break; case VERBOSE: if (altpage || dstpage) { WHERE for (i=0; i < count-1; i++) { printf("time: %d", i*interval); if (altpage) { alt = ntohs(alogdata[i]); if (alt & 0x8000) alt -= 0x10000; printf(" alt %d", alt); } if (dstpage) { dst = ntohs(dlogdata[i]); if (dst & 0x8000) dst -= 0x10000; printf(" dst %d", dst); } printf("\n"); } printf("\n"); } break; case XML: WHERE if (altpage || dstpage) { WHERE for (i=0; i < count-1; i++) { printf("%s\n"); } } } if (altpage) { WHERE A(ntohs(alogdata[count-1]) == (ushort)-1); } if (dstpage) { WHERE A(ntohs(dlogdata[count-1]) == (ushort)-1); } if (hrpage > 0) { if (opmode == CSV) printf("beat,tottime,beattime\n"); DUMP("hrlog", (uchar *)hrlogdata, hrread) decode2(hrlogdata, hrread, durtime/100, ends); } if (opmode == XML) { sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s\n", indents); } if (altpage) free(alogdata); if (dstpage) free(dlogdata); if (hrpage) free(hrlogdata); free(b); } void decode2 (ushort *hrdata, uint len, int dur, int ends) { uint bitlen = 6; /* length of next value, starts at 6 */ uint wn = 0; /* current word number */ ushort buffbits = 0; /* no. of bits in bit buffer (read 16 at a time) */ uint maxpos = (1 << (bitlen-1))-1; /* max positive no. */ ushort nextword; /* next 16 bit word in input */ uint bbuffer = 0; /* bit buffer */ int time = 0; /* next calculated time */ ushort samp; /* next sample */ short val; /* normalised sample */ ushort msb, nmsb; /* top two bits of sample */ ushort spec; /* type of value (if special) */ ulong oldabs = 0, longabs = 0; /* absolute time of beats */ ulong abs = 0; /* absolute time of beat (wraps) */ ushort ndec = 0; /* number of short values seen */ char *sep = " "; /* field separator */ int isbeat; /* beat value or marker only value */ int oldwrapped = 0; /* whether time has wrapped for bug */ int wrapped = 0; /* whether time has wrapped for bug */ int ended = 1; /* value after END marker */ WHERE if (opmode == CSV) sep = ","; /* first value must be 1 */ A(1 == (nextword = ntohs(hrdata[wn++]))); for(;;) { while(buffbits < bitlen) { /* fill bit buffer if necessary */ nextword = ntohs(hrdata[wn++]); if (d) fprintf(stderr, "FILL %x (wn %d of %d)\n", nextword, wn, len/2); if (wn > len/2) { if (d) fprintf(stderr, "EOD nw %d %d %d bb \n", nextword, wn, len/2); printf("EOD\n"); return; } bbuffer |= nextword << buffbits; if (d) fprintf(stderr, "bbuff %lx sh %d\n", bbuffer, buffbits); buffbits += 16; s.buffbits += 16; } /* extract the next value */ val = samp = bbuffer & ((1 << bitlen)-1); /* top two bits are interesting */ msb = (samp & (1<<(bitlen-1))) >> (bitlen-1); nmsb = (samp & (1<<(bitlen-2))) >> (bitlen-2); if (d) fprintf(stderr, "msb %d %d %x\n", msb, nmsb, samp); /* normalise it */ if (msb) { /* it's negative */ val = samp - (1 << bitlen); } if (d) fprintf(stderr, "bbuff1 %lx sh %d\n", bbuffer, buffbits); /* adjust bit buffer */ buffbits -= bitlen; bbuffer >>= bitlen; if (d) fprintf(stderr, "bbuff2 %lx sh %d\n", bbuffer, buffbits); if (d) fprintf(stderr, "next val %x %x %x (%d) %x\n", nextword, samp, val, (val), maxpos); /* process special marker values */ spec = SNONE; if (samp == maxpos) spec = SMAX; else if (samp == maxpos+1) spec = SMAX1; else if (samp == maxpos+2) spec = SMAX2; else if (samp == maxpos-1) spec = SMAXM1; else if (samp == maxpos-2) spec = SMAXM2; isbeat = 0; switch (spec) { case SMAX: /* absolute time value follows instead of offset */ /* bit buffer is discarded */ /* time is read as next 16 bit value */ if (d) fprintf(stderr, "abs next val %x %x (%d)\n", samp, val, val); nextword = ntohs(hrdata[wn++]); A(wn <= len/2); /* reset bit buffer */ buffbits = 0; bbuffer = 0; /* doesn't get reset, but really should */ /* ndec = 0; */ oldabs = longabs&0xffff; longabs = abs = nextword; time = (longabs-oldabs); oldwrapped = wrapped; if (time < 0) { time += 0x10000; wrapped = 1; } else wrapped = 0; if (d) fprintf(stderr, "OLD1 %lx %lx wrap %d old %d\n", oldabs&0xffff0000, longabs&0xffff0000, wrapped, oldwrapped); if (ended) isbeat = 0; else isbeat = 1; ended = 0; if (d) fprintf(stderr, "ABS %x %x t: %d\n", nextword, abs, time); if (d) fprintf(stderr, "LOG1 %x %x %x %d %d %d\n", oldabs, longabs, abs, time, 0, spec); oldabs = longabs; break; case SMAXM1: /* increment bitlen only */ bitlen++; if (d) fprintf(stderr, "bit++ now %x\n", bitlen); maxpos = (1 << (bitlen-1))-1; ndec = 0; /* reset no. of times seen small val */ if (d) fprintf(stderr, "LOG2 %x %x %x %d %d %d\n", oldabs, longabs, abs, longabs-oldabs, 0, spec); break; case SMAX1: if (d) fprintf(stderr, "SPEC %d\n", spec); /* reset bit buffer */ buffbits = 0; bbuffer = 0; break; case SMAX2: case SMAXM2: if (d) fprintf(stderr, "SPEC %d\n", spec); if (d) fprintf(stderr, "LOG3 %x %x %x %d %d %d\n", oldabs, longabs, abs, longabs-oldabs, 0, spec); case SNONE: /* normal offset */ /* short value if 00pos or 11neg */ /* min bitlen is 4 */ /* decide whether to reduce no. of bits */ if ((msb == nmsb) && (bitlen > 4)) { ndec++; if (d) fprintf(stderr, "wdec %d\n", ndec); if (ndec == 5) { /* decrement bitlen if see 5 short */ bitlen--; if (d) fprintf(stderr, "dec %d\n", bitlen); maxpos = (1 << (bitlen-1))-1; ndec = 0; } } else ndec = 0; if (d) fprintf(stderr, "norm %d %d\n", abs, val); if (d) fprintf(stderr, "time1 %d %d\n", time, val); time = longabs-oldabs+val; if (time == -1) { ends--; if (d) fprintf(stderr, "time2 %d %d\n", time, val); if (d) fprintf(stderr, "END1 %d\n", ends); if (ends <= 0) return; ended = 1; isbeat = 0; } else { if (time < 0) time += 0x10000; else if (time > 0xffff) time -= 0x10000; if (ended) { isbeat = 0; ended = 0; } else isbeat = 1; } oldabs = longabs; abs += time; longabs += time; abs &= 0xffff; oldwrapped = wrapped; wrapped = ((oldabs&0xffff0000) != (longabs&0xffff0000)); if (d) fprintf(stderr, "OLD %lx %lx wrap %d old %d\n", oldabs&0xffff0000, longabs&0xffff0000, wrapped, oldwrapped); if (d) fprintf(stderr, "time2 %d %d\n", time, val); if (d) fprintf(stderr, "LOG4 %x %x %x %d %d %d\n", oldabs, longabs, abs, time, 0, spec); break; default: if (d) fprintf(stderr, "SPEC %d\n", spec); if (d) fprintf(stderr, "LOG5 %x %x %x %d %d %d\n", oldabs, longabs, abs, longabs-oldabs, 0, spec); } if (isbeat) { s.nbeats++; s.beatbits += bitlen; switch(opmode) { case VERBOSE: printf("beat "); case CSV: case BRIEF: printf("%ld%s%.1f%s%d\n", s.nbeats, sep, (float)s.hrtime/1000, sep, time-wrapped); break; case XML: printf("%s\n", indents, time); } s.hrtime += time; } } } int readlaps(uchar page, uchar laps) { uchar l; uchar cp = page; uchar *b; uchar end, hr, hrav; ulong time,rawtime; short asc, dsc; short alt, dist; uchar lo=1; ushort off = LAP1OFF; uint o; char *sep; int ends = 0; WHERE if (laps > 100) { fprintf(stderr, "laps %u > 100\n", laps); laps = 100; } sep = " "; if (opmode == CSV) { sep = ","; printf("lap,end,time,alt,dsc,asc,hr,hrav,dist\n"); } else if (opmode == XML) { printf("%s\n", indents); sprintf(indents, "%*s", ++indent*IMULT, ""); } for (l = 1; l < laps+1; l++) { b = mread(PADDR(page)+off+(l-lo)*LAPSIZE, LAPSIZE); o = 0; end = *(b+o++); if (end == 1) ends++; rawtime = *(b+o+3)*10+*(b+o+2)*100+*(b+o+1)*6000+*(b+o)*360000; time = *(b+o+3)*10+*(b+o+2)*100+*(b+o+1)*10000+*(b+o)*1000000; o += 4; alt = ntohs(*(ushort *)(b+o)); o += 2; asc = ntohs(*(ushort *)(b+o)); o += 2; dsc = ntohs(*(ushort *)(b+o)); o += 2; hr = *(b+o++); hrav = *(b+o++); dist = ntohs(*(ushort *)(b+o)); o += 2; switch (opmode) { case CSV: case BRIEF: printf("%d%s%d%s%g%s%d%s%d%s%d%s%d%s%d%s%.1f\n", l, sep, end, sep, (double)rawtime/100, sep, alt, sep, asc, sep, dsc, sep, hr, sep, hrav, sep, (float)dist/100); break; case VERBOSE: printf("lap %d end %d time %08.1f " "alt %d asc %d dsc %d " "hr %d hrav %d dist %.1f\n", l, end, (float)time/100, alt, asc, dsc, hr, hrav, (float)dist/100); break; case XML: printf("%s\n", indents, l, end, (double)rawtime/100, alt, asc, dsc, hr, hrav, (float)dist/100); } free(b); if (l == 30 || l == 64 || l == 98) { lo = l+1; off = LAP2OFF; b = mread(PADDR(page)+0x1ff,1); page = *b; free(b); if (!page) { fprintf(stderr, "bad lap page %l\n", l); break; } } } if (opmode == XML) { sprintf(indents, "%*s", --indent*IMULT, ""); printf("%s\n", indents); } else printf("\n"); return ends; } uchar * readpages(uchar page, uint count, uint *nr) { uchar *b, *rb, *ra; uint nread; uint c = count; uchar *p; WHERE *nr = 0; if (!count) c = PGDATA; ra = rb = malloc(c); while (c > 0) { if (d) fprintf(stderr, "rp %d %d %d\n", page, count, c); A(page); b = mread(PADDR(page), 512); DUMP("PAGE", b, 512); s.npages++; nread = (c > PGDATA ? PGDATA : c); if (d) fprintf(stderr, "page %d %d <-> %d\n", page, *b, *(b+511)); memmove(ra, b+1, nread); free(b); page = *(b+511); *nr += nread; DUMP("RA", rb, *nr); ra += nread; c -= nread; if (!count && page) { c = PGDATA; rb = (void *)realloc(rb, (*nr)+PGDATA); ra = rb+*nr; } } WHERE if (d) fprintf(stderr, "FINISHED readpages\n"); return rb; } void mwrite(uint addr, uchar *buf, int count) { int l; uint nwrite; uchar *b; uchar *p; while (count > 0) { nwrite = (count > RWMAX ? RWMAX : count); /* TODO doesn't handle write across bank boundary */ if (addr > 0xffff) { b = cmd2221n(MWRITE2, 5+nwrite, addr/0x10000, addr % 0x10000, nwrite, buf, nwrite, &l); if (ntohs(*(ushort *)(b+0)) != addr/0x10000 || ntohs(*(ushort *)(b+2)) != addr % 0x10000 || *(b+4) != nwrite) { fprintf(stderr, "BAD WRITE %d %d\n", nwrite, l); WHERE DUMP("BAD READ1", b, l); exit(1); } addr += nwrite; buf += nwrite; count -= nwrite; free(b); } else { b = cmd221n(MWRITE, 3+nwrite, addr, nwrite, buf, nwrite, &l); if (ntohs(*(ushort *)(b+0)) != addr || *(b+2) != nwrite) { fprintf(stderr, "BAD WRITE %d %d %d\n", nwrite, l, (int)*(b+2)); WHERE DUMP("BAD WRITE2", b, l); exit(1); } addr += nwrite; buf += nwrite; count -= nwrite; free(b); } } } uchar * mread(uint addr, int count) { uchar *b, *rb, *ra; uint nread; int l; uchar *p; WHERE if (d) fprintf(stderr, "mreading %lx %d\n", addr, count); ra = rb = malloc(count); while (count > 0) { nread = (count > RWMAX ? RWMAX : count); if ((addr/0x10000) != ((addr + nread-1)/0x10000)) { /* cross bank boundary */ nread = ((addr+nread) & 0xffff0000)-addr; if (d) fprintf(stderr, "bumped nread %d\n", nread); } if (addr > 0xffff) { b = cmd2221(MREAD2, 5, addr/0x10000, addr % 0x10000, nread, &l); if (ntohs(*(ushort *)(b+0)) != addr/0x10000 || ntohs(*(ushort *)(b+2)) != addr % 0x10000 || *(b+4) != nread) { fprintf(stderr, "BAD READ %d %d\n", nread, l); WHERE DUMP("BAD READ1", b, l); exit(1); } memmove(ra, b+5, l-5); ra += l-5; addr += l-5; count -= l-5; free(b); } else { b = cmd221(MREAD, 3, addr, nread, &l); if (ntohs(*(ushort *)(b+0)) != addr || *(b+2) != nread) { fprintf(stderr, "BAD READ %d %d %d\n", nread, l, (int)*(b+2)); WHERE DUMP("BAD READ2", b, l); exit(1); } memmove(ra, b+3, l-3); ra += l-3; addr += l-3; count -= l-3; free(b); } if (d) fprintf(stderr, "mread %d %d %d\n", count, nread, l); } return rb; } void flushfd(void) { uchar buf[20]; int nr; WHERE while (20 == (nr = read(fd, buf, 20))) ; if (d) fprintf(stderr, "flushed %d\n", nr); } uchar * cmd(uchar *b, uint c, int *rl) { uchar *buf[10]; uchar *r[10]; int len[10]; int nr; uchar *p; uint l; uint nreads; int toget; int i; int success; int fails = 0; WHERE addck(b, c); for (;;) { for (i = 0; i < repeats; i++) A(buf[i] = malloc(20)); /* minimum return len */ /* repeat each command "repeats" times and compare results */ for (i = 0; i < repeats; i++) { for (;;) { msend(b, c); nr = read(fd, buf[i], 3); /* read cmd and len */ if (nr <= 0) { if (d) fprintf(stderr, "read fail\n"); flushfd; continue; } if (*(buf[i]) != *b) { printf("bad cmd read: %d %d\n", *buf[i], nr); flushfd; continue; } WHERE if (d) fprintf(stderr, "read %d %d\n", *buf[i], nr); len[i] = nr; nreads = 0; while (nreads < 3 && len[i] < 3) { S(nr = read(fd, buf[i]+len[i], 3-len[i])); len[i] += nr; nreads++; } if (nreads > 3) { WHERE fprintf(stderr, "abandoned\n"); continue; } toget = ntohs(*(ushort *)(buf[i]+1))+4; if (d) fprintf(stderr, "toget %d\n", toget); A(buf[i] = realloc((void *)buf[i], toget)); nreads = 0; while (nreads < 3 && len[i] < toget) { S(nr = read(fd, buf[i]+len[i], toget-len[i])); nreads++; len[i] += nr; } if (nreads > 3) { WHERE fprintf(stderr, "abandoned\n"); continue; } if (chkck(buf[i], len[i])) { WHERE if(d) fprintf(stderr, "cmd return %d\n", len[i]); len[i] -= 4; DUMP("cmd1", buf[i], len[i]) memmove(buf[i], buf[i]+3, len[i]); DUMP("cmd2", buf[i], len[i]) WHERE r[i] = buf[i]; break; } } } WHERE /* check comparison */ success = 1; for (i = 1; i < repeats; i++) { WHERE if (len[0] != len[i]) { fprintf(stderr, "diff len %d %d(%d)\n", len[0], len[1], i); success = 0; } else if (memcmp(buf[0], buf[i], len[0])) { fprintf(stderr, "diff data %d\n", i); success = 0; DUMP("buf0", buf[0], len[0]); DUMP("buf1", buf[i], len[i]); } free(buf[i]); } if (success) { if (d) fprintf(stderr, "ok data\n"); *rl = len[0]; return buf[0]; } else { fails++; if (fails > 3) { fprintf(stderr, "multiple failures. exiting\n"); exit(1); } } } } uchar * cmdn(uint c, uchar *b, uint n, int *rl) { uchar *buf; WHERE A(buf = malloc(1+2+n+1)); *(uchar *)buf = c; *(ushort *)(buf+1) = htons(n); memmove(buf+3, b, n); return cmd(buf, 1+2+n+1, rl); } uchar * cmd2(uint c, uint a1, int *rl) { uchar *buf; WHERE A(buf = malloc(1+2+1)); *(uchar *)buf = c; *(ushort *)(buf+1) = htons(a1); return cmd(buf, 1+2+1, rl); } uchar * cmd221(uint c, uint a1, uint a2, uint a3, int *rl) { uchar *buf; WHERE A(buf = malloc(1+2+2+1+1)); *(uchar *)buf = c; *(ushort *)(buf+1) = htons(a1); *(ushort *)(buf+3) = htons(a2); *(uchar *)(buf+5) = a3; return cmd(buf, 1+2+2+1+1, rl); } uchar * cmd221n(uint c, uint a1, uint a2, uint a3, uchar *b, uint n, int *rl) { uchar *buf; WHERE A(buf = malloc(1+2+2+1+1)); *(uchar *)buf = c; *(ushort *)(buf+1) = htons(a1); *(ushort *)(buf+3) = htons(a2); *(uchar *)(buf+5) = a3; memmove(buf+6, b, n); return cmd(buf, 1+2+2+1+n+1, rl); } uchar * cmd2221(uint c, uint a1, uint a2, uint a3, uint a4, int *rl) { uchar *buf; WHERE A(buf = malloc(1+2+2+2+1+1)); *(uchar *)buf = c; *(ushort *)(buf+1) = htons(a1); *(ushort *)(buf+3) = htons(a2); *(ushort *)(buf+5) = htons(a3); *(uchar *)(buf+7) = a4; return cmd(buf, 1+2+2+2+1+1, rl); } uchar * cmd2221n(uint c, uint a1, uint a2, uint a3, uint a4, uchar *b, uint n, int *rl) { uchar *buf; WHERE A(buf = malloc(1+2+2+2+1+1)); *(uchar *)buf = c; *(ushort *)(buf+1) = htons(a1); *(ushort *)(buf+3) = htons(a2); *(ushort *)(buf+5) = htons(a3); *(uchar *)(buf+7) = a4; memmove(buf+8, b, n); return cmd(buf, 1+2+2+2+1+n+1, rl); } void addck(uchar *buf, uint count) { uchar ck = 0; uchar *p; WHERE for (p = buf; p < buf+count-1; p++) ck ^= *p; *p = ck; } int chkck(uchar *buf, uint count) { uchar ck = 0; uchar *p; WHERE DUMP("cksum", buf, count) for (p = buf; p < buf+count-1; p++) ck ^= *p; if (*p != ck) { fprintf(stderr, "Checksum failed %d %02x %02x\n", *p, ck); return 0; /*exit(1);*/ } else return 1; } void msend(uchar *buf, uint count) { uchar *p; WHERE DUMP("sending", buf, count) S(write(fd, buf, count)); } void usage(void) { fprintf(stderr, "Usage: %s -f device \n" "[ -m \"hexstart length\" ] -- dump memory \n" "[ -M \"hexstart data\" ] -- write memory \n" "[ -r ] -- read settings\n" "[ -w \"settings\"] -- write settings (output from -Br)\n" "[ -h ] -- read history\n" "[ -H ] -- reset history\n" "[ -o ] -- read hr settings\n" "[ -O \"settings\"] -- write hr settings\n" "[ -S ] -- output log stats\n" "[ -s ] -- read serial number\n" "[ -l ] -- read log data\n" "[ -L ] -- read log headers\n" "[ -D \"n [m] ...\"] -- delete specific logs\n" "[ -c ] -- read calibration factors\n" "[ -n \"a [b [c]]]\" -- write calibration factors\n" "[ -v ] -- read versions\n" "[ -V ] -- saVe settings and memory dump\n" "[ -X ] -- output XML\n" "[ -B ] -- output brief\n" "[ -C ] -- output CSV\n" "[ -a ] -- read config -rhoscv\n" "[ logn [logm] ... ] -- specific log numbers\n", progname ); exit(1); } /* vim:se noic ai nows:*/