summaryrefslogtreecommitdiff
path: root/gpio.scm
blob: ca51064c5f398a02f43f3f173d663bb31543740d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
;;; guile-gpio --- Guile interface to the Linux GPIO Sysfs API
;;; Copyright © 2016 David Thompson <davet@gnu.org>
;;;
;;; This file is part of guile-gpio.
;;;
;;; Guile-gpio is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU Lesser General Public License as
;;; published by the Free Software Foundation; either version 3 of the
;;; License, or (at your option) any later version.
;;;
;;; Guile-gpio is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU Lesser General Public
;;; License along with guile-gpio.  If not, see
;;; <http://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; Wrapper over the Linux GPIO Sysfs API.
;;
;;; Code:

(define-module (gpio)
  #:use-module (ice-9 match)
  #:use-module (srfi srfi-9)
  #:export (make-pin
            pin?
            interrupt-generating-pin?
            pin-number
            pin-exported?
            pin-export!
            pin-unexport!
            pin-high?
            pin-low?
            pin-set!
            pin-edge
            set-pin-edge!
            pin-direction
            set-pin-direction!
            call-with-pin
            with-pins))

(define-record-type <pin>
  (make-pin number)
  pin?
  (number pin-number))

(define %gpio-sysfs "/sys/class/gpio")

(define (read-boolean port)
  "Read boolean value, as specified by the GPIO Sysfs API, from PORT.
The value is represented as a single character: \"1\" for true and
\"0\" for false, naturally."
  (eqv? (read-char port) #\1))

(define (pin-directory pin)
  "Return the directory name associated with PIN in the GPIO Sysfs
interface."
  (string-append %gpio-sysfs "/gpio" (number->string (pin-number pin))))

(define (pin-value-file pin)
  "Return the file name where the value of PIN is stored."
  (string-append (pin-directory pin) "/value"))

(define (pin-edge-file pin)
  "Return the file name where the edge of PIN is stored."
  (string-append (pin-directory pin) "/edge"))

(define (pin-direction-file pin)
  "Return the file name where the direction of PIN is stored."
  (string-append (pin-directory pin) "/direction"))

(define (pin-exported? pin)
  "Return #t if PIN has been exported, meaning that it can be
manipulated in userspace."
  (file-exists? (pin-directory pin)))

(define pin-export!
  (let ((export-file (string-append %gpio-sysfs "/export")))
    (lambda (pin)
      "Export PIN so that it may be manipulated in userspace."
      (call-with-output-file export-file
        (lambda (port)
          (display (number->string (pin-number pin)) port))))))

(define pin-unexport!
  (let ((unexport-file (string-append %gpio-sysfs "/unexport")))
    (lambda (pin)
      "Relinquish userspace control of PIN."
      (call-with-output-file unexport-file
        (lambda (port)
          (display (number->string (pin-number pin)) port))))))

(define (pin-high? pin)
  "Return if PIN is set to a high value."
  (call-with-input-file (pin-value-file pin) read-boolean))

(define (pin-low? pin)
  "Return #t if PIN is set to a low value."
  (not (pin-high? pin)))

(define (pin-set! pin high?)
  "Set PIN to a high value if HIGH?, otherwise set PIN to a low
value."
  (call-with-output-file (pin-value-file pin)
    (lambda (port)
      (display (if high? "1" "0") port))))

(define (interrupt-generating-pin? pin)
  "Return #t if PIN is capable of generating interrupts."
  (file-exists? (pin-edge-file pin)))

(define (pin-edge pin)
  "Return the signal edges of PIN that trigger interrupts.  The edge
type may be one of four possible values: none, rising, falling, or
both."
  (call-with-input-file (pin-edge-file pin) read))

(define (set-pin-edge! pin edge)
  "Set the signal EDGE type for PIN that will trigger an interrupt.
The edge type may be one of four possible values: none, rising,
falling, or both.  PIN must be an input pin."
  (match edge
    ((or 'none 'rising 'falling 'both)
     (call-with-output-file (pin-edge-file pin)
       (lambda (port)
         (write edge port))))))

(define (pin-direction pin)
  "Return the direction of PIN, either 'in' or 'out'."
  (call-with-input-file (pin-direction-file pin) read))

(define (set-pin-direction! pin direction)
  "Set the direction of PIN to DIRECTION, either 'in' or 'out'."
  (match direction
    ((or 'in 'out)
     (call-with-output-file (pin-direction-file pin)
       (lambda (port)
         (write direction port))))))

(define* (call-with-pin number proc #:key direction edge)
  "Import GPIO pin NUMBER and apply PROC with an object representing
that pin.  When PROC exits, the pin is unexported.  If DIRECTION is
specified, set the pin to that direction, otherwise use the system
default.  If EDGE is specified, set the pin to that edge, otherwise
use the system default.  Only interrupt generating input pins may
specify a value for EDGE."
  (let ((pin (make-pin number)))
    ;; Try to export before entering the dynamic-wind.  This way, if
    ;; the pin was already exported it won't be automatically
    ;; unexported, potentially screwing up some other process that was
    ;; using it.
    (pin-export! pin)
    (dynamic-wind
      (const #t)
      (lambda ()
        ;; It's important to set the direction first, because only
        ;; input pins may set their edge.
        (when direction (set-pin-direction! pin direction))
        (when edge (set-pin-edge! pin edge))
        (proc pin))
      (lambda ()
        (pin-unexport! pin)))))

(define-syntax with-pins
  (syntax-rules ()
    ((_ () body ...)
     (begin body ...))
    ((_ ((name number kwargs ...) . rest) body ...)
     (call-with-pin number
       (lambda (name)
         (with-pins rest body ...))
       kwargs ...))))