Skip to content

Tentative implementation using golang and CGO bindings #13

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 12 commits into from
Feb 28, 2023
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -2,3 +2,4 @@ compile.sh
dfu-util-*/*
dfu-discovery
*.o
dfu-util_*.c
14 changes: 6 additions & 8 deletions compile.sh
Original file line number Diff line number Diff line change
@@ -6,12 +6,10 @@ then
else
echo "Downloading $DFU_UTIL..."
curl https://dfu-util.sourceforge.net/releases/$DFU_UTIL.tar.gz -s | tar xz
fi

DEFINES="-DHAVE_UNISTD_H -DHAVE_NANOSLEEP"
INCLUDES="-I. -Idfu-util-0.11/src -I/usr/include/libusb-1.0"
gcc -c dfu-util-0.11/src/dfuse_mem.c $INCLUDES $DEFINES
gcc -c dfu-util-0.11/src/dfu_util.c $INCLUDES $DEFINES
gcc -c dfu-util-0.11/src/quirks.c $INCLUDES $DEFINES
g++ -c main.cpp $INCLUDES $DEFINES
g++ dfu_util.o quirks.o dfuse_mem.o main.o -lusb-1.0 -o dfu-discovery
cp dfu-util-0.11/src/dfuse_mem.c dfu-util_dfuse_mem.c
cp dfu-util-0.11/src/dfu_util.c dfu-util_dfu_util.c
cp dfu-util-0.11/src/quirks.c dfu-util_quirks.c
# for some reason quirks.c has the exec flag set
chmod -x dfu-util_quirks.c
fi
20 changes: 20 additions & 0 deletions err.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

/*
// The original macros are defined as follows:
# define warnx(...) do {\
fprintf(stderr, __VA_ARGS__);\
fprintf(stderr, "\n");\ } while (0)
# define warn(...) do {\
fprintf(stderr, "%s: ", strerror(errno));\
warnx(__VA_ARGS__); } while (0)
*/
// No-OP: do not report any warning
# define warnx(...) do { } while(0)
# define warn(...) do { } while(0)

# define errx(eval, ...) do {\
warnx(__VA_ARGS__);\
exit(eval); } while (0)
# define err(eval, ...) do {\
warn(__VA_ARGS__);\
exit(eval); } while (0)
13 changes: 13 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/arduino/dfu-discovery

go 1.19

require (
github.com/arduino/go-properties-orderedmap v1.7.1
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
)
680 changes: 680 additions & 0 deletions go.sum

Large diffs are not rendered by default.

24,640 changes: 0 additions & 24,640 deletions json.hpp

This file was deleted.

47 changes: 47 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <stdlib.h>
#include <dfu.h>
#include <dfu_util.h>

// Those are cmd-line arguments for dfu-util, we provide
// the default value for them
int match_devnum = -1;
int verbose = 0;
char *match_path = NULL;
int match_vendor = -1;
int match_product = -1;
int match_vendor_dfu = -1;
int match_product_dfu = -1;
int match_config_index = -1;
int match_iface_index = -1;
int match_iface_alt_index = -1;
const char *match_iface_alt_name = NULL;
const char *match_serial = NULL;
const char *match_serial_dfu = NULL;

// This provides dfu_malloc without the need to compile
// dfu_file.c and its dependencies.
void *dfu_malloc(size_t size) { return malloc(size); }

// This is the global DFU tree instance used in dfu_util.
struct dfu_if *dfu_root = NULL;

libusb_context *ctx;

const char *libusbOpen() {
if (ctx != NULL) return NULL;
int err = libusb_init(&ctx);
if (err != 0) {
return libusb_strerror(err);
}
return NULL;
}

void libusbClose() {
//libusb_exit(ctx);
//ctx = NULL;
}

void dfuProbeDevices() {
dfu_root = NULL;
probe_devices(ctx);
}
265 changes: 0 additions & 265 deletions main.cpp

This file was deleted.

166 changes: 166 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package main

