diff options
author | Přemysl Eric Janouch <p@janouch.name> | 2024-11-26 10:59:17 +0100 |
---|---|---|
committer | Přemysl Eric Janouch <p@janouch.name> | 2024-11-26 11:10:15 +0100 |
commit | be50fd4b8ffc11c7a1c5222758f646c43bd77815 (patch) | |
tree | 0e6315fecd77590347a7fe6d55ce4b5849818355 /eizo-pcap-decode.go | |
parent | 781fd392fe0cde3d281060a59df38d1745658b04 (diff) | |
download | usb-drivers-be50fd4b8ffc11c7a1c5222758f646c43bd77815.tar.gz usb-drivers-be50fd4b8ffc11c7a1c5222758f646c43bd77815.tar.xz usb-drivers-be50fd4b8ffc11c7a1c5222758f646c43bd77815.zip |
Add an EIZO packet decoding tool
It is fairly dumb, but it allows to make more sense of what is
going on over the wires while using EIZO software.
Diffstat (limited to 'eizo-pcap-decode.go')
-rw-r--r-- | eizo-pcap-decode.go | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/eizo-pcap-decode.go b/eizo-pcap-decode.go new file mode 100644 index 0000000..838919e --- /dev/null +++ b/eizo-pcap-decode.go @@ -0,0 +1,212 @@ +// Usage: tshark { -r FILE | -i INTERFACE } -l -T ek \ +// | go run eizo-pcap-decode.go [ | less -R] +// +// This cannot be done through -T json, because tshark doesn't immediately +// flush the current object's trailing newline, but rather waits to decide +// if it should follow it with a comma. Even with -l, it will flush it late. +// It would be good if we could convince it not to wrap packets in a big array. +package main + +import ( + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "os" + "strings" +) + +type Packet struct { + Layers struct { + USB struct { + Source string `json:"usb_usb_src"` + Destination string `json:"usb_usb_dst"` + Direction string `json:"usb_usb_endpoint_address_direction"` + MacEndpointType string `json:"usb_usb_darwin_endpoint_type"` + TransferType string `json:"usb_usb_transfer_type"` + } `json:"usb"` + CapData string `json:"usb_usb_capdata"` + ControlResponse string `json:"usb_usb_control_Response"` + DataFragment string `json:"usb_usb_data_fragment"` + } `json:"layers"` +} + +func (p *Packet) addr() string { + if p.Layers.USB.Source == "host" { + return p.Layers.USB.Destination + } else { + return p.Layers.USB.Source + } +} + +func (p *Packet) isInterrupt() bool { + return p.Layers.USB.MacEndpointType == "3" || + p.Layers.USB.TransferType == "0x01" +} + +func (p *Packet) isControl() bool { + return p.Layers.USB.MacEndpointType == "0" || + p.Layers.USB.TransferType == "0x02" +} + +func (p *Packet) isIncoming() bool { + return p.Layers.USB.Direction == "1" +} + +func hexDecode(encoded string) []byte { + decoded, err := hex.DecodeString(strings.ReplaceAll(encoded, ":", "")) + if err != nil { + panic(err) + } + return decoded +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +var ( + raw *bool + le = binary.LittleEndian + + fmtIn, fmtOut, fmtReset string +) + +func decodeSubreport(data []byte) string { + if len(data) < 6 { + return fmt.Sprintf("%x", data) + } + usage := uint32(le.Uint16(data[:2]))<<16 | uint32(le.Uint16(data[2:4])) + filtered := make([]byte, len(data)-6) + for i, b := range data[6:] { + if b < 32 || b > 126 { + filtered[i] = '.' + } else { + filtered[i] = b + } + } + return fmt.Sprintf("<> %08x %04x %x %s", usage, le.Uint16(data[4:6]), + data[6:], string(filtered)) +} + +func decodeResult(data []byte) string { + if len(data) < 7 { + return fmt.Sprintf("%x", data) + } + usage := uint32(le.Uint16(data[:2]))<<16 | uint32(le.Uint16(data[2:4])) + return fmt.Sprintf(">< %08x %04x %02x", usage, le.Uint16(data[4:6]), + data[6]) +} + +func decodeMP(data []byte) string { + var out string + for i := 0; i+1 < len(data); { + sz := int(data[i+1]) + if data[i] == 0xff || i+sz > len(data) { + break + } + if out != "" { + out += " " + } + out += fmt.Sprintf("[%02x] %x", data[i], data[i+2:i+2+sz]) + i += 2 + sz + } + return out +} + +func isSetSubreport(id byte) bool { + switch id { + case 2, 4, 11, 13: + return true + } + return false +} + +func isGetSubreport(id byte) bool { + switch id { + case 3, 5, 12, 14: + return true + } + return false +} + +func isSubreport(id byte) bool { + return isSetSubreport(id) || isGetSubreport(id) +} + +func processInterrupt(p *Packet) { + data := hexDecode(p.Layers.CapData) + if len(data) < 1 { + return + } + if *raw { + fmt.Printf("%s INT %02x %x\n", p.addr(), data[0], data[1:]) + } else if isSubreport(data[0]) { + fmt.Printf("%s INT %s\n", p.addr(), decodeSubreport(data[1:])) + } +} + +func processControl(p *Packet) { + // macOS (Darwin) and Linux report Set_Feature differently. + data := hexDecode(p.Layers.ControlResponse) + if len(data) == 0 { + data = hexDecode(p.Layers.DataFragment) + } + if len(data) < 1 { + return + } + if p.isIncoming() { + if *raw { + fmt.Printf("%s IN %02x %x\n", p.addr(), data[0], data[1:]) + } else if data[0] == 1 { + fmt.Printf("%s IN SR %x\n", p.addr(), data[5:]) + } else if isGetSubreport(data[0]) { + fmt.Printf("%s IN %s%s%s\n", p.addr(), + fmtIn, decodeSubreport(data[1:]), fmtReset) + } else if data[0] == 6 { + fmt.Printf("%s IN PC %04x\n", p.addr(), le.Uint16(data[1:])) + } else if data[0] == 7 { + fmt.Printf("%s IN %s\n", p.addr(), decodeResult(data[1:])) + } else if data[0] == 8 { + fmt.Printf("%s IN ID %s %s\n", p.addr(), data[1:9], data[9:]) + } else if data[0] == 9 { + fmt.Printf("%s IN MP %s\n", p.addr(), decodeMP(data[1:])) + } else { + fmt.Printf("%s IN %02x %x\n", p.addr(), data[0], data[1:]) + } + } else { + if *raw { + fmt.Printf("%s OUT %02x %x\n", p.addr(), data[0], data[1:]) + } else if isSetSubreport(data[0]) { + fmt.Printf("%s OUT %s%s%s\n", p.addr(), + fmtOut, decodeSubreport(data[1:]), fmtReset) + } else if data[0] != 1 && !isGetSubreport(data[0]) { + fmt.Printf("%s OUT %02x %x\n", p.addr(), data[0], data[1:]) + } + } +} + +func main() { + raw = flag.Bool("raw", false, "Do not decode EIZO packets") + flag.Parse() + + if _, ok := os.LookupEnv("NO_COLOR"); !ok { + fmtIn, fmtOut, fmtReset = "\x1b[34m", "\x1b[31m", "\x1b[m" + } + + decoder := json.NewDecoder(os.Stdin) + for { + var p Packet + if err := decoder.Decode(&p); err != nil { + if errors.Is(err, io.EOF) { + break + } + fmt.Fprintf(os.Stderr, "%v\n", err) + } else if p.isInterrupt() { + processInterrupt(&p) + } else if p.isControl() { + processControl(&p) + } + } +} |