-
Notifications
You must be signed in to change notification settings - Fork 0
/
cereal.go
253 lines (231 loc) · 6.49 KB
/
cereal.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
package cereal
import (
"errors"
"io"
"strconv"
"github.com/distributed/sers"
goburrow "github.com/goburrow/serial"
tarm "github.com/tarm/serial"
bugst "go.bug.st/serial"
"go.bug.st/serial/enumerator"
)
// Opener is an interface for working with serial port libraries to be able
// to easily interchange them.
//
// It is implemented by the various serial port libraries in this package for convenience.
type Opener interface {
// OpenPort opens a serial port with the given name and mode.
// portname is the name of the port to open, e.g. "/dev/ttyUSB0" or "COM1".
OpenPort(portname string, mode Mode) (io.ReadWriteCloser, error)
}
// PortDetails contains OS provided information on a USB or Serial port.
type PortDetails struct {
Name string
VID, PID uint16
IsUSB bool
}
// ForEachPort calls the given function for each serial port found.
//
// ForEachPort returns early with fn's error if fn returns an error or
// if halt is true.
func ForEachPort(fn func(details PortDetails) (halt bool, err error)) error {
detailedList, err := enumerator.GetDetailedPortsList()
if err != nil {
return err
}
// Add missing non-detailed to the list of detailed ports. On windows COM ports may be missing.
simpleList, err := bugst.GetPortsList()
if err == nil {
for _, portname := range simpleList {
contained := false
for _, detailedPort := range detailedList {
if detailedPort.Name == portname {
contained = true
break
}
}
if !contained {
detailedList = append(detailedList, &enumerator.PortDetails{Name: portname})
}
}
}
for _, port := range detailedList {
vid, _ := strconv.ParseUint(port.VID, 16, 16)
pid, _ := strconv.ParseUint(port.PID, 16, 16)
halt, err := fn(PortDetails{
Name: port.Name,
VID: uint16(vid),
PID: uint16(pid),
IsUSB: port.IsUSB,
})
if err != nil || halt {
return err
}
}
return nil
}
// Bugst implements the Opener interface for the go.bug.st/serial package.
type Bugst struct{}
func (Bugst) String() string { return "bugst" }
func (Bugst) PackagePath() string { return "go.bug.st/serial" }
func (Bugst) OpenPort(portname string, mode Mode) (io.ReadWriteCloser, error) {
if mode.ReadTimeout != 0 {
return nil, errReadTimeoutUnsupportedBugst
}
var parity bugst.Parity
switch mode.Parity {
case ParityNone:
parity = bugst.NoParity
case ParityOdd:
parity = bugst.OddParity
case ParityEven:
parity = bugst.EvenParity
case ParityMark:
parity = bugst.MarkParity
case ParitySpace:
parity = bugst.SpaceParity
default:
return nil, errInvalidParity
}
var stopbits bugst.StopBits
switch mode.StopBits {
case StopBits1:
stopbits = bugst.OneStopBit
case StopBits1Half:
stopbits = bugst.OnePointFiveStopBits
case StopBits2:
stopbits = bugst.TwoStopBits
default:
return nil, errInvalidStopbits
}
return bugst.Open(portname, &bugst.Mode{
BaudRate: mode.BaudRate,
DataBits: mode.DataBits,
Parity: parity,
StopBits: stopbits,
})
}
// Tarm implements the Opener interface for the github.com/tarm/serial package.
type Tarm struct{}
func (Tarm) String() string { return "tarm" }
func (Tarm) PackagePath() string { return "github.com/tarm/serial" }
func (Tarm) OpenPort(portname string, mode Mode) (io.ReadWriteCloser, error) {
var parity tarm.Parity = tarm.Parity(mode.Parity.Char())
return tarm.OpenPort(&tarm.Config{
Name: portname,
Baud: mode.BaudRate,
Size: byte(mode.DataBits),
Parity: parity,
ReadTimeout: mode.ReadTimeout,
StopBits: func() tarm.StopBits {
switch mode.StopBits {
case StopBits1:
return tarm.Stop1
case StopBits1Half:
return tarm.Stop1Half
case StopBits2:
return tarm.Stop2
default:
return 0
}
}(),
})
}
// Goburrow implements the Opener interface for the github.com/goburrow/serial package.
type Goburrow struct{}
func (Goburrow) String() string { return "goburrow" }
func (Goburrow) PackagePath() string { return "github.com/goburrow/serial" }
func (Goburrow) OpenPort(portname string, mode Mode) (io.ReadWriteCloser, error) {
if mode.StopBits == StopBits1Half {
return nil, errUnsupportedStopbits
}
return goburrow.Open(&goburrow.Config{
Address: portname,
BaudRate: mode.BaudRate,
DataBits: mode.DataBits,
StopBits: mode.StopBits.Halves() / 2,
Parity: string(mode.Parity.Char()),
Timeout: mode.ReadTimeout,
})
}
// Sers implements the Opener interface for the github.com/distributed/sers package.
type Sers struct{}
func (Sers) String() string { return "sers" }
func (Sers) PackagePath() string { return "github.com/distributed/sers" }
func (Sers) OpenPort(portname string, mode Mode) (io.ReadWriteCloser, error) {
sp, err := openSers(portname)
if err != nil {
return nil, err
}
if mode.ReadTimeout != 0 {
err = sp.SetReadParams(0, mode.ReadTimeout.Seconds())
if err != nil {
return nil, err
}
}
var parity, stopbits, databits int
if databits == 0 {
databits = 8
}
switch mode.Parity {
case ParityNone:
parity = sers.N
case ParityOdd:
parity = sers.O
case ParityEven:
parity = sers.E
case ParityMark, ParitySpace:
return nil, errUnsupportedParity
default:
return nil, errInvalidParity
}
switch mode.StopBits {
case StopBits1:
stopbits = 1
case StopBits2:
stopbits = 2
case StopBits1Half:
return nil, errUnsupportedStopbits
default:
return nil, errInvalidStopbits
}
err = sp.SetMode(mode.BaudRate, databits, parity, stopbits, sers.NO_HANDSHAKE)
if err != nil {
sp.Close() // ensure we close the port on error.
return nil, err
}
return sp, nil
}
// ResetInputBuffer discards data received but not read by the port. It expects a port type
// or an interface that implements `Reset()`/`Reset() error`/`ResetInputBuffer() error`. An error is returned
// if the functionality is not implemented by the port.
func ResetInputBuffer(port io.Reader) error {
// Test for common ports
switch r := port.(type) {
case sers.SerialPort, *tarm.Port, goburrow.Port:
return errors.New("cereal: sers/tarm/goburrow does not support ResetInputBuffer")
case bugst.Port:
return r.ResetInputBuffer()
case *NonBlocking:
r.Reset()
return nil
}
type resetter interface {
Reset()
}
type resetterErr interface {
Reset() error
}
type resetInputBuffer interface {
ResetInputBuffer() error
}
if r, ok := port.(resetter); ok {
r.Reset()
return nil
} else if r, ok := port.(resetterErr); ok {
return r.Reset()
} else if r, ok := port.(resetInputBuffer); ok {
return r.ResetInputBuffer()
}
return errors.New("cereal: ResetInputBuffer not implemented by argument")
}