aboutsummaryrefslogtreecommitdiffstats
path: root/sw/usbcom
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2025-09-17 09:09:05 -0400
committerSam Anthony <sam@samanthony.xyz>2025-09-17 09:09:05 -0400
commit8b1ef0ce3a520d994ed73117069cc7a49fb75ca4 (patch)
treed81a7c8ebcb8e845fa8f8d9b5042dc2ae81b6428 /sw/usbcom
parentdb3b9ef98ff189abcd08555e3ee30601713ef8f6 (diff)
downloadcan-gauge-interface-8b1ef0ce3a520d994ed73117069cc7a49fb75ca4.zip
usbcom go rewrite
Diffstat (limited to 'sw/usbcom')
-rw-r--r--sw/usbcom/go.mod8
-rw-r--r--sw/usbcom/go.sum8
-rw-r--r--sw/usbcom/main.go85
-rw-r--r--sw/usbcom/usb/usb.go161
-rw-r--r--sw/usbcom/usbcom.c194
5 files changed, 262 insertions, 194 deletions
diff --git a/sw/usbcom/go.mod b/sw/usbcom/go.mod
new file mode 100644
index 0000000..1870f76
--- /dev/null
+++ b/sw/usbcom/go.mod
@@ -0,0 +1,8 @@
+module git.samanthony.xyz/can_gauge_interface/sw/usbcom
+
+go 1.24.6
+
+require (
+ github.com/google/gousb v1.1.3 // indirect
+ golang.org/x/sync v0.17.0 // indirect
+)
diff --git a/sw/usbcom/go.sum b/sw/usbcom/go.sum
new file mode 100644
index 0000000..64a2dc3
--- /dev/null
+++ b/sw/usbcom/go.sum
@@ -0,0 +1,8 @@
+github.com/google/gousb v1.1.3 h1:xt6M5TDsGSZ+rlomz5Si5Hmd/Fvbmo2YCJHN+yGaK4o=
+github.com/google/gousb v1.1.3/go.mod h1:GGWUkK0gAXDzxhwrzetW592aOmkkqSGcj5KLEgmCVUg=
+github.com/gotmc/libusb/v2 v2.3.4 h1:xDKi52trBIq7QTMxlhHy9zgb+HRmimSBo8yhx+k1NWU=
+github.com/gotmc/libusb/v2 v2.3.4/go.mod h1:rU0mvps+snf/CHvEkISbxrwUlWpt6VluOkjqpKo6TFw=
+github.com/sam-rba/share v0.3.0 h1:aBVvgSbcBaH43Jrbq++iz15/DAso/D6oJDm+dDM4E40=
+github.com/sam-rba/share v0.3.0/go.mod h1:rDx/wFG1Vc6JO1F/F0eBnkcy2wHwV0T9100ig/hu12A=
+golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
+golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
diff --git a/sw/usbcom/main.go b/sw/usbcom/main.go
new file mode 100644
index 0000000..70661d9
--- /dev/null
+++ b/sw/usbcom/main.go
@@ -0,0 +1,85 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "os"
+
+ "golang.org/x/sync/errgroup"
+
+ "git.samanthony.xyz/can_gauge_interface/sw/usbcom/usb"
+)
+
+const bufSize = 512
+
+func main() {
+ exitCode := make(chan int)
+ go exit(exitCode)
+ defer close(exitCode)
+
+ dev, err := usb.Connect()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error connecting to device: %v\n", err)
+ exitCode <- 1
+ return
+ }
+ defer func() {
+ if err := dev.Close(); err != nil {
+ fmt.Fprintf(os.Stderr, "Error :%v\n", err)
+ exitCode <- 1
+ }
+ }()
+
+ var group errgroup.Group
+ ctx, cancel := context.WithCancel(context.Background())
+
+ // Host->Device
+ group.Go(func() error {
+ err := hostToDevice(dev)
+ cancel() // device->host routine
+ return err
+ })
+
+ // Device->Host
+ group.Go(func() error {
+ return deviceToHost(ctx, dev)
+ })
+
+ if err = group.Wait(); err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+ exitCode <- 1
+ }
+}
+
+func exit(code <-chan int) {
+ worst := 0
+ for c := range code {
+ if c > worst {
+ worst = c
+ }
+ }
+ os.Exit(worst)
+}
+
+func hostToDevice(dev *usb.Device) error {
+ _, err := io.Copy(dev, os.Stdin)
+ return err
+}
+
+func deviceToHost(ctx context.Context, dev *usb.Device) error {
+ buf := make([]byte, bufSize)
+ for {
+ select {
+ case <-ctx.Done():
+ return nil
+ default:
+ }
+
+ n, err := dev.Read(buf)
+ if err != nil {
+ return err
+ }
+ fmt.Print(string(buf[:n]))
+ }
+}
diff --git a/sw/usbcom/usb/usb.go b/sw/usbcom/usb/usb.go
new file mode 100644
index 0000000..1fecf4a
--- /dev/null
+++ b/sw/usbcom/usb/usb.go
@@ -0,0 +1,161 @@
+package usb
+
+import (
+ "fmt"
+
+ "github.com/google/gousb"
+)
+
+const (
+ vendorId gousb.ID = 0x04d8
+ productId gousb.ID = 0x000a
+
+ inEndpointAddr = 0x82
+ outEndpointAddr = 0x02
+)
+
+type Device struct {
+ ctx *gousb.Context
+ dev *gousb.Device
+ closeIntf func()
+ in *gousb.ReadStream // IN endpoint (device->host)
+ out *gousb.WriteStream // OUT endpoint (host->device)
+}
+
+func Connect() (*Device, error) {
+ ctx := gousb.NewContext()
+
+ dev, err := ctx.OpenDeviceWithVIDPID(vendorId, productId)
+ if err != nil {
+ ctx.Close()
+ return nil, err
+ }
+
+ intf, closeIntf, err := interfaceByClass(dev, gousb.ClassData)
+ if err != nil {
+ dev.Close()
+ ctx.Close()
+ return nil, err
+ }
+
+ inEp, err := intf.InEndpoint(inEndpointAddr)
+ if err != nil {
+ closeIntf()
+ dev.Close()
+ ctx.Close()
+ return nil, err
+ }
+
+ inEpDesc, ok := intf.Setting.Endpoints[inEndpointAddr]
+ if !ok {
+ closeIntf()
+ dev.Close()
+ ctx.Close()
+ return nil, fmt.Errorf("no such endpoint: %x", inEndpointAddr)
+ }
+
+ in, err := inEp.NewStream(inEpDesc.MaxPacketSize, 1)
+ if err != nil {
+ closeIntf()
+ dev.Close()
+ ctx.Close()
+ return nil, err
+ }
+
+ outEp, err := intf.OutEndpoint(outEndpointAddr)
+ if err != nil {
+ closeIntf()
+ dev.Close()
+ ctx.Close()
+ return nil, err
+ }
+
+ outEpDesc, ok := intf.Setting.Endpoints[outEndpointAddr]
+ if !ok {
+ closeIntf()
+ dev.Close()
+ ctx.Close()
+ return nil, fmt.Errorf("no such endpoint: %x", outEndpointAddr)
+ }
+
+ out, err := outEp.NewStream(outEpDesc.MaxPacketSize, 1)
+ if err != nil {
+ closeIntf()
+ dev.Close()
+ ctx.Close()
+ return nil, err
+ }
+
+ return &Device{
+ ctx,
+ dev,
+ closeIntf,
+ in, out,
+ }, nil
+}
+
+func interfaceByClass(dev *gousb.Device, class gousb.Class) (intf *gousb.Interface, close func(), err error) {
+ cfgNum, err := dev.ActiveConfigNum()
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to get active config number of device %s: %v", dev, err)
+ }
+ cfg, err := dev.Config(cfgNum)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to claim config %d of device %s: %v", cfgNum, dev, err)
+ }
+
+ intfNum, altNum, ok := func() (int, int, bool) {
+ for i, intf := range cfg.Desc.Interfaces {
+ for a, alt := range intf.AltSettings {
+ if alt.Class == class {
+ return i, a, true
+ }
+ }
+ }
+ return -1, -1, false
+ }()
+ if !ok {
+ cfg.Close()
+ return nil, nil, fmt.Errorf("failed to get %s interface of device %s: %v", class, dev, err)
+ }
+
+ intf, err = cfg.Interface(intfNum, altNum)
+ if err != nil {
+ cfg.Close()
+ return nil, nil, fmt.Errorf("failed to select interface (%d,%d) of config %d of device %s: %v", intfNum, altNum, cfgNum, dev, err)
+ }
+
+ return intf, func() {
+ intf.Close()
+ cfg.Close()
+ }, nil
+}
+
+func (dev *Device) Close() error {
+ outErr := dev.out.Close()
+ inErr := dev.in.Close()
+ dev.closeIntf()
+ devErr := dev.dev.Close()
+ ctxErr := dev.ctx.Close()
+
+ return nonNil(outErr, inErr, devErr, ctxErr)
+}
+
+func nonNil(errs ...error) error {
+ for _, err := range errs {
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Read from the IN endpoint (device->host)
+func (dev *Device) Read(p []byte) (n int, err error) {
+ return dev.in.Read(p)
+}
+
+// Write to the OUT endpoint (host->device)
+func (dev *Device) Write(p []byte) (n int, err error) {
+ return dev.out.Write(p)
+}
diff --git a/sw/usbcom/usbcom.c b/sw/usbcom/usbcom.c
deleted file mode 100644
index a374875..0000000
--- a/sw/usbcom/usbcom.c
+++ /dev/null
@@ -1,194 +0,0 @@
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <libusb-1.0/libusb.h>
-
-#define TIMEOUT_MS 5000
-
-#define BUF_SIZE 512
-
-#define INTERFACE 0
-
-enum {
- VENDOR_ID = 0x04d8,
- PRODUCT_ID = 0x000a,
-};
-
-enum {
- SET_LINE_CODING = 0x20,
- SET_CONTROL_LINE_STATE = 0x22,
-};
-
-enum {
- ACM_CTRL_DTR = 0x01,
- ACM_CTRL_RTS = 0x02,
-};
-
-enum {
- EP_OUT_ADDR = 0x02,
- EP_IN_ADDR = 0x82,
-};
-
-// line encoding: 9600 8N1
-// 9600 = 0x2580 LE
-static unsigned char encoding[] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08};
-
-// Returns NULL on error
-static libusb_device_handle *
-setup(void) {
- int res;
- libusb_device_handle *devh;
-
- res = libusb_init_context(NULL, NULL, 0);
- if (res < 0) {
- fprintf(stderr, "Error initializing libusb: %s\n", libusb_strerror(res));
- return NULL;
- }
-
- devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
- if (!devh) {
- fprintf(stderr, "Error finding USB device\n");
- return NULL;
- }
-
- res = libusb_claim_interface(devh, INTERFACE);
- if (res < 0) {
- fprintf(stderr, "Failed to claim interface: %s\n", libusb_strerror(res));
- libusb_close(devh);
- return NULL;
- }
-
- res = libusb_control_transfer(devh, 0x21, SET_CONTROL_LINE_STATE, ACM_CTRL_DTR | ACM_CTRL_RTS, 0, NULL, 0, TIMEOUT_MS);
- if (res < 0) {
- fprintf(stderr, "Error during control transfer: %s\n", libusb_strerror(res));
- libusb_close(devh);
- return NULL;
- }
-
- res = libusb_control_transfer(devh, 0x21, SET_LINE_CODING, 0, 0, encoding, sizeof(encoding), TIMEOUT_MS);
- if (res < 0) {
- fprintf(stderr, "Error during control transfer: %s\n", libusb_strerror(res));
- libusb_close(devh);
- return NULL;
- }
-
- return devh;
-}
-
-static void
-teardown(libusb_device_handle *devh) {
- libusb_release_interface(devh, INTERFACE);
- libusb_close(devh);
- libusb_exit(NULL);
-}
-
-// Send bytes to USB
-static void
-txChars(libusb_device_handle *devh, unsigned char *data, int n) {
- int actualLen, res;
-
- actualLen = 0;
- while (actualLen < n) {
- res = libusb_bulk_transfer(devh, EP_OUT_ADDR, data, n, &actualLen, TIMEOUT_MS);
- if (res == LIBUSB_ERROR_TIMEOUT) {
- printf("Tx timeout (%d)\n", actualLen);
- } else if (res < 0) {
- fprintf(stderr, "Tx error: %s\n", libusb_strerror(res));
- }
- assert(actualLen <= n);
- n -= actualLen;
- data += actualLen;
- }
-}
-
-// Read data from USB
-// Returns number of bytes read, or -1 on error
-static int
-rxChars(libusb_device_handle *devh, unsigned char *data, int size) {
- int res, len;
-
- res = libusb_bulk_transfer(devh, EP_IN_ADDR, data, size, &len, TIMEOUT_MS);
- if (res == LIBUSB_ERROR_TIMEOUT) {
- fprintf(stderr, "Rx timeout\n");
- return -1;
- } else if (res < 0) {
- fprintf(stderr, "Rx error: %s\n", libusb_strerror(res));
- return -1;
- }
- return len;
-}
-
-// Get line from stdin
-// Includes '\n'
-// Returns number of chars prior to NUL, or EOF on end of file or error
-static int
-scanline(char *buf, size_t size) {
- int c, n;
-
- c = '\0';
- n = 0;
- while (n+1 < size) {
- c = getchar();
- if (c == EOF) {
- break;
- }
- buf[n++] = c;
- if (c == '\n') {
- break;
- }
- }
-
- if (size > 0) {
- buf[n] = '\0';
- }
-
- return ((c == EOF) && (n < 1)) ? EOF : n;
-}
-
-static void
-sendCommand(libusb_device_handle *devh) {
- unsigned char buf[BUF_SIZE];
- int len;
-
- while ((len = scanline(buf, sizeof(buf))) != EOF) {
- txChars(devh, buf, len);
- if (len > 0 && buf[len-1] == '\n') {
- break;
- }
- }
-}
-
-static void
-printResponse(libusb_device_handle *devh) {
- unsigned char buf[BUF_SIZE];
- int len;
-
- while ((len = rxChars(devh, buf, sizeof(buf)-1)) >= 0) {
- buf[len] = '\0';
- printf("%s", buf);
- if (len > 0 && buf[len-1] == '\n') {
- return;
- }
- }
-}
-
-int
-main(int argc, char *argv[]) {
- libusb_device_handle *devh;
-
- devh = setup();
- if (!devh) {
- return 1;
- }
-
- for (;;) {
- sendCommand(devh);
- printResponse(devh);
- }
-
- teardown(devh);
- return 0;
-}