gpio.c: Fix interrupt callback args
[contrib/mraa.git] / src / gpio / gpio.c
1 /*
2  * Author: Thomas Ingleby <thomas.c.ingleby@intel.com>
3  * Author: Brendan Le Foll <brendan.le.foll@intel.com>
4  * Copyright (c) 2014, 2015 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 #include "gpio.h"
26 #include "mraa_internal.h"
27
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <poll.h>
33 #include <pthread.h>
34 #include <signal.h>
35 #include <sys/stat.h>
36 #include <sys/mman.h>
37
38 #define SYSFS_CLASS_GPIO "/sys/class/gpio"
39 #define MAX_SIZE 64
40 #define POLL_TIMEOUT
41
42 static mraa_result_t
43 mraa_gpio_get_valfp(mraa_gpio_context dev)
44 {
45     char bu[MAX_SIZE];
46     sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
47     dev->value_fp = open(bu, O_RDWR);
48     if (dev->value_fp == -1) {
49         return MRAA_ERROR_INVALID_RESOURCE;
50     }
51
52     return MRAA_SUCCESS;
53 }
54
55 static mraa_gpio_context
56 mraa_gpio_init_internal(mraa_adv_func_t* func_table, int pin)
57 {
58     if (pin < 0)
59         return NULL;
60
61     mraa_result_t status = MRAA_SUCCESS;
62     char bu[MAX_SIZE];
63     int length;
64
65     mraa_gpio_context dev = (mraa_gpio_context) calloc(1, sizeof(struct _gpio));
66     if (dev == NULL) {
67         syslog(LOG_CRIT, "gpio: Failed to allocate memory for context");
68         return NULL;
69     }
70
71     dev->advance_func = func_table;
72     dev->pin = pin;
73
74     if (IS_FUNC_DEFINED(dev, gpio_init_internal_replace)) {
75         status = dev->advance_func->gpio_init_internal_replace(pin);
76         if (status == MRAA_SUCCESS)
77             return dev;
78         else
79             goto init_internal_cleanup;
80     }
81
82     if (IS_FUNC_DEFINED(dev, gpio_init_pre)) {
83         status = dev->advance_func->gpio_init_pre(pin);
84         if (status != MRAA_SUCCESS)
85             goto init_internal_cleanup;
86     }
87
88     dev->value_fp = -1;
89     dev->isr_value_fp = -1;
90     dev->isr_thread_terminating = 0;
91     dev->phy_pin = -1;
92
93     // then check to make sure the pin is exported.
94     char directory[MAX_SIZE];
95     snprintf(directory, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/", dev->pin);
96     struct stat dir;
97     if (stat(directory, &dir) == 0 && S_ISDIR(dir.st_mode)) {
98         dev->owner = 0; // Not Owner
99     } else {
100         int export = open(SYSFS_CLASS_GPIO "/export", O_WRONLY);
101         if (export == -1) {
102             syslog(LOG_ERR, "gpio: Failed to open export for writing");
103             status = MRAA_ERROR_NO_RESOURCES;
104             goto init_internal_cleanup;
105         }
106         length = snprintf(bu, sizeof(bu), "%d", dev->pin);
107         if (write(export, bu, length * sizeof(char)) == -1) {
108             syslog(LOG_ERR, "gpio: Failed to write %d to export", dev->pin);
109             close(export);
110             status = MRAA_ERROR_NO_RESOURCES;
111             goto init_internal_cleanup;
112         }
113         dev->owner = 1;
114         close(export);
115     }
116
117 init_internal_cleanup:
118     if (status != MRAA_SUCCESS) {
119         if (dev != NULL)
120             free(dev);
121         return NULL;
122     }
123     return dev;
124 }
125
126 mraa_gpio_context
127 mraa_gpio_init(int pin)
128 {
129     mraa_board_t* board = plat;
130     if (board == NULL) {
131         syslog(LOG_ERR, "gpio: platform not initialised");
132         return NULL;
133     }
134
135     if (mraa_is_sub_platform_id(pin)) {
136         syslog(LOG_NOTICE, "gpio: Using sub platform");
137         board = board->sub_platform;
138         if (board == NULL) {
139             syslog(LOG_ERR, "gpio: Sub platform Not Initialised");
140             return NULL;
141         }
142         pin = mraa_get_sub_platform_index(pin);
143     }
144
145     if (pin < 0 || pin > board->phy_pin_count) {
146         syslog(LOG_ERR, "gpio: pin %i beyond platform definition", pin);
147         return NULL;
148     }
149     if (board->pins[pin].capabilites.gpio != 1) {
150         syslog(LOG_ERR, "gpio: pin %i not capable of gpio", pin);
151         return NULL;
152     }
153     if (board->pins[pin].gpio.mux_total > 0) {
154         if (mraa_setup_mux_mapped(board->pins[pin].gpio) != MRAA_SUCCESS) {
155             syslog(LOG_ERR, "gpio: unable to setup muxes");
156             return NULL;
157         }
158     }
159
160     mraa_gpio_context r = mraa_gpio_init_internal(board->adv_func, board->pins[pin].gpio.pinmap);
161     if (r == NULL) {
162         syslog(LOG_CRIT, "gpio: mraa_gpio_init_raw(%d) returned error", pin);
163         return NULL;
164     }
165     r->phy_pin = pin;
166
167     if (IS_FUNC_DEFINED(r, gpio_init_post)) {
168         mraa_result_t ret = r->advance_func->gpio_init_post(r);
169         if (ret != MRAA_SUCCESS) {
170             free(r);
171             return NULL;
172         }
173     }
174     return r;
175 }
176
177 mraa_gpio_context
178 mraa_gpio_init_raw(int pin)
179 {
180     return mraa_gpio_init_internal(plat == NULL ? NULL : plat->adv_func , pin);
181 }
182
183
184 static mraa_result_t
185 mraa_gpio_wait_interrupt(int fd)
186 {
187     unsigned char c;
188     struct pollfd pfd;
189
190     if (fd < 0) {
191         return MRAA_ERROR_INVALID_RESOURCE;
192     }
193
194     // setup poll on POLLPRI
195     pfd.fd = fd;
196     pfd.events = POLLPRI;
197
198     // do an initial read to clear interrupt
199     lseek(fd, 0, SEEK_SET);
200     read(fd, &c, 1);
201
202     // Wait for it forever or until pthread_cancel
203     // poll is a cancelable point like sleep()
204     int x = poll(&pfd, 1, -1);
205
206     // do a final read to clear interrupt
207     read(fd, &c, 1);
208
209     return MRAA_SUCCESS;
210 }
211
212 static void*
213 mraa_gpio_interrupt_handler(void* arg)
214 {
215     mraa_gpio_context dev = (mraa_gpio_context) arg;
216     if (IS_FUNC_DEFINED(dev, gpio_interrupt_handler_replace))
217         return dev->advance_func->gpio_interrupt_handler_replace(dev);
218
219     mraa_result_t ret;
220
221     // open gpio value with open(3)
222     char bu[MAX_SIZE];
223     sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
224     int fp = open(bu, O_RDONLY);
225     if (fp < 0) {
226         syslog(LOG_ERR, "gpio: failed to open gpio%d/value", dev->pin);
227         return NULL;
228     }
229     dev->isr_value_fp = fp;
230
231     for (;;) {
232         ret = mraa_gpio_wait_interrupt(dev->isr_value_fp);
233         if (ret == MRAA_SUCCESS && !dev->isr_thread_terminating) {
234             pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
235 #ifdef SWIGPYTHON
236             // In order to call a python object (all python functions are objects) we
237             // need to aquire the GIL (Global Interpreter Lock). This may not always be
238             // necessary but especially if doing IO (like print()) python will segfault
239             // if we do not hold a lock on the GIL
240             PyGILState_STATE gilstate = PyGILState_Ensure();
241             PyObject* arglist;
242             PyObject* ret;
243             arglist = Py_BuildValue("(O)", dev->isr_args);
244             if (arglist == NULL) {
245                 syslog(LOG_ERR, "gpio: Py_BuildValue NULL");
246             } else {
247                 ret = PyEval_CallObject((PyObject*) dev->isr, arglist);
248                 if (ret == NULL) {
249 // code is python2 only
250 #if PY_VERSION_HEX < 0x0300000
251                     syslog(LOG_ERR, "gpio: PyEval_CallObject failed");
252                     PyObject *pvalue, *ptype, *ptraceback;
253                     PyErr_Fetch(&pvalue, &ptype, &ptraceback);
254                     syslog(LOG_ERR, "gpio: the error was %s:%s:%s",
255                            PyString_AsString(PyObject_Str(pvalue)),
256                            PyString_AsString(PyObject_Str(ptype)),
257                            PyString_AsString(PyObject_Str(ptraceback))
258                     );
259                     Py_XDECREF(pvalue);
260                     Py_XDECREF(ptype);
261                     Py_XDECREF(ptraceback);
262 #endif
263                 } else {
264                     Py_DECREF(ret);
265                 }
266                 Py_DECREF(arglist);
267             }
268
269             PyGILState_Release(gilstate);
270 #else
271             dev->isr(dev->isr_args);
272 #endif
273             pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
274         } else {
275             // we must have got an error code or exit request so die nicely
276             pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
277             close(dev->isr_value_fp);
278             dev->isr_value_fp = -1;
279             return NULL;
280         }
281     }
282 }
283
284 mraa_result_t
285 mraa_gpio_edge_mode(mraa_gpio_context dev, mraa_gpio_edge_t mode)
286 {
287     if (IS_FUNC_DEFINED(dev, gpio_edge_mode_replace))
288         return dev->advance_func->gpio_edge_mode_replace(dev, mode);
289
290     if (dev->value_fp != -1) {
291         close(dev->value_fp);
292         dev->value_fp = -1;
293     }
294
295     char filepath[MAX_SIZE];
296     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/edge", dev->pin);
297
298     int edge = open(filepath, O_RDWR);
299     if (edge == -1) {
300         syslog(LOG_ERR, "gpio: Failed to open edge for writing");
301         return MRAA_ERROR_INVALID_RESOURCE;
302     }
303
304     char bu[MAX_SIZE];
305     int length;
306     switch (mode) {
307         case MRAA_GPIO_EDGE_NONE:
308             length = snprintf(bu, sizeof(bu), "none");
309             break;
310         case MRAA_GPIO_EDGE_BOTH:
311             length = snprintf(bu, sizeof(bu), "both");
312             break;
313         case MRAA_GPIO_EDGE_RISING:
314             length = snprintf(bu, sizeof(bu), "rising");
315             break;
316         case MRAA_GPIO_EDGE_FALLING:
317             length = snprintf(bu, sizeof(bu), "falling");
318             break;
319         default:
320             close(edge);
321             return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED;
322     }
323     if (write(edge, bu, length * sizeof(char)) == -1) {
324         syslog(LOG_ERR, "gpio: Failed to write to edge");
325         close(edge);
326         return MRAA_ERROR_INVALID_RESOURCE;
327     }
328
329     close(edge);
330     return MRAA_SUCCESS;
331 }
332
333 mraa_result_t
334 mraa_gpio_isr(mraa_gpio_context dev, mraa_gpio_edge_t mode, void (*fptr)(void*), void* args)
335 {
336     // we only allow one isr per mraa_gpio_context
337     if (dev->thread_id != 0) {
338         return MRAA_ERROR_NO_RESOURCES;
339     }
340
341     if (MRAA_SUCCESS != mraa_gpio_edge_mode(dev, mode)) {
342         return MRAA_ERROR_UNSPECIFIED;
343     }
344
345     dev->isr = fptr;
346     dev->isr_args = args;
347     pthread_create(&dev->thread_id, NULL, mraa_gpio_interrupt_handler, (void*) dev);
348
349     return MRAA_SUCCESS;
350 }
351
352 mraa_result_t
353 mraa_gpio_isr_exit(mraa_gpio_context dev)
354 {
355     mraa_result_t ret = MRAA_SUCCESS;
356
357     // wasting our time, there is no isr to exit from
358     if (dev->thread_id == 0 && dev->isr_value_fp == -1) {
359         return ret;
360     }
361     // mark the beginning of the thread termination process for interested parties
362     dev->isr_thread_terminating = 1;
363
364     // stop isr being useful
365     ret = mraa_gpio_edge_mode(dev, MRAA_GPIO_EDGE_NONE);
366
367     if ((dev->thread_id != 0)) {
368         if ((pthread_cancel(dev->thread_id) != 0) || (pthread_join(dev->thread_id, NULL) != 0)) {
369             ret = MRAA_ERROR_INVALID_HANDLE;
370         }
371     }
372
373     // close the filehandle in case it's still open
374     if (dev->isr_value_fp != -1) {
375         if (close(dev->isr_value_fp) != 0) {
376             ret = MRAA_ERROR_INVALID_PARAMETER;
377         }
378     }
379
380     // assume our thread will exit either way we just lost it's handle
381     dev->thread_id = 0;
382     dev->isr_value_fp = -1;
383     dev->isr_thread_terminating = 0;
384     return ret;
385 }
386
387 mraa_result_t
388 mraa_gpio_mode(mraa_gpio_context dev, mraa_gpio_mode_t mode)
389 {
390     if (IS_FUNC_DEFINED(dev, gpio_mode_replace))
391         return dev->advance_func->gpio_mode_replace(dev, mode);
392
393     if (IS_FUNC_DEFINED(dev, gpio_mode_pre)) {
394         mraa_result_t pre_ret = (dev->advance_func->gpio_mode_pre(dev, mode));
395         if (pre_ret != MRAA_SUCCESS)
396             return pre_ret;
397     }
398
399     if (dev->value_fp != -1) {
400         close(dev->value_fp);
401         dev->value_fp = -1;
402     }
403
404     char filepath[MAX_SIZE];
405     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/drive", dev->pin);
406
407     int drive = open(filepath, O_WRONLY);
408     if (drive == -1) {
409         syslog(LOG_ERR, "gpio: Failed to open drive for writing");
410         return MRAA_ERROR_INVALID_RESOURCE;
411     }
412
413     char bu[MAX_SIZE];
414     int length;
415     switch (mode) {
416         case MRAA_GPIO_STRONG:
417             length = snprintf(bu, sizeof(bu), "strong");
418             break;
419         case MRAA_GPIO_PULLUP:
420             length = snprintf(bu, sizeof(bu), "pullup");
421             break;
422         case MRAA_GPIO_PULLDOWN:
423             length = snprintf(bu, sizeof(bu), "pulldown");
424             break;
425         case MRAA_GPIO_HIZ:
426             length = snprintf(bu, sizeof(bu), "hiz");
427             break;
428         default:
429             close(drive);
430             return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED;
431     }
432     if (write(drive, bu, length * sizeof(char)) == -1) {
433         syslog(LOG_ERR, "gpio: Failed to write to drive mode");
434         close(drive);
435         return MRAA_ERROR_INVALID_RESOURCE;
436     }
437
438     close(drive);
439     if (IS_FUNC_DEFINED(dev, gpio_mode_post))
440         return dev->advance_func->gpio_mode_post(dev, mode);
441     return MRAA_SUCCESS;
442 }
443
444 mraa_result_t
445 mraa_gpio_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir)
446 {
447     if (IS_FUNC_DEFINED(dev, gpio_dir_replace)) {
448         return dev->advance_func->gpio_dir_replace(dev, dir);
449     }
450
451     if (IS_FUNC_DEFINED(dev, gpio_dir_pre)) {
452         mraa_result_t pre_ret = (dev->advance_func->gpio_dir_pre(dev, dir));
453         if (pre_ret != MRAA_SUCCESS) {
454             return pre_ret;
455         }
456     }
457
458     if (dev == NULL) {
459         return MRAA_ERROR_INVALID_HANDLE;
460     }
461     if (dev->value_fp != -1) {
462         close(dev->value_fp);
463         dev->value_fp = -1;
464     }
465     char filepath[MAX_SIZE];
466     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/direction", dev->pin);
467
468     int direction = open(filepath, O_RDWR);
469
470     if (direction == -1) {
471         // Direction Failed to Open. If HIGH or LOW was passed will try and set
472         // If not fail as usual.
473         switch (dir) {
474             case MRAA_GPIO_OUT_HIGH:
475                 return mraa_gpio_write(dev, 1);
476             case MRAA_GPIO_OUT_LOW:
477                 return mraa_gpio_write(dev, 0);
478             default:
479                 return MRAA_ERROR_INVALID_RESOURCE;
480         }
481     }
482
483     char bu[MAX_SIZE];
484     int length;
485     switch (dir) {
486         case MRAA_GPIO_OUT:
487             length = snprintf(bu, sizeof(bu), "out");
488             break;
489         case MRAA_GPIO_IN:
490             length = snprintf(bu, sizeof(bu), "in");
491             break;
492         case MRAA_GPIO_OUT_HIGH:
493             length = snprintf(bu, sizeof(bu), "high");
494             break;
495         case MRAA_GPIO_OUT_LOW:
496             length = snprintf(bu, sizeof(bu), "low");
497             break;
498         default:
499             close(direction);
500             return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED;
501     }
502
503     if (write(direction, bu, length * sizeof(char)) == -1) {
504         close(direction);
505         return MRAA_ERROR_INVALID_RESOURCE;
506     }
507
508     close(direction);
509     if (IS_FUNC_DEFINED(dev, gpio_dir_post))
510         return dev->advance_func->gpio_dir_post(dev, dir);
511     return MRAA_SUCCESS;
512 }
513
514 int
515 mraa_gpio_read(mraa_gpio_context dev)
516 {
517     if (dev == NULL)
518         return -1;
519
520     if (IS_FUNC_DEFINED(dev, gpio_read_replace))
521         return dev->advance_func->gpio_read_replace(dev);
522
523     if (dev->mmap_read != NULL)
524         return dev->mmap_read(dev);
525
526     if (dev->value_fp == -1) {
527         if (mraa_gpio_get_valfp(dev) != MRAA_SUCCESS) {
528             syslog(LOG_ERR, "gpio: Failed to get value file pointer");
529             return -1;
530         }
531     } else {
532         // if value_fp is new this is pointless
533         lseek(dev->value_fp, 0, SEEK_SET);
534     }
535     char bu[2];
536     if (read(dev->value_fp, bu, 2 * sizeof(char)) != 2) {
537         syslog(LOG_ERR, "gpio: Failed to read a sensible value from sysfs");
538         return -1;
539     }
540     lseek(dev->value_fp, 0, SEEK_SET);
541
542     return (int) strtol(bu, NULL, 10);
543 }
544
545 mraa_result_t
546 mraa_gpio_write(mraa_gpio_context dev, int value)
547 {
548     if (dev == NULL)
549         return MRAA_ERROR_INVALID_HANDLE;
550
551     if (dev->mmap_write != NULL)
552         return dev->mmap_write(dev, value);
553
554     if (IS_FUNC_DEFINED(dev, gpio_write_pre)) {
555         mraa_result_t pre_ret = (dev->advance_func->gpio_write_pre(dev, value));
556         if (pre_ret != MRAA_SUCCESS)
557             return pre_ret;
558     }
559
560     if (dev->value_fp == -1) {
561         if (mraa_gpio_get_valfp(dev) != MRAA_SUCCESS) {
562             return MRAA_ERROR_INVALID_RESOURCE;
563         }
564     }
565
566     if (lseek(dev->value_fp, 0, SEEK_SET) == -1) {
567         return MRAA_ERROR_INVALID_RESOURCE;
568     }
569
570     char bu[MAX_SIZE];
571     int length = snprintf(bu, sizeof(bu), "%d", value);
572     if (write(dev->value_fp, bu, length * sizeof(char)) == -1) {
573         return MRAA_ERROR_INVALID_HANDLE;
574     }
575
576     if (IS_FUNC_DEFINED(dev, gpio_write_post))
577         return dev->advance_func->gpio_write_post(dev, value);
578     return MRAA_SUCCESS;
579 }
580
581 static mraa_result_t
582 mraa_gpio_unexport_force(mraa_gpio_context dev)
583 {
584     int unexport = open(SYSFS_CLASS_GPIO "/unexport", O_WRONLY);
585     if (unexport == -1) {
586         syslog(LOG_ERR, "gpio: Failed to open unexport for writing");
587         return MRAA_ERROR_INVALID_RESOURCE;
588     }
589
590     char bu[MAX_SIZE];
591     int length = snprintf(bu, sizeof(bu), "%d", dev->pin);
592     if (write(unexport, bu, length * sizeof(char)) == -1) {
593         syslog(LOG_ERR, "gpio: Failed to write to unexport");
594         close(unexport);
595         return MRAA_ERROR_INVALID_RESOURCE;
596     }
597
598     close(unexport);
599     mraa_gpio_isr_exit(dev);
600     return MRAA_SUCCESS;
601 }
602 static mraa_result_t
603 mraa_gpio_unexport(mraa_gpio_context dev)
604 {
605     if (dev->owner) {
606         return mraa_gpio_unexport_force(dev);
607     }
608     return MRAA_ERROR_INVALID_RESOURCE;
609 }
610
611 mraa_result_t
612 mraa_gpio_close(mraa_gpio_context dev)
613 {
614     mraa_result_t result = MRAA_SUCCESS;
615
616     if (IS_FUNC_DEFINED(dev, gpio_close_pre)) {
617         result = dev->advance_func->gpio_close_pre(dev);
618     }
619
620     if (dev->value_fp != -1) {
621         close(dev->value_fp);
622     }
623     mraa_gpio_unexport(dev);
624     free(dev);
625     return result;
626 }
627
628 mraa_result_t
629 mraa_gpio_owner(mraa_gpio_context dev, mraa_boolean_t own)
630 {
631     if (dev == NULL) {
632         return MRAA_ERROR_INVALID_RESOURCE;
633     }
634     syslog(LOG_DEBUG, "gpio: Set owner to %d", (int) own);
635     dev->owner = own;
636     return MRAA_SUCCESS;
637 }
638
639 mraa_result_t
640 mraa_gpio_use_mmaped(mraa_gpio_context dev, mraa_boolean_t mmap_en)
641 {
642     if (IS_FUNC_DEFINED(dev, gpio_mmap_setup)) {
643         return dev->advance_func->gpio_mmap_setup(dev, mmap_en);
644     }
645
646     syslog(LOG_ERR, "gpio: mmap not implemented on this platform");
647     return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED;
648 }
649
650 int
651 mraa_gpio_get_pin(mraa_gpio_context dev)
652 {
653     if (dev == NULL) {
654         syslog(LOG_ERR, "gpio: context is invalid");
655         return -1;
656     }
657     return dev->phy_pin;
658 }
659
660 int
661 mraa_gpio_get_pin_raw(mraa_gpio_context dev)
662 {
663     if (dev == NULL) {
664         syslog(LOG_ERR, "gpio: context is invalid");
665         return -1;
666     }
667     return dev->pin;
668 }