Skip to content

Commit db8a62e

Browse files
authored
Merge pull request #21 from arduino/hotplug_unix
Added hotplug support from libusb
2 parents d02268f + fc2573a commit db8a62e

File tree

6 files changed

+174
-12
lines changed

6 files changed

+174
-12
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ go 1.19
44

55
require (
66
github.com/arduino/go-properties-orderedmap v1.7.1
7+
github.com/arduino/go-win32-utils v1.0.0
78
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.1.1
89
)
910

1011
require (
1112
github.com/arduino/go-paths-helper v1.8.0 // indirect
1213
github.com/pkg/errors v0.9.1 // indirect
14+
golang.org/x/sys v0.6.0 // indirect
1315
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ github.com/arduino/go-properties-orderedmap v1.7.1 h1:HQ9Pn/mk3+XyfrE39EEvaZwJkr
5252
github.com/arduino/go-properties-orderedmap v1.7.1/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk=
5353
github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b/go.mod h1:uwGy5PpN4lqW97FiLnbcx+xx8jly5YuPMJWfVwwjJiQ=
5454
github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b/go.mod h1:iIPnclBMYm1g32Q5kXoqng4jLhMStReIP7ZxaoUC2y8=
55+
github.com/arduino/go-win32-utils v1.0.0 h1:/cXB86sOJxOsCHP7sQmXGLkdValwJt56mIwOHYxgQjQ=
56+
github.com/arduino/go-win32-utils v1.0.0/go.mod h1:0jqM7doGEAs6DaJCxxhLBUDS5OawrqF48HqXkcEie/Q=
5557
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.1.1 h1:MPQZ2YImq5qBiOPwTFGOrl6E99XGSRHc+UzHA6hsjvc=
5658
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.1.1/go.mod h1:2lA930B1Xu/otYT1kbx3l1n5vFJuuyPNkQaqOoQHmPE=
5759
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
472474
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
473475
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
474476
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
477+
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
478+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
475479
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
476480
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
477481
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

main.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,44 @@ void dfuProbeDevices() {
7979
clearDfuRoot();
8080
probe_devices(ctx);
8181
}
82+
83+
volatile int has_event = 0;
84+
85+
int libusbHandleEvents() {
86+
struct timeval tv;
87+
tv.tv_sec = 0;
88+
tv.tv_usec = 500000;
89+
libusb_handle_events_timeout_completed(ctx, &tv, NULL);
90+
int res = has_event;
91+
has_event = 0;
92+
return res;
93+
}
94+
95+
int callback(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data) {
96+
has_event = 1;
97+
return 0;
98+
}
99+
100+
libusb_hotplug_callback_handle callbackHandle;
101+
102+
const char *libusbHotplugRegisterCallback() {
103+
int err = libusb_hotplug_register_callback(
104+
ctx,
105+
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
106+
LIBUSB_HOTPLUG_NO_FLAGS,
107+
LIBUSB_HOTPLUG_MATCH_ANY, // Vendor ID
108+
LIBUSB_HOTPLUG_MATCH_ANY, // Product ID
109+
LIBUSB_HOTPLUG_MATCH_ANY, // Device Class
110+
callback, // Actual callback function
111+
NULL, // User data
112+
&callbackHandle
113+
);
114+
if (err != 0) {
115+
return libusb_strerror(err);
116+
}
117+
return NULL;
118+
}
119+
120+
void libusbHotplugDeregisterCallback() {
121+
libusb_hotplug_deregister_callback(ctx, callbackHandle);
122+
}

main.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
package main
1919

2020
/*
21-
#cgo CPPFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/local/include/libusb-1.0
22-
#cgo CFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/local/include/libusb-1.0
21+
#cgo CPPFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/local/include/libusb-1.0 -Wall
22+
#cgo CFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/local/include/libusb-1.0 -Wall
2323
#cgo darwin LDFLAGS: -L/usr/local/lib -lusb-1.0 -framework IOKit -framework CoreFoundation -framework Security
2424
#cgo !darwin LDFLAGS: -L/usr/local/lib -lusb-1.0
2525
@@ -32,13 +32,16 @@ char *get_path(libusb_device *dev);
3232
const char *libusbOpen();
3333
void libusbClose();
3434
void dfuProbeDevices();
35+
const char *libusbHotplugRegisterCallback();
36+
void libusbHotplugDeregisterCallback();
37+
int libusbHandleEvents();
3538
*/
3639
import "C"
3740

3841
import (
42+
"errors"
3943
"fmt"
4044
"os"
41-
"time"
4245

4346
"github.com/arduino/go-properties-orderedmap"
4447
discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
@@ -55,7 +58,7 @@ func main() {
5558

5659
// DFUDiscovery is the implementation of the DFU pluggable-discovery
5760
type DFUDiscovery struct {
58-
closeChan chan<- struct{}
61+
close func()
5962
portsCache map[string]*discovery.Port
6063
}
6164

@@ -71,33 +74,46 @@ func (d *DFUDiscovery) Quit() {
7174

7275
// Stop is the handler for the pluggable-discovery STOP command
7376
func (d *DFUDiscovery) Stop() error {
74-
if d.closeChan != nil {
75-
close(d.closeChan)
76-
d.closeChan = nil
77+
if d.close != nil {
78+
d.close()
79+
d.close = nil
7780
}
7881
C.libusbClose()
7982
return nil
8083
}
8184

8285
// StartSync is the handler for the pluggable-discovery START_SYNC command
8386
func (d *DFUDiscovery) StartSync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error {
87+
d.portsCache = map[string]*discovery.Port{}
8488
if cErr := C.libusbOpen(); cErr != nil {
8589
return fmt.Errorf("can't open libusb: %s", C.GoString(cErr))
8690
}
91+
return d.sync(eventCB, errorCB)
92+
}
93+
94+
func (d *DFUDiscovery) libusbSync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error {
95+
if err := C.libusbHotplugRegisterCallback(); err != nil {
96+
return errors.New(C.GoString(err))
97+
}
98+
8799
closeChan := make(chan struct{})
88100
go func() {
89-
d.portsCache = map[string]*discovery.Port{}
101+
d.sendUpdates(eventCB, errorCB)
90102
for {
91-
d.sendUpdates(eventCB, errorCB)
103+
if C.libusbHandleEvents() != 0 {
104+
d.sendUpdates(eventCB, errorCB)
105+
}
92106
select {
93-
case <-time.After(5 * time.Second):
94107
case <-closeChan:
95-
d.portsCache = nil
108+
C.libusbHotplugDeregisterCallback()
96109
return
110+
default:
97111
}
98112
}
99113
}()
100-
d.closeChan = closeChan
114+
d.close = func() {
115+
close(closeChan)
116+
}
101117
return nil
102118
}
103119

sync_unix.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This file is part of dfu-discovery.
2+
//
3+
// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
//go:build !windows
19+
20+
package main
21+
22+
import (
23+
discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
24+
)
25+
26+
func (d *DFUDiscovery) sync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error {
27+
return d.libusbSync(eventCB, errorCB)
28+
}

sync_windows.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// This file is part of dfu-discovery.
2+
//
3+
// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package main
19+
20+
import (
21+
"context"
22+
"time"
23+
24+
"github.com/arduino/go-win32-utils/devicenotification"
25+
discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
26+
)
27+
28+
func (d *DFUDiscovery) sync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error {
29+
ctx, cancel := context.WithCancel(context.Background())
30+
d.close = cancel
31+
32+
deviceEventChan := make(chan bool, 1)
33+
go func() {
34+
err := devicenotification.Start(ctx, func() {
35+
select {
36+
case deviceEventChan <- true:
37+
default:
38+
}
39+
}, errorCB)
40+
if err != nil {
41+
errorCB(err.Error())
42+
}
43+
}()
44+
45+
go func() {
46+
d.sendUpdates(eventCB, errorCB)
47+
48+
for {
49+
select {
50+
case <-ctx.Done():
51+
return
52+
case <-deviceEventChan:
53+
}
54+
55+
again:
56+
d.sendUpdates(eventCB, errorCB)
57+
58+
// Trigger another update after 500ms because Windows might signal a
59+
// new port much before it becomes actually available.
60+
select {
61+
case <-ctx.Done():
62+
return
63+
case <-deviceEventChan:
64+
goto again
65+
case <-time.After(time.Millisecond * 500):
66+
}
67+
d.sendUpdates(eventCB, errorCB)
68+
}
69+
}()
70+
return nil
71+
}

0 commit comments

Comments
 (0)