Skip to content

Commit 30f35c3

Browse files
committed
add GetMode() to SerialPort
1 parent b4c5081 commit 30f35c3

File tree

8 files changed

+280
-20
lines changed

8 files changed

+280
-20
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
module github.com/distributed/sers
2+
3+
go 1.11

sers.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
// in a wide range of embedded projects.
88
package sers
99

10-
1110
import (
1211
"fmt"
1312
"io"
@@ -38,8 +37,16 @@ type SerialPort interface {
3837
// supported by the driver. parity is one of (N|O|E) for none, odd
3938
// or even parity. handshake is either NO_HANDSHAKE or
4039
// RTSCTS_HANDSHAKE.
40+
//
41+
// Known bug on Windows: Only NO_HANDSHAKE is supported.
4142
SetMode(baudrate, databits, parity, stopbits, handshake int) error
4243

44+
// GetMode retrieves the current mode settings.
45+
//
46+
// Known bug on OS X: GetMode only works after SetMode has been called
47+
// before. If not, it returns an error.
48+
GetMode() (Mode, error)
49+
4350
// SetReadParams sets the minimum number of bits to read and a read
4451
// timeout in seconds. These parameters roughly correspond to the
4552
// UNIX termios concepts of VMIN and VTIME.
@@ -135,7 +142,7 @@ type ParameterError struct {
135142
}
136143

137144
func (pe *ParameterError) Error() string {
138-
return fmt.Sprintf("error in parameter '%s': %s")
145+
return fmt.Sprintf("error in parameter '%s': %s", pe.Parameter, pe.Reason)
139146
}
140147

141148
type Error struct {

sers_darwin.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,29 @@ package sers
1313
#include <unistd.h>
1414
#include <fcntl.h>
1515
16+
1617
extern int fcntl1(int i, unsigned int r, unsigned int d);
1718
extern int ioctl1(int i, unsigned int r, void *d);*/
1819
import "C"
1920

2021
import (
22+
"sync"
2123
"unsafe"
2224
)
2325

26+
const (
27+
// using C.IOSSIOSPEED yields 0x80085402
28+
// which does not work. don't ask me why
29+
// this define is wrong in cgo.
30+
IOSSIOSPEED = 0x80045402
31+
)
32+
33+
type termiosPlatformData struct {
34+
lock sync.Mutex
35+
baudrateSet bool
36+
baudrate int
37+
}
38+
2439
func (bp *baseport) SetBaudRate(br int) error {
2540
var speed C.speed_t = C.speed_t(br)
2641

@@ -32,5 +47,27 @@ func (bp *baseport) SetBaudRate(br int) error {
3247
return &Error{"setting baud rate: ioctl", err}
3348
}
3449

50+
bp.platformData.lock.Lock()
51+
bp.platformData.baudrate = br
52+
bp.platformData.baudrateSet = true
53+
bp.platformData.lock.Unlock()
54+
3555
return nil
3656
}
57+
58+
type osxBaudrateRetrievalFailed struct{}
59+
60+
func (osxBaudrateRetrievalFailed) Error() string {
61+
return "sers: Cannot get current baud rate setting. You have to set the baudrate through SetMode before you can read it via GetMode. See documentation."
62+
}
63+
64+
func (bp *baseport) getBaudrate() (int, error) {
65+
bp.platformData.lock.Lock()
66+
defer bp.platformData.lock.Unlock()
67+
68+
if bp.platformData.baudrateSet {
69+
return bp.platformData.baudrate, nil
70+
}
71+
72+
return -1, osxBaudrateRetrievalFailed{}
73+
}

sers_linux.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,43 @@ speed_t lookupbaudrate(int br) {
9898
return 0;
9999
}
100100

101+
int speedtobaudrate(speed_t speed) {
102+
printf("enum baud rate reverse matching\n");
103+
switch (speed) {
104+
case B50 : return 50 ;
105+
case B75 : return 75 ;
106+
case B110 : return 110 ;
107+
case B134 : return 134 ;
108+
case B150 : return 150 ;
109+
case B200 : return 200 ;
110+
case B300 : return 300 ;
111+
case B600 : return 600 ;
112+
case B1200 : return 1200 ;
113+
case B1800 : return 1800 ;
114+
case B2400 : return 2400 ;
115+
case B4800 : return 4800 ;
116+
case B9600 : return 9600 ;
117+
case B19200 : return 19200 ;
118+
case B38400 : return 38400 ;
119+
case B57600 : return 57600 ;
120+
case B115200 : return 115200 ;
121+
case B230400 : return 230400 ;
122+
case B460800 : return 460800 ;
123+
case B500000 : return 500000 ;
124+
case B576000 : return 576000 ;
125+
case B921600 : return 921600 ;
126+
case B1000000: return 1000000 ;
127+
case B1152000: return 1152000 ;
128+
case B1500000: return 1500000 ;
129+
case B2000000: return 2000000 ;
130+
case B2500000: return 2500000 ;
131+
case B3000000: return 3000000 ;
132+
case B3500000: return 3500000 ;
133+
case B4000000: return 4000000 ;
134+
}
135+
return 0;
136+
}
137+
101138
int setbaudrate(int fd, int br) {
102139
// we try to set the baud rate via the old school interface first. this
103140
// should work more reliably, if only for certain baud rates.
@@ -132,3 +169,18 @@ int setbaudrate(int fd, int br) {
132169

133170
return ioctl(fd, TCSETS3, &tio);
134171
}
172+
173+
int getbaudrate(int fd, int *br) {
174+
struct termios3 tio;
175+
int ret = ioctl(fd, TCGETS3, &tio);
176+
if (ret == -1) return ret;
177+
178+
if (tio.c_cflag & CBAUDEX) {
179+
*br = tio.c_ispeed;
180+
return 0;
181+
}
182+
183+
*br = speedtobaudrate(tio.c_cflag & CBAUD);
184+
185+
return 0;
186+
}

sers_linux.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,32 @@ package sers
1111
1212
extern int setbaudrate(int fd, int br);
1313
extern int clearnonblocking(int fd);
14+
extern int getbaudrate(int fd, int *br);
1415
*/
1516
import "C"
17+
import "fmt"
18+
19+
type termiosPlatformData struct{}
1620

1721
func (bp *baseport) SetBaudRate(br int) error {
18-
// setting baud rate via new struct termios2 method
22+
// setting baud rate via new struct termios2 method
1923
_, err := C.setbaudrate(C.int(bp.fd), C.int(br))
2024
if err != nil {
2125
return err
2226
}
2327

2428
return nil
2529
}
30+
31+
func (bp *baseport) getBaudrate() (int, error) {
32+
var br C.int
33+
ret, errnoerr := C.getbaudrate(C.int(bp.fd), &br)
34+
if errnoerr != nil {
35+
return 0, fmt.Errorf("error getting baud rate: %v", errnoerr)
36+
}
37+
if ret != 0 {
38+
return 0, fmt.Errorf("error getting baud rate")
39+
}
40+
41+
return int(br), nil
42+
}

sers_termios.go

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,10 @@ import (
2424
"unsafe"
2525
)
2626

27-
const (
28-
// using C.IOSSIOSPEED yields 0x80085402
29-
// which does not work. don't ask me why
30-
// this define is wrong in cgo.
31-
IOSSIOSPEED = 0x80045402
32-
)
33-
3427
type baseport struct {
35-
fd int
36-
f *os.File
28+
fd int
29+
f *os.File
30+
platformData termiosPlatformData
3731
}
3832

3933
func takeOverFD(fd int, fn string) (SerialPort, error) {
@@ -68,7 +62,7 @@ func TakeOver(f *os.File) (SerialPort, error) {
6862
if f == nil {
6963
return nil, &ParameterError{"f", "needs to be non-nil"}
7064
}
71-
bp := &baseport{int(f.Fd()), f}
65+
bp := &baseport{fd: int(f.Fd()), f: f}
7266

7367
return bp, nil
7468
}
@@ -187,6 +181,53 @@ func (bp *baseport) SetMode(baudrate, databits, parity, stopbits, handshake int)
187181
return nil
188182
}
189183

184+
func (bp *baseport) GetMode() (mode Mode, err error) {
185+
var tio *C.struct_termios
186+
tio, err = bp.getattr()
187+
if err != nil {
188+
return
189+
}
190+
191+
tioCharSize := tio.c_cflag & C.CSIZE
192+
switch tioCharSize {
193+
case C.CS5:
194+
mode.DataBits = 5
195+
case C.CS6:
196+
mode.DataBits = 6
197+
case C.CS7:
198+
mode.DataBits = 7
199+
case C.CS8:
200+
mode.DataBits = 8
201+
default:
202+
err = fmt.Errorf("unknown character size field (%#08x) in termios", tioCharSize)
203+
}
204+
205+
mode.Stopbits = 1
206+
if tio.c_cflag&C.CSTOPB != 0 {
207+
mode.Stopbits = 2
208+
}
209+
210+
mode.Parity = N
211+
switch tio.c_cflag & (C.PARENB | C.PARODD) {
212+
case C.PARENB | C.PARODD:
213+
mode.Parity = O
214+
case C.PARENB:
215+
mode.Parity = E
216+
}
217+
218+
mode.Handshake = NO_HANDSHAKE
219+
if tio.c_cflag&C.CRTSCTS != 0 {
220+
mode.Handshake = RTSCTS_HANDSHAKE
221+
}
222+
223+
mode.Baudrate, err = bp.getBaudrate()
224+
if err != nil {
225+
return
226+
}
227+
228+
return
229+
}
230+
190231
func (bp *baseport) SetReadParams(minread int, timeout float64) error {
191232
inttimeout := int(timeout * 10)
192233
if inttimeout < 0 {

sers_windows.go

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ func (p *serialPort) SetBreak(on bool) error {
182182

183183
var (
184184
nSetCommState,
185+
nGetCommState,
185186
nSetCommTimeouts,
186187
nSetCommMask,
187188
nSetupComm,
@@ -200,6 +201,7 @@ func init() {
200201
defer syscall.FreeLibrary(k32)
201202

202203
nSetCommState = getProcAddr(k32, "SetCommState")
204+
nGetCommState = getProcAddr(k32, "GetCommState")
203205
nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts")
204206
nSetCommMask = getProcAddr(k32, "SetCommMask")
205207
nSetupComm = getProcAddr(k32, "SetupComm")
@@ -218,19 +220,19 @@ func getProcAddr(lib syscall.Handle, name string) uintptr {
218220
return addr
219221
}
220222

221-
func setCommState(h syscall.Handle, baud, databits, parity, handshake int) error {
223+
func setCommState(h syscall.Handle, mode Mode) error {
222224
var params structDCB
223225
params.DCBlength = uint32(unsafe.Sizeof(params))
224226

225227
params.flags[0] = 0x01 // fBinary
226228
params.flags[0] |= 0x10 // Assert DSR
227229

228-
params.ByteSize = byte(databits)
230+
params.ByteSize = byte(mode.DataBits)
229231

230-
params.BaudRate = uint32(baud)
232+
params.BaudRate = uint32(mode.Baudrate)
231233
//params.ByteSize = 8
232234

233-
switch parity {
235+
switch mode.Parity {
234236
case N:
235237
params.flags[0] &^= 0x02
236238
params.Parity = 0 // NOPARITY
@@ -244,11 +246,11 @@ func setCommState(h syscall.Handle, baud, databits, parity, handshake int) error
244246
return StringError("invalid parity setting")
245247
}
246248

247-
switch handshake {
249+
switch mode.Handshake {
248250
case NO_HANDSHAKE:
249251
// TODO: reset handshake
250252
default:
251-
return StringError("only NO_HANDSHAKE is supported on windows")
253+
return fmt.Errorf("setting mode %q: only NO_HANDSHAKE is supported on windows", mode)
252254
}
253255

254256
r, _, err := syscall.Syscall(nSetCommState, 2, uintptr(h), uintptr(unsafe.Pointer(&params)), 0)
@@ -258,6 +260,39 @@ func setCommState(h syscall.Handle, baud, databits, parity, handshake int) error
258260
return nil
259261
}
260262

263+
func (sp *serialPort) GetMode() (Mode, error) {
264+
var params structDCB
265+
var mode Mode = Mode{Handshake: NO_HANDSHAKE, Parity: N, Stopbits: 1}
266+
267+
r, _, err := syscall.Syscall(nGetCommState, 2, uintptr(syscall.Handle(sp.f.Fd())), uintptr(unsafe.Pointer(&params)), 0)
268+
if r == 0 {
269+
return mode, err
270+
}
271+
272+
mode.DataBits = int(params.ByteSize)
273+
if mode.DataBits > 8 {
274+
return mode, fmt.Errorf("error getting mode: ByteSize > 8")
275+
}
276+
277+
if mode.DataBits < 5 {
278+
return mode, fmt.Errorf("error getting mode: ByteSize < 5")
279+
}
280+
281+
mode.Baudrate = int(params.BaudRate)
282+
if params.flags[0]&0x02 != 0 {
283+
switch params.Parity {
284+
case 1:
285+
mode.Parity = O
286+
case 2:
287+
mode.Parity = E
288+
default:
289+
return mode, fmt.Errorf("error getting mode: unsupport Parity setting %d", params.Parity)
290+
}
291+
}
292+
293+
return mode, nil
294+
}
295+
261296
func setCommTimeouts(h syscall.Handle, constTimeout float64) error {
262297
var timeouts structTimeouts
263298
const MAXDWORD = 1<<32 - 1
@@ -348,7 +383,15 @@ func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int,
348383
}
349384

350385
func (sp *serialPort) SetMode(baudrate, databits, parity, stopbits, handshake int) error {
351-
if err := setCommState(syscall.Handle(sp.f.Fd()), baudrate, databits, parity, handshake); err != nil {
386+
mode := Mode{
387+
Baudrate: baudrate,
388+
DataBits: databits,
389+
Parity: parity,
390+
Stopbits: stopbits,
391+
Handshake: handshake,
392+
}
393+
394+
if err := setCommState(syscall.Handle(sp.f.Fd()), mode); err != nil {
352395
return err
353396
}
354397
//return StringError("SetMode not implemented yet on Windows")

0 commit comments

Comments
 (0)