forked from grid-x/serial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
serial_windows.go
169 lines (156 loc) · 3.51 KB
/
serial_windows.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
package serial
import (
"fmt"
"syscall"
)
type port struct {
handle syscall.Handle
oldDCB c_DCB
oldTimeouts c_COMMTIMEOUTS
}
// New allocates and returns a new serial port controller.
func newPort() *port {
return &port{
handle: syscall.InvalidHandle,
}
}
// Open connects to the given serial port.
func open(c *Config) (Port, error) {
p := newPort()
h, err := newHandle(c)
if err != nil {
return nil, err
}
p.handle = h
defer func() {
if err != nil {
syscall.CloseHandle(p.handle)
p.handle = syscall.InvalidHandle
}
}()
if err = p.setSerialConfig(c); err != nil {
return nil, err
}
return p, p.setTimeouts(c)
}
func (p *port) Close() (err error) {
if p.handle == syscall.InvalidHandle {
return
}
err1 := SetCommTimeouts(p.handle, &p.oldTimeouts)
err2 := SetCommState(p.handle, &p.oldDCB)
err = syscall.CloseHandle(p.handle)
if err == nil {
if err1 == nil {
err = err2
} else {
err = err1
}
}
p.handle = syscall.InvalidHandle
return
}
// Read reads from serial port.
// It is blocked until data received or timeout after p.timeout.
func (p *port) Read(b []byte) (n int, err error) {
var done uint32
if err = syscall.ReadFile(p.handle, b, &done, nil); err != nil {
return
}
if done == 0 {
err = ErrTimeout
return
}
n = int(done)
return
}
// Write writes data to the serial port.
func (p *port) Write(b []byte) (n int, err error) {
var done uint32
if err = syscall.WriteFile(p.handle, b, &done, nil); err != nil {
return
}
n = int(done)
return
}
func (p *port) setTimeouts(c *Config) error {
var timeouts c_COMMTIMEOUTS
// Read and write timeout
if c.Timeout > 0 {
timeout := toDWORD(int(c.Timeout.Nanoseconds() / 1e6))
// wait until a byte arrived or time out
timeouts.ReadIntervalTimeout = c_MAXDWORD
timeouts.ReadTotalTimeoutMultiplier = c_MAXDWORD
timeouts.ReadTotalTimeoutConstant = timeout
timeouts.WriteTotalTimeoutConstant = timeout
}
err := GetCommTimeouts(p.handle, &p.oldTimeouts)
if err != nil {
return err
}
err = SetCommTimeouts(p.handle, &timeouts)
if err != nil {
// reset
SetCommTimeouts(p.handle, &p.oldTimeouts)
}
return err
}
func (p *port) setSerialConfig(c *Config) error {
var dcb c_DCB
if c.BaudRate == 0 {
dcb.BaudRate = 19200
} else {
dcb.BaudRate = toDWORD(c.BaudRate)
}
// Data bits
if c.DataBits == 0 {
dcb.ByteSize = 8
} else {
dcb.ByteSize = toBYTE(c.DataBits)
}
// Stop bits
switch c.StopBits {
case 0, 1:
// Default is one stop bit.
dcb.StopBits = c_ONESTOPBIT
case 2:
dcb.StopBits = c_TWOSTOPBITS
default:
return fmt.Errorf("serial: unsupported stop bits %v", c.StopBits)
}
// Parity
switch c.Parity {
case "", "E":
// Default parity mode is Even.
dcb.Parity = c_EVENPARITY
dcb.Pad_cgo_0[0] |= 0x02 // fParity
case "O":
dcb.Parity = c_ODDPARITY
dcb.Pad_cgo_0[0] |= 0x02 // fParity
case "N":
dcb.Parity = c_NOPARITY
default:
return fmt.Errorf("serial: unsupported parity %v", c.Parity)
}
dcb.Pad_cgo_0[0] |= 0x01 // fBinary
err := GetCommState(p.handle, &p.oldDCB)
if err != nil {
return err
}
err = SetCommState(p.handle, &dcb)
if err != nil {
SetCommState(p.handle, &p.oldDCB)
}
return err
}
func newHandle(c *Config) (handle syscall.Handle, err error) {
handle, err = syscall.CreateFile(
syscall.StringToUTF16Ptr(c.Address),
syscall.GENERIC_READ|syscall.GENERIC_WRITE,
0, // mode
nil, // security
syscall.OPEN_EXISTING, // create mode
0, // attributes
0) // templates
return
}