summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2018-11-07 21:28:37 -0500
committerJune McEnroe <june@causal.agency>2018-11-07 21:28:37 -0500
commit507aced2e116a6acb6ffb83acddfab5545d40116 (patch)
treecb08651b4f4f9ae6cc672cebc31b32ac28942810
parentAdd Low — Double Negative (diff)
downloadsrc-507aced2e116a6acb6ffb83acddfab5545d40116.tar.gz
src-507aced2e116a6acb6ffb83acddfab5545d40116.zip
Implement all of MSR606
-rw-r--r--bin/man/msr606.1105
-rw-r--r--bin/msr606.c318
2 files changed, 294 insertions, 129 deletions
diff --git a/bin/man/msr606.1 b/bin/man/msr606.1
new file mode 100644
index 00000000..fc18635b
--- /dev/null
+++ b/bin/man/msr606.1
@@ -0,0 +1,105 @@
+.Dd November 7, 2018
+.Dt MSR606 1
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm msr606
+.Nd magnetic stripe card reader/writer
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl RTWchiltwz
+.Op Fl L Ar led
+.Op Fl Z Ar zero
+.Op Fl b Ar bpc
+.Op Fl e Ar track
+.Op Fl f Ar tty
+.
+.Sh DESCRIPTION
+.Nm
+operates the MSR606 magnetic stripe card reader/writer.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.
+.It Fl L Ar led
+Set LEDs.
+The values for
+.Ar led
+are:
+.Sy n
+for none,
+.Sy a
+for all,
+.Sy g
+for green,
+.Sy y
+for yellow,
+.Sy r
+for red.
+.
+.It Fl R
+Read raw data from a card to standard output.
+.
+.It Fl T
+Perform a sensor test.
+.
+.It Fl W
+Write raw data to a card from standard input.
+.
+.It Fl Z Ar zero
+Set leading zero.
+.Ar zero
+is a comma-separated list of two numbers
+indicating the leading zero setting
+for tracks 1 & 3
+and track 2,
+respectively.
+.
+.It Fl b Ar bpc
+Set bits per character for each track.
+.Ar bpc
+is a comma-separated list of 3 numbers
+indicating the BPC for each track.
+Valid values range from 5 to 8.
+.
+.It Fl c
+Print the coercivity status.
+.
+.It Fl e Ar track
+Erase a card.
+.Ar track
+is a number from 1 to 7
+representing a 3-bit set
+of tracks to erase.
+.
+.It Fl f Ar tty
+Open the device
+.Ar tty .
+The default device is
+.Pa /dev/ttyUSB0 .
+.
+.It Fl h
+Set high coercivity.
+.
+.It Fl i
+Reset the device.
+.
+.It Fl l
+Set low coercivity.
+.
+.It Fl r
+Read ISO format data from card to standard output.
+.
+.It Fl t
+Perform communication and RAM tests.
+Print the hardware model and firmware version.
+This is the default operation.
+.
+.It Fl w
+Write ISO format data to card from standard input.
+.
+.It Fl z
+Print the leading zero setting.
+.El
diff --git a/bin/msr606.c b/bin/msr606.c
index 5b74bed9..9ea8b94e 100644
--- a/bin/msr606.c
+++ b/bin/msr606.c
@@ -16,199 +16,259 @@
 
 #include <err.h>
 #include <fcntl.h>
-#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sysexits.h>
 #include <termios.h>
 #include <unistd.h>
 
