-
Notifications
You must be signed in to change notification settings - Fork 42
/
options.go
654 lines (550 loc) · 18.8 KB
/
options.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
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
// SPDX-FileCopyrightText: 2019 Kent Gibson <[email protected]>
//
// SPDX-License-Identifier: MIT
package gpiocdev
import (
"time"
"github.com/warthog618/go-gpiocdev/uapi"
)
// ChipOption defines the interface required to provide a Chip option.
type ChipOption interface {
applyChipOption(*ChipOptions)
}
// ChipOptions contains the options for a Chip.
type ChipOptions struct {
consumer string
config LineConfig
abi int
eh EventHandler
}
// ConsumerOption defines the consumer label for a line.
type ConsumerOption string
// WithConsumer provides the consumer label for the line.
//
// When applied to a chip it provides the default consumer label for all lines
// requested by the chip.
func WithConsumer(consumer string) ConsumerOption {
return ConsumerOption(consumer)
}
func (o ConsumerOption) applyChipOption(c *ChipOptions) {
c.consumer = string(o)
}
func (o ConsumerOption) applyLineReqOption(l *lineReqOptions) {
l.consumer = string(o)
}
// LineReqOption defines the interface required to provide an option for Line and
// Lines as part of a line request.
type LineReqOption interface {
applyLineReqOption(*lineReqOptions)
}
// LineConfigOption defines the interface required to update an option for
// Line and Lines.
type LineConfigOption interface {
applyLineConfigOption(*lineConfigOptions)
}
// SubsetLineConfigOption defines the interface required to update an option for a
// subset of requested lines.
type SubsetLineConfigOption interface {
applySubsetLineConfigOption([]int, *lineConfigOptions)
}
// lineReqOptions contains the options for a Line(s) request.
type lineReqOptions struct {
lineConfigOptions
consumer string
abi int
eh EventHandler
eventBufferSize int
}
// lineConfigOptions contains the configuration options for a Line(s) reconfigure.
type lineConfigOptions struct {
offsets []int
values map[int]int
defCfg LineConfig
lineCfg map[int]*LineConfig
}
func (lco *lineConfigOptions) lineConfig(offset int) *LineConfig {
if lco.lineCfg == nil {
lco.lineCfg = map[int]*LineConfig{}
}
lc := lco.lineCfg[offset]
if lc == nil {
tlc := lco.defCfg
lc = &tlc
lco.lineCfg[offset] = lc
}
return lc
}
func (lco lineConfigOptions) outputValues() uapi.OutputValues {
ov := uapi.LineBitmap(0)
for idx, val := range lco.offsets {
ov = ov.Set(idx, lco.values[val])
}
return uapi.OutputValues(ov)
}
type lineConfigAttributes []uapi.LineConfigAttribute
func (lca lineConfigAttributes) append(attr uapi.LineAttribute, mask uapi.LineBitmap) lineConfigAttributes {
for idx, cae := range lca {
if cae.Attr.ID == attr.ID {
lca[idx].Mask &^= mask
}
}
for idx, cae := range lca {
if cae.Attr == attr {
lca[idx].Mask |= mask
return lca
}
}
return append(lca, uapi.LineConfigAttribute{Attr: attr, Mask: mask})
}
func (lco lineConfigOptions) toULineConfig() (ulc uapi.LineConfig, err error) {
mask := uapi.NewLineBitMask(len(lco.offsets))
cfgAttrs := lineConfigAttributes{
// first cfg slot reserved for default flags
uapi.LineConfigAttribute{Attr: uapi.LineFlagV2(0).Encode(), Mask: mask},
}
attrs := lco.defCfg.toLineAttributes()
for _, attr := range attrs {
if attr.ID == uapi.LineAttributeIDFlags {
cfgAttrs[0].Attr = attr
} else {
cfgAttrs = cfgAttrs.append(attr, mask)
}
}
var outputMask uapi.LineBitmap
if lco.defCfg.Direction == LineDirectionOutput {
outputMask = mask
}
for idx, offset := range lco.offsets {
cfg := lco.lineCfg[offset]
if cfg == nil {
continue
}
mask = uapi.LineBitmap(1) << uint(idx)
attrs = cfg.toLineAttributes()
for _, attr := range attrs {
cfgAttrs = cfgAttrs.append(attr, mask)
}
if cfg.Direction == LineDirectionOutput {
outputMask |= mask
} else {
outputMask &^= mask
}
}
var defFlags uapi.LineFlagV2
defFlags.Decode(cfgAttrs[0].Attr)
// replace default flags in slot 0 with outputValues
cfgAttrs[0].Attr = lco.outputValues().Encode()
cfgAttrs[0].Mask = outputMask
// filter mask==0 entries
loopAttrs := cfgAttrs
cfgAttrs = cfgAttrs[:0]
for _, attr := range loopAttrs {
if attr.Mask != 0 {
cfgAttrs = append(cfgAttrs, attr)
}
}
if len(cfgAttrs) > 10 {
err = ErrConfigOverflow
return
}
ulc.Flags = defFlags
ulc.NumAttrs = uint32(len(cfgAttrs))
copy(ulc.Attrs[:], cfgAttrs)
return
}
// EventHandler is a receiver for line events.
type EventHandler func(LineEvent)
// AsIsOption indicates the line direction should be left as is.
type AsIsOption int
// AsIs indicates that a line be requested as neither an input or output.
//
// That is its direction is left as is. This option overrides and clears any
// previous Input or Output options.
const AsIs = AsIsOption(0)
func (o AsIsOption) applyLineReqOption(l *lineReqOptions) {
l.defCfg.Direction = LineDirectionUnknown
}
// InputOption indicates the line direction should be set to an input.
type InputOption int
// AsInput indicates that a line be requested as an input.
//
// This option overrides and clears any previous Output, OpenDrain, or
// OpenSource options.
const AsInput = InputOption(0)
func (o InputOption) applyLineConfig(lc *LineConfig) {
lc.Direction = LineDirectionInput
lc.Drive = LineDrivePushPull
}
func (o InputOption) applyChipOption(c *ChipOptions) {
c.config.Direction = LineDirectionInput
}
func (o InputOption) applyLineReqOption(lro *lineReqOptions) {
o.applyLineConfigOption(&lro.lineConfigOptions)
}
func (o InputOption) applyLineConfigOption(lco *lineConfigOptions) {
o.applyLineConfig(&lco.defCfg)
}
func (o InputOption) applySubsetLineConfigOption(offsets []int, l *lineConfigOptions) {
for _, offset := range offsets {
o.applyLineConfig(l.lineConfig(offset))
}
}
// OutputOption indicates the line direction should be set to an output.
type OutputOption []int
// AsOutput indicates that a line or lines be requested as an output.
//
// The initial active state for the line(s) can optionally be provided.
// If fewer values are provided than lines then the remaining lines default to
// inactive.
//
// This option overrides and clears any previous Input, RisingEdge, FallingEdge,
// BothEdges, or Debounce options.
func AsOutput(values ...int) OutputOption {
vv := append([]int(nil), values...)
return OutputOption(vv)
}
func (o OutputOption) applyLineReqOption(lro *lineReqOptions) {
o.applyLineConfigOption(&lro.lineConfigOptions)
}
func (o OutputOption) applyLineConfig(lc *LineConfig) {
lc.Direction = LineDirectionOutput
lc.Debounced = false
lc.DebouncePeriod = 0
lc.EdgeDetection = LineEdgeNone
}
func (o OutputOption) applyLineConfigOption(lco *lineConfigOptions) {
o.applyLineConfig(&lco.defCfg)
for idx, value := range o {
lco.values[lco.offsets[idx]] = value
}
}
func (o OutputOption) applySubsetLineConfigOption(offsets []int, lco *lineConfigOptions) {
for idx, offset := range offsets {
o.applyLineConfig(lco.lineConfig(offset))
lco.values[offset] = o[idx]
}
}
// LevelOption determines the line level that is considered active.
type LevelOption bool
func (o LevelOption) applyChipOption(c *ChipOptions) {
c.config.ActiveLow = bool(o)
}
func (o LevelOption) applyLineReqOption(lro *lineReqOptions) {
lro.defCfg.ActiveLow = bool(o)
}
func (o LevelOption) applyLineConfigOption(lco *lineConfigOptions) {
lco.defCfg.ActiveLow = bool(o)
}
func (o LevelOption) applySubsetLineConfigOption(offsets []int, lco *lineConfigOptions) {
for _, offset := range offsets {
lco.lineConfig(offset).ActiveLow = bool(o)
}
}
// AsActiveLow indicates that a line be considered active when the line level
// is low.
const AsActiveLow = LevelOption(true)
// AsActiveHigh indicates that a line be considered active when the line level
// is high.
//
// This is the default active level.
const AsActiveHigh = LevelOption(false)
func (o LineDrive) applyLineConfig(lc *LineConfig) {
lc.Drive = o
lc.Direction = LineDirectionOutput
lc.Debounced = false
lc.DebouncePeriod = 0
lc.EdgeDetection = LineEdgeNone
}
func (o LineDrive) applyChipOption(c *ChipOptions) {
o.applyLineConfig(&c.config)
}
func (o LineDrive) applyLineReqOption(lro *lineReqOptions) {
o.applyLineConfig(&lro.defCfg)
}
func (o LineDrive) applyLineConfigOption(lco *lineConfigOptions) {
o.applyLineConfig(&lco.defCfg)
}
func (o LineDrive) applySubsetLineConfigOption(offsets []int, lco *lineConfigOptions) {
for _, offset := range offsets {
o.applyLineConfig(lco.lineConfig(offset))
}
}
// AsOpenDrain indicates that a line be driven low but left floating for high.
//
// This option sets the Output option and overrides and clears any previous
// Input, RisingEdge, FallingEdge, BothEdges, OpenSource, or Debounce options.
const AsOpenDrain = LineDriveOpenDrain
// AsOpenSource indicates that a line be driven high but left floating for low.
//
// This option sets the Output option and overrides and clears any previous
// Input, RisingEdge, FallingEdge, BothEdges, OpenDrain, or Debounce options.
const AsOpenSource = LineDriveOpenSource
// AsPushPull indicates that a line be driven both low and high.
//
// This option sets the Output option and overrides and clears any previous
// Input, RisingEdge, FallingEdge, BothEdges, OpenDrain, OpenSource or Debounce
// options.
const AsPushPull = LineDrivePushPull
func (o LineBias) applyChipOption(c *ChipOptions) {
c.config.Bias = o
}
func (o LineBias) applyLineReqOption(lro *lineReqOptions) {
lro.defCfg.Bias = o
}
func (o LineBias) applyLineConfigOption(lco *lineConfigOptions) {
lco.defCfg.Bias = o
}
func (o LineBias) applySubsetLineConfigOption(offsets []int, lco *lineConfigOptions) {
for _, offset := range offsets {
lco.lineConfig(offset).Bias = o
}
}
// WithBiasAsIs indicates that a line have its internal bias left unchanged.
//
// This option corresponds to the default bias configuration and its only useful
// application is to clear any previous bias option in a chain of LineOptions,
// before that configuration is applied.
//
// Requires Linux 5.5 or later.
const WithBiasAsIs = LineBiasUnknown
// WithBiasDisabled indicates that a line have its internal bias disabled.
//
// This option overrides and clears any previous bias options.
//
// Requires Linux 5.5 or later.
const WithBiasDisabled = LineBiasDisabled
// WithPullDown indicates that a line have its internal pull-down enabled.
//
// This option overrides and clears any previous bias options.
//
// Requires Linux 5.5 or later.
const WithPullDown = LineBiasPullDown
// WithPullUp indicates that a line have its internal pull-up enabled.
//
// This option overrides and clears any previous bias options.
//
// Requires Linux 5.5 or later.
const WithPullUp = LineBiasPullUp
func (o EventHandler) applyChipOption(c *ChipOptions) {
c.eh = o
}
func (o EventHandler) applyLineReqOption(lro *lineReqOptions) {
lro.eh = o
}
// WithEventHandler indicates that a line will generate events when its active
// state transitions from high to low.
//
// Events are forwarded to the provided handler function.
//
// To maintain event ordering, the event handler is called serially for each
// event from the requested lines. To minimize the possibility of overflowing
// the queue of events in the kernel, the event handler should handle or
// hand-off the event and return as soon as possible.
//
// Note that calling Close on the requested line from within the event handler
// will result in deadlock, as the Close waits for the event handler to
// return. Therefore the Close must be called from a different goroutine.
func WithEventHandler(e EventHandler) EventHandler {
return e
}
func (o LineEdge) applyLineConfig(lc *LineConfig) {
lc.EdgeDetection = o
lc.Direction = LineDirectionInput
}
func (o LineEdge) applyLineReqOption(lro *lineReqOptions) {
o.applyLineConfig(&lro.defCfg)
}
func (o LineEdge) applyLineConfigOption(lco *lineConfigOptions) {
o.applyLineConfig(&lco.defCfg)
}
func (o LineEdge) applySubsetLineConfigOption(offsets []int, lco *lineConfigOptions) {
for _, offset := range offsets {
o.applyLineConfig(lco.lineConfig(offset))
}
}
// WithFallingEdge indicates that a line will generate events when its active
// state transitions from high to low.
//
// Events are forwarded to the provided handler function.
//
// This option sets the Input option and overrides and clears any previous
// Output, OpenDrain, or OpenSource options.
const WithFallingEdge = LineEdgeFalling
// WithRisingEdge indicates that a line will generate events when its active
// state transitions from low to high.
//
// Events are forwarded to the provided handler function.
//
// This option sets the Input option and overrides and clears any previous
// Output, OpenDrain, or OpenSource options.
const WithRisingEdge = LineEdgeRising
// WithBothEdges indicates that a line will generate events when its active
// state transitions from low to high and from high to low.
//
// Events are forwarded to the provided handler function.
//
// This option sets the Input option and overrides and clears any previous
// Output, OpenDrain, or OpenSource options.
const WithBothEdges = LineEdgeBoth
// WithoutEdges indicates that a line will not generate events due to active
// state transitions.
//
// This is the default for line requests, but allows the removal of edge
// detection by reconfigure.
//
// This option sets the Input option and overrides and clears any previous
// Output, OpenDrain, or OpenSource options.
//
// The WithoutEdges option requires Linux 5.10 or later.
const WithoutEdges = LineEdgeNone
func (o LineEventClock) applyChipOption(c *ChipOptions) {
c.config.EventClock = LineEventClock(o)
}
func (o LineEventClock) applyLineReqOption(lro *lineReqOptions) {
lro.defCfg.EventClock = LineEventClock(o)
}
func (o LineEventClock) applyLineConfigOption(lco *lineConfigOptions) {
lco.defCfg.EventClock = LineEventClock(o)
}
func (o LineEventClock) applySubsetLineConfigOption(offsets []int, lco *lineConfigOptions) {
for _, offset := range offsets {
lco.lineConfig(offset).EventClock = LineEventClock(o)
}
}
// WithMonotonicEventClock specifies that the edge event timestamps are sourced
// from CLOCK_MONOTONIC.
//
// This option corresponds to the default event clock configuration and its only
// useful application is to clear any previous event clock option in a chain of
// LineOptions, before that configuration is applied.
const WithMonotonicEventClock = LineEventClockMonotonic
// WithRealtimeEventClock specifies that the edge event timestamps are sourced
// from CLOCK_REALTIME.
//
// Requires Linux 5.11 or later.
const WithRealtimeEventClock = LineEventClockRealtime
// DebounceOption indicates that a line will be debounced.
//
// The DebounceOption requires Linux 5.10 or later.
type DebounceOption time.Duration
func (o DebounceOption) applyLineConfig(lc *LineConfig) {
lc.Direction = LineDirectionInput
lc.Debounced = true
lc.DebouncePeriod = time.Duration(o)
}
func (o DebounceOption) applyLineReqOption(lro *lineReqOptions) {
o.applyLineConfig(&lro.defCfg)
}
func (o DebounceOption) applyLineConfigOption(lco *lineConfigOptions) {
o.applyLineConfig(&lco.defCfg)
}
func (o DebounceOption) applySubsetLineConfigOption(offsets []int, lco *lineConfigOptions) {
for _, offset := range offsets {
o.applyLineConfig(lco.lineConfig(offset))
}
}
// WithDebounce indicates that a line will be debounced with the specified
// debounce period.
//
// This option sets the Input option and overrides and clears any previous
// Output, OpenDrain, or OpenSource options.
//
// Requires Linux 5.10 or later.
func WithDebounce(period time.Duration) DebounceOption {
return DebounceOption(period)
}
// ABIVersionOption selects the version of the GPIO ioctl commands to use.
//
// The default is to use the latest version supported by the kernel.
type ABIVersionOption int
func (o ABIVersionOption) applyChipOption(c *ChipOptions) {
c.abi = int(o)
}
func (o ABIVersionOption) applyLineReqOption(l *lineReqOptions) {
l.abi = int(o)
}
// WithABIVersion indicates the version of the GPIO ioctls to use.
//
// The default is to use the latest version supported by the kernel.
//
// ABI version 2 requires Linux 5.10 or later.
func WithABIVersion(version int) ABIVersionOption {
return ABIVersionOption(version)
}
// LinesOption specifies line options that are to be applied to a subset of
// the lines in a request.
type LinesOption struct {
offsets []int
options []SubsetLineConfigOption
}
func (o LinesOption) applyLineReqOption(lro *lineReqOptions) {
o.applyLineConfigOption(&lro.lineConfigOptions)
}
func (o LinesOption) applyLineConfigOption(lco *lineConfigOptions) {
for _, option := range o.options {
option.applySubsetLineConfigOption(o.offsets, lco)
}
}
// WithLines specifies line options to be applied to a subset of the lines in a
// request.
//
// The offsets should be a strict subset of the offsets provided to
// RequestLines().
// Any offsets outside that set are ignored.
func WithLines(offsets []int, options ...SubsetLineConfigOption) LinesOption {
return LinesOption{offsets, options}
}
// DefaultedOption resets the configuration to default values.
type DefaultedOption int
func (o DefaultedOption) applyLineReqOption(lro *lineReqOptions) {
o.applyLineConfigOption(&lro.lineConfigOptions)
}
func (o DefaultedOption) applyLineConfigOption(lco *lineConfigOptions) {
lco.defCfg = LineConfig{}
lco.values = map[int]int{}
}
func (o DefaultedOption) applySubsetLineConfigOption(offsets []int, lco *lineConfigOptions) {
if len(offsets) == 0 {
lco.lineCfg = nil
}
for _, offset := range offsets {
delete(lco.values, offset)
delete(lco.lineCfg, offset)
}
}
// Defaulted resets all configuration options to default values.
//
// This option provides the means to simply reset all configuration options to
// their default values. This is rarely necessary but is made available for
// completeness.
//
// When applied within WithLines() it resets the configuration of the lines to
// the default for the request, effectively clearing all previous WithLines()
// options for the specified offsets. If no offsets are specified then the
// configuration for all offsets is reset to the request default.
//
// When applied outside WithLines() it resets the default configuration for the
// request itself to default values but leaves any configuration set within
// WithLines() unchanged.
const Defaulted = DefaultedOption(0)
// EventBufferSizeOption provides a suggested minimum number of events the
// kernel will buffer for the line request.
//
// The EventBufferSizeOption requires Linux 5.10 or later.
type EventBufferSizeOption int
func (o EventBufferSizeOption) applyLineReqOption(lro *lineReqOptions) {
lro.eventBufferSize = int(o)
}
// WithEventBufferSize suggests a minimum number of events the kernel will
// buffer for the line request.
//
// Note that the value is only a suggestion, and the kernel may set higher
// values or place a cap on the buffer size.
//
// A zero value (the default) indicates that the kernel should use its default
// buffer size (the number of requested lines * 16).
//
// Requires Linux 5.10 or later.
func WithEventBufferSize(size int) EventBufferSizeOption {
return EventBufferSizeOption(size)
}