diff --git a/go.mod b/go.mod
index f2ba4d7..2a8e986 100644
--- a/go.mod
+++ b/go.mod
@@ -4,10 +4,12 @@ go 1.19
require (
github.com/arduino/go-properties-orderedmap v1.7.1
+ github.com/arduino/go-win32-utils v1.0.0
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.1.1
)
require (
github.com/arduino/go-paths-helper v1.8.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
+ golang.org/x/sys v0.6.0 // indirect
)
diff --git a/go.sum b/go.sum
index 3b199ee..eb924b0 100644
--- a/go.sum
+++ b/go.sum
@@ -52,6 +52,8 @@ github.com/arduino/go-properties-orderedmap v1.7.1 h1:HQ9Pn/mk3+XyfrE39EEvaZwJkr
github.com/arduino/go-properties-orderedmap v1.7.1/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk=
github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b/go.mod h1:uwGy5PpN4lqW97FiLnbcx+xx8jly5YuPMJWfVwwjJiQ=
github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b/go.mod h1:iIPnclBMYm1g32Q5kXoqng4jLhMStReIP7ZxaoUC2y8=
+github.com/arduino/go-win32-utils v1.0.0 h1:/cXB86sOJxOsCHP7sQmXGLkdValwJt56mIwOHYxgQjQ=
+github.com/arduino/go-win32-utils v1.0.0/go.mod h1:0jqM7doGEAs6DaJCxxhLBUDS5OawrqF48HqXkcEie/Q=
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.1.1 h1:MPQZ2YImq5qBiOPwTFGOrl6E99XGSRHc+UzHA6hsjvc=
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.1.1/go.mod h1:2lA930B1Xu/otYT1kbx3l1n5vFJuuyPNkQaqOoQHmPE=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -472,6 +474,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/main.c b/main.c
index a807611..e32b848 100644
--- a/main.c
+++ b/main.c
@@ -79,3 +79,44 @@ void dfuProbeDevices() {
clearDfuRoot();
probe_devices(ctx);
}
+
+volatile int has_event = 0;
+
+int libusbHandleEvents() {
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ libusb_handle_events_timeout_completed(ctx, &tv, NULL);
+ int res = has_event;
+ has_event = 0;
+ return res;
+}
+
+int callback(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data) {
+ has_event = 1;
+ return 0;
+}
+
+libusb_hotplug_callback_handle callbackHandle;
+
+const char *libusbHotplugRegisterCallback() {
+ int err = libusb_hotplug_register_callback(
+ ctx,
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+ LIBUSB_HOTPLUG_NO_FLAGS,
+ LIBUSB_HOTPLUG_MATCH_ANY, // Vendor ID
+ LIBUSB_HOTPLUG_MATCH_ANY, // Product ID
+ LIBUSB_HOTPLUG_MATCH_ANY, // Device Class
+ callback, // Actual callback function
+ NULL, // User data
+ &callbackHandle
+ );
+ if (err != 0) {
+ return libusb_strerror(err);
+ }
+ return NULL;
+}
+
+void libusbHotplugDeregisterCallback() {
+ libusb_hotplug_deregister_callback(ctx, callbackHandle);
+}
diff --git a/main.go b/main.go
index 7ceb0bf..af83659 100644
--- a/main.go
+++ b/main.go
@@ -18,8 +18,8 @@
package main
/*
-#cgo CPPFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/local/include/libusb-1.0
-#cgo CFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/local/include/libusb-1.0
+#cgo CPPFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/local/include/libusb-1.0 -Wall
+#cgo CFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/local/include/libusb-1.0 -Wall
#cgo darwin LDFLAGS: -L/usr/local/lib -lusb-1.0 -framework IOKit -framework CoreFoundation -framework Security
#cgo !darwin LDFLAGS: -L/usr/local/lib -lusb-1.0
@@ -32,13 +32,16 @@ char *get_path(libusb_device *dev);
const char *libusbOpen();
void libusbClose();
void dfuProbeDevices();
+const char *libusbHotplugRegisterCallback();
+void libusbHotplugDeregisterCallback();
+int libusbHandleEvents();
*/
import "C"
import (
+ "errors"
"fmt"
"os"
- "time"
"github.com/arduino/go-properties-orderedmap"
discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
@@ -55,7 +58,7 @@ func main() {
// DFUDiscovery is the implementation of the DFU pluggable-discovery
type DFUDiscovery struct {
- closeChan chan<- struct{}
+ close func()
portsCache map[string]*discovery.Port
}
@@ -71,9 +74,9 @@ func (d *DFUDiscovery) Quit() {
// Stop is the handler for the pluggable-discovery STOP command
func (d *DFUDiscovery) Stop() error {
- if d.closeChan != nil {
- close(d.closeChan)
- d.closeChan = nil
+ if d.close != nil {
+ d.close()
+ d.close = nil
}
C.libusbClose()
return nil
@@ -81,23 +84,36 @@ func (d *DFUDiscovery) Stop() error {
// StartSync is the handler for the pluggable-discovery START_SYNC command
func (d *DFUDiscovery) StartSync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error {
+ d.portsCache = map[string]*discovery.Port{}
if cErr := C.libusbOpen(); cErr != nil {
return fmt.Errorf("can't open libusb: %s", C.GoString(cErr))
}
+ return d.sync(eventCB, errorCB)
+}
+
+func (d *DFUDiscovery) libusbSync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error {
+ if err := C.libusbHotplugRegisterCallback(); err != nil {
+ return errors.New(C.GoString(err))
+ }
+
closeChan := make(chan struct{})
go func() {
- d.portsCache = map[string]*discovery.Port{}
+ d.sendUpdates(eventCB, errorCB)
for {
- d.sendUpdates(eventCB, errorCB)
+ if C.libusbHandleEvents() != 0 {
+ d.sendUpdates(eventCB, errorCB)
+ }
select {
- case <-time.After(5 * time.Second):
case <-closeChan:
- d.portsCache = nil
+ C.libusbHotplugDeregisterCallback()
return
+ default:
}
}
}()
- d.closeChan = closeChan
+ d.close = func() {
+ close(closeChan)
+ }
return nil
}
diff --git a/sync_unix.go b/sync_unix.go
new file mode 100644
index 0000000..bc911e7
--- /dev/null
+++ b/sync_unix.go
@@ -0,0 +1,28 @@
+// This file is part of dfu-discovery.
+//
+// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//go:build !windows
+
+package main
+
+import (
+ discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
+)
+
+func (d *DFUDiscovery) sync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error {
+ return d.libusbSync(eventCB, errorCB)
+}
diff --git a/sync_windows.go b/sync_windows.go
new file mode 100644
index 0000000..bb21bf5
--- /dev/null
+++ b/sync_windows.go
@@ -0,0 +1,71 @@
+// This file is part of dfu-discovery.
+//
+// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/arduino/go-win32-utils/devicenotification"
+ discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
+)
+
+func (d *DFUDiscovery) sync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error {
+ ctx, cancel := context.WithCancel(context.Background())
+ d.close = cancel
+
+ deviceEventChan := make(chan bool, 1)
+ go func() {
+ err := devicenotification.Start(ctx, func() {
+ select {
+ case deviceEventChan <- true:
+ default:
+ }
+ }, errorCB)
+ if err != nil {
+ errorCB(err.Error())
+ }
+ }()
+
+ go func() {
+ d.sendUpdates(eventCB, errorCB)
+
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-deviceEventChan:
+ }
+
+ again:
+ d.sendUpdates(eventCB, errorCB)
+
+ // Trigger another update after 500ms because Windows might signal a
+ // new port much before it becomes actually available.
+ select {
+ case <-ctx.Done():
+ return
+ case <-deviceEventChan:
+ goto again
+ case <-time.After(time.Millisecond * 500):
+ }
+ d.sendUpdates(eventCB, errorCB)
+ }
+ }()
+ return nil
+}