-static void writeAll(int fd, const uint8_t *ptr, size_t size) {
-	while (size) {
-		ssize_t ret = write(fd, ptr, size);
-		if (ret < 0) err(EX_IOERR, "write");
-		ptr += ret;
-		size -= ret;
-	}
+enum {
+	Esc = 0x1B,
+	FS  = 0x1C,
+};
+
+static const char *path = "/dev/ttyUSB0";
+static FILE *tty;
+
+static char ttyGet(void) {
+	int ch = fgetc(tty);
+	if (ferror(tty)) err(EX_IOERR, "%s", path);
+	if (ch == EOF) errx(EX_PROTOCOL, "unexpected EOF");
+	return ch;
 }
-static void readAll(int fd, uint8_t *ptr, size_t size) {
-	while (size) {
-		ssize_t ret = read(fd, ptr, size);
-		if (ret < 0) err(EX_IOERR, "read");
-		if (!ret) errx(EX_DATAERR, "unexpected eof");
-		ptr += ret;
-		size -= ret;
-	}
+
+static void ttyPut(char ch) {
+	fputc(ch, tty);
+	if (ferror(tty)) err(EX_IOERR, "%s", path);
 }
 
-static void req2(int fd, uint8_t c) {
-	uint8_t buf[] = { 0x1B, c };
-	writeAll(fd, buf, 2);
+static void msrReq(char req) {
+	ttyPut(Esc);
+	ttyPut(req);
+}
+static char msrRes(void) {
+	char esc = ttyGet();
+	if (esc != Esc) errx(EX_PROTOCOL, "response fail %hhX", esc);
+	return ttyGet();
 }
-static uint8_t res2(int fd) {
-	uint8_t buf[2];
-	readAll(fd, buf, 2);
-	if (buf[0] != 0x1B) errx(EX_PROTOCOL, "response fail %hhX", buf[0]);
-	return buf[1];
+static char msr(char req) {
+	msrReq(req);
+	return msrRes();
 }
 
-static void req3(int fd, uint8_t c, uint8_t d) {
-	uint8_t buf[] = { 0x1B, c, d };
-	writeAll(fd, buf, 3);
+static void msrReset(void) {
+	msrReq('a');
 }
-static struct Res3 { uint8_t c, d; } res3(int fd) {
-	uint8_t buf[3];
-	readAll(fd, buf, 3);
-	if (buf[0] != 0x1B) errx(EX_PROTOCOL, "response fail %hhX", buf[0]);
-	return (struct Res3) { buf[1], buf[2] };
+
+static void msrTest(void) {
+	char test = msr('e');
+	if (test != 'y') errx(EX_PROTOCOL, "test fail %hhX", test);
+
+	char ram = msr(0x87);
+	if (ram != '0') errx(EX_PROTOCOL, "ram fail %hhX", ram);
+
+	msrReq('t');
+	printf("model: %c%c\n", msrRes(), ttyGet());
+
+	printf("firmware: %hhu\n", msr('v'));
 }
 
-static void reset(int tty) {
-	req2(tty, 'a');
+static void msrSensorTest(void) {
+	char test = msr(0x86);
+	if (test != '0') errx(EX_PROTOCOL, "sensor fail %hhX", test);
 }
 
-static void test(int tty) {
-	uint8_t res;
+static void msrLED(char led) {
+	switch (led) {
+		break; case 'a': msrReq(0x82);
+		break; case 'g': msrReq(0x83);
+		break; case 'y': msrReq(0x84);
+		break; case 'r': msrReq(0x85);
+		break; default:  msrReq(0x81);
+	}
+}
 
-	req2(tty, 'e');
-	res = res2(tty);
-	if (res != 'y') errx(EX_PROTOCOL, "test fail %hhX", res);
+static void msrSetZero(char track1, char track2) {
+	msrReq('z');
+	ttyPut(track1);
+	ttyPut(track2);
+	char zero = msrRes();
+	if (zero != '0') errx(EX_PROTOCOL, "set leading zero fail %hhX", zero);
+}
 
-	req2(tty, 0x87);
-	res = res2(tty);
-	if (res != '0') errx(EX_PROTOCOL, "ram fail %hhX", res);
+static void msrGetZero(void) {
+	char track1 = msr('l');
+	char track2 = ttyGet();
+	printf("leading zero: %hhu,%hhu\n", track1, track2);
+}
 
-	req2(tty, 't');
-	struct Res3 model = res3(tty);
-	printf("model: %c%c\n", model.c, model.d);
+static void msrSetBPI(char track1, char track2, char track3) {
+	msrReq('b');
+	switch (track) {
+		break; case 1: ttyPut(bpi == 210 ? 0xA1 : 0xA0);
+		break; case 2: ttyPut(bpi == 210 ? 0xD2 : 0x4B);
+		break; case 3: ttyPut(bpi == 210 ? 0xC1 : 0xC0);
+		break; default: ttyPut(0);
+	}
+	char set = msrRes();
+	if (set != '0') errx(EX_PROTOCOL, "set BPI fail %hhX", set);
+}
 
-	req2(tty, 'v');
-	res = res2(tty);
-	printf("firmware: %hhu\n", res);
+static void msrSetBPC(char track1, char track2, char track3) {
+	msrReq('o');
+	ttyPut(track1);
+	ttyPut(track2);
+	ttyPut(track3);
+	char bpc = msrRes();
+	if (bpc != '0') errx(EX_PROTOCOL, "set BPC fail %hhX", bpc);
+	track1 = ttyGet();
+	track2 = ttyGet();
+	track3 = ttyGet();
+	printf("BPC: %hhu,%hhu,%hhu\n", track1, track2, track3);
 }
 
-static void sensorTest(int tty) {
-	req2(tty, 0x86);
-	uint8_t res = res2(tty);
-	if (res != '0') errx(EX_PROTOCOL, "sensor fail %hhX", res);
+static void msrHiCo(void) {
+	char co = msr('x');
+	if (co != '0') errx(EX_PROTOCOL, "hi-co fail %hhX", co);
 }
 
-static void led(int tty, char c) {
-	switch (c) {
-		break; case 'a': req2(tty, 0x82);
-		break; case 'g': req2(tty, 0x83);
-		break; case 'y': req2(tty, 0x84);
-		break; case 'r': req2(tty, 0x85);
-		break; default:  req2(tty, 0x81);
+static void msrLoCo(void) {
+	char co = msr('y');
+	if (co != '0') errx(EX_PROTOCOL, "lo-co fail %hhX", co);
+}
+
+static void msrGetCo(void) {
+	char co = msr('d');
+	switch (co) {
+		break; case 'h': printf("hi-co\n");
+		break; case 'l': printf("lo-co\n");
+		break; default:  errx(EX_PROTOCOL, "get co fail %hhX", co);
 	}
 }
 
-static void status(int tty) {
-	uint8_t res = res2(tty);
-	switch (res) {
-		break; case '0': return;
-		break; case '1': errx(EX_DATAERR, "read/write error");
-		break; case '2': errx(EX_DATAERR, "command format error");
-		break; case '4': errx(EX_DATAERR, "invalid command");
-		break; case '9': errx(EX_DATAERR, "invalid card swipe");
-		break; default:  errx(EX_PROTOCOL, "status fail %hhX", res);
+static void msrStatus(void) {
+	char status = msrRes();
+	switch (status) {
+		case '0': return;
+		case '1': errx(EX_DATAERR, "read/write error");
+		case '2': errx(EX_DATAERR, "command format error");
+		case '4': errx(EX_DATAERR, "invalid command");
+		case '9': errx(EX_DATAERR, "invalid card swipe");
+		default: errx(EX_PROTOCOL, "status fail %hhX", status);
 	}
 }
 
-static void readCard(int tty) {
-	req2(tty, 'r');
-	uint8_t res = res2(tty);
-	if (res != 's') errx(EX_PROTOCOL, "read fail %hhX", res);
+static void msrRead(void) {
+	char read = msr('r');
+	if (read != 's') errx(EX_PROTOCOL, "read fail %hhX", read);
 	for (;;) {
-		readAll(tty, &res, 1);
-		if (res == '?') {
-			readAll(tty, &res, 1);
-			if (res == 0x1C) break;
+		read = ttyGet();
+		if (read == '?') {
+			read = ttyGet();
+			if (read == FS) break;
 			printf("?");
-		} else {
-			printf("%c", res);
 		}
+		printf("%c", read);
 	}
-	status(tty);
+	msrStatus();
 }
 
-static void writeCard(int tty) {
-	req2(tty, 'w');
-	req2(tty, 's');
-	uint8_t buf[2];
-	ssize_t size;
-	while (0 < (size = read(STDIN_FILENO, buf, 2))) {
-		writeAll(tty, buf, 2);
+static void msrWrite(void) {
+	msrReq('w');
+	msrReq('s');
+	int write;
+	while (EOF != (write = getchar())) {
+		ttyPut(write);
 	}
-	if (size < 0) err(EX_IOERR, "(stdin)");
-	buf[0] = '?';
-	buf[1] = 0x1C;
-	writeAll(tty, buf, 2);
-	status(tty);
+	ttyPut('?');
+	ttyPut(FS);
+	msrStatus();
 }
 
-static void eraseCard(int tty, char c) {
-	req3(tty, 'c', c - '0');
-	uint8_t res = res2(tty);
-	if (res != '0') errx(EX_PROTOCOL, "erase fail %hhX", res);
+static void msrErase(char track) {
+	msrReq('c');
+	ttyPut(track);
+	char erase = msrRes();
+	if (erase != '0') errx(EX_PROTOCOL, "erase fail %hhX", erase);
 }
 
-static void setHiCo(int tty) {
-	req2(tty, 'x');
-	uint8_t res = res2(tty);
-	if (res != '0') errx(EX_PROTOCOL, "hi-co fail %hhX", res);
-}
-static void setLoCo(int tty) {
-	req2(tty, 'y');
-	uint8_t res = res2(tty);
-	if (res != '0') errx(EX_PROTOCOL, "lo-co fail %hhX", res);
+static void msrReadRaw(void) {
+	char read = msr('m');
+	if (read != 's') errx(EX_PROTOCOL, "read raw fail %hhX", read);
+	for (;;) {
+		read = ttyGet();
+		if (read == '?') {
+			read = ttyGet();
+			if (read == FS) break;
+			printf("?");
+		}
+		printf("%c", read);
+	}
+	msrStatus();
 }
-static void getCo(int tty) {
-	req2(tty, 'd');
-	uint8_t res = res2(tty);
-	switch (res) {
-		break; case 'h': printf("hi-co\n");
-		break; case 'l': printf("lo-co\n");
-		break; default:  errx(EX_PROTOCOL, "get co fail %hhX", res);
+
+static void msrWriteRaw(void) {
+	msrReq('n');
+	msrReq('s');
+	int write;
+	while (EOF != (write = getchar())) {
+		ttyPut(write);
 	}
+	ttyPut('?');
+	ttyPut(FS);
+	msrStatus();
+}
+
+static char parse(char **arg) {
+	char n = strtoul(*arg, arg, 0);
+	if ((*arg)[0]) (*arg)++;
+	return n;
 }
 
 int main(int argc, char *argv[]) {
-	const char *file = "/dev/ttyUSB0";
 	char func = 't';
-	const char *arg = NULL;
+	char *arg = NULL;
 
 	int opt;
-	while (0 < (opt = getopt(argc, argv, "L:RTce:f:hlrtw"))) {
+	while (0 < (opt = getopt(argc, argv, "L:RTWZ:b:cd:e:f:hilrtwz"))) {
 		switch (opt) {
-			break; case 'f': file = optarg;
+			break; case 'f': path = optarg;
 			break; case '?': return EX_USAGE;
 			break; default:  func = opt; arg = optarg;
 		}
 	}
 
-	int tty = open(file, O_RDWR);
-	if (tty < 0) err(EX_NOINPUT, "%s", file);
+	int fd = open(path, O_RDWR);
+	if (fd < 0) err(EX_NOINPUT, "%s", path);
 
 	struct termios attr;
-	int error = tcgetattr(tty, &attr);
+	int error = tcgetattr(fd, &attr);
 	if (error) err(EX_IOERR, "tcgetattr");
 
 	cfmakeraw(&attr);
-	error = tcsetattr(tty, TCSANOW, &attr);
+	error = tcsetattr(fd, TCSANOW, &attr);
 	if (error) err(EX_IOERR, "tcsetattr");
 
+	tty = fdopen(fd, "r+");
+	if (!tty) err(EX_IOERR, "fdopen");
+
 	switch (func) {
-		break; case 'L': led(tty, arg[0]);
-		break; case 'R': reset(tty);
-		break; case 'T': sensorTest(tty);
-		break; case 'c': getCo(tty);
-		break; case 'e': eraseCard(tty, arg[0]);
-		break; case 'h': setHiCo(tty);
-		break; case 'l': setLoCo(tty);
-		break; case 'r': readCard(tty);
-		break; case 't': test(tty);
-		break; case 'w': writeCard(tty);
+		break; case 'L': msrLED(arg[0]);
+		break; case 'R': msrReadRaw();
+		break; case 'T': msrSensorTest();
+		break; case 'W': msrWriteRaw();
+		break; case 'Z': msrSetZero(parse(&arg), parse(&arg));
+		break; case 'b': msrSetBPC(parse(&arg), parse(&arg), parse(&arg));
+		break; case 'c': msrGetCo();
+		break; case 'd': msrSetBPI(parse(&arg), parse(&arg));
+		break; case 'e': msrErase(arg[0] - '0');
+		break; case 'h': msrHiCo();
+		break; case 'i': msrReset();
+		break; case 'l': msrLoCo();
+		break; case 'r': msrRead();
+		break; case 't': msrTest();
+		break; case 'w': msrWrite();
+		break; case 'z': msrGetZero();
 		break; default:  return EX_USAGE;
 	}
 }