A20 Micro : force physical/logical UARTx/ttySx symmetry

Started by wirlo, January 22, 2016, 01:50:14 PM

Previous topic - Next topic

wirlo

Hi folks,

Using an A20 Olinuxino Micro 4GB board, with Debian 8.2 (latest Olimex image) and Sunxi kernel 3.4.104 (self-compiled, so I can hack it if needed).

Sorry if this is a basic question, but I didn't find documentation on that particular matter.

I noticed that when it comes to UARTs, the kernel registers the active hardware devices in sequence as /dev/ttySx. Eg if only UARTs 0 and 4 are active, it will register them respectively as /dev/ttyS0 and ttyS1.
If I also enable UART3, it will be registered as ttyS1 and UART4 will shift and become ttyS2.

For me and my application this is a future backward-compatibility mess waiting to happen: right now I can only identify some UARTs I need to use, and I can't tell whether in the future I'll need eg. UART2 or SPI1 (both sharing the same processor pins PI16 to PI19 since I need ethernet) so I'm forced to leave those unused devices disabled for now.

Problem is, if future me needs to use UART2 it will change the ttySx device names of UARTs 3/4, creating a maintenance hell for cards already in production (I need to be able to run newer software on older hardware, even if the software must degrade gracefully due to missing peripherals on the older boards).

The thing is, I2C ports don't have this problem: currently I only have TWI 3/4 enabled and they register as /dev/i2c-3 and i2c-4. If I enable TWI2 it correctly registers as i2c-2 without interfering with the TWI 3/4 device names.

So I guess this should be possible to do the same with serial ports: force the kernel to register UART0 as ttyS0, UART3 as ttyS3 etc.

Any idea how to achieve that at the kernel level?

Of course there's always the solution to add an indirection in my application (somehow detect the hardware configuration, and deduce the hardware / linux device correspondence) but this is not really clean and I'd rather avoid it.

Thanks for your attention.

JohnS

You will probably move to kernel 4.x so read up on DT (DeviceTree) and see how to do what you want.

John

wirlo

Thanks for the info John.

I have to admit I've been a bit reluctant to use the mainline kernel since, from what I gathered, hardware support is still incomplete, and I'm too much of a newbie on this board family to fully understand the implications of switching to mainline. That's why I played it safe until now and used the legacy kernel.

Nevertheless I will follow your suggestion and look into both the mainline kernel and device trees.

Anyone with a solution to achieve the same using the legacy kernel is welcome, though. :)

Gerrit

You can make udev rules to get persistent names for the uart

create

/etc/udev/rules.d/80-persistent-uart.rules

SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.0", NAME="ttyS0"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.1", NAME="ttyS1"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.2", NAME="ttyS2"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.3", NAME="ttyS3"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.4", NAME="ttyS4"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.5", NAME="ttyS5"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.6", NAME="ttyS6"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.7", NAME="ttyS7"


check with

udevadm info -a -n /dev/ttyS0
udevadm info -a -n /dev/ttyS1
.
.
.
.
udevadm info -a -n /dev/ttyS7


if the uart is attached to the right device

JohnS

For kernel 3.x the fex file can also play a part.

What I don't know is what happens to the "sunxi-uart.N" if you reallocate the function of the pins involved (i.e. uart N becomes some other non-uart device).  Does the ttySN not exist (so, a gap in the ttySN values) or do the others get renumbered or what?

John

wirlo

Thanks a lot Gerrit, this is a pretty neat idea to use udev to coerce the device names, but unfortunately it doesn't work as expected.

After verification, I found that the sunxi-uart module is built-in into the kernel and enabled very early in the boot process (around 0.7s according to dmesg), much earlier than even the SD card containing the rootfs (which happens between 3.0s and 4.8s). That makes sense, if one wants to have the debug console on ttyS0 during boot. Since the device names are assigned way before udev is started, it never gets the chance to do its job.

Right now I can see two ways to work around this:

1) Build sunxi-uart as an external module, and delay its loading until after udev has started. But I'll lose the debug console during the early boot sequence, which is a hard choice since that serial console has been a life-saver in the past on other boards to help troubleshoot in production...

