UART symlink fails with udev rules

This is more than you are asking about, but I thought I’d add the information in examining the device for sym linking that might be useful for people researching udev sym linking on a Jetson. Unfortunately, I did not find a final answer, but I did find rules that would identify /dev/ttyTHS0 and match it and attempt to run. I think maybe the driver needs some edits. This big long post is the process I went through before getting to the point that I need to open another thread specific to the driver setup. I’ll refer back and forth between the two threads:
https://forums.developer.nvidia.com/t/serial-driver-for-ttyths-udev-issues-does-driver-need-edit/306974


About Identifying devices…

Is it correct that it is ttyTHS0 you want changed? If so, then you would not use this:

KERNEL=="/dev/ttyTHS0*"

…you’d just use:

KERNEL=="/dev/ttyTHS0"

(the difference is no asterisk, *, at the end of ttyTHS0)

If you were naming all ttyTHS#, then it would be:

KERNEL=="/dev/ttyTHS*"

(without the 0)

Since it is only adding symbolic links, I don’t think that would cause any problem.

I want to point out a file that could be used as a kind of example:

/usr/lib/udev/rules.d/60-persistent-input.rules
 udevadm test -a add

You’ll find that it is standard practice on Ubuntu to create symbolic links of all of your mouse, joystick, and some similar devices. I’ll give you an example on my desktop PC (I currently have udevadm info -q path -nmy Orin without keyboard or monitor or mouse), located here:
/dev/input/by-id/*

There is also a “/dev/input/by-path/*”. The ones that show up depend on what devices are actually plugged in, and so if I mention mouse or other example, it is only because that’s what is on my desktop Ubuntu (you’d have to make name edits for your case). I’m looking at this:
/dev/input/by-id/usb-Thrustmaster_T.Flight_Hotas_X-joystick

The actual file behind this sym link:

# ls -l /dev/input/by-id/usb-Thrustmaster_T.Flight_Hotas_X-joystick
/dev/input/by-id/usb-Thrustmaster_T.Flight_Hotas_X-joystick -> ../js1 udevadm test -a add

So this means there is a rule somewhere matching the /dev/input/js1 device, and it creates a sym link to:
/dev/input/by-id/usb-Thrustmaster_T.Flight_Hotas_X-joystick udevadm info -q path -n

If I look closer at the js1 file, I see:

# cd /dev/input
# ls -l js1
crw-rw-r--+ 1 root input 13, 1 Sep 16 11:08 js1

You’ll note it is major device number 13, minor device 1. This is something that can be used in udev rule matching. If I were to investigate the sym link with this, or the non-sym link file, then it should lead to the same place:
udevadm info -q path -n /dev/input/by-id/usb-Thrustmaster_T.Flight_Hotas_X-joystick
udevadm info -q path -n /dev/input/js1

On my desktop PC the above shows up as:

/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:08:00.1/usb1/1-5/1-5.3/1-5.3.4/1-5.3.4.3/1-5.3.4.3:1.0/0003:044F:B108.000C/input/input14/js1

Trivia: It seems the USB controller itself is on a PCI bus.

Do you remember earlier when I mentioned the underlying device is major 13, minor 1 (I should also mention that serial devices are “character” devices)? There will be a matching /sys directory entry under this directory:
/sys/dev/char/

The related file:
/sys/dev/char/13:1

This file points at:

# ls -l /sys/dev/char/13:1
13:1 -> ../../devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:08:00.1/usb1/1-5/1-5.3/1-5.3.4/1-5.3.4.3/1-5.3.4.3:1.0/0003:044F:B108.000C/input/inputudevadm info -q path -n14/js1/

(the base of that is “/sys/devices/”, and it ends up going back to the earlier command output from udevadm info -q path -n; the actual js1 is a subdevice of a PCI controller that has a USB controller)


Note: It is better to quote “$(udevadm info -q path -n /dev/ttyTHS0)” when embedding it for substitution. There might be spaces in paths.

Back to: udevadm test -a add $(udevadm info -q path -n /dev/ttyTHS0); use this quoted version instead:
udevadm test -a add "(udevadm info -q path -n /dev/ttyTHS0)"

On an Orin AGX (which might differ from your NX), and trimming some output not relevant:

# udevadm test -a add "(udevadm info -q path -n /dev/ttyTHS0)"
Load module index
Network interface NamePolicy= disabled on kernel command line, ignoring.
Parsed configuration file /usr/lib/systemd/network/99-default.link
Parsed configuration file /usr/lib/systemd/network/73-usb-net-by-mac.link
Created link configuration context.
Reading rules file: /etc/udev/rules.d/10-nv-jetson-dm.rules
Reading rules file: /usr/lib/udev/rules.d/39-usbmuxd.rules
Reading rules file: /usr/lib/udev/rules.d/40-usb-media-players.rules
...snip...
Reading rules file: /usr/lib/udev/rules.d/85-regulatory.rules
Reading rules file: /etc/udev/rules.d/90-alsa-asound-tegra.rules
Reading rules file: /usr/lib/udev/rules.d/90-alsa-restore.rules
...snip...
Reading rules file: /etc/udev/rules.d/99-tegra-mmc-ra.rules
Failed to open device '/sys(udevadm info -q path -n /dev/ttyTHS0)': Invalid argument
Unload module index
Unloaded link configuration context.
This program is for debugging only, it does not run any program
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.

What is interesting in this:

  • Most rules are loaded from those in “/usr/lib/udev/rules.d”. These are part of .deb packages.
  • Some rules read for check are in “/etc/udev/rules.d/”. Files in “/etc/udev/rules.d/”, if they have the same name as a “/usr/lib/udev/” file, take precendence. The correct place for customization is in “/etc/udev/rules.d/”; if the rule is new, use a new name following the naming customs. If the rule is an edit of an existing rule, then copy the “/usr/lib/udev/” file to “/etc/udev/rules.d/” and make edits there.
  • If you can identify a similar rule in “/usr/lib/udev/”, then you have a template.
  • I did not find any ttyTHS rules. This is because the ttyTHS# is the device special file that the driver itself has produced, and there are no rules added anywhere (by default) to either sym link to this or to rename it.
  • The ttyS# files use the same underlying hardware as the ttyTHS# files, this is an unusual case where both a legacy driver and a newer driver are both compatible with the hardware, and either can be used. Identifying the ttyS# though is quite valuable:
# cd /usr/lib/udev/rules.d/
# egrep -R ttyS *
60-inputattach.rules:SUBSYSTEM=="tty", KERNEL=="ttyS[0-9]*", ATTRS{id}=="FUJ02e5", ACTION=="add|change", RUN+="/usr/bin/inputattach --daemon --baud 19200 --w8001 /dev/%k"
60-inputattach.rules:SUBSYSTEM=="tty", KERNEL=="ttyS[0-9]*", ATTRS{id}=="WACf00c", ACTION=="add|change", RUN+="/usr/bin/inputattach --daemon --baud 38400 --w8001 /dev/%k"
69-wacom.rules:# If a /dev/ttySx device with the WACf name is detected, start the
69-wacom.rules:SUBSYSTEM=="tty|pnp", KERNEL=="ttyS[0-9]*", ATTRS{id}=="WACf*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="wacom-inputattach@%k.service"

About those udev files for the legacy ttyS#:

  • The Wacom tablet is an ordinary “mouse-like” device in the sense that it provides a series of bytes describing where your pen is moving on the table, and it outputs via an ordinary serial UART. User space software might benefit from renaming this or adding sym links when the Wacom tablet is present. This is not from the Jetson UARTs, and so it won’t trigger unless the Wacom is plugged in.
  • It seems there are some other devices that use a standard UART, and they have an identifying attribute that would show up in a verbose “sudo lsusb -vvv /dev/<whatever the driver file is for this device>”.
  • The “KERNEL==ttyS[0-9]” looks like a regular expression, and “[0-9]” matches anything between 0 and 9.
  • Adapting this to include all ttyTHS# (only one #; going into double digits of 10 or up differs):
    KERNEL==ttyTHS[0-9]
  • I assume you want only ttyTHS0, and not all ttyTHS#. So we’d change the regular expression to 0:
    KERNEL==ttyTHS0

You can use different combinations of identifiers to “drill down” and get a smaller list of items. Maybe the “KERNEL==ttyTHS0” is sufficient to identify this one UART without others getting mixed in. I think probably that is all you need to identify it. Some of what is identified in the ttyS[0-9] example might be for editing, and not necessarily for identifying.

Just to be clear, the built-in UARTs on the Jetson (which includes the ttyS[0-3] and the ttyTHS*) are not USB devices. You cannot use USB identifiers. In fact, since ttyTHS0 is not hot plug at all (it is found by drivers via the device tree), I don’t know if a udev rule will be able to edit it. I think udev can edit if (A) the device is identified properly in the trigger rule, and (B) something causes the rule to be read at boot (there is no hot plug event, so it cannot generate a plug-in event). The rules from the ttyS# apply to external devices that plug in via USB. Still, they are good examples, and so now you have to make sure you have whatever identification you can get.

I will say ahead of time that if you look at the “ls -l /dev/ttyTHS0” output, the “group” is “dialout”. I’m not positive, but I’m fairly certain that the SUBSYSTEM=="tty" can be edited like this to aid in ID:
SUBSYSTEM="dialout"
(maybe…it can be tried without, and then when it triggers, see if that can be added back in; it is important to be as specific as possible in the ID in case of future hardware changes, including USB devices)

As an aid to identification of ttyTHS0:

# udevadm info -q all -a -n /dev/ttyTHS0

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/platform/3100000.serial/tty/ttyTHS0':
    KERNEL=="ttyTHS0"
    SUBSYSTEM=="tty"
    DRIVER==""
    ATTR{line}=="0"
    ATTR{xmit_fifo_size}=="36"
    ATTR{io_type}=="3"
    ATTR{console}=="N"
    ATTR{close_delay}=="50"
    ATTR{custom_divisor}=="0"
    ATTR{port}=="0x0"
    ATTR{closing_wait}=="3000"
    ATTR{iomem_reg_shift}=="2"
    ATTR{uartclk}=="0"
    ATTR{iomem_base}=="0x3100000"
    ATTR{flags}=="0x0"
    ATTR{type}=="20"
    ATTR{irq}=="13"

  looking at parent device '/devices/platform/3100000.serial':
    KERNELS=="3100000.serial"
    SUBSYSTEMS=="platform"
    DRIVERS=="serial-tegra"
    ATTRS{driver_override}=="(null)"

  looking at parent device '/devices/platform':
    KERNELS=="platform"
    SUBSYSTEMS==""
    DRIVERS==""

For my AGX Orin, this is PCI identifier that might be useful:
`/devices/platform/3100000.serial/tty/ttyTHS0’

This does say that subsystem is not dialout, but is instead tty, and some other useful information:

KERNEL=="ttyTHS0"
SUBSYSTEM=="tty"

Do you see all of those “ATTR{}”? Those can maybe aid identification:

    ATTR{line}=="0"
    ATTR{xmit_fifo_size}=="36"
    ATTR{io_type}=="3"
    ATTR{console}=="N"
    ATTR{close_delay}=="50"
    ATTR{custom_divisor}=="0"
    ATTR{port}=="0x0"
    ATTR{closing_wait}=="3000"
    ATTR{iomem_reg_shift}=="2"
    ATTR{uartclk}=="0"
    ATTR{iomem_base}=="0x3100000"
    ATTR{flags}=="0x0"
    ATTR{type}=="20"
    ATTR{irq}=="13"

(but some are more useful than others; the IRQ might change from one boot to the next, some attributes might also move, so we are interested in a subset)

Of the above, I think we should ignore the ATTR{}, but on some devices those ATTR{} might be constant, and so might help in other cases (or perhaps we might need to edit them).


A Proposed Sample Rule…

I’m going to create this file:
/etc/udev/rules.d/tty-ths0.rules

Initially, I have this (you’d have to adjust for your PCI id), which isn’t quite correct, but is a start:

# Symbolic link from Jetson file /dev/ttyTHS0 to /dev/mys

SUBSYSTEM=="tty", KERNEL=="/dev/ttyTHS0", ACTION=="add|change", SYMLINK+="mys"

If I now run “udevadm test /dev/ttyTHS0”, I get this error at the end, but it proves the rules were read and there was an attempt to work with it:

...snip...
Reading rules file: /etc/udev/rules.d/tty-ths0.rules
Failed to open device '/sys/dev/ttyTHS0': No such device
Unload module index
Unloaded link configuration context.

(there is an advantage in debug if the file loaded for test is the last one; it’s already at the bottom of the terminal scroll!)

At this point the rule is almost there, but I don’t know if there will be a simple workaround. In theory the /dev/ttyTHS driver should also create “/sys/dev/ttyTHS0”, in addition to “/dev/ttyTHS0”, but I’m not sure who is responsible for the /sys/dev/ entry creation. There are some ttyTHS0 files in /sys, but they are at different locations. We have to get udev to work with the correct /sys content or it will fail.

So I am going to switch gears here, and instead see if I can operate on a /sys file instead of the /dev/ file. They refer to the same device and driver. But can udev use /sys directly? We’ll find out…


Check the output of:

cd /sys
sudo find . -name 'ttyTHS0'

New /etc/udev/rules.d/tty-ths0.rules:

# Symbolic link from Jetson file /dev/ttyTHS0 to /dev/mys
SUBSYSTEM=="tty", DRIVERS=="serial-tegra", KERNEL=="/dev/ttyTHS0", KERNELS=="3100000.serial", ACTION=="add|change", SYMLINK+="mys"

From what I can tell in the “sudo udevadm test /dev/ttyTHS0” of the above tty-ths0.rules, the rules do in fact trigger, and the attributes listed for ID are quite specific (though you could leave out `KERNELS=="3100000.serial’…this would change when the device tree changes for different models of carrier boards or modules). I keep hitting this error:

Reading rules file: /etc/udev/rules.d/tty-ths0.rules
Failed to open device '/sys/dev/ttyTHS0': No such device

I’m pretty sure this rule is correct for finding the device, but something needs to create “/sys/dev/ttyTHS0”. I’m having problems finding out what is needed to create that file. It seems that the driver itself might need to set up some metadata for udev to be able to work with this. Sadly, I do not know enough about how udev relates to /sys/dev/ to answer this right now. I’m going to start a new thread related to just that question.

Alternate thread URL restated here:
https://forums.developer.nvidia.com/t/serial-driver-for-ttyths-udev-issues-does-driver-need-edit/306974