/*
#cgo CPPFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/include/libusb-1.0
#cgo CFLAGS: -DHAVE_UNISTD_H -DHAVE_NANOSLEEP -DHAVE_ERR -I. -Idfu-util-0.11/src -I/usr/include/libusb-1.0
#cgo LDFLAGS: -lusb-1.0
#include <dfu.h>
#include <dfu_util.h>
// This is missing from dfu_util.h
char *get_path(libusb_device *dev);
// Defined in main.c
const char *libusbOpen();
void libusbClose();
void dfuProbeDevices();
*/
import "C"

import (
"fmt"
"os"
"time"

"github.com/arduino/go-properties-orderedmap"
discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
)

func main() {
dfuDisc := &DFUDiscovery{
portsCache: map[string]*discovery.Port{},
}
disc := discovery.NewServer(dfuDisc)
if err := disc.Run(os.Stdin, os.Stdout); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
os.Exit(1)
}
}

// DFUDiscovery is the implementation of the DFU pluggable-discovery
type DFUDiscovery struct {
closeChan chan<- struct{}
portsCache map[string]*discovery.Port
}

// Hello is the handler for the pluggable-discovery HELLO command
func (d *DFUDiscovery) Hello(userAgent string, protocolVersion int) error {
return nil
}

// Quit is the handler for the pluggable-discovery QUIT command
func (d *DFUDiscovery) Quit() {
d.Stop()
}

// 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
}
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 {
if cErr := C.libusbOpen(); cErr != nil {
return fmt.Errorf("can't open libusb: %s", C.GoString(cErr))
}
closeChan := make(chan struct{})
go func() {
for {
d.sendUpdates(eventCB, errorCB)
select {
case <-time.After(5 * time.Second):
case <-closeChan:
return
}
}
}()
d.closeChan = closeChan
return nil
}

func (d *DFUDiscovery) sendUpdates(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) {
C.dfuProbeDevices()

newCache := map[string]*discovery.Port{}
for _, dfuIf := range d.getDFUInterfaces() {
newPort := dfuIf.AsDiscoveryPort()
newCache[newPort.Address] = newPort
if _, exist := d.portsCache[newPort.Address]; !exist {
eventCB("add", newPort)
}
}

for _, oldPort := range d.portsCache {
if _, exist := newCache[oldPort.Address]; !exist {
eventCB("remove", oldPort)
}
}

d.portsCache = newCache
}

func getPath(dev *C.struct_libusb_device) string {
return C.GoString(C.get_path(dev))
}

// DFUInterface represents a DFU Interface
type DFUInterface struct {
Path string
AltName string
VID, PID uint16
SerialNumber string
Flags uint32
}

// AsDiscoveryPort converts this DFUInterface into a discovery.Port
func (i *DFUInterface) AsDiscoveryPort() *discovery.Port {
props := properties.NewMap()
props.Set("vid", fmt.Sprintf("%04X", i.VID))
props.Set("pid", fmt.Sprintf("%04X", i.PID))
if i.SerialNumber != "" {
props.Set("serial", i.SerialNumber)
}
// ProtocolLabel: pdfu.flags & DFU_IFF_DFU ? "DFU" : "Runtime"
return &discovery.Port{
Address: i.Path,
AddressLabel: i.Path,
Protocol: "dfu",
ProtocolLabel: "USB DFU",
Properties: props,
HardwareID: props.Get("serial"),
}
}

func (d *DFUDiscovery) getDFUInterfaces() []*DFUInterface {
res := []*DFUInterface{}
for pdfu := C.dfu_root; pdfu != nil; pdfu = pdfu.next {
if (pdfu.flags & C.DFU_IFF_DFU) == 0 {
// decide what to do with DFU runtime objects
// for the time being, ignore them
continue
}
ifPath := getPath(pdfu.dev)
// for i := 0; i < index; i++ {
// // allow only one object
// if j["ports"][i]["address"] == ifPath {
// index = i
// break
// }
// }

dfuIf := &DFUInterface{
Path: ifPath,
AltName: C.GoString(pdfu.alt_name),
VID: uint16(pdfu.vendor),
PID: uint16(pdfu.product),
SerialNumber: C.GoString(pdfu.serial_name),
}
res = append(res, dfuIf)
}
return res
}