Gordons Projects

--> Projects Top-Level GIT

Bumped the version to 2.40 - correctly this time, I hope.
[wiringPi] / gpio / gpio.c
index e71e432..6fd71f8 100644 (file)
@@ -2,7 +2,7 @@
  * gpio.c:
  *     Swiss-Army-Knife, Set-UID command-line interface to the Raspberry
  *     Pi's GPIO.
- *     Copyright (c) 2012 Gordon Henderson
+ *     Copyright (c) 2012-2017 Gordon Henderson
  ***********************************************************************
  * This file is part of wiringPi:
  *     https://projects.drogon.net/raspberry-pi/wiringpi/
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <ctype.h>
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
-#include <sys/types.h>
 #include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include <wiringPi.h>
+#include <wpiExtensions.h>
+
 #include <gertboard.h>
+#include <piFace.h>
+
+#include "../version.h"
 
 extern int wiringPiDebug ;
 
+// External functions I can't be bothered creating a separate .h file for:
+
+extern void doReadall    (void) ;
+extern void doAllReadall (void) ;
+extern void doPins       (void) ;
+
 #ifndef TRUE
 #  define      TRUE    (1==1)
 #  define      FALSE   (1==2)
 #endif
 
-#define        VERSION "1.12"
+#define        PI_USB_POWER_CONTROL    38
+#define        I2CDETECT               "/usr/sbin/i2cdetect"
+#define        MODPROBE                "/sbin/modprobe"
+#define        RMMOD                   "/sbin/rmmod"
 
-static int wpMode ;
+int wpMode ;
 
 char *usage = "Usage: gpio -v\n"
               "       gpio -h\n"
-              "       gpio [-g] <read/write/wb/pwm/clock/mode> ...\n"
+              "       gpio [-g|-1] ...\n"
+              "       gpio [-d] ...\n"
+              "       [-x extension:params] [[ -x ...]] ...\n"
               "       gpio [-p] <read/write/wb> ...\n"
-             "       gpio readall\n"
-             "       gpio unexportall/exports ...\n"
+              "       gpio <read/write/aread/awritewb/pwm/clock/mode> ...\n"
+              "       gpio <toggle/blink> <pin>\n"
+             "       gpio readall/reset\n"
+             "       gpio unexportall/exports\n"
              "       gpio export/edge/unexport ...\n"
+             "       gpio wfi <pin> <mode>\n"
              "       gpio drive <group> <value>\n"
              "       gpio pwm-bal/pwm-ms \n"
              "       gpio pwmr <range> \n"
              "       gpio pwmc <divider> \n"
              "       gpio load spi/i2c\n"
+             "       gpio unload spi/i2c\n"
+             "       gpio i2cd/i2cdetect\n"
+             "       gpio rbx/rbd\n"
+             "       gpio wb <value>\n"
+             "       gpio usbp high/low\n"
              "       gpio gbr <channel>\n"
              "       gpio gbw <channel> <value>" ;     // No trailing newline needed here.
 
 
+#ifdef NOT_FOR_NOW
+/*
+ * decodePin:
+ *     Decode a pin "number" which can actually be a pin name to represent
+ *     one of the Pi's on-board pins.
+ *********************************************************************************
+ */
+
+static int decodePin (const char *str)
+{
+
+// The first case - see if it's a number:
+
+  if (isdigit (str [0]))
+    return atoi (str) ;
+
+  return 0 ;
+}
+#endif
+
+
+/*
+ * findExecutable:
+ *     Code to locate the path to the given executable. We have a fixed list
+ *     of locations to try which completely overrides any $PATH environment.
+ *     This may be detrimental, however it avoids the reliance on $PATH
+ *     which may be a security issue when this program is run a set-uid-root.
+ *********************************************************************************
+ */
+
+static const char *searchPath [] =
+{
+  "/sbin",
+  "/usr/sbin",
+  "/bin",
+  "/usr/bin",
+  NULL,
+} ;
+
+static char *findExecutable (const char *progName)
+{
+  static char *path = NULL ;
+  int len = strlen (progName) ;
+  int i = 0 ;
+  struct stat statBuf ;
+
+  for (i = 0 ; searchPath [i] != NULL ; ++i)
+  {
+    path = malloc (strlen (searchPath [i]) + len + 2) ;
+    sprintf (path, "%s/%s", searchPath [i], progName) ;
+
+    if (stat (path, &statBuf) == 0)
+      return path ;
+    free (path) ;
+  }
+
+  return NULL ;
+}
+
+
 /*
  * changeOwner:
  *     Change the ownership of the file to the real userId of the calling
@@ -76,13 +162,12 @@ static void changeOwner (char *cmd, char *file)
 
   if (chown (file, uid, gid) != 0)
   {
-    if (errno == ENOENT)       // Warn that it's not there
-      fprintf (stderr, "%s: Warning: File not present: %s\n", cmd, file) ;
-    else
-    {
+
+// Removed (ignoring) the check for not existing as I'm fed-up with morons telling me that
+//     the warning message is an error.
+
+    if (errno != ENOENT)
       fprintf (stderr, "%s: Unable to change ownership of %s: %s\n", cmd, file, strerror (errno)) ;
-      exit (1) ;
-    }
   }
 }
 
@@ -102,7 +187,7 @@ static int moduleLoaded (char *modName)
 
   if (fd == NULL)
   {
-    fprintf (stderr, "gpio: Unable to check modules: %s\n", strerror (errno)) ;
+    fprintf (stderr, "gpio: Unable to check /proc/modules: %s\n", strerror (errno)) ;
     exit (1) ;
   }
 
@@ -127,9 +212,25 @@ static int moduleLoaded (char *modName)
  *********************************************************************************
  */
 
