gary/debugger/serial/serial.go

140 lines
2.9 KiB
Go
Raw Permalink Normal View History

package serial
import (
2024-09-19 06:31:34 +02:00
"encoding/binary"
"errors"
2024-09-19 06:31:34 +02:00
"fmt"
"io"
"sync"
"time"
"go.bug.st/serial"
)
2024-09-19 06:31:34 +02:00
const (
portDev = "/dev/ttyUSB0"
portSpeed = 1_382_400 // 1.3Mbps
2024-09-19 06:31:34 +02:00
maxAddr = 1 << 17
pingResponse = 0xEA
readTimeout = 2 * time.Second
)
type Serial struct {
portMu sync.Mutex
port serial.Port
}
func Open() (*Serial, error) {
mode := &serial.Mode{
BaudRate: portSpeed,
}
port, err := serial.Open(portDev, mode)
if err != nil {
return nil, err
}
2024-09-19 06:31:34 +02:00
packet := encode(cmdPing, 0, 0)
if _, err := port.Write(packet[:]); err != nil {
port.Close()
return nil, err
}
2024-09-19 06:31:34 +02:00
port.SetReadTimeout(readTimeout)
if _, err := io.ReadFull(port, packet[:1]); err != nil {
port.Close()
return nil, err
}
if packet[0] != pingResponse {
port.Close()
return nil, fmt.Errorf("wrong ping response: got %x, want %x", packet[0], pingResponse)
}
return &Serial{port: port}, nil
}
func (d *Serial) Close() error {
d.portMu.Lock()
defer d.portMu.Unlock()
return d.port.Close()
}
2024-09-19 06:31:34 +02:00
const (
cmdReadByte = 1
cmdWriteByte = 2
cmdReadRange = 3
cmdPing = 0x7F
)
func encode(cmd int, addr int, data byte) [4]byte {
cmd = cmd & (1<<7 - 1)
addr = addr & (1<<17 - 1)
raw := (uint32(cmd) << 25) + (uint32(addr) << 8) + uint32(data)
var ret [4]byte
2024-09-19 06:31:34 +02:00
binary.BigEndian.PutUint32(ret[:], raw)
return ret
}
2024-09-19 06:31:34 +02:00
// readSlow reads memory one byte at a time, and is 10x slower than
// burst reading. The code is preserved here just because it's a
// simpler implementation in the hardware, and is thus sometimes
// useful for debugging.
func (d *Serial) readSlow(bs []byte, off int64) (int, error) {
d.portMu.Lock()
defer d.portMu.Unlock()
if off+int64(len(bs)) > maxAddr {
return 0, errors.New("OOB read")
}
for i := range bs {
2024-09-19 06:31:34 +02:00
packet := encode(cmdReadByte, int(off)+i, 0)
if _, err := d.port.Write(packet[:]); err != nil {
return i, err
}
2024-09-19 06:31:34 +02:00
d.port.SetReadTimeout(readTimeout)
n, err := io.ReadFull(d.port, bs[i:i+1])
if err != nil {
2024-09-19 06:31:34 +02:00
return i + n, err
}
}
return len(bs), nil
}
func (d *Serial) readBurst(bs []byte, off int64) (int, error) {
d.portMu.Lock()
defer d.portMu.Unlock()
if off+int64(len(bs)) > maxAddr {
2024-09-19 06:31:34 +02:00
return 0, errors.New("OOB read")
}
for i := 0; i < len(bs); i += 255 {
burstLen := min(len(bs)-i, 255)
packet := encode(cmdReadRange, int(off)+i, byte(burstLen))
if _, err := d.port.Write(packet[:]); err != nil {
return i, err
}
2024-09-19 06:31:34 +02:00
d.port.SetReadTimeout(readTimeout)
n, err := io.ReadFull(d.port, bs[i:i+burstLen])
if err != nil {
return i + n, err
}
}
return len(bs), nil
}
2024-09-19 06:31:34 +02:00
func (d *Serial) ReadAt(bs []byte, off int64) (int, error) {
return d.readBurst(bs, off)
}
func (d *Serial) WriteAt(bs []byte, off int64) (int, error) {
d.portMu.Lock()
defer d.portMu.Unlock()
if off+int64(len(bs)) > maxAddr {
return 0, errors.New("OOB write")
}
for i, v := range bs {
2024-09-19 06:31:34 +02:00
packet := encode(cmdWriteByte, int(off)+i, v)
if _, err := d.port.Write(packet[:]); err != nil {
return i, err
}
}
return len(bs), nil
}