Merge branch 'master' of git.dolda2000.com:/srv/git/r/utils
[utils.git] / gpio.c
diff --git a/gpio.c b/gpio.c
new file mode 100644 (file)
index 0000000..5c1d718
--- /dev/null
+++ b/gpio.c
@@ -0,0 +1,182 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+
+static const int bmap[] = {
+    -1,
+    -1, -1,  2, -1,  3, -1,  4, 14, -1, 15, 17, 18, 27, -1, 22, 23, -1, 24, 10, -1,
+     9, 25, 11,  8, -1,  7, -1, -1,  5, -1,  6, 12, 13, -1, 19, 16, 21, 20, -1, 21,
+};
+static const int imap[] = {
+    -1, -1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13,
+    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, 27,
+};
+static const int *umap = bmap;
+static int mapn = sizeof(bmap) / sizeof(*bmap);
+static int preserve = 0;
+
+static int mappin(int num)
+{
+    if((num < 0) || (num >= mapn))
+       return(-1);
+    return(umap[num]);
+}
+
+static void export(int p)
+{
+    char path[256];
+    FILE *fp;
+    int rp;
+    
+    if((rp = mappin(p)) < 0)
+       errx(1, "%i: no such port", p);
+    sprintf(path, "/sys/class/gpio/gpio%i", rp);
+    if(!access(path, R_OK | X_OK))
+       return;
+    if(preserve)
+       errx(2, "gpio%i: not exported", rp);
+    sprintf(path, "/sys/class/gpio/export");
+    if((fp = fopen(path, "w")) == NULL)
+       err(1, "%s", path);
+    fprintf(fp, "%i\n", rp); fflush(fp);
+    if(ferror(fp))
+       errx(1, "gpio%i: could not export", rp);
+    fclose(fp);
+    sprintf(path, "/sys/class/gpio/gpio%i", rp);
+    if(access(path, R_OK | X_OK))
+       errx(1, "gpio%i: still not available after export", rp);
+}
+
+static void checkdir(int rp, char *dir)
+{
+    char path[256], line[256];
+    FILE *fp;
+    int rv;
+    size_t ln;
+    
+    sprintf(path, "/sys/class/gpio/gpio%i/direction", rp);
+    if((fp = fopen(path, "r")) == NULL)
+       err(1, "%s", path);
+    rv = !!fgets(line, sizeof(line), fp);
+    fclose(fp);
+    if(!rv || ((ln = strlen(line)) < 1) || (line[ln - 1] != '\n'))
+       errx(1, "gpio%i: could not read direction", rp);
+    line[ln - 1] = 0;
+    if(strcmp(line, dir)) {
+       if(preserve)
+           errx(2, "gpio%i: direction not set to %s", rp, dir);
+       if((fp = fopen(path, "w")) == NULL)
+           err(1, "%s", path);
+       fprintf(fp, "%s\n", dir); fflush(fp);
+       if(ferror(fp))
+           errx(1, "gpio%i: could not set to direction to %s", rp, dir);
+       fclose(fp);
+    }
+}
+
+static void setport(int p, int v)
+{
+    char path[256];
+    FILE *fp;
+    int rp;
+    
+    if((rp = mappin(p)) < 0)
+       errx(1, "%i: no such port", p);
+    checkdir(rp, "out");
+    sprintf(path, "/sys/class/gpio/gpio%i/value", rp);
+    if((fp = fopen(path, "w")) == NULL)
+       err(1, "%s", path);
+    fprintf(fp, "%i\n", v); fflush(fp);
+    if(ferror(fp))
+       errx(1, "gpio%i: could not set value", rp);
+    fclose(fp);
+}
+
+static int getport(int p)
+{
+    char path[256], line[256], *ep;
+    FILE *fp;
+    int rv, rp;
+    size_t ln;
+    
+    if((rp = mappin(p)) < 0)
+       errx(1, "%i: no such port", p);
+    checkdir(rp, "in");
+    sprintf(path, "/sys/class/gpio/gpio%i/value", rp);
+    if((fp = fopen(path, "r")) == NULL)
+       err(1, "%s", path);
+    rv = !!fgets(line, sizeof(line), fp);
+    fclose(fp);
+    if(!rv || ((ln = strlen(line)) < 1) || (line[ln - 1] != '\n'))
+       errx(1, "gpio%i: could not read direction", rp);
+    line[ln - 1] = 0;
+    rv = (int)strtol(line, &ep, 10);
+    if(*ep)
+       errx(1, "gpio%i: unexpected contents: %s", rp, line);
+    return(rv);
+}
+
+static void usage(FILE *out)
+{
+    fprintf(out, "usage: gpio [-hip] {PORT=VAL|PORT==VAL|PORT?}...\n");
+}
+
+int main(int argc, char **argv)
+{
+    int c, i, port, val;
+    char *e;
+    
+    while((c = getopt(argc, argv, "hip")) >= 0) {
+       switch(c) {
+       case 'h':
+           usage(stdout);
+           return(0);
+       case 'i':
+           umap = imap;
+           mapn = sizeof(imap) / sizeof(*imap);
+           break;
+       case 'p':
+           preserve = 1;
+           break;
+       default:
+           usage(stderr);
+           return(1);
+       }
+    }
+    if(optind >= argc) {
+       usage(stderr);
+       return(1);
+    }
+    for(i = optind; i < argc; i++) {
+       port = strtol(argv[i], &e, 10);
+       if((e > argv[i]) && (e[0] == '=') && (e[1] == '=')) {
+           val = strtol(e + 2, &e, 10);
+           if(*e)
+               errx(1, "gpio: %s: not of the form PORT==VAL", argv[i]);
+           export(port);
+           return(getport(port) != val);
+       } else if((e > argv[i]) && (e[0] == '=')) {
+           val = strtol(e + 1, &e, 10);
+           if(*e)
+               errx(1, "gpio: %s: not of the form PORT=VAL", argv[i]);
+           export(port);
+           setport(port, val);
+       } else if((e > argv[i]) && (e[0] == '?') && !e[1]) {
+           export(port);
+           printf("%i\n", getport(port));
+       } else {
+           errx(1, "gpio: %s: not a valid argument", argv[i]);
+       }
+    }
+    return(0);
+}
+
+/*
+ * Local Variables:
+ * compile-command: "gcc -Wall -g -O2 -march=native -c -o gpio.o gpio.c && gcc -o gpio gpio.o"
+ * End:
+ */