+static void checkDevTree (char *argv [])
+{
+  struct stat statBuf ;
+
+  if (stat ("/proc/device-tree", &statBuf) == 0)       // We're on a devtree system ...
+  {
+    fprintf (stderr,
+"%s: Unable to load/unload modules as this Pi has the device tree enabled.\n"
+"  You need to run the raspi-config program (as root) and select the\n"
+"  modules (SPI or I2C) that you wish to load/unload there and reboot.\n"
+"  There is more information here:\n"
+"      https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=97314\n", argv [0]) ;
+    exit (1) ;
+  }
+}
+
 static void _doLoadUsage (char *argv [])
 {
-  fprintf (stderr, "Usage: %s load <spi/i2c> [SPI bufferSize in KB | I2C baudrate in Kb/sec]\n", argv [0]) ;
+  fprintf (stderr, "Usage: %s load <spi/i2c> [I2C baudrate in Kb/sec]\n", argv [0]) ;
   exit (1) ;
 }
 
@@ -140,6 +241,8 @@ static void doLoad (int argc, char *argv [])
   char *file1, *file2 ;
   char args1 [32], args2 [32] ;
 
+  checkDevTree (argv) ;
+
   if (argc < 3)
     _doLoadUsage (argv) ;
 
@@ -152,7 +255,10 @@ static void doLoad (int argc, char *argv [])
     file1  = "/dev/spidev0.0" ;
     file2  = "/dev/spidev0.1" ;
     if (argc == 4)
-      sprintf (args1, " bufsiz=%d", atoi (argv [3]) * 1024) ;
+    {
+      fprintf (stderr, "%s: Unable to set the buffer size now. Load aborted. Please see the man page.\n", argv [0]) ;
+      exit (1) ;
+    }
     else if (argc > 4)
       _doLoadUsage (argv) ;
   }
@@ -170,15 +276,18 @@ static void doLoad (int argc, char *argv [])
   else
     _doLoadUsage (argv) ;
 
+  if (findExecutable ("modprobe") == NULL)
+    printf ("No found\n") ;
+
   if (!moduleLoaded (module1))
   {
-    sprintf (cmd, "modprobe %s%s", module1, args1) ;
+    sprintf (cmd, "%s %s%s", findExecutable (MODPROBE), module1, args1) ;
     system (cmd) ;
   }
 
   if (!moduleLoaded (module2))
   {
-    sprintf (cmd, "modprobe %s%s", module2, args2) ;
+    sprintf (cmd, "%s %s%s", findExecutable (MODPROBE), module2, args2) ;
     system (cmd) ;
   }
 
@@ -196,46 +305,82 @@ static void doLoad (int argc, char *argv [])
 
 
 /*
- * doReadall:
- *     Read all the GPIO pins
+ * doUnLoad:
+ *     Un-Load either the spi or i2c modules and change device ownerships, etc.
  *********************************************************************************
  */
 
-static char *pinNames [] =
+static void _doUnLoadUsage (char *argv [])
 {
-  "GPIO 0", "GPIO 1", "GPIO 2", "GPIO 3", "GPIO 4", "GPIO 5", "GPIO 6", "GPIO 7",
-  "SDA   ", "SCL   ",
-  "CE0   ", "CE1   ", "MOSI  ", "MISO  ", "SCLK  ",
-  "TxD   ", "RxD   ",
-  "GPIO 8", "GPIO 9", "GPIO10", "GPIO11",
-} ;
+  fprintf (stderr, "Usage: %s unload <spi/i2c>\n", argv [0]) ;
+  exit (1) ;
+}
 
-static char *alts [] =
+static void doUnLoad (int argc, char *argv [])
 {
-  "IN  ", "OUT ", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"
-} ;
+  char *module1, *module2 ;
+  char cmd [80] ;
 
-static void doReadall (void)
-{
-  int pin ;
+  checkDevTree (argv) ;
 
-  printf ("+----------+------+--------+------+-------+\n") ;
-  printf ("| wiringPi | GPIO | Name   | Mode | Value |\n") ;
-  printf ("+----------+------+--------+------+-------+\n") ;
+  if (argc != 3)
+    _doUnLoadUsage (argv) ;
 
-  for (pin = 0 ; pin < 64 ; ++pin)
+  /**/ if (strcasecmp (argv [2], "spi") == 0)
   {
-    if (wpiPinToGpio (pin) == -1)
-      continue ;
+    module1 = "spidev" ;
+    module2 = "spi_bcm2708" ;
+  }
+  else if (strcasecmp (argv [2], "i2c") == 0)
+  {
+    module1 = "i2c_dev" ;
+    module2 = "i2c_bcm2708" ;
+  }
+  else
+    _doUnLoadUsage (argv) ;
 
-    printf ("| %6d   | %3d  | %s | %s | %s  |\n",
-       pin, wpiPinToGpio (pin),
-       pinNames [pin], 
-       alts [getAlt (pin)], 
-       digitalRead (pin) == HIGH ? "High" : "Low ") ;
+  if (moduleLoaded (module1))
+  {
+    sprintf (cmd, "%s %s", findExecutable (RMMOD), module1) ;
+    system (cmd) ;
   }
 
-  printf ("+----------+------+--------+------+-------+\n") ;
+  if (moduleLoaded (module2))
+  {
+    sprintf (cmd, "%s %s", findExecutable (RMMOD), module2) ;
+    system (cmd) ;
+  }
+}
+
+
+/*
+ * doI2Cdetect:
+ *     Run the i2cdetect command with the right runes for this Pi revision
+ *********************************************************************************
+ */
+
+static void doI2Cdetect (UNU int argc, char *argv [])
+{
+  int port = piGpioLayout () == 1 ? 0 : 1 ;
+  char *c, *command ;
+
+  if ((c = findExecutable (I2CDETECT)) == NULL)
+  {
+    fprintf (stderr, "%s: Unable to find i2cdetect command: %s\n", argv [0], strerror (errno)) ;
+    return ;
+  }
+
+  if (!moduleLoaded ("i2c_dev"))
+  {
+    fprintf (stderr, "%s: The I2C kernel module(s) are not loaded.\n", argv [0]) ;
+    return ;
+  }
+
+  command = malloc (strlen (c) + 16) ;
+  sprintf (command, "%s -y %d", c, port) ;
+  if (system (command) < 0)
+    fprintf (stderr, "%s: Unable to run i2cdetect: %s\n", argv [0], strerror (errno)) ;
+
 }
 
 
