V6
authorjoan <joan@abyz.me.uk>
Thu, 12 Dec 2013 10:27:22 +0000 (10:27 +0000)
committerjoan <joan@abyz.me.uk>
Thu, 12 Dec 2013 10:27:22 +0000 (10:27 +0000)
13 files changed:
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
README.md [new file with mode: 0644]
UNLICENCE [new file with mode: 0644]
checklib.c [new file with mode: 0644]
command.c [new file with mode: 0644]
command.h [new file with mode: 0644]
demolib.c [new file with mode: 0644]
pig2vcd.c [new file with mode: 0644]
pigpio.c [new file with mode: 0644]
pigpio.h [new file with mode: 0644]
pigpiod.c [new file with mode: 0644]
pigs.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2b69494
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,63 @@
+CC     = gcc
+AR      = ar
+RANLIB  = ranlib
+SIZE    = size
+
+CFLAGS = -O3 -Wall
+
+all:   libpigpio.a checklib demolib pig2vcd pigpiod pigs
+
+checklib:      checklib.o libpigpio.a
+       $(CC) -o checklib checklib.c -L. -lpigpio -lpthread -lrt
+
+demolib:       demolib.o libpigpio.a
+       $(CC) -o demolib demolib.c -L. -lpigpio -lpthread -lrt
+
+pig2vcd:       pig2vcd.o
+       $(CC) -o pig2vcd pig2vcd.c
+
+pigpiod:       pigpiod.o libpigpio.a
+       $(CC) -o pigpiod pigpiod.c -L. -lpigpio -lpthread -lrt
+
+pigs:  pigs.o command.o
+       $(CC) -o pigs pigs.c command.c
+
+.c.o:
+       $(CC) -c $(CFLAGS) $<
+
+clean:
+       rm -f *.o *.i *.s *~ libpigpio.a checklib demolib pigpiod pigs pig2vcd
+
+install:       $(LIB) 
+       sudo install -m 0755 -d          /usr/local/bin
+       sudo install -m 0755 -d          /usr/local/include
+       sudo install -m 0755 -d          /usr/local/lib
+       sudo install -m 0755 pig2vcd     /usr/local/bin
+       sudo install -m 0755 pigpiod     /usr/local/bin
+       sudo install -m 0755 pigs        /usr/local/bin
+       sudo install -m 0644 pigpio.h    /usr/local/include
+       sudo install -m 0644 libpigpio.a /usr/local/lib
+
+uninstall:
+       sudo rm -f /usr/local/bin/pig2vcd
+       sudo rm -f /usr/local/bin/pigpiod
+       sudo rm -f /usr/local/bin/pigs
+       sudo rm -f /usr/local/include/pigpio.h
+       sudo rm -f /usr/local/lib/libpigpio.a
+
+LIB     = libpigpio.a
+OBJ     = pigpio.o command.o
+
+$(LIB):        $(OBJ)
+       $(AR) rcs $(LIB) $(OBJ)
+       $(RANLIB) $(LIB)
+       $(SIZE)   $(LIB)
+
+
+# DO NOT DELETE
+
+checklib.o:    checklib.c pigpio.h
+demolib.o:     demolib.c pigpio.h
+pig2vcd:       pigpio.h
+pigpiod:       pigpiod.c pigpio.h
+pigs:          pigs.c command.c pigpio.h command.h
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..41c00cb
--- /dev/null
+++ b/README
@@ -0,0 +1,67 @@
+INSTALL
+
+Extract the archive to a directory.
+
+IN THAT DIRECTORY
+
+Enter the following two commands (in this order)
+
+make
+make install
+
+This will install:
+   the library (libpigpio.a) in /usr/local/lib
+   the header file (pigpio.h) in /usr/local/include
+   the daemon (pigpiod) in /usr/local/bin
+   the socket interface (pigs) in /usr/local/bin
+   the utility pig2vcd in /usr/local/bin
+
+TEST
+
+To test the library do
+
+sudo ./checklib
+
+checklib.c, demolib.c, pig2vcd.c, pigpiod.c, and pigs.c  show examples
+of interfacing with the library.
+
+DAEMON
+
+To launch the daemon do
+
+sudo pigpiod (pigpiod -? for options)
+
+Once the daemon is launched the socket and fifo interfaces will be
+available.
+
+When the library starts it locks
+
+/var/run/pigpio.pid
+
+The file should be deleted when the library terminates.
+
+SOCKET INTERFACE
+
+Use pigs for the socket interface (pigs help for help).
+
+FIFO INTERFACE
+
+The fifo interface accepts commands written to /dev/pigpio.
+
+Results are read from /dev/pigout.
+
+Errors are output on /dev/pigerr.
+
+To test the fifo interface perhaps do
+
+cat /dev/pigout &
+cat /dev/pigerr &
+
+echo "help" >/dev/pigpio
+
+STOP DAEMON
+
+To stop the daemon
+
+sudo killall pigpiod
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/UNLICENCE b/UNLICENCE
new file mode 100644 (file)
index 0000000..471f09f
--- /dev/null
+++ b/UNLICENCE
@@ -0,0 +1,25 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+
diff --git a/checklib.c b/checklib.c
new file mode 100644 (file)
index 0000000..bc2316d
--- /dev/null
@@ -0,0 +1,709 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "pigpio.h"
+
+#define GREENLED 16
+#define SDDET    47
+#define SDCLK    48
+
+int test  =1;
+int passes=0;
+int expect=0;
+
+struct timeval libInitTime;
+
+int GPIO=4;
+
+unsigned inited, count, onMicros, offMicros;
+
+void tick(void)
+{
+   /* count ticks
+   */
+
+   static struct timeval lastTime;
+   struct tm tmp;
+   struct timeval nowTime;
+   char buf[32];
+
+   gettimeofday(&nowTime, NULL);
+
+   localtime_r(&nowTime.tv_sec, &tmp);
+   strftime(buf, sizeof(buf), "%F@%T", &tmp);
+
+   printf("%s.%03d\n", buf, (int)nowTime.tv_usec/1000);
+
+   /*timersub(&nowTime, &lastTime, &diffTime);*/
+
+   lastTime = nowTime;
+
+   if (inited)
+   {
+      count++;
+   }
+   else
+   {
+      count = 1;
+
+      gettimeofday(&lastTime, NULL);
+
+      inited = 1;
+   }
+}
+
+void tickEx(void * userdata)
+{
+}
+
+void alert(int gpio, int level, uint32_t tick)
+{
+   /* accumulate number of level changes and average time gpio
+      was on and off.  Hopefully the ratio should reflect the
+      selected pulsewidth.
+   */
+
+   static uint32_t lastTick;
+
+   uint32_t diffTick;
+
+   if (inited)
+   {
+      count++;
+
+      diffTick = tick - lastTick;
+      
+      if (level == 0)
+      {
+         /* elapsed time was on */
+         onMicros = onMicros + diffTick;
+      }
+      else
+      {
+         /* elapsed time was off */
+         offMicros = offMicros + diffTick;
+      }
+      lastTick = tick;
+   }
+   else
+   {
+      count     = 1;
+      lastTick  = tick;
+      onMicros  = 0;
+      offMicros = 0;
+      inited    = 1;
+   }
+}
+
+void alertEx(int gpio, int level, uint32_t tick, void * userdata)
+{
+}
+
+static void timerTest(unsigned waitfor, unsigned ms)
+{
+   unsigned ep, ep1, ep2;
+   ep= (waitfor*1000)/ms; ep1=ep-1; ep2=ep+1;
+
+   printf("Timer ticktest (%d ms), wait %d seconds\n", ms, waitfor);
+   printf("Expect %d to %d ticks\n", ep1, ep2);
+
+   inited = 0;
+   gpioSetTimerFunc(0, ms, tick);
+   sleep(waitfor);
+   gpioSetTimerFunc(0, ms, NULL);
+
+   /* and the stats were? */
+
+   printf("ticks=%d\n", count);
+
+   if ((count>=ep1) && (count<=ep2))
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+   else
+   {
+      printf("TEST %d: FAILED\n\n", test);
+   }
+
+   ++test;
+}
+
+static void servoTest(unsigned waitfor, unsigned pulsewidth)
+{
+   int ticks, on, off;
+   unsigned expectedPulses, ep1, ep2;
+   float    expectedRatio,  er1, er2;
+   float ratio;
+   expectedPulses=(500*waitfor)/10; ep1=(490*waitfor)/10; ep2=(510*waitfor)/10;
+   expectedRatio = (float)(20000-pulsewidth)/(float)pulsewidth;
+   er1=expectedRatio*0.9; er2=expectedRatio*1.1;
+
+   printf("Servo pulse test (%d micros), wait %d seconds\n",
+      pulsewidth, waitfor);
+   printf("Expect %d pulses and an off/on ratio of %.1f\n",
+      expectedPulses, expectedRatio);
+
+   gpioServo(GPIO, pulsewidth); 
+
+   inited = 0;
+   gpioSetAlertFunc(GPIO, alert);
+   sleep(waitfor);
+   gpioSetAlertFunc(GPIO, NULL);
+
+   gpioServo(GPIO, 0);
+
+   /* and the stats were? */
+
+   ticks = count/2; on = onMicros/1000; off = offMicros/1000;
+   ratio = (float)off/(float)on;
+
+   printf("servo pulses=%d on ms=%d off ms=%d ratio=%.1f\n", ticks, on, off, ratio);
+
+   if ( ((ticks>ep1) && (ticks<ep2)) && ((ratio>er1) && (ratio<er2)) )
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+   else
+   {
+      printf("TEST %d: FAILED\n\n", test);
+   }
+
+   ++test;
+}
+
+static void pwmTest(unsigned waitfor, unsigned pulsewidth)
+{
+   int on, off;
+   float    expectedRatio,  er1, er2;
+   float ratio;
+
+   expectedRatio = (float)(255-pulsewidth)/255.0;
+   er1=expectedRatio*0.9; er2=expectedRatio*1.1;
+
+   printf("PWM test (%d), wait %d seconds\n", pulsewidth, waitfor);
+   printf("Expect an off/on ratio of %.3f\n", expectedRatio);
+
+   inited = 0;
+   gpioSetAlertFunc(GPIO, alert);
+   gpioPWM(GPIO, pulsewidth); 
+   sleep(waitfor);
+   gpioPWM(GPIO, 0);
+   gpioSetAlertFunc(GPIO, NULL);
+
+   /* and the stats were? */
+
+   on = onMicros/1000; off = offMicros/1000;
+   ratio = (float)off/((float)on+(float)off);
+
+   printf("pwm on ms=%d off ms=%d ratio=%.3f\n", on, off, ratio);
+
+   if ((ratio>er1) && (ratio<er2))
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+   else
+   {
+      printf("TEST %d: FAILED\n\n", test);
+   }
+
+   ++test;
+}
+
+void expectExpected(int expected)
+{
+   if (expect==expected)
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+   else printf("TEST %d: FAILED\n\n", test);
+
+   expect = 0;
+
+   ++test;
+}
+
+void checkValidation(void)
+{
+   int secs, micros;
+
+   /* check function parameter validation */
+
+   printf("Function parameter validation tests\n");
+
+   printf("Expect ERROR messages\n\n");
+
+   if (gpioSetMode(PI_MAX_GPIO+1, 0) == PI_BAD_GPIO)  expect++;
+   if (gpioSetMode(PI_MIN_GPIO-1, 0) == PI_BAD_GPIO)  expect++;
+   if (gpioSetMode(PI_MIN_GPIO, PI_ALT3+1)  == PI_BAD_MODE)  expect++;
+   if (gpioSetMode(PI_MAX_GPIO, PI_INPUT-1) == PI_BAD_MODE)  expect++;
+
+   expectExpected(4);
+
+   if (gpioGetMode(PI_MAX_GPIO+1) == PI_BAD_GPIO) expect++;
+   if (gpioGetMode(PI_MIN_GPIO-1) == PI_BAD_GPIO) expect++;
+
+   expectExpected(2);
+
+   if (gpioSetPullUpDown(PI_MAX_GPIO+1, 0)  == PI_BAD_GPIO) expect++;
+   if (gpioSetPullUpDown(PI_MIN_GPIO-1, 0)  == PI_BAD_GPIO) expect++;
+   if (gpioSetPullUpDown(PI_MIN_GPIO, PI_PUD_UP+1)  == PI_BAD_PUD)  expect++;
+   if (gpioSetPullUpDown(PI_MAX_GPIO, PI_PUD_OFF-1) == PI_BAD_PUD)  expect++;
+
+   expectExpected(4);
+
+   if (gpioRead(PI_MIN_GPIO-1) == PI_BAD_GPIO) expect++;
+   if (gpioRead(PI_MAX_GPIO+1) == PI_BAD_GPIO) expect++;
+
+   expectExpected(2);
+
+   if (gpioWrite(PI_MAX_GPIO+1, 0)  == PI_BAD_GPIO)  expect++;
+   if (gpioWrite(PI_MIN_GPIO-1, 0)  == PI_BAD_GPIO)  expect++;
+   if (gpioWrite(PI_MIN_GPIO, PI_ON+1)  == PI_BAD_LEVEL) expect++;
+   if (gpioWrite(PI_MAX_GPIO, PI_OFF-1) == PI_BAD_LEVEL) expect++;
+
+   expectExpected(4);
+
+   if (gpioPWM(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO)  expect++;
+   if (gpioPWM(PI_MIN_GPIO-1, 0) == PI_BAD_USER_GPIO)  expect++;
+   if (gpioPWM(PI_MIN_GPIO, PI_DEFAULT_DUTYCYCLE_RANGE+1) == 
+      PI_BAD_DUTYCYCLE)  expect++;
+   if (gpioPWM(PI_MAX_USER_GPIO, -1)  == PI_BAD_DUTYCYCLE)  expect++;
+
+   expectExpected(4);
+
+   if (gpioSetPWMrange(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO)  expect++;
+   if (gpioSetPWMrange(PI_MIN_GPIO-1, 0)      == PI_BAD_USER_GPIO)  expect++;
+   if (gpioSetPWMrange(GPIO, 24)    == PI_BAD_DUTY_RANGE) expect++;
+   if (gpioSetPWMrange(GPIO, 40001) == PI_BAD_DUTY_RANGE) expect++;
+
+   expectExpected(4);
+
+   if (gpioGetPWMrange(PI_MAX_USER_GPIO+1) == PI_BAD_USER_GPIO)  expect++;
+   if (gpioGetPWMrange(PI_MIN_GPIO-1)     == PI_BAD_USER_GPIO)  expect++;
+
+   expectExpected(2);
+
+   if (gpioGetPWMrealRange(PI_MAX_USER_GPIO+1) == PI_BAD_USER_GPIO) expect++;
+   if (gpioGetPWMrealRange(PI_MIN_GPIO-1)      == PI_BAD_USER_GPIO) expect++;
+
+   expectExpected(2);
+
+   if (gpioSetPWMfrequency(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO)  expect++;
+   if (gpioSetPWMfrequency(PI_MIN_GPIO-1, 0)      == PI_BAD_USER_GPIO)  expect++;
+
+   expectExpected(2);
+
+   if (gpioGetPWMfrequency(PI_MAX_USER_GPIO+1) == PI_BAD_USER_GPIO)  expect++;
+   if (gpioGetPWMfrequency(PI_MIN_GPIO-1)     == PI_BAD_USER_GPIO)  expect++;
+
+   expectExpected(2);
+
+   if (gpioServo(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO)  expect++;
+   if (gpioServo(PI_MIN_GPIO-1, 0)      == PI_BAD_USER_GPIO)  expect++;
+   if (gpioServo(GPIO, 1)    == PI_BAD_PULSEWIDTH) expect++;
+   if (gpioServo(GPIO,-1)    == PI_BAD_PULSEWIDTH) expect++;
+   if (gpioServo(GPIO, 499)  == PI_BAD_PULSEWIDTH) expect++;
+   if (gpioServo(GPIO, 2501) == PI_BAD_PULSEWIDTH) expect++;
+
+   expectExpected(6);
+
+   if (gpioSetAlertFunc(PI_MAX_USER_GPIO+1, alert) == PI_BAD_USER_GPIO) expect++;
+   if (gpioSetAlertFunc(PI_MIN_GPIO-1, alert) == PI_BAD_USER_GPIO) expect++;
+
+   expectExpected(2);
+
+   if (gpioSetAlertFuncEx(PI_MAX_USER_GPIO+1, alertEx, 0) == PI_BAD_USER_GPIO) expect++;
+   if (gpioSetAlertFuncEx(PI_MIN_GPIO-1, alertEx, 0) == PI_BAD_USER_GPIO) expect++;
+
+   expectExpected(2);
+
+   if (gpioSetWatchdog(PI_MAX_USER_GPIO+1, 0) == PI_BAD_USER_GPIO) expect++;
+   if (gpioSetWatchdog(PI_MIN_GPIO-1, 0)      == PI_BAD_USER_GPIO) expect++;
+   if (gpioSetWatchdog(GPIO, 60001) == PI_BAD_WDOG_TIMEOUT)   expect++;
+   if (gpioSetWatchdog(GPIO, -1)    == PI_BAD_WDOG_TIMEOUT)   expect++;
+   if (gpioSetWatchdog(GPIO, 0)     == 0)                     expect++;
+
+   expectExpected(5);
+
+   if (gpioSetTimerFunc(10,  20, tick)  == PI_BAD_TIMER) expect++;
+   if (gpioSetTimerFunc(-1, 20, tick)   == PI_BAD_TIMER) expect++;
+   if (gpioSetTimerFunc(0, 9, tick)     == PI_BAD_MS)    expect++;
+   if (gpioSetTimerFunc(0, 60001, tick) == PI_BAD_MS)    expect++;
+
+   expectExpected(4);
+
+   if (gpioSetTimerFuncEx(10,  20, tickEx, 0)  == PI_BAD_TIMER) expect++;
+   if (gpioSetTimerFuncEx(-1, 20, tickEx, 0)   == PI_BAD_TIMER) expect++;
+   if (gpioSetTimerFuncEx(0, 9, tickEx, 0)     == PI_BAD_MS)    expect++;
+   if (gpioSetTimerFuncEx(0, 60001, tickEx, 0) == PI_BAD_MS)    expect++;
+
+   expectExpected(4);
+
+   if (gpioTime(-1, &secs, &micros) == PI_BAD_TIMETYPE) expect++;
+   if (gpioTime(2,  &secs, &micros) == PI_BAD_TIMETYPE) expect++;
+
+   expectExpected(2);
+
+   if (gpioSleep(-1, 1, 0)                      == PI_BAD_TIMETYPE) expect++;
+   if (gpioSleep(2,  1, 0)                      == PI_BAD_TIMETYPE) expect++;
+   if (gpioSleep(PI_TIME_ABSOLUTE, -1,  0)      == PI_BAD_SECONDS)  expect++;
+   if (gpioSleep(PI_TIME_ABSOLUTE,  0, -1)      == PI_BAD_MICROS)   expect++;
+   if (gpioSleep(PI_TIME_ABSOLUTE,  0, 1000000) == PI_BAD_MICROS)   expect++;
+   if (gpioSleep(PI_TIME_RELATIVE, -1,  0)      == PI_BAD_SECONDS)  expect++;
+   if (gpioSleep(PI_TIME_RELATIVE,  0, -1)      == PI_BAD_MICROS)   expect++;
+   if (gpioSleep(PI_TIME_RELATIVE,  0, 1000000) == PI_BAD_MICROS)   expect++;
+
+   expectExpected(8);
+}
+
+void checkGpioTime(void)
+{
+   struct timeval nowA, nowR, tvR, tvA, diffR, diffA;
+   int diffMicrosA, diffMicrosR;
+   int i, ok;
+
+   int secR, micR, secA, micA;
+
+   printf("Library timer tests.\n");
+
+   ok = 0;
+
+   for (i=0; i<10; i++)
+   {
+      gettimeofday(&nowA, NULL);                /* reference absolute time */
+
+      gpioTime(PI_TIME_ABSOLUTE, &secA, &micA); /* absolute library time */
+      gpioTime(PI_TIME_RELATIVE, &secR, &micR); /* relative library time */
+
+      timersub(&nowA, &libInitTime, &nowR);     /* reference relative time */
+
+      tvA.tv_sec = secA; tvA.tv_usec = micA;
+      tvR.tv_sec = secR; tvR.tv_usec = micR;
+
+      /* difference between reference and library absolute time */
+      timersub(&tvA, &nowA, &diffA);
+
+      /* difference between reference and library relative time */
+      timersub(&tvR, &nowR, &diffR);
+
+
+      diffMicrosA = (diffA.tv_sec*1000000)+diffA.tv_usec;
+
+      diffMicrosR = (diffR.tv_sec*1000000)+diffR.tv_usec;
+
+      if (diffMicrosA < 0) diffMicrosA = -diffMicrosA;
+      if (diffMicrosR < 0) diffMicrosR = -diffMicrosR;
+
+      if ((diffMicrosA < 10) && (diffMicrosR < 500)) ok++;
+      
+      printf("ABS time diff=%d, REL time diff=%d\n",
+         diffMicrosA, diffMicrosR);
+   }
+
+   if (ok == 10)
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+   else printf("TEST %d: FAILED\n\n", test);
+
+   ++test;
+}
+
+void checkGpioSleep(unsigned timetype)
+{
+   struct timeval t1, t2, tD;
+   int i, ok, secs, micros, diffMicros, expMicros, errMicros;
+
+   if (timetype == PI_TIME_ABSOLUTE)
+      printf("Library gpioSleep ABSOLUTE tests.\n");
+   else
+      printf("Library gpioSleep RELATIVE tests.\n");
+
+   ok = 0;
+
+   for (i=15; i>0; i--)
+   {
+      expMicros = i * 100000;
+
+      if (timetype == PI_TIME_ABSOLUTE)
+      {
+         gpioTime(PI_TIME_ABSOLUTE, &secs, &micros);
+
+         secs   += (i / 10);
+         micros += (i % 10) * 100000;
+
+         if (micros > 999999) { secs++; micros -= 1000000; }
+      }
+      else
+      {
+         secs   = (i / 10);
+         micros = (i % 10) * 100000;
+      }
+
+      gettimeofday(&t1, NULL);
+
+      gpioSleep(timetype, secs, micros);
+
+      gettimeofday(&t2, NULL);
+
+      timersub(&t2, &t1, &tD);
+
+      diffMicros = (tD.tv_sec*1000000)+tD.tv_usec;
+
+      errMicros = diffMicros - expMicros;
+
+      if (errMicros < 500) ok++;
+      
+      printf("secs=%d micros=%d err=%d\n", secs, micros, errMicros);
+   }
+
+   if (ok == 15)
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+   else printf("TEST %d: FAILED\n\n", test);
+
+   ++test;
+}
+
+int countBank2PinChanges(int pin, int loops)
+{
+   static uint32_t old=0;
+
+   uint32_t new, changes;
+   int i, count;
+
+   count = 0;
+
+   for (i=0;i<loops;i++)
+   {
+      new = gpioRead_Bits_32_53();
+
+      changes = new ^ old;
+
+      if (changes & (1<<(pin-32))) count++;
+   }
+   return count;
+}
+
+void checkReadWriteBits(void)
+{
+   uint32_t bank1, bank2;
+
+   int i, ok, count1, count2;
+
+   printf("Library gpioRead/Write_Bits_x_x_Set/Clear Tests\n");
+   printf("Expect 0 for pin 47 and >200000 for pin 48.\n");
+   printf("Expect the green LED to flash.\n\n");
+
+   ok = 0;
+
+   for (i=0; i<20; i++)
+   {
+      gpioWrite_Bits_0_31_Set(1<<GREENLED);
+
+      count1 = countBank2PinChanges(SDDET, 1000000);
+
+      gpioWrite_Bits_0_31_Clear(1<<GREENLED);
+
+      count2 = countBank2PinChanges(SDCLK, 1000000);
+
+      bank1 = gpioRead_Bits_0_31();
+      bank2 = gpioRead_Bits_32_53();
+
+      printf("bank1=%08X, bank2=%08X, 47=%d 48=%d\n",
+         bank1, bank2, count1, count2);
+
+      if ((count1 == 0) && (count2 > 200000)) ok++;
+   }
+   
+   if (ok == 20)
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+   else printf("TEST %d: FAILED\n\n", test);
+
+   ++test;
+}
+
+void checkGpioTick(void)
+{
+   uint32_t startTick, endTick;
+   int diffTick;
+
+   printf("Library gpioTick Test\n");
+   printf("Expect approximately 2 million ticks to have elapsed.\n\n");
+
+   startTick = gpioTick();
+   sleep(2); /* 2 seconds being 2 million ticks */
+   endTick = gpioTick();
+
+   diffTick = endTick - startTick;
+
+   printf("%d ticks have elapsed\n", diffTick);
+   
+   if ((diffTick >= 1990000) && (diffTick <= 2010000))
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+   else printf("TEST %d: FAILED\n\n", test);
+
+   ++test;
+}
+
+int main(int argc, char *argv[])
+{
+
+   int waitfor;
+
+   int version, micros=5, millis=100;
+
+   if (argc > 1) GPIO = atoi(argv[1]);
+
+   fprintf(stderr,
+"*****************************************************************\n"\
+"* WARNING: This program sends pulses to gpio #%02d                *\n"\
+"* Make sure that nothing which could be damaged is              *\n"\
+"* connected to this gpio.  A LED or similar should be OK        *\n"\
+"* although nothing needs to be connected.                       *\n"\
+"*                                                               *\n"\
+"* NOTE: many of the tests are statistical in nature, assuming   *\n"\
+"* that events of a short nature will on average be detected     *\n"\
+"* by sampling.  Don't fret if a particular test fails, try      *\n"\
+"* running the tests again.                                      *\n"\
+"*                                                               *\n"\
+"* You may choose another gpio by specifying its number on       *\n"\
+"* the command line, e.g. sudo ./checklib 17 will use gpio 17.   *\n"\
+"*                                                               *\n"\
+"* Press y (RETURN) to continue, any other character to cancel.  *\n"\
+"*****************************************************************\n", GPIO);
+
+   if (getchar() != 'y') return 0;   
+
+   printf("Initialisation test\n");
+
+   if (argc > 2) micros = atoi(argv[2]);
+
+   if (argc > 3) millis = atoi(argv[3]);
+
+   gpioCfgBufferSize(millis);
+
+   gpioCfgClock(micros, PI_CLOCK_PCM, PI_CLOCK_PLLD);
+
+   gettimeofday(&libInitTime, NULL);
+
+   version = gpioInitialise();
+
+   if (version<0)
+   {
+      printf("TEST %d: FAILED\nFATAL ERROR\n", test);
+      return 1;
+   }
+   else
+   {
+      printf("TEST %d: PASS, pigpio version is %d\n\n", test, version);
+      ++passes;
+   }
+
+   ++test;
+
+   waitfor = 2;
+
+   printf("Alert function test, wait %d seconds\n", waitfor);
+   printf("No detected events on gpio 4 expected\n");
+
+   inited = 0;
+   gpioSetAlertFunc(GPIO, alert);
+   sleep(waitfor);
+   gpioSetAlertFunc(GPIO, NULL);
+
+   printf("Events=%d\n", count);
+
+   if (count) printf("TEST %d: FAILED\n\n", test);
+   else       
+   {
+      printf("TEST %d: PASS\n\n", test);
+      ++passes;
+   }
+
+   ++test;
+
+   servoTest(10,  500);
+   servoTest(10, 1500);
+   servoTest(10, 2500);
+
+   pwmTest(5,  50);
+   pwmTest(5, 100);
+   pwmTest(5, 150);
+   pwmTest(5, 200);
+
+   timerTest(5, 100);
+   timerTest(5, 250);
+   timerTest(5, 333);
+   timerTest(5, 1000);
+
+   checkValidation();
+
+   checkGpioTime();
+
+   checkGpioSleep(PI_TIME_RELATIVE);
+
+   checkGpioSleep(PI_TIME_ABSOLUTE);
+
+   checkReadWriteBits();
+
+   checkGpioTick();
+
+   printf("Hardware revision is %d\n\n", gpioHardwareRevision());
+
+   printf("Summary: %d tests, %d passes\n", test-1, passes);
+
+   gpioTerminate(); /* stop DMA and free memory */
+
+   return (passes - (test-1));
+}
+
diff --git a/command.c b/command.c
new file mode 100644 (file)
index 0000000..ab46aa8
--- /dev/null
+++ b/command.c
@@ -0,0 +1,292 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 4+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "pigpio.h"
+#include "command.h"
+
+cmdInfo_t cmdInfo[]=
+{
+   {PI_CMD_BR1,   "BR1",   1, 3},
+   {PI_CMD_BR2,   "BR2",   1, 3},
+   {PI_CMD_BC1,   "BC1",   7, 1},
+   {PI_CMD_BC2,   "BC2",   7, 1},
+   {PI_CMD_BS1,   "BS1",   7, 1},
+   {PI_CMD_BS2,   "BS2",   7, 1},
+   {PI_CMD_HWVER, "HWVER", 1, 4},
+   {PI_CMD_MODES, "MODES", 8, 0},
+   {PI_CMD_MODES, "M",     8, 0},
+   {PI_CMD_MODEG, "MODEG", 2, 2},
+   {PI_CMD_MODEG, "MG"   , 2, 2},
+   {PI_CMD_NO,    "NO",    1, 2},
+   {PI_CMD_NB,    "NB",    4, 0},
+   {PI_CMD_NP,    "NP",    2, 0},
+   {PI_CMD_NC,    "NC",    2, 0},
+   {PI_CMD_PWM,   "PWM",   3, 0},
+   {PI_CMD_PWM,   "P",     3, 0},
+   {PI_CMD_PFS,   "PFS",   3, 2},
+   {PI_CMD_PFG,   "PFG",   2, 2},
+   {PI_CMD_PRS,   "PRS",   3, 2},
+   {PI_CMD_PRG,   "PRG",   2, 2},
+   {PI_CMD_PRRG,  "PRRG",  2, 2},
+   {PI_CMD_PUD,   "PUD",   9, 0},
+   {PI_CMD_READ,  "READ",  2, 2},
+   {PI_CMD_READ,  "R",     2, 2},
+   {PI_CMD_SERVO, "SERVO", 3, 0},
+   {PI_CMD_SERVO, "S",     3, 0},
+   {PI_CMD_WRITE, "WRITE", 3, 0},
+   {PI_CMD_WRITE, "W",     3, 0},
+   {PI_CMD_WDOG,  "WDOG",  3, 0},
+   {PI_CMD_TICK,  "TICK",  1, 4},
+   {PI_CMD_TICK,  "T",     1, 4},
+   {PI_CMD_HELP,  "HELP",  6, 5},
+   {PI_CMD_HELP,  "H",     6, 5},
+};
+
+char * cmdUsage = "\
+BR1          read gpios bank 1\n\
+BR2          read gpios bank 2\n\
+BC1 x        clear gpios in bank 1\n\
+BC2 x        clear gpios in bank 2\n\
+BS1 x        set gpios in bank 1\n\
+BS2 x        set gpios in bank 2\n\
+HWVER        return hardware version\n\
+MODES/M g m  set gpio mode\n\
+MODEG/MG g   get gpio mode\n\
+NO           request notification handle\n\
+NB h x       start notification\n\
+NP h         pause notification\n\
+NC h         close notification\n\
+PWM/P u d    set PWM value for gpio\n\
+PFS u d      set PWM frequency for gpio\n\
+PFG u        get PWM frequency for gpio\n\
+PRS u d      set PWM range for gpio\n\
+PRG u        get PWM range for gpio\n\
+PRRG u       get PWM real range for gpio\n\
+PUD g p      set gpio pull up/down\n\
+READ/R g     read gpio\n\
+SERVO/S u d  set servo value for gpio\n\
+WRITE/W g d  write value to gpio\n\
+WDOG u d     set watchdog on gpio\n\
+TICK/T       return current tick\n\
+HELP/H       displays command help\n\
+\n\
+d = decimal value\n\
+g = gpio (0-53)\n\
+h = handle (0-31)\n\
+m = mode (RW540123)\n\
+p = pud (ODU)\n\
+u = user gpio (0-31)\n\
+x = hex value\n\
+";
+
+typedef struct
+{
+   int error;
+   char * str;
+} errInfo_t;
+
+static errInfo_t errInfo[]=
+{
+   {PI_INIT_FAILED      , "pigpio initialisation failed"},
+   {PI_BAD_USER_GPIO    , "gpio not 0-31"},
+   {PI_BAD_GPIO         , "gpio not 0-53"},
+   {PI_BAD_MODE         , "mode not 0-7"},
+   {PI_BAD_LEVEL        , "level not 0-1"},
+   {PI_BAD_PUD          , "pud not 0-2"},
+   {PI_BAD_PULSEWIDTH   , "pulsewidth not 0 or 500-2500"},
+   {PI_BAD_DUTYCYCLE    , "dutycycle not 0-255"},
+   {PI_BAD_TIMER        , "timer not 0-9"},
+   {PI_BAD_MS           , "ms not 10-60000"},
+   {PI_BAD_TIMETYPE     , "timetype not 0-1"},
+   {PI_BAD_SECONDS      , "seconds < 0"},
+   {PI_BAD_MICROS       , "micros not 0-999999"},
+   {PI_TIMER_FAILED     , "gpioSetTimerFunc failed"},
+   {PI_BAD_WDOG_TIMEOUT , "timeout not 0-60000"},
+   {PI_NO_ALERT_FUNC    , "DEPRECATED"},
+   {PI_BAD_CLK_PERIPH   , "clock peripheral not 0-1"},
+   {PI_BAD_CLK_SOURCE   , "clock source not 0-1"},
+   {PI_BAD_CLK_MICROS   , "clock micros not 1, 2, 4, 5, 8, or 10"},
+   {PI_BAD_BUF_MILLIS   , "buf millis not 100-10000"},
+   {PI_BAD_DUTY_RANGE   , "dutycycle range not 25-40000"},
+   {PI_BAD_SIGNUM       , "signum not 0-63"},
+   {PI_BAD_PATHNAME     , "can't open pathname"},
+   {PI_NO_HANDLE        , "no handle available"},
+   {PI_BAD_HANDLE       , "unknown notify handle"},
+   {PI_BAD_IF_FLAGS     , "ifFlags > 3"},
+   {PI_BAD_CHANNEL      , "DMA channel not 0-14"},
+   {PI_BAD_SOCKET_PORT  , "socket port not 1024-30000"},
+   {PI_BAD_FIFO_COMMAND , "unknown fifo command"},
+   {PI_BAD_SECO_CHANNEL , "DMA secondary channel not 0-6"},
+   {PI_NOT_INITIALISED  , "function called before gpioInitialise"},
+   {PI_INITIALISED      , "function called after gpioInitialise"},
+   {PI_BAD_WAVE_MODE    , "waveform mode not 0-1"},
+   {PI_BAD_CFG_INTERNAL , "bad parameter in gpioCfgInternals call"},
+   {PI_BAD_WAVE_BAUD    , "baud rate not 100-250000"},
+   {PI_TOO_MANY_PULSES  , "waveform has too many pulses"},
+   {PI_TOO_MANY_CHARS   , "waveform has too many chars"},
+   {PI_NOT_SERIAL_GPIO  , "no serial read in progress on gpio"},
+
+};
+
+static char * fmtMdeStr="RW540123";
+static char * fmtPudStr="ODU";
+
+static int cmdMatch(char * str)
+{
+   int i;
+
+   for (i=0; i<(sizeof(cmdInfo)/sizeof(cmdInfo_t)); i++)
+   {
+      if (strcasecmp(str, cmdInfo[i].name) == 0) return i;
+   }
+   return -1;
+}
+
+int cmdParse(char * buf, cmdCmd_t * cmd)
+{
+   char str[8];
+   int f, valid, idx, val;
+   char * ptr;
+   char c, t;
+
+   sscanf(buf, " %7s", str);
+
+   cmd->cmd = -1;
+
+   idx = cmdMatch(str);
+
+   if (idx < 0) return idx;
+
+   valid = 0;
+
+   cmd->cmd = cmdInfo[idx].cmd;
+   cmd->p1  = 0;
+   cmd->p2  = 0;
+
+   switch (cmdInfo[idx].vt)
+   {
+      case 1: /* BR1 BR2 HWVER NO TICK */
+         f = sscanf(buf, " %7s %c", str, &t);
+         if (f == 1) valid = 1;
+         break;
+
+      case 2: /* MODEG READ NC NP PFG PRG PRRG */
+         f = sscanf(buf, " %7s %d %c", str, &cmd->p1, &t);
+         if (f == 2) valid = 1;
+         break;
+   
+      case 3: /* WRITE PWM PRS PFS SERVO WDOG */
+         f = sscanf(buf, " %7s %d %d %c", str, &cmd->p1, &cmd->p2, &t);
+         if (f == 3) valid = 1;
+         break;
+   
+      case 4: /* NB */
+         f = sscanf(buf, " %7s %d %x %c", str, &cmd->p1, &cmd->p2, &t);
+         if (f == 3) valid = 1;
+         break;
+   
+      case 6: /* HELP */
+         valid = 1;
+         break;
+   
+      case 7: /* BC1 BC2 BS1 BS2 */
+         f = sscanf(buf, " %7s %x %c", str, &cmd->p1, &t);
+         if (f == 2) valid = 1;
+         break;
+   
+      case 8: /* MODES */
+         f = sscanf(buf, " %7s %d %c %c", str, &cmd->p1, &c, &t);
+         if (f == 3)
+         {
+            val = toupper(c);
+            ptr = strchr(fmtMdeStr, val);
+            if (ptr != NULL)
+            {
+               val = ptr - fmtMdeStr;
+               cmd->p2 = val;
+               valid = 1;
+            }
+         }
+         break;
+
+      case 9: /* PUD */
+         f = sscanf(buf, " %7s %d %c %c", str, &cmd->p1, &c, &t);
+         if (f == 3)
+         {
+            val = toupper(c);
+            ptr = strchr(fmtPudStr, val);
+            if (ptr != NULL)
+            {
+               val = ptr - fmtPudStr;
+               cmd->p2 = val;
+               valid = 1;
+            }
+         }
+         break;
+   }
+
+   if (valid) return idx;
+   else       return -1;
+}
+
+void cmdFatal(char *fmt, ...)
+{
+   char buf[128];
+   va_list ap;
+
+   va_start(ap, fmt);
+   vsnprintf(buf, sizeof(buf), fmt, ap);
+   va_end(ap);
+
+   fprintf(stderr, "%s\n", buf);
+
+   fflush(stderr);
+
+   exit(EXIT_FAILURE);
+}
+
+char * cmdErrStr(int error)
+{
+   int i;
+
+   for (i=0; i<(sizeof(errInfo)/sizeof(errInfo_t)); i++)
+   {
+      if (errInfo[i].error == error) return errInfo[i].str;
+   }
+   return "unknown error";
+}
+
diff --git a/command.h b/command.h
new file mode 100644 (file)
index 0000000..9d2da7d
--- /dev/null
+++ b/command.h
@@ -0,0 +1,58 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pigpio.h"
+
+typedef struct
+{
+   int    cmd;
+   char * name;
+   int    vt;
+   int    rv;
+} cmdInfo_t;
+
+extern cmdInfo_t cmdInfo[];
+
+extern char * cmdUsage;
+
+int    cmdParse(char * buf, cmdCmd_t * cmd);
+
+char * cmdErrStr(int error);
+
+void   cmdFatal(char *fmt, ...);
+
+#endif
diff --git a/demolib.c b/demolib.c
new file mode 100644 (file)
index 0000000..d2e9bd4
--- /dev/null
+++ b/demolib.c
@@ -0,0 +1,673 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+
+#include "pigpio.h"
+
+/* ===========================================================================
+THIS PROGRAM NEEDS THE I2C DEVICE AND DEVELOPMENT LIBRARY
+
+TO GET THE NEEDED FILES DO 
+
+sudo apt-get install libi2c-dev
+
+BEFORE RUNNING THE PROGRAM ENSURE THAT THE I2C DEVICE IS PRESENT
+
+sudo modprobe i2c-bcm2708
+sudo modprobe i2c-dev
+sudo chmod o+rw /dev/i2c*
+=========================================================================== */
+
+/*
+
+P1  Name  gpio    used for
+
+ 3  SDA   0/2     i2c
+ 5  SCL   1/3     i2c
+ 7  ---   4       LASER
+ 8  TXD   14      LED1
+10  RXD   15      LED2
+11  ---   17      SERVO 1
+12  ---   18      SERVO 2
+13  ---   21/27   SERVO 3
+15  ---   22      LED3
+16  ---   23      TI Launchpad
+18  ---   24      Sonar trigger
+19  MOSI  10      Sonar echo
+21  MISO  9       Motor B In 1
+22  ---   25      LDR
+23  SCLK  11      Motor B In 2
+24  CE0   8       Motor A In1
+26  CE1   7       Motor A In2
+
+*/
+
+#define LASER          4
+#define MOTOR_A_IN2    7
+#define MOTOR_A_IN1    8
+#define MOTOR_B_IN1    9
+#define SONAR_ECHO    10
+#define MOTOR_B_IN2   11
+#define LED1          14
+#define LED2          15
+#define SERVO1        17
+#define SERVO2        18
+#define SERVO3        21
+#define LED3          22
+#define LAUNCHPAD     23
+#define SONAR_TRIGGER 24
+#define LDR           25
+
+#define LEDS 4
+
+short rawAcc[3];
+short rawGyr[3];
+short rawMag[3];
+
+#define ROLL  0
+#define PITCH 1
+#define YAW   2
+
+#define ACC_ORIENTATION(X, Y, Z) \
+   {rawAcc[ROLL] = -X; rawAcc[PITCH] = -Y; rawAcc[YAW] = Z;}
+
+#define GYRO_ORIENTATION(X, Y, Z) \
+   {rawGyr[ROLL] = Y;  rawGyr[PITCH] = -X; rawGyr[YAW] = -Z;}
+
+#define MAG_ORIENTATION(X, Y, Z) \
+   {rawMag[ROLL] = X;  rawMag[PITCH] = Y;  rawMag[YAW] = -Z;}
+
+
+#define CALIBRATIONS 200
+
+#define ADXL345_I2C_ADDR  0x53
+#define ITG3200_I2C_ADDR  0x68
+
+static int version, micros=5, millis=100;
+
+static volatile unsigned long launchpadPulses;
+static volatile unsigned long launchpad5;
+static volatile unsigned long launchpad10;
+static volatile unsigned long launchpad15;
+static volatile unsigned long launchpadOutRange;
+static volatile int           launchpadErr;
+static volatile uint32_t      LDRrechargeTick;
+
+
+/* forward prototypes */
+
+void LEDlaserTick  (void);
+void motorTick(void);
+void i2cTick  (void);
+void servoTick  (void);
+void sonarLDRtick(void);
+
+void launchpadAlert(int gpio, int level, uint32_t tick);
+void sonarAlert(int gpio, int level, uint32_t tick);
+void LDRalert(int gpio, int level, uint32_t tick);
+
+void putTTY(char * buf);
+void putTTYstr(int row, int col, char * buf);
+
+
+int main(int argc, char *argv[])
+{
+   char str[256];
+
+   if (argc > 1) micros = atoi(argv[1]);
+
+   if (argc > 2) millis = atoi(argv[2]);
+
+   putTTY("\033c"); /* clear console */
+
+   gpioCfgBufferSize(millis);
+
+   gpioCfgClock(micros, PI_CLOCK_PCM, PI_CLOCK_PLLD);
+
+
+   /* before using the library you must call gpioInitialise */
+
+   version = gpioInitialise();
+
+   if (version >= 0)
+   {
+      /* initialise pins, only gpio numbers are supported */
+
+      gpioSetMode(SERVO1,        PI_OUTPUT);
+      gpioSetMode(SERVO2,        PI_OUTPUT);
+      gpioSetMode(SERVO3,        PI_OUTPUT);
+      gpioSetMode(LASER,         PI_OUTPUT);
+      gpioSetMode(LED1,          PI_OUTPUT);
+      gpioSetMode(LED2,          PI_OUTPUT);
+      gpioSetMode(LED3,          PI_OUTPUT);
+      gpioSetMode(MOTOR_A_IN1,   PI_OUTPUT);
+      gpioSetMode(MOTOR_A_IN2,   PI_OUTPUT);
+      gpioSetMode(MOTOR_B_IN1,   PI_OUTPUT);
+      gpioSetMode(MOTOR_B_IN2,   PI_OUTPUT);
+
+      gpioSetMode(SONAR_TRIGGER, PI_OUTPUT);
+      gpioWrite  (SONAR_TRIGGER, PI_OFF);
+
+      gpioSetMode(SONAR_ECHO,    PI_INPUT);
+      gpioSetMode(LAUNCHPAD,     PI_INPUT);
+      gpioSetMode(LDR,           PI_INPUT);
+
+      /* update i2c fifty times a second, timer #0 */
+
+      gpioSetTimerFunc(0, 20, i2cTick);
+
+      //gpioSetTimerFunc(0, 1000, servoTick);
+
+      /* update LEDs and laser once a second, timer #1 */
+
+      gpioSetTimerFunc(1, 1000, LEDlaserTick);
+      
+      /* update motors every three seconds, timer #2 */
+
+      gpioSetTimerFunc(2, 3000, motorTick);
+
+      /* update sonar/LDR 10 times a second, timer #3 */
+
+      gpioSetTimerFunc(3, 100, sonarLDRtick);
+
+      /* an attachecd TI launchpad is transmitting high pulses of
+      15, 35, 55, 75, ..., 975, 995 microseconds repeating with 50
+      microseconds off between each pulse */
+
+      gpioSetAlertFunc(LAUNCHPAD, launchpadAlert);
+
+      /* monitor sonar echos */
+
+      gpioSetAlertFunc(SONAR_ECHO, sonarAlert);
+
+      /* monitor LDR level changes */
+
+      gpioSetAlertFunc(LDR, LDRalert);
+
+      while (1)
+      {
+         sleep(1);
+
+         sprintf(str, "TI pulses %8ld", launchpadPulses);
+         putTTYstr(9, 1, str);
+
+         sprintf(str, "+/-5 %8ld", launchpad5);
+         putTTYstr(10, 6, str);
+
+         sprintf(str, "+/-10 %8ld", launchpad10);
+         putTTYstr(11, 5, str);
+
+         sprintf(str, "+/-15 %8ld", launchpad15);
+         putTTYstr(12, 5, str);
+
+         sprintf(str, "Others %8ld (last %d)    ",
+            launchpadOutRange, launchpadErr);
+         putTTYstr(13, 4, str);
+      }
+   }   
+
+   gpioTerminate();
+
+   return 0;
+}
+
+void LEDlaserTick(void)
+{
+   static int gpio[LEDS]={LED1, LED2, LED3, LASER};
+   static int pos [LEDS]={   0,    3,    6,     9};
+   static int inc [LEDS]={   1,    1,    1,     1};
+
+   static int vals[] = {0, 1, 2, 4, 8, 16, 32, 64, 128, 249};
+   int i;
+
+   for (i=0; i<LEDS; i++)
+   {
+      gpioPWM(gpio[i], vals[pos[i]]);
+
+      pos[i] += inc[i];
+
+      if ( (pos[i]>=(sizeof(vals)/4)) || (pos[i]<0) )
+      {
+         inc[i] = -inc[i];
+         pos[i] += inc[i];
+      }
+   }
+}
+
+void sonarLDRtick(void)
+{
+   /* trigger a sonar reading */
+
+   gpioWrite(SONAR_TRIGGER, PI_ON);
+   usleep(20);
+   gpioWrite(SONAR_TRIGGER, PI_OFF);
+
+   /* trigger a LDR reading */
+
+   gpioSetMode(LDR, PI_OUTPUT); /* drain capacitor */
+
+   gpioWrite(LDR, PI_OFF);
+
+   usleep(200);
+
+   LDRrechargeTick = gpioTick();
+
+   gpioSetMode(LDR, PI_INPUT);  /* start capacitor recharge */
+ }
+
+void motorTick(void)
+{
+   static int gpio_in1[2]={MOTOR_A_IN1, MOTOR_B_IN1};
+   static int gpio_in2[2]={MOTOR_A_IN2, MOTOR_B_IN2};
+   static int speed   [2]={         80,          80};
+   static int inc     [2]={         -50,         50};
+
+   int i;
+   char str[256];
+
+   for (i=0; i<2; i++)
+   {
+      speed[i]+=inc[i];
+
+      if (speed[i]<0)
+      {
+         gpioPWM(gpio_in1[i], -speed[i]);
+         gpioPWM(gpio_in2[i], 0);
+         if (speed[i] < -205) inc[i] = -inc[i];
+         sprintf(str, "MOT%d IN1=%3d IN2=%3d", i+1, -speed[i], 0);
+      }
+      else
+      {
+         gpioPWM(gpio_in2[i], speed[i]);
+         gpioPWM(gpio_in1[i], 0);
+         if (speed[i] > 205) inc[i] = -inc[i];
+         sprintf(str, "MOT%d IN1=%3d IN2=%3d", i+1, 0, speed[i]);
+      }
+      if (i) putTTYstr(7, 1, str); else putTTYstr(5, 1, str);
+   }
+}
+
+/* loads of code to read/write i2c */
+
+void selectDevice(int i2c, int addr, char * name)
+{
+   if (ioctl(i2c, I2C_SLAVE, addr) < 0)
+   {
+      fprintf(stderr, "%s not present\n", name);
+   }
+}
+
+void writeToDevice(int i2c, char * buf, int len)
+{
+   static int reported = 0;
+   if (write(i2c, buf, len) != len)
+   {
+      if (!reported)
+      {
+         fprintf(stderr, "Can't write to device\n");
+         reported = 1;
+      }
+   }
+   else reported = 0;
+}
+
+void readADXL345(int i2c)
+{
+   char buf[8];
+   static int reported = 0;
+
+   selectDevice(i2c, ADXL345_I2C_ADDR, "ADXL345");
+
+   writeToDevice(i2c, "\x32", 1);
+   
+   if (read(i2c, buf, 6) != 6)
+   {
+      if (!reported)
+      {
+         fprintf(stderr, "Unable to read from ADXL345\n");
+         reported = 1;
+      }
+   }
+   else
+   {
+      reported = 0;
+
+      ACC_ORIENTATION ( 
+         ((buf[1]<<8) | buf[0]),
+         ((buf[3]<<8) | buf[2]),
+         ((buf[5]<<8) | buf[4]) );
+   }
+}
+
+void readITG3200(int i2c)
+{
+   char buf[8];
+   static int reported = 0;
+
+   selectDevice(i2c, ITG3200_I2C_ADDR, "ITG3200");
+
+   writeToDevice(i2c, "\x1D", 1);
+   
+   if (read(i2c, buf, 6) != 6)
+   {
+      if (!reported)
+      {
+         fprintf(stderr, "Unable to read from ITG3200\n");
+         reported = 1;
+      }
+   }
+   else
+   {
+      reported = 0;
+
+      GYRO_ORIENTATION ( 
+         ((buf[0]<<8) | buf[1]),
+         ((buf[2]<<8) | buf[3]),
+         ((buf[4]<<8) | buf[5]) );
+   }
+}
+
+int initI2Cdevices(void)
+{
+   int i2c;
+
+   if ((i2c = open("/dev/i2c-0", O_RDWR)) < 0)
+   {
+      perror("Failed to open i2c bus");
+      exit(1);
+   }
+   
+   /* initialise ADXL345 */
+
+   selectDevice(i2c, ADXL345_I2C_ADDR, "ADXL345");
+
+   writeToDevice(i2c, "\x2d\x00", 2);
+   writeToDevice(i2c, "\x2d\x10", 2);
+   writeToDevice(i2c, "\x2d\x08", 2);
+   writeToDevice(i2c, "\x31\x00", 2);
+   writeToDevice(i2c, "\x31\x0b", 2);
+
+   /* initialise ITG3200 */
+
+   selectDevice(i2c, ITG3200_I2C_ADDR, "ITG3200");
+
+   writeToDevice(i2c, "\x16\b00011000", 2);
+
+   return i2c;
+}
+
+/* an attached IMU (GY-85) supplies orientation information which
+   is used to position the servos */
+
+float estimateAngle(int acc, int gyro, float oldAng, int elapsed)
+{
+   float angleAcc, angleInc, estAngle;
+   float secs;
+
+   secs = (float) elapsed / 1e6f;
+
+   angleAcc = (float) acc * 90.0f / 256.0f;
+
+   angleInc = (float) gyro * secs * 2000.0f / 32768.0f;
+
+   estAngle = 0.75 * (oldAng + angleInc) + 0.25 * angleAcc;
+
+   return estAngle;
+}
+
+void servoTick(void)
+{
+   static int wid1=1500, wid2=1500, wid3=1500;
+   static int inc1=50,   inc2=75,   inc3=100;
+
+   gpioServo(SERVO1, wid1);
+   gpioServo(SERVO2, wid2);
+   gpioServo(SERVO3, wid3);
+
+   wid1+=inc1; if ((wid1<1000) || (wid1>2000)) {inc1 = -inc1; wid1+=inc1;}
+   wid2+=inc2; if ((wid2<1000) || (wid2>2000)) {inc2 = -inc2; wid2+=inc2;}
+   wid3+=inc3; if ((wid3<1000) || (wid3>2000)) {inc3 = -inc3; wid3+=inc3;}
+}
+
+void i2cTick(void)
+{
+   static int inited = 0;
+   static int calibrated = 0;
+   static int calibrations = 0;
+   static int accCalibX  = 0, accCalibY  = 0, accCalibZ = 0;
+   static int gyroCalibX = 0, gyroCalibY = 0, gyroCalibZ = 0;
+   static int i2c;
+   static float X=0.0, Y=0.0, Z=0.0;
+
+   static uint32_t lastTick;
+
+   uint32_t tick;
+   int elapsed;
+   int pulse;
+   char str[256];
+
+   if (inited)
+   {
+      tick = gpioTick();
+      elapsed = tick - lastTick;
+      lastTick = tick;
+
+      readADXL345(i2c);
+      readITG3200(i2c);
+
+      if (calibrated)
+      {
+         X = estimateAngle(
+            rawAcc[ROLL], rawGyr[ROLL] -gyroCalibX, X, elapsed);
+
+         Y = estimateAngle(
+            rawAcc[PITCH], rawGyr[PITCH] - gyroCalibY, Y, elapsed);
+
+         Z = estimateAngle(
+            rawAcc[YAW], rawGyr[YAW] - gyroCalibZ, Z, elapsed);
+
+         pulse = 1500 + (Y * 1000 / 90);
+         if (pulse < 500)  pulse = 500;
+         if (pulse > 2500) pulse = 2500;
+         gpioServo(SERVO1, pulse);
+
+         pulse = 1500 - (X * 500 / 90);
+         if (pulse < 1000) pulse = 1000;
+         if (pulse > 2000) pulse = 2000;
+         gpioServo(SERVO2, pulse);
+
+         /* prefer Z but that doesn't change much */
+         pulse = 1500 - (Y * 500 / 90);
+         if (pulse < 800)  pulse = 800;
+         if (pulse > 2200) pulse = 2200;
+         gpioServo(SERVO3, pulse);
+
+         sprintf(str, "X=%4.0f Y=%4.0f Z=%4.0f ", X, Y, Z);
+         putTTYstr(1, 1, str);
+      }
+      else
+      {
+         accCalibX+=rawAcc[ROLL];
+         accCalibY+=rawAcc[PITCH];
+         accCalibZ+=rawAcc[YAW];
+
+         gyroCalibX+=rawGyr[ROLL];
+         gyroCalibY+=rawGyr[PITCH];
+         gyroCalibZ+=rawGyr[YAW];
+
+         if (++calibrations >= CALIBRATIONS)
+         {
+            accCalibX /= CALIBRATIONS;
+            accCalibY /= CALIBRATIONS;
+            accCalibZ /= CALIBRATIONS;
+
+            gyroCalibX /= CALIBRATIONS;
+            gyroCalibY /= CALIBRATIONS;
+            gyroCalibZ /= CALIBRATIONS;
+
+            calibrated = 1;
+         }
+      }
+   }
+   else
+   {
+      i2c = initI2Cdevices();
+
+      gpioServo(SERVO1, 1500);
+      gpioServo(SERVO2, 1500);
+      gpioServo(SERVO3, 1500);
+
+      inited = 1;
+   }
+}
+
+void sonarAlert(int gpio, int level, uint32_t tick)
+{
+   static uint32_t startTick;
+
+   int diffTick;
+   char str[256];
+
+   if (level == PI_ON)
+   {
+      startTick = tick;
+   }
+   else if (level == PI_OFF)
+   {
+      diffTick = tick - startTick;
+
+      if (diffTick < 26100)
+      {
+         sprintf(str, "Sonar %3d cms", (diffTick+29)/58);
+         putTTYstr(15, 1, str);
+      }
+   }
+}
+
+void LDRalert(int pin, int level, uint32_t tick)
+{
+   int diffTick;
+   char str[256];
+
+   if (level == PI_ON)
+   {
+      diffTick = tick - LDRrechargeTick;
+
+      sprintf(str, "LDR %4d micros", diffTick);
+      putTTYstr(17, 1, str);
+   }
+}
+
+void launchpadAlert(int pin, int level, uint32_t tick)
+{
+   static int inited = 0, lastTick, lastPulseLen;
+
+   int pulseLen, pulseDif;
+
+   if (inited)
+   {
+      pulseLen = tick - lastTick;
+      lastTick = tick;
+
+      if (level==0)
+      {
+         if (lastPulseLen)
+         {
+            pulseDif = pulseLen - lastPulseLen;
+
+            /* allow for wrap around */
+            if (pulseDif < 0) pulseDif += 1096;
+
+            /* now centre around expected value */
+            pulseDif -= 33;
+
+            if (pulseDif < 0) pulseDif = -pulseDif;
+
+            launchpadPulses++;
+
+            if      (pulseDif <= 5)
+            {
+               launchpad5++;
+            }
+            else if (pulseDif <= 10)
+            {
+               launchpad10++;
+            }
+            else if (pulseDif <= 15)
+            {
+               launchpad15++;
+            }
+            else
+            {
+               launchpadOutRange++;
+               launchpadErr = pulseDif;
+            }
+         }
+         lastPulseLen = pulseLen;
+      }
+   }
+   else
+   {
+      lastTick = tick;
+      lastPulseLen = 0;
+
+      launchpadPulses = 0;
+      launchpad5 = 0;
+      launchpad10 = 0;
+      launchpad15 = 0;
+      launchpadOutRange = 0;
+
+      inited = 1;
+   }
+}
+
+void putTTY(char * buf)
+{
+   write(1, buf, strlen(buf));
+}
+
+void putTTYstr(int row, int col, char * buf)
+{
+   char str[256];
+
+   sprintf(str, "\033[%d;%dH%s", row, col, buf);
+
+   putTTY(str);
+}
+
diff --git a/pig2vcd.c b/pig2vcd.c
new file mode 100644 (file)
index 0000000..c0afb0c
--- /dev/null
+++ b/pig2vcd.c
@@ -0,0 +1,121 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+#include "pigpio.h"
+
+/*
+This software converts pigpio notification reports
+into a VCD format understood by GTKWave.
+*/
+
+#define RS (sizeof(gpioReport_t))
+
+static char * timeStamp()
+{
+   static char buf[32];
+
+   struct timeval now;
+   struct tm tmp;
+
+   gettimeofday(&now, NULL);
+
+   localtime_r(&now.tv_sec, &tmp);
+   strftime(buf, sizeof(buf), "%F %T", &tmp);
+
+   return buf;
+}
+
+int symbol(int bit)
+{
+   if (bit < 26) return ('A' + bit);
+   else          return ('a' + bit - 26);
+}
+
+int main(int argc, char * argv[])
+{
+   int b, r, v;
+   uint32_t t0;
+   uint32_t lastLevel, changed;
+
+   gpioReport_t report;
+
+   r=read(STDIN_FILENO, &report, RS);
+
+   if (r != RS) exit(-1);
+
+   printf("$date %s $end\n", timeStamp());
+   printf("$version pig2vcd V1 $end\n");
+   printf("$timescale 1 us $end\n");
+   printf("$scope module top $end\n");
+
+   for (b=0; b<32; b++)
+      printf("$var wire 1 %c %d $end\n", symbol(b), b);
+        
+   printf("$upscope $end\n");
+   printf("$enddefinitions $end\n");
+         
+   t0 = report.tick;
+   lastLevel =0;
+
+   while ((r=read(STDIN_FILENO, &report, RS)) == RS)
+   {
+      if (report.level != lastLevel)
+      {
+         printf("#%u\n", report.tick - t0);
+
+         changed = report.level ^ lastLevel;
+
+         lastLevel = report.level;
+
+         for (b=0; b<32; b++)
+         {
+            if (changed & (1<<b))
+            {
+               if (report.level & (1<<b)) v='1'; else v='0';
+
+               printf("%c%c\n", v, symbol(b));
+            }
+         }
+      }
+   }
+   return 0;
+}
+
diff --git a/pigpio.c b/pigpio.c
new file mode 100644 (file)
index 0000000..c577ced
--- /dev/null
+++ b/pigpio.c
@@ -0,0 +1,5085 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/* pigpio version 6 */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <poll.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h> 
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/select.h>
+
+#include "pigpio.h"
+#include "command.h"
+
+/* --------------------------------------------------------------- */
+
+/*
+ 0 GPFSEL0   GPIO Function Select 0
+ 1 GPFSEL1   GPIO Function Select 1
+ 2 GPFSEL2   GPIO Function Select 2
+ 3 GPFSEL3   GPIO Function Select 3
+ 4 GPFSEL4   GPIO Function Select 4
+ 5 GPFSEL5   GPIO Function Select 5
+ 6 -         Reserved
+ 7 GPSET0    GPIO Pin Output Set 0
+ 8 GPSET1    GPIO Pin Output Set 1
+ 9 -         Reserved
+10 GPCLR0    GPIO Pin Output Clear 0
+11 GPCLR1    GPIO Pin Output Clear 1
+12 -         Reserved
+13 GPLEV0    GPIO Pin Level 0
+14 GPLEV1    GPIO Pin Level 1
+15 -         Reserved
+16 GPEDS0    GPIO Pin Event Detect Status 0
+17 GPEDS1    GPIO Pin Event Detect Status 1
+18 -         Reserved
+19 GPREN0    GPIO Pin Rising Edge Detect Enable 0
+20 GPREN1    GPIO Pin Rising Edge Detect Enable 1
+21 -         Reserved
+22 GPFEN0    GPIO Pin Falling Edge Detect Enable 0
+23 GPFEN1    GPIO Pin Falling Edge Detect Enable 1
+24 -         Reserved
+25 GPHEN0    GPIO Pin High Detect Enable 0
+26 GPHEN1    GPIO Pin High Detect Enable 1
+27 -         Reserved
+28 GPLEN0    GPIO Pin Low Detect Enable 0
+29 GPLEN1    GPIO Pin Low Detect Enable 1
+30 -         Reserved
+31 GPAREN0   GPIO Pin Async. Rising Edge Detect 0
+32 GPAREN1   GPIO Pin Async. Rising Edge Detect 1
+33 -         Reserved
+34 GPAFEN0   GPIO Pin Async. Falling Edge Detect 0
+35 GPAFEN1   GPIO Pin Async. Falling Edge Detect 1
+36 -         Reserved
+37 GPPUD     GPIO Pin Pull-up/down Enable
+38 GPPUDCLK0 GPIO Pin Pull-up/down Enable Clock 0
+39 GPPUDCLK1 GPIO Pin Pull-up/down Enable Clock 1
+40 -         Reserved
+41 -         Test
+*/
+
+/*
+0 CS           DMA Channel 0 Control and Status
+1 CPI_ONBLK_AD DMA Channel 0 Control Block Address
+2 TI           DMA Channel 0 CB Word 0 (Transfer Information)
+3 SOURCE_AD    DMA Channel 0 CB Word 1 (Source Address)
+4 DEST_AD      DMA Channel 0 CB Word 2 (Destination Address)
+5 TXFR_LEN     DMA Channel 0 CB Word 3 (Transfer Length)
+6 STRIDE       DMA Channel 0 CB Word 4 (2D Stride)
+7 NEXTCPI_ONBK DMA Channel 0 CB Word 5 (Next CB Address)
+8 DEBUG        DMA Channel 0 Debug 
+*/
+
+/*
+DEBUG register bits
+
+bit 2 READ_ERROR
+
+   Slave Read Response Error RW 0x0
+
+   Set if the read operation returned an error value on 
+   the read response bus. It can be cleared by writing 
+   a 1.
+
+bit 1 FIFO_ERROR
+
+   Fifo Error RW 0x0
+
+   Set if the optional read Fifo records an error 
+   condition. It can be cleared by writing a 1.
+
+bit 0 READ_LAST_NOT_SET_ERROR
+
+   Read Last Not Set Error RW 0x0
+
+   If the AXI read last signal was not set when 
+   expected, then this error bit will be set. It can be 
+   cleared by writing a 1. 
+*/
+
+/*
+0 CTL        PWM Control
+1 STA        PWM Status
+2 DMAC       PWM DMA Configuration
+4 RNG1       PWM Channel 1 Range
+5 DAT1       PWM Channel 1 Data
+6 FIF1       PWM FIFO Input
+8 RNG2       PWM Channel 2 Range
+9 DAT2       PWM Channel 2 Data
+*/
+
+/*
+0 PCM_CS     PCM Control and Status
+1 PCM_FIFO   PCM FIFO Data
+2 PCM_MODE   PCM Mode
+3 PCM_RXC    PCM Receive Configuration
+4 PCM_TXC    PCM Transmit Configuration
+5 PCM_DREQ   PCM DMA Request Level
+6 PCM_INTEN  PCM Interrupt Enables
+7 PCM_INTSTC PCM Interrupt Status & Clear
+8 PCM_GRAY   PCM Gray Mode Control
+*/
+
+/*
+0 CS  System Timer Control/Status
+1 CLO System Timer Counter Lower 32 bits
+2 CHI System Timer Counter Higher 32 bits
+3 C0  System Timer Compare 0
+4 C1  System Timer Compare 1
+5 C2  System Timer Compare 2
+6 C3  System Timer Compare 3
+*/
+
+/* --------------------------------------------------------------- */
+
+#define THOUSAND 1000
+#define MILLION  1000000
+#define BILLION  1000000000
+
+#define BANK (gpio>>5)
+
+#define BIT  (1<<(gpio&0x1F))
+
+
+#define CHECK_INITED                                               \
+   do                                                              \
+   {                                                               \
+      if (!libInitialised)                                         \
+      {                                                            \
+         fprintf(stderr,                                           \
+            "%s %s: pigpio uninitialised, call gpioInitialise()\n",\
+            myTimeStamp(), __FUNCTION__);                          \
+         return PI_NOT_INITIALISED;                                \
+      }                                                            \
+   }                                                               \
+   while (0)
+
+#define CHECK_NOT_INITED                                           \
+   do                                                              \
+   {                                                               \
+      if (libInitialised)                                          \
+      {                                                            \
+         fprintf(stderr,                                           \
+            "%s %s: pigpio initialised, call gpioTerminate()\n",   \
+            myTimeStamp(), __FUNCTION__);                          \
+         return PI_INITIALISED;                                    \
+      }                                                            \
+   }                                                               \
+   while (0)
+
+#define DBG(level, format, arg...)                                 \
+   do                                                              \
+   {                                                               \
+      if (gpioCfg.dbgLevel >= level)                               \
+         fprintf(stderr, "%s %s: " format "\n" ,                   \
+            myTimeStamp(), __FUNCTION__ , ## arg);                 \
+   }                                                               \
+   while (0)
+
+#define SOFT_ERROR(x, format, arg...)                              \
+   do                                                              \
+   {                                                               \
+      fprintf(stderr, "%s %s: " format "\n",                       \
+         myTimeStamp(), __FUNCTION__ , ## arg);                    \
+      return x;                                                    \
+   }                                                               \
+   while (0)
+
+#define TIMER_ADD(a, b, result)                                    \
+   do                                                              \
+   {                                                               \
+      (result)->tv_sec =  (a)->tv_sec  + (b)->tv_sec;              \
+      (result)->tv_nsec = (a)->tv_nsec + (b)->tv_nsec;             \
+      if ((result)->tv_nsec >= BILLION)                            \
+      {                                                            \
+        ++(result)->tv_sec;                                        \
+        (result)->tv_nsec -= BILLION;                              \
+      }                                                            \
+   }                                                               \
+   while (0)
+
+#define TIMER_SUB(a, b, result)                                    \
+   do                                                              \
+   {                                                               \
+      (result)->tv_sec =  (a)->tv_sec  - (b)->tv_sec;              \
+      (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec;             \
+      if ((result)->tv_nsec < 0)                                   \
+      {                                                            \
+         --(result)->tv_sec;                                       \
+         (result)->tv_nsec += BILLION;                             \
+      }                                                            \
+   }                                                               \
+   while (0)
+
+#define DMA_BUS_ADR 0x40000000
+
+#define CLK_BASE   0x20101000
+#define DMA_BASE   0x20007000
+#define DMA15_BASE 0x20E05000
+#define GPIO_BASE  0x20200000
+#define PCM_BASE   0x20203000
+#define PWM_BASE   0x2020C000
+#define SPI0_BASE  0x20204000
+#define SYST_BASE  0x20003000
+#define UART0_BASE 0x20201000
+#define UART1_BASE 0x20215000
+
+#define DMA_LEN   0x1000 /* allow access to all channels */
+#define CLK_LEN   0xA8
+#define GPIO_LEN  0xB4
+#define SYST_LEN  0x1C
+#define PCM_LEN   0x24
+#define PWM_LEN   0x28
+
+#define DMA_ENABLE (0xFF0/4)
+
+#define GPFSEL0    0
+
+#define GPSET0     7
+#define GPSET1     8
+
+#define GPCLR0    10
+#define GPCLR1    11
+
+#define GPLEV0    13
+#define GPLEV1    14
+
+#define GPEDS0    16
+#define GPEDS1    17
+
+#define GPREN0    19
+#define GPREN1    20
+#define GPFEN0    22
+#define GPFEN1    23
+#define GPHEN0    25
+#define GPHEN1    26
+#define GPLEN0    28
+#define GPLEN1    29
+#define GPAREN0   31
+#define GPAREN1   32
+#define GPAFEN0   34
+#define GPAFEN1   35
+
+#define GPPUD     37
+#define GPPUDCLK0 38
+#define GPPUDCLK1 39
+
+#define DMA_CS        0
+#define DMA_CONBLK_AD 1
+#define DMA_DEBUG     8
+
+/* DMA CS Control and Status bits */
+#define DMA_CHANNEL_RESET       (1<<31)
+#define DMA_WAIT_ON_WRITES      (1<<28)
+#define        DMA_PANIC_PRIORITY(x) ((x)<<20)
+#define        DMA_PRIORITY(x)       ((x)<<16)
+#define DMA_INTERRUPT_STATUS    (1<< 2)
+#define DMA_END_FLAG            (1<< 1)
+#define DMA_ACTIVATE            (1<< 0)
+
+/* DMA control block "info" field bits */
+#define DMA_NO_WIDE_BURSTS          (1<<26)
+#define        DMA_PERIPHERAL_MAPPING(x) ((x)<<16)
+#define        DMA_BURST_LENGTH(x)       ((x)<<12)
+#define DMA_SRC_IGNORE              (1<<11)
+#define DMA_SRC_DREQ                (1<<10)
+#define DMA_SRC_INC                 (1<< 8)
+#define DMA_DEST_IGNORE             (1<< 7)
+#define DMA_DEST_DREQ               (1<< 6)
+#define DMA_DEST_INC                (1<< 4)
+#define DMA_WAIT_RESP               (1<< 3)
+
+#define DMA_DEBUG_READ_ERR           (1<<2)
+#define DMA_DEBUG_FIFO_ERR           (1<<1)
+#define DMA_DEBUG_RD_LST_NOT_SET_ERR (1<<0)
+
+#define PWM_CTL      0
+#define PWM_STA      1
+#define PWM_DMAC     2
+#define PWM_RNG1     4
+#define PWM_DAT1     5
+#define PWM_FIFO     6
+#define PWM_RNG2     8
+#define PWM_DAT2     9
+
+#define PWM_CTL_CLRF1 (1<<6)
+#define PWM_CTL_USEF1 (1<<5)
+#define PWM_CTL_MODE1 (1<<1)
+#define PWM_CTL_PWEN1 (1<<0)
+
+#define PWM_DMAC_ENAB      (1 <<31)
+#define PWM_DMAC_PANIC(x) ((x)<< 8)
+#define PWM_DMAC_DREQ(x)   (x)
+
+#define PCM_CS     0
+#define PCM_FIFO   1
+#define PCM_MODE   2
+#define PCM_RXC    3
+#define PCM_TXC    4
+#define PCM_DREQ   5
+#define PCM_INTEN  6
+#define PCM_INTSTC 7
+#define PCM_GRAY   8
+
+#define PCM_CS_STBY     (1 <<25)
+#define PCM_CS_SYNC     (1 <<24)
+#define PCM_CS_RXSEX    (1 <<23)
+#define PCM_CS_RXERR    (1 <<16)
+#define PCM_CS_TXERR    (1 <<15)
+#define PCM_CS_DMAEN    (1  <<9)
+#define PCM_CS_RXTHR(x) ((x)<<7)
+#define PCM_CS_TXTHR(x) ((x)<<5)
+#define PCM_CS_RXCLR    (1  <<4)
+#define PCM_CS_TXCLR    (1  <<3)
+#define PCM_CS_TXON     (1  <<2)
+#define PCM_CS_RXON     (1  <<1)
+#define PCM_CS_EN       (1  <<0)
+
+#define PCM_MODE_CLK_DIS  (1  <<28)
+#define PCM_MODE_PDMN     (1  <<27)
+#define PCM_MODE_PDME     (1  <<26)
+#define PCM_MODE_FRXP     (1  <<25)
+#define PCM_MODE_FTXP     (1  <<24)
+#define PCM_MODE_CLKM     (1  <<23)
+#define PCM_MODE_CLKI     (1  <<22)
+#define PCM_MODE_FSM      (1  <<21)
+#define PCM_MODE_FSI      (1  <<20)
+#define PCM_MODE_FLEN(x)  ((x)<<10)
+#define PCM_MODE_FSLEN(x) ((x)<< 0)
+
+#define PCM_RXC_CH1WEX    (1  <<31)
+#define PCM_RXC_CH1EN     (1  <<30)
+#define PCM_RXC_CH1POS(x) ((x)<<20)
+#define PCM_RXC_CH1WID(x) ((x)<<16)
+#define PCM_RXC_CH2WEX    (1  <<15)
+#define PCM_RXC_CH2EN     (1  <<14)
+#define PCM_RXC_CH2POS(x) ((x)<< 4)
+#define PCM_RXC_CH2WID(x) ((x)<< 0)
+
+#define PCM_TXC_CH1WEX    (1  <<31)
+#define PCM_TXC_CH1EN     (1  <<30)
+#define PCM_TXC_CH1POS(x) ((x)<<20)
+#define PCM_TXC_CH1WID(x) ((x)<<16)
+#define PCM_TXC_CH2WEX    (1  <<15)
+#define PCM_TXC_CH2EN     (1  <<14)
+#define PCM_TXC_CH2POS(x) ((x)<< 4)
+#define PCM_TXC_CH2WID(x) ((x)<< 0)
+
+#define PCM_DREQ_TX_PANIC(x) ((x)<<24)
+#define PCM_DREQ_RX_PANIC(x) ((x)<<16)
+#define PCM_DREQ_TX_REQ_L(x) ((x)<< 8)
+#define PCM_DREQ_RX_REQ_L(x) ((x)<< 0)
+
+#define PCM_INTEN_RXERR (1<<3)
+#define PCM_INTEN_TXERR (1<<2)
+#define PCM_INTEN_RXR   (1<<1)
+#define PCM_INTEN_TXW   (1<<0)
+
+#define PCM_INTSTC_RXERR (1<<3)
+#define PCM_INTSTC_TXERR (1<<2)
+#define PCM_INTSTC_RXR   (1<<1)
+#define PCM_INTSTC_TXW   (1<<0)
+
+#define PCM_GRAY_FLUSH (1<<2)
+#define PCM_GRAY_CLR   (1<<1)
+#define PCM_GRAY_EN    (1<<0)
+
+#define CLK_PASSWD  (0x5A<<24)
+
+#define CLK_CTL_MASH(x)((x)<<9)
+#define CLK_CTL_BUSY    (1 <<7)
+#define CLK_CTL_KILL    (1 <<5)
+#define CLK_CTL_ENAB    (1 <<4)
+#define CLK_CTL_SRC(x) ((x)<<0)
+
+#define CLK_CTL_SRC_OSC  1  /*  19.2 MHz */
+#define CLK_CTL_SRC_PLLD 6  /* 500.0 MHz */
+
+#define CLK_DIV_DIVI(x) ((x)<<12)
+#define CLK_DIV_DIVF(x) ((x)<< 0)
+
+#define CLK_PCMCTL 38
+#define CLK_PCMDIV 39
+
+#define CLK_PWMCTL 40
+#define CLK_PWMDIV 41
+
+#define SYST_CS      0
+#define SYST_CLO     1
+#define SYST_CHI     2
+
+/* --------------------------------------------------------------- */
+
+#define NORMAL_DMA (DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP)
+
+#define TIMED_DMA(x)  (DMA_DEST_DREQ | DMA_PERIPHERAL_MAPPING(x))
+
+#define DBG_MIN_LEVEL 0
+#define DBG_STARTUP   1
+#define DBG_DMACBS    2
+#define DBG_USER      3
+#define DBG_INTERNAL  4
+#define DBG_SLOW_TICK 5
+#define DBG_FAST_TICK 6
+#define DBG_MAX_LEVEL 6
+
+#define GPIO_UNDEFINED 0
+#define GPIO_INPUT     1
+#define GPIO_OUTPUT    2
+#define GPIO_PWM       3
+#define GPIO_SERVO     4
+#define GPIO_ALTERNATE 5
+
+#define STACK_SIZE (256*1024)
+
+#define PAGE_SIZE 4096
+
+#define PWM_FREQS 18
+
+#define CYCLES_PER_BLOCK 80
+#define PULSE_PER_CYCLE  25
+
+#define PAGES_PER_BLOCK   53
+
+#define CBS_PER_IPAGE    117
+#define LVS_PER_IPAGE     38
+#define OFF_PER_IPAGE     38
+#define TCK_PER_IPAGE      2
+#define ON_PER_IPAGE       2
+
+#define CBS_PER_OPAGE    118
+#define ONOFF_PER_OPAGE   79
+
+#define CBS_PER_CYCLE ((PULSE_PER_CYCLE*3)+2)
+
+#define NUM_CBS (CBS_PER_CYCLE * bufferCycles)
+
+#define SUPERCYCLE 800
+#define SUPERLEVEL 20000
+
+#define BLOCK_SIZE (PAGES_PER_BLOCK*PAGE_SIZE)
+
+#define DMA_PAGES (PAGES_PER_BLOCK * bufferBlocks)
+
+#define TICKSLOTS 50
+
+#define PI_NOTIFY_SLOTS  32
+
+#define PI_NOTIFY_CLOSED  0
+#define PI_NOTIFY_CLOSING 1
+#define PI_NOTIFY_OPENED  2
+#define PI_NOTIFY_RUNNING 3
+#define PI_NOTIFY_PAUSED  4
+
+#define PI_WFRX_NONE   0
+#define PI_WFRX_SERIAL 1
+#define PI_WF_MICROS   2
+
+#define PI_WAVE_MAX_PULSES 3000
+
+#define DATUMS 2000
+
+#define DEFAULT_PWM_IDX 5
+
+#define MAX_EMITS (PIPE_BUF / sizeof(gpioReport_t))
+
+/* --------------------------------------------------------------- */
+
+typedef void (*callbk_t) ();
+
+typedef struct { /* linux/arch/arm/mach-bcm2708/include/mach/dma.h */
+   unsigned long info;
+   unsigned long src;
+   unsigned long dst;
+   unsigned long length;
+   unsigned long stride;
+   unsigned long next;
+   unsigned long pad[2];
+} dmaCbs_t;
+
+typedef struct
+{
+   dmaCbs_t cb           [128];
+} dmaPage_t;
+
+typedef struct
+{
+   dmaCbs_t cb           [CBS_PER_IPAGE];
+   uint32_t level        [LVS_PER_IPAGE];
+   uint32_t gpioOff      [OFF_PER_IPAGE];
+   uint32_t tick         [TCK_PER_IPAGE];
+   uint32_t gpioOn       [ON_PER_IPAGE];
+   uint32_t periphData;
+   uint32_t pad[7];
+} dmaIPage_t;
+
+typedef struct
+{
+   dmaCbs_t cb           [CBS_PER_OPAGE];
+   uint32_t gpioOnOff    [ONOFF_PER_OPAGE];
+   uint32_t periphData;
+} dmaOPage_t;
+
+typedef struct
+{
+   uint8_t  is;
+   uint8_t  pad;
+   uint16_t width;
+   uint16_t range; /* duty cycles specified by 0 .. range */
+   uint16_t freqIdx;
+} gpioInfo_t;
+
+typedef struct
+{
+   callbk_t func;
+   unsigned ex;
+   void *   userdata;
+   unsigned timeout;
+   uint32_t tick;
+} gpioAlert_t;
+
+typedef struct
+{
+   callbk_t func;
+   unsigned ex;
+   void *   userdata;
+} gpioSignal_t;
+
+typedef struct
+{
+   callbk_t func;
+   unsigned ex;
+   void *   userdata;
+   uint32_t bits;
+} gpioGetSamples_t;
+
+typedef struct
+{
+   callbk_t        func;
+   unsigned        ex;
+   void *          userdata;
+   unsigned        id;
+   unsigned        running;
+   unsigned        millis;
+   struct timespec nextTick;
+   pthread_t       pthId;
+} gpioTimer_t;
+
+typedef struct
+{
+   uint16_t valid;
+   uint16_t bits;
+   uint16_t divi;
+   uint16_t divf;
+   uint16_t mash;
+   uint16_t servoIdx;
+   uint16_t pwmIdx;
+} clkCfg_t;
+
+typedef struct
+{
+   uint16_t seqno;
+   uint16_t state;
+   uint32_t bits;
+   int      fd;
+   int      pipe;
+} gpioNotify_t;
+
+typedef struct
+{
+   uint32_t startTick;
+   uint32_t alertTicks;
+   uint32_t diffTick[TICKSLOTS];
+   uint32_t cbTicks;
+   uint32_t cbCalls;
+   uint32_t maxEmit;
+   uint32_t emitFrags;
+   uint32_t maxSamples;
+   uint32_t numSamples;
+} gpioStats_t;
+
+typedef struct
+{
+   unsigned bufferMilliseconds;
+   unsigned clockMicros;
+   unsigned clockPeriph;
+   unsigned clockSource;
+   unsigned DMAprimaryChannel;
+   unsigned DMAsecondaryChannel;
+   unsigned socketPort;
+   unsigned ifFlags;
+   int      dbgLevel;
+   unsigned showStats;
+} gpioCfg_t;
+
+typedef struct
+{
+   uint32_t micros;
+   uint32_t highMicros;
+   uint32_t maxMicros;
+   uint32_t pulses;
+   uint32_t highPulses;
+   uint32_t maxPulses;
+   uint32_t cbs;
+   uint32_t highCbs;
+   uint32_t maxCbs;
+} wfStats_t;
+
+typedef struct
+{
+   gpioRx_t * rxp;
+   uint32_t   baud;
+   uint32_t   fullBit;
+   uint32_t   halfBit;
+   uint32_t   startBitTick;
+   uint32_t   nextBitDiff;
+   int        bit;
+   int        byte;
+   int        level;
+   int        mode;
+} wfRx_t;
+
+
+/* --------------------------------------------------------------- */
+
+/* initialise once then preserve */
+
+static volatile gpioCfg_t gpioCfg =
+{
+   PI_DEFAULT_BUFFER_MILLIS,
+   PI_DEFAULT_CLK_MICROS,
+   PI_DEFAULT_CLK_PERIPHERAL,
+   PI_DEFAULT_CLK_SOURCE,
+   PI_DEFAULT_DMA_PRIMARY_CHANNEL,
+   PI_DEFAULT_DMA_SECONDARY_CHANNEL,
+   PI_DEFAULT_SOCKET_PORT,
+   PI_DEFAULT_IF_FLAGS,
+   0,
+   0,
+};
+
+static volatile gpioStats_t gpioStats;
+
+/* initialise every gpioInitialise */
+
+static struct timespec libStarted;
+
+/* initialse if not libInitialised */
+
+static gpioPulse_t wf[3][PI_WAVE_MAX_PULSES];
+
+static int wfc[3]={0, 0, 0};
+
+static int wfcur=0;
+
+static wfStats_t wfStats=
+{
+   0, 0, -1,
+   0, 0, PI_WAVE_MAX_PULSES,
+   0, 0, (PAGES_PER_BLOCK * CBS_PER_OPAGE)
+};
+
+static wfRx_t wfRx[PI_MAX_USER_GPIO+1];
+
+static volatile uint32_t alertBits    = 0;
+static volatile uint32_t monitorBits  = 0;
+static volatile uint32_t notifyBits   = 0;
+
+static volatile int DMAstarted = 0;
+
+static int      libInitialised   = 0;
+static unsigned hardwareRevision = 0;
+
+static int pthAlertRunning  = 0;
+static int pthFifoRunning   = 0;
+static int pthSocketRunning = 0;
+
+static gpioAlert_t      gpioAlert  [PI_MAX_USER_GPIO+1];
+
+static gpioGetSamples_t gpioGetSamples;
+
+static gpioInfo_t       gpioInfo   [PI_MAX_USER_GPIO+1];
+
+static gpioNotify_t     gpioNotify [PI_NOTIFY_SLOTS];
+
+static gpioSignal_t     gpioSignal [PI_MAX_SIGNUM+1];
+
+static gpioTimer_t      gpioTimer  [PI_MAX_TIMER+1];
+
+static int pwmFreq[PWM_FREQS];
+
+/* no initialisation required */
+
+static unsigned bufferBlocks; /* number of blocks in buffer */
+static unsigned bufferCycles; /* number of cycles */
+
+static pthread_attr_t pthAttr;
+
+static pthread_t pthAlert;
+static pthread_t pthFifo;
+static pthread_t pthSocket;
+
+static gpioSample_t gpioSample[DATUMS];
+static gpioReport_t gpioReport[DATUMS];
+
+/* resources which must be released on gpioTerminate */
+
+static FILE * inpFifo = NULL;
+static FILE * outFifo = NULL;
+
+static int fdLock = -1;
+static int fdMem  = -1;
+static int fdSock = -1;
+
+static dmaPage_t * * dmaBloc = MAP_FAILED;
+static dmaPage_t * * dmaVirt = MAP_FAILED;
+static dmaPage_t * * dmaPhys = MAP_FAILED;
+
+static dmaIPage_t * * dmaIVirt = MAP_FAILED;
+static dmaIPage_t * * dmaIPhys = MAP_FAILED;
+
+static dmaOPage_t * * dmaOVirt = MAP_FAILED;
+static dmaOPage_t * * dmaOPhys = MAP_FAILED;
+
+static volatile uint32_t  * clkReg  = MAP_FAILED;
+static volatile uint32_t  * dmaReg  = MAP_FAILED;
+static volatile uint32_t  * gpioReg = MAP_FAILED;
+static volatile uint32_t  * pcmReg  = MAP_FAILED;
+static volatile uint32_t  * pwmReg  = MAP_FAILED;
+static volatile uint32_t  * systReg = MAP_FAILED;
+
+static volatile uint32_t  * dmaIn   = MAP_FAILED;
+static volatile uint32_t  * dmaOut  = MAP_FAILED;
+
+/* constant data */
+
+static const clkCfg_t clkCfg[]=
+{
+   /* valid bits  divi  divf mash servo                pwm */
+      {   0,   0,    0,    0,   0,    0,                  0}, /*  0 */
+      {   1,   9,    2,  546,   1,   17,    DEFAULT_PWM_IDX}, /*  1 */
+      {   1,  19,    2,   86,   1,   16,    DEFAULT_PWM_IDX}, /*  2 */
+      {   0,  19,    3,  129,   1,    0,                  0}, /*  3 */
+      {   1,  11,    6, 4021,   1,   15,    DEFAULT_PWM_IDX}, /*  4 */
+      {   1,   8,   12,    0,   0,   14,    DEFAULT_PWM_IDX}, /*  5 */
+      {   0,  23,    5,   35,   1,    0,                  0}, /*  6 */
+      {   0,  27, 4004,    0,   1,    0,                  0}, /*  7 */
+      {   1,  51,    3,   48,   1,   13,    DEFAULT_PWM_IDX}, /*  8 */
+      {   0,  43,    4,   76,   1,    0,                  0}, /*  9 */
+      {   1,   8,   24,    0,   0,   12,    DEFAULT_PWM_IDX}, /* 10 */
+};
+
+static const uint16_t pwmCycles[PWM_FREQS]=
+   {  1,    2,    4,    5,    8,   10,   16,    20,    25,
+     32,   40,   50,   80,  100,  160,  200,   400,   800};
+static const uint16_t pwmRealRange[PWM_FREQS]=
+   { 25,   50,  100,  125,  200,  250,  400,   500,   625,
+    800, 1000, 1250, 2000, 2500, 4000, 5000, 10000, 20000};
+/* ======================================================================= */
+
+static void intNotifyBits(void);
+
+static int  gpioNotifyOpenInBand(int fd);
+
+static void myGpioSleep(int seconds, int micros)
+{
+   struct timespec ts, rem;
+
+   ts.tv_sec  = seconds;
+   ts.tv_nsec = micros * 1000;
+
+   while (clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem))
+   {
+      /* copy remaining time to ts */
+      ts.tv_sec  = rem.tv_sec;
+      ts.tv_nsec = rem.tv_nsec;
+   }
+}
+
+static uint32_t myGpioDelay(uint32_t micros)
+{
+   uint32_t start;
+
+   start = systReg[SYST_CLO];
+
+   if (micros < 100) while ((systReg[SYST_CLO] - start) <= micros) ;
+
+   else myGpioSleep(micros/MILLION, micros%MILLION);
+
+   return (systReg[SYST_CLO] - start);
+}
+
+static char * myTimeStamp()
+{
+   static struct timeval last;
+   static char buf[32];
+   struct timeval now;
+
+   struct tm tmp;
+
+   gettimeofday(&now, NULL);
+
+   if (now.tv_sec != last.tv_sec)
+   {
+      localtime_r(&now.tv_sec, &tmp);
+      strftime(buf, sizeof(buf), "%F %T", &tmp);
+      last.tv_sec = now.tv_sec;
+   }
+
+   return buf;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myCreatePipe(char * name, int perm)
+{
+   unlink(name);
+
+   mkfifo(name, perm);
+
+   if (chmod(name, perm) < 0)
+   {
+      DBG(DBG_MIN_LEVEL, "Can't set permissions (%d) for %s, %m", perm, name);
+      return;
+   }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myOffPageSlot(int pos, int * page, int * slot)
+{
+   *page = pos/OFF_PER_IPAGE;
+   *slot = pos%OFF_PER_IPAGE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myLvsPageSlot(int pos, int * page, int * slot)
+{
+   *page = pos/LVS_PER_IPAGE;
+   *slot = pos%LVS_PER_IPAGE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myTckPageSlot(int pos, int * page, int * slot)
+{
+   *page = pos/TCK_PER_IPAGE;
+   *slot = pos%TCK_PER_IPAGE;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t myGetLevel(int pos)
+{
+   uint32_t level;
+   int page, slot;
+
+   myLvsPageSlot(pos, &page, &slot);
+
+   level = dmaIVirt[page]->level[slot];
+
+   return level;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t myGetTick(int pos)
+{
+   uint32_t tick;
+   int page, slot;
+
+   myTckPageSlot(pos, &page, &slot);
+
+   tick = dmaIVirt[page]->tick[slot];
+
+   return tick;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myDoCommand(cmdCmd_t * cmd)
+{
+   int p1, p2, res;
+
+   p1  = cmd->p1;
+   p2  = cmd->p2;
+
+   res = 0;
+
+   switch (cmd->cmd)
+   {
+      case PI_CMD_MODES:
+         res = gpioSetMode(p1, p2);
+         break;
+
+      case PI_CMD_MODEG:
+         res = gpioGetMode(p1);
+         break;
+
+      case PI_CMD_PUD:
+         res = gpioSetPullUpDown(p1, p2);
+         break;
+
+      case PI_CMD_READ:
+         res = gpioRead(p1);
+         break;
+
+      case PI_CMD_WRITE:
+         res = gpioWrite(p1, p2);
+         break;
+
+      case PI_CMD_PWM:
+         res = gpioPWM(p1, p2);
+         break;
+
+      case PI_CMD_PRS:
+         res = gpioSetPWMrange(p1, p2);
+         break;
+
+      case PI_CMD_PFS:
+         res = gpioSetPWMfrequency(p1, p2);
+         break;
+
+      case PI_CMD_SERVO:
+         res = gpioServo(p1, p2);
+         break;
+
+      case PI_CMD_WDOG:
+         res = gpioSetWatchdog(p1, p2);
+         break;
+
+      case PI_CMD_BR1:
+         res = gpioRead_Bits_0_31();
+         break;
+
+      case PI_CMD_BR2:
+         res = gpioRead_Bits_32_53();
+         break;
+
+      case PI_CMD_BC1:
+         gpioWrite_Bits_0_31_Clear(p1);
+         break;
+
+      case PI_CMD_BC2:
+         gpioWrite_Bits_32_53_Clear(p1);
+         break;
+
+      case PI_CMD_BS1:
+         gpioWrite_Bits_0_31_Set(p1);
+         break;
+
+      case PI_CMD_BS2:
+         gpioWrite_Bits_32_53_Set(p1);
+         break;
+
+      case PI_CMD_TICK:
+         res = gpioTick();
+         break;
+
+      case PI_CMD_HWVER:
+         res = gpioHardwareRevision();
+         break;
+
+      case PI_CMD_PRG:
+         res = gpioGetPWMrange(p1);
+         break;
+
+      case PI_CMD_PFG:
+         res = gpioGetPWMfrequency(p1);
+         break;
+
+      case PI_CMD_PRRG:
+         res = gpioGetPWMrealRange(p1);
+         break;
+
+      case PI_CMD_NO:
+         res = gpioNotifyOpen();
+         break;
+
+      case PI_CMD_NB:
+         res = gpioNotifyBegin(p1, p2);
+         break;
+
+      case PI_CMD_NP:
+         res = gpioNotifyPause(p1);
+         break;
+
+      case PI_CMD_NC:
+         res = gpioNotifyClose(p1);
+         break;
+
+      case PI_CMD_HELP:
+         break;
+
+      }
+
+      cmd->res = res;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void mySetGpioOff(unsigned gpio, int pos)
+{
+   int page, slot;
+
+   myOffPageSlot(pos, &page, &slot);
+
+   dmaIVirt[page]->gpioOff[slot] |= (1<<gpio);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myClearGpioOff(unsigned gpio, int pos)
+{
+   int page, slot;
+
+   myOffPageSlot(pos, &page, &slot);
+
+   dmaIVirt[page]->gpioOff[slot] &= ~(1<<gpio);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void mySetGpioOn(unsigned gpio, int pos)
+{
+   int page, slot;
+
+   page = pos/ON_PER_IPAGE;
+   slot = pos%ON_PER_IPAGE;
+
+   dmaIVirt[page]->gpioOn[slot] |= (1<<gpio);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myClearGpioOn(unsigned gpio, int pos)
+{
+   int page, slot;
+
+   page = pos/ON_PER_IPAGE;
+   slot = pos%ON_PER_IPAGE;
+
+   dmaIVirt[page]->gpioOn[slot] &= ~(1<<gpio);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myGpioSetPwm(unsigned gpio, int oldVal, int newVal)
+{
+   int switchGpioOff;
+   int newOff, oldOff, realRange, cycles, i;
+
+   DBG(DBG_INTERNAL,
+      "myGpioSetPwm %d from %d to %d", gpio, oldVal, newVal);
+
+   switchGpioOff = 0;
+
+   realRange = pwmRealRange[gpioInfo[gpio].freqIdx];
+
+   cycles    = pwmCycles   [gpioInfo[gpio].freqIdx];
+
+   newOff = (newVal * realRange)/gpioInfo[gpio].range;
+   oldOff = (oldVal * realRange)/gpioInfo[gpio].range;
+
+   if (newOff != oldOff)
+   {
+      if (newOff && oldOff)                      /* PWM CHANGE */
+      {
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            mySetGpioOff(gpio, i+newOff);
+
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            myClearGpioOff(gpio, i+oldOff);
+      }
+      else if (newOff)                           /* PWM START */
+      {
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            mySetGpioOff(gpio, i+newOff);
+
+         /* schedule new gpio on */
+
+         for (i=0; i<SUPERCYCLE; i+=cycles) mySetGpioOn(gpio, i);
+      }
+      else                                       /* PWM STOP */
+      {
+         /* deschedule gpio on */
+
+         for (i=0; i<SUPERCYCLE; i+=cycles)
+            myClearGpioOn(gpio, i);
+
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            myClearGpioOff(gpio, i+oldOff);
+
+         switchGpioOff = 1;
+      }
+
+      if (switchGpioOff)
+      {
+         *(gpioReg + GPCLR0) = (1<<gpio);
+         *(gpioReg + GPCLR0) = (1<<gpio);
+      }
+   }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void myGpioSetServo(unsigned gpio, int oldVal, int newVal)
+{
+   int switchGpioOff;
+   int newOff, oldOff, realRange, cycles, i;
+
+   DBG(DBG_INTERNAL,
+      "myGpioSetServo %d from %d to %d", gpio, oldVal, newVal);
+
+   switchGpioOff = 0;
+
+   realRange = pwmRealRange[gpioInfo[gpio].freqIdx];
+   cycles    = pwmCycles   [gpioInfo[gpio].freqIdx];
+
+   newOff = (newVal * realRange)/20000;
+   oldOff = (oldVal * realRange)/20000;
+
+   if (newOff != oldOff)
+   {
+      if (newOff && oldOff)                       /* SERVO CHANGE */
+      {
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            mySetGpioOff(gpio, i+newOff);
+
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            myClearGpioOff(gpio, i+oldOff);
+      }
+      else if (newOff)                            /* SERVO START */
+      {
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            mySetGpioOff(gpio, i+newOff);
+
+         /* schedule new gpio on */
+
+         for (i=0; i<SUPERCYCLE; i+=cycles)
+            mySetGpioOn(gpio, i);
+      }
+      else                                        /* SERVO STOP */
+      {
+         /* deschedule gpio on */
+
+         for (i=0; i<SUPERCYCLE; i+=cycles)
+            myClearGpioOn(gpio, i);
+
+         for (i=0; i<SUPERLEVEL; i+=realRange)
+            myClearGpioOff(gpio, i+oldOff);
+
+         switchGpioOff = 1;
+      }
+
+      if (switchGpioOff)
+      {
+         *(gpioReg + GPCLR0) = (1<<gpio);
+         *(gpioReg + GPCLR0) = (1<<gpio);
+      }
+   }
+}
+
+/* ======================================================================= */
+
+static dmaCbs_t * waveCbVOadr(int pos)
+{
+   int page, slot;
+
+   page = pos/CBS_PER_OPAGE;
+   slot = pos%CBS_PER_OPAGE;
+
+   return &dmaOVirt[page]->cb[slot];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t waveCbPOadr(int pos)
+{
+   int page, slot;
+
+   page = pos/CBS_PER_OPAGE;
+   slot = pos%CBS_PER_OPAGE;
+
+   return (uint32_t) &dmaOPhys[page]->cb[slot];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t waveOnOffPOadr(int pos)
+{
+   int page, slot;
+
+   page = pos/ONOFF_PER_OPAGE;
+   slot = pos%ONOFF_PER_OPAGE;
+
+   return (uint32_t) &dmaOPhys[page]->gpioOnOff[slot];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void waveCbOPrint(int pos)
+{
+   dmaCbs_t * p;
+
+   p = waveCbVOadr(pos);
+
+   fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx",
+      p->info, p->src, p->dst, p->length, p->stride, p->next);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+void waveBitDelay(unsigned baud, unsigned * bitDelay)
+{
+   unsigned fullBit, halfBit, s, e, d, m, i, err;
+
+   fullBit = 100000000 / baud;
+   halfBit =  50000000 / baud;
+
+   d = (fullBit/200)*200;
+
+   s = 0;
+
+   e = d;
+
+   bitDelay[0] = d/100;
+
+   err = d / 3;
+
+   for (i=0; i<8; i++)
+   {
+      s = e;
+
+      m = halfBit + (i+1)*fullBit;
+
+      e = s + d;
+
+      if ((e-m) < err) e+=200;
+
+      bitDelay[i+1] = (e-s)/100;
+   }
+
+   s = e;
+
+   e = ((1000000000 / baud)+100)/200*200;
+
+   bitDelay[9] = (e-s)/100;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void gpioWaveDump(void)
+{
+   int i;
+
+   unsigned numPulses, t;
+
+   gpioPulse_t * pulses;
+
+   numPulses = wfc[wfcur];
+   pulses    = wf [wfcur];
+
+   t = 0;
+
+   for (i=0; i<numPulses; i++)
+   {
+      printf("%10u %08X %08X %10u\n",
+         t, pulses[i].gpioOn, pulses[i].gpioOff, pulses[i].usDelay);
+      t += pulses[i].usDelay;
+   }
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static int wave2Cbs(unsigned mode)
+{
+   int cb=0, onoff=0;
+
+   dmaCbs_t * p=NULL;
+
+   unsigned i, half, repeatCb;
+
+   unsigned numPulses;
+
+   gpioPulse_t * pulses;
+
+   numPulses = wfc[wfcur];
+   pulses    = wf [wfcur];
+
+   half = PI_WF_MICROS/2;
+
+   /* add delay cb at start of DMA */
+
+   p = waveCbVOadr(cb++);
+
+   /* use the secondary clock */
+
+   if (gpioCfg.clockPeriph != PI_CLOCK_PCM)
+   {
+      p->info   = NORMAL_DMA |
+                  DMA_DEST_DREQ |
+                  DMA_PERIPHERAL_MAPPING(2);
+
+      p->dst    = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+   }
+   else
+   {
+      p->info   = NORMAL_DMA |
+                  DMA_DEST_DREQ |
+                  DMA_PERIPHERAL_MAPPING(5);
+
+      p->dst    = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+   }
+
+   p->src    = (uint32_t) (&dmaOPhys[0]->periphData) | DMA_BUS_ADR;
+   p->length = 4 * 50 / PI_WF_MICROS; /* 50 micros delay */
+   p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
+
+   repeatCb = cb;
+
+   for (i=0; i<numPulses; i++)
+   {
+      if (pulses[i].gpioOn)
+      {
+         dmaOVirt[onoff/ONOFF_PER_OPAGE]->gpioOnOff[onoff%ONOFF_PER_OPAGE] =
+            pulses[i].gpioOn;
+
+         p = waveCbVOadr(cb++);
+
+         p->info   = NORMAL_DMA;
+         p->src    = waveOnOffPOadr(onoff++) | DMA_BUS_ADR;
+         p->dst    = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | 0x7e000000;
+         p->length = 4;
+         p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
+      }
+
+      if (pulses[i].gpioOff)
+      {
+         dmaOVirt[onoff/ONOFF_PER_OPAGE]->gpioOnOff[onoff%ONOFF_PER_OPAGE] =
+            pulses[i].gpioOff;
+
+         p = waveCbVOadr(cb++);
+
+         p->info   = NORMAL_DMA;
+         p->src    = waveOnOffPOadr(onoff++) | DMA_BUS_ADR;
+         p->dst    = ((GPIO_BASE + (GPCLR0*4)) & 0x00ffffff) | 0x7e000000;
+         p->length = 4;
+         p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
+      }
+
+      if (pulses[i].usDelay)
+      {
+         p = waveCbVOadr(cb++);
+
+         /* use the secondary clock */
+
+         if (gpioCfg.clockPeriph != PI_CLOCK_PCM)
+         {
+            p->info   = NORMAL_DMA |
+                        DMA_DEST_DREQ |
+                        DMA_PERIPHERAL_MAPPING(2);
+
+            p->dst    = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+         }
+         else
+         {
+            p->info   = NORMAL_DMA |
+                        DMA_DEST_DREQ |
+                        DMA_PERIPHERAL_MAPPING(5);
+
+            p->dst    = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+         }
+
+         p->src    = (uint32_t) (&dmaOPhys[0]->periphData) | DMA_BUS_ADR;
+         p->length = 4 * ((pulses[i].usDelay+half)/PI_WF_MICROS);
+         p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
+      }
+   }
+
+   if (p != NULL)
+   {
+      if (mode == PI_WAVE_MODE_ONE_SHOT)
+           p->next = 0;
+      else p->next = waveCbPOadr(repeatCb) | DMA_BUS_ADR;
+   }
+
+   return cb;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void waveRxSerial(wfRx_t * s, int level, uint32_t tick)
+{
+   int diffTicks;
+
+   if (s->bit >= 0)
+   {
+      diffTicks = tick - s->startBitTick;
+
+      if (level != PI_TIMEOUT) s->level = level;
+
+      while ((s->bit < 9) && (diffTicks > s->nextBitDiff))
+      {
+         if (s->bit)
+         {
+            if (!(s->level)) s->byte |= (1<<(s->bit-1));
+         }
+         else s->byte = 0;
+
+         ++(s->bit);
+
+         s->nextBitDiff += s->fullBit;
+      }
+
+      if (s->bit == 9)
+      {
+         s->rxp->buf[s->rxp->writePos] = s->byte;
+
+         if (++s->rxp->writePos >= s->rxp->bufSize) s->rxp->writePos = 0;
+
+         if (level == 0) /* true transition high->low, not a timeout */
+         {
+            s->bit          = 0;
+            s->startBitTick = tick;
+            s->nextBitDiff  = s->halfBit;
+         }
+         else s->bit = -1;
+      }
+   }
+   else
+   {
+      /* start bit if high->low */
+
+      if (level == 0)
+      {
+         s->level        = 0;
+         s->bit          = 0;
+         s->startBitTick = tick;
+         s->nextBitDiff  = s->halfBit;
+      }
+   }
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void waveRxBit(int gpio, int level, uint32_t tick)
+{
+   switch (wfRx[gpio].mode)
+   {
+      case PI_WFRX_NONE:
+         break;
+
+      case PI_WFRX_SERIAL:
+         waveRxSerial(&wfRx[gpio], level, tick);
+   }
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
+{
+   unsigned inPos1=0, inPos2=0, outPos=0;
+
+   unsigned cbs=0;
+
+   unsigned numIn2, numOut;
+
+   uint32_t tNow, tNext1, tNext2, tDelay;
+
+   gpioPulse_t * in2, * out;
+
+   numIn2 = wfc[wfcur];
+   in2    = wf[wfcur];
+
+   numOut = PI_WAVE_MAX_PULSES;
+   out   = wf[1-wfcur];
+
+   tNow = 0;
+  
+   if (!numIn1) tNext1 = -1; else tNext1 = 0;
+   if (!numIn2) tNext2 = -1; else tNext2 = 0;
+
+   while (((inPos1<numIn1) || (inPos2<numIn2)) && (outPos<numOut))
+   {
+      if (tNext1 < tNext2)
+      {
+         /* pulse 1 due */
+
+         if (tNow < tNext1)
+         {
+            /* extend previous delay */
+            out[outPos-1].usDelay += (tNext1 - tNow);
+            tNow = tNext1;
+         }
+
+         out[outPos].gpioOn  = in1[inPos1].gpioOn;
+         out[outPos].gpioOff = in1[inPos1].gpioOff;
+
+         tNext1 = tNow + in1[inPos1].usDelay; ++inPos1;
+      }
+      else if (tNext2 < tNext1)
+      {
+         /* pulse 2 due */
+
+         if (tNow < tNext2)
+         {
+            /* extend previous delay */
+            out[outPos-1].usDelay += (tNext2 - tNow);
+            tNow = tNext2;
+         }
+
+         out[outPos].gpioOn  = in2[inPos2].gpioOn;
+         out[outPos].gpioOff = in2[inPos2].gpioOff;
+
+         tNext2 = tNow + in2[inPos2].usDelay; ++inPos2;
+      }
+      else
+      {
+         /* pulse 1 and 2 both due */
+
+         if (tNow < tNext1)
+         {
+            /* extend previous delay */
+            out[outPos-1].usDelay += (tNext1 - tNow);
+            tNow = tNext1;
+         }
+
+         out[outPos].gpioOn  = in1[inPos1].gpioOn  | in2[inPos2].gpioOn;
+         out[outPos].gpioOff = in1[inPos1].gpioOff | in2[inPos2].gpioOff;
+
+         tNext1 = tNow + in1[inPos1].usDelay; ++inPos1;
+         tNext2 = tNow + in2[inPos2].usDelay; ++inPos2;
+      }
+
+      if (tNext1 <= tNext2) { tDelay = tNext1 - tNow; tNow = tNext1; }
+      else                  { tDelay = tNext2 - tNow; tNow = tNext2; }
+
+      out[outPos].usDelay = tDelay;
+
+      cbs++; /* one cb for delay */
+
+      if (out[outPos].gpioOn) cbs++; /* one cb if gpio on */
+
+      if (out[outPos].gpioOff) cbs++; /* one cb if gpio off */
+
+      outPos++;
+
+      if (inPos1 >= numIn1) tNext1 = -1;
+      if (inPos2 >= numIn2) tNext2 = -1;
+
+   }
+
+   if (outPos < numOut)
+   {
+      wfStats.micros = tNow;
+
+      if (tNow > wfStats.highMicros) wfStats.highMicros = tNow;
+
+      wfStats.pulses = outPos;
+
+      if (outPos > wfStats.highPulses) wfStats.highPulses = outPos;
+
+      wfStats.cbs    = cbs;
+
+      if (cbs > wfStats.highCbs) wfStats.highCbs = cbs;
+
+      wfc[1-wfcur] = outPos;
+      wfcur = 1 - wfcur;
+
+      return outPos;
+   }
+   else return PI_TOO_MANY_PULSES;
+}
+
+
+/* ======================================================================= */
+
+static dmaCbs_t * dmaCB2adr(int pos)
+{
+   int page, slot;
+
+   page = pos/CBS_PER_IPAGE;
+   slot = pos%CBS_PER_IPAGE;
+
+   return &dmaIVirt[page]->cb[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaCbPrint(int pos)
+{
+   dmaCbs_t * p;
+
+   p = dmaCB2adr(pos);
+
+   fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx",
+      p->info, p->src, p->dst, p->length, p->stride, p->next);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned dmaCurrentCb(void)
+{
+   unsigned cb;
+   static unsigned lastPage=0;
+   unsigned page;
+   uint32_t cbAddr;
+   uint32_t startTick, endTick;
+
+   startTick = systReg[SYST_CLO];
+
+   cbAddr = dmaIn[DMA_CONBLK_AD];
+
+   page = lastPage;
+
+   /* which page are we dma'ing? */
+
+   while (1)
+   {
+      cb = (cbAddr - ((int)dmaIPhys[page] | DMA_BUS_ADR)) / 32;
+
+      if (cb < CBS_PER_IPAGE)
+      {
+         endTick = systReg[SYST_CLO];
+
+         if (endTick != startTick)
+              gpioStats.cbTicks += (endTick - startTick);
+         else gpioStats.cbTicks ++;
+
+         gpioStats.cbCalls++;
+
+         lastPage = page;
+
+         return (page*CBS_PER_IPAGE) + cb;
+      }
+
+      if (page++ >= DMA_PAGES) page=0;
+
+      if (page == lastPage) break;
+   }
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned dmaCurrentSlot(unsigned pos)
+{
+   unsigned cycle=0, slot=0, tmp;
+
+   cycle = (pos/CBS_PER_CYCLE);
+   tmp   = (pos%CBS_PER_CYCLE);
+
+   if (tmp > 2) slot = ((tmp-2)/3);
+
+   return (cycle*PULSE_PER_CYCLE)+slot;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaPwmDataAdr(int pos)
+{
+   return (uint32_t) &dmaIPhys[pos]->periphData;
+}
+
+static uint32_t dmaGpioOnAdr(int pos)
+{
+   int page, slot;
+
+   page = pos/ON_PER_IPAGE;
+   slot = pos%ON_PER_IPAGE;
+
+   return (uint32_t) &dmaIPhys[page]->gpioOn[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaGpioOffAdr(int pos)
+{
+   int page, slot;
+
+   myOffPageSlot(pos, &page, &slot);
+
+   return (uint32_t) &dmaIPhys[page]->gpioOff[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaTickAdr(int pos)
+{
+   int page, slot;
+
+   myTckPageSlot(pos, &page, &slot);
+
+   return (uint32_t) &dmaIPhys[page]->tick[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaReadLevelsAdr(int pos)
+{
+   int page, slot;
+
+   myLvsPageSlot(pos, &page, &slot);
+
+   return (uint32_t) &dmaIPhys[page]->level[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t dmaCbAdr(int pos)
+{
+   int page, slot;
+
+   page = (pos/CBS_PER_IPAGE);
+   slot = (pos%CBS_PER_IPAGE);
+
+   return (uint32_t) &dmaIPhys[page]->cb[slot];
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaGpioOnCb(int b, int pos)
+{   
+   dmaCbs_t * p;
+
+   p = dmaCB2adr(b);
+
+   p->info   = NORMAL_DMA;
+   p->src    = dmaGpioOnAdr(pos) | DMA_BUS_ADR;
+   p->dst    = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | 0x7e000000;
+   p->length = 4;
+   p->next   = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaTickCb(int b, int pos)
+{   
+   dmaCbs_t * p;
+
+   p = dmaCB2adr(b);
+
+   p->info   = NORMAL_DMA;
+   p->src    = ((SYST_BASE + (SYST_CLO*4)) & 0x00ffffff) | 0x7e000000;
+   p->dst    = dmaTickAdr(pos) | DMA_BUS_ADR;
+   p->length = 4;
+   p->next   = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaGpioOffCb(int b, int pos)
+{
+   dmaCbs_t * p;
+
+   p = dmaCB2adr(b);
+
+   p->info   = NORMAL_DMA;
+   p->src    = dmaGpioOffAdr(pos) | DMA_BUS_ADR;
+   p->dst    = ((GPIO_BASE + (GPCLR0*4)) & 0x00ffffff) | 0x7e000000;
+   p->length = 4;
+   p->next   = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaReadLevelsCb(int b, int pos)
+{
+   dmaCbs_t * p;
+
+   p = dmaCB2adr(b);
+
+   p->info   = NORMAL_DMA;
+   p->src    = ((GPIO_BASE + (GPLEV0*4)) & 0x00ffffff) | 0x7e000000;
+   p->dst    = dmaReadLevelsAdr(pos) | DMA_BUS_ADR;
+   p->length = 4;
+   p->next   = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaDelayCb(int b)
+{
+   dmaCbs_t * p;
+
+   p = dmaCB2adr(b);
+
+   if (gpioCfg.clockPeriph == PI_CLOCK_PCM)
+   {
+      p->info   = NORMAL_DMA | TIMED_DMA(2);
+      p->dst    = ((PCM_BASE + PCM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+   }
+   else
+   {
+      p->info   = NORMAL_DMA | TIMED_DMA(5);
+      p->dst    = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
+   }
+
+   p->src    = dmaPwmDataAdr(b%DMA_PAGES) | DMA_BUS_ADR;
+   p->length = 4;
+   p->next   = dmaCbAdr(b+1) | DMA_BUS_ADR;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void dmaInitCbs(void)
+{
+   int b, pulse, level, cycle;
+
+   dmaCbs_t * p;
+
+   /* set up the DMA control blocks */
+
+   DBG(DBG_STARTUP, "");
+
+   b = -1;
+   level = 0;
+
+   for (cycle=0; cycle<bufferCycles; cycle++)
+   {
+      b++; dmaGpioOnCb(b, cycle%SUPERCYCLE); /* gpio on slot */
+
+      b++; dmaTickCb(b, cycle);              /* tick slot */
+
+      for (pulse=0; pulse<PULSE_PER_CYCLE; pulse++)
+      {
+         b++; dmaReadLevelsCb(b, level);               /* read levels slot */
+
+         b++; dmaDelayCb(b);                           /* delay slot */
+
+         b++; dmaGpioOffCb(b, (level%SUPERLEVEL)+1);   /* gpio off slot */
+
+         ++level;
+      }
+   }
+
+   /* point last cb back to first for continuous loop */
+
+   p = dmaCB2adr(b);
+
+   p->next = dmaCbAdr(0) | DMA_BUS_ADR;
+
+   DBG(DBG_STARTUP, "DMA page type size = %d", sizeof(dmaIPage_t));
+
+   DBG(DBG_STARTUP, "%d control blocks (exp=%d)", b+1, NUM_CBS);
+}
+
+/* ======================================================================= */
+
+
+static void sigHandler(int signum)
+{
+   if ((signum >= PI_MIN_SIGNUM) && (signum <= PI_MAX_SIGNUM))
+   {
+      if (gpioSignal[signum].func)
+      {
+         if (gpioSignal[signum].ex)
+         {
+            (gpioSignal[signum].func)(signum, gpioSignal[signum].userdata);
+         }
+         else
+         {
+            (gpioSignal[signum].func)(signum);
+         }
+      }
+      else
+      {
+         if (signum == SIGUSR1)
+         {
+            if (gpioCfg.dbgLevel > DBG_MIN_LEVEL)
+            {
+               --gpioCfg.dbgLevel;
+            }
+            else gpioCfg.dbgLevel = DBG_MIN_LEVEL;
+
+            DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel);
+         }
+         else if (signum == SIGUSR2)
+         {
+            if (gpioCfg.dbgLevel < DBG_MAX_LEVEL)
+            {
+               ++gpioCfg.dbgLevel;
+            }
+            else gpioCfg.dbgLevel = DBG_MAX_LEVEL;
+
+            DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel);
+         }
+         else
+         {
+            /* close library safely and exit */
+
+            DBG(DBG_USER, "Unhandled signal %d, terminating\n", signum);
+
+            gpioTerminate();
+
+            exit(-1);
+         }
+      }
+   }
+   else
+   {
+      /* close library safely and exit */
+
+      DBG(DBG_USER, "Unhandled signal %d, terminating\n", signum);
+
+      gpioTerminate();
+
+      exit(-1);
+   }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void sigSetHandler(void)
+{
+   int i;
+   struct sigaction new;
+
+   for (i=PI_MIN_SIGNUM; i<=PI_MAX_SIGNUM; i++)
+   {
+
+      memset(&new, 0, sizeof(new));
+      new.sa_handler = sigHandler;
+
+      sigaction(i, &new, NULL);
+   }
+}
+
+/* ======================================================================= */
+
+static void * pthAlertThread(void *x)
+{
+   struct timespec req, rem;
+   uint32_t oldLevel, newLevel, level, reportedLevel;
+   uint32_t oldSlot,  newSlot;
+   uint32_t tick, expected;
+   int32_t diff;
+   int cycle, pulse;
+   int emit, seqno, emitted;
+   uint32_t changes, bits, changedBits, timeoutBits;
+   int numSamples, d;
+   int b, n, v;
+   int err;
+   char fifo[32];
+
+   req.tv_sec = 0;
+
+   /* don't start until DMA started */
+
+   while (!DMAstarted) myGpioDelay(1000);
+
+   myGpioDelay(20000); /* let DMA run for a while */
+
+   reportedLevel = gpioReg[GPLEV0];
+
+   tick = systReg[SYST_CLO];
+
+   gpioStats.startTick = tick;
+
+   oldSlot = dmaCurrentSlot(dmaCurrentCb());
+
+   cycle = (oldSlot/PULSE_PER_CYCLE);
+   pulse = (oldSlot%PULSE_PER_CYCLE);
+
+   while (1)
+   {
+      gpioStats.alertTicks++;
+
+      req.tv_nsec = 850000;
+
+      while (nanosleep(&req, &rem))
+      {
+         req.tv_sec  = rem.tv_sec;
+         req.tv_nsec = rem.tv_nsec;
+      }
+
+      newSlot = dmaCurrentSlot(dmaCurrentCb());
+
+      numSamples = 0;
+
+      changedBits = 0;
+
+      oldLevel = reportedLevel & monitorBits;
+
+      while ((oldSlot != newSlot) && (numSamples < DATUMS))
+      {
+         level = myGetLevel(oldSlot++);
+
+         newLevel = (level & monitorBits);
+
+         if (newLevel != oldLevel)
+         {
+            gpioSample[numSamples].tick  = tick;
+            gpioSample[numSamples].level = level;
+
+            changedBits |= (newLevel ^ oldLevel);
+
+            oldLevel = newLevel;
+
+            numSamples++;
+         }
+         
+         tick += gpioCfg.clockMicros;
+
+         if (++pulse >= PULSE_PER_CYCLE)
+         {
+            pulse = 0;
+
+            if (++cycle >= bufferCycles)
+            {
+               cycle = 0;
+               oldSlot = 0;
+            }
+            
+            expected = tick;
+
+            tick = myGetTick(cycle);
+
+            diff = tick - expected;
+
+            diff += (TICKSLOTS/2);
+
+            if (diff < 0) gpioStats.diffTick[0]++;
+
+            else if (diff >= TICKSLOTS)
+               gpioStats.diffTick[TICKSLOTS-1]++;
+
+            else gpioStats.diffTick[diff]++;
+         }
+      }
+
+      /* should gpioGetSamples be called */
+
+      if (changedBits)
+      {
+         if (gpioGetSamples.func)
+         {
+            if (gpioGetSamples.ex)
+            {
+               (gpioGetSamples.func)
+                  (gpioSample, numSamples, gpioGetSamples.userdata);
+            }
+            else
+            {
+               (gpioGetSamples.func)
+                  (gpioSample, numSamples);
+            }
+         }
+      }
+
+      /* reset timeouts for any changed bits */
+
+      if (changedBits)
+      {
+         for (b=0; b<=PI_MAX_USER_GPIO; b++)
+         {
+            if (changedBits & (1<<b)) gpioAlert[b].tick = tick;
+         }
+      }
+
+      /* call alert callbacks for each bit transition */
+
+      if (changedBits & alertBits)
+      {
+         oldLevel = reportedLevel & alertBits;
+
+         for (d=0; d<numSamples; d++)
+         {
+            newLevel = gpioSample[d].level & alertBits;
+
+            if (newLevel != oldLevel)
+            {
+               changes = newLevel ^ oldLevel;
+
+               for (b=0; b<=PI_MAX_USER_GPIO; b++)
+               {
+                  if (changes & (1<<b))
+                  {
+                     if (newLevel & (1<<b)) v = 1; else v = 0;
+
+                     if (gpioAlert[b].func)
+                     {
+                        if (gpioAlert[b].ex)
+                        {
+                           (gpioAlert[b].func)
+                              (b, v, gpioSample[d].tick,
+                               gpioAlert[b].userdata);
+                        }
+                        else
+                        {
+                           (gpioAlert[b].func)
+                              (b, v, gpioSample[d].tick);
+                        }
+                     }
+                  }
+               }
+               oldLevel = newLevel;
+            }
+         }
+      }
+
+      /* check for timeout watchdogs */
+
+      timeoutBits = 0;
+
+      for (b=0; b<=PI_MAX_USER_GPIO; b++)
+      {
+         if (gpioAlert[b].timeout)
+         {
+            diff = tick - gpioAlert[b].tick;
+
+            if (diff > (gpioAlert[b].timeout*1000))
+            {
+               timeoutBits |= (1<<b);
+
+               gpioAlert[b].tick += (gpioAlert[b].timeout*1000);
+
+               if (gpioAlert[b].func)
+               {
+                  if (gpioAlert[b].ex)
+                  {
+                     (gpioAlert[b].func)(b, 2, tick, gpioAlert[b].userdata);
+                  }
+                  else
+                  {
+                     (gpioAlert[b].func)(b, 2, tick);
+                  }
+               }
+            }
+         }
+      }
+
+      for (n=0; n<PI_NOTIFY_SLOTS; n++)
+      {
+         if (gpioNotify[n].state == PI_NOTIFY_CLOSING)
+         {
+            if (gpioNotify[n].pipe)
+            {
+               close(gpioNotify[n].fd);
+
+               sprintf(fifo, "/dev/pigpio%d", n);
+
+               unlink(fifo);
+            }
+
+            gpioNotify[n].state = PI_NOTIFY_CLOSED;
+         }
+         else if (gpioNotify[n].state == PI_NOTIFY_RUNNING)
+         {
+            bits = gpioNotify[n].bits;
+
+            emit = 0;
+
+            seqno = gpioNotify[n].seqno;
+
+            /* check to see if any bits have changed for this
+               notification.
+
+               bits        is the set of notification bits
+               changedBits is the set of changed bits
+            */
+
+            if (changedBits & bits)
+            {
+               oldLevel = reportedLevel & bits;
+
+               for (d=0; d<numSamples; d++)
+               {
+                  newLevel = gpioSample[d].level & bits;
+
+                  if (newLevel != oldLevel)
+                  {
+                     gpioReport[emit].seqno = seqno;
+                     gpioReport[emit].flags = 0;
+                     gpioReport[emit].tick  = gpioSample[d].tick;
+                     gpioReport[emit].level = gpioSample[d].level;
+
+                     oldLevel = newLevel;
+
+                     emit++;
+                     seqno++;
+                  }
+               }
+            }
+
+            /* check to see if any watchdogs are due for this
+               notification.
+
+               bits        is the set of notification bits
+               timeoutBits is the set of timed out bits
+            */
+
+            if (timeoutBits & bits)
+            {
+               /* at least one watchdog has fired for this
+                  notification.
+               */
+
+               for (b=0; b<=PI_MAX_USER_GPIO; b++)
+               {
+                  if (timeoutBits & bits & (1<<b))
+                  {
+                     if (numSamples)
+                        newLevel = gpioSample[numSamples-1].level;
+                     else
+                        newLevel = reportedLevel;
+
+                     gpioReport[emit].seqno = seqno;
+                     gpioReport[emit].flags = PI_NTFY_FLAGS_WDOG |
+                                          PI_NTFY_FLAGS_BIT(b);
+                     gpioReport[emit].tick  = tick;
+                     gpioReport[emit].level = newLevel;
+
+                     emit++;
+                     seqno++;
+                  }
+               }
+            }
+
+            if (emit)
+            {
+               if (emit > gpioStats.maxEmit) gpioStats.maxEmit = emit;
+
+               emitted = 0;
+
+               while (emit > 0)
+               {
+                  if (emit > MAX_EMITS)
+                  {
+                     gpioStats.emitFrags++;
+
+                     err = write(gpioNotify[n].fd,
+                              gpioReport+emitted,
+                              MAX_EMITS*sizeof(gpioReport_t));
+
+                     if (err != (MAX_EMITS*sizeof(gpioReport_t)))
+                     {
+                        DBG(0, "fd=%d err=%d errno=%d",
+                          gpioNotify[n].fd, err, errno);
+                        if (err < 0) DBG(0, "%s", strerror(errno));
+                        if ((err != EAGAIN) && (err != EWOULDBLOCK))
+                        {
+                           /* serious error, no point continuing */
+                           gpioNotify[n].bits  = 0;
+                           gpioNotify[n].state = PI_NOTIFY_CLOSING;
+                           intNotifyBits();
+                           break;
+                        }
+                     }
+
+                     emitted += MAX_EMITS;
+                     emit    -= MAX_EMITS;
+                  }
+                  else
+                  {
+                     err = write(gpioNotify[n].fd,
+                              gpioReport+emitted,
+                              emit*sizeof(gpioReport_t));
+
+                     if (err != (emit*sizeof(gpioReport_t)))
+                     {
+                        DBG(0, "fd=%d err=%d errno=%d",
+                          gpioNotify[n].fd, err, errno);
+                        if (err < 0) DBG(0, "%s", strerror(errno));
+                        if ((err != EAGAIN) && (err != EWOULDBLOCK))
+                        {
+                           /* serious error, no point continuing */
+                           gpioNotify[n].bits  = 0;
+                           gpioNotify[n].state = PI_NOTIFY_CLOSING;
+                           intNotifyBits();
+                           break;
+                        }
+                     }
+
+                     emitted += emit;
+                     emit = 0;
+                  }
+               }
+
+               gpioNotify[n].seqno = seqno;
+            }
+         }
+      }
+
+      /* once all outputs have been emitted set reported level */
+
+      if (numSamples) reportedLevel = gpioSample[numSamples-1].level;
+
+      if (numSamples > gpioStats.maxSamples)
+         gpioStats.maxSamples = numSamples;
+      
+      gpioStats.numSamples += numSamples;
+   }
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void * pthTimerTick(void *x)
+{
+   gpioTimer_t *     tp;
+   struct timespec   req, rem, period;
+   char              buf[256];
+
+   tp = x;
+
+   clock_gettime(CLOCK_REALTIME, &tp->nextTick);
+
+   while (1)
+   {
+      clock_gettime(CLOCK_REALTIME, &rem);
+
+      period.tv_sec  = tp->millis / THOUSAND;
+      period.tv_nsec = (tp->millis % THOUSAND) * THOUSAND * THOUSAND;
+
+      do
+      {
+         TIMER_ADD(&tp->nextTick, &period, &tp->nextTick);
+
+         TIMER_SUB(&tp->nextTick, &rem, &req);
+      }
+      while (req.tv_sec < 0);
+
+      while (nanosleep(&req, &rem))
+      {
+         req.tv_sec  = rem.tv_sec;
+         req.tv_nsec = rem.tv_nsec;
+      }
+
+      if (gpioCfg.dbgLevel >= DBG_SLOW_TICK)
+      {
+         if ((tp->millis > 50) || (gpioCfg.dbgLevel >= DBG_FAST_TICK))
+         {
+            sprintf(buf, "pigpio: TIMER=%d @ %u %u\n",
+               tp->id,
+              (unsigned)tp->nextTick.tv_sec,
+              (unsigned)tp->nextTick.tv_nsec);
+            fprintf(stderr, buf);
+         }
+      }
+
+      if (tp->ex) (tp->func)(tp->userdata);
+      else        (tp->func)();
+   }
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+
+static void * pthFifoThread(void *x)
+{
+   char inBuf[128];
+   int idx, flags;
+   cmdCmd_t cmd;
+
+   myCreatePipe(PI_INPFIFO, 0662);
+
+   if ((inpFifo = fopen(PI_INPFIFO, "r+")) == NULL)
+      SOFT_ERROR((void*)PI_INIT_FAILED, "fopen %s failed(%m)", PI_INPFIFO);
+
+   myCreatePipe(PI_OUTFIFO, 0664);
+
+   if ((outFifo = fopen(PI_OUTFIFO, "w+")) == NULL)
+      SOFT_ERROR((void*)PI_INIT_FAILED, "fopen %s failed (%m)", PI_OUTFIFO);
+
+   /* set outFifo non-blocking */
+
+   flags = fcntl(fileno(outFifo), F_GETFL, 0);
+   fcntl(fileno(outFifo), F_SETFL, flags | O_NONBLOCK);
+
+   while (1)
+   {
+      if (fgets(inBuf, sizeof(inBuf), inpFifo) == NULL)
+         SOFT_ERROR((void*)PI_INIT_FAILED, "fifo fgets failed (%m)");
+
+      if ((idx=cmdParse(inBuf, &cmd)) >= 0)
+      {
+         myDoCommand(&cmd);
+
+         switch (cmdInfo[idx].rv)
+         {
+            case 0:
+               fprintf(outFifo, "%d\n", cmd.res);
+               break;
+
+            case 1:
+               break;
+
+            case 2:
+               fprintf(outFifo, "%d\n", cmd.res);
+               break;
+
+            case 3:
+               fprintf(outFifo, "%08X\n", cmd.res);
+               break;
+
+            case 4:
+               fprintf(outFifo, "%u\n", cmd.res);
+               break;
+
+            case 5:
+               fprintf(outFifo, cmdUsage);
+               break;
+         }
+
+      }
+      else fprintf(outFifo, "%d\n", PI_BAD_FIFO_COMMAND);
+
+      fflush(outFifo);
+   }
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void * pthSocketThreadHandler(void *fdC)
+{
+   int sock = *(int*)fdC;
+   cmdCmd_t cmd;
+   
+   free(fdC);
+
+   while(1)
+   {
+      if (recv(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
+      {
+         if (cmd.cmd != PI_CMD_NOIB)
+         {
+            myDoCommand(&cmd);
+         }
+         else
+         {
+            cmd.res = gpioNotifyOpenInBand(sock);
+         }
+
+         write(sock, &cmd, sizeof(cmdCmd_t));
+
+      }
+      else break;
+   }
+
+   close(sock);
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void * pthSocketThread(void *x)
+{
+   int fdC, c, *sock;
+   struct sockaddr_in server, client;
+   pthread_attr_t attr;
+   char * portStr;
+   unsigned port;
+   if (pthread_attr_init(&attr))
+      SOFT_ERROR((void*)PI_INIT_FAILED,
+         "pthread_attr_init failed (%m)");
+
+   if (pthread_attr_setstacksize(&attr, STACK_SIZE))
+      SOFT_ERROR((void*)PI_INIT_FAILED,
+         "pthread_attr_setstacksize failed (%m)");
+
+   if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
+      SOFT_ERROR((void*)PI_INIT_FAILED,
+         "pthread_attr_setdetachstate failed (%m)");
+
+   fdSock = socket(AF_INET , SOCK_STREAM , 0);
+
+   if (fdSock == -1)
+      SOFT_ERROR((void*)PI_INIT_FAILED, "socket failed (%m)");
+   
+   portStr = getenv(PI_ENVPORT);
+
+   if (portStr) port = atoi(portStr); else port = gpioCfg.socketPort;
+
+   server.sin_family      = AF_INET;
+   server.sin_addr.s_addr = INADDR_ANY;
+   server.sin_port = htons(port);
+   
+   if (bind(fdSock,(struct sockaddr *)&server , sizeof(server)) < 0)
+      SOFT_ERROR((void*)PI_INIT_FAILED, "bind failed (%m)");
+
+   listen(fdSock, 100);
+   
+   c = sizeof(struct sockaddr_in);
+
+   while ((fdC =
+      accept(fdSock, (struct sockaddr *)&client, (socklen_t*)&c)))
+   {
+      pthread_t thr;
+
+      sock = malloc(sizeof(int));
+
+      *sock = fdC;
+      
+      if (pthread_create
+         (&thr, &attr, pthSocketThreadHandler, (void*) sock) < 0)
+         SOFT_ERROR((void*)PI_INIT_FAILED,
+            "socket pthread_create failed (%m)");
+   }
+   
+   if (fdC < 0)
+      SOFT_ERROR((void*)PI_INIT_FAILED, "accept failed (%m)");
+
+   return 0;
+}
+
+/* ======================================================================= */
+
+static int initGrabLockFile(void)
+{
+   int  fd;
+   int  lockResult;
+   char pidStr[20];
+
+   /* try to grab the lock file */
+
+   fd = open(PI_LOCKFILE, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0644);
+   
+   if (fd != -1)
+   {
+      lockResult = flock(fd, LOCK_EX|LOCK_NB);
+   
+      if(lockResult == 0)
+      {
+         sprintf(pidStr, "%d\n", (int)getpid());
+   
+         write(fd, pidStr, strlen(pidStr));
+      }
+      else
+      {
+         close(fd);
+         return -1;
+      }
+   }
+
+   return fd;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int initZaps
+(
+   int       pmapFd,
+   dmaPage_t *dmaV1,
+   dmaPage_t *dmaV2[],
+   dmaPage_t *dmaP[],
+   int       pages)
+{
+   int n;
+   long index;
+   off_t offset;
+   ssize_t t;
+   int status;
+   uint32_t pageAdr2;
+   unsigned long long pa;
+
+   DBG(DBG_STARTUP, "");
+
+   status = 0;
+
+   pageAdr2 = (uint32_t) dmaV2[0];
+
+   index  = ((uint32_t)dmaV1 / PAGE_SIZE) * 8;
+
+   offset = lseek(pmapFd, index, SEEK_SET);
+
+   if (offset != index)
+      SOFT_ERROR(PI_INIT_FAILED, "lseek pagemap failed (%m)");
+
+   for (n=0; n<pages; n++)
+   {
+      t = read(pmapFd, &pa, sizeof(pa));
+
+      if (t != sizeof(pa))
+         SOFT_ERROR(PI_INIT_FAILED, "read pagemap failed (%m)");
+
+      DBG(DBG_STARTUP, "pf%d=%016llX", n, pa);
+
+      dmaP[n] = (dmaPage_t *) (uint32_t) (PAGE_SIZE * (pa & 0xFFFFFFFF));
+
+      dmaV2[n] = mmap
+      (
+         (void *)pageAdr2,
+         PAGE_SIZE,
+         PROT_READ|PROT_WRITE,
+         MAP_SHARED|MAP_FIXED|MAP_LOCKED|MAP_NORESERVE,
+         fdMem,
+         (uint32_t)dmaP[n] | 0x40000000
+      );
+
+      pageAdr2 += PAGE_SIZE;
+
+      if (dmaP[n] == 0) status = 1;
+   }
+
+   return status;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static uint32_t * initMapMem(int fd, uint32_t addr, uint32_t len)
+{
+    return (uint32_t *) mmap(0, len,
+       PROT_READ|PROT_WRITE|PROT_EXEC,
+       MAP_SHARED|MAP_LOCKED,
+       fd, addr);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int initCheckPermitted(void)
+{
+   DBG(DBG_STARTUP, "");
+
+   if ((fdMem = open("/dev/mem", O_RDWR | O_SYNC) ) < 0)
+   {
+      fprintf(stderr,
+         "\n" \
+         "+---------------------------------------------------------+\n" \
+         "|Sorry, you don't have permission to run this program.    |\n" \
+         "|Try running as root, e.g. precede the command with sudo. |\n" \
+         "+---------------------------------------------------------+\n\n");
+      exit(-1);
+   }
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int initPeripherals(void)
+{
+   uint32_t dmaBase;
+
+   DBG(DBG_STARTUP, "");
+
+   gpioReg = initMapMem(fdMem, GPIO_BASE, GPIO_LEN);
+
+   if (gpioReg == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap gpio failed (%m)");
+
+   /* dma channels 0-14 share one page, 15 has another */
+
+   if (gpioCfg.DMAprimaryChannel < 15)
+   {
+      dmaBase = DMA_BASE;
+   }
+   else dmaBase = DMA15_BASE;
+
+   dmaReg = initMapMem(fdMem, dmaBase,  DMA_LEN);
+
+   if (dmaReg == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap dma failed (%m)");
+
+   if (gpioCfg.DMAprimaryChannel < 15)
+   {
+      dmaIn =  dmaReg + (gpioCfg.DMAprimaryChannel   * 0x40);
+      dmaOut = dmaReg + (gpioCfg.DMAsecondaryChannel * 0x40);
+   }
+
+   DBG(DBG_STARTUP, "DMA #%d @ %08X @ %08X",
+      gpioCfg.DMAprimaryChannel, dmaBase, (uint32_t)dmaIn);
+
+   DBG(DBG_STARTUP, "debug reg is %08X", dmaIn[DMA_DEBUG]);
+
+   clkReg  = initMapMem(fdMem, CLK_BASE,  CLK_LEN);
+
+   if (clkReg == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap clk failed (%m)");
+
+   systReg  = initMapMem(fdMem, SYST_BASE,  SYST_LEN);
+
+   if (systReg == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap syst failed (%m)");
+
+   pwmReg  = initMapMem(fdMem, PWM_BASE,  PWM_LEN);
+
+   if (pwmReg == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap pwm failed (%m)");
+
+   pcmReg  = initMapMem(fdMem, PCM_BASE,  PCM_LEN);
+
+   if (pcmReg == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap pcm failed (%m)");
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int initDMAblock(int pagemapFd, int block)
+{
+   int trys, ok;
+   unsigned pageNum;
+
+   DBG(DBG_STARTUP, "");
+
+   dmaBloc[block] = mmap(
+       0, (PAGES_PER_BLOCK*PAGE_SIZE),
+       PROT_READ|PROT_WRITE,
+       MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
+       -1, 0);
+
+   if (dmaBloc[block] == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap dma block %d failed (%m)", block);
+
+   /* force allocation of physical memory */
+
+   memset((void *)dmaBloc[block], 0, (PAGES_PER_BLOCK*PAGE_SIZE));
+
+   pageNum = block * PAGES_PER_BLOCK;
+
+   dmaVirt[pageNum] = mmap(
+       0, (PAGES_PER_BLOCK*PAGE_SIZE),
+       PROT_READ|PROT_WRITE,
+       MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
+       -1, 0);
+
+   if (dmaVirt[pageNum] == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap dma block %d failed (%m)", block);
+
+   munmap(dmaVirt[pageNum], PAGES_PER_BLOCK*PAGE_SIZE);
+
+   trys = 0;
+   ok = 0;
+
+   while ((trys < 10) && !ok)
+   {
+      if (initZaps(pagemapFd,
+                    dmaBloc[block],
+                    &dmaVirt[pageNum],
+                    &dmaPhys[pageNum],
+                    PAGES_PER_BLOCK) == 0) ok = 1;
+      else myGpioDelay(50000);
+
+      ++trys;
+   }
+
+   if (!ok) SOFT_ERROR(PI_INIT_FAILED, "initZaps failed");
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int initDMAcbs(void)
+{
+   int pid;
+   char str[64];
+   int pagemapFd;
+   int i, servoCycles, superCycles;
+
+   DBG(DBG_STARTUP, "");
+
+   /* Calculate the number of blocks needed for buffers.  The number
+      of blocks must be a multiple of the 20ms servo cycle.
+   */
+
+   servoCycles = gpioCfg.bufferMilliseconds / 20;
+   if           (gpioCfg.bufferMilliseconds % 20) servoCycles++;
+
+   bufferCycles = (SUPERCYCLE * servoCycles) / gpioCfg.clockMicros;
+
+   superCycles = bufferCycles / SUPERCYCLE;
+   if           (bufferCycles % SUPERCYCLE) superCycles++;
+
+   bufferCycles = SUPERCYCLE * superCycles;
+
+   bufferBlocks = bufferCycles / CYCLES_PER_BLOCK;
+
+   DBG(DBG_STARTUP, "bmillis=%d mics=%d bblk=%d bcyc=%d",
+      gpioCfg.bufferMilliseconds, gpioCfg.clockMicros,
+      bufferBlocks, bufferCycles);
+
+   /* allocate memory for pointers to virtual and physical pages */
+
+   dmaBloc = mmap(
+       0, (bufferBlocks+1)*sizeof(dmaPage_t *),
+       PROT_READ|PROT_WRITE,
+       MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
+       -1, 0);
+
+   if (dmaBloc == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap dma virtual failed (%m)");
+
+   dmaVirt = mmap(
+       0, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *),
+       PROT_READ|PROT_WRITE,
+       MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
+       -1, 0);
+
+   if (dmaVirt == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap dma virtual failed (%m)");
+
+   dmaPhys = mmap(
+       0, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *),
+       PROT_READ|PROT_WRITE,
+       MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED,
+       -1, 0);
+
+   if (dmaPhys == MAP_FAILED)
+      SOFT_ERROR(PI_INIT_FAILED, "mmap dma physical failed (%m)");
+
+   dmaIPhys = (dmaIPage_t **) dmaPhys;
+   dmaIVirt = (dmaIPage_t **) dmaVirt;
+
+   dmaOPhys = (dmaOPage_t **)(dmaPhys + (PAGES_PER_BLOCK*bufferBlocks));
+   dmaOVirt = (dmaOPage_t **)(dmaVirt + (PAGES_PER_BLOCK*bufferBlocks));
+
+   pid = getpid();
+
+   sprintf(str, "/proc/%d/pagemap", pid);
+
+   pagemapFd = open(str, O_RDONLY);
+  
+   if (pagemapFd < 0)
+      SOFT_ERROR(PI_INIT_FAILED, "open pagemap failed(%m)");
+
+   for (i=0; i<(bufferBlocks+1); i++) initDMAblock(pagemapFd, i);
+
+   close(pagemapFd);
+
+   DBG(DBG_STARTUP, "dmaBloc=%08X dmaIn=%08X",
+      (uint32_t)dmaBloc, (uint32_t)dmaIn);
+
+   DBG(DBG_STARTUP, "gpioReg=%08X pwmReg=%08X pcmReg=%08X clkReg=%08X",
+      (uint32_t)gpioReg, (uint32_t)pwmReg,
+      (uint32_t)pcmReg,  (uint32_t)clkReg); 
+
+   for (i=0; i<DMA_PAGES; i++)
+      DBG(DBG_STARTUP, "dmaIPhys[%d]=%08X", i, (uint32_t)dmaIPhys[i]);
+
+   dmaInitCbs();
+
+   if (gpioCfg.dbgLevel >= DBG_DMACBS)
+      for (i=0; i<NUM_CBS; i++) dmaCbPrint(i);
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initPWM(unsigned bits)
+{
+   DBG(DBG_STARTUP, "");
+
+   /* reset PWM */
+
+   pwmReg[PWM_CTL] = 0;
+
+   myGpioDelay(10);
+
+   pwmReg[PWM_STA] = -1;
+
+   myGpioDelay(10);
+
+   /* set number of bits to transmit */
+
+   pwmReg[PWM_RNG1] = bits;
+
+   myGpioDelay(10);
+
+   dmaIVirt[0]->periphData = 1;
+   
+   /* enable PWM DMA, raise panic and dreq thresholds to 15 */
+
+   pwmReg[PWM_DMAC] = PWM_DMAC_ENAB      |
+                      PWM_DMAC_PANIC(15) |
+                      PWM_DMAC_DREQ(15);
+
+   myGpioDelay(10);
+
+   /* clear PWM fifo */
+
+   pwmReg[PWM_CTL] = PWM_CTL_CLRF1;
+
+   myGpioDelay(10);
+
+   /* enable PWM channel 1 and use fifo */
+
+   pwmReg[PWM_CTL] = PWM_CTL_USEF1 | PWM_CTL_MODE1 | PWM_CTL_PWEN1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initPCM(unsigned bits)
+{
+   DBG(DBG_STARTUP, "");
+
+   /* disable PCM so we can modify the regs */
+
+   pcmReg[PCM_CS] = 0;
+
+   myGpioDelay(1000);
+
+   pcmReg[PCM_FIFO]   = 0;
+   pcmReg[PCM_MODE]   = 0;
+   pcmReg[PCM_RXC]    = 0;
+   pcmReg[PCM_TXC]    = 0;
+   pcmReg[PCM_DREQ]   = 0;
+   pcmReg[PCM_INTEN]  = 0;
+   pcmReg[PCM_INTSTC] = 0;
+   pcmReg[PCM_GRAY]   = 0;
+
+   myGpioDelay(1000);
+  
+   pcmReg[PCM_MODE] = PCM_MODE_FLEN(bits-1); /* # bits in frame */
+
+   /* enable channel 1 with # bits width */
+
+   pcmReg[PCM_TXC] = PCM_TXC_CH1EN | PCM_TXC_CH1WID(bits-8);
+
+   pcmReg[PCM_CS] |= PCM_CS_STBY; /* clear standby */
+
+   myGpioDelay(1000);
+
+   pcmReg[PCM_CS] |= PCM_CS_TXCLR; /* clear TX FIFO */
+
+   pcmReg[PCM_CS] |= PCM_CS_DMAEN; /* enable DREQ */
+
+   pcmReg[PCM_DREQ] = PCM_DREQ_TX_PANIC(16) | PCM_DREQ_TX_REQ_L(30);
+
+   pcmReg[PCM_INTSTC] = 0b1111; /* clear status bits */
+
+   /* enable PCM */
+
+   pcmReg[PCM_CS] |= PCM_CS_EN ;
+
+   /* enable tx */
+
+   pcmReg[PCM_CS] |= PCM_CS_TXON;
+
+   dmaIVirt[0]->periphData = 0x0F;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initClock(int mainClock)
+{
+   unsigned clkCtl, clkDiv, clkSrc, clkDivI, clkDivF, clkMash, clkBits;
+   char * per, * src;
+   unsigned micros;
+
+   DBG(DBG_STARTUP, "");
+
+   if (mainClock) micros = gpioCfg.clockMicros;
+   else           micros = PI_WF_MICROS;
+
+   if ((gpioCfg.clockPeriph == PI_CLOCK_PCM) && mainClock)
+   {
+      clkCtl = CLK_PCMCTL;
+      clkDiv = CLK_PCMDIV;
+      per = "PCM";
+   }
+   else
+   {
+      clkCtl = CLK_PWMCTL;
+      clkDiv = CLK_PWMDIV;
+      per = "PWM";
+   }
+
+   if (gpioCfg.clockSource == PI_CLOCK_PLLD)
+   {
+      clkSrc  = CLK_CTL_SRC_PLLD;
+      clkDivI = 50 * micros;
+      clkDivF = 0;
+      clkMash = 0;
+      clkBits = 10;
+      src = "PLLD";
+   }
+   else
+   {
+      clkSrc  = CLK_CTL_SRC_OSC;
+      clkDivI = clkCfg[micros].divi;
+      clkDivF = clkCfg[micros].divf;
+      clkMash = clkCfg[micros].mash;
+      clkBits = clkCfg[micros].bits;
+      src = "OSC";
+   }
+
+   DBG(DBG_STARTUP, "%s %s divi=%d divf=%d mash=%d bits=%d",
+      per, src, clkDivI, clkDivF, clkMash, clkBits);
+
+   clkReg[clkCtl] = CLK_PASSWD | CLK_CTL_KILL;
+
+   myGpioDelay(10);
+
+   clkReg[clkDiv] =
+      (CLK_PASSWD | CLK_DIV_DIVI(clkDivI) | CLK_DIV_DIVF(clkDivF));
+
+   myGpioDelay(10);
+
+   clkReg[clkCtl] =
+      (CLK_PASSWD | CLK_CTL_MASH(clkMash) | CLK_CTL_SRC(clkSrc));
+
+   myGpioDelay(10);
+
+   clkReg[clkCtl] |= (CLK_PASSWD | CLK_CTL_ENAB);
+
+   myGpioDelay(10);
+
+   if ((gpioCfg.clockPeriph == PI_CLOCK_PCM) && mainClock)
+        initPCM(clkBits);
+   else initPWM(clkBits);
+
+   myGpioDelay(2000);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initDMAgo(uint32_t  * dmaAddr, uint32_t cbAddr)
+{
+   DBG(DBG_STARTUP, "");
+
+   dmaAddr[DMA_CS] = DMA_CHANNEL_RESET;
+
+   dmaAddr[DMA_CS] = DMA_INTERRUPT_STATUS | DMA_END_FLAG;
+
+   dmaAddr[DMA_CONBLK_AD] = cbAddr | DMA_BUS_ADR;
+
+   /* clear READ/FIFO/READ_LAST_NOT_SET error bits */
+
+   dmaAddr[DMA_DEBUG] = DMA_DEBUG_READ_ERR            |          
+                        DMA_DEBUG_FIFO_ERR            |
+                        DMA_DEBUG_RD_LST_NOT_SET_ERR;
+
+
+   dmaAddr[DMA_CS] = DMA_WAIT_ON_WRITES    |
+                     DMA_PANIC_PRIORITY(8) |
+                     DMA_PRIORITY(8)       |
+                     DMA_ACTIVATE;
+
+   DMAstarted = 1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initClearGlobals(void)
+{
+   int i;
+
+   DBG(DBG_STARTUP, "");
+
+   alertBits    = 0;
+   monitorBits  = 0;
+   notifyBits   = 0;
+
+   libInitialised   = 0;
+   DMAstarted       = 0;
+   hardwareRevision = 0;
+
+   pthAlertRunning  = 0;
+   pthFifoRunning   = 0;
+   pthSocketRunning = 0;
+
+   wfc[0] = 0;
+   wfc[1] = 0;
+   wfc[2] = 0;
+
+   wfcur=0;
+
+   wfStats.micros     = 0;
+   wfStats.highMicros = 0;
+   wfStats.maxMicros  = -1;
+
+   wfStats.pulses     = 0;
+   wfStats.highPulses = 0;
+   wfStats.maxPulses  = PI_WAVE_MAX_PULSES;
+
+   wfStats.cbs        = 0;
+   wfStats.highCbs    = 0;
+   wfStats.maxCbs     = (PAGES_PER_BLOCK * CBS_PER_OPAGE);
+
+   gpioGetSamples.func     = NULL;
+   gpioGetSamples.ex       = 0;
+   gpioGetSamples.userdata = NULL;
+   gpioGetSamples.bits     = 0;
+
+   for (i=0; i<=PI_MAX_USER_GPIO; i++)
+   {
+      wfRx[i].mode         = PI_WFRX_NONE;
+
+      gpioAlert[i].func    = NULL;
+
+      gpioInfo [i].is      = GPIO_UNDEFINED;
+      gpioInfo [i].width   = 0;
+      gpioInfo [i].range   = PI_DEFAULT_DUTYCYCLE_RANGE;
+      gpioInfo [i].freqIdx = DEFAULT_PWM_IDX;
+   }
+
+   for (i=0; i<PI_NOTIFY_SLOTS; i++)
+   {
+      gpioNotify[i].seqno = 0;
+      gpioNotify[i].state = PI_NOTIFY_CLOSED;
+   }
+
+   for (i=0; i<=PI_MAX_SIGNUM; i++)
+   {
+      gpioSignal[i].func     = NULL;
+      gpioSignal[i].ex       = 0;
+      gpioSignal[i].userdata = NULL;
+   }
+
+   for (i=0; i<=PI_MAX_TIMER; i++)
+   {
+      gpioTimer[i].running = 0;
+      gpioTimer[i].func    = NULL;
+   }
+
+   /* calculate the usable PWM frequencies */
+
+   for (i=0; i<PWM_FREQS; i++)
+   {
+      pwmFreq[i]=
+         (1000000.0/
+            ((float)PULSE_PER_CYCLE*gpioCfg.clockMicros*pwmCycles[i]))+0.5;
+
+      DBG(DBG_STARTUP, "f%d is %d", i, pwmFreq[i]);
+   }
+
+   inpFifo = NULL;
+   outFifo = NULL;
+
+   fdLock = -1;
+   fdMem  = -1;
+   fdSock = -1;
+
+   dmaBloc = MAP_FAILED;
+   dmaVirt = MAP_FAILED;
+   dmaPhys = MAP_FAILED;
+
+   clkReg  = MAP_FAILED;
+   dmaReg  = MAP_FAILED;
+   gpioReg = MAP_FAILED;
+   pcmReg  = MAP_FAILED;
+   pwmReg  = MAP_FAILED;
+   systReg = MAP_FAILED;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned initHardwareRevision(void)
+{
+   FILE * filp;
+   unsigned rev;
+   char buf[512];
+   char term;
+
+   rev = 0;
+
+   filp = fopen ("/proc/cpuinfo", "r");
+
+   if (filp != NULL)
+   {
+      while (fgets(buf, sizeof(buf), filp) != NULL)
+      {
+         if (!strncasecmp("revision\t", buf, 9))
+         {
+            if (sscanf(buf+strlen(buf)-5, "%x%c", &rev, &term) == 2)
+            {
+               if (term == '\n') break;
+               rev = 0;
+            }
+         }
+      }
+      fclose(filp);
+   }
+   return rev;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void initReleaseResources(void)
+{
+   int i;
+
+   DBG(DBG_STARTUP, "");
+
+   /* shut down running threads */
+
+   for (i=0; i<=PI_MAX_TIMER; i++)
+   {
+      if (gpioTimer[i].running)
+      {
+         /* destroy thread */
+
+         pthread_cancel(gpioTimer[i].pthId);
+         pthread_join(gpioTimer[i].pthId, NULL);
+         gpioTimer[i].running = 0;
+      }
+   }
+
+   if (pthAlertRunning)
+   {
+      pthread_cancel(pthAlert);
+      pthread_join(pthAlert, NULL);
+      pthAlertRunning = 0;
+   }
+
+   if (pthFifoRunning)
+   {
+      pthread_cancel(pthFifo);
+      pthread_join(pthFifo, NULL);
+      pthFifoRunning = 0;
+   }
+
+   if (pthSocketRunning)
+   {
+      pthread_cancel(pthSocket);
+      pthread_join(pthSocket, NULL);
+      pthSocketRunning = 0;
+   }
+
+   /* release mmap'd memory */
+
+   if (clkReg  != MAP_FAILED) munmap((void *)clkReg,  CLK_LEN);
+   if (dmaReg  != MAP_FAILED) munmap((void *)dmaReg,   DMA_LEN);
+   if (gpioReg != MAP_FAILED) munmap((void *)gpioReg, GPIO_LEN);
+   if (pcmReg  != MAP_FAILED) munmap((void *)pcmReg,  PCM_LEN);
+   if (pwmReg  != MAP_FAILED) munmap((void *)pwmReg,  PWM_LEN);
+   if (systReg != MAP_FAILED) munmap((void *)systReg, SYST_LEN);
+
+   clkReg  = MAP_FAILED;
+   dmaReg  = MAP_FAILED;
+   gpioReg = MAP_FAILED;
+   pcmReg  = MAP_FAILED;
+   pwmReg  = MAP_FAILED;
+   systReg = MAP_FAILED;
+
+   if (dmaVirt != MAP_FAILED)
+   {
+      for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+1); i++)
+      {
+         munmap(dmaVirt[i], PAGE_SIZE);
+      }
+
+      munmap(dmaVirt, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *));
+   }
+
+   dmaVirt = MAP_FAILED;
+
+   if (dmaPhys != MAP_FAILED)
+   {
+      for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+1); i++)
+      {
+         munmap(dmaPhys[i], PAGE_SIZE);
+      }
+
+      munmap(dmaPhys, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *));
+   }
+
+   dmaPhys = MAP_FAILED;
+
+   if (dmaBloc != MAP_FAILED)
+   {
+      for (i=0; i<(bufferBlocks+1); i++)
+      {
+         munmap(dmaBloc[i], PAGES_PER_BLOCK*PAGE_SIZE);
+      }
+
+      munmap(dmaBloc, (bufferBlocks+1)*sizeof(dmaPage_t *));
+   }
+
+   dmaBloc = MAP_FAILED;
+
+   if (inpFifo != NULL)
+   {
+      fclose(inpFifo);
+      unlink(PI_INPFIFO);
+      inpFifo = NULL;
+   }
+
+   if (outFifo != NULL)
+   {
+      fclose(outFifo);
+      unlink(PI_OUTFIFO);
+      outFifo = NULL;
+   }
+
+   if (fdMem != -1)
+   {
+      close(fdMem);
+      fdMem = -1;
+   }
+
+   if (fdLock != -1)
+   {
+      close(fdLock);
+      unlink(PI_LOCKFILE);
+      fdLock = -1;
+   }
+
+   if (fdSock != -1)
+   {
+      close(fdSock);
+      fdSock = -1;
+   }
+}
+
+/* ======================================================================= */
+
+int gpioInitialise(void)
+{
+   int i;
+
+   clock_gettime(CLOCK_REALTIME, &libStarted);
+
+   DBG(DBG_STARTUP, "");
+
+   if (libInitialised) return PIGPIO_VERSION;
+
+   initClearGlobals();
+
+   if (initCheckPermitted() < 0) return PI_INIT_FAILED;
+
+   fdLock = initGrabLockFile();
+
+   if (fdLock < 0)
+      SOFT_ERROR(PI_INIT_FAILED, "Can't lock %s", PI_LOCKFILE);
+
+   sigSetHandler();
+
+   if (initPeripherals() < 0) return PI_INIT_FAILED;
+
+   if (initDMAcbs() < 0) return PI_INIT_FAILED;
+
+   /* done with /dev/mem */
+
+   if (fdMem != -1)
+   {
+      close(fdMem);
+      fdMem = -1;
+   }
+
+   initClock(1); /* initialise main clock */
+
+   libInitialised = 1;
+
+   atexit(gpioTerminate);
+
+   if (pthread_attr_init(&pthAttr))
+      SOFT_ERROR(PI_INIT_FAILED, "pthread_attr_init failed (%m)");
+
+   if (pthread_attr_setstacksize(&pthAttr, STACK_SIZE))
+      SOFT_ERROR(PI_INIT_FAILED, "pthread_attr_setstacksize failed (%m)");
+
+   if (pthread_create(&pthAlert, &pthAttr, pthAlertThread, &i))
+      SOFT_ERROR(PI_INIT_FAILED, "pthread_create alert failed (%m)");
+
+   pthAlertRunning = 1;
+
+   if (!(gpioCfg.ifFlags & PI_DISABLE_FIFO_IF))
+   {
+      if (pthread_create(&pthFifo, &pthAttr, pthFifoThread, &i))
+         SOFT_ERROR(PI_INIT_FAILED, "pthread_create fifo failed (%m)");
+
+      pthFifoRunning = 1;
+   }
+
+   if (!(gpioCfg.ifFlags & PI_DISABLE_SOCK_IF))
+   {
+      if (pthread_create(&pthSocket, &pthAttr, pthSocketThread, &i))
+         SOFT_ERROR(PI_INIT_FAILED, "pthread_create socket failed (%m)");
+
+      pthSocketRunning = 1;
+   }
+
+   hardwareRevision = initHardwareRevision();
+
+   initDMAgo((uint32_t *)dmaIn, (uint32_t)dmaIPhys[0]);
+
+   return PIGPIO_VERSION;
+ }
+
+/* ----------------------------------------------------------------------- */
+
+void gpioTerminate(void)
+{
+   int i;
+
+   DBG(DBG_USER, "");
+
+
+   if (libInitialised)
+   {
+      /* reset DMA */
+
+      dmaIn[DMA_CS] = DMA_CHANNEL_RESET;
+      dmaOut[DMA_CS] = DMA_CHANNEL_RESET;
+
+      /* reset PWM */
+
+      pwmReg[PWM_CTL] = 0;
+
+      libInitialised = 0;
+
+      DMAstarted = 0;
+
+      if (gpioCfg.showStats)
+      {
+         fprintf(stderr, "micros=%d\n", gpioCfg.clockMicros);
+
+         fprintf(stderr, "samples %u maxSamples %u maxEmit %u emitFrags %u\n",
+            gpioStats.numSamples, gpioStats.maxSamples,
+            gpioStats.maxEmit, gpioStats.emitFrags);
+
+         fprintf(stderr, "cb time %d, calls %u alert ticks %u\n",
+            gpioStats.cbTicks, gpioStats.cbCalls, gpioStats.alertTicks);
+
+         for (i=0; i< TICKSLOTS; i++)
+            fprintf(stderr, "%9u ", gpioStats.diffTick[i]);
+
+         fprintf(stderr, "\n");
+      }
+   }
+
+   initReleaseResources();
+
+   fflush(NULL);
+}
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetMode(unsigned gpio, unsigned mode)
+{
+   int reg, shift;
+
+   DBG(DBG_USER, "gpio=%d mode=%d", gpio, mode);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_GPIO)
+      SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+   if (mode > PI_ALT3)
+      SOFT_ERROR(PI_BAD_MODE, "gpio %d, bad mode (%d)", gpio, mode);
+
+   reg   =  gpio/10;
+   shift = (gpio%10) * 3;
+
+   if (gpio <= PI_MAX_USER_GPIO)
+   {
+      if (mode != PI_OUTPUT)
+      {
+         switch (gpioInfo[gpio].is)
+         {
+            case GPIO_SERVO:
+               /* switch servo off */
+               myGpioSetServo(gpio,
+                  gpioInfo[gpio].width/gpioCfg.clockMicros, 0);
+               break;
+
+            case GPIO_PWM:
+               /* switch pwm off */
+               myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
+               break;
+
+         }
+
+         gpioInfo[gpio].is = GPIO_UNDEFINED;
+      }
+   }
+
+   gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioGetMode(unsigned gpio)
+{
+   int reg, shift;
+
+   DBG(DBG_USER, "gpio=%d", gpio);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_GPIO)
+      SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+   reg   =  gpio/10;
+   shift = (gpio%10) * 3;
+
+   return (*(gpioReg + reg) >> shift) & 7;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetPullUpDown(unsigned gpio, unsigned pud)
+{
+   DBG(DBG_USER, "gpio=%d pud=%d", gpio, pud);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_GPIO)
+      SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+   if (pud > PI_PUD_UP)
+      SOFT_ERROR(PI_BAD_PUD, "gpio %d, bad pud (%d)", gpio, pud);
+
+   *(gpioReg + GPPUD) = pud;
+
+   myGpioDelay(20);
+
+   *(gpioReg + GPPUDCLK0 + BANK) = BIT;
+
+   myGpioDelay(20);
+  
+   *(gpioReg + GPPUD) = 0;
+
+   *(gpioReg + GPPUDCLK0 + BANK) = 0;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioRead(unsigned gpio)
+{
+   DBG(DBG_USER, "gpio=%d", gpio);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_GPIO)
+      SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+   if ((*(gpioReg + GPLEV0 + BANK) & BIT) != 0) return PI_ON;
+   else                                         return PI_OFF;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite(unsigned gpio, unsigned level)
+{
+   DBG(DBG_USER, "gpio=%d level=%d", gpio, level);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_GPIO)
+      SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);
+
+   if (level > PI_ON)
+      SOFT_ERROR(PI_BAD_LEVEL, "gpio %d, bad level (%d)", gpio, level);
+
+   if (gpio <= PI_MAX_USER_GPIO)
+   {
+      if (gpioInfo[gpio].is != GPIO_OUTPUT)
+      {
+         if (gpioInfo[gpio].is == GPIO_UNDEFINED)
+         {
+            gpioSetMode(gpio, PI_OUTPUT);
+         }
+         else if (gpioInfo[gpio].is == GPIO_PWM)
+         {
+            /* switch pwm off */
+            myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
+         }
+         else if (gpioInfo[gpio].is == GPIO_SERVO)
+         {
+            /* switch servo off */
+            myGpioSetServo(
+               gpio, gpioInfo[gpio].width/gpioCfg.clockMicros, 0);
+         }
+
+         gpioInfo[gpio].is=GPIO_OUTPUT;
+         gpioInfo[gpio].width=0;
+      }
+   }
+
+   if (level == PI_OFF) *(gpioReg + GPCLR0 + BANK) = BIT;
+   else                 *(gpioReg + GPSET0 + BANK) = BIT;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioPWM(unsigned gpio, unsigned val)
+{
+   DBG(DBG_USER, "gpio=%d dutycycle=%d", gpio, val);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if (val > gpioInfo[gpio].range) 
+      SOFT_ERROR(PI_BAD_DUTYCYCLE, "gpio %d, bad dutycycle (%d)", gpio, val);
+
+   if (gpioInfo[gpio].is != GPIO_PWM)
+   {
+      if (gpioInfo[gpio].is == GPIO_UNDEFINED)
+      {
+         gpioSetMode(gpio, PI_OUTPUT);
+      }
+      else if (gpioInfo[gpio].is == GPIO_SERVO)
+      {
+         /* switch servo off */
+         myGpioSetServo(gpio, gpioInfo[gpio].width, 0);
+         gpioInfo[gpio].width = 0;
+         gpioInfo[gpio].freqIdx = DEFAULT_PWM_IDX; /* default frequency */
+      }
+      gpioInfo[gpio].is = GPIO_PWM;
+   }
+
+   myGpioSetPwm(gpio, gpioInfo[gpio].width, val);
+
+   gpioInfo[gpio].width=val;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetPWMrange(unsigned gpio, unsigned range)
+{
+   int oldWidth, newWidth;
+
+   DBG(DBG_USER, "gpio=%d range=%d", gpio, range);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if ((range < PI_MIN_DUTYCYCLE_RANGE)  || (range > PI_MAX_DUTYCYCLE_RANGE))
+      SOFT_ERROR(PI_BAD_DUTY_RANGE, "gpio %d, bad range (%d)", gpio, range);
+
+   oldWidth = gpioInfo[gpio].width;
+
+   if (oldWidth)
+   {
+      if (gpioInfo[gpio].is == GPIO_PWM)
+      {
+         newWidth = (range * oldWidth) / gpioInfo[gpio].range;
+
+         myGpioSetPwm(gpio, oldWidth, 0);
+         gpioInfo[gpio].range = range;
+         gpioInfo[gpio].width = newWidth;
+         myGpioSetPwm(gpio, 0, newWidth);
+      }
+   }
+
+   gpioInfo[gpio].range = range;
+
+   /* return the actual range for the current gpio frequency */
+
+   return pwmRealRange[gpioInfo[gpio].freqIdx];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioGetPWMrange(unsigned gpio)
+{
+   DBG(DBG_USER, "gpio=%d", gpio);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   return (gpioInfo[gpio].range);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioGetPWMrealRange(unsigned gpio)
+{
+   DBG(DBG_USER, "gpio=%d", gpio);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   return pwmRealRange[gpioInfo[gpio].freqIdx];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetPWMfrequency(unsigned gpio, unsigned frequency)
+{
+   int i, width;
+   unsigned diff, best, idx;
+
+   DBG(DBG_USER, "gpio=%d frequency=%d", gpio, frequency);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if      (frequency > pwmFreq[0])           idx = 0;
+   else if (frequency < pwmFreq[PWM_FREQS-1]) idx = PWM_FREQS-1;
+   else
+   {
+      best = 100000; /* impossibly high frequency difference */
+      idx = 0;
+
+      for (i=0; i<PWM_FREQS; i++)
+      {
+         if (frequency > pwmFreq[i]) diff = frequency - pwmFreq[i];
+         else                        diff = pwmFreq[i] - frequency;
+
+         if (diff < best)
+         {
+            best = diff;
+            idx = i;
+         }
+      }
+   }
+
+   width = gpioInfo[gpio].width;
+
+   if (width)
+   {
+      if (gpioInfo[gpio].is == GPIO_PWM)
+      {
+         myGpioSetPwm(gpio, width, 0);
+         gpioInfo[gpio].freqIdx = idx;
+         myGpioSetPwm(gpio, 0, width);
+      }
+   }
+
+   gpioInfo[gpio].freqIdx = idx;
+
+   return pwmFreq[idx];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioGetPWMfrequency(unsigned gpio)
+{
+   DBG(DBG_USER, "gpio=%d", gpio);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   return (pwmFreq[gpioInfo[gpio].freqIdx]);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioServo(unsigned gpio, unsigned val)
+{
+   DBG(DBG_USER, "gpio=%d pulsewidth=%d", gpio, val);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if ((val!=PI_SERVO_OFF) && (val<PI_MIN_SERVO_PULSEWIDTH))
+      SOFT_ERROR(PI_BAD_PULSEWIDTH,
+         "gpio %d, bad pulsewidth (%d)", gpio, val);
+
+   if (val>PI_MAX_SERVO_PULSEWIDTH)
+      SOFT_ERROR(PI_BAD_PULSEWIDTH,
+         "gpio %d, bad pulsewidth (%d)", gpio, val);
+
+   if (gpioInfo[gpio].is != GPIO_SERVO)
+   {
+      if (gpioInfo[gpio].is == GPIO_UNDEFINED)
+      {
+         gpioSetMode(gpio, PI_OUTPUT);
+      }
+      else if (gpioInfo[gpio].is == GPIO_PWM)
+      {
+         /* switch pwm off */
+         myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
+         gpioInfo[gpio].width=0;
+      }
+      gpioInfo[gpio].is = GPIO_SERVO;
+      gpioInfo[gpio].freqIdx = clkCfg[gpioCfg.clockMicros].servoIdx;
+   }
+
+   myGpioSetServo(gpio, gpioInfo[gpio].width, val);
+
+   gpioInfo[gpio].width=val;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetMicros(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.micros;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetHighMicros(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.highMicros;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetMaxMicros(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.maxMicros;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetPulses(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.pulses;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetHighPulses(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.highPulses;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetMaxPulses(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.maxPulses;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetCbs(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.cbs;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetHighCbs(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.highCbs;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveGetMaxCbs(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return wfStats.maxCbs;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveClear(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   wfc[0] = 0;
+   wfc[1] = 0;
+   wfc[2] = 0;
+
+   wfcur = 0;
+
+   wfStats.micros = 0;
+   wfStats.pulses = 0;
+   wfStats.cbs    = 0;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveAddGeneric(unsigned numPulses, gpioPulse_t * pulses)
+{
+   DBG(DBG_USER, "numPulses=%u pulses=%08X", numPulses, (uint32_t)pulses);
+
+   CHECK_INITED;
+
+   if (numPulses > PI_WAVE_MAX_PULSES)
+      SOFT_ERROR(PI_TOO_MANY_PULSES, "bad number of pulses (%d)", numPulses);
+
+   return waveMerge(numPulses, pulses);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveAddSerial(unsigned gpio,
+                      unsigned baud,
+                      unsigned numChar,
+                      char *   str)
+{
+   int i, b, p, lev, c, v;
+
+   unsigned bitDelay[10];
+
+   DBG(DBG_USER, "gpio=%d baud=%d numChar=%d str*=%08X",
+      gpio, baud, numChar, (uint32_t)str);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if ((baud < PI_WAVE_MIN_BAUD) || (baud > PI_WAVE_MAX_BAUD))
+      SOFT_ERROR(PI_BAD_WAVE_BAUD,
+         "gpio %d, bad baud rate (%d)", gpio, baud);
+
+   if (numChar > PI_WAVE_MAX_CHARS)
+      SOFT_ERROR(PI_TOO_MANY_CHARS, "too many chars (%d)", numChar);
+
+   if (!numChar) return 0;
+
+   waveBitDelay(baud, bitDelay);
+
+   p = 0;
+
+   wf[2][p].gpioOn  = (1<<gpio);
+   wf[2][p].gpioOff = 0;
+   wf[2][p].usDelay = bitDelay[0];
+
+   for (i=0; i<numChar; i++)
+   {
+      p++;
+   
+      /* start bit */
+
+      wf[2][p].gpioOn = 0;
+      wf[2][p].gpioOff = (1<<gpio);
+      wf[2][p].usDelay = bitDelay[0];
+
+      lev = 0;
+
+      c = str[i];
+
+      for (b=0; b<8; b++)
+      {
+         if (c & (1<<b)) v=1; else v=0;
+
+         if (v == lev) wf[2][p].usDelay += bitDelay[b+1];
+         else
+         {
+            p++;
+
+            lev = v;
+
+            if (lev)
+            {
+               wf[2][p].gpioOn = (1<<gpio);
+               wf[2][p].gpioOff = 0;
+            }
+            else
+            {
+               wf[2][p].gpioOn = 0;
+               wf[2][p].gpioOff = (1<<gpio);
+            }
+
+            wf[2][p].usDelay = bitDelay[b+1];
+         }
+      }
+
+      /* stop bit */
+
+      if (lev) wf[2][p].usDelay += bitDelay[9];
+      else
+      {
+         p++;
+
+         wf[2][p].gpioOn = (1<<gpio);
+         wf[2][p].gpioOff = 0;
+         wf[2][p].usDelay = bitDelay[9];
+      }
+   }
+
+   p++;
+
+   wf[2][p].gpioOn  = (1<<gpio);
+   wf[2][p].gpioOff = 0;
+   wf[2][p].usDelay = bitDelay[0];
+
+   return waveMerge(p, wf[2]);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+int gpioWaveSerialReadStart(unsigned gpio,
+                            unsigned baud,
+                            gpioRx_t *rxp)
+{
+   int bitTime, timeoutMs;
+
+   DBG(DBG_USER, "gpio=%d baud=%d rxp*=%08X", gpio, baud, (uint32_t)rxp);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if ((baud < PI_WAVE_MIN_BAUD) || (baud > PI_WAVE_MAX_BAUD))
+      SOFT_ERROR(PI_BAD_WAVE_BAUD,
+         "gpio %d, bad baud rate (%d)", gpio, baud);
+
+   if (rxp == NULL)
+      SOFT_ERROR(PI_BAD_SERIAL_STRUC, "Null structure pointer");
+
+   if (rxp->buf == NULL)
+      SOFT_ERROR(PI_BAD_SERIAL_BUF, "Null buffer pointer");
+
+   bitTime = MILLION / baud;
+
+   timeoutMs = ((12 * bitTime)+1000)/1000;
+
+   wfRx[gpio].mode     = PI_WFRX_SERIAL;
+   wfRx[gpio].baud     = baud;
+   wfRx[gpio].rxp      = rxp;
+   wfRx[gpio].baud     = baud;
+   wfRx[gpio].fullBit  = bitTime;
+   wfRx[gpio].halfBit  = bitTime/2;
+   wfRx[gpio].rxp->readPos  = 0;
+   wfRx[gpio].rxp->writePos = 0;
+   wfRx[gpio].bit      = -1;
+
+   gpioSetWatchdog(gpio, timeoutMs); /* get a nudge if no change */
+
+   gpioSetAlertFunc(gpio, waveRxBit);
+
+   return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+int gpioWaveSerialReadStop(unsigned gpio)
+{
+   DBG(DBG_USER, "gpio=%d", gpio);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   switch(wfRx[gpio].mode)
+   {
+      case PI_WFRX_NONE:
+
+         SOFT_ERROR(PI_NOT_SERIAL_GPIO, "no serial read on gpio (%d)", gpio);
+
+         break;
+
+      case PI_WFRX_SERIAL:
+
+         gpioSetWatchdog(gpio, 0); /* switch off timeouts */
+
+         gpioSetAlertFunc(gpio, NULL); /* cancel alert */
+
+         wfRx[gpio].mode = PI_WFRX_NONE;
+
+         break;
+   }
+
+   return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+int gpioWaveTxBusy(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   if (dmaOut[DMA_CONBLK_AD])
+      return 1;
+   else
+      return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveTxStart(unsigned mode)
+{
+   static int secondaryClockInited = 0;
+
+   int cb, i;
+
+   DBG(DBG_USER, "mode=%d", mode);
+
+   CHECK_INITED;
+
+   if (mode > PI_WAVE_MODE_REPEAT)
+      SOFT_ERROR(PI_BAD_WAVE_MODE, "bad wave mode (%d)", mode);
+
+   if (wfc[wfcur] == 0) return 0;
+   
+   if (!secondaryClockInited)
+   {
+      initClock(0); /* initialise secondary clock */
+      secondaryClockInited = 1;
+   }
+
+   cb = wave2Cbs(mode);
+
+   if (gpioCfg.dbgLevel >= DBG_SLOW_TICK)
+      for (i=0; i<cb; i++) waveCbOPrint(i);
+
+   initDMAgo((uint32_t *)dmaOut, (uint32_t)dmaOPhys[0]);
+
+   return cb;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveTxStop(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   dmaOut[DMA_CS] = DMA_CHANNEL_RESET;
+
+   dmaOut[DMA_CONBLK_AD] = 0;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int intGpioSetAlertFunc(
+   unsigned gpio,
+   void *   f,
+   int      user,
+   void *   userdata)
+{
+   DBG(DBG_INTERNAL, "gpio=%d function=%08X, user=%d, userdata=%08X",
+      gpio, (uint32_t)f, user, (uint32_t)userdata);
+
+   gpioAlert[gpio].ex = user;
+   gpioAlert[gpio].userdata = userdata;
+
+   gpioAlert[gpio].func = f;
+
+   if (f)
+   {
+      alertBits |= BIT;
+   }
+   else
+   {
+      alertBits &= ~BIT;
+   }
+
+   monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetAlertFunc(unsigned gpio, gpioAlertFunc_t f)
+{
+   DBG(DBG_USER, "gpio=%d function=%08X", gpio, (uint32_t)f);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   intGpioSetAlertFunc(gpio, f, 0, NULL);
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetAlertFuncEx(unsigned gpio, gpioAlertFuncEx_t f, void * userdata)
+{
+   DBG(DBG_USER, "gpio=%d function=%08X userdata=%08X",
+      gpio, (uint32_t)f, (uint32_t)userdata);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   intGpioSetAlertFunc(gpio, f, 1, userdata);
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioNotifyOpen(void)
+{
+   int i, slot, fd;
+   char name[32];
+
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   slot = -1;
+
+   for (i=0; i<PI_NOTIFY_SLOTS; i++)
+   {
+      if (gpioNotify[i].state == PI_NOTIFY_CLOSED)
+      {
+         slot = i;
+         break;
+      }
+   }
+
+   if (slot < 0)
+      SOFT_ERROR(PI_NO_HANDLE, "no handle");
+
+   sprintf(name, "/dev/pigpio%d", slot);
+
+   myCreatePipe(name, 0664);
+
+   fd = open(name, O_RDWR|O_NONBLOCK);
+
+   if (fd < 0)
+      SOFT_ERROR(PI_BAD_PATHNAME, "open %s failed (%m)", name);
+
+   gpioNotify[slot].state = PI_NOTIFY_OPENED;
+   gpioNotify[slot].seqno = 0;
+   gpioNotify[slot].bits  = 0;
+   gpioNotify[slot].fd    = fd;
+   gpioNotify[slot].pipe  = 1;
+
+   return slot;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int gpioNotifyOpenInBand(int fd)
+{
+   int i, slot;
+
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   slot = -1;
+
+   for (i=0; i<PI_NOTIFY_SLOTS; i++)
+   {
+      if (gpioNotify[i].state == PI_NOTIFY_CLOSED)
+      {
+         slot = i;
+         break;
+      }
+   }
+
+   if (slot < 0) SOFT_ERROR(PI_NO_HANDLE, "no handle");
+
+   gpioNotify[slot].state = PI_NOTIFY_OPENED;
+   gpioNotify[slot].seqno = 0;
+   gpioNotify[slot].bits  = 0;
+   gpioNotify[slot].fd    = fd;
+   gpioNotify[slot].pipe  = 0;
+
+   return slot;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void intNotifyBits(void)
+{
+   int i;
+   uint32_t bits;
+
+   bits = 0;
+
+   for (i=0; i<PI_NOTIFY_SLOTS; i++)
+   {
+      if (gpioNotify[i].state == PI_NOTIFY_RUNNING)
+      {
+         bits |= gpioNotify[i].bits;
+      }
+   }
+
+   notifyBits = bits;
+
+   monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioNotifyBegin(unsigned handle, uint32_t bits)
+{
+   DBG(DBG_USER, "handle=%d bits=%08X", handle, bits);
+
+   CHECK_INITED;
+
+   if (handle > PI_NOTIFY_SLOTS)
+      SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+   if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
+      SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+   gpioNotify[handle].bits  = bits;
+
+   gpioNotify[handle].state = PI_NOTIFY_RUNNING;
+
+   intNotifyBits();
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioNotifyPause (unsigned handle)
+{
+   DBG(DBG_USER, "handle=%d", handle);
+
+   CHECK_INITED;
+
+   if (handle > PI_NOTIFY_SLOTS)
+      SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+   if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
+      SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+   gpioNotify[handle].bits  = 0;
+
+   gpioNotify[handle].state = PI_NOTIFY_PAUSED;
+
+   intNotifyBits();
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioNotifyClose(unsigned handle)
+{
+   DBG(DBG_USER, "handle=%d", handle);
+
+   CHECK_INITED;
+
+   if (handle > PI_NOTIFY_SLOTS)
+      SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+   if (gpioNotify[handle].state <= PI_NOTIFY_CLOSING)
+      SOFT_ERROR(PI_BAD_HANDLE, "bad handle (%d)", handle);
+
+   gpioNotify[handle].bits  = 0;
+
+   gpioNotify[handle].state = PI_NOTIFY_CLOSING;
+
+   intNotifyBits();
+
+   /* actual close done in alert thread */
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetWatchdog(unsigned gpio, unsigned timeout)
+{
+   DBG(DBG_USER, "gpio=%d timeout=%d", gpio, timeout);
+
+   CHECK_INITED;
+
+   if (gpio > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", gpio);
+
+   if (timeout > PI_MAX_WDOG_TIMEOUT)
+      SOFT_ERROR(PI_BAD_WDOG_TIMEOUT,
+         "gpio %d, bad timeout (%d)", gpio, timeout);
+
+   gpioAlert[gpio].timeout = timeout;
+   gpioAlert[gpio].tick    = systReg[SYST_CLO];
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetGetSamplesFunc(gpioGetSamplesFunc_t f, uint32_t bits)
+{
+   DBG(DBG_USER, "function=%08X bits=%08X", (uint32_t)f, bits);
+
+   CHECK_INITED;
+
+   gpioGetSamples.ex       = 0;
+   gpioGetSamples.userdata = NULL;
+   gpioGetSamples.func     = f;
+
+   if (f) gpioGetSamples.bits = bits;
+   else   gpioGetSamples.bits = 0;
+
+   monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetGetSamplesFuncEx(gpioGetSamplesFuncEx_t f,
+                            uint32_t bits,
+                            void * userdata)
+{
+   DBG(DBG_USER, "function=%08X bits=%08X", (uint32_t)f, bits);
+
+   CHECK_INITED;
+
+   gpioGetSamples.ex       = 1;
+   gpioGetSamples.userdata = userdata;
+   gpioGetSamples.func     = f;
+
+   if (f) gpioGetSamples.bits = bits;
+   else   gpioGetSamples.bits = 0;
+
+   monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static int intGpioSetTimerFunc(unsigned id,
+                               unsigned ms,
+                               void * f,
+                               int user,
+                               void * userdata)
+{
+   DBG(DBG_INTERNAL, "id=%d ms=%d function=%08X user=%d userdata=%08X",
+      id, ms, (uint32_t)f, user, (uint32_t)userdata);
+
+   gpioTimer[id].id   = id;
+
+   if (f)
+   {
+      gpioTimer[id].func     = f;
+      gpioTimer[id].ex       = user;
+      gpioTimer[id].userdata = userdata;
+      gpioTimer[id].millis   = ms;
+
+      if (!gpioTimer[id].running)
+      {
+         if (pthread_create(
+            &gpioTimer[id].pthId, &pthAttr, pthTimerTick, &gpioTimer[id]))
+               SOFT_ERROR(PI_TIMER_FAILED,
+                  "timer %d, create failed (%m)", id);
+
+         gpioTimer[id].running = 1;
+      }
+   }
+   else
+   {
+      if (gpioTimer[id].running)
+      {
+         /* destroy thread */
+
+         if (pthread_cancel(gpioTimer[id].pthId))
+            SOFT_ERROR(PI_TIMER_FAILED, "timer %d, cancel failed (%m)", id);
+
+         if (pthread_join(gpioTimer[id].pthId, NULL))
+            SOFT_ERROR(PI_TIMER_FAILED, "timer %d, join failed (%m)", id);
+
+         gpioTimer[id].running = 0;
+         gpioTimer[id].func    = f;
+      }
+   }
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetTimerFunc(unsigned id, unsigned ms, gpioTimerFunc_t f)
+{
+   DBG(DBG_USER, "id=%d ms=%d function=%08X", id, ms, (uint32_t)f);
+
+   CHECK_INITED;
+
+   if (id > PI_MAX_TIMER)
+      SOFT_ERROR(PI_BAD_TIMER, "bad timer id (%d)", id);
+
+   if ((ms < PI_MIN_MS) || (ms > PI_MAX_MS))
+      SOFT_ERROR(PI_BAD_MS, "timer %d, bad ms (%d)", id, ms);
+
+   intGpioSetTimerFunc(id, ms, f, 0, NULL);
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetTimerFuncEx(unsigned id, unsigned ms, gpioTimerFuncEx_t f,
+                       void * userdata)
+{
+   DBG(DBG_USER, "id=%d ms=%d function=%08X, userdata=%08X",
+      id, ms, (uint32_t)f, (uint32_t)userdata);
+
+   CHECK_INITED;
+
+   if (id > PI_MAX_TIMER)
+      SOFT_ERROR(PI_BAD_TIMER, "bad timer id (%d)", id);
+
+   if ((ms < PI_MIN_MS) || (ms > PI_MAX_MS))
+      SOFT_ERROR(PI_BAD_MS, "timer %d, bad ms (%d)", id, ms);
+
+   intGpioSetTimerFunc(id, ms, f, 1, userdata);
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f)
+{
+   DBG(DBG_USER, "signum=%d function=%08X", signum, (uint32_t)f);
+
+   CHECK_INITED;
+
+   if (signum > PI_MAX_SIGNUM)
+      SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
+
+   gpioSignal[signum].ex = 0;
+   gpioSignal[signum].userdata = NULL;
+
+   gpioSignal[signum].func = f;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSetSignalFuncEx(unsigned signum, gpioSignalFuncEx_t f,
+                        void * userdata)
+{
+   DBG(DBG_USER, "signum=%d function=%08X userdata=%08X",
+      signum, (uint32_t)f, (uint32_t)userdata);
+
+   CHECK_INITED;
+
+   if (signum > PI_MAX_SIGNUM)
+      SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
+
+   gpioSignal[signum].ex = 1;
+   gpioSignal[signum].userdata = userdata;
+
+   gpioSignal[signum].func = f;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t gpioRead_Bits_0_31(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return (*(gpioReg + GPLEV0));
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t gpioRead_Bits_32_53(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return (*(gpioReg + GPLEV1));
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite_Bits_0_31_Clear(uint32_t levels)
+{
+   DBG(DBG_USER, "levels=%08X", levels);
+
+   CHECK_INITED;
+
+   *(gpioReg + GPCLR0) = levels;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite_Bits_32_53_Clear(uint32_t levels)
+{
+   DBG(DBG_USER, "levels=%08X", levels);
+
+   CHECK_INITED;
+
+   *(gpioReg + GPCLR1) = levels;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite_Bits_0_31_Set(uint32_t levels)
+{
+   DBG(DBG_USER, "levels=%08X", levels);
+
+   CHECK_INITED;
+
+   *(gpioReg + GPSET0) = levels;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioWrite_Bits_32_53_Set(uint32_t levels)
+{
+   DBG(DBG_USER, "levels=%08X", levels);
+
+   CHECK_INITED;
+
+   *(gpioReg + GPSET1) = levels;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioTime(unsigned timetype, int * seconds, int * micros)
+{
+   struct timespec ts;
+
+   DBG(DBG_USER, "timetype=%d &seconds=%08X &micros=%08X",
+      timetype, (uint32_t)seconds, (uint32_t)micros);
+
+   CHECK_INITED;
+
+   if (timetype > PI_TIME_ABSOLUTE)
+      SOFT_ERROR(PI_BAD_TIMETYPE, "bad timetype (%d)", timetype);
+
+   if (timetype == PI_TIME_ABSOLUTE)
+   {
+      clock_gettime(CLOCK_REALTIME, &ts);
+      *seconds = ts.tv_sec;
+      *micros  = ts.tv_nsec/1000;
+   }
+   else
+   {
+      clock_gettime(CLOCK_REALTIME, &ts);
+
+      TIMER_SUB(&ts, &libStarted, &ts);
+
+      *seconds = ts.tv_sec;
+      *micros  = ts.tv_nsec/1000;
+   }
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioSleep(unsigned timetype, int seconds, int micros)
+{
+   struct timespec ts, rem;
+
+   DBG(DBG_USER, "timetype=%d seconds=%d micros=%d",
+      timetype, seconds, micros);
+
+   CHECK_INITED;
+
+   if (timetype > PI_TIME_ABSOLUTE)
+      SOFT_ERROR(PI_BAD_TIMETYPE, "bad timetype (%d)", timetype);
+
+   if (seconds < 0)
+      SOFT_ERROR(PI_BAD_SECONDS, "bad seconds (%d)", seconds);
+
+   if ((micros < 0) || (micros > 999999))
+      SOFT_ERROR(PI_BAD_MICROS, "bad micros (%d)", micros);
+
+   ts.tv_sec  = seconds;
+   ts.tv_nsec = micros * 1000;
+
+   if (timetype == PI_TIME_ABSOLUTE)
+   {
+      while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, &rem));
+   }
+   else
+   {
+      while (clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem))
+      {
+         /* copy remaining time to ts */
+         ts.tv_sec  = rem.tv_sec;
+         ts.tv_nsec = rem.tv_nsec;
+      }
+   }
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t gpioDelay(uint32_t micros)
+{
+   uint32_t start;
+
+   DBG(DBG_USER, "microseconds=%u", micros);
+
+   CHECK_INITED;
+
+   start = systReg[SYST_CLO];
+
+   if (micros < 100) while ((systReg[SYST_CLO] - start) <= micros) ;
+
+   else gpioSleep(PI_TIME_RELATIVE, (micros/MILLION), (micros%MILLION));
+
+   return (systReg[SYST_CLO] - start);
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t gpioTick(void)
+{
+   CHECK_INITED;
+
+   return systReg[SYST_CLO];
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+unsigned gpioHardwareRevision(void)
+{
+   DBG(DBG_USER, "");
+
+   CHECK_INITED;
+
+   return hardwareRevision;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgBufferSize(unsigned millis)
+{
+   DBG(DBG_USER, "millis=%d", millis);
+
+   CHECK_NOT_INITED;
+
+   if ((millis < PI_BUF_MILLIS_MIN) || (millis > PI_BUF_MILLIS_MAX))
+      SOFT_ERROR(PI_BAD_BUF_MILLIS, "bad millis (%d)", millis);
+
+   gpioCfg.bufferMilliseconds = millis;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgClock(unsigned micros, unsigned peripheral, unsigned source)
+{
+   DBG(DBG_USER, "micros=%d peripheral=%d source=%d",
+      micros, peripheral, source);
+
+   CHECK_NOT_INITED;
+
+   if ((micros < 1) || (micros > 10))
+      SOFT_ERROR(PI_BAD_CLK_MICROS, "bad micros (%d)", micros);
+
+   if (!clkCfg[micros].valid)
+      SOFT_ERROR(PI_BAD_CLK_MICROS, "bad micros (%d)", micros);
+
+   if (peripheral > PI_CLOCK_PCM)
+      SOFT_ERROR(PI_BAD_CLK_PERIPH, "bad peripheral (%d)", peripheral);
+
+   if (source > PI_CLOCK_PLLD)
+      SOFT_ERROR(PI_BAD_CLK_SOURCE, "bad clock (%d)", source);
+
+   gpioCfg.clockMicros = micros;
+   gpioCfg.clockPeriph = peripheral;
+   gpioCfg.clockSource = source;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgDMAchannel(unsigned channel)
+{
+   DBG(DBG_USER, "channel=%d", channel);
+
+   CHECK_NOT_INITED;
+
+   if ((channel < PI_MIN_DMA_CHANNEL) || (channel > PI_MAX_DMA_CHANNEL))
+      SOFT_ERROR(PI_BAD_CHANNEL, "bad channel (%d)", channel);
+
+   gpioCfg.DMAprimaryChannel = channel;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgDMAchannels(unsigned primaryChannel, unsigned secondaryChannel)
+{
+   DBG(DBG_USER, "primary channel=%d, secondary channel=%d",
+      primaryChannel, secondaryChannel);
+
+   CHECK_NOT_INITED;
+
+   if (primaryChannel > PI_MAX_PRIMARY_CHANNEL)
+      SOFT_ERROR(PI_BAD_PRIM_CHANNEL, "bad primary channel (%d)",
+         primaryChannel);
+
+   if (secondaryChannel > PI_MAX_SECONDARY_CHANNEL)
+      SOFT_ERROR(PI_BAD_SECO_CHANNEL, "bad secondary channel (%d)",
+         secondaryChannel);
+
+   gpioCfg.DMAprimaryChannel   = primaryChannel;
+   gpioCfg.DMAsecondaryChannel = secondaryChannel;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgInterfaces(unsigned ifFlags)
+{
+   DBG(DBG_USER, "ifFlags=%X", ifFlags);
+
+   CHECK_NOT_INITED;
+
+   if (ifFlags > 3)
+      SOFT_ERROR(PI_BAD_IF_FLAGS, "bad ifFlags (%X)", ifFlags);
+
+   gpioCfg.ifFlags = ifFlags;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgSocketPort(unsigned port)
+{
+   DBG(DBG_USER, "port=%d", port);
+
+   CHECK_NOT_INITED;
+
+   if ((port < PI_MIN_SOCKET_PORT) || (port > PI_MAX_SOCKET_PORT))
+      SOFT_ERROR(PI_BAD_SOCKET_PORT, "bad port (%d)", port);
+
+   gpioCfg.socketPort = port;
+
+   return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+int gpioCfgInternals(unsigned what, int value)
+{
+   int retVal = PI_BAD_CFG_INTERNAL;
+
+   DBG(DBG_USER, "what=%u, value=%d", what, value);
+
+   CHECK_NOT_INITED;
+
+   /* 
+   133084774
+   207081315 
+   293640712
+   394342930
+   472769257
+   430873902
+   635370313
+   684442696
+   786301093
+   816051706
+   858202631
+   997413601
+   */
+
+   switch(what)
+   {
+      case 562484977:
+
+         gpioCfg.showStats = value;
+
+         DBG(DBG_MIN_LEVEL, "showStats is %u", value);
+
+         retVal = 0;
+
+         break;
+
+      case 984762879:
+
+         if (value < DBG_MIN_LEVEL) value = DBG_MIN_LEVEL;
+
+         if (value > DBG_MAX_LEVEL) value = DBG_MAX_LEVEL;
+
+         gpioCfg.dbgLevel = value;
+
+         DBG(DBG_MIN_LEVEL, "Debug level is %u", value);
+
+         retVal = 0;
+
+         break;
+   }
+
+   return retVal;
+}
+
diff --git a/pigpio.h b/pigpio.h
new file mode 100644 (file)
index 0000000..aa95892
--- /dev/null
+++ b/pigpio.h
@@ -0,0 +1,1785 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 6
+*/
+
+#ifndef PIGPIO_H
+#define PIGPIO_H
+
+/************************************************************************** /
+/                                                                           /
+/ pigpio is a C library for the Raspberry Pi which allows                   /
+/ control of the gpios.                                                     /
+/                                                                           /
+/ Its main features are:                                                    /
+/                                                                           /
+/ 1) provision of PWM on any number of gpios 0-31 simultaneously.           /
+/ 2) provision of servo pulses on any number of gpios 0-31 simultaneously.  /
+/ 3) callbacks when any of gpios 0-31 change state.                         /
+/ 4) callbacks at timed intervals.                                          /
+/ 5) reading/writing all of the gpios in a bank (0-31, 32-53) as a          /
+/    single operation.                                                      /
+/ 6) individually setting gpio modes, reading and writing.                  /
+/ 7) notifications when any of gpios 0-31 change state.                     /
+/ 8) the construction of arbitrary waveforms to give precise timing of      /
+/    output gpio level changes.                                             /
+/                                                                           /
+/ NOTE:                                                                     /
+/                                                                           /
+/ ALL gpios are identified by their Broadcom number.                        /
+/                                                                           /
+*************************************************************************** /
+/                                                                           /
+/ The PWM and servo pulses are timed using the DMA and PWM peripherals.     /
+/                                                                           /
+/ This use was inspired by Richard Hirst's servoblaster kernel module.      /
+/ See https://github.com/richardghirst/PiBits                               /
+/ Tag rgh on the Raspberry Pi forums http://www.raspberrypi.org/phpBB3/     /
+/                                                                           /
+*************************************************************************** /
+/                                                                           /
+/ Usage:                                                                    /
+/                                                                           /
+/ copy libpigpio.a to /usr/local/lib                                        /
+/ copy pigpio.h    to /usr/local/include                                    /
+/                                                                           /
+/ #include <pigpio.h> in your source files                                  /
+/                                                                           /
+/ Assuming your source is in example.c use the following command to build   /
+/                                                                           /
+/ gcc -o example example.c -lpigpio -lpthread -lrt                          /
+/                                                                           /
+/ For examples see checklib.c, demolib.c, pigpio.c, pigpiod.c, pig2vcd.c,   /
+/ and pigs.c                                                                /
+/                                                                           /
+****************************************************************************/
+
+#include <stdint.h>
+
+#define PIGPIO_VERSION 6
+
+/*-------------------------------------------------------------------------*/
+
+/* 
+
+Function                   Usage
+--------                   -----
+
+gpioInitialise             Initialise library.
+gpioTerminate              Terminate library.
+
+gpioSetMode                Set a gpio mode.
+gpioGetMode                Get a gpio mode.
+
+gpioSetPullUpDown          Set/clear gpio pull up/down resistor.
+
+gpioRead                   Read a gpio.
+gpioWrite                  Write a gpio.
+
+gpioPWM                    Start/stop PWM pulses on a gpio.
+
+gpioSetPWMrange            Configure PWM range for a gpio.
+gpioGetPWMrange            Get configured PWM range for a gpio.
+gpioGetPWMrealRange        Get underlying PWM range for a gpio.
+
+gpioSetPWMfrequency        Configure PWM frequency for a gpio.
+gpioGetPWMfrequency        Get configured PWM frequency for a gpio.
+
+gpioServo                  Start/stop servo pulses on a gpio.
+
+gpioSetAlertFunc           Request a gpio change callback.
+gpioSetAlertFuncEx         Request a gpio change callback, extended.
+
+gpioNotifyOpen             Open a gpio(s) changed notification.
+gpioNotifyBegin            Begin a gpio(s) changed notification.
+gpioNotifyPause            Pause a gpio(s) changed notification.
+gpioNotifyClose            Close a gpio(s) changed notification.
+
+gpioWaveClear              Initialises a new waveform.
+gpioWaveAddGeneric         Adds a series of pulses to the waveform.
+gpioWaveAddSerial          Adds serial data to the waveform.
+
+gpioWaveTxStart            Transmits the waveform.
+gpioWaveTxBusy             Checks to see if the waveform has ended.
+gpioWaveTxStop             Aborts the current waveform.
+
+gpioWaveSerialReadStart    Reads serial data from a user gpio.
+gpioWaveSerialReadStop     Stops reading serial data from a user gpio.
+
+gpioWaveGetMicros          Length in microseconds of the current waveform.
+gpioWaveGetHighMicros      Length of longest waveform so far.
+gpioWaveGetMaxMicros       Absolute maximum allowed micros.
+
+gpioWaveGetPulses          Length in pulses of the current waveform.
+gpioWaveGetHighPulses      Length of longest waveform so far.
+gpioWaveGetMaxPulses       Absolute maximum allowed pulses.
+
+gpioWaveGetCbs             Length in cbs of the current waveform.
+gpioWaveGetHighCbs         Length of longest waveform so far.
+gpioWaveGetMaxCbs          Absolute maximum allowed cbs.
+
+gpioSetWatchdog            Set a watchdog on a gpio.
+
+gpioSetGetSamplesFunc      Requests a gpio samples callback.
+gpioSetGetSamplesFuncEx    Requests a gpio samples callback, extended.
+
+gpioSetTimerFunc           Request a regular timed callback.
+gpioSetTimerFuncEx         Request a regular timed callback, extended.
+
+gpioSetSignalFunc          Request a signal callback.
+gpioSetSignalFuncEx        Request a signal callback, extended.
+
+gpioRead_Bits_0_31         Read gpios in bank 1.
+gpioRead_Bits_32_53        Read gpios in bank 2.
+
+gpioWrite_Bits_0_31_Clear  Clear gpios in bank 1.
+gpioWrite_Bits_32_53_Clear Clear gpios in bank 2.
+
+gpioWrite_Bits_0_31_Set    Set gpios in bank 1.
+gpioWrite_Bits_32_53_Set   Set gpios in bank 2.
+
+gpioTime                   Get current time.
+
+gpioSleep                  Sleep for specified time.
+gpioDelay                  Delay for microseconds.
+
+gpioTick                   Get current tick (microseconds).
+
+gpioHardwareRevision       Get hardware version.
+
+gpioCfgBufferSize          Configure the gpio sample buffer size.
+gpioCfgClock               Configure the gpio sample rate.
+gpioCfgDMAchannel          Configure the DMA channel.
+gpioCfgInterfaces          Configure user interfaces.
+gpioCfgSocketPort          Configure socket port.
+
+*/
+
+/*-------------------------------------------------------------------------*/
+
+
+#define PI_INPFIFO "/dev/pigpio"
+#define PI_OUTFIFO "/dev/pigout"
+#define PI_ERRFIFO "/dev/pigerr"
+
+#define PI_ENVPORT "PIGPIO_PORT"
+#define PI_ENVADDR "PIGPIO_ADDR"
+
+#define PI_LOCKFILE "/var/run/pigpio.pid"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+   uint32_t cmd;
+   uint32_t p1;
+   uint32_t p2;
+   uint32_t res;
+} cmdCmd_t;
+
+typedef struct
+{
+   uint32_t tick;
+   uint32_t level;
+} gpioSample_t;
+
+typedef struct
+{
+   uint16_t seqno;
+   uint16_t flags;
+   uint32_t tick;
+   uint32_t level;
+} gpioReport_t;
+
+typedef struct
+{
+   uint32_t gpioOn;
+   uint32_t gpioOff;
+   uint32_t usDelay;
+} gpioPulse_t;
+
+typedef struct
+{
+   char   * buf;
+   uint32_t bufSize;
+   int      readPos;
+   int      writePos;
+} gpioRx_t;
+
+typedef void (*gpioAlertFunc_t)    (int      gpio,
+                                    int      level,
+                                    uint32_t tick);
+
+typedef void (*gpioAlertFuncEx_t)  (int      gpio,
+                                    int      level,
+                                    uint32_t tick,
+                                    void *   userdata);
+
+typedef void (*gpioTimerFunc_t)    (void);
+
+typedef void (*gpioTimerFuncEx_t)  (void * userdata);
+
+typedef void (*gpioSignalFunc_t)   (int    signum);
+
+typedef void (*gpioSignalFuncEx_t) (int    signum,
+                                    void * userdata);
+
+typedef void (*gpioGetSamplesFunc_t)   (const gpioSample_t * samples,
+                                        int                  numSamples);
+
+typedef void (*gpioGetSamplesFuncEx_t) (const gpioSample_t * samples,
+                                        int                  numSamples,
+                                        void *               userdata);
+
+/*
+   All the functions which return an int return < 0 on error.
+
+   If the library isn't initialised all but the gpioCfg* functions
+   will return error PI_NOT_INITIALISED.
+
+   If the library is initialised the gpioCfg* functions will
+   return error PI_INITIALISED.  
+*/
+
+
+/*-------------------------------------------------------------------------*/
+int gpioInitialise(void);
+/*-------------------------------------------------------------------------*/
+/* Initialises the library.
+
+   Call before using the other library functions.
+
+   Returns the pigpio version number if OK, otherwise PI_INIT_FAILED.
+
+   NOTES:
+
+   The only exception is the optional gpioCfg* functions, see later.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+void gpioTerminate(void);
+/*-------------------------------------------------------------------------*/
+/* Terminates the library.
+
+   Returns nothing.
+
+   Call before program exit.
+
+   NOTES:
+
+   This function resets the DMA and PWM peripherals, releases memory, and
+   terminates any running threads.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetMode(unsigned gpio,
+                unsigned mode);
+/*-------------------------------------------------------------------------*/
+/* Sets the gpio mode, typically input or output.
+
+   Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_MODE.
+
+   Arduino style: pinMode.
+
+   EXAMPLE:
+   ...
+   gpioSetMode(17, PI_INPUT);  // set gpio17 as input
+   gpioSetMode(18, PI_OUTPUT); // set gpio18 as output
+   gpioSetMode(22,PI_ALT0);    // set gpio22 to alternative mode 0
+   ... 
+*/
+
+/* gpio: 0-53 */
+
+#define PI_MIN_GPIO       0
+#define PI_MAX_GPIO      53
+
+/* mode: 0-7 */
+
+#define PI_INPUT  0
+#define PI_OUTPUT 1
+#define PI_ALT0   4
+#define PI_ALT1   5
+#define PI_ALT2   6
+#define PI_ALT3   7
+#define PI_ALT4   3
+#define PI_ALT5   2
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioGetMode(unsigned gpio);
+/*-------------------------------------------------------------------------*/
+/* Gets the gpio mode.
+
+   Returns the gpio mode if OK, otherwise PI_BAD_GPIO.
+
+   EXAMPLE:
+   ...
+   if (gpioGetMode(17) != PI_ALT0)
+   {
+      gpioSetMode(17, PI_ALT0);  // set gpio17 to ALT0  
+   }
+   ...
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetPullUpDown(unsigned gpio,
+                      unsigned pud);
+/*-------------------------------------------------------------------------*/
+/* Sets or clears resistor pull ups or downs on the gpio.
+
+   Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_PUD.
+
+   EXAMPLE:
+   ...
+   gpioSetPullUpDown(17, PI_PUD_UP);   // sets a pull-up on gpio17
+   gpioSetPullUpDown(18, PI_PUD_DOWN); // sets a pull-down on gpio18
+   gpioSetPullUpDown(23, PI_PUD_OFF);  // clear pull-ups/downs on gpio23
+   ...
+*/
+
+/* pud: 0-2 */
+
+#define        PI_PUD_OFF  0
+#define        PI_PUD_DOWN 1
+#define        PI_PUD_UP   2
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioRead (unsigned gpio);
+/*-------------------------------------------------------------------------*/
+/* Reads the gpio level, on or off.
+
+   Returns the gpio level if OK, otherwise PI_BAD_GPIO.
+
+   EXAMPLE:
+   ...
+   printf("gpio24 is level %d\n", gpioRead(24));
+   ...
+
+   NOTES:
+
+   Arduino style: digitalRead.
+*/
+
+/* level: 0-1 */
+
+#define        PI_OFF   0
+#define        PI_ON    1
+
+#define        PI_CLEAR 0
+#define        PI_SET   1
+
+#define        PI_LOW   0
+#define        PI_HIGH  1
+
+/* level: only reported for gpio timeout, see gpioSetWatchdogTimeout */
+
+#define PI_TIMEOUT 2
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite(unsigned gpio,
+              unsigned level);
+/*-------------------------------------------------------------------------*/
+/* Sets the gpio level, on or off.
+
+   Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_LEVEL.
+
+   EXAMPLE:
+   ...
+   gpioWrite(24, 1); // sets gpio24 high
+   ...
+
+   NOTES:
+
+   If PWM or servo pulses are active on the gpio they are switched off.
+
+   Arduino style: digitalWrite
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioPWM(unsigned user_gpio,
+            unsigned dutycycle);
+/*-------------------------------------------------------------------------*/
+/* Starts PWM on the gpio, dutycycle between 0 (off) and range (fully on).
+   Range defaults to 255.
+
+   Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_DUTYCYCLE.
+
+   EXAMPLE:
+   ...
+   gpioPWM(17, 255); // sets gpio17 full on
+   gpioPWM(18, 128); // sets gpio18 half on
+   gpioPWM(23, 0);   // sets gpio23 full off
+   ...
+
+   NOTES:
+
+   Arduino style: analogWrite
+
+   This and the servo functionality use the DMA and PWM or PCM peripherals
+   to control and schedule the pulse lengths and duty cycles.
+
+   The gpioSetPWMrange funtion can change the default range of 255.
+*/
+
+/* user_gpio: 0-31 */
+
+#define PI_MAX_USER_GPIO 31
+
+/* dutycycle: 0-range */
+
+#define PI_DEFAULT_DUTYCYCLE_RANGE   255
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetPWMrange(unsigned user_gpio,
+                    unsigned range);
+/*-------------------------------------------------------------------------*/
+/* Selects the dutycycle range to be used for the gpio.  Subsequent calls
+   to gpioPWM will use a dutycycle between 0 (off) and range (fully on).
+
+   Returns the real range for the given gpio's frequency if OK,
+   otherwise PI_BAD_USER_GPIO or PI_BAD_DUTY_RANGE.
+
+   EXAMPLE:
+   ...
+   gpioSetPWMrange(24, 2000); // now 2000 is fully on, 1000 is half on etc.
+   ...
+
+   NOTES:
+
+   If PWM is currently active on the gpio its dutycycle will be scaled
+   to reflect the new range.
+
+   The real range, the number of steps between fully off and fully
+   on for each frequency, is given in the following table.
+
+     25,   50,  100,  125,  200,  250,  400,   500,   625,
+    800, 1000, 1250, 2000, 2500, 4000, 5000, 10000, 20000
+
+    The real value set by gpioPWM is
+
+       (dutycycle * real range) / range.
+*/
+
+/* range: 25-40000 */
+
+#define PI_MIN_DUTYCYCLE_RANGE        25
+#define PI_MAX_DUTYCYCLE_RANGE     40000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioGetPWMrange(unsigned user_gpio);
+/*-------------------------------------------------------------------------*/
+/* Returns the dutycycle range used for the gpio if OK, otherwise
+   PI_BAD_USER_GPIO.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioGetPWMrealRange(unsigned user_gpio);
+/*-------------------------------------------------------------------------*/
+/* Returns the real range used for the gpio if OK, otherwise
+   PI_BAD_USER_GPIO.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetPWMfrequency(unsigned user_gpio,
+                        unsigned frequency);
+/*-------------------------------------------------------------------------*/
+/* Sets the frequency in hertz to be used for the gpio.
+
+   Returns the numerically closest frequency if OK, otherwise
+   PI_BAD_USER_GPIO.
+
+   The selectable frequencies depend upon the sample rate which
+   may be 1, 2, 4, 5, 8, or 10 microseconds (default 5).
+
+   Each gpio can be independently set to one of 18 different PWM
+   frequencies.
+
+   If PWM is currently active on the gpio it will be
+   switched off and then back on at the new frequency.
+
+   NOTES:
+
+   The frequencies for each sample rate are:
+
+                             Hertz
+
+       1: 40000 20000 10000 8000 5000 4000 2500 2000 1600
+           1250  1000   800  500  400  250  200  100   50 
+
+       2: 20000 10000  5000 4000 2500 2000 1250 1000  800
+            625   500   400  250  200  125  100   50   25 
+
+       4: 10000  5000  2500 2000 1250 1000  625  500  400
+            313   250   200  125  100   63   50   25   13 
+sample
+ rate
+ (us)  5:  8000  4000  2000 1600 1000  800  500  400  320
+            250   200   160  100   80   50   40   20   10 
+
+       8:  5000  2500  1250 1000  625  500  313  250  200
+            156   125   100   63   50   31   25   13    6 
+
+      10:  4000  2000  1000  800  500  400  250  200  160
+            125   100    80   50   40   25   20   10    5
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioGetPWMfrequency(unsigned user_gpio);
+/*-------------------------------------------------------------------------*/
+/* Returns the frequency (in hertz) used for the gpio if OK, otherwise
+   PI_BAD_USER_GPIO.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioServo(unsigned user_gpio,
+              unsigned pulsewidth);
+/*-------------------------------------------------------------------------*/
+/* Starts servo pulses on the gpio, 0 (off), 500 (most anti-clockwise) to
+   2500 (most clockwise).
+
+   Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_PULSEWIDTH.
+
+   NOTES:
+
+   The range supported by servos varies and should probably be determined
+   by experiment.  A value of 1500 should always be safe and represents
+   the mid-point of rotation.  You can DAMAGE a servo if you command it
+   to move beyond its limits.
+
+   EXAMPLE:
+
+   ...
+   gpioServo(17, 1500);
+   ...
+
+   This example causes an on pulse of 1500 microseconds duration to be
+   transmitted on gpio 17 at a rate of 50 times per second.
+
+   This will command a servo connected to gpio 17 to rotate to
+   its mid-point.
+
+   OTHER UPDATE RATES:
+
+   This function updates servos at 50Hz.  If you wish to use a different
+   update frequency you will have to use the PWM functions.
+
+   PWM Hz    50   100  200  400  500
+   1E6/Hz 20000 10000 5000 2500 2000
+
+   Firstly set the desired PWM frequency using gpioSetPWMfrequency.
+
+   Then set the PWM range using gpioSetPWMrange to 1E6/frequency.
+   Doing this allows you to use units of microseconds when setting
+   the servo pulse width.
+
+   E.g. If you want to update a servo connected to gpio 25 at 400Hz
+
+   gpioSetPWMfrequency(25, 400);
+   gpioSetPWMrange(25, 2500);
+
+   Thereafter use the PWM command to move the servo,
+   e.g. gpioPWM(25, 1500) will set a 1500 us pulse. 
+
+*/
+
+/* pulsewidth: 0, 500-2500 */
+
+#define PI_SERVO_OFF 0
+#define PI_MIN_SERVO_PULSEWIDTH 500
+#define PI_MAX_SERVO_PULSEWIDTH 2500
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetAlertFunc(unsigned        user_gpio,
+                     gpioAlertFunc_t f);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) when the specified
+   gpio changes state.
+
+   Returns 0 if OK, otherwise PI_BAD_USER_GPIO.
+
+   One function may be registered per gpio.
+
+   The function is passed the gpio, the new level, and the tick.
+
+   The alert may be cancelled by passing NULL as the function.
+
+   EXAMPLE:
+   ...
+   void aFunction(int gpio, int level, uint32_t tick)
+   {
+      printf("gpio %d became %d at %d\n", gpio, level, tick);
+   }
+   ...
+   gpioSetAlertFunc(4, aFunction);
+   ...
+
+   This example causes aFunction to be called whenever
+   gpio 4 changes state.
+
+   NOTES:
+
+   The gpios are sampled at a rate set when the library is started.
+
+   If a value isn't specifically set the default of 5 us is used.
+
+   The number of samples per second is given in the following table.
+
+                 samples
+                 per sec
+
+            1  1,000,000
+            2    500,000
+   sample   4    250,000
+   rate     5    200,000
+   (us)     8    125,000
+           10    100,000
+
+   Level changes of length less than the sample rate may be missed.
+
+   The thread which calls the alert functions is triggered nominally
+   1000 times per second.  The active alert functions will be called
+   once per level change since the last time the thread was activated.
+   i.e. The active alert functions will get all level changes but there
+   will be a latency.
+
+   The tick value is the time stamp of the sample in microseconds, see
+   gpioTick for more details.
+*/
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetAlertFuncEx(unsigned          user_gpio,
+                       gpioAlertFuncEx_t f,
+                       void *            userdata);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) when the specified
+   gpio changes state.
+
+   Returns 0 if OK, otherwise PI_BAD_USER_GPIO.
+
+   One function may be registered per gpio.
+
+   The function is passed the gpio, the new level, the tick, and
+   the userdata pointer.
+
+   Only one of gpioSetAlertFunc or gpioSetAlertFuncEx can be
+   registered per gpio.
+
+   See gpioSetAlertFunc for further details.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioNotifyOpen(void);
+/*-------------------------------------------------------------------------*/
+/* This function requests a free notification handle.
+
+   Returns a handle greater than or equal to zero if OK,
+   otherwise PI_NO_HANDLE.
+
+   A notification is a method for being notified of gpio state changes
+   via a pipe or socket.
+
+   Pipe notifications for handle x will be available at the pipe
+   named /dev/pigpiox (where x is the handle number).  E.g. if the
+   function returns 15 then the notifications must be read
+   from /dev/pigpio15.
+
+   Socket notifications are returned to the socket which requested the
+   handle.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioNotifyBegin(unsigned handle,
+                    uint32_t bits);
+/*-------------------------------------------------------------------------*/
+/* This function starts notifications on a previously opened handle.
+
+   Returns 0 if OK, otherwise PI_BAD_HANDLE.
+
+   The notification sends state changes for each gpio whose corresponding
+   bit in bits is set.
+
+   EXAMPLE:
+
+   gpioNotifyBegin(0, 1234) will start notifications for gpios 1, 4, 6,
+   7, 10 (1234 = 0x04D2 = 0b0000010011010010).
+
+   NOTES:
+
+   Each notification occupies 12 bytes in the fifo and has the
+   following structure.
+
+   typedef struct
+   {
+      uint16_t seqno;
+      uint16_t flags;
+      uint32_t tick;
+      uint32_t level;
+   } gpioReport_t;
+
+   seqno starts at 0 each time the handle is opened and then increments
+   by one for each report.
+
+   flags, if bit 5 is set then bits 0-4 of the flags indicate a gpio
+   which has had a watchdog timeout.
+
+   tick is the number of microseconds since system boot.
+
+   level indicates the level of each gpio.
+*/
+
+#define PI_NTFY_FLAGS_WDOG     (1 <<5)
+#define PI_NTFY_FLAGS_BIT(x) (((x)<<0)&31)
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioNotifyPause(unsigned handle);
+/*-------------------------------------------------------------------------*/
+/* This function pauses notifications on a previously opened handle.
+
+   Returns 0 if OK, otherwise PI_BAD_HANDLE.
+
+   Notifications for the handle are suspended until gpioNotifyBegin
+   is called again.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioNotifyClose(unsigned handle);
+/*-------------------------------------------------------------------------*/
+/* This function stops notifications on a previously opened handle
+   and releases the handle for reuse.
+
+   Returns 0 if OK, otherwise PI_BAD_HANDLE.
+*/
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveClear(void);
+/*-------------------------------------------------------------------------*/
+/* This function initialises a new waveform.
+
+   Returns 0 if OK.
+
+   A waveform comprises one of more pulses.  Each pulse consists of a
+   gpioPulse_t structure.
+
+   typedef struct
+   {
+      uint32_t gpioOn;
+      uint32_t gpioOff;
+      uint32_t usDelay;
+   } gpioPulse_t;
+
+   The fields specify
+
+   1) the gpios to be switched on at the start of the pulse.
+   2) the gpios to be switched off at the end of the pulse.
+   3) the delay in microseconds before the next pulse.
+
+   Any or all the fields can be zero.  It doesn't make any sense to
+   set all the fields to zero (the pulse will be ignored).
+
+   When a waveform is started each pulse is executed in order with the
+   specified delay between the pulse and the next.
+*/
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveAddGeneric(unsigned numPulses, gpioPulse_t * pulses);
+/*-------------------------------------------------------------------------*/
+/* This function adds a number of pulses to the current waveform.
+
+   Returns the new total number of pulses in the current waveform if OK,
+   otherwise PI_TOO_MANY_PULSES.
+
+   NOTES:
+
+   The   pulses are interleaved in time order within the existing waveform
+   (if any).
+
+   Merging allows the waveform to be built in parts, that is the settings
+   for gpio#1 can be added, and then gpio#2 etc.
+
+   If the added waveform is intended to start after or within the existing
+   waveform then the first pulse should consist of a delay.
+
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveAddSerial(unsigned user_gpio,
+                      unsigned baud,
+                      unsigned numChar,
+                      char *   str);
+/*-------------------------------------------------------------------------*/
+/* This function adds a waveform representing serial data to the
+   existing waveform (if any).
+
+   Returns the new total number of pulses in the current waveform if OK,
+   otherwise PI_BAD_USER_GPIO, PI_BAD_WAVE_BAUD, PI_TOO_MANY_CHARS, or
+   PI_TOO_MANY_PULSES.
+
+   NOTES:
+
+   The serial data is formatted as one start bit, eight data bits, and one
+   stop bit.
+
+   It is legal to add serial data streams with different baud rates to
+   the same waveform.
+*/
+
+#define PI_WAVE_MIN_BAUD      100
+#define PI_WAVE_MAX_BAUD      250000
+#define PI_WAVE_MAX_CHARS     256
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveTxStart(unsigned mode);
+/*-------------------------------------------------------------------------*/
+/* This function transmits the current waveform.  The mode determines
+   whether the waveform is sent once or cycles endlessly.
+
+   Returns 0 if OK, otherwise PI_BAD_WAVE_MODE.
+*/
+
+#define PI_WAVE_MODE_ONE_SHOT 0
+#define PI_WAVE_MODE_REPEAT   1
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveTxBusy(void);
+/*-------------------------------------------------------------------------*/
+/* This function checks to see if a waveform is currently being
+   transmitted.
+
+   Returns 1 if a waveform is currently being transmitted, otherwise 0.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveTxStop(void);
+/*-------------------------------------------------------------------------*/
+/* This function aborts the transmission of the current waveform.
+
+   Returns 0 if OK.
+
+   NOTES:
+
+   This function is intended to stop a waveform started with the repeat mode.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveSerialReadStart(unsigned   user_gpio,
+                            unsigned   baud,
+                            gpioRx_t * rxp);
+/*-------------------------------------------------------------------------*/
+/* This function starts the reception of serial data with the
+   specified baud rate on a gpio.
+
+   Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_WAVE_BAUD,
+   PI_BAD_SERIAL_STRUC, or PI_BAD_SERIAL_BUF.
+
+   NOTES:
+
+   typedef struct
+   {
+      char   * buf;
+      uint32_t bufSize;
+      int      readPos;
+      int      writePos;
+   } gpioRx_t;
+
+   The serial data is returned in a cyclic buffer which MUST be allocated
+   by the caller.  The caller specifies the location and size of the
+   buffer in buf and bufSize.
+
+   It is the caller's responsibility to read data from the cyclic buffer
+   in a timely fashion.  Data is available when readPos is not equal to
+   writePos.
+
+   EXAMPLE:
+
+   #define BUFSIZE 1000
+
+   char buf[BUFSIZE];
+   int bytes, wpos;
+   FILE * outFile;
+
+   gpioRx_t rx;
+
+   ...
+
+   rx.buf = buf;
+   rx.bufSize = sizeof(buf);
+
+   if (gpioWaveSerialReadStart(4, 38400, &rx) == 0)
+   {
+      ...
+
+      while (rx.readPos != rx.writePos)
+      {
+         wpos = rx.writePos;
+
+         if (wpos > rx.readPos) bytes = wpos - rx.readPos;
+         else                   bytes = rx.bufSize - rx.readPos;
+
+         fwrite(rx.buf+rx.readPos, 1, bytes, outFile);
+
+         rx.readPos += bytes;
+
+         if (rx.readPos >= rx.bufSize) rx.readPos = 0;
+      }
+      ...
+   }
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveSerialReadStop(unsigned user_gpio);
+/*-------------------------------------------------------------------------*/
+/* This function stops reading serial data from a gpio.
+
+   Returns 0 if OK, otherwise PI_BAD_USER_GPIO, or PI_NOT_SERIAL_GPIO.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetMicros(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in microseconds of the current
+   waveform.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetHighMicros(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in microseconds of the longest waveform
+   created since gpioInitialise was called.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetMaxMicros(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the maximum possible size of a waveform in 
+   microseconds.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetPulses(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in pulses of the current waveform.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetHighPulses(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in pulses of the longest waveform
+   created since gpioInitialise was called.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetMaxPulses(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the maximum possible size of a waveform in pulses.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetCbs(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in DMA control blocks of the current
+   waveform.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetHighCbs(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the length in DMA control blocks of the longest
+   waveform created since gpioInitialise was called.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWaveGetMaxCbs(void);
+/*-------------------------------------------------------------------------*/
+/* This function returns the maximum possible size of a waveform in DMA
+   control blocks.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetWatchdog(unsigned user_gpio,
+                    unsigned timeout);
+/*-------------------------------------------------------------------------*/
+/* Sets a watchdog for a gpio.
+
+   Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_WDOG_TIMEOUT.
+   
+   The watchdog is nominally in milliseconds.
+
+   One watchdog may be registered per gpio.
+
+   The watchdog may be cancelled by setting timeout to 0.
+
+   If no level change has been detected for the gpio for timeout
+   milliseconds:-
+
+   1) any registered alert function for the gpio is called with
+      the level set to PI_TIMEOUT.
+   2) any notification for the gpio has a report written to the
+      fifo with the flags set to indicate a watchdog timeout.
+
+   EXAMPLE:
+
+   void aFunction(int gpio, int level, uint32_t tick)
+   {
+      printf("gpio %d became %d at %d\n", gpio, level, tick);
+   }
+   ...
+   gpioSetAlertFunc(4, aFunction);
+   gpioSetWatchdogTimeout(4, 5);
+   ...
+
+   This example causes aFunction to be called whenever
+   gpio 4 changes state or approximately every 5 ms.
+*/
+
+/* timeout: 0-60000 */
+
+#define PI_MIN_WDOG_TIMEOUT 0
+#define PI_MAX_WDOG_TIMEOUT 60000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetGetSamplesFunc(gpioGetSamplesFunc_t f,
+                          uint32_t             bits);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) every millisecond
+   with the latest gpio samples.
+
+   Returns 0 if OK.
+
+   The function is passed a pointer to the samples and the number
+   of samples.
+
+   Only one function can be registered.
+
+   The callback may be cancelled by passing NULL as the function.
+
+   NOTES:
+
+   The samples returned will be the union of bits, plus any active alerts,
+   plus any active notifications.
+
+   e.g.  if there are alerts for gpios 7, 8, and 9, notifications for gpios
+   8, 10, 23, 24, and bits is (1<<23)|(1<<17) then samples for gpios
+   7, 8, 9, 10, 17, 23, and 24 will be reported.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetGetSamplesFuncEx(gpioGetSamplesFuncEx_t f,
+                            uint32_t               bits,
+                            void *                 userdata);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) every millisecond
+   with the latest gpio samples.
+
+   Returns 0 if OK.
+
+   The function is passed a pointer to the samples, the number
+   of samples, and the userdata pointer.
+
+   Only one of gpioGetSamplesFunc or gpioGetSamplesFuncEx can be
+   registered.
+
+   See gpioSetGetSamplesFunc for further details.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetTimerFunc(unsigned        timer,
+                     unsigned        ms,
+                     gpioTimerFunc_t f);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) every ms milliseconds.
+
+   Returns 0 if OK, otherwise PI_BAD_TIMER, PI_BAD_MS, or PI_TIMER_FAILED.
+
+   10 timers are supported numbered 0 to 9.
+
+   One function may be registered per timer.
+
+   The timer may be cancelled by passing NULL as the function.
+
+   EXAMPLE:
+
+   ...
+   void bFunction(void)
+   {
+      printf("two seconds have elapsed\n");
+   }
+   ...
+   gpioSetTimerFunc(0, 2000, bFunction);
+   ...
+
+   This example causes bFunction to be called every 2000 milliseconds.
+*/
+
+/* timer: 0-9 */
+
+#define PI_MIN_TIMER 0
+#define PI_MAX_TIMER 9
+
+/* ms: 10-60000 */
+
+#define PI_MIN_MS 10
+#define PI_MAX_MS 60000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetTimerFuncEx(unsigned          timer,
+                       unsigned          ms,
+                       gpioTimerFuncEx_t f,
+                       void *            userdata);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) every ms milliseconds.
+
+   Returns 0 if OK, otherwise PI_BAD_TIMER, PI_BAD_MS, or PI_TIMER_FAILED.
+
+   The function is passed the userdata pointer.
+
+   Only one of gpioSetTimerFunc or gpioSetTimerFuncEx can be
+   registered per timer.
+
+   See gpioSetTimerFunc for further details.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetSignalFunc(unsigned         signum,
+                      gpioSignalFunc_t f);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) when a signal occurs.
+
+   Returns 0 if OK, otherwise PI_BAD_SIGNUM.
+
+   The function is passed the signal number.
+
+   One function may be registered per signal.
+
+   The callback may be cancelled by passing NULL.
+
+   NOTES:
+
+   By default all signals are treated as fatal and cause the library
+   to call gpioTerminate and then exit.
+*/
+
+/* signum: 0-63 */
+
+#define PI_MIN_SIGNUM 0
+#define PI_MAX_SIGNUM 63
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSetSignalFuncEx(unsigned           signum,
+                        gpioSignalFuncEx_t f,
+                        void *             userdata);
+/*-------------------------------------------------------------------------*/
+/* Registers a function to be called (a callback) when a signal occurs.
+
+   Returns 0 if OK, otherwise PI_BAD_SIGNUM.
+
+   The function is passed the signal number and the userdata pointer.
+
+   Only one of gpioSetSignalFunc or gpioSetSignalFuncEx can be
+   registered per signal.
+
+   See gpioSetSignalFunc for further details.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+uint32_t gpioRead_Bits_0_31(void);
+/*-------------------------------------------------------------------------*/
+/* Returns the current level of gpios 0-31.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+uint32_t gpioRead_Bits_32_53(void);
+/*-------------------------------------------------------------------------*/
+/* Returns the current level of gpios 32-53.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite_Bits_0_31_Clear(uint32_t levels);
+/*-------------------------------------------------------------------------*/
+/* Clears gpios 0-31 if the corresponding bit in levels is set.
+
+   Returns 0 if OK.
+
+   EXAMPLE:
+
+   To clear (set to 0) gpios 4, 7, and 15.
+
+   ...
+   gpioWrite_Bits_0_31_Clear( (1<<4) | (1<<7) | (1<<15) );
+   ...
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite_Bits_32_53_Clear(uint32_t levels);
+/*-------------------------------------------------------------------------*/
+/* Clears gpios 32-53 if the corresponding bit (0-21) in levels is set.
+
+   Returns 0 if OK.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite_Bits_0_31_Set(uint32_t levels);
+/*-------------------------------------------------------------------------*/
+/* Sets gpios 0-31 if the corresponding bit in levels is set.
+
+   Returns 0 if OK.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioWrite_Bits_32_53_Set(uint32_t levels);
+/*-------------------------------------------------------------------------*/
+/* Sets gpios 32-53 if the corresponding bit (0-21) in levels is set.
+
+   Returns 0 if OK.
+
+   EXAMPLE:
+
+   To set (set to 1) gpios 32, 40, and 53.
+
+   ...
+   gpioWrite_Bits_32_53_Set( (1<<(32-32)) | (1<<(40-32)) | (1<<(53-32)) );
+   ...
+*/
+
+/*-------------------------------------------------------------------------*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioTime(unsigned timetype,
+             int *    seconds,
+             int *    micros);
+/*-------------------------------------------------------------------------*/
+/* Updates the seconds and micros variables with the current time.
+
+   Returns 0 if OK, otherwise PI_BAD_TIMETYPE.
+
+   If timetype is PI_TIME_ABSOLUTE updates seconds and micros with the
+   number of seconds and microseconds since the epoch (1st January 1970).
+
+   If timetype is PI_TIME_RELATIVE updates seconds and micros with the
+   number of seconds and microseconds since the library was initialised.
+
+   EXAMPLE:
+
+   ...
+   int secs, mics;
+   ...
+   gpioTime(PI_TIME_RELATIVE, &secs, &mics);
+   printf("library started %d.%03d seconds ago\n", secs, mics/1000);
+   ...
+   prints the number of seconds since the library was started.
+*/
+
+/* timetype: 0-1 */
+
+#define PI_TIME_RELATIVE 0
+#define PI_TIME_ABSOLUTE 1
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioSleep(unsigned timetype,
+              int      seconds,
+              int      micros);
+/*-------------------------------------------------------------------------*/
+/* Sleeps for the number of seconds and microseconds specified by seconds
+   and micros.
+
+   Returns 0 if OK, otherwise PI_BAD_TIMETYPE, PI_BAD_SECONDS,
+   or PI_BAD_MICROS.
+  
+   If timetype is PI_TIME_ABSOLUTE the sleep ends when the number of seconds
+   and microseconds since the epoch (1st January 1970) has elapsed.  System
+   clock changes are taken into account.
+
+   If timetype is PI_TIME_RELATIVE the sleep is for the specified number
+   of seconds and microseconds.  System clock changes do not effect the
+   sleep length.
+
+   NOTES:
+
+   For short delays (say, 250 microseonds or less) use gpioDelayMicroseconds.
+
+   EXAMPLE:
+
+   ...
+   gpioSleep(PI_TIME_RELATIVE, 2, 500000); // sleep for 2.5 seconds
+   ...
+   gpioSleep(PI_TIME_RELATIVE, 0, 100000); // sleep for 1/10th of a second
+   ...
+   gpioSleep(PI_TIME_RELATIVE, 60, 0);     // sleep for one minute
+   ...
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+uint32_t gpioDelay(uint32_t micros);
+/*-------------------------------------------------------------------------*/
+/* Delays for at least the number of microseconds specified by micros.
+
+   Returns the actual length of the delay in microseconds.  
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+uint32_t gpioTick(void);
+/*-------------------------------------------------------------------------*/
+/* Returns the current system tick.
+
+   Tick is the number of microseconds since system boot.
+
+   NOTES:
+
+   As tick is an unsigned 32 bit quantity it wraps around after
+   2^32 microseconds, which is approximately 1 hour 12 minutes.
+
+   You don't need to worry about the wrap around as long as you
+   take a tick (uint32_t) from another tick, i.e. the following
+   code will always provide the correct difference.
+
+   EXAMPLE:
+
+   uint32_t startTick, endTick;
+   int diffTick;
+   ...
+   startTick = gpioTick();
+   ...
+   // do some processing
+   ...
+   endTick = gpioTick();
+
+   diffTick = endTick - startTick;
+
+   printf("some processing took %d microseconds\n", diffTick);
+   ...
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+unsigned gpioHardwareRevision(void);
+/*-------------------------------------------------------------------------*/
+/* Returns the hardware revision.
+
+   If the hardware revision can not be found or is not a valid hexadecimal
+   number the function returns 0.
+
+   NOTES:
+
+   The hardware revision is the last 4 characters on the Revision line of
+   /proc/cpuinfo.
+
+   The revision number can be used to determine the assignment of gpios
+   to pins.
+
+   There are at least two types of board.
+
+   Type 1 has gpio 0 on P1-3, gpio 1 on P1-5, and gpio 21 on P1-13.
+
+   Type 2 has gpio 2 on P1-3, gpio 3 on P1-5, gpio 27 on P1-13, and
+   gpios 28-31 on P5.
+
+   Type 1 boards have hardware revision numbers of 2 and 3.
+
+   Type 2 boards have hardware revision numbers of 4, 5, 6, and 15.
+
+   EXAMPLES:
+
+   for "Revision       : 0002" the function returns 2.
+   for "Revision       : 000f" the function returns 15.
+   for "Revision       : 000g" the function returns 0.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgBufferSize(unsigned millis);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to buffer millis milliseconds of gpio samples.
+
+   The default setting is 120 milliseconds.
+
+   NOTES:
+
+   The intention is to allow for bursts of data and protection against
+   other processes hogging cpu time.
+
+   I haven't seen a process locked out for more than 100 milliseconds.
+
+   Making the buffer bigger uses a LOT of memory at the more frequent
+   sampling rates as shown in the following table in MBs.
+
+                     buffer milliseconds
+               120 250 500 1sec 2sec 4sec 8sec
+
+         1      16  31  55  107  ---  ---  ---
+         2      10  18  31   55  107  ---  ---
+sample   4       8  12  18   31   55  107  ---
+ rate    5       8  10  14   24   45   87  ---
+ (us)    8       6   8  12   18   31   55  107
+        10       6   8  10   14   24   45   87
+*/
+
+/* millis */
+
+#define PI_BUF_MILLIS_MIN 100
+#define PI_BUF_MILLIS_MAX 10000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgClock(unsigned micros,
+                 unsigned peripheral,
+                 unsigned source);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to use a sample rate of micros microseconds,
+   permitted values are 1, 2, 4, 5, 8 and 10.
+
+   The timings are provided by the specified peripheral (PWM or PCM)
+   using the frequency source (OSC or PLLD).
+
+   The default setting is 5 microseconds using the PCM peripheral
+   with the PLLD source.
+
+   NOTES:
+
+   The approximate CPU percentage used for each sample rate is:
+
+   sample  cpu
+    rate    %
+
+     1     25
+     2     16
+     4     11
+     5     10
+     8     15
+    10     14
+
+    A sample rate of 5 microseconds seeems to be the sweet spot.
+
+    These readings were done by checking the resources used by
+    the demolib program (which is reasonably busy).
+*/
+
+/* micros: 1, 2, 4, 5, 8, or 10 */
+
+/* peripheral: 0-1 */
+
+#define PI_CLOCK_PWM 0
+#define PI_CLOCK_PCM 1
+
+/* source: 0-1 */
+
+#define PI_CLOCK_OSC  0
+#define PI_CLOCK_PLLD 1
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgDMAchannel(unsigned channel);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to use the specified DMA channel.
+
+   The default setting is to use channel 14.
+*/
+
+/* channel: 0-14 */
+
+#define PI_MIN_DMA_CHANNEL 0
+#define PI_MAX_DMA_CHANNEL 14
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgDMAchannels(unsigned primaryChannel,
+                       unsigned secondaryChannel);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to use the specified DMA channels.
+
+   The default setting is to use channel 14 for the primary channel and
+   channel 6 for the secondary channel.
+*/
+
+#define PI_MAX_PRIMARY_CHANNEL   14
+#define PI_MAX_SECONDARY_CHANNEL  6
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgSocketPort(unsigned port);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio to use the specified socket port.
+
+   The default setting is to use port 8888.
+*/
+
+/* port: 1024-9999 */
+
+#define PI_MIN_SOCKET_PORT 1024
+#define PI_MAX_SOCKET_PORT 32000
+
+
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgInterfaces(unsigned ifFlags);
+/*-------------------------------------------------------------------------*/
+/* Configures pigpio support of the fifo and socket interfaces.
+
+   The default setting is that both interfaces are enabled.
+*/
+
+/* ifFlags: */
+
+#define PI_DISABLE_FIFO_IF 1
+#define PI_DISABLE_SOCK_IF 2
+
+/*-------------------------------------------------------------------------*/
+int gpioCfgInternals(unsigned what,
+                     int      value);
+/*-------------------------------------------------------------------------*/
+/* Used to tune internal settings.
+   Not intended for general use.
+*/
+
+
+
+/*-------------------------------------------------------------------------*/
+void gpioWaveDump(void);
+/*-------------------------------------------------------------------------*/
+/* Used to print a readable version of the current waveform to stdout.
+   Not intended for general use.
+*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define PI_CMD_MODES  0
+#define PI_CMD_MODEG  1
+#define PI_CMD_PUD    2
+#define PI_CMD_READ   3
+#define PI_CMD_WRITE  4
+#define PI_CMD_PWM    5
+#define PI_CMD_PRS    6
+#define PI_CMD_PFS    7
+#define PI_CMD_SERVO  8
+#define PI_CMD_WDOG   9
+#define PI_CMD_BR1   10
+#define PI_CMD_BR2   11
+#define PI_CMD_BC1   12
+#define PI_CMD_BC2   13
+#define PI_CMD_BS1   14
+#define PI_CMD_BS2   15
+#define PI_CMD_TICK  16
+#define PI_CMD_HWVER 17
+#define PI_CMD_NO    18
+#define PI_CMD_NB    19
+#define PI_CMD_NP    20
+#define PI_CMD_NC    21
+#define PI_CMD_PRG   22
+#define PI_CMD_PFG   23
+#define PI_CMD_PRRG  24
+#define PI_CMD_HELP  25
+
+/*
+The following command only works on the socket interface.
+It returns a spare notification handle.  Notifications for
+that handle will be sent to the socket (rather than a
+/dev/pigpiox pipe).
+
+The socket should be dedicated to receiving notifications
+after this command is issued.
+*/
+
+#define PI_CMD_NOIB  99
+
+
+/*-------------------------------------------------------------------------*/
+
+/* error numbers reported by functions */
+
+#define PI_INIT_FAILED       -1 /* gpioInitialise failed                   */
+#define PI_BAD_USER_GPIO     -2 /* gpio not 0-31                           */
+#define PI_BAD_GPIO          -3 /* gpio not 0-53                           */
+#define PI_BAD_MODE          -4 /* mode not 0-7                            */
+#define PI_BAD_LEVEL         -5 /* level not 0-1                           */
+#define PI_BAD_PUD           -6 /* pud not 0-2                             */
+#define PI_BAD_PULSEWIDTH    -7 /* pulsewidth not 0 or 500-2500            */
+#define PI_BAD_DUTYCYCLE     -8 /* dutycycle not 0-255                     */
+#define PI_BAD_TIMER         -9 /* timer not 0-9                           */
+#define PI_BAD_MS           -10 /* ms not 10-60000                         */
+#define PI_BAD_TIMETYPE     -11 /* timetype not 0-1                        */
+#define PI_BAD_SECONDS      -12 /* seconds < 0                             */
+#define PI_BAD_MICROS       -13 /* micros not 0-999999                     */
+#define PI_TIMER_FAILED     -14 /* gpioSetTimerFunc failed                 */
+#define PI_BAD_WDOG_TIMEOUT -15 /* timeout not 0-60000                     */
+#define PI_NO_ALERT_FUNC    -16 /* DEPRECATED                              */
+#define PI_BAD_CLK_PERIPH   -17 /* clock peripheral not 0-1                */
+#define PI_BAD_CLK_SOURCE   -18 /* clock source not 0-1                    */
+#define PI_BAD_CLK_MICROS   -19 /* clock micros not 1, 2, 4, 5, 8, or 10   */
+#define PI_BAD_BUF_MILLIS   -20 /* buf millis not 100-10000                */
+#define PI_BAD_DUTY_RANGE   -21 /* dutycycle range not 25-40000            */
+#define PI_BAD_SIGNUM       -22 /* signum not 0-63                         */
+#define PI_BAD_PATHNAME     -23 /* can't open pathname                     */
+#define PI_NO_HANDLE        -24 /* no handle available                     */
+#define PI_BAD_HANDLE       -25 /* unknown notify handle                   */
+#define PI_BAD_IF_FLAGS     -26 /* ifFlags > 3                             */
+#define PI_BAD_CHANNEL      -27 /* DMA channel not 0-14                    */
+#define PI_BAD_PRIM_CHANNEL -27 /* DMA primary channel not 0-14            */
+#define PI_BAD_SOCKET_PORT  -28 /* socket port not 1024-32000              */
+#define PI_BAD_FIFO_COMMAND -29 /* unrecognized fifo command               */
+#define PI_BAD_SECO_CHANNEL -30 /* DMA secondary channel not 0-6           */
+#define PI_NOT_INITIALISED  -31 /* function called before gpioInitialise   */
+#define PI_INITIALISED      -32 /* function called after gpioInitialise    */
+#define PI_BAD_WAVE_MODE    -33 /* waveform mode not 0-1                   */
+#define PI_BAD_CFG_INTERNAL -34 /* bad parameter in gpioCfgInternals call  */
+#define PI_BAD_WAVE_BAUD    -35 /* baud rate not 100-250000                */
+#define PI_TOO_MANY_PULSES  -36 /* waveform has too many pulses            */
+#define PI_TOO_MANY_CHARS   -37 /* waveform has too many chars             */
+#define PI_NOT_SERIAL_GPIO  -38 /* no serial read in progress on gpio      */
+#define PI_BAD_SERIAL_STRUC -39 /* bad null serial structure parameter     */
+#define PI_BAD_SERIAL_BUF   -40 /* bad null serial buf parameter           */
+
+
+/*-------------------------------------------------------------------------*/
+
+#define PI_DEFAULT_BUFFER_MILLIS         120
+#define PI_DEFAULT_CLK_MICROS            5
+#define PI_DEFAULT_CLK_PERIPHERAL        PI_CLOCK_PCM
+#define PI_DEFAULT_CLK_SOURCE            PI_CLOCK_PLLD
+#define PI_DEFAULT_IF_FLAGS              0
+#define PI_DEFAULT_DMA_CHANNEL           14
+#define PI_DEFAULT_DMA_PRIMARY_CHANNEL   14
+#define PI_DEFAULT_DMA_SECONDARY_CHANNEL 6
+#define PI_DEFAULT_SOCKET_PORT           8888
+
+#endif
+
diff --git a/pigpiod.c b/pigpiod.c
new file mode 100644 (file)
index 0000000..106354b
--- /dev/null
+++ b/pigpiod.c
@@ -0,0 +1,278 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 4+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "pigpio.h"
+#include "command.h"
+
+/*
+This program starts the pigpio library as a daemon.
+*/
+
+static unsigned bufferSizeMilliseconds = PI_DEFAULT_BUFFER_MILLIS;
+static unsigned clockMicros            = PI_DEFAULT_CLK_MICROS;
+static unsigned clockPeripheral        = PI_DEFAULT_CLK_PERIPHERAL;
+static unsigned clockSource            = PI_DEFAULT_CLK_SOURCE;
+static unsigned ifFlags                = PI_DEFAULT_IF_FLAGS;
+static unsigned DMAchannelChannel      = PI_DEFAULT_DMA_CHANNEL;
+static unsigned socketPort             = PI_DEFAULT_SOCKET_PORT;
+
+static FILE * errFifo;
+
+void usage()
+{
+   fprintf(stderr, "\n" \
+      "Usage: sudo pigpiod [OPTION] ...\n" \
+      "   -b value, gpio sample buffer in milliseconds, default 120\n" \
+      "   -d value, DMA channel, 0-14,                  default 14\n" \
+      "   -f,       disable fifo interface,             default enabled\n" \
+      "   -k,       disable socket interface,           default enabled\n" \
+      "   -p value, socket port, 1024-32000,            default 8888\n" \
+      "   -s value, sample rate, 1, 2, 4, 5, 8, or 10,  default 5\n" \
+      "   -t value, clock peripheral, 0=PWM 1=PCM,      default PCM\n" \
+      "   -u value, clock source, 0=OSC 1=PLLD,         default PLLD\n" \
+      "EXAMPLE\n" \
+      "sudo pigpiod -s 2 -b 200 -f\n" \
+      "  Set a sample rate of 2 microseconds with a 200 millisecond\n" \
+      "  buffer.  Disable the fifo interface.\n" \
+   "\n");
+}
+
+static void initOpts(int argc, char *argv[])
+{
+   int i, opt;
+
+   while ((opt = getopt(argc, argv, "b:d:fkp:s:t:u:")) != -1)
+   {
+      i = -1;
+
+      switch (opt)
+      {
+         case 'b':
+            i = atoi(optarg);
+            if ((i >= PI_BUF_MILLIS_MIN) && (i <= PI_BUF_MILLIS_MAX))
+               bufferSizeMilliseconds = i;
+            else cmdFatal("invalid -b option (%d)", i);
+            break;
+
+         case 'd':
+            i = atoi(optarg);
+            if ((i >= PI_MIN_DMA_CHANNEL) && (i <= PI_MAX_DMA_CHANNEL))
+               DMAchannelChannel = i;
+            else cmdFatal("invalid -d option (%d)", i);
+            break;
+
+         case 'f':
+            ifFlags |= PI_DISABLE_FIFO_IF;
+            break; 
+
+         case 'k':
+            ifFlags |= PI_DISABLE_SOCK_IF;
+            break; 
+
+         case 'p':
+            i = atoi(optarg);
+            if ((i >= PI_MIN_SOCKET_PORT) && (i <= PI_MAX_SOCKET_PORT))
+               socketPort = i;
+            else cmdFatal("invalid -p option (%d)", i);
+            break;
+
+         case 's':
+            i = atoi(optarg);
+
+            switch(i)
+            {
+               case 1:
+               case 2:
+               case 4:
+               case 5:
+               case 8:
+               case 10:
+                  clockMicros = i;
+                  break;
+
+               default:
+                  cmdFatal("invalid -s option (%d)", i);
+                  break;
+            }
+            break;
+
+         case 't':
+            i = atoi(optarg);
+            if ((i >= PI_CLOCK_PWM) && (i <= PI_CLOCK_PCM))
+               clockPeripheral = i;
+            else cmdFatal("invalid -t option (%d)", i);
+            break;
+
+         case 'u':
+            i = atoi(optarg);
+            if ((i >= PI_CLOCK_OSC) && (i <= PI_CLOCK_PLLD))
+               clockSource = i;
+            else cmdFatal("invalid -u option (%d)", i);
+            break;
+
+        default: /* '?' */
+           usage();
+           exit(-1);
+        }
+    }
+}
+
+void terminate(int signum)
+{
+   /* only registered for SIGHUP/SIGTERM */
+
+   gpioTerminate();
+
+   fprintf(errFifo, "SIGHUP/SIGTERM received\n");
+
+   fflush(NULL);
+
+   fclose(errFifo);
+
+   unlink(PI_ERRFIFO);
+
+   exit(0);
+}
+
+
+int main(int argc, char **argv)
+{
+   pid_t pid;
+   int flags;
+
+   /* Fork off the parent process */
+
+   pid = fork();
+
+   if (pid < 0) { exit(EXIT_FAILURE); }
+
+   /* If we got a good PID, then we can exit the parent process. */
+
+   if (pid > 0) { exit(EXIT_SUCCESS); }
+
+   /* Change the file mode mask */
+
+   umask(0);       
+   
+   /* Open any logs here */
+
+   /* NONE */
+   
+   /* Create a new SID for the child process */
+
+   if (setsid() < 0) cmdFatal("setsid failed (%m)");
+
+   /* Change the current working directory */
+
+   if ((chdir("/")) < 0) cmdFatal("chdir failed (%m)");
+   
+   /* check command line parameters */
+
+   initOpts(argc, argv);
+   
+   /* Close out the standard file descriptors */
+
+   fclose(stdin);
+   fclose(stdout);
+
+   /* configure library */
+
+   gpioCfgBufferSize(bufferSizeMilliseconds);
+
+   gpioCfgClock(clockMicros, clockPeripheral, clockSource);
+
+   gpioCfgInterfaces(ifFlags);
+
+   gpioCfgDMAchannel(DMAchannelChannel);
+
+   gpioCfgSocketPort(socketPort);
+
+   /* start library */
+
+   if (gpioInitialise()< 0) cmdFatal("Can't initialise pigpio library");
+
+   /* create pipe for error reporting */
+
+   unlink(PI_ERRFIFO);
+
+   mkfifo(PI_ERRFIFO, 0664);
+
+   if (chmod(PI_ERRFIFO, 0664) < 0)
+      cmdFatal("chmod %s failed (%m)", PI_ERRFIFO);
+
+   errFifo = freopen(PI_ERRFIFO, "w+", stderr);
+
+   if (errFifo)
+   {
+      /* set stderr non-blocking */
+
+      flags = fcntl(fileno(errFifo), F_GETFL, 0);
+      fcntl(fileno(errFifo), F_SETFL, flags | O_NONBLOCK);
+
+      /* request SIGHUP/SIGTERM from libarary for termination */
+
+      gpioSetSignalFunc(SIGHUP, terminate);
+      gpioSetSignalFunc(SIGTERM, terminate);
+
+      /* sleep forever */
+
+      while (1)
+      {
+         /* cat /dev/pigerr to view daemon errors */
+
+         sleep(5);
+
+         fflush(errFifo);
+      }
+   }
+   else
+   {
+      fprintf(stderr, "freopen failed (%m)");
+
+      gpioTerminate();
+   }
+
+   return 0;
+}
+
diff --git a/pigs.c b/pigs.c
new file mode 100644 (file)
index 0000000..e794632
--- /dev/null
+++ b/pigs.c
@@ -0,0 +1,144 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
+
+/*
+This version is for pigpio version 3+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "pigpio.h"
+#include "command.h"
+
+/*
+This program provides a socket interface
+to the commands available from pigpio.
+*/
+
+int main(int argc , char *argv[])
+{
+   int sock, r, idx, port;
+   struct sockaddr_in server;
+   cmdCmd_t cmd;
+   char * portStr, * addrStr;
+   char buf[128];
+   sock = socket(AF_INET, SOCK_STREAM, 0);
+   if (sock != -1)
+   {
+      portStr = getenv(PI_ENVPORT);
+
+      if (portStr) port = atoi(portStr);
+      else         port = PI_DEFAULT_SOCKET_PORT;
+
+      addrStr = getenv(PI_ENVADDR);
+
+      if (!addrStr) addrStr="127.0.0.1";
+
+      server.sin_addr.s_addr = inet_addr(addrStr);
+      server.sin_family = AF_INET;
+      server.sin_port = htons(port);
+
+      if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == 0)
+      {
+         switch(argc)
+         {
+            case 1:
+               exit(0);
+
+            case 2:
+               sprintf(buf, "%10s", argv[1]);
+               break;
+
+            case 3:
+               sprintf(buf, "%10s %10s", argv[1], argv[2]);
+               break;
+
+            case 4:
+               sprintf(buf, "%10s %10s %10s", argv[1], argv[2], argv[3]);
+               break;
+
+            default:
+               cmdFatal("what?");
+         }
+
+         if ((idx=cmdParse(buf, &cmd)) >= 0)
+         {
+            if (send(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
+            {
+               if (recv(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
+               {
+                  switch (cmdInfo[idx].rv)
+                  {
+                     case 0:
+                        r = cmd.res;
+                        if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r));
+                        break;
+
+                     case 1:
+                        break;
+
+                     case 2:
+                        r = cmd.res;
+                        if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r));
+                        else printf("%d\n", r);
+                        break;
+
+                     case 3:
+                        printf("%08X\n", cmd.res);
+                        break;
+
+                     case 4:
+                        printf("%u\n", cmd.res);
+                        break;
+
+                     case 5:
+                        printf(cmdUsage);
+                        break;
+                  }
+               }
+               else cmdFatal("recv failed, %m");
+            }
+            else cmdFatal("send failed, %m");
+         }
+         else cmdFatal("what?");
+      }
+      else cmdFatal("connect failed, %m");
+
+      close(sock);
+   }
+   else cmdFatal("socket failed, %m");
+
+   return 0;
+}
+