File: serial_windows.go

package info (click to toggle)
golang-github-goburrow-serial 0.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 132 kB
  • sloc: makefile: 5
file content (168 lines) | stat: -rw-r--r-- 3,479 bytes parent folder | download
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
}