-
Notifications
You must be signed in to change notification settings - Fork 3
/
event.go
125 lines (113 loc) · 2.64 KB
/
event.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
package gpio
import (
"fmt"
"os"
"sync/atomic"
"syscall"
"time"
"unsafe"
"github.com/juju/errors"
)
func (c *chip) GetLineEvent(line uint32, flag RequestFlag, events EventFlag, consumerLabel string) (Eventer, error) {
if !c.fa.incref() {
return nil, ErrClosed
}
req := EventRequest{
LineOffset: line,
RequestFlags: GPIOHANDLE_REQUEST_INPUT | flag,
EventFlags: events,
}
copy(req.ConsumerLabel[:], []byte(consumerLabel))
err := RawGetLineEvent(c.fa.fd, &req)
if err != nil {
c.fa.decref()
err = errors.Annotate(err, "GPIO_GET_LINEEVENT_IOCTL")
return nil, err
}
if err := syscall.SetNonblock(int(req.Fd), true); err != nil {
c.fa.decref()
err = errors.Annotate(err, "SetNonblock")
return nil, err
}
le := &lineEvent{
chip: c,
f: os.NewFile(uintptr(req.Fd), fmt.Sprintf("gpio:event:%d", line)),
reqFlag: req.RequestFlags,
events: req.EventFlags,
line: line,
}
// runtime.SetFinalizer(le, func(le *lineEvent) { le.Close() })
return le, nil
}
type lineEvent struct {
chip *chip
f *os.File
reqFlag RequestFlag
events EventFlag
line uint32
closed uint32
}
func (self *lineEvent) Close() error {
if atomic.AddUint32(&self.closed, 1) == 1 {
// _ = self.f.SetDeadline(time.Time{})
// _ = syscall.SetNonblock(int(self.f.Fd()), false)
err := self.f.Close()
self.chip.fa.decref()
return err
}
return ErrClosed
}
func (self *lineEvent) Read() (byte, error) {
var data HandleData
err := RawGetLineValues(int(self.f.Fd()), &data)
if err != nil {
err = errors.Annotate(err, "event.Read")
}
return data.Values[0], err
}
func (self *lineEvent) Wait(timeout time.Duration) (EventData, error) {
const tag = "event.Wait"
var deadline time.Time
var e EventData
var err error
if timeout != 0 {
deadline = time.Now().Add(timeout)
}
if err = self.f.SetDeadline(deadline); err != nil {
err = errors.Annotate(err, tag)
return e, err
}
e, err = self.readEvent()
// specifically don't annotate timeout, to ease external code checks
if err == ErrTimeout {
return e, err
}
if err != nil {
err = errors.Annotate(err, tag)
}
return e, err
}
func (self *lineEvent) readEvent() (EventData, error) {
// dance around File.Read []byte
const esz = int(unsafe.Sizeof(EventData{}))
type eventBuf [esz]byte
var buf eventBuf
var e EventData
var n int
var err error
n, err = self.f.Read(buf[:])
// n, err = f.ReadAt(buf[:], 0)
if IsTimeout(err) {
return e, ErrTimeout
}
if err != nil {
return e, err
}
if n != esz {
err = errors.Errorf("readEvent fail n=%d expected=%d", n, esz)
return e, err
}
eb := (*eventBuf)(unsafe.Pointer(&e))
copy((*eb)[:], buf[:])
return e, nil
}