Dygmalab / Bazecor

Graphical configurator for Dygma Products
https://dygma.com/
GNU General Public License v3.0
391 stars 85 forks source link

Bazecor on ChromeOS is possible! #601

Open rspier opened 9 months ago

rspier commented 9 months ago

Describe the bug

It's possible to run Bazecor in the Linux container on ChromeOS, but requires a hack so Bazecor can figure out which port to connect to.

If you enable chrome://flags/#permissive-usb-passthrough, then the device itself is available inside the Linux container after clicking the popup. (i.e. /dev/ttyACM0) (Normal keypresses won't work, but it's the serial port that matters.)

Bazecor uses metadata provided by the serialport library to figure out the USB ID and serial number. The serialport library gets it from udevadm info -e. Sadly, the ChromeOS USB passthrough into the container doesn't populate that data in a way udev can extract.

This hack of a patch hardcodes any /dev/ttyUSBx ports to be Defy Wireless with a serial number of FFFFFFFFFFFFFFFF;

diff --git a/src/api/focus/index.js b/src/api/focus/index.js
index ae5cc511..a00b5531 100644
--- a/src/api/focus/index.js
+++ b/src/api/focus/index.js
@@ -20,6 +20,7 @@ import { spawn } from "child_process";

 const { SerialPort } = eval('require("serialport")');
 const { DelimiterParser } = eval('require("@serialport/parser-delimiter")');
+import { Defy_wireless, Defy_wirelessBootloader } from "../hardware-dygma-defy-wireless";

 global.focus_instance = null;

@@ -54,12 +55,15 @@ class Focus {
     console.log("Passed devices", devices);
     console.log("Port list from where");
     for (const port of portList) {
-      for (const device of devices) {
-        if (parseInt(`0x${port.productId}`) == device.usb.productId && parseInt(`0x${port.vendorId}`) == device.usb.vendorId) {
+      if (port.path.startsWith("/dev/ttyACM")) {
+      const device = Defy_wireless;
+      port.serialNumber = "FFFFFFFFFFFFFFFF";
+      //for (const device of devices) {
+        //if (parseInt(`0x${port.productId}`) == device.usb.productId && parseInt(`0x${port.vendorId}`) == device.usb.vendorId) {
           const newPort = { ...port };
           newPort.device = device;
           foundDevices.push(newPort);
-        }
+        //}
       }
     }

A "proper" solution would allow for the user to specify the port and the device manually.

(But tonight all I had time for was the research and proof of concept.)

Desktop (please complete the following information):

rspier commented 9 months ago

udevadm info -e for a Defy on ChromeOS Linux container:

P: /devices/pci0000:00/0000:00:0d.0/usb1/1-1/1-1:1.0/tty/ttyACM0
N: ttyACM0
L: 0
E: DEVPATH=/devices/pci0000:00/0000:00:0d.0/usb1/1-1/1-1:1.0/tty/ttyACM0
E: SUBSYSTEM=tty
E: DEVNAME=/dev/ttyACM0
E: MAJOR=166
E: MINOR=0

udevadm info -e for a Raise on Linux:

P: /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.1
N: bus/usb/003/061
L: 0
E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.1
E: SUBSYSTEM=usb
E: DEVNAME=/dev/bus/usb/003/061
E: DEVTYPE=usb_device
E: DRIVER=usb
E: PRODUCT=1209/2201/100
E: TYPE=239/2/1
E: BUSNUM=003
E: DEVNUM=061
E: MAJOR=189
E: MINOR=316
E: USEC_INITIALIZED=898523186027
E: ID_VENDOR=Dygma
E: ID_VENDOR_ENC=Dygma
E: ID_VENDOR_ID=1209
E: ID_MODEL=Raise
E: ID_MODEL_ENC=Raise
E: ID_MODEL_ID=2201
E: ID_REVISION=0100
E: ID_SERIAL=Dygma_Raise_<snip>
E: ID_SERIAL_SHORT=<snip>
E: ID_BUS=usb
E: ID_USB_INTERFACES=:020200:0a0000:030101:030000:
E: ID_VENDOR_FROM_DATABASE=Generic
E: ID_MODEL_FROM_DATABASE=Dygma Shortcut Keyboard
E: ID_PATH=pci-0000:00:14.0-usb-0:3.1
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_3_1
E: TAGS=:snap_chromium_chromium:snap_cups_ippeveprinter:snap_cups_cupsd:snap_chromium_chromedriver:
E: CURRENT_TAGS=:snap_chromium_chromium:snap_cups_ippeveprinter:snap_cups_cupsd:snap_chromium_chromedriver:
rspier commented 9 months ago

Interestingly,

udevadm info --query=all --name=/dev/ttyACM0 --attribute-walk

shows that all the information Bazecor needs is available to udev, so it's still an open question as to why udevadm info -e isn't finding it.

rspier commented 9 months ago

More details about why udev doesn't work on ChromeOS from https://github.com/systemd/systemd/issues/28156.

sysfs is not virtualized for containers on Linux kernel. RUnning udev inside containers hence is not supported. udevadm trigger is conditioned on /sys/ being writable, which is how container managers should communicate that udev is not supposed to run.

It looks like it might theoretically be possible to send a message directly to udevd to get it to update its database in /run/udev, but this is going to be fragile.

Better solution is not to not 100% rely on udev and provide an alternate path.

tl;dr: udev has the ability to see the data (udevadm test /sys/devices/pci0000:00/0000:00:0d.0/usb1/1-1/1-1:1.0/tty/ttyACM0) , but because udev doesn't work inside containers, it can't function properly to write to its database.