Gordons Projects

--> Projects Top-Level GIT

Updated the GPIO command to add in new features for PWM
[wiringPi] / gpio / gpio.c
1 /*
2  * gpio.c:
3  *      Set-UID command-line interface to the Raspberry Pi's GPIO
4  *      Copyright (c) 2012 Gordon Henderson
5  ***********************************************************************
6  * This file is part of wiringPi:
7  *      https://projects.drogon.net/raspberry-pi/wiringpi/
8  *
9  *    wiringPi is free software: you can redistribute it and/or modify
10  *    it under the terms of the GNU Lesser General Public License as published by
11  *    the Free Software Foundation, either version 3 of the License, or
12  *    (at your option) any later version.
13  *
14  *    wiringPi is distributed in the hope that it will be useful,
15  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *    GNU Lesser General Public License for more details.
18  *
19  *    You should have received a copy of the GNU Lesser General Public License
20  *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
21  ***********************************************************************
22  */
23
24 #include <wiringPi.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdint.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <fcntl.h>
34
35 #ifndef TRUE
36 #  define       TRUE    (1==1)
37 #  define       FALSE   (1==2)
38 #endif
39
40 #define VERSION "1.1"
41
42 static int wpMode ;
43
44 char *usage = "Usage: gpio -v\n"
45               "       gpio [-g] <read/write/pwm/mode> ...\n"
46               "       gpio [-p] <read/write/mode> ...\n"
47               "       gpio export/edge/unexport/unexportall/exports ...\n"
48               "       gpio drive <group> <value>\n"
49               "       gpio pwm-bal/pwm-ms \n"
50               "       gpio pwmr <range> \n"
51               "       gpio load spi/i2c" ;
52
53
54
55 /*
56  * changeOwner:
57  *      Change the ownership of the file to the real userId of the calling
58  *      program so we can access it.
59  *********************************************************************************
60  */
61
62 static void changeOwner (char *cmd, char *file)
63 {
64   uid_t uid = getuid () ;
65   uid_t gid = getgid () ;
66
67   if (chown (file, uid, gid) != 0)
68   {
69     if (errno == ENOENT)        // Warn that it's not there
70       fprintf (stderr, "%s: Warning: File not present: %s\n", cmd, file) ;
71     else
72     {
73       fprintf (stderr, "%s: Unable to change ownership of %s: %s\n", cmd, file, strerror (errno)) ;
74       exit (1) ;
75     }
76   }
77 }
78
79
80 /*
81  * moduleLoaded:
82  *      Return true/false if the supplied module is loaded
83  *********************************************************************************
84  */
85
86 static int moduleLoaded (char *modName)
87 {
88   int len   = strlen (modName) ;
89   int found = FALSE ;
90   FILE *fd = fopen ("/proc/modules", "r") ;
91   char line [80] ;
92
93   if (fd == NULL)
94   {
95     fprintf (stderr, "gpio: Unable to check modules: %s\n", strerror (errno)) ;
96     exit (1) ;
97   }
98
99   while (fgets (line, 80, fd) != NULL)
100   {
101     if (strncmp (line, modName, len) != 0)
102       continue ;
103
104     found = TRUE ;
105     break ;
106   }
107
108   fclose (fd) ;
109
110   return found ;
111 }
112
113
114 /*
115  * doLoad:
116  *      Load either the spi or i2c modules and change device ownerships, etc.
117  *********************************************************************************
118  */
119
120 static void _doLoadUsage (char *argv [])
121 {
122   fprintf (stderr, "Usage: %s load <spi/i2c>\n", argv [0]) ;
123   exit (1) ;
124 }
125
126 static void doLoad (int argc, char *argv [])
127 {
128   char *module ;
129   char cmd [80] ;
130   char *file1, *file2 ;
131
132   if (argc != 3)
133     _doLoadUsage (argv) ;
134
135   /**/ if (strcasecmp (argv [2], "spi") == 0)
136   {
137     module = "spi_bcm2708" ;
138     file1  = "/dev/spidev0.0" ;
139     file2  = "/dev/spidev0.1" ;
140   }
141   else if (strcasecmp (argv [2], "i2c") == 0)
142   {
143     module = "i2c_bcm2708" ;
144     file1  = "/dev/i2c-0" ;
145     file2  = "/dev/i2c-1" ;
146   }
147   else
148     _doLoadUsage (argv) ;
149
150   if (!moduleLoaded (module))
151   {
152     sprintf (cmd, "modprobe %s", module) ;
153     system (cmd) ;
154   }
155
156   if (!moduleLoaded (module))
157   {
158     fprintf (stderr, "%s: Unable to load %s\n", argv [0], module) ;
159     exit (1) ;
160   }
161
162   sleep (1) ;   // To let things get settled
163
164   changeOwner (argv [0], file1) ;
165   changeOwner (argv [0], file2) ;
166 }
167
168
169
170 /*
171  * doExports:
172  *      List all GPIO exports
173  *********************************************************************************
174  */
175
176 void doExports (void)
177 {
178   int fd ;
179   int i, l, first ;
180   char fName [128] ;
181   char buf [16] ;
182
183 // Rather crude, but who knows what others are up to...
184
185   for (first = 0, i = 0 ; i < 64 ; ++i)
186   {
187
188 // Try to read the direction
189
190     sprintf (fName, "/sys/class/gpio/gpio%d/direction", i) ;
191     if ((fd = open (fName, O_RDONLY)) == -1)
192       continue ;
193
194     if (first == 0)
195     {
196       ++first ;
197       printf ("GPIO Pins exported:\n") ;
198     }
199
200     printf ("%4d: ", i) ;
201
202     if ((l = read (fd, buf, 16)) == 0)
203       sprintf (buf, "%s", "?") ;
204  
205     buf [l] = 0 ;
206     if ((buf [strlen (buf) - 1]) == '\n')
207       buf [strlen (buf) - 1] = 0 ;
208
209     printf ("%-3s", buf) ;
210
211     close (fd) ;
212
213 // Try to Read the value
214
215     sprintf (fName, "/sys/class/gpio/gpio%d/value", i) ;
216     if ((fd = open (fName, O_RDONLY)) == -1)
217     {
218       printf ("No Value file (huh?)\n") ;
219       continue ;
220     }
221
222     if ((l = read (fd, buf, 16)) == 0)
223       sprintf (buf, "%s", "?") ;
224
225     buf [l] = 0 ;
226     if ((buf [strlen (buf) - 1]) == '\n')
227       buf [strlen (buf) - 1] = 0 ;
228
229     printf ("  %s", buf) ;
230
231 // Read any edge trigger file
232
233     sprintf (fName, "/sys/class/gpio/gpio%d/edge", i) ;
234     if ((fd = open (fName, O_RDONLY)) == -1)
235     {
236       printf ("\n") ;
237       continue ;
238     }
239
240     if ((l = read (fd, buf, 16)) == 0)
241       sprintf (buf, "%s", "?") ;
242
243     buf [l] = 0 ;
244     if ((buf [strlen (buf) - 1]) == '\n')
245       buf [strlen (buf) - 1] = 0 ;
246
247     printf ("  %-8s\n", buf) ;
248
249     close (fd) ;
250   }
251 }
252
253
254 /*
255  * doExport:
256  *      gpio export pin mode
257  *      This uses the /sys/class/gpio device interface.
258  *********************************************************************************
259  */
260
261 void doExport (int argc, char *argv [])
262 {
263   FILE *fd ;
264   int pin ;
265   char *mode ;
266   char fName [128] ;
267
268   if (argc != 4)
269   {
270     fprintf (stderr, "Usage: %s export pin mode\n", argv [0]) ;
271     exit (1) ;
272   }
273
274   pin = atoi (argv [2]) ;
275
276   mode = argv [3] ;
277
278   if ((fd = fopen ("/sys/class/gpio/export", "w")) == NULL)
279   {
280     fprintf (stderr, "%s: Unable to open GPIO export interface: %s\n", argv [0], strerror (errno)) ;
281     exit (1) ;
282   }
283
284   fprintf (fd, "%d\n", pin) ;
285   fclose (fd) ;
286
287   sprintf (fName, "/sys/class/gpio/gpio%d/direction", pin) ;
288   if ((fd = fopen (fName, "w")) == NULL)
289   {
290     fprintf (stderr, "%s: Unable to open GPIO direction interface for pin %d: %s\n", argv [0], pin, strerror (errno)) ;
291     exit (1) ;
292   }
293
294   /**/ if ((strcasecmp (mode, "in")  == 0) || (strcasecmp (mode, "input")  == 0))
295     fprintf (fd, "in\n") ;
296   else if ((strcasecmp (mode, "out") == 0) || (strcasecmp (mode, "output") == 0))
297     fprintf (fd, "out\n") ;
298   else
299   {
300     fprintf (stderr, "%s: Invalid mode: %s. Should be in or out\n", argv [1], mode) ;
301     exit (1) ;
302   }
303
304   fclose (fd) ;
305
306 // Change ownership so the current user can actually use it!
307
308   sprintf (fName, "/sys/class/gpio/gpio%d/value", pin) ;
309   changeOwner (argv [0], fName) ;
310
311   sprintf (fName, "/sys/class/gpio/gpio%d/edge", pin) ;
312   changeOwner (argv [0], fName) ;
313
314 }
315
316
317 /*
318  * doEdge:
319  *      gpio edge pin mode
320  *      Easy access to changing the edge trigger on a GPIO pin
321  *      This uses the /sys/class/gpio device interface.
322  *********************************************************************************
323  */
324
325 void doEdge (int argc, char *argv [])
326 {
327   FILE *fd ;
328   int pin ;
329   char *mode ;
330   char fName [128] ;
331   uid_t uid ;
332   gid_t gid ;
333
334   if (argc != 4)
335   {
336     fprintf (stderr, "Usage: %s edge pin mode\n", argv [0]) ;
337     exit (1) ;
338   }
339
340   pin = atoi (argv [2]) ;
341
342   mode = argv [3] ;
343
344 // Export the pin and set direction to input
345
346   if ((fd = fopen ("/sys/class/gpio/export", "w")) == NULL)
347   {
348     fprintf (stderr, "%s: Unable to open GPIO export interface: %s\n", argv [0], strerror (errno)) ;
349     exit (1) ;
350   }
351
352   fprintf (fd, "%d\n", pin) ;
353   fclose (fd) ;
354
355   sprintf (fName, "/sys/class/gpio/gpio%d/direction", pin) ;
356   if ((fd = fopen (fName, "w")) == NULL)
357   {
358     fprintf (stderr, "%s: Unable to open GPIO direction interface for pin %d: %s\n", argv [0], pin, strerror (errno)) ;
359     exit (1) ;
360   }
361
362   fprintf (fd, "in\n") ;
363   fclose (fd) ;
364
365   sprintf (fName, "/sys/class/gpio/gpio%d/edge", pin) ;
366   if ((fd = fopen (fName, "w")) == NULL)
367   {
368     fprintf (stderr, "%s: Unable to open GPIO edge interface for pin %d: %s\n", argv [0], pin, strerror (errno)) ;
369     exit (1) ;
370   }
371
372   /**/ if (strcasecmp (mode, "none")    == 0)
373     fprintf (fd, "none\n") ;
374   else if (strcasecmp (mode, "rising")  == 0)
375     fprintf (fd, "rising\n") ;
376   else if (strcasecmp (mode, "falling") == 0)
377     fprintf (fd, "falling\n") ;
378   else if (strcasecmp (mode, "both")    == 0)
379     fprintf (fd, "both\n") ;
380   else
381   {
382     fprintf (stderr, "%s: Invalid mode: %s. Should be none, rising, falling or both\n", argv [1], mode) ;
383     exit (1) ;
384   }
385
386 // Change ownership so the current user can actually use it!
387
388   uid = getuid () ;
389   gid = getgid () ;
390
391   sprintf (fName, "/sys/class/gpio/gpio%d/value", pin) ;
392   if (chown (fName, uid, gid) != 0)
393   {
394     fprintf (stderr, "%s: Unable to change ownership of the value file: %s\n", argv [1], strerror (errno)) ;
395     exit (1) ;
396   }
397
398 // Also change ownership of the edge file
399
400   sprintf (fName, "/sys/class/gpio/gpio%d/edge", pin) ;
401   if (chown (fName, uid, gid) != 0)
402   {
403     fprintf (stderr, "%s: Unable to change ownership of the value file: %s\n", argv [1], strerror (errno)) ;
404     exit (1) ;
405   }
406
407   fclose (fd) ;
408 }
409
410
411 /*
412  * doUnexport:
413  *      gpio unexport pin
414  *      This uses the /sys/class/gpio device interface.
415  *********************************************************************************
416  */
417
418 void doUnexport (int argc, char *argv [])
419 {
420   FILE *fd ;
421   int pin ;
422
423   if (argc != 3)
424   {
425     fprintf (stderr, "Usage: %s unexport pin\n", argv [0]) ;
426     exit (1) ;
427   }
428
429   pin = atoi (argv [2]) ;
430
431   if ((fd = fopen ("/sys/class/gpio/unexport", "w")) == NULL)
432   {
433     fprintf (stderr, "%s: Unable to open GPIO export interface\n", argv [0]) ;
434     exit (1) ;
435   }
436
437   fprintf (fd, "%d\n", pin) ;
438   fclose (fd) ;
439 }
440
441
442 /*
443  * doUnexportAll:
444  *      gpio unexportall
445  *      Un-Export all the GPIO pins.
446  *      This uses the /sys/class/gpio device interface.
447  *********************************************************************************
448  */
449
450 void doUnexportall (int argc, char *argv [])
451 {
452   FILE *fd ;
453   int pin ;
454
455   for (pin = 0 ; pin < 63 ; ++pin)
456   {
457     if ((fd = fopen ("/sys/class/gpio/unexport", "w")) == NULL)
458     {
459       fprintf (stderr, "%s: Unable to open GPIO export interface\n", argv [0]) ;
460       exit (1) ;
461     }
462     fprintf (fd, "%d\n", pin) ;
463     fclose (fd) ;
464   }
465 }
466
467
468 /*
469  * doMode:
470  *      gpio mode pin mode ...
471  *********************************************************************************
472  */
473
474 void doMode (int argc, char *argv [])
475 {
476   int pin ;
477   char *mode ;
478
479   if (argc != 4)
480   {
481     fprintf (stderr, "Usage: %s mode pin mode\n", argv [0]) ;
482     exit (1) ;
483   }
484
485   pin = atoi (argv [2]) ;
486
487   if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
488     return ;
489
490   mode = argv [3] ;
491
492   /**/ if (strcasecmp (mode, "in")   == 0) pinMode         (pin, INPUT) ;
493   else if (strcasecmp (mode, "out")  == 0) pinMode         (pin, OUTPUT) ;
494   else if (strcasecmp (mode, "pwm")  == 0) pinMode         (pin, PWM_OUTPUT) ;
495   else if (strcasecmp (mode, "up")   == 0) pullUpDnControl (pin, PUD_UP) ;
496   else if (strcasecmp (mode, "down") == 0) pullUpDnControl (pin, PUD_DOWN) ;
497   else if (strcasecmp (mode, "tri")  == 0) pullUpDnControl (pin, PUD_OFF) ;
498   else
499   {
500     fprintf (stderr, "%s: Invalid mode: %s. Should be in/out/pwm/up/down/tri\n", argv [1], mode) ;
501     exit (1) ;
502   }
503 }
504
505
506 /*
507  * doPadDrive:
508  *      gpio drive group value
509  *********************************************************************************
510  */
511
512 void doPadDrive (int argc, char *argv [])
513 {
514   int group, val ;
515
516   if (argc != 4)
517   {
518     fprintf (stderr, "Usage: %s drive group value\n", argv [0]) ;
519     exit (1) ;
520   }
521
522   group = atoi (argv [2]) ;
523   val   = atoi (argv [3]) ;
524
525   if ((group < 0) || (group > 2))
526   {
527     fprintf (stderr, "%s: drive group not 0, 1 or 2: %d\n", argv [0], group) ;
528     exit (1) ;
529   }
530
531   if ((val < 0) || (val > 7))
532   {
533     fprintf (stderr, "%s: drive value not 0-7: %d\n", argv [0], val) ;
534     exit (1) ;
535   }
536
537   setPadDrive (group, val) ;
538 }
539
540
541 /*
542  * doWrite:
543  *      gpio write pin value
544  *********************************************************************************
545  */
546
547 void doWrite (int argc, char *argv [])
548 {
549   int pin, val ;
550
551   if (argc != 4)
552   {
553     fprintf (stderr, "Usage: %s write pin value\n", argv [0]) ;
554     exit (1) ;
555   }
556
557   pin = atoi (argv [2]) ;
558
559   if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
560     return ;
561
562   val = atoi (argv [3]) ;
563
564   /**/ if (val == 0)
565     digitalWrite (pin, LOW) ;
566   else
567     digitalWrite (pin, HIGH) ;
568 }
569
570
571 /*
572  * doRead:
573  *      Read a pin and return the value
574  *********************************************************************************
575  */
576
577 void doRead (int argc, char *argv []) 
578 {
579   int pin, val ;
580
581   if (argc != 3)
582   {
583     fprintf (stderr, "Usage: %s read pin\n", argv [0]) ;
584     exit (1) ;
585   }
586
587   pin = atoi (argv [2]) ;
588
589   if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
590   {
591     printf ("0\n") ;
592     return ;
593   }
594
595   val = digitalRead (pin) ;
596
597   printf ("%s\n", val == 0 ? "0" : "1") ;
598 }
599
600
601 /*
602  * doPwm:
603  *      Output a PWM value on a pin
604  *********************************************************************************
605  */
606
607 void doPwm (int argc, char *argv [])
608 {
609   int pin, val ;
610
611   if (argc != 4)
612   {
613     fprintf (stderr, "Usage: %s pwm <pin> <value>\n", argv [0]) ;
614     exit (1) ;
615   }
616
617   pin = atoi (argv [2]) ;
618
619   if ((wpMode == WPI_MODE_PINS) && ((pin < 0) || (pin >= NUM_PINS)))
620     return ;
621
622   val = atoi (argv [3]) ;
623
624   pwmWrite (pin, val) ;
625 }
626
627
628 /*
629  * doPwmMode: doPwmRange:
630  *      Change the PWM mode and Range values
631  *********************************************************************************
632  */
633
634 static void doPwmMode (int mode)
635 {
636   pwmSetMode (mode) ;
637 }
638
639 static void doPwmRange (int argc, char *argv [])
640 {
641   unsigned int range ;
642
643   if (argc != 3)
644   {
645     fprintf (stderr, "Usage: %s pwmr <range>\n", argv [0]) ;
646     exit (1) ;
647   }
648
649   range = (unsigned int)strtoul (argv [2], NULL, 10) ;
650
651   if (range == 0)
652   {
653     fprintf (stderr, "%s: range must be > 0\n", argv [0]) ;
654     exit (1) ;
655   }
656
657   pwmSetRange (range) ;
658 }
659
660
661 /*
662  * main:
663  *      Start here
664  *********************************************************************************
665  */
666
667 int main (int argc, char *argv [])
668 {
669   int i ;
670
671   if (argc == 1)
672   {
673     fprintf (stderr, "%s: %s\n", argv [0], usage) ;
674     return 1 ;
675   }
676
677   if (strcasecmp (argv [1], "-v") == 0)
678   {
679     printf ("gpio version: %s\n", VERSION) ;
680     printf ("Copyright (c) 2012 Gordon Henderson\n") ;
681     printf ("This is free software with ABSOLUTELY NO WARRANTY.\n") ;
682     printf ("For details type: %s -warranty\n", argv [0]) ;
683     return 0 ;
684   }
685
686   if (strcasecmp (argv [1], "-warranty") == 0)
687   {
688     printf ("gpio version: %s\n", VERSION) ;
689     printf ("Copyright (c) 2012 Gordon Henderson\n") ;
690     printf ("\n") ;
691     printf ("    This program is free software; you can redistribute it and/or modify\n") ;
692     printf ("    it under the terms of the GNU Leser General Public License as published\n") ;
693     printf ("    by the Free Software Foundation, either version 3 of the License, or\n") ;
694     printf ("    (at your option) any later version.\n") ;
695     printf ("\n") ;
696     printf ("    This program is distributed in the hope that it will be useful,\n") ;
697     printf ("    but WITHOUT ANY WARRANTY; without even the implied warranty of\n") ;
698     printf ("    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n") ;
699     printf ("    GNU Lesser General Public License for more details.\n") ;
700     printf ("\n") ;
701     printf ("    You should have received a copy of the GNU Lesser General Public License\n") ;
702     printf ("    along with this program. If not, see <http://www.gnu.org/licenses/>.\n") ;
703     printf ("\n") ;
704     return 0 ;
705   }
706
707   if (geteuid () != 0)
708   {
709     fprintf (stderr, "%s: Must be root to run. Program should be suid root. This is an error.\n", argv [0]) ;
710     return 1 ;
711   }
712
713 // Initial test for /sys/class/gpio operations:
714
715   /**/ if (strcasecmp (argv [1], "exports"    ) == 0)   { doExports () ;                return 0 ; }
716   else if (strcasecmp (argv [1], "export"     ) == 0)   { doExport (argc, argv) ;       return 0 ; }
717   else if (strcasecmp (argv [1], "edge"       ) == 0)   { doEdge (argc, argv) ;         return 0 ; }
718   else if (strcasecmp (argv [1], "unexportall") == 0)   { doUnexportall (argc, argv) ;  return 0 ; }
719   else if (strcasecmp (argv [1], "unexport"   ) == 0)   { doUnexport (argc, argv) ;     return 0 ; }
720   else if (strcasecmp (argv [1], "load"       ) == 0)   { doLoad (argc, argv) ;         return 0 ; }
721
722 // Check for -g argument
723
724   if (strcasecmp (argv [1], "-g") == 0)
725   {
726     if (wiringPiSetupGpio () == -1)
727     {
728       fprintf (stderr, "%s: Unable to initialise GPIO in GPIO mode.\n", argv [0]) ;
729       exit (1) ;
730     }
731
732     for (i = 2 ; i < argc ; ++i)
733       argv [i - 1] = argv [i] ;
734     --argc ;
735     wpMode = WPI_MODE_GPIO ;
736   }
737
738 // Check for -p argument for PiFace
739
740   else if (strcasecmp (argv [1], "-p") == 0)
741   {
742     if (wiringPiSetupPiFaceForGpioProg () == -1)
743     {
744       fprintf (stderr, "%s: Unable to initialise PiFace.\n", argv [0]) ;
745       exit (1) ;
746     }
747
748     for (i = 2 ; i < argc ; ++i)
749       argv [i - 1] = argv [i] ;
750     --argc ;
751     wpMode = WPI_MODE_PIFACE ;
752   }
753
754 // Default to wiringPi mode
755
756   else
757   {
758     if (wiringPiSetup () == -1)
759     {
760       fprintf (stderr, "%s: Unable to initialise GPIO in wiringPi mode\n", argv [0]) ;
761       exit (1) ;
762     }
763     wpMode = WPI_MODE_PINS ;
764   }
765
766 // Check for PWM operations
767
768   if (wpMode != WPI_MODE_PIFACE)
769   {
770     if (strcasecmp (argv [1], "pwm-bal") == 0)  { doPwmMode  (PWM_MODE_BAL) ; return 0 ; }
771     if (strcasecmp (argv [1], "pwm-ms")  == 0)  { doPwmMode  (PWM_MODE_MS) ;  return 0 ; }
772     if (strcasecmp (argv [1], "pwmr")    == 0)  { doPwmRange (argc, argv) ;   return 0 ; }
773   }
774
775 // Check for wiring commands
776
777   /**/ if (strcasecmp (argv [1], "write"   ) == 0) doWrite    (argc, argv) ;
778   else if (strcasecmp (argv [1], "read"    ) == 0) doRead     (argc, argv) ;
779   else if (strcasecmp (argv [1], "mode"    ) == 0) doMode     (argc, argv) ;
780   else if (strcasecmp (argv [1], "pwm"     ) == 0) doPwm      (argc, argv) ;
781   else if (strcasecmp (argv [1], "drive"   ) == 0) doPadDrive (argc, argv) ;
782   else
783   {
784     fprintf (stderr, "%s: Unknown command: %s. (read/write/pwm/mode/drive expected)\n", argv [0], argv [1]) ;
785     exit (1) ;
786   }
787   return 0 ;
788 }