grim/gousb2snes

Lots of work, most rearranging
draft
2019-03-03, Gary Kramlich
c3cd4e43b5a9
Parents 32c861e0f399
Children 58b2dfa1d93e
Lots of work, most rearranging
--- a/commands/byteslice.go Wed Feb 27 00:21:12 2019 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package commands
-
-// overwrite will overwrite the a with the entire run of b
-func overwrite(a, b []byte) {
- for i, v := range b {
- a[i] = v
- }
-}
-
-// equal checks if byte slice a starts with b
-func equal(a, b []byte) bool {
- if len(a) < len(b) {
- return false
- }
-
- for i, v := range b {
- if a[i] != v {
- return false
- }
- }
-
- return true
-}
--- a/commands/commands.go Wed Feb 27 00:21:12 2019 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-package commands
-
-import (
- "errors"
- "io"
-
- "bitbucket.org/rw_grim/gousb2snes/protocol"
-)
-
-type Command interface {
- Send(rwc io.ReadWriteCloser) error
-}
-
-func sync(rwc io.ReadWriteCloser, opcode protocol.OpCode, ns protocol.Namespace, flags protocol.Flag) ([]byte, error) {
- req := make([]byte, 512)
- header := []byte{'U', 'S', 'B', 'A', byte(opcode), byte(ns), byte(flags)}
-
- overwrite(req, header)
-
- n, err := rwc.Write(req)
- if err != nil {
- return nil, err
- }
-
- if n == 0 {
- return nil, errors.New("failed to write!")
- }
-
- resp := make([]byte, 512)
- total := 0
- for total < 512 {
- n, err = rwc.Read(resp[total:])
- total += n
-
- if err != nil {
- return nil, err
- }
- }
-
- if !equal(resp, []byte{'U', 'S', 'B', 'A', byte(protocol.OpCodeResponse)}) {
- return nil, errors.New("Bad response")
- }
-
- if resp[5] == 1 {
- return nil, errors.New("Bad response")
- }
-
- return resp, nil
-}
--- a/commands/info.go Wed Feb 27 00:21:12 2019 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-package commands
-
-import (
- "encoding/binary"
- "fmt"
- "io"
- "strings"
-
- "bitbucket.org/rw_grim/gousb2snes/protocol"
-)
-
-type Info struct {
- version string
- fwver string
- features InfoFeature
- rom string
-}
-
-type InfoFeature byte
-
-const (
- InfoFeatureDSPX InfoFeature = 1 << iota
- InfoFeatureST0010
- InfoFeatureSRTC
- InfoFeatureMSU1
- InfoFeature213F
- InfoFeatureCmdUnlock
- InfoFeatureUSB1
- InfoFeatureDMA1
-)
-
-func (f InfoFeature) String() string {
- r := []string{}
-
- if f&InfoFeatureDSPX != 0 {
- r = append(r, "FEAT_DSPX")
- }
- if f&InfoFeatureST0010 != 0 {
- r = append(r, "FEAT_ST0010")
- }
- if f&InfoFeatureSRTC != 0 {
- r = append(r, "FEAT_SRTC")
- }
- if f&InfoFeatureMSU1 != 0 {
- r = append(r, "FEAT_MSU1")
- }
- if f&InfoFeature213F != 0 {
- r = append(r, "FEAT_213F")
- }
- if f&InfoFeatureCmdUnlock != 0 {
- r = append(r, "FEAT_CMD_UNLOCK")
- }
- if f&InfoFeatureUSB1 != 0 {
- r = append(r, "FEAT_USB1")
- }
- if f&InfoFeatureDMA1 != 0 {
- r = append(r, "FEAT_DMA1")
- }
- return strings.Join(r, "|")
-}
-
-func (i *Info) Send(rwc io.ReadWriteCloser) error {
- resp, err := sync(rwc, protocol.OpCodeInfo, protocol.NamespaceSNES, protocol.FlagNone)
- if err != nil {
- return err
- }
-
- fwver := binary.BigEndian.Uint32(resp[256:])
- i.fwver = fmt.Sprintf("%08x", fwver)
-
- i.version = nullTerminated(resp[260:])
- i.features = InfoFeature(resp[6])
- i.rom = nullTerminated(resp[16:])
-
- return nil
-}
-
-func (i *Info) String() string {
- return fmt.Sprintf(
- "version: %s\nfwver: %s\nfeatures: %s\nrom: %s",
- i.version,
- i.fwver,
- i.features,
- i.rom,
- )
-}
--- a/commands/string.go Wed Feb 27 00:21:12 2019 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package commands
-
-func nullTerminated(buffer []byte) string {
- r := ""
-
- for _, b := range buffer {
- if b == 0x0 {
- break
- }
-
- r += string(b)
- }
-
- return r
-}
--- a/main.go Wed Feb 27 00:21:12 2019 -0600
+++ b/main.go Sun Mar 03 18:52:34 2019 -0600
@@ -3,35 +3,54 @@
import (
"fmt"
"os"
+ "os/signal"
+ "syscall"
- "github.com/jacobsa/go-serial/serial"
-
- "bitbucket.org/rw_grim/gousb2snes/commands"
+ "bitbucket.org/rw_grim/gousb2snes/network"
+ "bitbucket.org/rw_grim/gousb2snes/sd2snes"
)
func main() {
- opts := serial.OpenOptions{
- PortName: "/dev/ttyACM0",
- BaudRate: 9600,
- DataBits: 8,
- StopBits: 1,
- MinimumReadSize: 4,
- }
-
- sock, err := serial.Open(opts)
+ sock, err := sd2snes.New()
if err != nil {
fmt.Printf("failed to open serial device : %v\n", err)
- os.Exit(1)
+ return
}
-
defer sock.Close()
- info := &commands.Info{}
- err = info.Send(sock)
+ info := &sd2snes.Info{}
+ err = sock.Send(info)
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
+ fmt.Printf("-----\n%s\n", info)
- fmt.Printf("-----\n%s\n", info)
+ // setup the tcp server
+ server := network.NewServer()
+ err = server.Listen(":8001")
+ if err != nil {
+ fmt.Printf("Error: %#v\n", err)
+ return
+ }
+
+ // create our error channel
+ errChan := make(chan error)
+
+ go server.Run(errChan)
+ defer server.Shutdown()
+
+ // add unix signal handling
+ signalChan := make(chan os.Signal, 1)
+ signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
+
+ for {
+ select {
+ case err := <-errChan:
+ fmt.Printf("Error: %#v\n", err)
+ case s := <-signalChan:
+ fmt.Printf("caught signal %v\n", s)
+ return
+ }
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/network/client.go Sun Mar 03 18:52:34 2019 -0600
@@ -0,0 +1,60 @@
+package network
+
+import (
+ "encoding/binary"
+ "fmt"
+ "net"
+)
+
+const (
+ packetSize = 16
+)
+
+type Client struct {
+ connection net.Conn
+ running bool
+}
+
+func NewClient(c net.Conn) *Client {
+ return &Client{
+ connection: c,
+ running: false,
+ }
+}
+
+func (c *Client) Run(errChan chan error) {
+ c.running = true
+ defer func() {
+ c.running = false
+ c.connection.Close()
+ }()
+
+ for {
+ if !c.running {
+ break
+ }
+
+ packet := []byte{}
+
+ n, err := c.connection.Read(packet)
+ if err != nil {
+ errChan <- err
+ return
+ }
+
+ if n != packetSize {
+ errChan <- fmt.Errorf("read %d bytes expected %d", n, packetSize)
+ return
+ }
+
+ typ := binary.BigEndian.Uint32(packet[0:])
+ release := binary.BigEndian.Uint32(packet[4:])
+ address := binary.BigEndian.Uint32(packet[8:])
+
+ fmt.Printf("type: %v, release: %v, address: %v\n", typ, release, address)
+ }
+}
+
+func (c *Client) Close() {
+ c.running = false
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/network/server.go Sun Mar 03 18:52:34 2019 -0600
@@ -0,0 +1,52 @@
+package network
+
+import (
+ "net"
+)
+
+type Server struct {
+ running bool
+ listener net.Listener
+}
+
+func NewServer() *Server {
+ return &Server{}
+}
+
+func (s *Server) Listen(addr string) error {
+ listener, err := net.Listen("tcp", addr)
+ if err != nil {
+ return err
+ }
+
+ s.listener = listener
+
+ return nil
+}
+
+func (s *Server) Run(errChan chan error) {
+ s.running = true
+ defer func() {
+ s.running = false
+ s.listener.Close()
+ }()
+
+ for {
+ if !s.running {
+ break
+ }
+
+ c, err := s.listener.Accept()
+ if err != nil {
+ errChan <- err
+ continue
+ }
+
+ client := NewClient(c)
+ go client.Run(errChan)
+ }
+}
+
+func (s *Server) Shutdown() {
+ s.running = false
+}
--- a/protocol/consts.go Wed Feb 27 00:21:12 2019 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-package protocol
-
-type OpCode byte
-
-const (
- OpCodeGet OpCode = iota
- OpCodePut
- OpCodeVGet
- OpCodeVPut
- OpCodeLs
- OpCodeMkdir
- OpCodeRm
- OpCodeMv
- OpCodeReset
- OpCodeBoot
- OpCodePowerCycle
- OpCodeInfo
- OpCodeMenuReset
- OpCodeStream
- OpCodeTime
- OpCodeResponse
-)
-
-type Flag byte
-
-const (
- FlagNone Flag = 1 << iota
- FlagSkiPreset
- FlagOnlyReset
- FlagClrX
- FlagSetX
- FlagStreamBurst
- FlagNoResponse
- FlagData64B
-)
-
-type Namespace byte
-
-const (
- NamespaceFile Namespace = iota
- NamespaceSNES
- NamespaceMSU
- NamespaceCommand
- NamespaceConfig
-)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sd2snes/byteslice.go Sun Mar 03 18:52:34 2019 -0600
@@ -0,0 +1,23 @@
+package sd2snes
+
+// overwrite will overwrite the a with the entire run of b
+func overwrite(a, b []byte) {
+ for i, v := range b {
+ a[i] = v
+ }
+}
+
+// equal checks if byte slice a starts with b
+func equal(a, b []byte) bool {
+ if len(a) < len(b) {
+ return false
+ }
+
+ for i, v := range b {
+ if a[i] != v {
+ return false
+ }
+ }
+
+ return true
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sd2snes/client.go Sun Mar 03 18:52:34 2019 -0600
@@ -0,0 +1,74 @@
+package sd2snes
+
+import (
+ "errors"
+ "io"
+
+ "github.com/jacobsa/go-serial/serial"
+)
+
+type Client struct {
+ sock io.ReadWriteCloser
+}
+
+func New() (*Client, error) {
+ opts := serial.OpenOptions{
+ PortName: "/dev/ttyACM0",
+ BaudRate: 9600,
+ DataBits: 8,
+ StopBits: 1,
+ MinimumReadSize: 4,
+ }
+
+ sock, err := serial.Open(opts)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Client{
+ sock: sock,
+ }, nil
+}
+
+func (c *Client) Close() {
+ c.sock.Close()
+}
+
+func (c *Client) Send(cmd Command) error {
+ opcode, ns, flags := cmd.Identifier()
+
+ req := make([]byte, 512)
+ header := []byte{'U', 'S', 'B', 'A', byte(opcode), byte(ns), byte(flags)}
+
+ overwrite(req, header)
+
+ n, err := c.sock.Write(req)
+ if err != nil {
+ return err
+ }
+
+ if n == 0 {
+ return errors.New("failed to write!")
+ }
+
+ resp := make([]byte, 512)
+ total := 0
+ for total < 512 {
+ n, err = c.sock.Read(resp[total:])
+ total += n
+
+ if err != nil {
+ return err
+ }
+ }
+
+ if !equal(resp, []byte{'U', 'S', 'B', 'A', byte(OpCodeResponse)}) {
+ return errors.New("Bad response")
+ }
+
+ if resp[5] == 1 {
+ return errors.New("Bad response")
+ }
+
+ return cmd.Process(resp)
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sd2snes/commands.go Sun Mar 03 18:52:34 2019 -0600
@@ -0,0 +1,6 @@
+package sd2snes
+
+type Command interface {
+ Identifier() (OpCode, Namespace, Flag)
+ Process([]byte) error
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sd2snes/consts.go Sun Mar 03 18:52:34 2019 -0600
@@ -0,0 +1,45 @@
+package sd2snes
+
+type OpCode byte
+
+const (
+ OpCodeGet OpCode = iota
+ OpCodePut
+ OpCodeVGet
+ OpCodeVPut
+ OpCodeLs
+ OpCodeMkdir
+ OpCodeRm
+ OpCodeMv
+ OpCodeReset
+ OpCodeBoot
+ OpCodePowerCycle
+ OpCodeInfo
+ OpCodeMenuReset
+ OpCodeStream
+ OpCodeTime
+ OpCodeResponse
+)
+
+type Flag byte
+
+const (
+ FlagNone Flag = 1 << iota
+ FlagSkiPreset
+ FlagOnlyReset
+ FlagClrX
+ FlagSetX
+ FlagStreamBurst
+ FlagNoResponse
+ FlagData64B
+)
+
+type Namespace byte
+
+const (
+ NamespaceFile Namespace = iota
+ NamespaceSNES
+ NamespaceMSU
+ NamespaceCommand
+ NamespaceConfig
+)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sd2snes/info.go Sun Mar 03 18:52:34 2019 -0600
@@ -0,0 +1,82 @@
+package sd2snes
+
+import (
+ "encoding/binary"
+ "fmt"
+ "strings"
+)
+
+type Info struct {
+ version string
+ fwver string
+ features InfoFeature
+ rom string
+}
+
+type InfoFeature byte
+
+const (
+ InfoFeatureDSPX InfoFeature = 1 << iota
+ InfoFeatureST0010
+ InfoFeatureSRTC
+ InfoFeatureMSU1
+ InfoFeature213F
+ InfoFeatureCmdUnlock
+ InfoFeatureUSB1
+ InfoFeatureDMA1
+)
+
+func (f InfoFeature) String() string {
+ r := []string{}
+
+ if f&InfoFeatureDSPX != 0 {
+ r = append(r, "FEAT_DSPX")
+ }
+ if f&InfoFeatureST0010 != 0 {
+ r = append(r, "FEAT_ST0010")
+ }
+ if f&InfoFeatureSRTC != 0 {
+ r = append(r, "FEAT_SRTC")
+ }
+ if f&InfoFeatureMSU1 != 0 {
+ r = append(r, "FEAT_MSU1")
+ }
+ if f&InfoFeature213F != 0 {
+ r = append(r, "FEAT_213F")
+ }
+ if f&InfoFeatureCmdUnlock != 0 {
+ r = append(r, "FEAT_CMD_UNLOCK")
+ }
+ if f&InfoFeatureUSB1 != 0 {
+ r = append(r, "FEAT_USB1")
+ }
+ if f&InfoFeatureDMA1 != 0 {
+ r = append(r, "FEAT_DMA1")
+ }
+ return strings.Join(r, "|")
+}
+
+func (i *Info) Identifier() (OpCode, Namespace, Flag) {
+ return OpCodeInfo, NamespaceSNES, FlagNone
+}
+
+func (i *Info) Process(data []byte) error {
+ fwver := binary.BigEndian.Uint32(data[256:])
+ i.fwver = fmt.Sprintf("%08x", fwver)
+
+ i.version = nullTerminated(data[260:])
+ i.features = InfoFeature(data[6])
+ i.rom = nullTerminated(data[16:])
+
+ return nil
+}
+
+func (i *Info) String() string {
+ return fmt.Sprintf(
+ "version: %s\nfwver: %s\nfeatures: %s\nrom: %s",
+ i.version,
+ i.fwver,
+ i.features,
+ i.rom,
+ )
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sd2snes/string.go Sun Mar 03 18:52:34 2019 -0600
@@ -0,0 +1,15 @@
+package sd2snes
+
+func nullTerminated(buffer []byte) string {
+ r := ""
+
+ for _, b := range buffer {
+ if b == 0x0 {
+ break
+ }
+
+ r += string(b)
+ }
+
+ return r
+}