1 package serial
2
3 import (
4 "errors"
5 "io"
6 "os"
7 "syscall"
8 "unsafe"
9
10 "golang.org/x/sys/unix"
11 )
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 const (
28 kTCSETS2 = 0x402C542B
29 kBOTHER = 0x1000
30 kNCCS = 19
31 )
32
33
34
35
36
37 type cc_t byte
38 type speed_t uint32
39 type tcflag_t uint32
40 type termios2 struct {
41 c_iflag tcflag_t
42 c_oflag tcflag_t
43 c_cflag tcflag_t
44 c_lflag tcflag_t
45 c_line cc_t
46 c_cc [kNCCS]cc_t
47 c_ispeed speed_t
48 c_ospeed speed_t
49 }
50
51
52
53 const (
54 sER_RS485_ENABLED = (1 << 0)
55 sER_RS485_RTS_ON_SEND = (1 << 1)
56 sER_RS485_RTS_AFTER_SEND = (1 << 2)
57 sER_RS485_RX_DURING_TX = (1 << 4)
58 tIOCSRS485 = 0x542F
59 )
60
61 type serial_rs485 struct {
62 flags uint32
63 delay_rts_before_send uint32
64 delay_rts_after_send uint32
65 padding [5]uint32
66 }
67
68
69
70
71
72
73 func makeTermios2(options OpenOptions) (*termios2, error) {
74
75
76
77 vtime := uint(round(float64(options.InterCharacterTimeout)/100.0) * 100)
78 vmin := options.MinimumReadSize
79
80 if vmin == 0 && vtime < 100 {
81 return nil, errors.New("invalid values for InterCharacterTimeout and MinimumReadSize")
82 }
83
84 if vtime > 25500 {
85 return nil, errors.New("invalid value for InterCharacterTimeout")
86 }
87
88 ccOpts := [kNCCS]cc_t{}
89 ccOpts[syscall.VTIME] = cc_t(vtime / 100)
90 ccOpts[syscall.VMIN] = cc_t(vmin)
91
92 t2 := &termios2{
93 c_cflag: syscall.CLOCAL | syscall.CREAD | kBOTHER,
94 c_ispeed: speed_t(options.BaudRate),
95 c_ospeed: speed_t(options.BaudRate),
96 c_cc: ccOpts,
97 }
98
99 switch options.StopBits {
100 case 1:
101 case 2:
102 t2.c_cflag |= syscall.CSTOPB
103
104 default:
105 return nil, errors.New("invalid setting for StopBits")
106 }
107
108 switch options.ParityMode {
109 case PARITY_NONE:
110 case PARITY_ODD:
111 t2.c_cflag |= syscall.PARENB
112 t2.c_cflag |= syscall.PARODD
113
114 case PARITY_EVEN:
115 t2.c_cflag |= syscall.PARENB
116
117 default:
118 return nil, errors.New("invalid setting for ParityMode")
119 }
120
121 switch options.DataBits {
122 case 5:
123 t2.c_cflag |= syscall.CS5
124 case 6:
125 t2.c_cflag |= syscall.CS6
126 case 7:
127 t2.c_cflag |= syscall.CS7
128 case 8:
129 t2.c_cflag |= syscall.CS8
130 default:
131 return nil, errors.New("invalid setting for DataBits")
132 }
133
134 if options.RTSCTSFlowControl {
135 t2.c_cflag |= unix.CRTSCTS
136 }
137
138 return t2, nil
139 }
140
141 func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
142
143 file, openErr :=
144 os.OpenFile(
145 options.PortName,
146 syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK,
147 0600)
148 if openErr != nil {
149 return nil, openErr
150 }
151
152
153 nonblockErr := syscall.SetNonblock(int(file.Fd()), false)
154 if nonblockErr != nil {
155 return nil, nonblockErr
156 }
157
158 t2, optErr := makeTermios2(options)
159 if optErr != nil {
160 return nil, optErr
161 }
162
163 r, _, errno := syscall.Syscall(
164 syscall.SYS_IOCTL,
165 uintptr(file.Fd()),
166 uintptr(kTCSETS2),
167 uintptr(unsafe.Pointer(t2)))
168
169 if errno != 0 {
170 return nil, os.NewSyscallError("SYS_IOCTL", errno)
171 }
172
173 if r != 0 {
174 return nil, errors.New("unknown error from SYS_IOCTL")
175 }
176
177 if options.Rs485Enable {
178 rs485 := serial_rs485{
179 sER_RS485_ENABLED,
180 uint32(options.Rs485DelayRtsBeforeSend),
181 uint32(options.Rs485DelayRtsAfterSend),
182 [5]uint32{0, 0, 0, 0, 0},
183 }
184
185 if options.Rs485RtsHighDuringSend {
186 rs485.flags |= sER_RS485_RTS_ON_SEND
187 }
188
189 if options.Rs485RtsHighAfterSend {
190 rs485.flags |= sER_RS485_RTS_AFTER_SEND
191 }
192
193 r, _, errno := syscall.Syscall(
194 syscall.SYS_IOCTL,
195 uintptr(file.Fd()),
196 uintptr(tIOCSRS485),
197 uintptr(unsafe.Pointer(&rs485)))
198
199 if errno != 0 {
200 return nil, os.NewSyscallError("SYS_IOCTL (RS485)", errno)
201 }
202
203 if r != 0 {
204 return nil, errors.New("Unknown error from SYS_IOCTL (RS485)")
205 }
206 }
207
208 return file, nil
209 }
210
View as plain text