diff --git a/Makefile b/Makefile index 332750d..9065f75 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,8 @@ install-vm: install qubes-rpc/qubes.USB $(DESTDIR)/etc/qubes-rpc install -d $(DESTDIR)/usr/lib/qubes install src/usb-* $(DESTDIR)/usr/lib/qubes + install -d $(DESTDIR)/usr/lib/udev/rules.d + install src/*.rules $(DESTDIR)/usr/lib/udev/rules.d install -d $(DESTDIR)/etc/qubes/suspend-pre.d ln -s ../../../usr/lib/qubes/usb-detach-all \ $(DESTDIR)/etc/qubes/suspend-pre.d/usb-detach-all.sh diff --git a/rpm_spec/qubes-usb-proxy.spec.in b/rpm_spec/qubes-usb-proxy.spec.in index e3b3939..e69600b 100644 --- a/rpm_spec/qubes-usb-proxy.spec.in +++ b/rpm_spec/qubes-usb-proxy.spec.in @@ -31,6 +31,8 @@ make install-vm DESTDIR=${RPM_BUILD_ROOT} /usr/lib/qubes/usb-import /usr/lib/qubes/usb-export /usr/lib/qubes/usb-detach-all +/usr/lib/qubes/usb-reset +/usr/lib/udev/rules.d/80-qubes-usb-reset.rules %changelog @CHANGELOG@ diff --git a/src/80-qubes-usb-reset.rules b/src/80-qubes-usb-reset.rules new file mode 100644 index 0000000..697b9b8 --- /dev/null +++ b/src/80-qubes-usb-reset.rules @@ -0,0 +1,3 @@ +# Nitrokey 3 Bootloader, requires reset on attach +# See https://github.com/QubesOS/qubes-issues/issues/8953 +SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="20a0", ENV{ID_MODEL_ID}=="42dd", ENV{QUBES_USB_RESET}="1" diff --git a/src/usb-export b/src/usb-export index 061947a..ad2ab2b 100755 --- a/src/usb-export +++ b/src/usb-export @@ -93,14 +93,32 @@ modprobe usbip-host # Request that both IN and OUT be handled on a single (stdin) socket kill -USR1 "$QREXEC_AGENT_PID" || exit 1 +attach_to_usbip=true # Unbind the device from the driver if [ -d "$devpath/driver" ]; then - printf %s "$busid" > "$devpath/driver/unbind" || exit 1 + old_driver=$(readlink -f "$devpath/driver") + if [ "$old_driver" != "$SYS_USBIP_HOST" ]; then + printf %s "$busid" > "$devpath/driver/unbind" || exit 1 + else + attach_to_usbip= + fi fi # Bind to the usbip-host driver printf 'add %s' "$busid" > "$SYS_USBIP_HOST/match_busid" || exit 1 -echo "$busid" > "$SYS_USBIP_HOST/bind" || exit 1 +if [ -n "$attach_to_usbip" ]; then + echo "$busid" > "$SYS_USBIP_HOST/bind" || exit 1 + + # optionally reset the device to clear any state from previous driver + reset_on_attach=$(udevadm info --query=property \ + --value --property=QUBES_USB_RESET --path="$devpath") + if [ -f /run/qubes-service/usb-reset-on-attach ]; then + reset_on_attach=1 + fi + if [ -n "$reset_on_attach" ]; then + /usr/lib/qubes/usb-reset "$devpath" + fi +fi # One more safety check - make sure the device is available read status < "$devpath/usbip_status" diff --git a/src/usb-reset b/src/usb-reset new file mode 100755 index 0000000..1b2e20f --- /dev/null +++ b/src/usb-reset @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import os +import fcntl +from pathlib import Path + +# from /usr/include/linux/usbdevice_fs.h +# _IO('U', 20) +USBDEVFS_RESET = 0x5514 + +def main(): + if len(sys.argv) != 2: + print("Usage: usb-reset sysfs-devpath", file=sys.stderr()) + exit(2) + devpath = sys.argv[1] + uevent = (Path(devpath) / "uevent").read_text() + devname = [line.partition("=")[2] + for line in uevent.splitlines() + if line.startswith("DEVNAME=")][0] + with (Path("/dev") / devname).open("w") as dev_f: + fcntl.ioctl(dev_f, USBDEVFS_RESET, 0) + +if __name__ == "__main__": + main()