gpio: rework of gpio - using open() for value_fp
[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
33 #define SYSFS_CLASS_GPIO "/sys/class/gpio"
34 #define MAX_SIZE 64
35 #define POLL_TIMEOUT
36
37 static maa_result_t
38 maa_gpio_get_valfp(maa_gpio_context *dev)
39 {
40     char bu[MAX_SIZE];
41     sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
42
43     dev->value_fp = open(bu, O_RDWR);
44     if (dev->value_fp == -1) {
45         return MAA_ERROR_INVALID_RESOURCE;
46     }
47
48     return MAA_SUCCESS;
49 }
50
51 maa_gpio_context*
52 maa_gpio_init(int pin)
53 {
54     int pinm = maa_check_gpio(pin);
55     if (pinm < 0)
56         return NULL;
57
58     return maa_gpio_init_raw(pinm);
59 }
60
61 maa_gpio_context*
62 maa_gpio_init_raw(int pin)
63 {
64     if (pin < 0)
65         return NULL;
66
67     FILE *export_f;
68     char bu[MAX_SIZE];
69     int length;
70
71     maa_gpio_context* dev = (maa_gpio_context*) malloc(sizeof(maa_gpio_context));
72     memset(dev, 0, sizeof(maa_gpio_context));
73     dev->value_fp = -1;
74     dev->pin = pin;
75
76     if ((export_f = fopen(SYSFS_CLASS_GPIO "/export", "w")) == NULL) {
77         fprintf(stderr, "Failed to open export for writing!\n");
78         return NULL;
79     }
80     length = snprintf(bu, sizeof(bu), "%d", dev->pin);
81     fwrite(bu, sizeof(char), length, export_f);
82
83     fclose(export_f);
84     return dev;
85 }
86
87 static maa_result_t
88 maa_gpio_wait_interrupt(int fd)
89 {
90     unsigned char c;
91     struct pollfd pfd;
92
93     // setup poll on POLLPRI
94     pfd.fd = fd;
95     pfd.events = POLLPRI;
96
97     // do an initial read to clear interupt
98     read (fd, &c, 1);
99
100     if (fd <= 0) {
101         return MAA_ERROR_INVALID_RESOURCE;
102     }
103
104     // Wait for it forever
105     int x = poll (&pfd, 1, -1);
106
107     // do a final read to clear interupt
108     read (fd, &c, 1);
109
110     return MAA_SUCCESS;
111 }
112
113 static void*
114 maa_gpio_interrupt_handler(void* arg)
115 {
116     maa_gpio_context* dev = (maa_gpio_context*) arg;
117     maa_result_t ret;
118
119     // open gpio value with open(3)
120     char bu[MAX_SIZE];
121     sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
122     dev->isr_value_fp = open(bu, O_RDONLY);
123
124     for (;;) {
125         ret = maa_gpio_wait_interrupt(dev->isr_value_fp);
126         if (ret == MAA_SUCCESS) {
127             dev->isr();
128         } else {
129             // we must have got an error code so die nicely
130             close(dev->isr_value_fp);
131             return;
132         }
133     }
134 }
135
136 maa_result_t
137 maa_gpio_edge_mode(maa_gpio_context *dev, gpio_edge_t mode)
138 {
139     if (dev->value_fp != -1) {
140          close(dev->value_fp);
141          dev->value_fp = -1;
142     }
143
144     char filepath[MAX_SIZE];
145     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/edge", dev->pin);
146
147     FILE *edge;
148     if ((edge= fopen(filepath, "w")) == NULL) {
149         fprintf(stderr, "Failed to open edge for writing!\n");
150         return MAA_ERROR_INVALID_RESOURCE;
151     }
152
153     char bu[MAX_SIZE];
154     int length;
155     switch(mode) {
156         case MAA_GPIO_EDGE_NONE:
157             length = snprintf(bu, sizeof(bu), "none");
158             break;
159         case MAA_GPIO_EDGE_BOTH:
160             length = snprintf(bu, sizeof(bu), "both");
161             break;
162         case MAA_GPIO_EDGE_RISING:
163             length = snprintf(bu, sizeof(bu), "rising");
164             break;
165         case MAA_GPIO_EDGE_FALLING:
166             length = snprintf(bu, sizeof(bu), "falling");
167             break;
168         default:
169             fclose(edge);
170             return MAA_ERROR_FEATURE_NOT_IMPLEMENTED;
171     }
172     fwrite(bu, sizeof(char), length, edge);
173
174     fclose(edge);
175     return MAA_SUCCESS;
176 }
177
178 maa_result_t
179 maa_gpio_isr(maa_gpio_context *dev, gpio_edge_t mode, void (*fptr)(void))
180 {
181     maa_gpio_edge_mode(dev, mode);
182     dev->isr = fptr;
183     pthread_create (&dev->thread_id, NULL, maa_gpio_interrupt_handler, (void *) dev);
184
185     return MAA_SUCCESS;
186 }
187
188 maa_result_t
189 maa_gpio_isr_exit(maa_gpio_context *dev)
190 {
191     maa_result_t ret = MAA_SUCCESS;
192
193     if (dev->thread_id == 0) {
194         return ret;
195     }
196
197     if (pthread_kill(dev->thread_id) != 0) {
198        ret = MAA_ERROR_INVALID_HANDLE;
199     }
200     if (close(dev->isr_value_fp) != 0) {
201        ret = MAA_ERROR_INVALID_PARAMETER;
202     }
203
204     // this is only required if we had an isr setup
205     if (ret == MAA_SUCCESS) {
206        ret = maa_gpio_edge_mode(dev, MAA_GPIO_EDGE_NONE);
207     }
208
209     return ret;
210 }
211
212 maa_result_t
213 maa_gpio_mode(maa_gpio_context *dev, gpio_mode_t mode)
214 {
215     if (dev->value_fp != -1) {
216          close(dev->value_fp);
217          dev->value_fp = -1;
218     }
219
220     char filepath[MAX_SIZE];
221     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/drive", dev->pin);
222
223     FILE *drive;
224     if ((drive = fopen(filepath, "w")) == NULL) {
225         fprintf(stderr, "Failed to open drive for writing!\n");
226         return MAA_ERROR_INVALID_RESOURCE;
227     }
228
229     char bu[MAX_SIZE];
230     int length;
231     switch(mode) {
232         case MAA_GPIO_STRONG:
233             length = snprintf(bu, sizeof(bu), "strong");
234             break;
235         case MAA_GPIO_PULLUP:
236             length = snprintf(bu, sizeof(bu), "pullup");
237             break;
238         case MAA_GPIO_PULLDOWN:
239             length = snprintf(bu, sizeof(bu), "pulldown");
240             break;
241         case MAA_GPIO_HIZ:
242             length = snprintf(bu, sizeof(bu), "hiz");
243             break;
244         default:
245             fclose(drive);
246             return MAA_ERROR_FEATURE_NOT_IMPLEMENTED;
247     }
248     fwrite(bu, sizeof(char), length, drive);
249
250     fclose(drive);
251     return MAA_SUCCESS;
252 }
253
254 maa_result_t
255 maa_gpio_dir(maa_gpio_context *dev, gpio_dir_t dir)
256 {
257     if (dev == NULL) {
258         return MAA_ERROR_INVALID_HANDLE;
259     }
260     if (dev->value_fp != -1) {
261          close(dev->value_fp);
262          dev->value_fp = -1;
263     }
264     char filepath[MAX_SIZE];
265     snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/direction", dev->pin);
266
267     FILE *direction;
268     if ((direction = fopen(filepath, "w")) == NULL) {
269         fprintf(stderr, "Failed to open direction for writing!\n");
270         return MAA_ERROR_INVALID_RESOURCE;
271     }
272
273     char bu[MAX_SIZE];
274     int length;
275     switch(dir) {
276         case MAA_GPIO_OUT:
277             length = snprintf(bu, sizeof(bu), "out");
278             break;
279         case MAA_GPIO_IN:
280             length = snprintf(bu, sizeof(bu), "in");
281             break;
282         default:
283             fclose(direction);
284             return MAA_ERROR_FEATURE_NOT_IMPLEMENTED;
285     }
286     fwrite(bu, sizeof(char), length, direction);
287
288     fclose(direction);
289     return MAA_SUCCESS;
290 }
291
292 int
293 maa_gpio_read(maa_gpio_context *dev)
294 {
295     if (dev->value_fp == -1) {
296         if (maa_gpio_get_valfp(dev) != MAA_SUCCESS) {
297              fprintf(stderr, "Failed to get value file pointer\n");
298         }
299     }
300     else {
301         // if value_fp is new this is pointless
302         lseek(dev->value_fp, 0, SEEK_SET);
303     }
304     char bu[2];
305     if (read(dev->value_fp, bu, 2*sizeof(char)) != 2) {
306         fprintf(stderr, "Failed to read a sensible value from sysfs");
307     }
308     lseek(dev->value_fp, 0, SEEK_SET);
309     int ret = strtol(bu, NULL, 10);
310
311     return ret;
312 }
313
314 maa_result_t
315 maa_gpio_write(maa_gpio_context *dev, int value)
316 {
317     if (dev->value_fp == -1) {
318         maa_gpio_get_valfp(dev);
319     }
320     if (lseek(dev->value_fp, 0, SEEK_SET) == -1) {
321         return MAA_ERROR_INVALID_RESOURCE;
322     }
323
324     char bu[MAX_SIZE];
325     int length = snprintf(bu, sizeof(bu), "%d", value);
326     if (write(dev->value_fp, bu, length*sizeof(char)) == -1) {
327         return MAA_ERROR_INVALID_HANDLE;
328     }
329
330     return MAA_SUCCESS;
331 }
332
333 maa_result_t
334 maa_gpio_unexport(maa_gpio_context *dev)
335 {
336     FILE *unexport_f;
337
338     if ((unexport_f = fopen(SYSFS_CLASS_GPIO "/unexport", "w")) == NULL) {
339         fprintf(stderr, "Failed to open unexport for writing!\n");
340         return MAA_ERROR_INVALID_RESOURCE;
341     }
342
343     char bu[MAX_SIZE];
344     int length = snprintf(bu, sizeof(bu), "%d", dev->pin);
345     fwrite(bu, sizeof(char), length, unexport_f);
346     fclose(unexport_f);
347
348     maa_gpio_isr_exit(dev);
349
350     return MAA_SUCCESS;
351 }
352
353 maa_result_t
354 maa_gpio_close(maa_gpio_context *dev)
355 {
356     if (dev->value_fp != -1) {
357         close(dev->value_fp);
358     }
359     maa_gpio_unexport(dev);
360     free(dev);
361     return MAA_SUCCESS;
362 }