Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/y-ponomarev/serial

go 1.13

require golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 h1:XDXtA5hveEEV8JB2l7nhMTp3t3cHp9ZpwcdjqyEWLlo=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
35 changes: 35 additions & 0 deletions serial_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,38 @@ func fdisset(fd int, fds *syscall.FdSet) bool {
idx, pos := fdget(fd, fds)
return fds.Bits[idx]&(1<<uint(pos)) != 0
}

const (
// baudrate ioctls
TCGETS2 = 0x802C542A
TCSETS2 = 0x402C542B
CBAUD = 0x100F
BOTHER = 0x1000 //0o010000
)

func isCustomBaudrate(baudrate int) bool {
var ok bool
_, ok = baudRates[baudrate]
return !ok
}

func setSpecialBaudrate(fd int, baudrate int) (bool, syscall.Errno) {
var buf [64]int32 // right size is 44 on x86_64, allow for some growth

_, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL),
uintptr(fd), uintptr(TCGETS2), uintptr(unsafe.Pointer(&buf)))
if errno != 0 {
return false, errno
}
// set custom speed
buf[2] &^= CBAUD
buf[2] |= BOTHER
buf[9], buf[10] = int32(baudrate), int32(baudrate)
_, _, errno = syscall.Syscall(uintptr(syscall.SYS_IOCTL),
uintptr(fd), uintptr(TCSETS2), uintptr(unsafe.Pointer(&buf)))
if errno != 0 {
return false, errno
}

return true, syscall.Errno(0)
}
59 changes: 37 additions & 22 deletions serial_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"syscall"
"time"
"unsafe"
"golang.org/x/sys/unix"
)

// port implements Port interface.
Expand Down Expand Up @@ -43,6 +44,11 @@ func New() Port {

// Open connects to the given serial port.
func (p *port) Open(c *Config) (err error) {
var custom_baudrate int
if isCustomBaudrate(c.BaudRate) {
custom_baudrate = c.BaudRate
c.BaudRate = 38400
}
termios, err := newTermios(c)
if err != nil {
return
Expand All @@ -68,6 +74,15 @@ func (p *port) Open(c *Config) (err error) {
return err
}
p.timeout = c.Timeout

if custom_baudrate > 0 {
c.BaudRate = custom_baudrate
ok, errno := setSpecialBaudrate(p.fd, custom_baudrate)
if !ok {
return error(errno)
}
}

return
}

Expand All @@ -85,33 +100,33 @@ func (p *port) Close() (err error) {
// Read reads from serial port. Port must be opened before calling this method.
// It is blocked until all data received or timeout after p.timeout.
func (p *port) Read(b []byte) (n int, err error) {
var rfds syscall.FdSet

fd := p.fd
fdset(fd, &rfds)
fds := []unix.PollFd{{Fd: int32(p.fd), Events: unix.POLLIN}}
deadline := time.Now().Add(p.timeout)

var tv *syscall.Timeval
if p.timeout > 0 {
timeout := syscall.NsecToTimeval(p.timeout.Nanoseconds())
tv = &timeout
}
for {
// If syscall.Select() returns EINTR (Interrupted system call), retry it
if err = syscallSelect(fd+1, &rfds, nil, nil, tv); err == nil {
break
timeout := -1
if p.timeout > 0 {
timeout = int(deadline.Sub(time.Now()).Milliseconds())
}
if err != syscall.EINTR {
err = fmt.Errorf("serial: could not select: %v", err)
return

n, err := unix.Poll(fds, timeout)
if err != nil {
if errors.Is(err, syscall.EINTR) {
continue
}
return 0, fmt.Errorf("serial: could not poll: %v", err)
}

if n == 0 {
return 0, ErrTimeout
}

if fds[0].Revents&unix.POLLIN == unix.POLLIN {
break
}
}
if !fdisset(fd, &rfds) {
// Timeout
err = ErrTimeout
return
}
n, err = syscall.Read(fd, b)
return

return syscall.Read(p.fd, b)
}

// Write writes data to the serial port.
Expand Down
14 changes: 9 additions & 5 deletions serial_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,17 @@ func (p *port) setSerialConfig(c *Config) error {
}

func newHandle(c *Config) (handle syscall.Handle, err error) {
name := c.Address
if len(name) > 0 && name[0] != '\\' {
name = "\\\\.\\" + name
}
handle, err = syscall.CreateFile(
syscall.StringToUTF16Ptr(c.Address),
syscall.StringToUTF16Ptr(name),
syscall.GENERIC_READ|syscall.GENERIC_WRITE,
0, // mode
nil, // security
0, // mode
nil, // security
syscall.OPEN_EXISTING, // create mode
0, // attributes
0) // templates
0, // attributes
0) // templates
return
}