Skip to content

Commit c643e60

Browse files
core3ext: reattach assigned USB devices on resume from suspend
This patch ensures USB devices are reattached on resume, which is necessary for S0ix since we don't detach the USB controller drivers and hence the udev rules aren't retriggered automatically.
1 parent c3ca2a3 commit c643e60

File tree

1 file changed

+39
-27
lines changed

1 file changed

+39
-27
lines changed

qubesusbproxy/core3ext.py

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,37 @@ def __init__(self):
521521
"/etc/qubes-rpc/qubes.USB"
522522
)
523523
self.devices_cache = collections.defaultdict(dict)
524+
self.autoattach_locks = collections.defaultdict(asyncio.Lock)
525+
526+
async def _auto_attach_devices(self, vm):
527+
async with self.autoattach_locks[vm.uuid]:
528+
to_attach = {}
529+
assignments = get_assigned_devices(vm.devices["usb"])
530+
# the most specific assignments first
531+
for assignment in reversed(sorted(assignments)):
532+
for device in assignment.devices:
533+
if isinstance(device, qubes.device_protocol.UnknownDevice):
534+
continue
535+
if device.attachment:
536+
continue
537+
if not assignment.matches(device):
538+
print(
539+
"Unrecognized identity, skipping attachment of device "
540+
f"from the port {assignment}",
541+
file=sys.stderr,
542+
)
543+
continue
544+
# chose first assignment (the most specific) and ignore rest
545+
if device not in to_attach:
546+
# make it unique
547+
to_attach[device] = assignment.clone(device=device)
548+
in_progress = set()
549+
for assignment in to_attach.values():
550+
in_progress.add(
551+
asyncio.ensure_future(self.attach_and_notify(vm, assignment))
552+
)
553+
if in_progress:
554+
await asyncio.wait(in_progress)
524555

525556
@qubes.ext.handler("domain-init", "domain-load")
526557
def on_domain_init_load(self, vm, event):
@@ -744,41 +775,22 @@ async def on_device_assign_usb(self, vm, event, device, options):
744775
@qubes.ext.handler("domain-start")
745776
async def on_domain_start(self, vm, _event, **_kwargs):
746777
# pylint: disable=unused-argument
747-
to_attach = {}
748-
assignments = get_assigned_devices(vm.devices["usb"])
749-
# the most specific assignments first
750-
for assignment in reversed(sorted(assignments)):
751-
for device in assignment.devices:
752-
if isinstance(device, qubes.device_protocol.UnknownDevice):
753-
continue
754-
if device.attachment:
755-
continue
756-
if not assignment.matches(device):
757-
print(
758-
"Unrecognized identity, skipping attachment of device "
759-
f"from the port {assignment}",
760-
file=sys.stderr,
761-
)
762-
continue
763-
# chose first assignment (the most specific) and ignore rest
764-
if device not in to_attach:
765-
# make it unique
766-
to_attach[device] = assignment.clone(device=device)
767-
in_progress = set()
768-
for assignment in to_attach.values():
769-
in_progress.add(
770-
asyncio.ensure_future(self.attach_and_notify(vm, assignment))
771-
)
772-
if in_progress:
773-
await asyncio.wait(in_progress)
778+
await self._auto_attach_devices(vm)
774779

775780
@qubes.ext.handler("domain-shutdown")
776781
async def on_domain_shutdown(self, vm, _event, **_kwargs):
777782
# pylint: disable=unused-argument
778783
vm.fire_event("device-list-change:usb")
779784
utils.device_list_change(self, {}, vm, None, USBDevice)
785+
del self.autoattach_locks[vm.uuid]
786+
787+
@qubes.ext.handler("domain-resumed")
788+
async def on_domain_resumed(self, vm, _event, **_kwargs):
789+
# pylint: disable=unused-argument
790+
await self._auto_attach_devices(vm)
780791

781792
@qubes.ext.handler("qubes-close", system=True)
782793
def on_qubes_close(self, app, event):
783794
# pylint: disable=unused-argument
784795
self.devices_cache.clear()
796+
self.autoattach_locks.clear()

0 commit comments

Comments
 (0)