Stuart Longland (VK4MSL) on Nostr: Playing around with #NetBSD again… so I have a Intel NUC running it. One of the ...
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.
Published at
2026-07-26 01:11:57 UTCEvent JSON
{
"id": "3fd9debd32c9324cac1a5765aeb4f0c23c05a8af725d8777a51260e488ffcc88",
"pubkey": "54e8ecdf233aeff783f2894eb63943db8238c447956b1f850a3989c15b292b6d",
"created_at": 1785028317,
"kind": 1,
"tags": [
[
"t",
"serialports"
],
[
"proxy",
"https://mastodon.longlandclan.id.au/@stuartl/116983615819262018",
"web"
],
[
"t",
"netbsd"
],
[
"proxy",
"https://mastodon.longlandclan.id.au/users/stuartl/statuses/116983615819262018",
"activitypub"
],
[
"L",
"pink.momostr"
],
[
"l",
"pink.momostr.activitypub:https://mastodon.longlandclan.id.au/users/stuartl/statuses/116983615819262018",
"pink.momostr"
],
[
"-"
]
],
"content": "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.\n\nMore 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.\n\nWe 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.\n\nI 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:\n\n```\nstuartl@rikishi ~ $ ls -l /dev/serial/by-id\ntotal 0\nlrwxrwxrwx 1 root root 13 Jul 26 10:58 usb-WideSky.Cloud_Pty_Ltd_WideSky_Hub_Test_Rig_E663AC91D3665F30-if00 -\u003e ../../ttyACM0\nlrwxrwxrwx 1 root root 13 Jul 26 10:58 usb-WideSky.Cloud_Pty_Ltd_WideSky_Hub_Test_Rig_E663AC91D3665F30-if02 -\u003e ../../ttyACM1\nstuartl@rikishi ~ $ ls -l /dev/serial/by-path/\ntotal 0\nlrwxrwxrwx 1 root root 13 Jul 26 10:58 pci-0000:00:14.0-usb-0:2:1.0 -\u003e ../../ttyACM0\nlrwxrwxrwx 1 root root 13 Jul 26 10:58 pci-0000:00:14.0-usb-0:2:1.2 -\u003e ../../ttyACM1\nlrwxrwxrwx 1 root root 13 Jul 26 10:58 pci-0000:00:14.0-usbv2-0:2:1.0 -\u003e ../../ttyACM0\nlrwxrwxrwx 1 root root 13 Jul 26 10:58 pci-0000:00:14.0-usbv2-0:2:1.2 -\u003e ../../ttyACM1\n```\n\nThis 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.\n\nIn NetBSD, I plug this same MCU in and I get 4 devices:\n- `/dev/dtyU0`\n- `/dev/dtyU1`\n- `/dev/ttyU0`\n- `/dev/ttyU1`\n\nThe files actually always exist, but they get mapped to this physical device when the driver detects it and enumerates it on USB.\n\nIf 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.\n\nThe only clue I get is the `usbdevs -v`; it shows a device number and serial number information.\n\n```\nvk4msl-nbsd# usbdevs -v\nController /dev/usb0:\naddr 0: super speed, self powered, config 1, xHCI root hub(0x0000), NetBSD(0x0000), rev 1.00(0x0100)\n port 1 disabled\n port 2 disabled\n port 3 disabled\n port 4 disabled\nController /dev/usb1:\naddr 0: high speed, self powered, config 1, xHCI root hub(0x0000), NetBSD(0x0000), rev 1.00(0x0100)\n port 1 powered\n port 2 addr 17: full speed, power 100 mA, config 1, USB-Serial Controller D(0x2303), Prolific Technology Inc.(0x067b), rev 4.00(0x0400)\n port 3 addr 1: low speed, power 100 mA, config 1, CASUE USB KB(0x6a21), vendor 2a7a(0x2a7a), rev 0.01(0x0001)\n 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\n port 5 powered\n port 6 powered\n port 7 addr 3: full speed, self powered, config 1, product 0a2a(0x0a2a), Intel(0x8087), rev 0.01(0x0001)\n port 8 powered\n port 9 powered\n port 10 powered\n port 11 powered\n…\n```\n\nlacking is the knowledge that there are *two* devices allocated to device 18 (the test rig).\n\nGiven 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.",
"sig": "c2713b59b81765fe808605b4c83efdfcbc6240421f59e430516d5a8b3265e6d3dbfeb193db7101e357d19868fce12eb49273c93ee32900f422d2675487c36a5e"
}