244 lines
6.2 KiB
Go
244 lines
6.2 KiB
Go
package update
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/albenik/go-serial/v2"
|
|
"github.com/go-playground/log/v7"
|
|
"github.com/schollz/progressbar/v3"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const CHUNKSIZE = 256
|
|
|
|
type modem struct {
|
|
port serial.Port
|
|
}
|
|
|
|
func initmodem(serport string, baudrate int) (*modem, error) {
|
|
p, err := serial.Open(serport,
|
|
serial.WithBaudrate(baudrate),
|
|
serial.WithDataBits(8),
|
|
serial.WithParity(serial.NoParity),
|
|
serial.WithStopBits(serial.OneStopBit),
|
|
serial.WithReadTimeout(70),
|
|
serial.WithWriteTimeout(70)) // 50 was not stable, bump to 70
|
|
if err != nil {
|
|
fmt.Printf("ERROR: File %s could not be opened\n", serport)
|
|
return nil, err
|
|
}
|
|
m := &modem{*p}
|
|
return m, nil
|
|
}
|
|
|
|
func (m *modem) write(out string, nolog ...bool) ([]byte, error) {
|
|
if len(nolog) == 0 {
|
|
log.Debug("Sending " + out)
|
|
} else {
|
|
if len(out) < 200 { // in other cases it's the firmware!
|
|
log.Debugf("Sending %d byte(s)", len(out))
|
|
}
|
|
}
|
|
_, err := m.port.Write([]byte(out))
|
|
if err != nil {
|
|
log.Errorf("ERROR while sending serial command: %s\n", out)
|
|
log.Error(err)
|
|
return []byte(""), err
|
|
}
|
|
buff := make([]byte, 1024)
|
|
if len(nolog) == 0 {
|
|
time.Sleep(50 * time.Millisecond) // give the PTC some time to answer, no needed with binary
|
|
}
|
|
n, err := m.port.Read(buff)
|
|
// fmt.Printf("Read %d bytes\n", n)
|
|
if err != nil {
|
|
log.Errorf("ERROR: cannot read answer from command: %s\n", out)
|
|
return []byte(""), err
|
|
}
|
|
// TODO: Handle EOF
|
|
return buff[:n], nil
|
|
|
|
}
|
|
|
|
func (m *modem) getSerNum() (string, error) {
|
|
answer, err := m.write("\rsys sern\r")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
re := regexp.MustCompile(`Serial number: \w{2,16}`)
|
|
sn := strings.ReplaceAll(string(re.Find(answer)), "Serial number: ", "")
|
|
return sn, nil
|
|
}
|
|
|
|
func (m *modem) setDateTime() error {
|
|
ct := time.Now()
|
|
answer, err := m.write("DATE " + ct.Format("020106\r"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if strings.Contains(string(answer), "BAD ARGUMENT") {
|
|
return errors.New("setting DATE returns a BAD ARGUMENT error!")
|
|
}
|
|
answer, err = m.write("TIME " + ct.Format("150405\r"))
|
|
if strings.Contains(string(answer), "BAD ARGUMENT") {
|
|
return errors.New("setting TIME returns a BAD ARGUMENT error!")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *modem) writeUpdate(fwfile string) error {
|
|
_, err := m.write("UPDATE\r")
|
|
if err != nil {
|
|
_, _ = m.write("\033", true) // send ESC to cancel the update
|
|
return err
|
|
}
|
|
answer, err := m.write("\006", true)
|
|
if err != nil {
|
|
_, _ = m.write("\033", true) // send ESC to cancel the update
|
|
return err
|
|
}
|
|
|
|
log.Debugf("flash stamp: % X", binary.LittleEndian.Uint32(answer[2:6]))
|
|
flashId := binary.LittleEndian.Uint16(answer[0:2])
|
|
log.Debugf("flash id: % X", flashId)
|
|
if flashId != 0xa41f && flashId != 0x5b1f && flashId != 0xda1f {
|
|
_, _ = m.write("\033", true) // send ESC to cancel the update
|
|
return errors.New("wrong flash id")
|
|
}
|
|
r, err := os.Open(fwfile)
|
|
if err != nil {
|
|
_, _ = m.write("\033", true) // send ESC to cancel the update
|
|
return err
|
|
}
|
|
fstat, _ := r.Stat()
|
|
fsize := fstat.Size()
|
|
_ = r.Close()
|
|
|
|
fwblob, err := ioutil.ReadFile(fwfile)
|
|
if err != nil {
|
|
_, _ = m.write("\033", true) // send ESC to cancel the update
|
|
return err
|
|
}
|
|
// fmt.Println(fsize)
|
|
chunks := uint16(fsize / 256)
|
|
if fsize%256 != 0 {
|
|
chunks++
|
|
}
|
|
|
|
// send ACK
|
|
_, err = m.write("\006", true)
|
|
if err != nil {
|
|
_, _ = m.write("\033", true) // send ESC to cancel the update
|
|
return err
|
|
}
|
|
|
|
// send number of chunks
|
|
a := make([]byte, 2)
|
|
binary.BigEndian.PutUint16(a, chunks)
|
|
res, err := m.write(string(a), true)
|
|
if err != nil || string(res) != "\006" { //no ACK
|
|
_, _ = m.write("\033", true) // send ESC to cancel the update
|
|
return err
|
|
}
|
|
|
|
bar := progressbar.Default(int64(chunks), "flashing chunk")
|
|
var sendbuff string = ""
|
|
for i := 0; i < int(chunks); i++ {
|
|
_ = bar.Add(1)
|
|
//log.Debugf("Sending chunk %d", i)
|
|
if i*CHUNKSIZE+CHUNKSIZE > len(fwblob) {
|
|
nullbytes := make([]byte, CHUNKSIZE-len(fwblob[(i*CHUNKSIZE):]))
|
|
sendbuff = string(append(fwblob[(i*CHUNKSIZE):], nullbytes...))
|
|
} else {
|
|
sendbuff = string(fwblob[i*CHUNKSIZE : i*CHUNKSIZE+CHUNKSIZE])
|
|
}
|
|
res, err = m.write(sendbuff, true)
|
|
if err != nil || string(res) != "\006" { //no ACK
|
|
_, _ = m.write("\033", true) // send ESC to cancel the update
|
|
log.Errorf("Error in Handshake! Rx: %s", hex.EncodeToString(res))
|
|
return err
|
|
}
|
|
}
|
|
log.Infof("Firmware file %s successfully written", fwfile)
|
|
_, err = m.write("\r", true)
|
|
if err != nil {
|
|
log.Error("Error in Handshake: cannot send final cr")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *modem) checkFirmwareFits(fwfile string) error {
|
|
answer, err := m.write("ver ##\r")
|
|
if err != nil {
|
|
log.Errorf("Error while reading the modem version: %s", err)
|
|
return err
|
|
}
|
|
re := regexp.MustCompile(`\w#1`)
|
|
version := strings.ReplaceAll(string(re.Find(answer)), "#1", "")
|
|
type modemtype struct {
|
|
ext, modem string
|
|
}
|
|
var extensions = map[string]modemtype{
|
|
"A": {".pt2", "PTC-II"},
|
|
"B": {".pro", "PTC-IIpro"},
|
|
"C": {".pte", "PTC-IIe"},
|
|
"D": {".pex", "PTC-IIex"},
|
|
"E": {".ptu", "PTC-IIusb"},
|
|
"F": {".ptn", "PTC-IInet"},
|
|
"H": {".dr7", "DR-7800"},
|
|
"I": {".dr7", "DR-7400"},
|
|
"K": {".pr7", "DR-7000"},
|
|
"L": {".p3u", "PTC-IIIusb"},
|
|
"T": {".ptx", "PTC-IIItrx"},
|
|
}
|
|
|
|
modem, exists := extensions[version]
|
|
if exists {
|
|
if modem.ext == path.Ext(fwfile) {
|
|
log.Infof("Firmware %s matches detected modem type %s.", fwfile, modem.modem)
|
|
return nil
|
|
} else {
|
|
log.Fatalf("Firmware %s does NOT match detected modem type %s.", fwfile, modem.modem)
|
|
return errors.New("Firmware and modem type do not match")
|
|
}
|
|
} else {
|
|
log.Fatal("Cannot detect modem type.")
|
|
return errors.New("Cannot detect modem type")
|
|
}
|
|
|
|
}
|
|
|
|
func Update(serport string, baudrate int, fwfile string) error {
|
|
m, err := initmodem(serport, baudrate)
|
|
defer m.port.Close()
|
|
if err != nil {
|
|
return errors.New("FEHLER in Update")
|
|
}
|
|
sn, err := m.getSerNum()
|
|
if len(sn) < 6 {
|
|
// can't be a valid serial number, something's wrong, bump out here
|
|
log.Errorf("ERROR: got invalid serial number: %s", sn)
|
|
return err
|
|
} else {
|
|
log.Infof("Serial number of your modem is: %s", sn)
|
|
}
|
|
if m.setDateTime() != nil {
|
|
return err
|
|
}
|
|
if m.checkFirmwareFits(fwfile) != nil {
|
|
return err
|
|
}
|
|
if m.writeUpdate(fwfile) != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|