@@ -245,16 +390,14 @@ static void doReadall (void)
  *********************************************************************************
  */
 
-static void doExports (int argc, char *argv [])
+static void doExports (UNU int argc, UNU char *argv [])
 {
   int fd ;
   int i, l, first ;
   char fName [128] ;
   char buf [16] ;
 
-// Rather crude, but who knows what others are up to...
-
-  for (first = 0, i = 0 ; i < 64 ; ++i)
+  for (first = 0, i = 0 ; i < 64 ; ++i)        // Crude, but effective
   {
 
 // Try to read the direction
@@ -363,19 +506,23 @@ void doExport (int argc, char *argv [])
     exit (1) ;
   }
 
-  /**/ if ((strcasecmp (mode, "in")  == 0) || (strcasecmp (mode, "input")  == 0))
+  /**/ if ((strcasecmp (mode, "in")   == 0) || (strcasecmp (mode, "input")  == 0))
     fprintf (fd, "in\n") ;
-  else if ((strcasecmp (mode, "out") == 0) || (strcasecmp (mode, "output") == 0))
+  else if ((strcasecmp (mode, "out")  == 0) || (strcasecmp (mode, "output") == 0))
     fprintf (fd, "out\n") ;
+  else if ((strcasecmp (mode, "high") == 0) || (strcasecmp (mode, "up")     == 0))
+    fprintf (fd, "high\n") ;
+  else if ((strcasecmp (mode, "low")  == 0) || (strcasecmp (mode, "down")   == 0))
+    fprintf (fd, "low\n") ;
   else
   {
-    fprintf (stderr, "%s: Invalid mode: %s. Should be in or out\n", argv [1], mode) ;
+    fprintf (stderr, "%s: Invalid mode: %s. Should be in, out, high or low\n", argv [1], mode) ;
     exit (1) ;
   }
 
   fclose (fd) ;
 
-// Change ownership so the current user can actually use it!
+// Change ownership so the current user can actually use it
 
   sprintf (fName, "/sys/class/gpio/gpio%d/value", pin) ;
   changeOwner (argv [0], fName) ;
@@ -387,6 +534,52 @@ void doExport (int argc, char *argv [])
 
 
 /*
+ * doWfi:
+ *     gpio wfi pin mode
+ *     Wait for Interrupt on a given pin.
+ *     Slight cheat here - it's easier to actually use ISR now (which calls
+ *     gpio to set the pin modes!) then we simply sleep, and expect the thread
+ *     to exit the program. Crude but effective.
+ *********************************************************************************
+ */
+
+static void wfi (void)
+  { exit (0) ; }
+
+void doWfi (int argc, char *argv [])
+{
+  int pin, mode ;
+
+  if (argc != 4)
+  {
+    fprintf (stderr, "Usage: %s wfi pin mode\n", argv [0]) ;
+    exit (1) ;
+  }
+
+  pin  = atoi (argv [2]) ;
+
+  /**/ if (strcasecmp (argv [3], "rising")  == 0) mode = INT_EDGE_RISING ;
+  else if (strcasecmp (argv [3], "falling") == 0) mode = INT_EDGE_FALLING ;
+  else if (strcasecmp (argv [3], "both")    == 0) mode = INT_EDGE_BOTH ;
+  else
+  {
+    fprintf (stderr, "%s: wfi: Invalid mode: %s. Should be rising, falling or both\n", argv [1], argv [3]) ;
+    exit (1) ;
+  }
+
+  if (wiringPiISR (pin, mode, &wfi) < 0)
+  {
+    fprintf (stderr, "%s: wfi: Unable to setup ISR: %s\n", argv [1], strerror (errno)) ;
+    exit (1) ;
+  }
+
+  for (;;)
+    delay (9999) ;
+}
+
+
+
+/*
  * doEdge:
  *     gpio edge pin mode
  *     Easy access to changing the edge trigger on a GPIO pin
@@ -499,7 +692,7 @@ void doUnexport (int argc, char *argv [])
  *********************************************************************************
  */
 
-void doUnexportall (int argc, char *argv [])
+void doUnexportall (char *progName)
 {
   FILE *fd ;
   int pin ;
@@ -508,7 +701,7 @@ void doUnexportall (int argc, char *argv [])
   {
     if ((fd = fopen ("/sys/class/gpio/unexport", "w")) == NULL)
     {
-      fprintf (stderr, "%s: Unable to open GPIO export interface\n", argv [0]) ;
+      fprintf (stderr, "%s: Unable to open GPIO export interface\n", progName) ;
       exit (1) ;
     }
     fprintf (fd, "%d\n", pin) ;
@@ -518,6 +711,20 @@ void doUnexportall (int argc, char *argv [])
 
 
 /*
+ * doReset:
+ *     Reset the GPIO pins - as much as we can do
+ *********************************************************************************
+ */
+
+static void doReset (UNU char *progName)
+{
+  printf ("GPIO Reset is dangerous and has been removed from the gpio command.\n") ;
+  printf (" - Please write a shell-script to reset the GPIO pins into the state\n") ;
+  printf ("   that you need them in for your applications.\n") ;
+}
+
+
+/*
  * doMode:
  *     gpio mode pin mode ...
  *********************************************************************************
@@ -536,18 +743,25 @@ void doMode (int argc, char *argv [])
 
   pin = atoi (argv [2]) ;
 
-  if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
-    return ;
-
   mode = argv [3] ;
 
-  /**/ if (strcasecmp (mode, "in")     == 0) pinMode         (pin, INPUT) ;
-  else if (strcasecmp (mode, "out")    == 0) pinMode         (pin, OUTPUT) ;
-  else if (strcasecmp (mode, "pwm")    == 0) pinMode         (pin, PWM_OUTPUT) ;
-  else if (strcasecmp (mode, "clock")  == 0) pinMode         (pin, GPIO_CLOCK) ;
-  else if (strcasecmp (mode, "up")     == 0) pullUpDnControl (pin, PUD_UP) ;
-  else if (strcasecmp (mode, "down")   == 0) pullUpDnControl (pin, PUD_DOWN) ;
-  else if (strcasecmp (mode, "tri")    == 0) pullUpDnControl (pin, PUD_OFF) ;
+  /**/ if (strcasecmp (mode, "in")      == 0) pinMode         (pin, INPUT) ;
+  else if (strcasecmp (mode, "input")   == 0) pinMode         (pin, INPUT) ;
+  else if (strcasecmp (mode, "out")     == 0) pinMode         (pin, OUTPUT) ;
+  else if (strcasecmp (mode, "output")  == 0) pinMode         (pin, OUTPUT) ;
+  else if (strcasecmp (mode, "pwm")     == 0) pinMode         (pin, PWM_OUTPUT) ;
+  else if (strcasecmp (mode, "pwmTone") == 0) pinMode         (pin, PWM_TONE_OUTPUT) ;
+  else if (strcasecmp (mode, "clock")   == 0) pinMode         (pin, GPIO_CLOCK) ;
+  else if (strcasecmp (mode, "up")      == 0) pullUpDnControl (pin, PUD_UP) ;
+  else if (strcasecmp (mode, "down")    == 0) pullUpDnControl (pin, PUD_DOWN) ;
+  else if (strcasecmp (mode, "tri")     == 0) pullUpDnControl (pin, PUD_OFF) ;
+  else if (strcasecmp (mode, "off")     == 0) pullUpDnControl (pin, PUD_OFF) ;
+  else if (strcasecmp (mode, "alt0")    == 0) pinModeAlt (pin, 0b100) ;
+  else if (strcasecmp (mode, "alt1")    == 0) pinModeAlt (pin, 0b101) ;
+  else if (strcasecmp (mode, "alt2")    == 0) pinModeAlt (pin, 0b110) ;
+  else if (strcasecmp (mode, "alt3")    == 0) pinModeAlt (pin, 0b111) ;
+  else if (strcasecmp (mode, "alt4")    == 0) pinModeAlt (pin, 0b011) ;
+  else if (strcasecmp (mode, "alt5")    == 0) pinModeAlt (pin, 0b010) ;
   else
   {
     fprintf (stderr, "%s: Invalid mode: %s. Should be in/out/pwm/clock/up/down/tri\n", argv [1], mode) ;
@@ -592,6 +806,58 @@ static void doPadDrive (int argc, char *argv [])
 
 
 /*
+ * doUsbP:
+ *     Control USB Power - High (1.2A) or Low (600mA)
+ *     gpio usbp high/low
+ *********************************************************************************
+ */
+
+static void doUsbP (int argc, char *argv [])
+{
+  int model, rev, mem, maker, overVolted ;
+
+  if (argc != 3)
+  {
+    fprintf (stderr, "Usage: %s usbp high|low\n", argv [0]) ;
+    exit (1) ;
+  }
+
+// Make sure we're on a B+
+
+  piBoardId (&model, &rev, &mem, &maker, &overVolted) ;
+
+  if (!((model == PI_MODEL_BP) || (model == PI_MODEL_2)))
+  {
+    fprintf (stderr, "USB power contol is applicable to B+ and v2 boards only.\n") ;
+    exit (1) ;
+  }
+    
+// Make sure we start in BCM_GPIO mode
+
+  wiringPiSetupGpio () ;
+
+  if ((strcasecmp (argv [2], "high") == 0) || (strcasecmp (argv [2], "hi") == 0))
+  {
+    digitalWrite (PI_USB_POWER_CONTROL, 1) ;
+    pinMode (PI_USB_POWER_CONTROL, OUTPUT) ;
+    printf ("Switched to HIGH current USB (1.2A)\n") ;
+    return ;
+  }
+
+  if ((strcasecmp (argv [2], "low") == 0) || (strcasecmp (argv [2], "lo") == 0))
+  {
+    digitalWrite (PI_USB_POWER_CONTROL, 0) ;
+    pinMode (PI_USB_POWER_CONTROL, OUTPUT) ;
+    printf ("Switched to LOW current USB (600mA)\n") ;
+    return ;
+  }
+
+  fprintf (stderr, "Usage: %s usbp high|low\n", argv [0]) ;
+  exit (1) ;
+}
+
+
+/*
  * doGbw:
  *     gpio gbw channel value
  *     Gertboard Write - To the Analog output
@@ -604,7 +870,7 @@ static void doGbw (int argc, char *argv [])
 
   if (argc != 4)
   {
-    fprintf (stderr, "Usage: %s gbr <channel> <value>\n", argv [0]) ;
+    fprintf (stderr, "Usage: %s gbw <channel> <value>\n", argv [0]) ;
     exit (1) ;
   }
 
@@ -613,23 +879,23 @@ static void doGbw (int argc, char *argv [])
 
   if ((channel < 0) || (channel > 1))
   {
-    fprintf (stderr, "%s: channel must be 0 or 1\n", argv [0]) ;
+    fprintf (stderr, "%s: gbw: Channel number must be 0 or 1\n", argv [0]) ;
     exit (1) ;
   }
 
-  if ((value < 0) || (value > 1023))
+  if ((value < 0) || (value > 255))
   {
-    fprintf (stderr, "%s: value must be from 0 to 255\n", argv [0]) ;
+    fprintf (stderr, "%s: gbw: Value must be from 0 to 255\n", argv [0]) ;
     exit (1) ;
   }
 
-  if (gertboardSPISetup () == -1)
+  if (gertboardAnalogSetup (64) < 0)
   {
     fprintf (stderr, "Unable to initialise the Gertboard SPI interface: %s\n", strerror (errno)) ;
     exit (1) ;
   }
 
-  gertboardAnalogWrite (channel, value) ;
+  analogWrite (64 + channel, value) ;
 }
 
 
@@ -654,21 +920,20 @@ static void doGbr (int argc, char *argv [])
 
   if ((channel < 0) || (channel > 1))
   {
-    fprintf (stderr, "%s: channel must be 0 or 1\n", argv [0]) ;
+    fprintf (stderr, "%s: gbr: Channel number must be 0 or 1\n", argv [0]) ;
     exit (1) ;
   }
 
-  if (gertboardSPISetup () == -1)
+  if (gertboardAnalogSetup (64) < 0)
   {
     fprintf (stderr, "Unable to initialise the Gertboard SPI interface: %s\n", strerror (errno)) ;
     exit (1) ;
   }
 
-  printf ("%d\n",gertboardAnalogRead (channel)) ;
+  printf ("%d\n", analogRead (64 + channel)) ;
 }
 
 
-
 /*
  * doWrite:
  *     gpio write pin value
@@ -687,9 +952,6 @@ static void doWrite (int argc, char *argv [])
 
   pin = atoi (argv [2]) ;
 
-  if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
-    return ;
-
   /**/ if ((strcasecmp (argv [3], "up") == 0) || (strcasecmp (argv [3], "on") == 0))
     val = 1 ;
   else if ((strcasecmp (argv [3], "down") == 0) || (strcasecmp (argv [3], "off") == 0))
@@ -703,9 +965,34 @@ static void doWrite (int argc, char *argv [])
     digitalWrite (pin, HIGH) ;
 }
 
+
+/*
+ * doAwriterite:
+ *     gpio awrite pin value
+ *********************************************************************************
+ */
+
+static void doAwrite (int argc, char *argv [])
+{
+  int pin, val ;
+
+  if (argc != 4)
+  {
+    fprintf (stderr, "Usage: %s awrite pin value\n", argv [0]) ;
+    exit (1) ;
+  }
+
+  pin = atoi (argv [2]) ;
+
+  val = atoi (argv [3]) ;
+
+  analogWrite (pin, val) ;
+}
+
+
 /*
  * doWriteByte:
- *     gpio write value
+ *     gpio wb value
  *********************************************************************************
  */
 
@@ -726,6 +1013,30 @@ static void doWriteByte (int argc, char *argv [])
 
 
 /*
+ * doReadByte:
+ *     gpio rbx|rbd value
+ *********************************************************************************
+ */
+
+static void doReadByte (int argc, char *argv [], int printHex)
+{
+  int val ;
+
+  if (argc != 2)
+  {
+    fprintf (stderr, "Usage: %s rbx|rbd\n", argv [0]) ;
+    exit (1) ;
+  }
+
+  val = digitalReadByte () ;
+  if (printHex)
+    printf ("%02X\n", val) ;
+  else
+    printf ("%d\n", val) ;
+}
+
+
+/*
  * doRead:
  *     Read a pin and return the value
  *********************************************************************************
@@ -742,16 +1053,100 @@ void doRead (int argc, char *argv [])
   }
 
   pin = atoi (argv [2]) ;
+  val = digitalRead (pin) ;
+
+  printf ("%s\n", val == 0 ? "0" : "1") ;
+}
+
 
-  if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
+/*
+ * doAread:
+ *     Read an analog pin and return the value
+ *********************************************************************************
+ */
+
+void doAread (int argc, char *argv []) 
+{
+  if (argc != 3)
   {
-    printf ("0\n") ;
-    return ;
+    fprintf (stderr, "Usage: %s aread pin\n", argv [0]) ;
+    exit (1) ;
   }
 
-  val = digitalRead (pin) ;
+  printf ("%d\n", analogRead (atoi (argv [2]))) ;
+}
 
-  printf ("%s\n", val == 0 ? "0" : "1") ;
+
+/*
+ * doToggle:
+ *     Toggle an IO pin
+ *********************************************************************************
+ */
+
+void doToggle (int argc, char *argv [])
+{
+  int pin ;
+
+  if (argc != 3)
+  {
+    fprintf (stderr, "Usage: %s toggle pin\n", argv [0]) ;
+    exit (1) ;
+  }
+
+  pin = atoi (argv [2]) ;
+
+  digitalWrite (pin, !digitalRead (pin)) ;
+}
+
+
+/*
+ * doBlink:
+ *     Blink an IO pin
+ *********************************************************************************
+ */
+
+void doBlink (int argc, char *argv [])
+{
+  int pin ;
+
+  if (argc != 3)
+  {
+    fprintf (stderr, "Usage: %s blink pin\n", argv [0]) ;
+    exit (1) ;
+  }
+
+  pin = atoi (argv [2]) ;
+
+  pinMode (pin, OUTPUT) ;
+  for (;;)
+  {
+    digitalWrite (pin, !digitalRead (pin)) ;
+    delay (500) ;
+  }
+
+}
+
+
+/*
+ * doPwmTone:
+ *     Output a tone in a PWM pin
+ *********************************************************************************
+ */
+
+void doPwmTone (int argc, char *argv [])
+{
+  int pin, freq ;
+
+  if (argc != 4)
+  {
+    fprintf (stderr, "Usage: %s pwmTone <pin> <freq>\n", argv [0]) ;
+    exit (1) ;
+  }
+
+  pin = atoi (argv [2]) ;
+  freq = atoi (argv [3]) ;
+
+  pwmToneWrite (pin, freq) ;
 }
 
 
@@ -773,9 +1168,6 @@ void doClock (int argc, char *argv [])
 
   pin = atoi (argv [2]) ;
 
-  if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
-    return ;
-
   freq = atoi (argv [3]) ;
 
   gpioClockSet (pin, freq) ;
@@ -800,9 +1192,6 @@ void doPwm (int argc, char *argv [])
 
   pin = atoi (argv [2]) ;
 
-  if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
-    return ;
-
   val = atoi (argv [3]) ;
 
   pwmWrite (pin, val) ;
@@ -864,6 +1253,56 @@ static void doPwmClock (int argc, char *argv [])
 
 
 /*
+ * doVersion:
+ *     Handle the ever more complicated version command and print out
+ *     some usefull information.
+ *********************************************************************************
+ */
+
+static void doVersion (char *argv [])
+{
+  int model, rev, mem, maker, warranty ;
+  struct stat statBuf ;
+  char name [80] ;
+  FILE *fd ;
+
+  int vMaj, vMin ;
+
+  wiringPiVersion (&vMaj, &vMin) ;
+  printf ("gpio version: %d.%d\n", vMaj, vMin) ;
+  printf ("Copyright (c) 2012-2017 Gordon Henderson\n") ;
+  printf ("This is free software with ABSOLUTELY NO WARRANTY.\n") ;
+  printf ("For details type: %s -warranty\n", argv [0]) ;
+  printf ("\n") ;
+  piBoardId (&model, &rev, &mem, &maker, &warranty) ;
+
+  printf ("Raspberry Pi Details:\n") ;
+  printf ("  Type: %s, Revision: %s, Memory: %dMB, Maker: %s %s\n", 
+      piModelNames [model], piRevisionNames [rev], piMemorySize [mem], piMakerNames [maker], warranty ? "[Out of Warranty]" : "") ;
+
+// Check for device tree
+
+  if (stat ("/proc/device-tree", &statBuf) == 0)       // We're on a devtree system ...
+    printf ("  * Device tree is enabled.\n") ;
+
+  if (stat ("/proc/device-tree/model", &statBuf) == 0) // Output Kernel idea of board type
+  {
+    if ((fd = fopen ("/proc/device-tree/model", "r")) != NULL)
+    {
+      fgets (name, 80, fd) ;
+      fclose (fd) ;
+      printf ("  *--> %s\n", name) ;
+    }
+  }
+
+  if (stat ("/dev/gpiomem", &statBuf) == 0)            // User level GPIO is GO
+    printf ("  * This Raspberry Pi supports user-level GPIO access.\n") ;
+  else
+    printf ("  * Root or sudo required for GPIO access.\n") ;
+}
+
+
+/*
  * main:
  *     Start here
  *********************************************************************************
@@ -885,27 +1324,35 @@ int main (int argc, char *argv [])
     return 1 ;
   }
 
+// Help
+
   if (strcasecmp (argv [1], "-h") == 0)
   {
     printf ("%s: %s\n", argv [0], usage) ;
     return 0 ;
   }
 
-  if (strcasecmp (argv [1], "-v") == 0)
+// Version & Warranty
+//     Wish I could remember why I have both -R and -V ...
+
+  if ((strcmp (argv [1], "-R") == 0) || (strcmp (argv [1], "-V") == 0))
   {
-    printf ("gpio version: %s\n", VERSION) ;
-    printf ("Copyright (c) 2012 Gordon Henderson\n") ;
-    printf ("This is free software with ABSOLUTELY NO WARRANTY.\n") ;
-    printf ("For details type: %s -warranty\n", argv [0]) ;
-    printf ("\n") ;
-    printf ("This Raspberry Pi is a revision %d board.\n", piBoardRev ()) ;
+    printf ("%d\n", piGpioLayout ()) ;
+    return 0 ;
+  }
+
+// Version and information
+
+  if (strcmp (argv [1], "-v") == 0)
+  {
+    doVersion (argv) ;
     return 0 ;
   }
 
   if (strcasecmp (argv [1], "-warranty") == 0)
   {
     printf ("gpio version: %s\n", VERSION) ;
-    printf ("Copyright (c) 2012 Gordon Henderson\n") ;
+    printf ("Copyright (c) 2012-2017 Gordon Henderson\n") ;
     printf ("\n") ;
     printf ("    This program is free software; you can redistribute it and/or modify\n") ;
     printf ("    it under the terms of the GNU Leser General Public License as published\n") ;
@@ -934,27 +1381,37 @@ int main (int argc, char *argv [])
   /**/ if (strcasecmp (argv [1], "exports"    ) == 0)  { doExports     (argc, argv) ;  return 0 ; }
   else if (strcasecmp (argv [1], "export"     ) == 0)  { doExport      (argc, argv) ;  return 0 ; }
   else if (strcasecmp (argv [1], "edge"       ) == 0)  { doEdge        (argc, argv) ;  return 0 ; }
-  else if (strcasecmp (argv [1], "unexportall") == 0)  { doUnexportall (argc, argv) ;  return 0 ; }
   else if (strcasecmp (argv [1], "unexport"   ) == 0)  { doUnexport    (argc, argv) ;  return 0 ; }
+  else if (strcasecmp (argv [1], "unexportall") == 0)  { doUnexportall (argv [0]) ;    return 0 ; }
 
 // Check for load command:
 
-  if (strcasecmp (argv [1], "load" ) == 0)     { doLoad     (argc, argv) ; return 0 ; }
+  if (strcasecmp (argv [1], "load"   ) == 0)   { doLoad   (argc, argv) ; return 0 ; }
+  if (strcasecmp (argv [1], "unload" ) == 0)   { doUnLoad (argc, argv) ; return 0 ; }
+
+// Check for usb power command
+
+  if (strcasecmp (argv [1], "usbp"   ) == 0)   { doUsbP   (argc, argv) ; return 0 ; }
 
 // Gertboard commands
 
   if (strcasecmp (argv [1], "gbr" ) == 0)      { doGbr (argc, argv) ; return 0 ; }
   if (strcasecmp (argv [1], "gbw" ) == 0)      { doGbw (argc, argv) ; return 0 ; }
 
+// Check for allreadall command, force Gpio mode
+
+  if (strcasecmp (argv [1], "allreadall") == 0)
+  {
+    wiringPiSetupGpio () ;
+    doAllReadall      () ;
+    return 0 ;
+  }
+
 // Check for -g argument
 
-  if (strcasecmp (argv [1], "-g") == 0)
+  /**/ if (strcasecmp (argv [1], "-g") == 0)
   {
-    if (wiringPiSetupGpio () == -1)
-    {
-      fprintf (stderr, "%s: Unable to initialise GPIO mode.\n", argv [0]) ;
-      exit (1) ;
-    }
+    wiringPiSetupGpio () ;
 
     for (i = 2 ; i < argc ; ++i)
       argv [i - 1] = argv [i] ;
@@ -962,15 +1419,23 @@ int main (int argc, char *argv [])
     wpMode = WPI_MODE_GPIO ;
   }
 
+// Check for -1 argument
+
+  else if (strcasecmp (argv [1], "-1") == 0)
+  {
+    wiringPiSetupPhys () ;
+
+    for (i = 2 ; i < argc ; ++i)
+      argv [i - 1] = argv [i] ;
+    --argc ;
+    wpMode = WPI_MODE_PHYS ;
+  }
+
 // Check for -p argument for PiFace
 
   else if (strcasecmp (argv [1], "-p") == 0)
   {
-    if (wiringPiSetupPiFaceForGpioProg () == -1)
-    {
-      fprintf (stderr, "%s: Unable to initialise PiFace.\n", argv [0]) ;
-      exit (1) ;
-    }
+    piFaceSetup (200) ;
 
     for (i = 2 ; i < argc ; ++i)
       argv [i - 1] = argv [i] ;
@@ -978,42 +1443,93 @@ int main (int argc, char *argv [])
     wpMode = WPI_MODE_PIFACE ;
   }
 
+// Check for -z argument so we don't actually initialise wiringPi
+
+  else if (strcasecmp (argv [1], "-z") == 0)
+  {
+    for (i = 2 ; i < argc ; ++i)
+      argv [i - 1] = argv [i] ;
+    --argc ;
+    wpMode = WPI_MODE_UNINITIALISED ;
+  }
+
 // Default to wiringPi mode
 
   else
   {
-    if (wiringPiSetup () == -1)
-    {
-      fprintf (stderr, "%s: Unable to initialise wiringPi mode\n", argv [0]) ;
-      exit (1) ;
-    }
+    wiringPiSetup () ;
     wpMode = WPI_MODE_PINS ;
   }
 
-// Check for PWM or Pad Drive operations
+// Check for -x argument to load in a new extension
+//     -x extension:base:args
+//     Can load many modules, but unless daemon mode we can only send one
+//     command at a time.
 
-  if (wpMode != WPI_MODE_PIFACE)
+  while (strcasecmp (argv [1], "-x") == 0)
   {
-    if (strcasecmp (argv [1], "pwm-bal") == 0) { doPwmMode  (PWM_MODE_BAL) ;   return 0 ; }
-    if (strcasecmp (argv [1], "pwm-ms")  == 0) { doPwmMode  (PWM_MODE_MS) ;    return 0 ; }
-    if (strcasecmp (argv [1], "pwmr")    == 0) { doPwmRange (argc, argv) ;     return 0 ; }
-    if (strcasecmp (argv [1], "pwmc")    == 0) { doPwmClock (argc, argv) ;     return 0 ; }
-    if (strcasecmp (argv [1], "drive")   == 0) { doPadDrive (argc, argv) ;     return 0 ; }
+    if (argc < 3)
+    {
+      fprintf (stderr, "%s: -x missing extension command.\n", argv [0]) ;
+      exit (EXIT_FAILURE) ;
+    }
+
+    if (!loadWPiExtension (argv [0], argv [2], TRUE))
+    {
+      fprintf (stderr, "%s: Extension load failed: %s\n", argv [0], strerror (errno)) ;
+      exit (EXIT_FAILURE) ;
+    }
+
+// Shift args down by 2
+
+    for (i = 3 ; i < argc ; ++i)
+      argv [i - 2] = argv [i] ;
+    argc -= 2 ;
   }
 
-// Check for wiring commands
+  if (argc <= 1)
+  {
+    fprintf (stderr, "%s: no command given\n", argv [0]) ;
+    exit (EXIT_FAILURE) ;
+  }
 
-  /**/ if (strcasecmp (argv [1], "readall" ) == 0) doReadall   () ;
-  else if (strcasecmp (argv [1], "read" )    == 0) doRead      (argc, argv) ;
-  else if (strcasecmp (argv [1], "write")    == 0) doWrite     (argc, argv) ;
-  else if (strcasecmp (argv [1], "wb")       == 0) doWriteByte (argc, argv) ;
-  else if (strcasecmp (argv [1], "pwm"  )    == 0) doPwm       (argc, argv) ;
-  else if (strcasecmp (argv [1], "clock")    == 0) doClock     (argc, argv) ;
-  else if (strcasecmp (argv [1], "mode" )    == 0) doMode      (argc, argv) ;
+// Core wiringPi functions
+
+  /**/ if (strcasecmp (argv [1], "mode"   ) == 0) doMode      (argc, argv) ;
+  else if (strcasecmp (argv [1], "read"   ) == 0) doRead      (argc, argv) ;
+  else if (strcasecmp (argv [1], "write"  ) == 0) doWrite     (argc, argv) ;
+  else if (strcasecmp (argv [1], "pwm"    ) == 0) doPwm       (argc, argv) ;
+  else if (strcasecmp (argv [1], "awrite" ) == 0) doAwrite    (argc, argv) ;
+  else if (strcasecmp (argv [1], "aread"  ) == 0) doAread     (argc, argv) ;
+
+// GPIO Nicies
+
+  else if (strcasecmp (argv [1], "toggle" ) == 0) doToggle    (argc, argv) ;
+  else if (strcasecmp (argv [1], "blink"  ) == 0) doBlink     (argc, argv) ;
+
+// Pi Specifics
+
+  else if (strcasecmp (argv [1], "pwm-bal"  ) == 0) doPwmMode    (PWM_MODE_BAL) ;
+  else if (strcasecmp (argv [1], "pwm-ms"   ) == 0) doPwmMode    (PWM_MODE_MS) ;
+  else if (strcasecmp (argv [1], "pwmr"     ) == 0) doPwmRange   (argc, argv) ;
+  else if (strcasecmp (argv [1], "pwmc"     ) == 0) doPwmClock   (argc, argv) ;
+  else if (strcasecmp (argv [1], "pwmTone"  ) == 0) doPwmTone    (argc, argv) ;
+  else if (strcasecmp (argv [1], "drive"    ) == 0) doPadDrive   (argc, argv) ;
+  else if (strcasecmp (argv [1], "readall"  ) == 0) doReadall    () ;
+  else if (strcasecmp (argv [1], "nreadall" ) == 0) doReadall    () ;
+  else if (strcasecmp (argv [1], "pins"     ) == 0) doPins       () ;
+  else if (strcasecmp (argv [1], "i2cdetect") == 0) doI2Cdetect  (argc, argv) ;
+  else if (strcasecmp (argv [1], "i2cd"     ) == 0) doI2Cdetect  (argc, argv) ;
+  else if (strcasecmp (argv [1], "reset"    ) == 0) doReset      (argv [0]) ;
+  else if (strcasecmp (argv [1], "wb"       ) == 0) doWriteByte  (argc, argv) ;
+  else if (strcasecmp (argv [1], "rbx"      ) == 0) doReadByte   (argc, argv, TRUE) ;
+  else if (strcasecmp (argv [1], "rbd"      ) == 0) doReadByte   (argc, argv, FALSE) ;
+  else if (strcasecmp (argv [1], "clock"    ) == 0) doClock      (argc, argv) ;
+  else if (strcasecmp (argv [1], "wfi"      ) == 0) doWfi        (argc, argv) ;
   else
   {
     fprintf (stderr, "%s: Unknown command: %s.\n", argv [0], argv [1]) ;
-    exit (1) ;
+    exit (EXIT_FAILURE) ;
   }
   return 0 ;
 }