/
dial.go
executable file
·170 lines (139 loc) · 3.71 KB
/
dial.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
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
169
170
package pactor
import (
"fmt"
"io"
"net"
"runtime"
"strconv"
"time"
"github.com/la5nta/wl2k-go/transport"
)
// DialURL dials pactor:// URLs
//
// BLOCK until connection is established or timeoud occured
func (p *Modem) DialURL(url *transport.URL) (net.Conn, error) {
if url.Scheme != "pactor" {
return nil, transport.ErrUnsupportedScheme
}
if err := p.call(url.Target); err != nil {
return nil, err
}
return p, nil
}
// Read bytes from the Software receive buffer. The receive thread takes care of
// reading them from the pactor modem
//
// BLOCK until receive buffer has any data!
func (p *Modem) Read(d []byte) (int, error) {
p.mux.read.Lock()
defer p.mux.read.Unlock()
if p.state != Connected {
return 0, fmt.Errorf("Read from closed connection")
}
if len(d) == 0 {
return 0, nil
}
data, ok := <-p.recvBuf
if !ok {
return 0, io.EOF
}
if len(data) > len(d) {
panic("too large") //TODO: Handle
}
for i, b := range data {
d[i] = byte(b)
}
return len(data), nil
}
// Write bytes to the software send buffer. The send thread will take care of
// fowarding them to the pactor modem
//
// BLOCK if send buffer is full! Remains as soon as there is space left in the
// send buffer
func (p *Modem) Write(d []byte) (int, error) {
p.mux.write.Lock()
defer p.mux.write.Unlock()
if p.state != Connected {
return 0, fmt.Errorf("Read from closed connection")
}
for _, b := range d {
select {
case <-p.flags.closeWriting:
return 0, fmt.Errorf("Writing on closed connection")
case p.sendBuf <- b:
}
p.mux.bufLen.Lock()
p.sendBufLen++
p.mux.bufLen.Unlock()
}
return len(d), nil
}
// Flush waits for the last frames to be transmitted.
//
// Will throw error if remaining frames could not bet sent within 120s
func (p *Modem) Flush() (err error) {
if p.state != Connected {
return fmt.Errorf("Flush a closed connection")
}
writeDebug("Flush called", 2)
if err = p.waitTransmissionFinish(120 * time.Second); err != nil {
writeDebug(err.Error(), 2)
}
return
}
// Close closes the current connection.
//
// Will abort ("dirty disconnect") after 60 seconds if normal "disconnect" have
// not succeeded yet.
func (p *Modem) Close() error {
if p.flags.closed {
return nil
}
p.mux.close.Lock()
_, file, no, ok := runtime.Caller(1)
if ok {
writeDebug("Close called from "+file+"#"+strconv.Itoa(no), 2)
} else {
writeDebug("Close called", 2)
}
if p.flags.closeCalled != true {
defer p.mux.close.Unlock()
p.flags.closeCalled = true
if p.state == Connected {
// Connected to remote, try to send remaining frames and disconnect
// gracefully
defer p.close()
// Wait for remaining data to be transmitted and acknowledged
if err := p.waitTransmissionFinish(90 * time.Second); err != nil {
writeDebug(err.Error(), 2)
}
p.disconnect()
// Wait for disconnect command to be transmitted and acknowledged
if err := p.waitTransmissionFinish(30 * time.Second); err != nil {
writeDebug(err.Error(), 2)
}
} else {
// Link Setup (connection) not yet successful, force disconnect
p.forceDisconnect()
}
// Wait for the modem to change state from connected to disconnected
select {
case <-p.flags.disconnected:
writeDebug("Disconnect successful", 1)
return nil
case <-time.After(60 * time.Second):
p.forceDisconnect()
return fmt.Errorf("Disconnect timed out")
}
}
writeDebug("Should never reach this...", 1)
return nil
}
// TxBufferLen returns the number of bytes in the out buffer queue.
//
func (p *Modem) TxBufferLen() int {
p.mux.bufLen.Lock()
defer p.mux.bufLen.Unlock()
writeDebug("TxBufferLen called ("+strconv.Itoa(p.sendBufLen)+" bytes remaining in buffer)", 2)
return p.sendBufLen + (p.getNumFramesNotTransmitted() * MaxSendData)
}