f54eaabbd21b4518684e1e0dd116eeef6ff642bb
[contrib/mraa.git] / src / mraa.c
1 /*
2  * Author: Brendan Le Foll <brendan.le.foll@intel.com>
3  * Author: Thomas Ingleby <thomas.c.ingleby@intel.com>
4  * Copyright (c) 2014 Intel Corporation.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 #define _GNU_SOURCE
27 #if !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600
28 #define _XOPEN_SOURCE 600 /* Get nftw() and S_IFSOCK declarations */
29 #endif
30
31 #include <stddef.h>
32 #include <stdlib.h>
33 #include <sched.h>
34 #include <string.h>
35 #include <pwd.h>
36 #include <glob.h>
37 #include <ftw.h>
38 #include <dirent.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <string.h>
42 #include <stdio.h>
43
44 #include "mraa_internal.h"
45 #include "gpio.h"
46 #include "version.h"
47
48 #define MAX_PLATFORM_NAME_LENGTH 128
49 mraa_board_t* plat = NULL;
50 // static mraa_board_t* current_plat = NULL;
51
52 static char platform_name[MAX_PLATFORM_NAME_LENGTH];
53
54 static int num_i2c_devices = 0;
55 static int num_iio_devices = 0;
56
57 const char*
58 mraa_get_version()
59 {
60     return gVERSION;
61 }
62
63 mraa_result_t
64 mraa_set_log_level(int level)
65 {
66     if (level <= 7 && level >= 0) {
67         setlogmask(LOG_UPTO(level));
68         syslog(LOG_DEBUG, "Loglevel %d is set", level);
69         return MRAA_SUCCESS;
70     }
71     syslog(LOG_NOTICE, "Invalid loglevel %d requested", level);
72     return MRAA_ERROR_INVALID_PARAMETER;
73 }
74
75 static int
76 mraa_count_iio_devices(const char* path, const struct stat* sb, int flag, struct FTW* ftwb)
77 {
78     switch (sb->st_mode & S_IFMT) {
79         case S_IFLNK:
80             num_iio_devices++;
81             break;
82     }
83     return 0;
84 }
85
86 #if (defined SWIGPYTHON) || (defined SWIG)
87 mraa_result_t
88 #else
89 mraa_result_t __attribute__((constructor))
90 #endif
91 mraa_init()
92 {
93     if (plat != NULL) {
94         return MRAA_ERROR_PLATFORM_ALREADY_INITIALISED;
95     }
96
97     uid_t proc_euid = geteuid();
98     struct passwd* proc_user = getpwuid(proc_euid);
99
100 #ifdef DEBUG
101     setlogmask(LOG_UPTO(LOG_DEBUG));
102 #else
103     setlogmask(LOG_UPTO(LOG_NOTICE));
104 #endif
105
106     openlog("libmraa", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
107     syslog(LOG_NOTICE, "libmraa version %s initialised by user '%s' with EUID %d",
108            mraa_get_version(), (proc_user != NULL) ? proc_user->pw_name : "<unknown>", proc_euid);
109
110 #ifdef SWIGPYTHON
111     // Initialise python threads, this allows use to grab the GIL when we are
112     // required to do so
113     Py_InitializeEx(0);
114     PyEval_InitThreads();
115 #endif
116
117     mraa_platform_t platform_type;
118 #if defined(X86PLAT)
119     // Use runtime x86 platform detection
120     platform_type = mraa_x86_platform();
121 #elif defined(ARMPLAT)
122     // Use runtime ARM platform detection
123     platform_type = mraa_arm_platform();
124 #else
125 #error mraa_ARCH NOTHING
126 #endif
127
128     if (plat != NULL)
129         plat->platform_type = platform_type;
130
131     // This is a platform extender so create null base platform if one doesn't already exist
132     if (plat == NULL) {
133         plat = (mraa_board_t*) calloc(1, sizeof(mraa_board_t));
134         if (plat != NULL) {
135             plat->platform_type = MRAA_NULL_PLATFORM;
136             plat->platform_name = "Null platform";
137         }
138     }
139 #if defined(USBPLAT)
140     // Now detect sub platform
141     if (plat != NULL) {
142         mraa_platform_t usb_platform_type = mraa_usb_platform_extender(plat);
143         if (plat->platform_type == MRAA_UNKNOWN_PLATFORM && usb_platform_type != MRAA_UNKNOWN_PLATFORM) {
144             plat->platform_type = usb_platform_type;
145         } else {
146             return MRAA_ERROR_PLATFORM_NOT_INITIALISED;
147         }
148     }
149     if (plat == NULL) {
150         printf("mraa: FATAL error, failed to initialise platform\n");
151         return MRAA_ERROR_PLATFORM_NOT_INITIALISED;
152     }
153 #endif
154
155     // Now detect IIO devices, linux only
156     // find how many i2c buses we have if we haven't already
157     if (num_iio_devices == 0) {
158         if (nftw("/sys/bus/iio/devices", &mraa_count_iio_devices, 20, FTW_PHYS) == -1) {
159             return MRAA_ERROR_UNSPECIFIED;
160         }
161     }
162     char name[64], filepath[64];
163     int fd, len, i;
164     plat->iio_device_count = num_iio_devices;
165     plat->iio_devices = calloc(num_iio_devices, sizeof(struct _iio));
166     struct _iio* device;
167     for (i=0; i < num_iio_devices; i++) {
168         device = &plat->iio_devices[i];
169         device->num = i;
170         snprintf(filepath, 64, "/sys/bus/iio/devices/iio:device%d/name", i);
171         fd = open(filepath, O_RDONLY);
172         if (fd > 0) {
173             len = read(fd, &name, 64);
174             if (len > 1) {
175                 // use strndup
176                 device->name = malloc((sizeof(char) * len) + sizeof(char));
177                 strncpy(device->name, name, len);
178             }
179             close(fd);
180         }
181     }
182
183     syslog(LOG_NOTICE, "libmraa initialised for platform '%s' of type %d", mraa_get_platform_name(), mraa_get_platform_type());
184     return MRAA_SUCCESS;
185 }
186
187 void
188 mraa_deinit()
189 {
190     if (plat != NULL) {
191         if (plat->pins != NULL) {
192             free(plat->pins);
193         }
194         mraa_board_t* sub_plat = plat->sub_platform;
195         if (sub_plat != NULL) {
196             if (sub_plat->pins != NULL) {
197                 free(sub_plat->pins);
198             }
199             free(sub_plat);
200         }
201         free(plat);
202
203     }
204     closelog();
205 }
206
207 int
208 mraa_set_priority(const unsigned int priority)
209 {
210     struct sched_param sched_s;
211
212     memset(&sched_s, 0, sizeof(struct sched_param));
213     if (priority > sched_get_priority_max(SCHED_RR)) {
214         sched_s.sched_priority = sched_get_priority_max(SCHED_RR);
215     } else {
216         sched_s.sched_priority = priority;
217     }
218
219     return sched_setscheduler(0, SCHED_RR, &sched_s);
220 }
221
222 mraa_result_t
223 mraa_setup_mux_mapped(mraa_pin_t meta)
224 {
225     int mi;
226
227     for (mi = 0; mi < meta.mux_total; mi++) {
228         mraa_gpio_context mux_i;
229         mux_i = mraa_gpio_init_raw(meta.mux[mi].pin);
230         if (mux_i == NULL) {
231             return MRAA_ERROR_INVALID_HANDLE;
232         }
233         // this function will sometimes fail, however this is not critical as
234         // long as the write succeeds - Test case galileo gen2 pin2
235         mraa_gpio_dir(mux_i, MRAA_GPIO_OUT);
236         mraa_gpio_owner(mux_i, 0);
237
238         if (mraa_gpio_write(mux_i, meta.mux[mi].value) != MRAA_SUCCESS) {
239             mraa_gpio_close(mux_i);
240             return MRAA_ERROR_INVALID_RESOURCE;
241         }
242         mraa_gpio_close(mux_i);
243     }
244
245     return MRAA_SUCCESS;
246 }
247
248 void
249 mraa_result_print(mraa_result_t result)
250 {
251     switch (result) {
252         case MRAA_SUCCESS:
253             fprintf(stdout, "MRAA: SUCCESS\n");
254             break;
255         case MRAA_ERROR_FEATURE_NOT_IMPLEMENTED:
256             fprintf(stdout, "MRAA: Feature not implemented.\n");
257             break;
258         case MRAA_ERROR_FEATURE_NOT_SUPPORTED:
259             fprintf(stdout, "MRAA: Feature not supported by Hardware.\n");
260             break;
261         case MRAA_ERROR_INVALID_VERBOSITY_LEVEL:
262             fprintf(stdout, "MRAA: Invalid verbosity level.\n");
263             break;
264         case MRAA_ERROR_INVALID_PARAMETER:
265             fprintf(stdout, "MRAA: Invalid parameter.\n");
266             break;
267         case MRAA_ERROR_INVALID_HANDLE:
268             fprintf(stdout, "MRAA: Invalid Handle.\n");
269             break;
270         case MRAA_ERROR_NO_RESOURCES:
271             fprintf(stdout, "MRAA: No resources.\n");
272             break;
273         case MRAA_ERROR_INVALID_RESOURCE:
274             fprintf(stdout, "MRAA: Invalid resource.\n");
275             break;
276         case MRAA_ERROR_INVALID_QUEUE_TYPE:
277             fprintf(stdout, "MRAA: Invalid Queue Type.\n");
278             break;
279         case MRAA_ERROR_NO_DATA_AVAILABLE:
280             fprintf(stdout, "MRAA: No Data available.\n");
281             break;
282         case MRAA_ERROR_INVALID_PLATFORM:
283             fprintf(stdout, "MRAA: Platform not recognised.\n");
284             break;
285         case MRAA_ERROR_PLATFORM_NOT_INITIALISED:
286             fprintf(stdout, "MRAA: Platform not initialised.\n");
287             break;
288         case MRAA_ERROR_PLATFORM_ALREADY_INITIALISED:
289             fprintf(stdout, "MRAA: Platform already initialised.\n");
290             break;
291         case MRAA_ERROR_UNSPECIFIED:
292             fprintf(stdout, "MRAA: Unspecified Error.\n");
293             break;
294         default:
295             fprintf(stdout, "MRAA: Unrecognised error.\n");
296             break;
297     }
298 }
299
300
301 mraa_boolean_t
302 mraa_has_sub_platform()
303 {
304     return (plat != NULL) && (plat->sub_platform != NULL);
305 }
306
307 mraa_boolean_t
308 mraa_pin_mode_test(int pin, mraa_pinmodes_t mode)
309 {
310     if (plat == NULL)
311         return 0;
312
313     mraa_board_t* current_plat = plat;
314     if (mraa_is_sub_platform_id(pin)) {
315         current_plat = plat->sub_platform;
316         if (current_plat == NULL) {
317             syslog(LOG_ERR, "mraa_pin_mode_test: Sub platform Not Initialised");
318             return 0;
319         }
320         pin = mraa_get_sub_platform_index(pin);
321     }
322
323     if (current_plat == NULL || current_plat->platform_type == MRAA_UNKNOWN_PLATFORM) {
324         return 0;
325     }
326     if (pin > (current_plat->phy_pin_count - 1) || pin < 0)
327         return 0;
328
329     switch (mode) {
330         case MRAA_PIN_VALID:
331             if (current_plat->pins[pin].capabilites.valid == 1)
332                 return 1;
333             break;
334         case MRAA_PIN_GPIO:
335             if (current_plat->pins[pin].capabilites.gpio == 1)
336                 return 1;
337             break;
338         case MRAA_PIN_PWM:
339             if (current_plat->pins[pin].capabilites.pwm == 1)
340                 return 1;
341             break;
342         case MRAA_PIN_FAST_GPIO:
343             if (current_plat->pins[pin].capabilites.fast_gpio == 1)
344                 return 1;
345             break;
346         case MRAA_PIN_SPI:
347             if (current_plat->pins[pin].capabilites.spi == 1)
348                 return 1;
349             break;
350         case MRAA_PIN_I2C:
351             if (current_plat->pins[pin].capabilites.i2c == 1)
352                 return 1;
353             break;
354         case MRAA_PIN_AIO:
355             if (current_plat->pins[pin].capabilites.aio == 1)
356                 return 1;
357             break;
358         case MRAA_PIN_UART:
359             if (current_plat->pins[pin].capabilites.uart == 1)
360                 return 1;
361             break;
362         default:
363             syslog(LOG_NOTICE, "requested pinmode invalid");
364             break;
365     }
366     return 0;
367 }
368
369 mraa_platform_t
370 mraa_get_platform_type()
371 {
372     if (plat == NULL)
373         return MRAA_UNKNOWN_PLATFORM;
374     return plat->platform_type;
375 }
376
377 int
378 mraa_get_platform_combined_type()
379 {
380     int type = mraa_get_platform_type();
381     int sub_type = mraa_has_sub_platform() ? plat->sub_platform->platform_type : MRAA_UNKNOWN_PLATFORM;
382     return type | (sub_type << 8);
383 }
384
385 unsigned int
386 mraa_adc_raw_bits()
387 {
388     if (plat == NULL)
389         return 0;
390
391     if (plat->aio_count == 0)
392         return 0;
393
394     return plat->adc_raw;
395 }
396
397 unsigned int
398 mraa_get_platform_adc_raw_bits(uint8_t platform_offset)
399 {
400     if (platform_offset == MRAA_MAIN_PLATFORM_OFFSET)
401         return mraa_adc_raw_bits();
402     else {
403         if (!mraa_has_sub_platform())
404             return 0;
405
406         if (plat->sub_platform->aio_count == 0)
407             return 0;
408
409         return plat->sub_platform->adc_raw;
410     }
411 }
412
413
414 unsigned int
415 mraa_adc_supported_bits()
416 {
417     if (plat == NULL)
418         return 0;
419
420     if (plat->aio_count == 0)
421         return 0;
422
423     return plat->adc_supported;
424 }
425
426 unsigned int
427 mraa_get_platform_adc_supported_bits(int platform_offset)
428 {
429     if (platform_offset == MRAA_MAIN_PLATFORM_OFFSET)
430         return mraa_adc_supported_bits();
431     else {
432         if (!mraa_has_sub_platform())
433             return 0;
434
435         if (plat->sub_platform->aio_count == 0)
436             return 0;
437
438         return plat->sub_platform->adc_supported;
439     }
440 }
441
442
443 char*
444 mraa_get_platform_name()
445 {
446     if (plat == NULL) {
447         return NULL;
448     }
449     if (mraa_has_sub_platform()) {
450         snprintf(platform_name, MAX_PLATFORM_NAME_LENGTH, "%s + %s", plat->platform_name, plat->sub_platform->platform_name);
451     } else {
452         strncpy(platform_name, plat->platform_name, MAX_PLATFORM_NAME_LENGTH-1);
453     }
454
455     return platform_name;
456 }
457
458 int
459 mraa_get_i2c_bus_count()
460 {
461     if (plat == NULL) {
462         return -1;
463     }
464     return plat->i2c_bus_count;
465 }
466
467 int
468 mraa_get_i2c_bus_id(unsigned i2c_bus)
469 {
470     if (plat == NULL) {
471         return -1;
472     }
473
474     if (i2c_bus >= plat->i2c_bus_count) {
475         return -1;
476     }
477
478     return plat->i2c_bus[i2c_bus].bus_id;
479 }
480
481 unsigned int
482 mraa_get_pin_count()
483 {
484     if (plat == NULL) {
485         return 0;
486     }
487     return plat->phy_pin_count;
488 }
489
490 unsigned int
491 mraa_get_platform_pin_count(uint8_t platform_offset)
492 {
493     if (platform_offset == MRAA_MAIN_PLATFORM_OFFSET)
494         return mraa_get_pin_count();
495     else {
496         if (mraa_has_sub_platform())
497            return plat->sub_platform->phy_pin_count;
498         else
499            return 0;
500     }
501 }
502
503
504 char*
505 mraa_get_pin_name(int pin)
506 {
507     if (plat == NULL)
508         return 0;
509
510     mraa_board_t* current_plat = plat;
511     if (mraa_is_sub_platform_id(pin)) {
512         current_plat = plat->sub_platform;
513         if (current_plat == NULL) {
514             syslog(LOG_ERR, "mraa_get_pin_name: Sub platform Not Initialised");
515             return 0;
516         }
517         pin = mraa_get_sub_platform_index(pin);
518     }
519
520     if (pin > (current_plat->phy_pin_count - 1) || pin < 0)
521         return NULL;
522     return (char*) current_plat->pins[pin].name;
523 }
524
525 int
526 mraa_get_default_i2c_bus(uint8_t platform_offset)
527 {
528     if (plat == NULL)
529         return -1;
530     if (platform_offset == MRAA_MAIN_PLATFORM_OFFSET) {
531         return plat->def_i2c_bus;
532     } else {
533         if (mraa_has_sub_platform())
534            return plat->sub_platform->def_i2c_bus;
535         else
536            return -1;
537     }
538 }
539
540
541 mraa_boolean_t
542 mraa_file_exist(const char* filename)
543 {
544     glob_t results;
545     results.gl_pathc = 0;
546     glob(filename, 0, NULL, &results);
547     int file_found = results.gl_pathc == 1;
548     globfree(&results);
549     return file_found;
550 }
551
552 mraa_boolean_t
553 mraa_file_contains(const char* filename, const char* content)
554 {
555     mraa_boolean_t found = 0;
556     if ((filename == NULL) || (content == NULL)) {
557         return 0;
558     }
559
560     char* file = mraa_file_unglob(filename);
561     if (file != NULL) {
562         size_t len = 1024;
563         char* line = malloc(len);
564         if (line == NULL) {
565             free(file);
566             return 0;
567         }
568         FILE* fh = fopen(file, "r");
569         if (fh == NULL) {
570             free(file);
571             free(line);
572             return 0;
573         }
574         while ((getline(&line, &len, fh) != -1) && (found == 0)) {
575             if (strstr(line, content)) {
576                 found = 1;
577                 break;
578             }
579         }
580         fclose(fh);
581         free(file);
582         free(line);
583     }
584     return found;
585 }
586
587 mraa_boolean_t
588 mraa_file_contains_both(const char* filename, const char* content, const char* content2)
589 {
590     mraa_boolean_t found = 0;
591     if ((filename == NULL) || (content == NULL)) {
592         return 0;
593     }
594
595     char* file = mraa_file_unglob(filename);
596     if (file != NULL) {
597         size_t len = 1024;
598         char* line = malloc(len);
599         if (line == NULL) {
600             free(file);
601             return 0;
602         }
603         FILE* fh = fopen(file, "r");
604         if (fh == NULL) {
605             free(file);
606             free(line);
607             return 0;
608         }
609         while ((getline(&line, &len, fh) != -1) && (found == 0)) {
610             if (strstr(line, content) && strstr(line, content2)) {
611                 found = 1;
612                 break;
613             }
614         }
615         fclose(fh);
616         free(file);
617         free(line);
618     }
619     return found;
620 }
621
622 char*
623 mraa_file_unglob(const char* filename)
624 {
625     glob_t results;
626     char* res = NULL;
627     results.gl_pathc = 0;
628     glob(filename, 0, NULL, &results);
629     if (results.gl_pathc == 1)
630         res = strdup(results.gl_pathv[0]);
631     globfree(&results);
632     return res;
633 }
634
635 mraa_boolean_t
636 mraa_link_targets(const char* filename, const char* targetname)
637 {
638     int size = 100;
639     int nchars = 0;
640     char* buffer = NULL;
641     while (nchars == 0) {
642         buffer = (char*) realloc(buffer, size);
643         if (buffer == NULL)
644             return 0;
645         nchars = readlink(filename, buffer, size);
646         if (nchars < 0) {
647             free(buffer);
648             return 0;
649         } else {
650             buffer[nchars] = '\0';
651         }
652         if (nchars >= size) {
653             size *= 2;
654             nchars = 0;
655         }
656     }
657     if (strstr(buffer, targetname)) {
658         free(buffer);
659         return 1;
660     } else {
661         free(buffer);
662         return 0;
663     }
664 }
665
666 static int
667 mraa_count_i2c_files(const char* path, const struct stat* sb, int flag, struct FTW* ftwb)
668 {
669     switch (sb->st_mode & S_IFMT) {
670         case S_IFLNK:
671             num_i2c_devices++;
672             break;
673     }
674     return 0;
675 }
676
677 int
678 mraa_find_i2c_bus(const char* devname, int startfrom)
679 {
680     char path[64];
681     int fd;
682     int i = startfrom;
683     int ret = -1;
684
685     // because feeding mraa_find_i2c_bus result back into the function is
686     // useful treat -1 as 0
687     if (startfrom < 0) {
688         startfrom = 0;
689     }
690
691     // find how many i2c buses we have if we haven't already
692     if (num_i2c_devices == 0) {
693         if (nftw("/sys/class/i2c-dev/", &mraa_count_i2c_files, 20, FTW_PHYS) == -1) {
694             return -1;
695         }
696     }
697
698     // i2c devices are numbered numerically so 0 must exist otherwise there is
699     // no i2c-dev loaded
700     if (mraa_file_exist("/sys/class/i2c-dev/i2c-0")) {
701         for (i; i < num_i2c_devices; i++) {
702             off_t size, err;
703             snprintf(path, 64, "/sys/class/i2c-dev/i2c-%u/name", i);
704             fd = open(path, O_RDONLY);
705             if (fd < 0) {
706                 break;
707             }
708             size = lseek(fd, 0, SEEK_END);
709             if (size < 0) {
710                 syslog(LOG_WARNING, "mraa: failed to seek i2c filename file");
711                 close(fd);
712                 break;
713             }
714             err = lseek(fd, 0, SEEK_SET);
715             if (err < 0) {
716                 syslog(LOG_WARNING, "mraa: failed to seek i2c filename file");
717                 close(fd);
718                 break;
719             }
720             char* value = malloc(size);
721             if (value == NULL) {
722                 syslog(LOG_ERR, "mraa: failed to allocate memory for i2c file");
723                 close(fd);
724                 break;
725             }
726             ssize_t r = read(fd, value, size);
727             if (r > 0) {
728                 if (strcasestr(value, devname) != NULL) {
729                     free(value);
730                     close(fd);
731                     return i;
732                 }
733             } else {
734                 syslog(LOG_ERR, "mraa: sysfs i2cdev failed");
735             }
736             free(value);
737             close(fd);
738         }
739     } else {
740         syslog(LOG_WARNING, "mraa: no i2c-dev detected, load i2c-dev");
741     }
742
743     return ret;
744 }
745
746 mraa_boolean_t
747 mraa_is_sub_platform_id(int pin_or_bus)
748 {
749     return (pin_or_bus & MRAA_SUB_PLATFORM_MASK) != 0;
750 }
751
752 int
753 mraa_get_sub_platform_id(int pin_or_bus)
754 {
755     return pin_or_bus | MRAA_SUB_PLATFORM_MASK;
756 }
757
758 int
759 mraa_get_sub_platform_index(int pin_or_bus)
760 {
761     return pin_or_bus & (~MRAA_SUB_PLATFORM_MASK);
762 }
763
764 int
765 mraa_get_iio_device_count()
766 {
767     return plat->iio_device_count;
768 }
769
770 int
771 mraa_find_iio_device(const char* devicename)
772 {
773     int i = 0;
774     for (i; i < plat->iio_device_count; i++) {
775 #if 0
776         // compare with devices array
777         if (!strcmp() {
778         }
779 #endif
780     }
781     return 0;
782 }