rpi_gpio 0.2.0 → 0.3.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) 2013-2016 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,575 @@
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-2016 Nick Lowery
5
+
6
+ Copyright (c) 2013-2016 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 retry;
103
+ struct timespec delay;
104
+ int fd;
105
+ char filename[33];
106
+
107
+ snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/direction", gpio);
108
+
109
+ // retry waiting for udev to set correct file permissions
110
+ delay.tv_sec = 0;
111
+ delay.tv_nsec = 10000000L; // 10ms
112
+ for (retry=0; retry<100; retry++) {
113
+ if ((fd = open(filename, O_WRONLY)) >= 0)
114
+ break;
115
+ nanosleep(&delay, NULL);
116
+ }
117
+ if (retry >= 100) {
118
+ return -1;
119
+ }
120
+
121
+ if (in_flag) {
122
+ write(fd, "in", 3);
123
+ } else {
124
+ write(fd, "out", 4);
125
+ }
126
+
127
+ close(fd);
128
+ return 0;
129
+ }
130
+
131
+ int gpio_set_edge(unsigned int gpio, unsigned int edge)
132
+ {
133
+ int fd;
134
+ char filename[28];
135
+
136
+ snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/edge", gpio);
137
+
138
+ if ((fd = open(filename, O_WRONLY)) < 0)
139
+ return -1;
140
+
141
+ write(fd, stredge[edge], strlen(stredge[edge]) + 1);
142
+ close(fd);
143
+ return 0;
144
+ }
145
+
146
+ int open_value_file(unsigned int gpio)
147
+ {
148
+ int fd;
149
+ char filename[29];
150
+
151
+ // create file descriptor of value file
152
+ snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio);
153
+ if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
154
+ return -1;
155
+ return fd;
156
+ }
157
+
158
+ /********* gpio list functions **********/
159
+ struct gpios *get_gpio(unsigned int gpio)
160
+ {
161
+ struct gpios *g = gpio_list;
162
+ while (g != NULL) {
163
+ if (g->gpio == gpio)
164
+ return g;
165
+ g = g->next;
166
+ }
167
+ return NULL;
168
+ }
169
+
170
+ struct gpios *get_gpio_from_value_fd(int fd)
171
+ {
172
+ struct gpios *g = gpio_list;
173
+ while (g != NULL) {
174
+ if (g->value_fd == fd)
175
+ return g;
176
+ g = g->next;
177
+ }
178
+ return NULL;
179
+ }
180
+
181
+ struct gpios *new_gpio(unsigned int gpio)
182
+ {
183
+ struct gpios *new_gpio;
184
+
185
+ new_gpio = malloc(sizeof(struct gpios));
186
+ if (new_gpio == 0) {
187
+ return NULL; // out of memory
188
+ }
189
+
190
+ new_gpio->gpio = gpio;
191
+ if (gpio_export(gpio) != 0) {
192
+ free(new_gpio);
193
+ return NULL;
194
+ }
195
+ new_gpio->exported = 1;
196
+
197
+ if (gpio_set_direction(gpio,1) != 0) { // 1==input
198
+ free(new_gpio);
199
+ return NULL;
200
+ }
201
+
202
+ if ((new_gpio->value_fd = open_value_file(gpio)) == -1) {
203
+ gpio_unexport(gpio);
204
+ free(new_gpio);
205
+ return NULL;
206
+ }
207
+
208
+ new_gpio->initial_thread = 1;
209
+ new_gpio->initial_wait = 1;
210
+ new_gpio->bouncetime = -666;
211
+ new_gpio->lastcall = 0;
212
+ new_gpio->thread_added = 0;
213
+
214
+ if (gpio_list == NULL) {
215
+ new_gpio->next = NULL;
216
+ } else {
217
+ new_gpio->next = gpio_list;
218
+ }
219
+ gpio_list = new_gpio;
220
+ return new_gpio;
221
+ }
222
+
223
+ void delete_gpio(unsigned int gpio)
224
+ {
225
+ struct gpios *g = gpio_list;
226
+ struct gpios *temp;
227
+ struct gpios *prev = NULL;
228
+
229
+ while (g != NULL) {
230
+ if (g->gpio == gpio) {
231
+ if (prev == NULL)
232
+ gpio_list = g->next;
233
+ else
234
+ prev->next = g->next;
235
+ temp = g;
236
+ g = g->next;
237
+ free(temp);
238
+ return;
239
+ } else {
240
+ prev = g;
241
+ g = g->next;
242
+ }
243
+ }
244
+ }
245
+
246
+ int gpio_event_added(unsigned int gpio)
247
+ {
248
+ struct gpios *g = gpio_list;
249
+ while (g != NULL) {
250
+ if (g->gpio == gpio)
251
+ return g->edge;
252
+ g = g->next;
253
+ }
254
+ return 0;
255
+ }
256
+
257
+ /******* callback list functions ********/
258
+ int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio))
259
+ {
260
+ struct callback *cb = callbacks;
261
+ struct callback *new_cb;
262
+
263
+ new_cb = malloc(sizeof(struct callback));
264
+ if (new_cb == 0)
265
+ return -1; // out of memory
266
+
267
+ new_cb->gpio = gpio;
268
+ new_cb->func = func;
269
+ new_cb->next = NULL;
270
+
271
+ if (callbacks == NULL) {
272
+ // start new list
273
+ callbacks = new_cb;
274
+ } else {
275
+ // add to end of list
276
+ while (cb->next != NULL)
277
+ cb = cb->next;
278
+ cb->next = new_cb;
279
+ }
280
+ return 0;
281
+ }
282
+
283
+ int callback_exists(unsigned int gpio)
284
+ {
285
+ struct callback *cb = callbacks;
286
+ while (cb != NULL) {
287
+ if (cb->gpio == gpio)
288
+ return 1;
289
+ cb = cb->next;
290
+ }
291
+ return 0;
292
+ }
293
+
294
+ void run_callbacks(unsigned int gpio)
295
+ {
296
+ struct callback *cb = callbacks;
297
+ while (cb != NULL)
298
+ {
299
+ if (cb->gpio == gpio)
300
+ cb->func(cb->gpio);
301
+ cb = cb->next;
302
+ }
303
+ }
304
+
305
+ void remove_callbacks(unsigned int gpio)
306
+ {
307
+ struct callback *cb = callbacks;
308
+ struct callback *temp;
309
+ struct callback *prev = NULL;
310
+
311
+ while (cb != NULL)
312
+ {
313
+ if (cb->gpio == gpio)
314
+ {
315
+ if (prev == NULL)
316
+ callbacks = cb->next;
317
+ else
318
+ prev->next = cb->next;
319
+ temp = cb;
320
+ cb = cb->next;
321
+ free(temp);
322
+ } else {
323
+ prev = cb;
324
+ cb = cb->next;
325
+ }
326
+ }
327
+ }
328
+
329
+ void *poll_thread(void *threadarg)
330
+ {
331
+ struct epoll_event events;
332
+ char buf;
333
+ struct timeval tv_timenow;
334
+ unsigned long long timenow;
335
+ struct gpios *g;
336
+ int n;
337
+
338
+ thread_running = 1;
339
+ while (thread_running) {
340
+ if ((n = epoll_wait(epfd_thread, &events, 1, -1)) == -1) {
341
+ thread_running = 0;
342
+ pthread_exit(NULL);
343
+ }
344
+ if (n > 0) {
345
+ lseek(events.data.fd, 0, SEEK_SET);
346
+ if (read(events.data.fd, &buf, 1) != 1) {
347
+ thread_running = 0;
348
+ pthread_exit(NULL);
349
+ }
350
+ g = get_gpio_from_value_fd(events.data.fd);
351
+ if (g->initial_thread) { // ignore first epoll trigger
352
+ g->initial_thread = 0;
353
+ } else {
354
+ gettimeofday(&tv_timenow, NULL);
355
+ timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec;
356
+ if (g->bouncetime == -666 || timenow - g->lastcall > g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) {
357
+ g->lastcall = timenow;
358
+ event_occurred[g->gpio] = 1;
359
+ run_callbacks(g->gpio);
360
+ }
361
+ }
362
+ }
363
+ }
364
+ thread_running = 0;
365
+ pthread_exit(NULL);
366
+ }
367
+
368
+ void remove_edge_detect(unsigned int gpio)
369
+ {
370
+ struct epoll_event ev;
371
+ struct gpios *g = get_gpio(gpio);
372
+
373
+ if (g == NULL)
374
+ return;
375
+
376
+ // delete epoll of fd
377
+
378
+ ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
379
+ ev.data.fd = g->value_fd;
380
+ epoll_ctl(epfd_thread, EPOLL_CTL_DEL, g->value_fd, &ev);
381
+
382
+ // delete callbacks for gpio
383
+ remove_callbacks(gpio);
384
+
385
+ // btc fixme - check return result??
386
+ gpio_set_edge(gpio, NO_EDGE);
387
+ g->edge = NO_EDGE;
388
+
389
+ if (g->value_fd != -1)
390
+ close(g->value_fd);
391
+
392
+ // btc fixme - check return result??
393
+ gpio_unexport(gpio);
394
+ event_occurred[gpio] = 0;
395
+
396
+ delete_gpio(gpio);
397
+ }
398
+
399
+ int event_detected(unsigned int gpio)
400
+ {
401
+ if (event_occurred[gpio]) {
402
+ event_occurred[gpio] = 0;
403
+ return 1;
404
+ } else {
405
+ return 0;
406
+ }
407
+ }
408
+
409
+ void event_cleanup(unsigned int gpio)
410
+ // gpio of -666 means clean every channel used
411
+ {
412
+ struct gpios *g = gpio_list;
413
+ struct gpios *temp = NULL;
414
+
415
+ while (g != NULL) {
416
+ if ((gpio == -666) || (g->gpio == gpio))
417
+ temp = g->next;
418
+ remove_edge_detect(g->gpio);
419
+ g = temp;
420
+ }
421
+ if (gpio_list == NULL)
422
+ if (epfd_blocking != -1)
423
+ close(epfd_blocking);
424
+ epfd_blocking = -1;
425
+ if (epfd_thread != -1)
426
+ close(epfd_thread);
427
+ epfd_thread = -1;
428
+ thread_running = 0;
429
+ }
430
+
431
+ void event_cleanup_all(void)
432
+ {
433
+ event_cleanup(-666);
434
+ }
435
+
436
+ int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime)
437
+ // return values:
438
+ // 0 - Success
439
+ // 1 - Edge detection already added
440
+ // 2 - Other error
441
+ {
442
+ pthread_t threads;
443
+ struct epoll_event ev;
444
+ long t = 0;
445
+ struct gpios *g;
446
+ int i = -1;
447
+
448
+ i = gpio_event_added(gpio);
449
+ if (i == 0) { // event not already added
450
+ if ((g = new_gpio(gpio)) == NULL)
451
+ return 2;
452
+
453
+ gpio_set_edge(gpio, edge);
454
+ g->edge = edge;
455
+ g->bouncetime = bouncetime;
456
+ } else if (i == edge) { // get existing event
457
+ g = get_gpio(gpio);
458
+ if ((bouncetime != -666 && g->bouncetime != bouncetime) || // different event bouncetime used
459
+ (g->thread_added)) // event already added
460
+ return 1;
461
+ } else {
462
+ return 1;
463
+ }
464
+
465
+ // create epfd_thread if not already open
466
+ if ((epfd_thread == -1) && ((epfd_thread = epoll_create(1)) == -1))
467
+ return 2;
468
+
469
+ // add to epoll fd
470
+ ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
471
+ ev.data.fd = g->value_fd;
472
+ if (epoll_ctl(epfd_thread, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) {
473
+ remove_edge_detect(gpio);
474
+ return 2;
475
+ }
476
+ g->thread_added = 1;
477
+
478
+ // start poll thread if it is not already running
479
+ if (!thread_running) {
480
+ if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0) {
481
+ remove_edge_detect(gpio);
482
+ return 2;
483
+ }
484
+ }
485
+ return 0;
486
+ }
487
+
488
+ int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout)
489
+ // return values:
490
+ // 1 - Success (edge detected)
491
+ // 0 - Timeout
492
+ // -1 - Edge detection already added
493
+ // -2 - Other error
494
+ {
495
+ int n, ed;
496
+ struct epoll_event events, ev;
497
+ char buf;
498
+ struct gpios *g = NULL;
499
+ struct timeval tv_timenow;
500
+ unsigned long long timenow;
501
+ int finished = 0;
502
+ int initial_edge = 1;
503
+
504
+ if (callback_exists(gpio)) {
505
+ return -1;
506
+ }
507
+
508
+ // add gpio if it has not been added already
509
+ ed = gpio_event_added(gpio);
510
+ if (ed == edge) { // get existing record
511
+ g = get_gpio(gpio);
512
+ if (g->bouncetime != -666 && g->bouncetime != bouncetime) {
513
+ return -1;
514
+ }
515
+ } else if (ed == NO_EDGE) { // not found so add event
516
+ if ((g = new_gpio(gpio)) == NULL) {
517
+ return -2;
518
+ }
519
+ gpio_set_edge(gpio, edge);
520
+ g->edge = edge;
521
+ g->bouncetime = bouncetime;
522
+ } else { // ed != edge - event for a different edge
523
+ g = get_gpio(gpio);
524
+ gpio_set_edge(gpio, edge);
525
+ g->edge = edge;
526
+ g->bouncetime = bouncetime;
527
+ g->initial_wait = 1;
528
+ }
529
+
530
+ // create epfd_blocking if not already open
531
+ if ((epfd_blocking == -1) && ((epfd_blocking = epoll_create(1)) == -1)) {
532
+ return -2;
533
+ }
534
+
535
+ // add to epoll fd
536
+ ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
537
+ ev.data.fd = g->value_fd;
538
+ if (epoll_ctl(epfd_blocking, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) {
539
+ return -2;
540
+ }
541
+
542
+ // wait for edge
543
+ while (!finished) {
544
+ if ((n = epoll_wait(epfd_blocking, &events, 1, timeout)) == -1) {
545
+ epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
546
+ return -2;
547
+ }
548
+ if (initial_edge) { // first time triggers with current state, so ignore
549
+ initial_edge = 0;
550
+ } else {
551
+ gettimeofday(&tv_timenow, NULL);
552
+ timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec;
553
+ if (g->bouncetime == -666 || timenow - g->lastcall > g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) {
554
+ g->lastcall = timenow;
555
+ finished = 1;
556
+ }
557
+ }
558
+ }
559
+
560
+ // check event was valid
561
+ if (n > 0) {
562
+ lseek(events.data.fd, 0, SEEK_SET);
563
+ if ((read(events.data.fd, &buf, 1) != 1) || (events.data.fd != g->value_fd)) {
564
+ epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
565
+ return -2;
566
+ }
567
+ }
568
+
569
+ epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev);
570
+ if (n == 0) {
571
+ return 0; // timeout
572
+ } else {
573
+ return 1; // edge found
574
+ }
575
+ }