Login
You're viewing the twiukraine.com public feed.
  • Jul 26, 2026, 1:11 AM

    Playing around with #NetBSD again… so I have a Intel NUC running it. One of the thing I frequently have to deal with, being an embedded software developer *and* an amateur radio operator, is #SerialPorts.

    More often than not, USB serial ports. Now, I'm used to the Linux way of doing things, where everything is accessed through a /dev/tty* device.

    We used to do it the BSD way with a device like /dev/cua0 (16550 UART on ISA 0x3f8 IRQ 4; COM1 for you DOS people) being your serial port ignoring carrier detect, and /dev/ttyS0 was the same serial port but required CD to be up. This got deprecated years ago, so /dev/cua0 disappeared, and we were left with /dev/ttyS0.

    I grabbed a test rig I made for work, this is Zephyr RTOS on a Raspberry Pi Pico. It enumerates two CDC-ACM serial ports, one with the Zephyr shell, and the other is a USB serial port. You use the shell port to tell it how to route the RXD/TXD lines on the other serial port, with the board supporting RS-485, RS-422 and TTL serial. On Linux, it shows up as `/dev/ttyACM${n}` and `/dev/ttyACM$(( ${n} + 1 )`. `udev` also creates these symbolic links:

    ```
    stuartl@rikishi ~ $ ls -l /dev/serial/by-id
    total 0
    lrwxrwxrwx 1 root root 13 Jul 26 10:58 usb-WideSky.Cloud_Pty_Ltd_WideSky_Hub_Test_Rig_E663AC91D3665F30-if00 -> ../../ttyACM0
    lrwxrwxrwx 1 root root 13 Jul 26 10:58 usb-WideSky.Cloud_Pty_Ltd_WideSky_Hub_Test_Rig_E663AC91D3665F30-if02 -> ../../ttyACM1
    stuartl@rikishi ~ $ ls -l /dev/serial/by-path/
    total 0
    lrwxrwxrwx 1 root root 13 Jul 26 10:58 pci-0000:00:14.0-usb-0:2:1.0 -> ../../ttyACM0
    lrwxrwxrwx 1 root root 13 Jul 26 10:58 pci-0000:00:14.0-usb-0:2:1.2 -> ../../ttyACM1
    lrwxrwxrwx 1 root root 13 Jul 26 10:58 pci-0000:00:14.0-usbv2-0:2:1.0 -> ../../ttyACM0
    lrwxrwxrwx 1 root root 13 Jul 26 10:58 pci-0000:00:14.0-usbv2-0:2:1.2 -> ../../ttyACM1
    ```

    This is handy because if I have two of these, it doesn't matter which gets detected first, I know which device is which. The links ending in `-if02` or `.2` will be the data port, and the `-if00` / `.0` ones will be the shell control port.

    In NetBSD, I plug this same MCU in and I get 4 devices:
    - `/dev/dtyU0`
    - `/dev/dtyU1`
    - `/dev/ttyU0`
    - `/dev/ttyU1`

    The files actually always exist, but they get mapped to this physical device when the driver detects it and enumerates it on USB.

    If I plug in another dongle, I get another pair, a `/dev/dtyU2` and a `/dev/ttyU2`. The choice of `/dev/dtyU[012]` is dependent on which order the devices is detected… which on USB is indeterminate as they can be hotplugged.

    The only clue I get is the `usbdevs -v`; it shows a device number and serial number information.

    ```
    vk4msl-nbsd# usbdevs -v
    Controller /dev/usb0:
    addr 0: super speed, self powered, config 1, xHCI root hub(0x0000), NetBSD(0x0000), rev 1.00(0x0100)
    port 1 disabled
    port 2 disabled
    port 3 disabled
    port 4 disabled
    Controller /dev/usb1:
    addr 0: high speed, self powered, config 1, xHCI root hub(0x0000), NetBSD(0x0000), rev 1.00(0x0100)
    port 1 powered
    port 2 addr 17: full speed, power 100 mA, config 1, USB-Serial Controller D(0x2303), Prolific Technology Inc.(0x067b), rev 4.00(0x0400)
    port 3 addr 1: low speed, power 100 mA, config 1, CASUE USB KB(0x6a21), vendor 2a7a(0x2a7a), rev 0.01(0x0001)
    port 4 addr 18: full speed, self powered, config 1, WideSky Hub Test Rig(0x5678), WideSky.Cloud Pty Ltd(0x1234), rev 4.03(0x0403), serial E663AC91D3665F30
    port 5 powered
    port 6 powered
    port 7 addr 3: full speed, self powered, config 1, product 0a2a(0x0a2a), Intel(0x8087), rev 0.01(0x0001)
    port 8 powered
    port 9 powered
    port 10 powered
    port 11 powered
    …
    ```

    lacking is the knowledge that there are *two* devices allocated to device 18 (the test rig).

    Given a `/dev/ttyU${n}` or `/dev/dtyU${n}` device node, how do I find out about the device attached to it? Seems assuming a device node is fraught with danger.

    💬 1🔄 5⭐ 5

Replies

  • Jul 26, 2026, 3:02 AM

    Whilst researching this, I had a look at some of the libraries out there to see how they do it on different operating systems.

    Microsoft Windows and Apple MacOS X provide system APIs for doing this.
    On Linux, some libraries go poking around in `/dev/serial/*`, others go nosing through `/sys/class/tty/*`. Some just look for specific names in `/dev` and hope for the best. `pyserial` also checks `/proc/tty/drivers` on Linux, but everywhere else in POSIX land it uses `glob`.

    Most do not consider BSD at all. You're on your own to learn what serial ports your computer has. It's a mess.

    #NetBSD has a `/kern` virtual filesystem which is similar to `/sys` in Linux… so there *is* scope there to maybe put some sort of virtual text file that dumps a mapping of devices and what device nodes they're at. My mind also thinks about OpenFirmware's DeviceTree.

    BSD in general, has the same problem Linux had years ago: it assumed the hardware never changed. In the 80s and 90s, that was mostly true, your devices were hard-wired into "busses". You mostly ignored that there was a ISA bus, you just "knew" you had a device at address 0x300 and "talked" to it. It was there at power on, and there at shutdown.

    SCSI was a little more complicated, you spoke to your disk drive *through* this HBA card.

    Then came along Firewire, USB and Bluetooth. Devices could come and go as they pleased. Moreover, USB is a tree structure and Bluetooth is a mesh radio network. A flat structure doesn't quite describe the situation. I'm seeing a lot of wisdom behind the idea of Open Firmware's DeviceTree… and it seems to me, the kernel of a modern OS needs to track a live changing representation of this. A big part of keeping track will be providing a mechanism for the userspace to learn of changes in this tree, and to find where things are.

    While I don't expect them to wholesale copy Linux, I think there is merit in the kernel calling a userspace application to "set up" devices when they are detected, and "tear down" those devices when they are removed.

    This is `udev` in Linux and it is very handy even if it can be a pain in the bum to debug.

    I've also seen the virtual `/dev` filesystem route used. Linux used to have `devfs`, and SyllableOS also used such a system. Not saying that's the best approach, but it is another way of exposing this information to the system.

    Not sure which is the "right" approach, but I don't think a static "nothing ever changes" approach is viable here.

    💬 0🔄 1⭐ 1