/*
 * ubbctl.c - Set and query UBB signals
 *
 * Written 2013 by Werner Almesberger
 * Copyright 2013 Werner Almesberger
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>	/* for strcasecmp, strncasecmp */

#include <ubb/ubb.h>


static struct pin {
	const char *name;
	uint32_t mask;
} pins[] = {
	{ "nPWR",	UBB_nPWR },
	{ "DAT2",	UBB_DAT2 },
	{ "DAT3",	UBB_DAT3 },
	{ "CMD",	UBB_CMD },
	{ "CLK",	UBB_CLK },
	{ "DAT0",	UBB_DAT0 },
	{ "DAT1",	UBB_DAT1 },
	{ NULL }
};


static void show_pins(void)
{
	const struct pin *p;
	int pin, set;

	for (p = pins; p->name; p++) {
		printf("%s%s=", p == pins ? "" : " ", p->name);
		pin = PIN(p->mask);
		if (PDFUN & p->mask) {
			putchar('F');
		} else if (PDDIR & p->mask) {
			set = !!(PDDAT & p->mask);
			if (pin != set)
				printf("%d!", set);
			
		} else {
			putchar(PDPULL & p->mask ? 'Z' : 'R');
		}
		printf("%d", pin);
	}
}


/*
 * The order of the IO operations below is important to avoid glitches.
 */

static int setup_pin(const char *s, int doit)
{
	const struct pin *p;
	const char *eq;

	if (!strcasecmp(s, "on"))
		s = "nPWR=0";
	else if (!strcasecmp(s, "off"))
		s = "nPWR=1";

	eq = strchr(s, '=');
	if (!eq)
		return 0;

	for (p = pins; p->name; p++)
		if (strlen(p->name) == eq-s && !strncasecmp(p->name, s, eq-s))
			break;
	if (!p->name)
		return 0;

	if (!strcasecmp(eq+1, "f")) {
		if (doit)
			PDFUNS = p->mask;
	} else if (!strcmp(eq+1, "0")) {
		if (doit) {
			PDDATC = p->mask;
			PDDIRS = p->mask;
			PDFUNC = p->mask;
		}
	} else if (!strcmp(eq+1, "1")) {
		if (doit) {
			PDDATS = p->mask;
			PDDIRS = p->mask;
			PDFUNC = p->mask;
		}
	} else if (!strcasecmp(eq+1, "r")) {
		if (doit) {
			PDPULLC = p->mask;
			PDDIRC = p->mask;
			PDFUNC = p->mask;
		}
	} else if (!strcasecmp(eq+1, "z")) {
		if (doit) {
			PDPULLS = p->mask;
			PDDIRC = p->mask;
			PDFUNC = p->mask;
		}
	} else {
		return 0;
	}
	return 1;
}


static void usage(const char *name)
{
        fprintf(stderr,
"usage: %s [-c]\n"
"       %s name=value|action ...\n\n"
"  -c  continously update the pin status (until user interrupts)\n\n"
"Names:   nPWR, CMD, CLK, DAT0, DAT1, DAT2, DAT3\n"
"Values:  F, 0, 1, Z, R\n"
"Actions: ON, OFF\n"
    , name, name);
	exit(1);
}


int main(int argc, char **argv)
{
	int continuous = 0;
	int c, i;

	while ((c = getopt(argc, argv, "c")) != EOF)
		switch (c) {
		case 'c':
			continuous = 1;
			break;
		default:
			usage(*argv);
		}

	if (argc != optind && continuous)
		usage(*argv);

	for (i = optind; i != argc; i++)
		if (!setup_pin(argv[i], 0))
			usage(*argv);

	ubb_open(UBB_ALL);
	if (argc == optind) {
		if (continuous) {
			while (1) {
				show_pins();
				printf("%*s\r", sizeof(pins)/sizeof(*pins)*2,
				    "");
				fflush(stdout);
				usleep(200*1000);
			}
		} else {
			show_pins();
			putchar('\n');
		}
	} else {
		for (i = optind; i != argc; i++)
			setup_pin(argv[i], 1);
	}
	return 0;
}