2) I have read somewhere that we can reload a script.bin (and thus reconfigure the hardware) once the full Debian system is up and running. The idea would be to load a basic script.bin at boot time, with only ttyS0 enabled to be able to access the debug console, and later on once udev is started reload a fully-fledged script.bin with all the UARTs I actually need, which should now fall under the control of udev... provided the sunxi-uart driver correctly detects the new definitions.

I'll do some more research and keep you people informed. In the meantime, any thoughts or ideas are, of course, quite welcome.


@JohnS: Good point, as this could have rendered the whole topic moot. So I tested it and sunxi-uart.X does indeed map to the corresponding hardware port, if enabled, and irrespective of the other enabled ports. If you disable or even reassign the pins to another function, the corresponding sunxi-uart.X simply disappears from the kernel without affecting or renumbering the others.

Gerrit

Quote from: wirlo on January 25, 2016, 05:31:04 PM
Thanks a lot Gerrit, this is a pretty neat idea to use udev to coerce the device names, but unfortunately it doesn't work as expected.

After verification, I found that the sunxi-uart module is built-in into the kernel and enabled very early in the boot process (around 0.7s according to dmesg), much earlier than even the SD card containing the rootfs (which happens between 3.0s and 4.8s). That makes sense, if one wants to have the debug console on ttyS0 during boot. Since the device names are assigned way before udev is started, it never gets the chance to do its job.


As far as i tested it udev does reassign the uart to the new device and also keeps at at the old device as long as it not gets assign to an other uart, if you want you can use other names also like

SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.4", NAME="ttyUART4"


wirlo

Quote from: Gerrit on January 25, 2016, 05:56:48 PM
As far as i tested it udev does reassign the uart to the new device and also keeps at at the old device as long as it not gets assign to an other uart, if you want you can use other names also like

SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.4", NAME="ttyUART4"

That's quite weird: when I changed the device names as you suggest, the new ttyUARTx names didn't appear in /dev as they should have.

After some fiddling, it appears that my udev somehow doesn't want to process the NAME rule, as if it wasn't able to reassign the names (I couldn't really figure out why, that's baffling as it works on your side). If I use SYMLINK instead of NAME then it works fine...
The only small limitation, of course, is that the ttySx names will still change whenever the hardware configuration changes. But that's not a real problem since now I have an alternate set of symlinks (ttyUARTx) that stays stable thanks to your udev rules, my goal is met.

So for future reference here's what I'm using right now.

# cat /etc/udev/rules.d/80-persistent-uart.rules
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.0", SYMLINK+="ttyUART0"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.1", SYMLINK+="ttyUART1"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.2", SYMLINK+="ttyUART2"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.3", SYMLINK+="ttyUART3"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.4", SYMLINK+="ttyUART4"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.5", SYMLINK+="ttyUART5"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.6", SYMLINK+="ttyUART6"
SUBSYSTEM=="tty", DRIVERS=="sunxi-uart", KERNELS=="sunxi-uart.7", SYMLINK+="ttyUART7"

# ll /dev/ttyUART*
lrwxrwxrwx 1 root root        5 2010-01-01 00:00 /dev/ttyUART0 -> ttyS0
lrwxrwxrwx 1 root root        5 2010-01-01 00:00 /dev/ttyUART5 -> ttyS1
lrwxrwxrwx 1 root root        5 2010-01-01 00:00 /dev/ttyUART7 -> ttyS2


Adding UART2 to the FEX file, it behaves as expected:
# ll /dev/ttyUART*
lrwxrwxrwx 1 root root 5 2010-01-01 00:00 /dev/ttyUART0 -> ttyS0
lrwxrwxrwx 1 root root 5 2010-01-01 00:00 /dev/ttyUART2 -> ttyS1
lrwxrwxrwx 1 root root 5 2010-01-01 00:00 /dev/ttyUART5 -> ttyS2
lrwxrwxrwx 1 root root 5 2010-01-01 00:00 /dev/ttyUART7 -> ttyS3


As far as I'm concerned the case is closed, even though I'm still a bit curious about that udev behaviour difference regarding the NAME rule.

Many thanks to both of you, especially Gerrit.
I'll investigate John's suggestion to switch to the mainline kernel, as soon as I can dedicate enough time to it.