rpi_gpio 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
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);
@@ -0,0 +1,605 @@
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
+ }