USB serial not working inside VM

Hey,

i’m currently trying to migrate my home assistant installation inside an incus VM, the VM itself is using the home assistant OS. This worked without problems and I’m impressed by how easy this was with incus :slight_smile:

However I’m facing one major issue, usb passthrough of my dresden elektronik ConBee II. I’ve attached this using incus config device add home-assistant zigbee usb vendorid='1cf1' productid='0030' required=true. The device itself shows up inside the VM, but home assistant is unable to use it.

After some digging around I found a LKML thread taking about the cdc_acm driver claiming the interface on the host before it is passed into the VM, however unloading this driver (modprobe -r cdc_acm) didn’t solve this issue.

I’ve verified that other USB devices can be passed into the VM with a thumb drive that I had laying around.

For comparison, I pass a USB serial device to a container (not a VM). Instead of passing the raw USB device, I pass the character device from the host:

incus config device add solis modbus unix-char \
  source=/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AQ01WAFV-if00-port0 path=/dev/ttyUSB0 uid=2000 gid=2000

I use the /dev/serial/by-id/... path on the host because that’s guaranteed to be stable, but expose it as /dev/ttyUSB0 in the container for simplicity.

However, I don’t see why you can’t pass a raw USB device to a VM. The VM’s own kernel should have the device driver for it.

The device itself shows up inside the VM

… do you mean lsusb shows it? What about ls -l /dev/serial/by-id? If the serial device exists, can you open it with a generic terminal program like picocom?

but home assistant is unable to use it.

Further explanation would be helpful. Are you seeing an error message from the application and/or an error in dmesg, and if so, what do they say?

For comparison, I pass a USB serial device to a container (not a VM). Instead of passing the raw USB device, I pass the character device from the host:

This works and I was able to verify this with the zigpy-cli python package.

root@ubuntu-test:~# zigpy radio  deconz /dev/ttyUSB0 info
2024-06-23 12:21:52.762 ubuntu-test zigpy.application WARNING Unknown device AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD962)
2024-06-23 12:21:52.772 ubuntu-test zigpy.application WARNING Unknown device AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xFD38)
2024-06-23 12:21:52.784 ubuntu-test zigpy.application WARNING Unknown device AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD962)
2024-06-23 12:21:52.873 ubuntu-test zigpy.application WARNING Unknown device AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x8D57)
2024-06-23 12:21:52.890 ubuntu-test zigpy.application WARNING Unknown device AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x5FFF)
PAN ID:                0xXXXX
Extended PAN ID:       xx:xx:xx:xx:xx:xx:xx:xx
Channel:               15
Channel mask:          [15]
NWK update ID:         0
Device IEEE:           xx:xx:xx:xx:xx:xx:xx:xx
Device NWK:            0x0000
Network key:           xxx
Network key sequence:  0
Network key counter:   4672462

The same command results in a timeout when run inside the VM.

homeassistant:/config# zigpy radio deconz /dev/ttyACM0 info
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/zigpy_deconz/api.py", line 589, in _command
    return await fut
           ^^^^^^^^^
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/zigpy", line 8, in <module>
    sys.exit(cli())
             ^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/decorators.py", line 45, in new_func
    return f(get_current_context().obj, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/zigpy_cli/cli.py", line 20, in inner
    return loop.run_until_complete(cmd(*args, **kwargs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 685, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/zigpy_cli/radio.py", line 85, in info
    await app.connect()
  File "/usr/local/lib/python3.12/site-packages/zigpy_deconz/zigbee/application.py", line 97, in connect
    await api.connect()
  File "/usr/local/lib/python3.12/site-packages/zigpy_deconz/api.py", line 466, in connect
    await self.version()
  File "/usr/local/lib/python3.12/site-packages/zigpy_deconz/api.py", line 813, in version
    self._protocol_version = await self.read_parameter(
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/zigpy_deconz/api.py", line 832, in read_parameter
    rsp = await self.send_command(
          ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/zigpy_deconz/api.py", line 508, in send_command
    return await self._command(cmd, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/zigpy_deconz/api.py", line 588, in _command
    async with asyncio_timeout(COMMAND_TIMEOUT):
  File "/usr/local/lib/python3.12/asyncio/timeouts.py", line 115, in __aexit__
    raise TimeoutError from exc_val
TimeoutError

… do you mean lsusb shows it? What about ls -l /dev/serial/by-id? If the serial device exists, can you open it with a generic terminal program like picocom?

It shows up in lsusb and in /dev/serial/by-id, but with the error massage above I’m thinking the issue is related to reading or writing to the serial device.

Another thing I noticed it this line in the host dmesg.

[67933.728024] usb 1-2: usbfs: process 109134 (.qemu-system-x8) did not claim interface 0 before use

Before I removed the cbc_acm kernel module this would print everytime the VM tried to access the USB device with an additional message about interface 1.

The device exposes two interface as seen below. (note: cbc_acm model was loaded again in order to perform the requested container test)

$  lsusb -t
/:  Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/16p, 480M
    |__ Port 002: Dev 040, If 0, Class=Communications, Driver=cdc_acm, 12M
    |__ Port 002: Dev 040, If 1, Class=CDC Data, Driver=cdc_acm, 12M

I’ve just reproduce this in a ArchLinux VM.

lsusb is able to see the device and both interfaces on it, but trying to read from it with zigpy-cli results in the same error as above. This seams to be a VM related issue.

[root@archlinux ~]# lsusb -t
/:  Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/8p, 480M
    |__ Port 004: Dev 002, If 0, Class=Communications, Driver=cdc_acm, 12M
    |__ Port 004: Dev 002, If 1, Class=CDC Data, Driver=cdc_acm, 12M

I was able to workaroud this by passing the serial device to the VM via.

config:
  raw.qemu.conf: |-
    [chardev "serial"]
    id = "cma"
    backend = "serial"
    path = "/dev/serial/by-id/usb-dresden_elektronik_ingenieurtechnik_GmbH_ConBee_II_DE2669785-if00"
    [device "serial"]
    driver = "usb-serial"
    chardev = "cma"
1 Like