Gordons Projects

--> Projects Top-Level GIT

update for the v3+
[wiringPi] / wiringPi / wpiExtensions.c
1 /*
2  * extensions.c:
3  *      Originally part of the GPIO program to test, peek, poke and otherwise
4  *      noodle with the GPIO hardware on the Raspberry Pi.
5  *      Now used as a general purpose library to allow systems to dynamically
6  *      add in new devices into wiringPi at program run-time.
7  *      Copyright (c) 2012-2015 Gordon Henderson
8  ***********************************************************************
9  * This file is part of wiringPi:
10  *      https://projects.drogon.net/raspberry-pi/wiringpi/
11  *
12  *    wiringPi is free software: you can redistribute it and/or modify
13  *    it under the terms of the GNU Lesser General Public License as published by
14  *    the Free Software Foundation, either version 3 of the License, or
15  *    (at your option) any later version.
16  *
17  *    wiringPi is distributed in the hope that it will be useful,
18  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *    GNU Lesser General Public License for more details.
21  *
22  *    You should have received a copy of the GNU Lesser General Public License
23  *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
24  ***********************************************************************
25  */
26
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <stdarg.h>
32 #include <ctype.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <fcntl.h>
38
39 #include <wiringPi.h>
40
41 #include "mcp23008.h"
42 #include "mcp23016.h"
43 #include "mcp23017.h"
44 #include "mcp23s08.h"
45 #include "mcp23s17.h"
46 #include "sr595.h"
47 #include "pcf8574.h"
48 #include "pcf8591.h"
49 #include "mcp3002.h"
50 #include "mcp3004.h"
51 #include "mcp4802.h"
52 #include "mcp3422.h"
53 #include "max31855.h"
54 #include "max5322.h"
55 #include "ads1115.h"
56 #include "sn3218.h"
57 #include "drcSerial.h"
58 #include "drcNet.h"
59 #include "../wiringPiD/drcNetCmd.h"
60 #include "pseudoPins.h"
61 #include "bmp180.h"
62 #include "htu21d.h"
63 #include "ds18b20.h"
64 #include "rht03.h"
65
66 #include "wpiExtensions.h"
67
68 extern int wiringPiDebug ;
69
70 static int verbose ;
71 static char errorMessage [1024] ;
72
73
74 // Local structure to hold details
75
76 struct extensionFunctionStruct
77 {
78   const char *name ;
79   int   (*function)(char *progName, int pinBase, char *params) ;
80 } ;
81
82
83 /*
84  * verbError:
85  *      Convenient error handling
86  *********************************************************************************
87  */
88
89 static void verbError (const char *message, ...)
90 {
91   va_list argp ;
92   va_start (argp, message) ;
93     vsnprintf (errorMessage, 1023, message, argp) ;
94   va_end (argp) ;
95
96   if (verbose)
97     fprintf (stderr, "%s\n", errorMessage) ;
98 }
99
100
101 /*
102  * extractInt:
103  *      Check & return an integer at the given location (prefixed by a :)
104  *********************************************************************************
105  */
106
107 static char *extractInt (char *progName, char *p, int *num)
108 {
109   if (*p != ':')
110   {
111     verbError ("%s: colon expected", progName) ;
112     return NULL ;
113   }
114
115   ++p ;
116
117   if (!isdigit (*p))
118   {
119     verbError ("%s: digit expected", progName) ;
120     return NULL ;
121   }
122
123   *num = strtol (p, NULL, 0) ;
124
125 // Increment p, but we need to check for hex 0x
126
127   if ((*p == '0') && (*(p + 1) == 'x'))
128     p +=2 ;
129
130   while (isxdigit (*p))
131     ++p ;
132
133   return p ;
134 }
135
136
137 /*
138  * extractStr:
139  *      Check & return a string at the given location (prefixed by a :)
140  *      Note: The string can be enclosed in []'s to escape colons. This is
141  *      so we can handle IPv6 addresses which contain colons and the []'s is
142  *      a common way to prepresent them.
143  *********************************************************************************
144  */
145
146 static char *extractStr (char *progName, char *p, char **str)
147 {
148   char *q, *r ;
149   int quoted = FALSE ;
150
151   if (*p != ':')
152   {
153     verbError ("%s: colon expected", progName) ;
154     return NULL ;
155   }
156
157   ++p ;
158
159   if (*p == '[')
160   {
161     quoted = TRUE ;
162     ++p ;
163   }
164
165   if (!isprint (*p))    // Is this needed?
166   {
167     verbError ("%s: character expected", progName) ;
168     return NULL ;
169   }
170
171   q = p ;
172   if (quoted)
173   {
174     while ((*q != 0) && (*q != ']'))
175       ++q ;
176   }
177   else
178   {
179     while ((*q != 0) && (*q != ':'))
180       ++q ;
181   }
182
183   *str = r = calloc (q - p + 2, 1) ;    // Zeros it
184
185   while (p != q)
186     *r++ = *p++ ;
187
188   if (quoted)                           // Skip over the ] to the :
189     ++p ;
190
191   return p ;
192 }
193
194
195
196 /*
197  * doExtensionMcp23008:
198  *      MCP23008 - 8-bit I2C GPIO expansion chip
199  *      mcp23002:base:i2cAddr
200  *********************************************************************************
201  */
202
203 static int doExtensionMcp23008 (char *progName, int pinBase, char *params)
204 {
205   int i2c ;
206
207   if ((params = extractInt (progName, params, &i2c)) == NULL)
208     return FALSE ;
209
210   if ((i2c < 0x01) || (i2c > 0x77))
211   {
212     verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
213     return FALSE ;
214   }
215
216   mcp23008Setup (pinBase, i2c) ;
217
218   return TRUE ;
219 }
220
221
222 /*
223  * doExtensionMcp23016:
224  *      MCP230016- 16-bit I2C GPIO expansion chip
225  *      mcp23016:base:i2cAddr
226  *********************************************************************************
227  */
228
229 static int doExtensionMcp23016 (char *progName, int pinBase, char *params)
230 {
231   int i2c ;
232
233   if ((params = extractInt (progName, params, &i2c)) == NULL)
234     return FALSE ;
235
236   if ((i2c < 0x03) || (i2c > 0x77))
237   {
238     verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
239     return FALSE ;
240   }
241
242   mcp23016Setup (pinBase, i2c) ;
243
244   return TRUE ;
245 }
246
247
248 /*
249  * doExtensionMcp23017:
250  *      MCP230017- 16-bit I2C GPIO expansion chip
251  *      mcp23017:base:i2cAddr
252  *********************************************************************************
253  */
254
255 static int doExtensionMcp23017 (char *progName, int pinBase, char *params)
256 {
257   int i2c ;
258
259   if ((params = extractInt (progName, params, &i2c)) == NULL)
260     return FALSE ;
261
262   if ((i2c < 0x03) || (i2c > 0x77))
263   {
264     verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
265     return FALSE ;
266   }
267
268   mcp23017Setup (pinBase, i2c) ;
269
270   return TRUE ;
271 }
272
273
274 /*
275  * doExtensionMcp23s08:
276  *      MCP23s08 - 8-bit SPI GPIO expansion chip
277  *      mcp23s08:base:spi:port
278  *********************************************************************************
279  */
280
281 static int doExtensionMcp23s08 (char *progName, int pinBase, char *params)
282 {
283   int spi, port ;
284
285   if ((params = extractInt (progName, params, &spi)) == NULL)
286     return FALSE ;
287
288   if ((spi < 0) || (spi > 1))
289   {
290     verbError ("%s: SPI address (%d) out of range", progName, spi) ;
291     return FALSE ;
292   }
293
294   if ((params = extractInt (progName, params, &port)) == NULL)
295     return FALSE ;
296
297   if ((port < 0) || (port > 7))
298   {
299     verbError ("%s: port address (%d) out of range", progName, port) ;
300     return FALSE ;
301   }
302
303   mcp23s08Setup (pinBase, spi, port) ;
304
305   return TRUE ;
306 }
307
308
309 /*
310  * doExtensionMcp23s17:
311  *      MCP23s17 - 16-bit SPI GPIO expansion chip
312  *      mcp23s17:base:spi:port
313  *********************************************************************************
314  */
315
316 static int doExtensionMcp23s17 (char *progName, int pinBase, char *params)
317 {
318   int spi, port ;
319
320   if ((params = extractInt (progName, params, &spi)) == NULL)
321     return FALSE ;
322
323   if ((spi < 0) || (spi > 1))
324   {
325     verbError ("%s: SPI address (%d) out of range", progName, spi) ;
326     return FALSE ;
327   }
328
329   if ((params = extractInt (progName, params, &port)) == NULL)
330     return FALSE ;
331
332   if ((port < 0) || (port > 7))
333   {
334     verbError ("%s: port address (%d) out of range", progName, port) ;
335     return FALSE ;
336   }
337
338   mcp23s17Setup (pinBase, spi, port) ;
339
340   return TRUE ;
341 }
342
343
344 /*
345  * doExtensionSr595:
346  *      Shift Register 74x595
347  *      sr595:base:pins:data:clock:latch
348  *********************************************************************************
349  */
350
351 static int doExtensionSr595 (char *progName, int pinBase, char *params)
352 {
353   int pins, data, clock, latch ;
354
355 // Extract pins
356
357   if ((params = extractInt (progName, params, &pins)) == NULL)
358     return FALSE ;
359
360   if ((pins < 8) || (pins > 32))
361   {
362     verbError ("%s: pin count (%d) out of range - 8-32 expected.", progName, pins) ;
363     return FALSE ;
364   }
365
366   if ((params = extractInt (progName, params, &data)) == NULL)
367     return FALSE ;
368
369   if ((params = extractInt (progName, params, &clock)) == NULL)
370     return FALSE ;
371
372   if ((params = extractInt (progName, params, &latch)) == NULL)
373     return FALSE ;
374
375   sr595Setup (pinBase, pins, data, clock, latch) ;
376
377   return TRUE ;
378 }
379
380
381 /*
382  * doExtensionPcf8574:
383  *      Digital IO (Crude!)
384  *      pcf8574:base:i2cAddr
385  *********************************************************************************
386  */
387
388 static int doExtensionPcf8574 (char *progName, int pinBase, char *params)
389 {
390   int i2c ;
391
392   if ((params = extractInt (progName, params, &i2c)) == NULL)
393     return FALSE ;
394
395   if ((i2c < 0x03) || (i2c > 0x77))
396   {
397     verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
398     return FALSE ;
399   }
400
401   pcf8574Setup (pinBase, i2c) ;
402
403   return TRUE ;
404 }
405
406
407 /*
408  * doExtensionAds1115:
409  *      Analog Input
410  *      ads1115:base:i2cAddr
411  *********************************************************************************
412  */
413
414 static int doExtensionAds1115 (char *progName, int pinBase, char *params)
415 {
416   int i2c ;
417
418   if ((params = extractInt (progName, params, &i2c)) == NULL)
419     return FALSE ;
420
421   if ((i2c < 0x03) || (i2c > 0x77))
422   {
423     verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
424     return FALSE ;
425   }
426
427   ads1115Setup (pinBase, i2c) ;
428
429   return TRUE ;
430 }
431
432
433 /*
434  * doExtensionPcf8591:
435  *      Analog IO
436  *      pcf8591:base:i2cAddr
437  *********************************************************************************
438  */
439
440 static int doExtensionPcf8591 (char *progName, int pinBase, char *params)
441 {
442   int i2c ;
443
444   if ((params = extractInt (progName, params, &i2c)) == NULL)
445     return FALSE ;
446
447   if ((i2c < 0x03) || (i2c > 0x77))
448   {
449     verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
450     return FALSE ;
451   }
452
453   pcf8591Setup (pinBase, i2c) ;
454
455   return TRUE ;
456 }
457
458
459 /*
460  * doExtensionPseudoPins:
461  *      64 Memory resident pseudo pins
462  *      pseudoPins:base
463  *********************************************************************************
464  */
465
466 static int doExtensionPseudoPins (UNU char *progName, int pinBase, UNU char *params)
467 {
468   pseudoPinsSetup (pinBase) ;
469
470   return TRUE ;
471 }
472
473
474 /*
475  * doExtensionBmp180:
476  *      Analog Temp + Pressure
477  *      bmp180:base
478  *********************************************************************************
479  */
480
481 static int doExtensionBmp180 (UNU char *progName, int pinBase, UNU char *params)
482 {
483   bmp180Setup (pinBase) ;
484
485   return TRUE ;
486 }
487
488
489 /*
490  * doExtensionHtu21d:
491  *      Analog humidity + Pressure
492  *      htu21d:base
493  *********************************************************************************
494  */
495
496 static int doExtensionHtu21d (UNU char *progName, int pinBase, UNU char *params)
497 {
498   htu21dSetup (pinBase) ;
499
500   return TRUE ;
501 }
502
503
504 /*
505  * doExtensionDs18b20:
506  *      1-Wire Temperature
507  *      htu21d:base:serialNum
508  *********************************************************************************
509  */
510
511 static int doExtensionDs18b20 (char *progName, int pinBase, char *params)
512 {
513   char *serialNum ;
514
515   if ((params = extractStr (progName, params, &serialNum)) == NULL)
516     return FALSE ;
517
518   return ds18b20Setup (pinBase, serialNum) ;
519 }
520
521
522 /*
523  * doExtensionRht03:
524  *      Maxdetect 1-Wire Temperature & Humidity
525  *      rht03:base:piPin
526  *********************************************************************************
527  */
528
529 static int doExtensionRht03 (char *progName, int pinBase, char *params)
530 {
531   int piPin ;
532
533   if ((params = extractInt (progName, params, &piPin)) == NULL)
534     return FALSE ;
535
536   return rht03Setup (pinBase, piPin) ;
537 }
538
539
540 /*
541  * doExtensionMax31855:
542  *      Analog IO
543  *      max31855:base:spiChan
544  *********************************************************************************
545  */
546
547 static int doExtensionMax31855 (char *progName, int pinBase, char *params)
548 {
549   int spi ;
550
551   if ((params = extractInt (progName, params, &spi)) == NULL)
552     return FALSE ;
553
554   if ((spi < 0) || (spi > 1))
555   {
556     verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
557     return FALSE ;
558   }
559
560   max31855Setup (pinBase, spi) ;
561
562   return TRUE ;
563 }
564
565
566 /*
567  * doExtensionMcp3002:
568  *      Analog IO
569  *      mcp3002:base:spiChan
570  *********************************************************************************
571  */
572
573 static int doExtensionMcp3002 (char *progName, int pinBase, char *params)
574 {
575   int spi ;
576
577   if ((params = extractInt (progName, params, &spi)) == NULL)
578     return FALSE ;
579
580   if ((spi < 0) || (spi > 1))
581   {
582     verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
583     return FALSE ;
584   }
585
586   mcp3002Setup (pinBase, spi) ;
587
588   return TRUE ;
589 }
590
591
592 /*
593  * doExtensionMcp3004:
594  *      Analog IO
595  *      mcp3004:base:spiChan
596  *********************************************************************************
597  */
598
599 static int doExtensionMcp3004 (char *progName, int pinBase, char *params)
600 {
601   int spi ;
602
603   if ((params = extractInt (progName, params, &spi)) == NULL)
604     return FALSE ;
605
606   if ((spi < 0) || (spi > 1))
607   {
608     verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
609     return FALSE ;
610   }
611
612   mcp3004Setup (pinBase, spi) ;
613
614   return TRUE ;
615 }
616
617
618 /*
619  * doExtensionMax5322:
620  *      Analog O
621  *      max5322:base:spiChan
622  *********************************************************************************
623  */
624
625 static int doExtensionMax5322 (char *progName, int pinBase, char *params)
626 {
627   int spi ;
628
629   if ((params = extractInt (progName, params, &spi)) == NULL)
630     return FALSE ;
631
632   if ((spi < 0) || (spi > 1))
633   {
634     verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
635     return FALSE ;
636   }
637
638   max5322Setup (pinBase, spi) ;
639
640   return TRUE ;
641 }
642
643
644 /*
645  * doExtensionMcp4802:
646  *      Analog IO
647  *      mcp4802:base:spiChan
648  *********************************************************************************
649  */
650
651 static int doExtensionMcp4802 (char *progName, int pinBase, char *params)
652 {
653   int spi ;
654
655   if ((params = extractInt (progName, params, &spi)) == NULL)
656     return FALSE ;
657
658   if ((spi < 0) || (spi > 1))
659   {
660     verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
661     return FALSE ;
662   }
663
664   mcp4802Setup (pinBase, spi) ;
665
666   return TRUE ;
667 }
668
669
670 /*
671  * doExtensionSn3218:
672  *      Analog Output (LED Driver)
673  *      sn3218:base
674  *********************************************************************************
675  */
676
677 static int doExtensionSn3218 (UNU char *progName, int pinBase, UNU char *params)
678 {
679   sn3218Setup (pinBase) ;
680   return TRUE ;
681 }
682
683
684 /*
685  * doExtensionMcp3422:
686  *      Analog IO
687  *      mcp3422:base:i2cAddr
688  *********************************************************************************
689  */
690
691 static int doExtensionMcp3422 (char *progName, int pinBase, char *params)
692 {
693   int i2c, sampleRate, gain ;
694
695   if ((params = extractInt (progName, params, &i2c)) == NULL)
696     return FALSE ;
697
698   if ((i2c < 0x03) || (i2c > 0x77))
699   {
700     verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
701     return FALSE ;
702   }
703
704   if ((params = extractInt (progName, params, &sampleRate)) == NULL)
705     return FALSE ;
706
707   if ((sampleRate < 0) || (sampleRate > 3))
708   {
709     verbError ("%s: sample rate (%d) out of range", progName, sampleRate) ;
710     return FALSE ;
711   }
712
713   if ((params = extractInt (progName, params, &gain)) == NULL)
714     return FALSE ;
715
716   if ((gain < 0) || (gain > 3))
717   {
718     verbError ("%s: gain (%d) out of range", progName, gain) ;
719     return FALSE ;
720   }
721
722   mcp3422Setup (pinBase, i2c, sampleRate, gain) ;
723
724   return TRUE ;
725 }
726
727
728 /*
729  * doExtensionDrcS:
730  *      Interface to a DRC Serial system
731  *      drcs:base:pins:serialPort:baud
732  *********************************************************************************
733  */
734
735 static int doExtensionDrcS (char *progName, int pinBase, char *params)
736 {
737   char *port ;
738   int pins, baud ;
739
740   if ((params = extractInt (progName, params, &pins)) == NULL)
741     return FALSE ;
742
743   if ((pins < 1) || (pins > 1000))
744   {
745     verbError ("%s: pins (%d) out of range (2-1000)", progName, pins) ;
746     return FALSE ;
747   }
748   
749   if ((params = extractStr (progName, params, &port)) == NULL)
750     return FALSE ;
751
752   if (strlen (port) == 0)
753   {
754     verbError ("%s: serial port device name required", progName) ;
755     return FALSE ;
756   }
757
758   if ((params = extractInt (progName, params, &baud)) == NULL)
759     return FALSE ;
760
761   if ((baud < 1) || (baud > 4000000))
762   {
763     verbError ("%s: baud rate (%d) out of range", progName, baud) ;
764     return FALSE ;
765   }
766
767   drcSetupSerial (pinBase, pins, port, baud) ;
768
769   return TRUE ;
770 }
771
772
773 /*
774  * doExtensionDrcNet:
775  *      Interface to a DRC Network system
776  *      drcn:base:pins:ipAddress:port:password
777  *********************************************************************************
778  */
779
780 static int doExtensionDrcNet (char *progName, int pinBase, char *params)
781 {
782   int pins ;
783   char *ipAddress, *port, *password ;
784   char pPort [1024] ;
785
786   if ((params = extractInt (progName, params, &pins)) == NULL)
787     return FALSE ;
788
789   if ((pins < 1) || (pins > 1000))
790   {
791     verbError ("%s: pins (%d) out of range (2-1000)", progName, pins) ;
792     return FALSE ;
793   }
794   
795   if ((params = extractStr (progName, params, &ipAddress)) == NULL)
796     return FALSE ;
797
798   if (strlen (ipAddress) == 0)
799   {
800     verbError ("%s: ipAddress required", progName) ;
801     return FALSE ;
802   }
803
804   if ((params = extractStr (progName, params, &port)) == NULL)
805     return FALSE ;
806
807   if (strlen (port) == 0)
808   {
809     sprintf (pPort, "%d", DEFAULT_SERVER_PORT) ;
810     port = pPort ;
811   }
812
813   if ((params = extractStr (progName, params, &password)) == NULL)
814     return FALSE ;
815
816   if (strlen (password) == 0)
817   {
818     verbError ("%s: password required", progName) ;
819     return FALSE ;
820   }
821
822   return drcSetupNet (pinBase, pins, ipAddress, port, password) ;
823 }
824
825
826
827 /*
828  * Function list
829  *********************************************************************************
830  */
831
832 static struct extensionFunctionStruct extensionFunctions [] = 
833 {
834   { "mcp23008",         &doExtensionMcp23008    },
835   { "mcp23016",         &doExtensionMcp23016    },
836   { "mcp23017",         &doExtensionMcp23017    },
837   { "mcp23s08",         &doExtensionMcp23s08    },
838   { "mcp23s17",         &doExtensionMcp23s17    },
839   { "sr595",            &doExtensionSr595       },
840   { "pcf8574",          &doExtensionPcf8574     },
841   { "pcf8591",          &doExtensionPcf8591     },
842   { "bmp180",           &doExtensionBmp180      },
843   { "pseudoPins",       &doExtensionPseudoPins  },
844   { "htu21d",           &doExtensionHtu21d      },
845   { "ds18b20",          &doExtensionDs18b20     },
846   { "rht03",            &doExtensionRht03       },
847   { "mcp3002",          &doExtensionMcp3002     },
848   { "mcp3004",          &doExtensionMcp3004     },
849   { "mcp4802",          &doExtensionMcp4802     },
850   { "mcp3422",          &doExtensionMcp3422     },
851   { "max31855",         &doExtensionMax31855    },
852   { "ads1115",          &doExtensionAds1115     },
853   { "max5322",          &doExtensionMax5322     },
854   { "sn3218",           &doExtensionSn3218      },
855   { "drcs",             &doExtensionDrcS        },
856   { "drcn",             &doExtensionDrcNet      },
857   { NULL,               NULL                    },
858 } ;
859
860
861 /*
862  * loadWPiExtension:
863  *      Load in a wiringPi extension
864  *      The extensionData always starts with the name, a colon then the pinBase
865  *      number. Other parameters after that are decoded by the module in question.
866  *********************************************************************************
867  */
868
869 int loadWPiExtension (char *progName, char *extensionData, int printErrors)
870 {
871   char *p ;
872   char *extension = extensionData ;
873   struct extensionFunctionStruct *extensionFn ;
874   int pinBase = 0 ;
875
876   verbose = printErrors ;
877
878 // Get the extension name by finding the first colon
879
880   p = extension ;
881   while (*p != ':')
882   {
883     if (!*p)    // ran out of characters
884     {
885       verbError ("%s: extension name not terminated by a colon", progName) ;
886       return FALSE ;
887     }
888     ++p ;
889   }
890   *p++ = 0 ;
891
892 // Simple ATOI code
893
894   if (!isdigit (*p))
895   {
896     verbError ("%s: decimal pinBase number expected after extension name", progName) ;
897     return FALSE ;
898   }
899
900   while (isdigit (*p))
901   {
902     if (pinBase > 2147483647) // 2^31-1 ... Lets be realistic here...
903     {
904       verbError ("%s: pinBase too large", progName) ;
905       return FALSE ;
906     }
907
908     pinBase = pinBase * 10 + (*p - '0') ;
909     ++p ;
910   }
911
912   if (pinBase < 64)
913   {
914     verbError ("%s: pinBase (%d) too small. Minimum is 64.", progName, pinBase) ;
915     return FALSE ;
916   }
917
918 // Search for extensions:
919
920   for (extensionFn = extensionFunctions ; extensionFn->name != NULL ; ++extensionFn)
921   {
922     if (strcmp (extensionFn->name, extension) == 0)
923       return extensionFn->function (progName, pinBase, p) ;
924   }
925
926   fprintf (stderr, "%s: extension %s not found", progName, extension) ;
927   return FALSE ;
928 }