Skip to content

Added hotplug support from libusb #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
41 changes: 41 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
40 changes: 28 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand All @@ -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
}

Expand All @@ -71,33 +74,46 @@ 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
}

// 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
}

Expand Down
28 changes: 28 additions & 0 deletions sync_unix.go
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

//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)
}
71 changes: 71 additions & 0 deletions sync_windows.go
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

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
}