Robustness for the USB connection.
ant: replace usb.GetDevice with direct gousb usage Remove dependency on antgo/driver/usb to fix libusb context leaks on device-not-found errors. The previous code called usb.GetDevice which creates a gousb.Context via libusb_init but leaks it on every error path (device not found, interface claim failure, etc.). Calling ListenForTrainer repeatedly (e.g. starting with no dongle and plugging it in later) triggered a libusb assertion in pthread_key_create because the leaked contexts were never torn down. Replace with direct gousb calls in ListenForTrainer, managing the gousb.Context lifecycle explicitly. Cleanup is ordered correctly on all paths: release interface, close device, close context, cancel the ANT message goroutine context. Also convert panics to returned errors for graceful retry.
This commit is contained in:
parent
c75c2ea0e1
commit
9efdfc9b83
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user