aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2024-11-26 10:59:17 +0100
committerPřemysl Eric Janouch <p@janouch.name>2024-11-26 11:10:15 +0100
commitbe50fd4b8ffc11c7a1c5222758f646c43bd77815 (patch)
tree0e6315fecd77590347a7fe6d55ce4b5849818355
parent781fd392fe0cde3d281060a59df38d1745658b04 (diff)
downloadusb-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.
-rw-r--r--eizo-pcap-decode.go212
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)
+ }
+ }
+}