-
Notifications
You must be signed in to change notification settings - Fork 109
/
digital_interrupts.go
145 lines (125 loc) · 4.27 KB
/
digital_interrupts.go
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
//go:build linux
// Package genericlinux is for Linux boards, and this particular file is for digital interrupt pins
// using the ioctl interface, indirectly by way of mkch's gpio package.
package genericlinux
import (
"context"
"sync"
"github.com/mkch/gpio"
"github.com/pkg/errors"
"go.uber.org/multierr"
"go.viam.com/utils"
"go.viam.com/rdk/components/board"
)
type digitalInterrupt struct {
boardWorkers *sync.WaitGroup
interrupt board.ReconfigurableDigitalInterrupt
line *gpio.LineWithEvent
cancelCtx context.Context
cancelFunc func()
config *board.DigitalInterruptConfig
}
func (b *Board) createDigitalInterrupt(
ctx context.Context,
config board.DigitalInterruptConfig,
gpioMappings map[string]GPIOBoardMapping,
// If we are reconfiguring a board, we might already have channels subscribed and listening for
// updates from an old interrupt that we're creating on a new pin. In that case, reuse the part
// that holds the callbacks.
oldCallbackHolder board.ReconfigurableDigitalInterrupt,
) (*digitalInterrupt, error) {
mapping, ok := gpioMappings[config.Pin]
if !ok {
return nil, errors.Errorf("unknown interrupt pin %s", config.Pin)
}
chip, err := gpio.OpenChip(mapping.GPIOChipDev)
if err != nil {
return nil, err
}
defer utils.UncheckedErrorFunc(chip.Close)
line, err := chip.OpenLineWithEvents(
uint32(mapping.GPIO), gpio.Input, gpio.BothEdges, "viam-interrupt")
if err != nil {
return nil, err
}
var interrupt board.ReconfigurableDigitalInterrupt
if oldCallbackHolder == nil {
interrupt, err = board.CreateDigitalInterrupt(config)
if err != nil {
return nil, multierr.Combine(err, line.Close())
}
} else {
interrupt = oldCallbackHolder
if err := interrupt.Reconfigure(config); err != nil {
return nil, err // Should never have errors, but this makes the linter happy
}
}
cancelCtx, cancelFunc := context.WithCancel(ctx)
result := digitalInterrupt{
boardWorkers: &b.activeBackgroundWorkers,
interrupt: interrupt,
line: line,
cancelCtx: cancelCtx,
cancelFunc: cancelFunc,
config: &config,
}
result.startMonitor()
return &result, nil
}
func (di *digitalInterrupt) startMonitor() {
di.boardWorkers.Add(1)
utils.ManagedGo(func() {
for {
select {
case <-di.cancelCtx.Done():
return
case event := <-di.line.Events():
utils.UncheckedError(di.interrupt.Tick(
di.cancelCtx, event.RisingEdge, uint64(event.Time.UnixNano())))
}
}
}, di.boardWorkers.Done)
}
func (di *digitalInterrupt) Close() error {
// We shut down the background goroutine that monitors this interrupt, but don't need to wait
// for it to finish shutting down because it doesn't use anything in the line itself (just a
// channel of events that the line generates). It will shut down sometime soon, and if that's
// after the line is closed, that's fine.
di.cancelFunc()
return di.line.Close()
}
// struct implements board.GPIOPin to support reading current state of digital interrupt pins as GPIO inputs.
type gpioInterruptWrapperPin struct {
interrupt digitalInterrupt
}
func (gp gpioInterruptWrapperPin) Set(
ctx context.Context, isHigh bool, extra map[string]interface{},
) error {
return errors.New("cannot set value of a digital interrupt pin")
}
func (gp gpioInterruptWrapperPin) Get(ctx context.Context, extra map[string]interface{}) (result bool, err error) {
value, err := gp.interrupt.line.Value()
if err != nil {
return false, err
}
// We'd expect value to be either 0 or 1, but any non-zero value should be considered high.
return (value != 0), nil
}
func (gp gpioInterruptWrapperPin) PWM(ctx context.Context, extra map[string]interface{}) (float64, error) {
return 0, errors.New("cannot get PWM of a digital interrupt pin")
}
func (gp gpioInterruptWrapperPin) SetPWM(
ctx context.Context, dutyCyclePct float64, extra map[string]interface{},
) error {
return errors.New("cannot set PWM of a digital interrupt pin")
}
func (gp gpioInterruptWrapperPin) PWMFreq(
ctx context.Context, extra map[string]interface{},
) (uint, error) {
return 0, errors.New("cannot get PWM freq of a digital interrupt pin")
}
func (gp gpioInterruptWrapperPin) SetPWMFreq(
ctx context.Context, freqHz uint, extra map[string]interface{},
) error {
return errors.New("cannot set PWM freq of a digital interrupt pin")
}