diff --git a/cmd/sni/server.go b/cmd/sni/server.go index 8c7fec4..9c21538 100644 --- a/cmd/sni/server.go +++ b/cmd/sni/server.go @@ -69,18 +69,17 @@ func (s *deviceMemoryService) MappingDetect(gctx context.Context, request *sni.D return } - gerr = snes.UseDeviceMemory( + gerr = snes.WithDevice( gctx, uri, - // TODO: this capability is optional; validate it in the call itself when it is about to be used: - []sni.DeviceCapability{sni.DeviceCapability_ReadMemory}, - func(mctx context.Context, memory snes.DeviceMemory) (err error) { + nil, + func(mctx context.Context, device snes.Device) (err error) { var memoryMapping sni.MemoryMapping var confidence bool var outHeaderBytes []byte memoryMapping, confidence, outHeaderBytes, err = mapping.Detect( mctx, - memory, + device, request.FallbackMemoryMapping, request.RomHeader00FFB0, ) @@ -117,7 +116,7 @@ func (s *deviceMemoryService) SingleRead( return } - gerr = snes.UseDeviceMemory( + gerr = snes.WithDeviceMemory( rctx, uri, []sni.DeviceCapability{sni.DeviceCapability_ReadMemory}, @@ -185,7 +184,7 @@ func (s *deviceMemoryService) SingleWrite( return } - gerr = snes.UseDeviceMemory( + gerr = snes.WithDeviceMemory( rctx, uri, []sni.DeviceCapability{sni.DeviceCapability_WriteMemory}, @@ -254,7 +253,7 @@ func (s *deviceMemoryService) MultiRead( } var grsps []*sni.ReadMemoryResponse - gerr = snes.UseDeviceMemory( + gerr = snes.WithDeviceMemory( gctx, uri, []sni.DeviceCapability{sni.DeviceCapability_ReadMemory}, @@ -339,7 +338,7 @@ func (s *deviceMemoryService) MultiWrite( } var grsps []*sni.WriteMemoryResponse - gerr = snes.UseDeviceMemory( + gerr = snes.WithDeviceMemory( gctx, uri, []sni.DeviceCapability{sni.DeviceCapability_WriteMemory}, @@ -424,7 +423,7 @@ func (d *deviceControlService) ResetSystem(gctx context.Context, request *sni.Re return } - gerr = snes.UseDeviceControl( + gerr = snes.WithDeviceControl( gctx, uri, []sni.DeviceCapability{sni.DeviceCapability_ResetSystem}, @@ -456,7 +455,7 @@ func (d *deviceControlService) PauseUnpauseEmulation(gctx context.Context, reque } paused := false - gerr = snes.UseDeviceControl( + gerr = snes.WithDeviceControl( gctx, uri, []sni.DeviceCapability{sni.DeviceCapability_PauseUnpauseEmulation}, @@ -489,7 +488,7 @@ func (d *deviceControlService) PauseToggleEmulation(gctx context.Context, reques return } - gerr = snes.UseDeviceControl( + gerr = snes.WithDeviceControl( gctx, uri, []sni.DeviceCapability{sni.DeviceCapability_PauseToggleEmulation}, diff --git a/protos/sni/sni.proto b/protos/sni/sni.proto index 731bbc2..16c8bae 100644 --- a/protos/sni/sni.proto +++ b/protos/sni/sni.proto @@ -60,7 +60,7 @@ message DevicesResponse { // URI that describes exactly how to connect to the device, e.g.: // RetroArch: "ra://127.0.0.1:55355" // FX Pak Pro: "fxpakpro:///dev/cu.usbmodemDEMO000000001" (MacOS) - // "fxpakpro://COM4" (Windows) + // "fxpakpro://./COM4" (Windows) // uri is used as the unique identifier of the device for clients to refer to string uri = 1; // friendly display name of the device diff --git a/snes/basedriver.go b/snes/basedriver.go index 62c39bd..4b581a9 100644 --- a/snes/basedriver.go +++ b/snes/basedriver.go @@ -8,20 +8,21 @@ import ( ) type BaseDeviceDriver struct { + DeviceDriver + // track opened devices by URI devicesRw sync.RWMutex devicesMap map[string]Device } -func (b *BaseDeviceDriver) UseDevice( - ctx context.Context, - deviceKey string, - openDevice func() (Device, error), - use DeviceUser, -) (err error) { +func (b *BaseDeviceDriver) UseDevice(ctx context.Context, deviceKey string, requiredCapabilities []sni.DeviceCapability, openDevice func() (Device, error), use DeviceUser, ) (err error) { var device Device var ok bool + if ok, err = b.DeviceDriver.HasCapabilities(requiredCapabilities...); !ok { + return + } + b.devicesRw.RLock() device, ok = b.devicesMap[deviceKey] b.devicesRw.RUnlock() @@ -41,7 +42,7 @@ func (b *BaseDeviceDriver) UseDevice( b.devicesRw.Unlock() } - err = device.Use(ctx, use) + err = use(ctx, device) if device.IsClosed() { b.devicesRw.Lock() diff --git a/snes/control.go b/snes/control.go index 6a1b33f..70a3c1b 100644 --- a/snes/control.go +++ b/snes/control.go @@ -1,6 +1,9 @@ package snes -import "context" +import ( + "context" + "sni/protos/sni" +) type DeviceControlUser func(ctx context.Context, control DeviceControl) error @@ -10,3 +13,8 @@ type DeviceControl interface { PauseUnpause(ctx context.Context, pausedState bool) (bool, error) PauseToggle(ctx context.Context) error } + +type UseControl interface { + // UseControl provides exclusive access to only the control subsystem of the device to the user func + UseControl(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user DeviceControlUser) error +} diff --git a/snes/device.go b/snes/device.go index dfcf3b0..a3d1944 100644 --- a/snes/device.go +++ b/snes/device.go @@ -1,19 +1,9 @@ package snes -import ( - "context" -) - // Device acts as an exclusive-access gateway to the subsystems of the SNES device type Device interface { - IsClosed() bool - - // Use provides non-exclusive access to the device's subsystems to the user func - Use(ctx context.Context, user DeviceUser) error + UseControl + UseMemory - // UseMemory provides exclusive access to only the memory subsystem of the device to the user func - UseMemory(ctx context.Context, user DeviceMemoryUser) error - - // UseControl provides exclusive access to only the control subsystem of the device to the user func - UseControl(ctx context.Context, user DeviceControlUser) error + IsClosed() bool } diff --git a/snes/driver.go b/snes/driver.go index 2c028dd..400923c 100644 --- a/snes/driver.go +++ b/snes/driver.go @@ -26,12 +26,16 @@ type Driver interface { type DeviceUser func(context.Context, Device) error +type UseDevice interface { + // UseDevice grants non-exclusive access for DeviceUser to a Device uniquely identified by its uri + UseDevice(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user DeviceUser) error +} + // DeviceDriver extends Driver type DeviceDriver interface { - DeviceKey(uri *url.URL) string + UseDevice - // UseDevice grants non-exclusive access for DeviceUser to a Device uniquely identified by its uri - UseDevice(ctx context.Context, uri *url.URL, user DeviceUser) error + DeviceKey(uri *url.URL) string HasCapabilities(capabilities ...sni.DeviceCapability) (bool, error) } @@ -138,46 +142,36 @@ func DeviceDriverByUri(uri *url.URL) (drv DeviceDriver, err error) { return } -func UseDevice(ctx context.Context, uri *url.URL, user DeviceUser) (err error) { +func WithDevice(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user DeviceUser) (err error) { var drv DeviceDriver drv, err = DeviceDriverByUri(uri) if err != nil { return } - return drv.UseDevice(ctx, uri, user) + return drv.UseDevice(ctx, uri, requiredCapabilities, user) } -func UseDeviceMemory(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user DeviceMemoryUser) (err error) { +func WithDeviceMemory(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user DeviceMemoryUser) (err error) { var drv DeviceDriver drv, err = DeviceDriverByUri(uri) if err != nil { return } - var ok bool - if ok, err = drv.HasCapabilities(requiredCapabilities...); !ok { - return - } - - return drv.UseDevice(ctx, uri, func(ctx context.Context, device Device) error { - return device.UseMemory(ctx, user) + return drv.UseDevice(ctx, uri, requiredCapabilities, func(ctx context.Context, device Device) error { + return device.UseMemory(ctx, nil, user) }) } -func UseDeviceControl(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user DeviceControlUser) (err error) { +func WithDeviceControl(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user DeviceControlUser) (err error) { var drv DeviceDriver drv, err = DeviceDriverByUri(uri) if err != nil { return } - var ok bool - if ok, err = drv.HasCapabilities(requiredCapabilities...); !ok { - return - } - - return drv.UseDevice(ctx, uri, func(ctx context.Context, device Device) error { - return device.UseControl(ctx, user) + return drv.UseDevice(ctx, uri, requiredCapabilities, func(ctx context.Context, device Device) error { + return device.UseControl(ctx, nil, user) }) } diff --git a/snes/fxpakpro/device.go b/snes/fxpakpro/device.go index 8f905fb..be46320 100644 --- a/snes/fxpakpro/device.go +++ b/snes/fxpakpro/device.go @@ -25,17 +25,13 @@ func (d *Device) IsClosed() bool { return d.isClosed } -func (d *Device) Use(ctx context.Context, user snes.DeviceUser) error { +func (d *Device) UseMemory(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user snes.DeviceMemoryUser) error { if user == nil { return nil } - return user(ctx, d) -} - -func (d *Device) UseMemory(ctx context.Context, user snes.DeviceMemoryUser) error { - if user == nil { - return nil + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err } defer d.lock.Unlock() @@ -44,11 +40,15 @@ func (d *Device) UseMemory(ctx context.Context, user snes.DeviceMemoryUser) erro return user(ctx, d) } -func (d *Device) UseControl(ctx context.Context, user snes.DeviceControlUser) error { +func (d *Device) UseControl(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user snes.DeviceControlUser) error { if user == nil { return nil } + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err + } + defer d.lock.Unlock() d.lock.Lock() diff --git a/snes/fxpakpro/driver.go b/snes/fxpakpro/driver.go index 0f86332..d05eeaf 100644 --- a/snes/fxpakpro/driver.go +++ b/snes/fxpakpro/driver.go @@ -18,6 +18,8 @@ const ( driverName = "fxpakpro" ) +var driver *Driver + var ( ErrNoFXPakProFound = fmt.Errorf("%s: no device found among serial ports", driverName) @@ -186,13 +188,12 @@ func (d *Driver) openAsDevice(uri *url.URL) (device snes.Device, err error) { return } -func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, user snes.DeviceUser) error { +func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user snes.DeviceUser) error { return d.base.UseDevice( ctx, d.DeviceKey(uri), - func() (snes.Device, error) { - return d.openAsDevice(uri) - }, + requiredCapabilities, + func() (snes.Device, error) { return d.openAsDevice(uri) }, user, ) } @@ -202,5 +203,7 @@ func init() { log.Printf("disabling fxpakpro snes driver\n") return } - snes.Register(driverName, &Driver{}) + driver = &Driver{} + driver.base.DeviceDriver = driver + snes.Register(driverName, driver) } diff --git a/snes/luabridge/device.go b/snes/luabridge/device.go index 41d1133..c4d7e85 100644 --- a/snes/luabridge/device.go +++ b/snes/luabridge/device.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net" + "sni/protos/sni" "sni/snes" "strings" "sync" @@ -15,7 +16,6 @@ type Device struct { lock sync.Mutex c *net.TCPConn - driver *Driver deviceKey string isClosed bool @@ -24,10 +24,9 @@ type Device struct { version string } -func NewDevice(conn *net.TCPConn, key string, driver *Driver) *Device { +func NewDevice(conn *net.TCPConn, key string) *Device { d := &Device{ c: conn, - driver: driver, deviceKey: key, isClosed: false, clientName: "Unknown", @@ -170,23 +169,27 @@ func (d *Device) Close() (err error) { err = d.c.Close() // remove device from driver: - d.driver.devicesRw.Lock() - delete(d.driver.devicesMap, d.deviceKey) - d.driver.devicesRw.Unlock() + driver.devicesRw.Lock() + delete(driver.devicesMap, d.deviceKey) + driver.devicesRw.Unlock() return } func (d *Device) IsClosed() bool { return d.isClosed } -func (d *Device) Use(ctx context.Context, user snes.DeviceUser) error { - return user(ctx, d) -} +func (d *Device) UseMemory(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user snes.DeviceMemoryUser) error { + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err + } -func (d *Device) UseMemory(ctx context.Context, user snes.DeviceMemoryUser) error { return user(ctx, d) } -func (d *Device) UseControl(ctx context.Context, user snes.DeviceControlUser) error { +func (d *Device) UseControl(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user snes.DeviceControlUser) error { + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err + } + return user(ctx, d) } diff --git a/snes/luabridge/driver.go b/snes/luabridge/driver.go index b110327..66a83ab 100644 --- a/snes/luabridge/driver.go +++ b/snes/luabridge/driver.go @@ -19,6 +19,8 @@ type Driver struct { devicesMap map[string]*Device } +var driver *Driver + func (d *Driver) Kind() string { return "luabridge" } @@ -52,7 +54,11 @@ func (d *Driver) DeviceKey(uri *url.URL) string { return uri.Host } -func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, user snes.DeviceUser) (err error) { +func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user snes.DeviceUser) (err error) { + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err + } + deviceKey := d.DeviceKey(uri) d.devicesRw.RLock() @@ -63,7 +69,7 @@ func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, user snes.DeviceUs return fmt.Errorf("no device found") } - err = device.Use(ctx, user) + err = user(ctx, device) return } @@ -103,7 +109,7 @@ func (d *Driver) runServer(listener *net.TCPListener) { // create the Device to handle this connection: deviceKey := conn.RemoteAddr().String() - device := NewDevice(conn, deviceKey, d) + device := NewDevice(conn, deviceKey) // store the Device for reference: d.devicesRw.Lock() @@ -117,7 +123,7 @@ func (d *Driver) runServer(listener *net.TCPListener) { func init() { // attempt to start the luabridge server: - driver := &Driver{} + driver = &Driver{} err := driver.StartServer() if err != nil { log.Printf("luabridge: could not start server: %v\n", err) diff --git a/snes/mapping/detect.go b/snes/mapping/detect.go index 5f3a1b9..2f948dc 100644 --- a/snes/mapping/detect.go +++ b/snes/mapping/detect.go @@ -14,7 +14,7 @@ import ( func Detect( ctx context.Context, - memory snes.DeviceMemory, + useMemory snes.UseMemory, fallbackMapping *sni.MemoryMapping, inHeaderBytes []byte, ) (mapping sni.MemoryMapping, confidence bool, outHeaderBytes []byte, err error) { @@ -37,36 +37,44 @@ func Detect( var deviceAddress = snes.AddressTuple{} headerAddresses := []uint32{0x00FFB0, 0x40FFB0, 0x80FFB0, 0xC0FFB0} errors := make([]string, len(headerAddresses)) - for j, headerAddress := range headerAddresses { - // read the ROM header: - var responses []snes.MemoryReadResponse - tuple := snes.AddressTuple{ - Address: headerAddress, - AddressSpace: sni.AddressSpace_SnesABus, - MemoryMapping: guessMapping, - } - readRequest := snes.MemoryReadRequest{ - RequestAddress: tuple, - Size: 0x50, - } - log.Printf( - "detect: read {address:%s,size:$%x}\n", - &tuple, - readRequest.Size, - ) - responses, err = memory.MultiReadMemory(ctx, readRequest) - if err != nil { - err = fmt.Errorf("%w: %s", err, &tuple) - errors[j] = err.Error() - log.Printf("detect: %v\n", errors[j]) - continue - } - outHeaderBytes = responses[0].Data - deviceAddress = responses[0].DeviceAddress - deviceAddress.MemoryMapping = tuple.MemoryMapping - break - } + _ = useMemory.UseMemory( + ctx, + []sni.DeviceCapability{sni.DeviceCapability_ReadMemory}, + func(mctx context.Context, memory snes.DeviceMemory) error { + for j, headerAddress := range headerAddresses { + // read the ROM header: + var responses []snes.MemoryReadResponse + tuple := snes.AddressTuple{ + Address: headerAddress, + AddressSpace: sni.AddressSpace_SnesABus, + MemoryMapping: guessMapping, + } + readRequest := snes.MemoryReadRequest{ + RequestAddress: tuple, + Size: 0x50, + } + log.Printf( + "detect: read {address:%s,size:$%x}\n", + &tuple, + readRequest.Size, + ) + responses, err = memory.MultiReadMemory(mctx, readRequest) + if err != nil { + err = fmt.Errorf("%w: %s", err, &tuple) + errors[j] = err.Error() + log.Printf("detect: %v\n", errors[j]) + continue + } + + outHeaderBytes = responses[0].Data + deviceAddress = responses[0].DeviceAddress + deviceAddress.MemoryMapping = tuple.MemoryMapping + break + } + return nil + }, + ) if outHeaderBytes == nil { err = snes.WithCode(codes.FailedPrecondition, fmt.Errorf( diff --git a/snes/memory.go b/snes/memory.go index 3643962..6eeaa70 100644 --- a/snes/memory.go +++ b/snes/memory.go @@ -2,6 +2,7 @@ package snes import ( "context" + "sni/protos/sni" ) type MemoryReadRequest struct { @@ -36,3 +37,8 @@ type DeviceMemory interface { MultiReadMemory(ctx context.Context, reads ...MemoryReadRequest) ([]MemoryReadResponse, error) MultiWriteMemory(ctx context.Context, writes ...MemoryWriteRequest) ([]MemoryWriteResponse, error) } + +type UseMemory interface { + // UseMemory provides exclusive access to only the memory subsystem of the device to the user func + UseMemory(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user DeviceMemoryUser) error +} diff --git a/snes/mock/device.go b/snes/mock/device.go index 6e13c28..a7befcb 100644 --- a/snes/mock/device.go +++ b/snes/mock/device.go @@ -2,6 +2,7 @@ package mock import ( "context" + "sni/protos/sni" "sni/snes" "sync" "time" @@ -32,34 +33,34 @@ func (d *Device) IsClosed() bool { return false } -func (d *Device) Use(context context.Context, user snes.DeviceUser) error { +func (d *Device) UseMemory(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user snes.DeviceMemoryUser) error { if user == nil { return nil } - return user(context, d) -} - -func (d *Device) UseMemory(context context.Context, user snes.DeviceMemoryUser) error { - if user == nil { - return nil + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err } defer d.lock.Unlock() d.lock.Lock() - return user(context, d) + return user(ctx, d) } -func (d *Device) UseControl(context context.Context, user snes.DeviceControlUser) error { +func (d *Device) UseControl(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user snes.DeviceControlUser) error { if user == nil { return nil } + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err + } + defer d.lock.Unlock() d.lock.Lock() - return user(context, d) + return user(ctx, d) } func (d *Device) MultiReadMemory(context context.Context, reads ...snes.MemoryReadRequest) (mrsps []snes.MemoryReadResponse, err error) { diff --git a/snes/mock/driver.go b/snes/mock/driver.go index de9d85c..b7b7172 100644 --- a/snes/mock/driver.go +++ b/snes/mock/driver.go @@ -16,6 +16,8 @@ type Driver struct { base snes.BaseDeviceDriver } +var driver *Driver + func (d *Driver) DisplayOrder() int { return 1000 } @@ -64,10 +66,11 @@ func (d *Driver) openDevice(uri *url.URL) (snes.Device, error) { return mock, nil } -func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, user snes.DeviceUser) error { +func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user snes.DeviceUser) error { return d.base.UseDevice( ctx, d.DeviceKey(uri), + requiredCapabilities, func() (snes.Device, error) { return d.openDevice(uri) }, user, ) @@ -78,6 +81,7 @@ func (d *Driver) DeviceKey(uri *url.URL) string { return uri.Opaque } func init() { if util.IsTruthy(env.GetOrDefault("SNI_MOCK_ENABLE", "0")) { log.Printf("enabling mock snes driver\n") - snes.Register(driverName, &Driver{}) + driver = &Driver{} + snes.Register(driverName, driver) } } diff --git a/snes/retroarch/device.go b/snes/retroarch/device.go index 5accdee..6ebd51e 100644 --- a/snes/retroarch/device.go +++ b/snes/retroarch/device.go @@ -2,6 +2,7 @@ package retroarch import ( "context" + "sni/protos/sni" "sni/snes" "sync" ) @@ -20,17 +21,13 @@ func (d *Device) IsClosed() bool { return d.c.IsClosed() } -func (d *Device) Use(ctx context.Context, user snes.DeviceUser) error { +func (d *Device) UseMemory(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user snes.DeviceMemoryUser) (err error) { if user == nil { return nil } - return user(ctx, d) -} - -func (d *Device) UseMemory(ctx context.Context, user snes.DeviceMemoryUser) error { - if user == nil { - return nil + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err } defer d.lock.Unlock() @@ -39,11 +36,15 @@ func (d *Device) UseMemory(ctx context.Context, user snes.DeviceMemoryUser) erro return user(ctx, d.c) } -func (d *Device) UseControl(ctx context.Context, user snes.DeviceControlUser) error { +func (d *Device) UseControl(ctx context.Context, requiredCapabilities []sni.DeviceCapability, user snes.DeviceControlUser) error { if user == nil { return nil } + if ok, err := driver.HasCapabilities(requiredCapabilities...); !ok { + return err + } + defer d.lock.Unlock() d.lock.Lock() diff --git a/snes/retroarch/driver.go b/snes/retroarch/driver.go index a85effa..ce8a86c 100644 --- a/snes/retroarch/driver.go +++ b/snes/retroarch/driver.go @@ -16,6 +16,7 @@ import ( const driverName = "ra" var logDetector = false +var driver *Driver type Driver struct { base snes.BaseDeviceDriver @@ -27,6 +28,7 @@ func NewDriver(addresses []*net.UDPAddr) *Driver { d := &Driver{ detectors: make([]*RAClient, len(addresses)), } + d.base.DeviceDriver = d for i, addr := range addresses { c := NewRAClient(addr, fmt.Sprintf("retroarch[%d]", i)) @@ -139,10 +141,11 @@ func (d *Driver) DeviceKey(uri *url.URL) string { return uri.Host } -func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, user snes.DeviceUser) error { +func (d *Driver) UseDevice(ctx context.Context, uri *url.URL, requiredCapabilities []sni.DeviceCapability, user snes.DeviceUser) error { return d.base.UseDevice( ctx, d.DeviceKey(uri), + requiredCapabilities, func() (snes.Device, error) { return d.openDevice(uri) }, user, ) @@ -194,5 +197,6 @@ func init() { } // register the driver: - snes.Register(driverName, NewDriver(addresses)) + driver = NewDriver(addresses) + snes.Register(driverName, driver) }