sungrow/vendor/github.com/goburrow/serial/serial_windows.go

169 lines
3.4 KiB
Go
Raw Normal View History

2021-01-10 15:20:11 +11:00
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 New() Port {
return &port{
handle: syscall.InvalidHandle,
}
}
// Open connects to the given serial port.
func (p *port) Open(c *Config) (err error) {
p.handle, err = newHandle(c)
if err != nil {
return
}
defer func() {
if err != nil {
syscall.CloseHandle(p.handle)
p.handle = syscall.InvalidHandle
}
}()
err = p.setSerialConfig(c)
if err != nil {
return
}
err = p.setTimeouts(c)
return
}
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
}