diff --git a/cmd/bridge/ant.go b/cmd/bridge/ant.go index edc6d8a..eaf8aff 100644 --- a/cmd/bridge/ant.go +++ b/cmd/bridge/ant.go @@ -1,16 +1,25 @@ package main import ( + "bufio" "context" + "fmt" "io" "time" "github.com/google/gousb" "github.com/half2me/antgo/ant" "github.com/half2me/antgo/device" - "github.com/half2me/antgo/driver/usb" ) +type antUSB struct { + in *bufio.Reader + out *gousb.OutEndpoint +} + +func (d *antUSB) Read(p []byte) (int, error) { return d.in.Read(p) } +func (d *antUSB) Write(p []byte) (int, error) { return d.out.Write(p) } + const ( antDataPageMask = 0x7F // Mask for data page number (7 bits, clears toggle bit) antDataPageCyclingPower = 0x10 // Data page 16 = Cycling Power Feature page @@ -33,21 +42,44 @@ func resetAndWait(drv io.ReadWriter) error { } func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32, - events chan<- Event) { + events chan<- Event) error { - drv, err := usb.GetDevice(gousb.ID(vendorId), gousb.ID(productId)) + gctx := gousb.NewContext() + defer gctx.Close() + + dev, err := gctx.OpenDeviceWithVIDPID(gousb.ID(vendorId), gousb.ID(productId)) if err != nil { - panic(err) + return fmt.Errorf("ant: open device: %w", err) } - defer drv.Close() + if dev == nil { + return fmt.Errorf("ant: device not found (0x%04x:0x%04x)", vendorId, productId) + } + defer dev.Close() + + intf, done, err := dev.DefaultInterface() + if err != nil { + return fmt.Errorf("ant: claim interface: %w", err) + } + defer done() + + out, err := intf.OutEndpoint(1) + if err != nil { + return fmt.Errorf("ant: open out endpoint: %w", err) + } + inp, err := intf.InEndpoint(1) + if err != nil { + return fmt.Errorf("ant: open in endpoint: %w", err) + } + + drv := &antUSB{in: bufio.NewReader(inp), out: out} logMsg("ant: device opened") if err := resetAndWait(drv); err != nil { - panic(err) + return err } if err := device.StartRxScanModeWait(drv); err != nil { - panic(err) + return err } logMsg("ant: scan mode started") @@ -92,7 +124,7 @@ func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32, } case <-time.After(10 * time.Second): logMsg("ant: no more messages arrived, restarting") - return + return nil } } } diff --git a/cmd/bridge/cli.go b/cmd/bridge/cli.go index 4d9e13e..33c4460 100644 --- a/cmd/bridge/cli.go +++ b/cmd/bridge/cli.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" "strconv" + "time" "github.com/spf13/cobra" ) @@ -83,7 +84,10 @@ This allows these trainers to be used with modern devices that don't support ANT events := make(chan Event, 1) go func() { for { - ListenForTrainer(uint16(c.vendorID), uint16(c.productID), c.deviceNumber, events) + if err := ListenForTrainer(uint16(c.vendorID), uint16(c.productID), c.deviceNumber, events); err != nil { + logPrintf("ant: listener error: %v, retry in 10 seconds\n", err) + time.Sleep(10 * time.Second) + } } }() ExposeBluetooth(events)