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
|
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
}
|