-
Notifications
You must be signed in to change notification settings - Fork 28
/
serial-linux.go
135 lines (117 loc) · 2.69 KB
/
serial-linux.go
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
// +build linux
package main
import (
"os"
"github.com/google/goterm/term"
)
type serialPortStruct struct {
pty *term.PTY
symlink string
writeLoopDeinitNeededChan chan bool
writeLoopDeinitFinishedChan chan bool
readLoopDeinitNeededChan chan bool
readLoopDeinitFinishedChan chan bool
// Read from this channel to receive serial data.
read chan []byte
// Write to this channel to send serial data.
write chan []byte
}
var serialPort serialPortStruct
func (s *serialPortStruct) writeLoop() {
var b []byte
for {
select {
case b = <-s.write:
case <-s.writeLoopDeinitNeededChan:
s.writeLoopDeinitFinishedChan <- true
return
}
for len(b) > 0 {
written, err := s.pty.Master.Write(b)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
reportError(err)
}
}
b = b[written:]
}
}
}
func (s *serialPortStruct) readLoop() {
for {
b := make([]byte, maxSerialFrameLength)
n, err := s.pty.Master.Read(b)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
reportError(err)
}
}
select {
case s.read <- b[:n]:
case <-s.readLoopDeinitNeededChan:
s.readLoopDeinitFinishedChan <- true
return
}
}
}
// We only init the virtual serial port once, with the first device name we acquire, so apps using the
// virtual serial port won't have issues with the interface going down while the app is running.
func (s *serialPortStruct) initIfNeeded(devName string) (err error) {
if s.pty != nil {
// Depleting channel which may contain data while the serial connection to the server was offline.
for {
select {
case <-s.read:
default:
return
}
}
}
s.pty, err = term.OpenPTY()
if err != nil {
return err
}
var t term.Termios
t.Raw()
err = t.Set(s.pty.Master)
if err != nil {
return err
}
err = t.Set(s.pty.Slave)
if err != nil {
return err
}
n, err := s.pty.PTSName()
if err != nil {
return err
}
s.symlink = "/tmp/kappanhang-" + devName + ".pty"
_ = os.Remove(s.symlink)
if err := os.Symlink(n, s.symlink); err != nil {
return err
}
log.Print("opened ", n, " as ", s.symlink)
s.write = make(chan []byte)
s.read = make(chan []byte)
s.readLoopDeinitNeededChan = make(chan bool)
s.readLoopDeinitFinishedChan = make(chan bool)
go s.readLoop()
s.writeLoopDeinitNeededChan = make(chan bool)
s.writeLoopDeinitFinishedChan = make(chan bool)
go s.writeLoop()
return nil
}
func (s *serialPortStruct) deinit() {
if s.pty != nil {
s.pty.Close()
}
_ = os.Remove(s.symlink)
if s.readLoopDeinitNeededChan != nil {
s.readLoopDeinitNeededChan <- true
<-s.readLoopDeinitFinishedChan
}
if s.writeLoopDeinitNeededChan != nil {
s.writeLoopDeinitNeededChan <- true
<-s.writeLoopDeinitFinishedChan
}
}