mirror of
https://github.com/nonoo/kappanhang.git
synced 2026-01-18 14:50:32 +01:00
Added/adjusted various comments update of package versions used (IE 'go mod tidy' after adjusting to point to AD8IM repo)
406 lines
16 KiB
Go
406 lines
16 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
const controlStreamPort = 50001
|
|
const serialStreamPort = 50002
|
|
const audioStreamPort = 50003
|
|
|
|
const reauthInterval = time.Minute
|
|
const reauthTimeout = 3 * time.Second
|
|
|
|
type controlStream struct {
|
|
common streamCommon
|
|
serial serialStream
|
|
audio audioStream
|
|
|
|
deinitNeededChan chan bool
|
|
deinitFinishedChan chan bool
|
|
|
|
authInnerSendSeq uint16
|
|
authID [6]byte
|
|
gotAuthID bool
|
|
authOk bool
|
|
|
|
a8replyID [16]byte
|
|
gotA8ReplyID bool
|
|
|
|
serialAndAudioStreamOpened bool
|
|
deinitializing bool
|
|
|
|
requestSerialAndAudioTimeout *time.Timer
|
|
reauthTimeoutTimer *time.Timer
|
|
}
|
|
|
|
func (s *controlStream) sendPktLogin() error {
|
|
// The reply to the auth packet will contain a 6 bytes long auth ID with the first 2 bytes set to our ID.
|
|
var authStartID [2]byte
|
|
if _, err := rand.Read(authStartID[:]); err != nil {
|
|
return err
|
|
}
|
|
usernameEncoded := passcode(username)
|
|
passwordEncoded := passcode(password)
|
|
p := []byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
byte(s.common.localSID >> 24), byte(s.common.localSID >> 16), byte(s.common.localSID >> 8), byte(s.common.localSID),
|
|
byte(s.common.remoteSID >> 24), byte(s.common.remoteSID >> 16), byte(s.common.remoteSID >> 8), byte(s.common.remoteSID),
|
|
0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, byte(s.authInnerSendSeq),
|
|
byte(s.authInnerSendSeq >> 8), 0x00, authStartID[0], authStartID[1], 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
usernameEncoded[0], usernameEncoded[1], usernameEncoded[2], usernameEncoded[3],
|
|
usernameEncoded[4], usernameEncoded[5], usernameEncoded[6], usernameEncoded[7],
|
|
usernameEncoded[8], usernameEncoded[9], usernameEncoded[10], usernameEncoded[11],
|
|
usernameEncoded[12], usernameEncoded[13], usernameEncoded[14], usernameEncoded[15],
|
|
passwordEncoded[0], passwordEncoded[1], passwordEncoded[2], passwordEncoded[3],
|
|
passwordEncoded[4], passwordEncoded[5], passwordEncoded[6], passwordEncoded[7],
|
|
passwordEncoded[8], passwordEncoded[9], passwordEncoded[10], passwordEncoded[11],
|
|
passwordEncoded[12], passwordEncoded[13], passwordEncoded[14], passwordEncoded[15],
|
|
0x69, 0x63, 0x6f, 0x6d, 0x2d, 0x70, 0x63, 0x00, // icom-pc in plain text
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
if err := s.common.pkt0.sendTrackedPacket(&s.common, p); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.authInnerSendSeq++
|
|
return nil
|
|
}
|
|
|
|
func (s *controlStream) sendPktAuth(magic byte) error {
|
|
// Example request from PC: 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
|
|
// 0xbb, 0x41, 0x3f, 0x2b, 0xe6, 0xb2, 0x7b, 0x7b,
|
|
// 0x00, 0x00, 0x00, 0x30, 0x01, 0x05, 0x00, 0x02,
|
|
// 0x00, 0x00, 0x5d, 0x37, 0x12, 0x82, 0x3b, 0xde,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
// Example reply from radio: 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00,
|
|
// 0xe6, 0xb2, 0x7b, 0x7b, 0xbb, 0x41, 0x3f, 0x2b,
|
|
// 0x00, 0x00, 0x00, 0x30, 0x02, 0x05, 0x00, 0x02,
|
|
// 0x00, 0x00, 0x5d, 0x37, 0x12, 0x82, 0x3b, 0xde,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
p := []byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
byte(s.common.localSID >> 24), byte(s.common.localSID >> 16), byte(s.common.localSID >> 8), byte(s.common.localSID),
|
|
byte(s.common.remoteSID >> 24), byte(s.common.remoteSID >> 16), byte(s.common.remoteSID >> 8), byte(s.common.remoteSID),
|
|
0x00, 0x00, 0x00, 0x30, 0x01, magic, 0x00, byte(s.authInnerSendSeq),
|
|
byte(s.authInnerSendSeq >> 8), 0x00, s.authID[0], s.authID[1], s.authID[2], s.authID[3], s.authID[4], s.authID[5],
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
if err := s.common.pkt0.sendTrackedPacket(&s.common, p); err != nil {
|
|
return err
|
|
}
|
|
s.authInnerSendSeq++
|
|
return nil
|
|
}
|
|
|
|
func (s *controlStream) sendRequestSerialAndAudio() error {
|
|
log.Debug("requesting serial and audio stream")
|
|
|
|
txSeqBufLengthMs := uint16(txSeqBufLength.Milliseconds())
|
|
|
|
usernameEncoded := passcode(username)
|
|
p := []byte{0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
byte(s.common.localSID >> 24), byte(s.common.localSID >> 16), byte(s.common.localSID >> 8), byte(s.common.localSID),
|
|
byte(s.common.remoteSID >> 24), byte(s.common.remoteSID >> 16), byte(s.common.remoteSID >> 8), byte(s.common.remoteSID),
|
|
0x00, 0x00, 0x00, 0x80, 0x01, 0x03, 0x00, byte(s.authInnerSendSeq),
|
|
byte(s.authInnerSendSeq >> 8), 0x00, s.authID[0], s.authID[1], s.authID[2], s.authID[3], s.authID[4], s.authID[5],
|
|
s.a8replyID[0], s.a8replyID[1], s.a8replyID[2], s.a8replyID[3], s.a8replyID[4], s.a8replyID[5], s.a8replyID[6], s.a8replyID[7],
|
|
s.a8replyID[8], s.a8replyID[9], s.a8replyID[10], s.a8replyID[11], s.a8replyID[12], s.a8replyID[13], s.a8replyID[14], s.a8replyID[15],
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x49, 0x43, 0x2d, 0x37, 0x30, 0x35, 0x00, 0x00, // IC-705 in plain text
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
usernameEncoded[0], usernameEncoded[1], usernameEncoded[2], usernameEncoded[3],
|
|
usernameEncoded[4], usernameEncoded[5], usernameEncoded[6], usernameEncoded[7],
|
|
usernameEncoded[8], usernameEncoded[9], usernameEncoded[10], usernameEncoded[11],
|
|
usernameEncoded[12], usernameEncoded[13], usernameEncoded[14], usernameEncoded[15],
|
|
0x01, 0x01, 0x04, 0x04, 0x00, 0x00, byte(audioSampleRate >> 8), byte(audioSampleRate & 0xff),
|
|
0x00, 0x00, byte(audioSampleRate >> 8), byte(audioSampleRate & 0xff),
|
|
0x00, 0x00, byte(serialStreamPort >> 8), byte(serialStreamPort & 0xff),
|
|
0x00, 0x00, byte(audioStreamPort >> 8), byte(audioStreamPort & 0xff), 0x00, 0x00,
|
|
byte(txSeqBufLengthMs >> 8), byte(txSeqBufLengthMs & 0xff), 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
if err := s.common.pkt0.sendTrackedPacket(&s.common, p); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.authInnerSendSeq++
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *controlStream) sendRequestSerialAndAudioIfPossible() {
|
|
if !s.serialAndAudioStreamOpened && s.authOk && s.gotA8ReplyID {
|
|
if err := s.sendRequestSerialAndAudio(); err != nil {
|
|
reportError(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *controlStream) handleRead(r []byte) error {
|
|
switch len(r) {
|
|
case 168:
|
|
if bytes.Equal(r[:6], []byte{0xa8, 0x00, 0x00, 0x00, 0x00, 0x00}) {
|
|
// Example answer from radio:
|
|
// 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
|
|
// 0x01, 0x13, 0x11, 0x18, 0x38, 0xff, 0x55, 0x7d,
|
|
// 0x00, 0x00, 0x00, 0x98, 0x02, 0x02, 0x00, 0x07,
|
|
// 0x00, 0x00, 0x7f, 0x91, 0x00, 0x00, 0x4f, 0x0d,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x01, 0x93, 0x8a, 0x01, 0x24, 0x17, 0x64,
|
|
// 0xbc, 0x4b, 0xa3, 0xa0, 0x13, 0x58, 0x41, 0x04,
|
|
// 0x58, 0x2d, 0x49, 0x43, 0x2d, 0x37, 0x30, 0x35,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x49, 0x43, 0x4f, 0x4d, 0x5f, 0x56,
|
|
// 0x41, 0x55, 0x44, 0x49, 0x4f, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x3f, 0x3f, 0xa4, 0x01, 0xff, 0x01,
|
|
// 0xff, 0x01, 0x01, 0x01, 0x00, 0x00, 0x4b, 0x00,
|
|
// 0x01, 0x50, 0x00, 0xb8, 0x0b, 0x00, 0x00, 0x00
|
|
copy(s.a8replyID[:], r[66:82])
|
|
s.gotA8ReplyID = true
|
|
}
|
|
case 64:
|
|
if bytes.Equal(r[:6], []byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00}) {
|
|
// Example answer from radio: 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
|
|
// 0x33, 0x60, 0xd4, 0xe5, 0xf4, 0x67, 0x86, 0xe1,
|
|
// 0x00, 0x00, 0x00, 0x30, 0x02, 0x05, 0x00, 0x02,
|
|
// 0x00, 0x00, 0x35, 0x34, 0x76, 0x11, 0xb9, 0xd0,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
s.reauthTimeoutTimer.Stop()
|
|
|
|
log.Debug("auth ok")
|
|
|
|
if r[21] == 0x05 { // Answer for our second auth?
|
|
s.authOk = true
|
|
s.sendRequestSerialAndAudioIfPossible()
|
|
}
|
|
}
|
|
case 80:
|
|
if bytes.Equal(r[:6], []byte{0x50, 0x00, 0x00, 0x00, 0x00, 0x00}) {
|
|
// Example answer from radio: 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
|
|
// 0x86, 0x1f, 0x2f, 0xcc, 0x03, 0x03, 0x89, 0x29,
|
|
// 0x00, 0x00, 0x00, 0x40, 0x02, 0x03, 0x00, 0x52,
|
|
// 0x00, 0x00, 0xf8, 0xad, 0x06, 0x8d, 0xda, 0x7b,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
|
|
// 0x80, 0x00, 0x00, 0x90, 0xc7, 0x0e, 0x86, 0x01,
|
|
// 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
if bytes.Equal(r[48:51], []byte{0xff, 0xff, 0xff}) {
|
|
if !s.serialAndAudioStreamOpened {
|
|
return errors.New("auth failed, try rebooting the radio")
|
|
}
|
|
return errors.New("auth failed")
|
|
}
|
|
if bytes.Equal(r[48:51], []byte{0x00, 0x00, 0x00}) && r[64] == 0x01 {
|
|
return errors.New("got radio disconnected")
|
|
}
|
|
}
|
|
case 144:
|
|
if !s.serialAndAudioStreamOpened && bytes.Equal(r[:6], []byte{0x90, 0x00, 0x00, 0x00, 0x00, 0x00}) && r[96] == 1 {
|
|
// Example answer:
|
|
// 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00,
|
|
// 0xc6, 0x5f, 0x6f, 0x0c, 0x5f, 0x8b, 0x1e, 0x89,
|
|
// 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x31, 0x30, 0x31, 0x47, 0x39, 0x07,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
|
|
// 0x80, 0x00, 0x00, 0x90, 0xc7, 0x0e, 0x86, 0x01,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x49, 0x43, 0x2d, 0x37, 0x30, 0x35, 0x00, 0x00, // IC-705 in plain text
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x01, 0x00, 0x00, 0x00, 0x69, 0x63, 0x6f, 0x6d,
|
|
// 0x2d, 0x70, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x03, 0x03,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
|
|
s.requestSerialAndAudioTimeout.Stop()
|
|
|
|
devName := parseNullTerminatedString(r[64:])
|
|
log.Print("got serial and audio request success, device name: ", devName)
|
|
|
|
// Stuff can change in the meantime because of a previous login...
|
|
s.common.remoteSID = binary.BigEndian.Uint32(r[8:12])
|
|
s.common.localSID = binary.BigEndian.Uint32(r[12:16])
|
|
copy(s.authID[:], r[26:32])
|
|
s.gotAuthID = true
|
|
|
|
statusLog.startPeriodicPrint()
|
|
|
|
if err := s.serial.init(devName); err != nil {
|
|
return errors.New("serial/" + err.Error())
|
|
}
|
|
|
|
if err := s.audio.init(devName); err != nil {
|
|
return errors.New("audio/" + err.Error())
|
|
}
|
|
|
|
s.serialAndAudioStreamOpened = true
|
|
|
|
runCmdRunner.startIfNeeded(runCmd)
|
|
if enableSerialDevice {
|
|
serialCmdRunner.startIfNeeded(runCmdOnSerialPortCreated)
|
|
}
|
|
if err := rigctld.initIfNeeded(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *controlStream) loop() {
|
|
netstat.reset()
|
|
|
|
s.reauthTimeoutTimer = time.NewTimer(0)
|
|
<-s.reauthTimeoutTimer.C
|
|
|
|
reauthTicker := time.NewTicker(reauthInterval)
|
|
|
|
for {
|
|
select {
|
|
case r := <-s.common.readChan:
|
|
// unless we're trying to deinit the connection, handle the data sent by the radio
|
|
if !s.deinitializing {
|
|
if err := s.handleRead(r); err != nil {
|
|
reportError(err)
|
|
}
|
|
}
|
|
case <-reauthTicker.C:
|
|
log.Debug("sending auth")
|
|
s.reauthTimeoutTimer.Reset(reauthTimeout)
|
|
if err := s.sendPktAuth(0x05); err != nil {
|
|
reportError(err)
|
|
}
|
|
case <-s.reauthTimeoutTimer.C:
|
|
log.Error("auth timeout, audio/serial stream may stop")
|
|
case <-s.deinitNeededChan:
|
|
s.deinitFinishedChan <- true
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *controlStream) init() error {
|
|
log.Debug("init")
|
|
|
|
if err := s.common.init("control", controlStreamPort); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := s.common.start(); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.common.pkt0.init(&s.common)
|
|
if err := s.sendPktLogin(); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debug("expecting login answer")
|
|
// Example success auth packet: 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
|
// 0xe6, 0xb2, 0x7b, 0x7b, 0xbb, 0x41, 0x3f, 0x2b,
|
|
// 0x00, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x5d, 0x37, 0x12, 0x82, 0x3b, 0xde,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x46, 0x54, 0x54, 0x48, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
r, err := s.common.expect(96, []byte{0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if bytes.Equal(r[48:52], []byte{0xff, 0xff, 0xff, 0xfe}) {
|
|
return errors.New("invalid username/password")
|
|
}
|
|
|
|
s.common.pkt7.startPeriodicSend(&s.common, 2, false)
|
|
|
|
copy(s.authID[:], r[26:32])
|
|
s.gotAuthID = true
|
|
if err := s.sendPktAuth(0x02); err != nil {
|
|
return err
|
|
}
|
|
log.Debug("login ok, first auth sent...")
|
|
|
|
s.common.pkt0.startPeriodicSend(&s.common)
|
|
|
|
if err := s.sendPktAuth(0x05); err != nil {
|
|
return err
|
|
}
|
|
log.Debug("second auth sent...")
|
|
|
|
s.requestSerialAndAudioTimeout = time.AfterFunc(5*time.Second, func() {
|
|
reportError(errors.New("login/serial/audio request timeout"))
|
|
})
|
|
|
|
s.deinitNeededChan = make(chan bool)
|
|
s.deinitFinishedChan = make(chan bool)
|
|
go s.loop()
|
|
return nil
|
|
}
|
|
|
|
func (s *controlStream) deinit() {
|
|
s.deinitializing = true
|
|
s.serialAndAudioStreamOpened = false
|
|
statusLog.stopPeriodicPrint()
|
|
|
|
if s.deinitNeededChan != nil {
|
|
s.deinitNeededChan <- true
|
|
<-s.deinitFinishedChan
|
|
}
|
|
if s.requestSerialAndAudioTimeout != nil {
|
|
s.requestSerialAndAudioTimeout.Stop()
|
|
s.requestSerialAndAudioTimeout = nil
|
|
}
|
|
|
|
if s.gotAuthID && s.common.gotRemoteSID && s.common.conn != nil {
|
|
log.Debug("sending deauth")
|
|
_ = s.sendPktAuth(0x01)
|
|
// Waiting a little bit to make sure the radio can send retransmit requests.
|
|
time.Sleep(500 * time.Millisecond)
|
|
}
|
|
|
|
s.common.deinit()
|
|
s.serial.deinit()
|
|
s.audio.deinit()
|
|
}
|