rpi_gpio 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,40 +1,40 @@
1
- /*
2
- Original code by Ben Croston modified for Ruby by Nick Lowery
3
- (github.com/clockvapor)
4
- Copyright (c) 2014-2015 Nick Lowery
5
-
6
- Copyright (c) 2013-2014 Ben Croston
7
-
8
- Permission is hereby granted, free of charge, to any person obtaining a copy of
9
- this software and associated documentation files (the "Software"), to deal in
10
- the Software without restriction, including without limitation the rights to
11
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12
- of the Software, and to permit persons to whom the Software is furnished to do
13
- so, subject to the following conditions:
14
-
15
- The above copyright notice and this permission notice shall be included in all
16
- copies or substantial portions of the Software.
17
-
18
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
- SOFTWARE.
25
- */
26
-
27
- #ifndef CPUINFO_H
28
- #define CPUINFO_H
29
- typedef struct
30
- {
31
- int p1_revision;
32
- char *ram;
33
- char *manufacturer;
34
- char *processor;
35
- char *type;
36
- char revision[1024];
37
- } rpi_info;
38
- #endif /* CPUINFO_H */
39
-
40
- int get_rpi_info(rpi_info *info);
1
+ /*
2
+ Original code by Ben Croston modified for Ruby by Nick Lowery
3
+ (github.com/clockvapor)
4
+ Copyright (c) 2014-2016 Nick Lowery
5
+
6
+ Copyright (c) 2012-2015 Ben Croston
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
9
+ this software and associated documentation files (the "Software"), to deal in
10
+ the Software without restriction, including without limitation the rights to
11
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12
+ of the Software, and to permit persons to whom the Software is furnished to do
13
+ so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+ */
26
+
27
+ #ifndef CPUINFO_H
28
+ #define CPUINFO_H
29
+ typedef struct
30
+ {
31
+ int p1_revision;
32
+ char *ram;
33
+ char *manufacturer;
34
+ char *processor;
35
+ char *type;
36
+ char revision[1024];
37
+ } rpi_info;
38
+ #endif /* CPUINFO_H */
39
+
40
+ int get_rpi_info(rpi_info *info);
@@ -1,561 +1,605 @@
1
- /*
2
- Original code by Ben Croston modified for Ruby by Nick Lowery
3
- (github.com/clockvapor)
4
- Copyright (c) 2014-2015 Nick Lowery
5
-
6
- Copyright (c) 2013-2014 Ben Croston
7
-
8
- Permission is hereby granted, free of charge, to any person obtaining a copy of
9
- this software and associated documentation files (the "Software"), to deal in
10
- the Software without restriction, including without limitation the rights to
11
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12
- of the Software, and to permit persons to whom the Software is furnished to do
13
- so, subject to the following conditions:
14
-
15
- The above copyright notice and this permission notice shall be included in all
16
- copies or substantial portions of the Software.
17
-
18
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
- SOFTWARE.
25
- */
26
-
27
- #include <pthread.h>
28
- #include <sys/epoll.h>
29
- #include <stdio.h>
30
- #include <stdlib.h>
31
- #include <fcntl.h>
32
- #include <unistd.h>
33
- #include <string.h>
34
- #include <sys/time.h>
35
- #include "event_gpio.h"
36
-
37
- const char *stredge[4] = {"none", "rising", "falling", "both"};
38
-
39
- struct gpios
40
- {
41
- unsigned int gpio;
42
- int value_fd;
43
- int exported;
44
- int edge;
45
- int initial_thread;
46
- int initial_wait;
47
- int thread_added;
48
- int bouncetime;
49
- unsigned long long lastcall;
50
- struct gpios *next;
51
- };
52
- struct gpios *gpio_list = NULL;
53
-
54
- // event callbacks
55
- struct callback
56
- {
57
- unsigned int gpio;
58
- void (*func)(unsigned int gpio);
59
- struct callback *next;
60
- };
61
- struct callback *callbacks = NULL;
62
-
63
- pthread_t threads;
64
- int event_occurred[54] = { 0 };
65
- int thread_running = 0;
66
- int epfd_thread = -1;
67
- int epfd_blocking = -1;
68
-
69
- /************* /sys/class/gpio functions ************/
70
- int gpio_export(unsigned int gpio)
71
- {
72
- int fd, len;
73
- char str_gpio[3];
74
-
75
- if ((fd = open("/sys/class/gpio/export", O_WRONLY)) < 0)
76
- return -1;
77
-
78
- len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio);
79
- write(fd, str_gpio, len);
80
- close(fd);
81
-
82
- return 0;
83
- }
84
-
85
- int gpio_unexport(unsigned int gpio)
86
- {
87
- int fd, len;
88
- char str_gpio[3];
89
-
90
- if ((fd = open("/sys/class/gpio/unexport", O_WRONLY)) < 0)
91
- return -1;
92
-
93
- len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio);
94
- write(fd, str_gpio, len);
95
- close(fd);
96
-
97
- return 0;
98
- }
99
-
100
- int gpio_set_direction(unsigned int gpio, unsigned int in_flag)
101
- {
102
- int fd;
103
- char filename[33];
104
-
105
- snprintf(filename, sizeof(filename),
106
- "/sys/class/gpio/gpio%d/direction", gpio);
107
- if ((fd = open(filename, O_WRONLY)) < 0) {
108
- return -1;
109
- }
110
-
111
- if (in_flag) {
112
- write(fd, "in", 3);
113
- } else {
114
- write(fd, "out", 4);
115
- }
116
-
117
- close(fd);
118
- return 0;
119
- }
120
-
121
- int gpio_set_edge(unsigned int gpio, unsigned int edge)
122
- {
123
- int fd;
124
- char filename[28];
125
-
126
- snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/edge", gpio);
127
-
128
- if ((fd = open(filename, O_WRONLY)) < 0)
129
- return -1;
130
-
131
- write(fd, stredge[edge], strlen(stredge[edge]) + 1);
132
- close(fd);
133
- return 0;
134
- }
135
-
136
- int open_value_file(unsigned int gpio)
137
- {
138
- int fd;
139
- char filename[29];
140
-
141
- // create file descriptor of value file
142
- snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio);
143
- if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
144
- return -1;
145
- return fd;
146
- }
147
-
148
- /********* gpio list functions **********/
149
- struct gpios *get_gpio(unsigned int gpio)
150
- {
151
- struct gpios *g = gpio_list;
152
- while (g != NULL) {
153
- if (g->gpio == gpio)
154
- return g;
155
- g = g->next;
156
- }
157
- return NULL;
158
- }
159
-
160
- struct gpios *get_gpio_from_value_fd(int fd)
161
- {
162
- struct gpios *g = gpio_list;
163
- while (g != NULL) {
164
- if (g->value_fd == fd)
165
- return g;
166
- g = g->next;
167
- }
168
- return NULL;
169
- }
170
-
171
- struct gpios *new_gpio(unsigned int gpio)
172
- {
173
- struct gpios *new_gpio;
174
-
175
- new_gpio = malloc(sizeof(struct gpios));
176
- if (new_gpio == 0)
177
- return NULL; // out of memory
178
-
179
- new_gpio->gpio = gpio;
180
- if (gpio_export(gpio) != 0) {
181
- free(new_gpio);
182
- return NULL;
183
- }
184
- new_gpio->exported = 1;
185
-
186
- if (gpio_set_direction(gpio,1) != 0) { // 1==input
187
- free(new_gpio);
188
- return NULL;
189
- }
190
-
191
- if ((new_gpio->value_fd = open_value_file(gpio)) == -1) {
192
- gpio_unexport(gpio);
193
- free(new_gpio);
194
- return NULL;
195
- }
196
-
197
- new_gpio->initial_thread = 1;
198
- new_gpio->initial_wait = 1;
199
- new_gpio->bouncetime = -666;
200
- new_gpio->lastcall = 0;
201
- new_gpio->thread_added = 0;
202
-
203
- if (gpio_list == NULL) {
204
- new_gpio->next = NULL;
205
- } else {
206
- new_gpio->next = gpio_list;
207
- }
208
- gpio_list = new_gpio;
209
- return new_gpio;
210
- }
211
-
212
- void delete_gpio(unsigned int gpio)
213
- {
214
- struct gpios *g = gpio_list;
215
- struct gpios *temp;
216
- struct gpios *prev = NULL;
217
-
218
- while (g != NULL) {
219
- if (g->gpio == gpio) {
220
- if (prev == NULL)
221
- gpio_list = g->next;
222
- else
223
- prev->next = g->next;
224
- temp = g;
225
- g = g->next;
226
- free(temp);
227
- return;
228
- } else {
229
- prev = g;
230
- g = g->next;
231
- }
232
- }
233
- }
234
-
235
- int gpio_event_added(unsigned int gpio)
236
- {
237
- struct gpios *g = gpio_list;
238
- while (g != NULL) {
239
- if (g->gpio == gpio)
240
- return g->edge;
241
- g = g->next;
242
- }
243
- return 0;
244
- }
245
-
246
- /******* callback list functions ********/
247
- int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio))
248
- {
249
- struct callback *cb = callbacks;
250
- struct callback *new_cb;
251
-
252
- new_cb = malloc(sizeof(struct callback));
253
- if (new_cb == 0)
254
- return -1; // out of memory
255
-
256
- new_cb->gpio = gpio;
257
- new_cb->func = func;
258
- new_cb->next = NULL;
259
-
260
- if (callbacks == NULL) {
261
- // start new list
262
- callbacks = new_cb;
263
- } else {
264
- // add to end of list
265
- while (cb->next != NULL)
266
- cb = cb->next;
267
- cb->next = new_cb;
268
- }
269
- return 0;
270
- }
271
-
272
- int callback_exists(unsigned int gpio)
273
- {
274
- struct callback *cb = callbacks;
275
- while (cb != NULL) {
276
- if (cb->gpio == gpio)
277
- return 1;
278
- cb = cb->next;
279
- }
280
- return 0;
281
- }
282
-
283
- void run_callbacks(unsigned int gpio)
284
- {
285
- struct callback *cb = callbacks;
286
- while (cb != NULL)
287
- {
288
- if (cb->gpio == gpio)
289
- cb->func(cb->gpio);
290
- cb = cb->next;
291
- }
292
- }
293
-
294
- void remove_callbacks(unsigned int gpio)
295
- {
296
- struct callback *cb = callbacks;
297
- struct callback *temp;
298
- struct callback *prev = NULL;
299
-
300
- while (cb != NULL)
301
- {
302
- if (cb->gpio == gpio)
303
- {
304
- if (prev == NULL)
305
- callbacks = cb->next;
306
- else
307
- prev->next = cb->next;
308
- temp = cb;
309
- cb = cb->next;
310
- free(temp);
311
- } else {
312
- prev = cb;
313
- cb = cb->next;
314
- }
315
- }
316
- }
317
-
318
- void *poll_thread(void *threadarg)
319
- {
320
- struct epoll_event events;
321
- char buf;
322
- struct timeval tv_timenow;
323
- unsigned long long timenow;
324
- struct gpios *g;
325
- int n;
326
-
327
- thread_running = 1;
328
- while (thread_running) {
329
- if ((n = epoll_wait(epfd_thread, &events, 1, -1)) == -1) {
330
- thread_running = 0;
331
- pthread_exit(NULL);
332
- }
333
- if (n > 0) {
334
- lseek(events.data.fd, 0, SEEK_SET);
335
- if (read(events.data.fd, &buf, 1) != 1) {
336
- thread_running = 0;
337
- pthread_exit(NULL);
338
- }
339
- g = get_gpio_from_value_fd(events.data.fd);
340
- if (g->initial_thread) { // ignore first epoll trigger
341
- g->initial_thread = 0;
342
- } else {
343
- gettimeofday(&tv_timenow, NULL);
344
- timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec;
345
- if (g->bouncetime == -666 || timenow - g->lastcall > g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) {
346
- g->lastcall = timenow;
347
- event_occurred[g->gpio] = 1;
348
- run_callbacks(g->gpio);
349
- }
350
- }
351
- }
352
- }
353
- thread_running = 0;
354
- pthread_exit(NULL);
355
- }
356
-
357
- void remove_edge_detect(unsigned int gpio)
358
- {
359
- struct epoll_event ev;
360
- struct gpios *g = get_gpio(gpio);
361
-
362
- if (g == NULL)
363
- return;
364
-
365
- // delete epoll of fd
366
-
367
- ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
368
- ev.data.fd = g->value_fd;
369
- epoll_ctl(epfd_thread, EPOLL_CTL_DEL, g->value_fd, &ev);
370
-
371
- // delete callbacks for gpio
372
- remove_callbacks(gpio);
373
-
374
- // btc fixme - check return result??
375
- gpio_set_edge(gpio, NO_EDGE);
376
- g->edge = NO_EDGE;
377
-
378
- if (g->value_fd != -1)
379
- close(g->value_fd);
380
-
381
- // btc fixme - check return result??
382
- gpio_unexport(gpio);
383
- event_occurred[gpio] = 0;
384
-
385
- delete_gpio(gpio);
386
- }
387
-
388
- int event_detected(unsigned int gpio)
389
- {
390
- if (event_occurred[gpio]) {
391
- event_occurred[gpio] = 0;
392
- return 1;
393
- } else {
394
- return 0;
395
- }
396
- }
397
-
398
- void event_cleanup(unsigned int gpio)
399
- // gpio of -666 means clean every channel used
400
- {
401
- struct gpios *g = gpio_list;
402
- struct gpios *temp = NULL;
403
-
404
- while (g != NULL) {
405
- if ((gpio == -666) || (g->gpio == gpio))
406
- temp = g->next;
407
- remove_edge_detect(g->gpio);
408
- g = temp;
409
- }
410
- if (gpio_list == NULL)
411
- if (epfd_blocking != -1)
412
- close(epfd_blocking);
413
- epfd_blocking = -1;
414
- if (epfd_thread != -1)
415
- close(epfd_thread);
416
- epfd_thread = -1;
417
- thread_running = 0;
418
- }
419
-
420
- void event_cleanup_all(void)
421
- {
422
- event_cleanup(-666);
423
- }
424
-
425
- int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime)
426
- // return values:
427
- // 0 - Success
428
- // 1 - Edge detection already added
429
- // 2 - Other error
430
- {
431
- pthread_t threads;
432
- struct epoll_event ev;
433
- long t = 0;
434
- struct gpios *g;
435
- int i = -1;
436
-
437
- i = gpio_event_added(gpio);
438
- if (i == 0) { // event not already added
439
- if ((g = new_gpio(gpio)) == NULL)
440
- return 2;
441
-
442
- gpio_set_edge(gpio, edge);
443
- g->edge = edge;
444
- g->bouncetime = bouncetime;
445
- } else if (i == edge) { // get existing event
446
- g = get_gpio(gpio);
447
- if ((bouncetime != -666 && g->bouncetime != bouncetime) || // different event bouncetime used
448
- (g->thread_added)) // event already added
449
- return 1;
450
- } else {
451
- return 1;
452
- }
453
-
454
- // create epfd_thread if not already open
455
- if ((epfd_thread == -1) && ((epfd_thread = epoll_create(1)) == -1))
456
- return 2;
457
-
458
- // add to epoll fd
459
- ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
460
- ev.data.fd = g->value_fd;
461
- if (epoll_ctl(epfd_thread, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) {
462
- remove_edge_detect(gpio);
463
- return 2;
464
- }
465
- g->thread_added = 1;
466
-
467
- // start poll thread if it is not already running
468
- if (!thread_running) {
469
- if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0) {
470
- remove_edge_detect(gpio);
471
- return 2;
472
- }
473
- }
474
- return 0;
475
- }
476
-
477
- int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime)
478
- // return values:
479
- // 0 - Success
480
- // 1 - Edge detection already added
481
- // 2 - Other error
482
- {
483
- int n, ed;
484
- struct epoll_event events, ev;
485
- char buf;
486
- struct gpios *g = NULL;
487
- struct timeval tv_timenow;
488
- unsigned long long timenow;
489
- int finished = 0;
490
- int initial_edge = 1;
491
-
492
- if (callback_exists(gpio)) {
493
- return 1;
494
- }
495
-
496
- // add gpio if it has not been added already
497
- ed = gpio_event_added(gpio);
498
- if (ed == edge) { // get existing record
499
- g = get_gpio(gpio);
500
- if (g->bouncetime != -666 && g->bouncetime != bouncetime) {
501
- return 1;
502
- }
503
- } else if (ed == NO_EDGE) { // not found so add event
504
- if ((g = new_gpio(gpio)) == NULL)
505
- return 2;
506
- gpio_set_edge(gpio, edge);
507
- g->edge = edge;
508
- g->bouncetime = bouncetime;
509
- } else { // ed != edge - event for a different edge
510
- g = get_gpio(gpio);
511
- gpio_set_edge(gpio, edge);
512
- g->edge = edge;
513
- g->bouncetime = bouncetime;
514
- g->initial_wait = 1;
515
- }
516
-
517
- // create epfd_blocking if not already open
518
- if ((epfd_blocking == -1) && ((epfd_blocking = epoll_create(1)) == -1)) {
519
- return 2;
520
- }
521
-
522
- // add to epoll fd
523
- ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
524
- ev.data.fd = g->value_fd;
525
- if (epoll_ctl(epfd_blocking, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) {
526
- return 2;
527
- }
528
-
529
- // wait for edge
530
- while (!finished) {
531
- if ((n = epoll_wait(epfd_blocking, &events, 1, -1)) == -1) {
532
- epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
533
- return 2;
534
- }
535
- if (initial_edge) { // first time triggers with current state, so ignore
536
- initial_edge = 0;
537
- } else {
538
- gettimeofday(&tv_timenow, NULL);
539
- timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec;
540
- if (g->bouncetime == -666 ||
541
- timenow - g->lastcall > g->bouncetime*1000 ||
542
- g->lastcall == 0 ||
543
- g->lastcall > timenow) {
544
- g->lastcall = timenow;
545
- finished = 1;
546
- }
547
- }
548
- }
549
-
550
- // check event was valid
551
- if (n > 0) {
552
- lseek(events.data.fd, 0, SEEK_SET);
553
- if ((read(events.data.fd, &buf, 1) != 1) || (events.data.fd != g->value_fd)) {
554
- epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
555
- return 2;
556
- }
557
- }
558
-
559
- epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
560
- return 0;
561
- }
1
+ /*
2
+ Original code by Ben Croston modified for Ruby by Nick Lowery
3
+ (github.com/clockvapor)
4
+ Copyright (c) 2014-2020 Nick Lowery
5
+
6
+ Copyright (c) 2013-2018 Ben Croston
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
9
+ this software and associated documentation files (the "Software"), to deal in
10
+ the Software without restriction, including without limitation the rights to
11
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12
+ of the Software, and to permit persons to whom the Software is furnished to do
13
+ so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+ */
26
+
27
+ #include <pthread.h>
28
+ #include <sys/epoll.h>
29
+ #include <stdio.h>
30
+ #include <stdlib.h>
31
+ #include <errno.h>
32
+ #include <fcntl.h>
33
+ #include <unistd.h>
34
+ #include <string.h>
35
+ #include <sys/time.h>
36
+ #include "event_gpio.h"
37
+
38
+ const char *stredge[4] = {"none", "rising", "falling", "both"};
39
+
40
+ struct gpios
41
+ {
42
+ unsigned int gpio;
43
+ int value_fd;
44
+ int exported;
45
+ int edge;
46
+ int initial_thread;
47
+ int initial_wait;
48
+ int thread_added;
49
+ int bouncetime;
50
+ unsigned long long lastcall;
51
+ struct gpios *next;
52
+ };
53
+ struct gpios *gpio_list = NULL;
54
+
55
+ // event callbacks
56
+ struct callback
57
+ {
58
+ unsigned int gpio;
59
+ void (*func)(unsigned int gpio);
60
+ struct callback *next;
61
+ };
62
+ struct callback *callbacks = NULL;
63
+
64
+ pthread_t threads;
65
+ int event_occurred[54] = { 0 };
66
+ int thread_running = 0;
67
+ int epfd_thread = -1;
68
+ int epfd_blocking = -1;
69
+
70
+ /************* /sys/class/gpio functions ************/
71
+ #define x_write(fd, buf, len) do { \
72
+ size_t x_write_len = (len); \
73
+ \
74
+ if ((size_t)write((fd), (buf), x_write_len) != x_write_len) { \
75
+ close(fd); \
76
+ return (-1); \
77
+ } \
78
+ } while (/* CONSTCOND */ 0)
79
+
80
+ int gpio_export(unsigned int gpio)
81
+ {
82
+ int fd, len;
83
+ char str_gpio[3];
84
+ char filename[33];
85
+
86
+ snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d", gpio);
87
+
88
+ /* return if gpio already exported */
89
+ if (access(filename, F_OK) != -1) {
90
+ return 0;
91
+ }
92
+
93
+ if ((fd = open("/sys/class/gpio/export", O_WRONLY)) < 0) {
94
+ return -1;
95
+ }
96
+
97
+ len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio);
98
+ x_write(fd, str_gpio, len);
99
+ close(fd);
100
+
101
+ return 0;
102
+ }
103
+
104
+ int gpio_unexport(unsigned int gpio)
105
+ {
106
+ int fd, len;
107
+ char str_gpio[3];
108
+
109
+ if ((fd = open("/sys/class/gpio/unexport", O_WRONLY)) < 0)
110
+ return -1;
111
+
112
+ len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio);
113
+ x_write(fd, str_gpio, len);
114
+ close(fd);
115
+
116
+ return 0;
117
+ }
118
+
119
+ int gpio_set_direction(unsigned int gpio, unsigned int in_flag)
120
+ {
121
+ int retry;
122
+ struct timespec delay;
123
+ int fd;
124
+ char filename[33];
125
+
126
+ snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/direction", gpio);
127
+
128
+ // retry waiting for udev to set correct file permissions
129
+ delay.tv_sec = 0;
130
+ delay.tv_nsec = 10000000L; // 10ms
131
+ for (retry=0; retry<100; retry++) {
132
+ if ((fd = open(filename, O_WRONLY)) >= 0)
133
+ break;
134
+ nanosleep(&delay, NULL);
135
+ }
136
+ if (retry >= 100)
137
+ return -1;
138
+
139
+ if (in_flag)
140
+ x_write(fd, "in", 3);
141
+ else
142
+ x_write(fd, "out", 4);
143
+
144
+ close(fd);
145
+ return 0;
146
+ }
147
+
148
+ int gpio_set_edge(unsigned int gpio, unsigned int edge)
149
+ {
150
+ int fd;
151
+ char filename[28];
152
+
153
+ snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/edge", gpio);
154
+
155
+ if ((fd = open(filename, O_WRONLY)) < 0)
156
+ return -1;
157
+
158
+ x_write(fd, stredge[edge], strlen(stredge[edge]) + 1);
159
+ close(fd);
160
+ return 0;
161
+ }
162
+
163
+ int open_value_file(unsigned int gpio)
164
+ {
165
+ int fd;
166
+ char filename[29];
167
+
168
+ // create file descriptor of value file
169
+ snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio);
170
+ if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
171
+ return -1;
172
+ return fd;
173
+ }
174
+
175
+ /********* gpio list functions **********/
176
+ struct gpios *get_gpio(unsigned int gpio)
177
+ {
178
+ struct gpios *g = gpio_list;
179
+ while (g != NULL) {
180
+ if (g->gpio == gpio)
181
+ return g;
182
+ g = g->next;
183
+ }
184
+ return NULL;
185
+ }
186
+
187
+ struct gpios *get_gpio_from_value_fd(int fd)
188
+ {
189
+ struct gpios *g = gpio_list;
190
+ while (g != NULL) {
191
+ if (g->value_fd == fd)
192
+ return g;
193
+ g = g->next;
194
+ }
195
+ return NULL;
196
+ }
197
+
198
+ struct gpios *new_gpio(unsigned int gpio)
199
+ {
200
+ struct gpios *new_gpio;
201
+
202
+ new_gpio = malloc(sizeof(struct gpios));
203
+ if (new_gpio == 0) {
204
+ return NULL; // out of memory
205
+ }
206
+
207
+ new_gpio->gpio = gpio;
208
+ if (gpio_export(gpio) != 0) {
209
+ free(new_gpio);
210
+ return NULL;
211
+ }
212
+ new_gpio->exported = 1;
213
+
214
+ if (gpio_set_direction(gpio,1) != 0) { // 1==input
215
+ free(new_gpio);
216
+ return NULL;
217
+ }
218
+
219
+ if ((new_gpio->value_fd = open_value_file(gpio)) == -1) {
220
+ gpio_unexport(gpio);
221
+ free(new_gpio);
222
+ return NULL;
223
+ }
224
+
225
+ new_gpio->initial_thread = 1;
226
+ new_gpio->initial_wait = 1;
227
+ new_gpio->bouncetime = -666;
228
+ new_gpio->lastcall = 0;
229
+ new_gpio->thread_added = 0;
230
+
231
+ if (gpio_list == NULL) {
232
+ new_gpio->next = NULL;
233
+ } else {
234
+ new_gpio->next = gpio_list;
235
+ }
236
+ gpio_list = new_gpio;
237
+ return new_gpio;
238
+ }
239
+
240
+ void delete_gpio(unsigned int gpio)
241
+ {
242
+ struct gpios *g = gpio_list;
243
+ struct gpios *prev = NULL;
244
+
245
+ while (g != NULL) {
246
+ if (g->gpio == gpio) {
247
+ if (prev == NULL)
248
+ gpio_list = g->next;
249
+ else
250
+ prev->next = g->next;
251
+ free(g);
252
+ return;
253
+ } else {
254
+ prev = g;
255
+ g = g->next;
256
+ }
257
+ }
258
+ }
259
+
260
+ int gpio_event_added(unsigned int gpio)
261
+ {
262
+ struct gpios *g = gpio_list;
263
+ while (g != NULL) {
264
+ if (g->gpio == gpio)
265
+ return g->edge;
266
+ g = g->next;
267
+ }
268
+ return 0;
269
+ }
270
+
271
+ /******* callback list functions ********/
272
+ int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio))
273
+ {
274
+ struct callback *cb = callbacks;
275
+ struct callback *new_cb;
276
+
277
+ new_cb = malloc(sizeof(struct callback));
278
+ if (new_cb == 0)
279
+ return -1; // out of memory
280
+
281
+ new_cb->gpio = gpio;
282
+ new_cb->func = func;
283
+ new_cb->next = NULL;
284
+
285
+ if (callbacks == NULL) {
286
+ // start new list
287
+ callbacks = new_cb;
288
+ } else {
289
+ // add to end of list
290
+ while (cb->next != NULL)
291
+ cb = cb->next;
292
+ cb->next = new_cb;
293
+ }
294
+ return 0;
295
+ }
296
+
297
+ int callback_exists(unsigned int gpio)
298
+ {
299
+ struct callback *cb = callbacks;
300
+ while (cb != NULL) {
301
+ if (cb->gpio == gpio)
302
+ return 1;
303
+ cb = cb->next;
304
+ }
305
+ return 0;
306
+ }
307
+
308
+ void run_callbacks(unsigned int gpio)
309
+ {
310
+ struct callback *cb = callbacks;
311
+ while (cb != NULL)
312
+ {
313
+ if (cb->gpio == gpio)
314
+ cb->func(cb->gpio);
315
+ cb = cb->next;
316
+ }
317
+ }
318
+
319
+ void remove_callbacks(unsigned int gpio)
320
+ {
321
+ struct callback *cb = callbacks;
322
+ struct callback *temp;
323
+ struct callback *prev = NULL;
324
+
325
+ while (cb != NULL)
326
+ {
327
+ if (cb->gpio == gpio)
328
+ {
329
+ if (prev == NULL)
330
+ callbacks = cb->next;
331
+ else
332
+ prev->next = cb->next;
333
+ temp = cb;
334
+ cb = cb->next;
335
+ free(temp);
336
+ } else {
337
+ prev = cb;
338
+ cb = cb->next;
339
+ }
340
+ }
341
+ }
342
+
343
+ void *poll_thread(void *threadarg)
344
+ {
345
+ struct epoll_event events;
346
+ char buf;
347
+ struct timeval tv_timenow;
348
+ unsigned long long timenow;
349
+ struct gpios *g;
350
+ int n;
351
+
352
+ thread_running = 1;
353
+ while (thread_running) {
354
+ n = epoll_wait(epfd_thread, &events, 1, -1);
355
+ if (n > 0) {
356
+ lseek(events.data.fd, 0, SEEK_SET);
357
+ if (read(events.data.fd, &buf, 1) != 1) {
358
+ thread_running = 0;
359
+ pthread_exit(NULL);
360
+ }
361
+ g = get_gpio_from_value_fd(events.data.fd);
362
+ if (g->initial_thread) { // ignore first epoll trigger
363
+ g->initial_thread = 0;
364
+ } else {
365
+ gettimeofday(&tv_timenow, NULL);
366
+ timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec;
367
+ if (g->bouncetime == -666 || timenow - g->lastcall > (unsigned int)g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) {
368
+ g->lastcall = timenow;
369
+ event_occurred[g->gpio] = 1;
370
+ run_callbacks(g->gpio);
371
+ }
372
+ }
373
+ } else if (n == -1) {
374
+ /* If a signal is received while we are waiting,
375
+ epoll_wait will return with an EINTR error.
376
+ Just try again in that case. */
377
+ if (errno == EINTR) {
378
+ continue;
379
+ }
380
+ thread_running = 0;
381
+ pthread_exit(NULL);
382
+ }
383
+ }
384
+ thread_running = 0;
385
+ pthread_exit(NULL);
386
+ }
387
+
388
+ void remove_edge_detect(unsigned int gpio)
389
+ {
390
+ struct epoll_event ev;
391
+ struct gpios *g = get_gpio(gpio);
392
+
393
+ if (g == NULL)
394
+ return;
395
+
396
+ // delete epoll of fd
397
+
398
+ ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
399
+ ev.data.fd = g->value_fd;
400
+ epoll_ctl(epfd_thread, EPOLL_CTL_DEL, g->value_fd, &ev);
401
+
402
+ // delete callbacks for gpio
403
+ remove_callbacks(gpio);
404
+
405
+ // btc fixme - check return result??
406
+ gpio_set_edge(gpio, NO_EDGE);
407
+ g->edge = NO_EDGE;
408
+
409
+ if (g->value_fd != -1)
410
+ close(g->value_fd);
411
+
412
+ // btc fixme - check return result??
413
+ gpio_unexport(gpio);
414
+ event_occurred[gpio] = 0;
415
+
416
+ delete_gpio(gpio);
417
+ }
418
+
419
+ int event_detected(unsigned int gpio)
420
+ {
421
+ if (event_occurred[gpio]) {
422
+ event_occurred[gpio] = 0;
423
+ return 1;
424
+ } else {
425
+ return 0;
426
+ }
427
+ }
428
+
429
+ void event_cleanup(int gpio)
430
+ // gpio of -666 means clean every channel used
431
+ {
432
+ struct gpios *g = gpio_list;
433
+ struct gpios *next_gpio = NULL;
434
+
435
+ while (g != NULL) {
436
+ next_gpio = g->next;
437
+ if ((gpio == -666) || ((int)g->gpio == gpio))
438
+ remove_edge_detect(g->gpio);
439
+ g = next_gpio;
440
+ }
441
+ if (gpio_list == NULL) {
442
+ if (epfd_blocking != -1) {
443
+ close(epfd_blocking);
444
+ epfd_blocking = -1;
445
+ }
446
+ if (epfd_thread != -1) {
447
+ close(epfd_thread);
448
+ epfd_thread = -1;
449
+ }
450
+ thread_running = 0;
451
+ }
452
+ }
453
+
454
+ void event_cleanup_all(void)
455
+ {
456
+ event_cleanup(-666);
457
+ }
458
+
459
+ int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime)
460
+ // return values:
461
+ // 0 - Success
462
+ // 1 - Edge detection already added
463
+ // 2 - Other error
464
+ {
465
+ pthread_t threads;
466
+ struct epoll_event ev;
467
+ long t = 0;
468
+ struct gpios *g;
469
+ int i = -1;
470
+
471
+ i = gpio_event_added(gpio);
472
+ if (i == 0) { // event not already added
473
+ if ((g = new_gpio(gpio)) == NULL) {
474
+ return 2;
475
+ }
476
+
477
+ gpio_set_edge(gpio, edge);
478
+ g->edge = edge;
479
+ g->bouncetime = bouncetime;
480
+ } else if (i == (int)edge) { // get existing event
481
+ g = get_gpio(gpio);
482
+ if ((bouncetime != -666 && g->bouncetime != bouncetime) || // different event bouncetime used
483
+ (g->thread_added)) // event already added
484
+ return 1;
485
+ } else {
486
+ return 1;
487
+ }
488
+
489
+ // create epfd_thread if not already open
490
+ if ((epfd_thread == -1) && ((epfd_thread = epoll_create(1)) == -1))
491
+ return 2;
492
+
493
+ // add to epoll fd
494
+ ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
495
+ ev.data.fd = g->value_fd;
496
+ if (epoll_ctl(epfd_thread, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) {
497
+ remove_edge_detect(gpio);
498
+ return 2;
499
+ }
500
+ g->thread_added = 1;
501
+
502
+ // start poll thread if it is not already running
503
+ if (!thread_running) {
504
+ if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0) {
505
+ remove_edge_detect(gpio);
506
+ return 2;
507
+ }
508
+ }
509
+ return 0;
510
+ }
511
+
512
+ int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout)
513
+ // return values:
514
+ // 1 - Success (edge detected)
515
+ // 0 - Timeout
516
+ // -1 - Edge detection already added
517
+ // -2 - Other error
518
+ {
519
+ int n, ed;
520
+ struct epoll_event events, ev;
521
+ char buf;
522
+ struct gpios *g = NULL;
523
+ struct timeval tv_timenow;
524
+ unsigned long long timenow;
525
+ int finished = 0;
526
+ int initial_edge = 1;
527
+
528
+ if (callback_exists(gpio))
529
+ return -1;
530
+
531
+ // add gpio if it has not been added already
532
+ ed = gpio_event_added(gpio);
533
+ if (ed == (int)edge) { // get existing record
534
+ g = get_gpio(gpio);
535
+ if (g->bouncetime != -666 && g->bouncetime != bouncetime) {
536
+ return -1;
537
+ }
538
+ } else if (ed == NO_EDGE) { // not found so add event
539
+ if ((g = new_gpio(gpio)) == NULL) {
540
+ return -2;
541
+ }
542
+ gpio_set_edge(gpio, edge);
543
+ g->edge = edge;
544
+ g->bouncetime = bouncetime;
545
+ } else { // ed != edge - event for a different edge
546
+ g = get_gpio(gpio);
547
+ gpio_set_edge(gpio, edge);
548
+ g->edge = edge;
549
+ g->bouncetime = bouncetime;
550
+ g->initial_wait = 1;
551
+ }
552
+
553
+ // create epfd_blocking if not already open
554
+ if ((epfd_blocking == -1) && ((epfd_blocking = epoll_create(1)) == -1)) {
555
+ return -2;
556
+ }
557
+
558
+ // add to epoll fd
559
+ ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
560
+ ev.data.fd = g->value_fd;
561
+ if (epoll_ctl(epfd_blocking, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) {
562
+ return -2;
563
+ }
564
+
565
+ // wait for edge
566
+ while (!finished) {
567
+ n = epoll_wait(epfd_blocking, &events, 1, timeout);
568
+ if (n == -1) {
569
+ /* If a signal is received while we are waiting,
570
+ epoll_wait will return with an EINTR error.
571
+ Just try again in that case. */
572
+ if (errno == EINTR) {
573
+ continue;
574
+ }
575
+ epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
576
+ return -2;
577
+ }
578
+ if (initial_edge) { // first time triggers with current state, so ignore
579
+ initial_edge = 0;
580
+ } else {
581
+ gettimeofday(&tv_timenow, NULL);
582
+ timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec;
583
+ if (g->bouncetime == -666 || timenow - g->lastcall > (unsigned int)g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) {
584
+ g->lastcall = timenow;
585
+ finished = 1;
586
+ }
587
+ }
588
+ }
589
+
590
+ // check event was valid
591
+ if (n > 0) {
592
+ lseek(events.data.fd, 0, SEEK_SET);
593
+ if ((read(events.data.fd, &buf, 1) != 1) || (events.data.fd != g->value_fd)) {
594
+ epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
595
+ return -2;
596
+ }
597
+ }
598
+
599
+ epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
600
+ if (n == 0) {
601
+ return 0; // timeout
602
+ } else {
603
+ return 1; // edge found
604
+ }
605
+ }