package genericftdi import ( "context" "dmxconnect/hardware" "fmt" goRuntime "runtime" "sync" "time" "unsafe" "github.com/rs/zerolog/log" ) /* #include #cgo LDFLAGS: -L${SRCDIR}/../../build/bin -ldetectFTDI #include "cpp/include/detectFTDIBridge.h" */ import "C" // Provider manages all the FTDI endpoints type Provider struct { wg sync.WaitGroup mu sync.Mutex detected map[string]*Endpoint // Detected endpoints scanEvery time.Duration // Scans endpoints periodically onArrival func(context.Context, hardware.Endpoint) // When a endpoint arrives onRemoval func(context.Context, hardware.Endpoint) // When a endpoint goes away } // NewProvider creates a new FTDI provider func NewProvider(scanEvery time.Duration) *Provider { log.Trace().Str("file", "FTDIProvider").Msg("FTDI provider created") return &Provider{ scanEvery: scanEvery, detected: make(map[string]*Endpoint), } } // OnArrival is the callback function when a new endpoint arrives func (f *Provider) OnArrival(cb func(context.Context, hardware.Endpoint)) { f.onArrival = cb } // OnRemoval i the callback when a endpoint goes away func (f *Provider) OnRemoval(cb func(context.Context, hardware.Endpoint)) { f.onRemoval = cb } // Initialize initializes the FTDI provider func (f *Provider) Initialize() error { // Check platform if goRuntime.GOOS != "windows" { log.Error().Str("file", "FTDIProvider").Str("platform", goRuntime.GOOS).Msg("FTDI provider not compatible with your platform") return fmt.Errorf("the FTDI provider is not compatible with your platform yet (%s)", goRuntime.GOOS) } log.Trace().Str("file", "FTDIProvider").Msg("FTDI provider initialized") return nil } // Create creates a new endpoint, based on the endpoint information (manually created) func (f *Provider) Create(ctx context.Context, endpointInfo hardware.EndpointInfo) (hardware.EndpointInfo, error) { return hardware.EndpointInfo{}, nil } // Remove removes an existing endpoint (manually created) func (f *Provider) Remove(ctx context.Context, endpoint hardware.Endpoint) error { return nil } // Start starts the provider and search for endpoints func (f *Provider) Start(ctx context.Context) error { f.wg.Add(1) go func() { ticker := time.NewTicker(f.scanEvery) defer ticker.Stop() defer f.wg.Done() for { select { case <-ctx.Done(): return case <-ticker.C: // Scan the endpoints err := f.scanEndpoints(ctx) if err != nil { log.Err(err).Str("file", "FTDIProvider").Msg("unable to scan FTDI endpoints") } } } }() return nil } // GetName returns the name of the driver func (f *Provider) GetName() string { return "FTDI" } // scanEndpoints scans the FTDI endpoints func (f *Provider) scanEndpoints(ctx context.Context) error { log.Trace().Str("file", "FTDIProvider").Msg("FTDI scan triggered") count := int(C.get_endpoints_number()) log.Info().Int("number", count).Msg("number of FTDI devices connected") // Allocating C array size := C.size_t(count) * C.size_t(unsafe.Sizeof(C.FTDIEndpointC{})) devicesPtr := C.malloc(size) defer C.free(devicesPtr) devices := (*[1 << 20]C.FTDIEndpointC)(devicesPtr)[:count:count] C.get_ftdi_devices((*C.FTDIEndpointC)(devicesPtr), C.int(count)) currentMap := make(map[string]hardware.EndpointInfo) for i := 0; i < count; i++ { d := devices[i] sn := C.GoString(d.serialNumber) desc := C.GoString(d.description) // isOpen := d.isOpen != 0 currentMap[sn] = hardware.EndpointInfo{ SerialNumber: sn, Name: desc, // IsOpen: isOpen, ProtocolName: "FTDI", } // Free C memory C.free_ftdi_device(&d) } log.Info().Any("endpoints", currentMap).Msg("available FTDI endpoints") // Detect arrivals for sn, endpointData := range currentMap { // If the scanned endpoint isn't in the detected list, create it if _, known := f.detected[sn]; !known { endpoint := NewEndpoint(endpointData) if f.onArrival != nil { f.onArrival(ctx, endpoint) } } } // Detect removals for detectedSN, detectedEndpoint := range f.detected { if _, still := currentMap[detectedSN]; !still { // Delete it from the detected list delete(f.detected, detectedSN) // Execute the removal callback if f.onRemoval != nil { f.onRemoval(ctx, detectedEndpoint) } } } return nil } // WaitStop stops the provider func (f *Provider) WaitStop() error { log.Trace().Str("file", "FTDIProvider").Msg("stopping the FTDI provider...") // Wait for goroutines to stop f.wg.Wait() log.Trace().Str("file", "FTDIProvider").Msg("FTDI provider stopped") return nil }