gpio: make isr_exit function use pthread_cancel and treat error cases more cleanly
[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 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
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <poll.h>
32 #include <pthread.h>
33 #include <signal.h>
34
35 #define SYSFS_CLASS_GPIO "/sys/class/gpio"
36 #define MAX_SIZE 64
37 #define POLL_TIMEOUT
38
39 static maa_result_t
40 maa_gpio_get_valfp(maa_gpio_context *dev)
41 {
42     char bu[MAX_SIZE];
43     sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
44
45     dev->value_fp = open(bu, O_RDWR);
46     if (dev->value_fp == -1) {
47         return MAA_ERROR_INVALID_RESOURCE;
48     }
49
50     return MAA_SUCCESS;
51 }
52
53 maa_gpio_context*
54 maa_gpio_init(int pin)
55 {
56     int pinm = maa_check_gpio(pin);
57     if (pinm < 0)
58         return NULL;
59
60     return maa_gpio_init_raw(pinm);
61 }
62
63 maa_gpio_context*
64 maa_gpio_init_raw(int pin)
65 {
66     if (pin < 0)
67         return NULL;
68
69     FILE *export_f;
70     char bu[MAX_SIZE];
71     int length;
72
73     maa_gpio_context* dev = (maa_gpio_context*) malloc(sizeof(maa_gpio_context));
74     memset(dev, 0, sizeof(maa_gpio_context));
75     dev->value_fp = -1;
76     dev->isr_value_fp = -1;
77     dev->pin = pin;
78
79     if ((export_f = fopen(SYSFS_CLASS_GPIO "/export", "w")) == NULL) {
80         fprintf(stderr, "Failed to open export for writing!\n");
81         return NULL;
82     }
83     length = snprintf(bu, sizeof(bu), "%d", dev->pin);
84     fwrite(bu, sizeof(char), length, export_f);
85
86     fclose(export_f);
87     return dev;
88 }
89
90 static maa_result_t
91 maa_gpio_wait_interrupt(int fd)
92 {
93     unsigned char c;
94     struct pollfd pfd;
95
96     // setup poll on POLLPRI
97     pfd.fd = fd;
98     pfd.events = POLLPRI;
99
100     // do an initial read to clear interupt
101     read (fd, &c, 1);
102
103     if (fd <= 0) {
104         return MAA_ERROR_INVALID_RESOURCE;
105     }
106
107     // Wait for it forever or until pthread_cancel
108     // poll is a cancelable point like sleep()
109     int x = poll (&pfd, 1, -1);
110
111     // do a final read to clear interupt
112     read (fd, &c, 1);
113
114     return MAA_SUCCESS;
115 }
116
117 static void*
118 maa_gpio_interrupt_handler(void* arg)
119 {
120     maa_gpio_context* dev = (maa_gpio_context*) arg;
121     maa_result_t ret;
122
123     // open gpio value with open(3)
124     char bu[MAX_SIZE];
125     sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
126     dev->isr_value_fp = open(bu, O_RDONLY);
127
128     for (;;) {
129         ret = maa_gpio_wait_interrupt(dev->isr_value_fp);
130         if (ret == MAA_SUCCESS) {
131             pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
132 #ifdef SWIGPYTHON
133             // In order to call a python object (all python functions are objects) we
134             // need to aquire the GIL (Global Interpreter Lock). This may not always be
135             // nessecary but especially if doing IO (like print()) python will segfault
136             // if we do not hold a lock on the GIL
137             PyGILState_STATE gilstate = PyGILState_Ensure();
138
139             PyEval_CallObject(dev->isr, NULL);
140
141             PyGILState_Release (gilstate);
142 #else
143             dev->isr();
144 #endif
145             pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
146         } else {
147             // we must have got an error code so die nicely
148             pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
149             close(dev->isr_value_fp);
150             dev->isr_value_fp = -1;
151             return NULL;
152         }
153     }
154 }
155
156 maa_result_t
157 maa_gpio_edge_mode(maa_gpio_context *dev, gpio_edge_t mode)
158 {
159     if (dev->value_fp != -1) {
160          close(dev->value_fp);
161          dev->value_fp = -1;
162     }
163
164     char filepath[MAX_SIZE];
165     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/edge", dev->pin);
166
167     FILE *edge;
168     if ((edge= fopen(filepath, "w")) == NULL) {
169         fprintf(stderr, "Failed to open edge for writing!\n");
170         return MAA_ERROR_INVALID_RESOURCE;
171     }
172
173     char bu[MAX_SIZE];
174     int length;
175     switch(mode) {
176         case MAA_GPIO_EDGE_NONE:
177             length = snprintf(bu, sizeof(bu), "none");
178             break;
179         case MAA_GPIO_EDGE_BOTH:
180             length = snprintf(bu, sizeof(bu), "both");
181             break;
182         case MAA_GPIO_EDGE_RISING:
183             length = snprintf(bu, sizeof(bu), "rising");
184             break;
185         case MAA_GPIO_EDGE_FALLING:
186             length = snprintf(bu, sizeof(bu), "falling");
187             break;
188         default:
189             fclose(edge);
190             return MAA_ERROR_FEATURE_NOT_IMPLEMENTED;
191     }
192     fwrite(bu, sizeof(char), length, edge);
193
194     fclose(edge);
195     return MAA_SUCCESS;
196 }
197
198 maa_result_t
199 maa_gpio_isr(maa_gpio_context *dev, gpio_edge_t mode, void (*fptr)(void))
200 {
201     // we only allow one isr per maa_gpio_context
202     if (dev->thread_id != 0) {
203         return MAA_ERROR_NO_RESOURCES;
204     }
205
206     maa_gpio_edge_mode(dev, mode);
207     dev->isr = fptr;
208     pthread_create (&dev->thread_id, NULL, maa_gpio_interrupt_handler, (void *) dev);
209
210     return MAA_SUCCESS;
211 }
212
213 maa_result_t
214 maa_gpio_isr_exit(maa_gpio_context *dev)
215 {
216     maa_result_t ret = MAA_SUCCESS;
217
218     // wasting our time, there is no isr to exit from
219     if (dev->thread_id == 0 && dev->isr_value_fp == -1) {
220         return ret;
221     }
222
223     // stop isr being useful
224     ret = maa_gpio_edge_mode(dev, MAA_GPIO_EDGE_NONE);
225
226     if ((dev->thread_id != 0) &&
227         (pthread_cancel(dev->thread_id) != 0)) {
228         ret = MAA_ERROR_INVALID_HANDLE;
229     }
230
231     // close the filehandle in case it's still open
232     if (dev->isr_value_fp != -1) {
233           if (close(dev->isr_value_fp) != 0) {
234               ret = MAA_ERROR_INVALID_PARAMETER;
235           }
236     }
237
238 #ifdef SWIGPYTHON
239     // Dereference our Python call back function
240     Py_DECREF(dev->isr);
241 #endif
242
243     // assume our thread will exit either way we just lost it's handle
244     dev->thread_id = 0;
245     dev->isr_value_fp = -1;
246
247     return ret;
248 }
249
250 maa_result_t
251 maa_gpio_mode(maa_gpio_context *dev, gpio_mode_t mode)
252 {
253     if (dev->value_fp != -1) {
254          close(dev->value_fp);
255          dev->value_fp = -1;
256     }
257
258     char filepath[MAX_SIZE];
259     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/drive", dev->pin);
260
261     FILE *drive;
262     if ((drive = fopen(filepath, "w")) == NULL) {
263         fprintf(stderr, "Failed to open drive for writing!\n");
264         return MAA_ERROR_INVALID_RESOURCE;
265     }
266
267     char bu[MAX_SIZE];
268     int length;
269     switch(mode) {
270         case MAA_GPIO_STRONG:
271             length = snprintf(bu, sizeof(bu), "strong");
272             break;
273         case MAA_GPIO_PULLUP:
274             length = snprintf(bu, sizeof(bu), "pullup");
275             break;
276         case MAA_GPIO_PULLDOWN:
277             length = snprintf(bu, sizeof(bu), "pulldown");
278             break;
279         case MAA_GPIO_HIZ:
280             length = snprintf(bu, sizeof(bu), "hiz");
281             break;
282         default:
283             fclose(drive);
284             return MAA_ERROR_FEATURE_NOT_IMPLEMENTED;
285     }
286     fwrite(bu, sizeof(char), length, drive);
287
288     fclose(drive);
289     return MAA_SUCCESS;
290 }
291
292 maa_result_t
293 maa_gpio_dir(maa_gpio_context *dev, gpio_dir_t dir)
294 {
295     if (dev == NULL) {
296         return MAA_ERROR_INVALID_HANDLE;
297     }
298     if (dev->value_fp != -1) {
299          close(dev->value_fp);
300          dev->value_fp = -1;
301     }
302     char filepath[MAX_SIZE];
303     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/direction", dev->pin);
304
305     FILE *direction;
306     if ((direction = fopen(filepath, "w")) == NULL) {
307         fprintf(stderr, "Failed to open direction for writing!\n");
308         return MAA_ERROR_INVALID_RESOURCE;
309     }
310
311     char bu[MAX_SIZE];
312     int length;
313     switch(dir) {
314         case MAA_GPIO_OUT:
315             length = snprintf(bu, sizeof(bu), "out");
316             break;
317         case MAA_GPIO_IN:
318             length = snprintf(bu, sizeof(bu), "in");
319             break;
320         default:
321             fclose(direction);
322             return MAA_ERROR_FEATURE_NOT_IMPLEMENTED;
323     }
324     fwrite(bu, sizeof(char), length, direction);
325
326     fclose(direction);
327     return MAA_SUCCESS;
328 }
329
330 int
331 maa_gpio_read(maa_gpio_context *dev)
332 {
333     if (dev->value_fp == -1) {
334         if (maa_gpio_get_valfp(dev) != MAA_SUCCESS) {
335              fprintf(stderr, "Failed to get value file pointer\n");
336         }
337     }
338     else {
339         // if value_fp is new this is pointless
340         lseek(dev->value_fp, 0, SEEK_SET);
341     }
342     char bu[2];
343     if (read(dev->value_fp, bu, 2*sizeof(char)) != 2) {
344         fprintf(stderr, "Failed to read a sensible value from sysfs");
345     }
346     lseek(dev->value_fp, 0, SEEK_SET);
347     int ret = strtol(bu, NULL, 10);
348
349     return ret;
350 }
351
352 maa_result_t
353 maa_gpio_write(maa_gpio_context *dev, int value)
354 {
355     if (dev->value_fp == -1) {
356         maa_gpio_get_valfp(dev);
357     }
358     if (lseek(dev->value_fp, 0, SEEK_SET) == -1) {
359         return MAA_ERROR_INVALID_RESOURCE;
360     }
361
362     char bu[MAX_SIZE];
363     int length = snprintf(bu, sizeof(bu), "%d", value);
364     if (write(dev->value_fp, bu, length*sizeof(char)) == -1) {
365         return MAA_ERROR_INVALID_HANDLE;
366     }
367
368     return MAA_SUCCESS;
369 }
370
371 maa_result_t
372 maa_gpio_unexport(maa_gpio_context *dev)
373 {
374     FILE *unexport_f;
375
376     if ((unexport_f = fopen(SYSFS_CLASS_GPIO "/unexport", "w")) == NULL) {
377         fprintf(stderr, "Failed to open unexport for writing!\n");
378         return MAA_ERROR_INVALID_RESOURCE;
379     }
380
381     char bu[MAX_SIZE];
382     int length = snprintf(bu, sizeof(bu), "%d", dev->pin);
383     fwrite(bu, sizeof(char), length, unexport_f);
384     fclose(unexport_f);
385
386     maa_gpio_isr_exit(dev);
387
388     return MAA_SUCCESS;
389 }
390
391 maa_result_t
392 maa_gpio_close(maa_gpio_context *dev)
393 {
394     if (dev->value_fp != -1) {
395         close(dev->value_fp);
396     }
397     maa_gpio_unexport(dev);
398     free(dev);
399     return MAA_SUCCESS;
400 }