Opening one of the 3 uarts on verdin 8MPlus

I am using a Verdin 8MPlus SOM and using Torizoncore 6. I am trying to do a simple open of any of the 3 uart ports on it, verdin-uart1, verdin-uart2, or verdin-uart3. I have added them to my list of devices in docker-compse.yml and can see them in the container. However, if I use the standard open of:
fd = open(portname, O_RDWR | O_NOCTTY | O_NDELAY);

They all fail with -1. Is there something else I need to do to be able to open these?

Also, in some of the documentation, it states that there is a ttyusb device, which I don’t seem to have and that the UART4 is tied to the Cortex-M core, but then in other places is says it is uart2.

Thanks,
Steve

Greetings @Evets,

Could you give more details on your setup? Including code, your compose file, and other pertinent information.

I just created a small test app that’s basically just a single line fd = open() targeting one of the free UARTs and it worked without any error. I mean it’s just doing an open() and nothing else but it didn’t fail with -1 for me.

Best Regards,
Jeremias

Well, I am running a lot more code than just that. I am running in C++, and have SPI and the CAN bus(es) running, all of which is working.
What is your open command?
deviceName
Here is my SerialClass:: Initialize() function that opens the port:
int SerialClass::Initialize(string deviceName, int bits, int stop, int parity)
{
pSerialClass = this;
m_deviceName = deviceName;
m_bits = bits;
m_stop = stop;
m_parity = parity;

m_fd = open(deviceName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (m_fd == -1)
{
    char buf[50];
    sprintf(buf, "open_port: Unable to open %s\n",m_deviceName.c_str());
    perror(buf);
    return(-1);
}
return(0);

}
My .yml file:
version: “3.9”
services:
gimbal3bcontainer-debug:
build:
context: .
dockerfile: Dockerfile.debug
image: ${LOCAL_REGISTRY}:5002/gimbal3bcontainer-debug:${TAG}
network_mode: “host”
cap_add:
- NET_ADMIN
devices:
- “/dev/spidev1.0”
- “/dev/spidev2.1”
- “/dev/gpiochip0”
- “/dev/gpiochip1”
- “/dev/gpiochip2”
- “/dev/gpiochip3”
- “/dev/gpiochip4”
- “/dev/gpiochip5”
- “/dev/verdin-uart1”
- “/dev/verdin-uart2”
- “/dev/verdin-uart3”
ports:
- 2230:2230

gimbal3bcontainer:
build:
context: .
dockerfile: Dockerfile
image: ${DOCKER_LOGIN}/gimbal3bcontainer:${TAG}
network_mode: “host”
devices:
- “/dev/spidev1.0”
- “/dev/spidev2.1”
- “/dev/gpiochip0”
- “/dev/gpiochip1”
- “/dev/gpiochip2”
- “/dev/gpiochip3”
- “/dev/gpiochip4”
- “/dev/gpiochip5”
- “/dev/verdin-uart1”
- “/dev/verdin-uart2”
- “/dev/verdin-uart3”

Is there something I’m missing here?

My code is just this:

int main(int argc, char *argv[])
{
    int fd;
    int flags = O_RDWR | O_NOCTTY | O_NDELAY;

    fd = open("<PATH TO UART FILE>", flags);
    printf("fd = %d", fd);
}

I’m just checking the return state of open() since that’s what you said was giving an error. With this the return value of fd I get is a positive integer which indicates success. I only get a -1 if I try to open some non-existing file-path like /dev/verdin-uart200 or something like that.

This code is just a shortened version of the sample code found here: UART (Linux) | Toradex Developer Center

Does your open() work if isolated from the rest of your code?

Best Regards,
Jeremias

Hi @jeremias.tx ,
I haven’t tried that yet. I am curious, are you giving the full path for the name? i.e. “/dev/verdin-uart1”?
I have tried that as well, but it isn’t what I’ve had to do in the past under Linux.

I tried your code and for both /dev/verdin-uart1, /dev/verdin-uart2 and /dev/verdin-uart3, and they all fail. Are you using a verdin dev board?

What is your docker-compose.yml file and what are your #includes?

Steve

Use this serial port library

Get one of the examples to work in your environment.

I used that in the last Toradex based project I worked on. Once you get a port to read and write via an example, use that code in your program.

@seasoned_geek
Thanks, I will try this, but I think the problem is more basic than that. Even a basic serial port open fails. That shouldn’t happen unless they are already opened by something else. I’ve already checked to see if they were open, but nothing shows up.

Steve

A docker world is an ugly thing.

On a standard Linux desktop like Ubuntu a user must be a member of group dialout to use a serial port. In your custom build did you forget to add toradex user (or whatever user you created) to dialout?

@seasoned_geek
Interesting. Nothing in the online docs about that. I am running as root now, but that didn’t make any difference. Would that be the case? root is not a member of dialout?

Steve

Are you certain? If you are running inside a docker container as part of the Torizon/Toradex development environment you are most certainly not running as root. You are running as the Torizon user. (I forget the exact name, either check the doc or spew the output of “who”, “whoami”, echo $USER to some place you can see. That user will most definitely need to be part of dialout

You also need to check and be certain you aren’t conflicting with whatever port getty and/or mgetty is using.

If you can connect a “terminal” via RS-232 and a null modem cable to your board, hit enter, and get a login prompt, there is some form of getty running. Without that process you do not get a login prompt from a serial port.

I haven’t tried that yet. I am curious, are you giving the full path for the name? i.e. “/dev/verdin-uart1”?

Yes I’m just using the full path here in open().

I tried your code and for both /dev/verdin-uart1, /dev/verdin-uart2 and /dev/verdin-uart3, and they all fail. Are you using a verdin dev board?

Yes I’m using the exact same hardware you have and I tried all 3 uarts and open() never returned a -1 for me.

What is your docker-compose.yml file and what are your #includes?

Well my compose file is really simple just adding the UART devices:

version: "3.9"
services:
  test-debug:
    build:
      context: .
      dockerfile: Dockerfile.debug
    image: ${LOCAL_REGISTRY}:5002/test-debug:${TAG}
    ports:
      - 2230:2230
    devices:
      - "/dev/verdin-uart1:/dev/verdin-uart1"
      - "/dev/verdin-uart2:/dev/verdin-uart2"
      - "/dev/verdin-uart3:/dev/verdin-uart3"

  test:
    build:
      context: .
      dockerfile: Dockerfile
    image: ${DOCKER_LOGIN}/test:${TAG}

As for my includes there’s nothing special there, in fact I probably included more things than needed since I started with a bigger code example and slimmed it down:

#include <stdio.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

I really don’t know why it’s not working for you I didn’t do anything else outside of what I showed here. Unless you modified your system in some way that would affect this, but I would have no way of knowing.

You are running as the Torizon user. (I forget the exact name, either check the doc or spew the output of “who”, “whoami”, echo $USER to some place you can see. That user will most definitely need to be part of dialout

If you’re using a container derived from our base debian containers, which you should be if using our extension then we add the torizon user to this group already as seen here: https://github.com/toradex/torizon-containers/blob/bookworm/debian-docker-images/base/users-groups.sh

In my test I didn’t have to add any user to any user-group. It works with both the torizon user and root user on vanilla Torizon software.

Best Regards,
Jeremias

I changed the “torizon_run_as”: “root”, and I’ve checked in the containter that I am root. I did get the thing to open the serial port (finally!) in my test program after some fiddling, so now I will see if I can get it to run in my actual program.

Last comment I will make on this.

I strongly suggest you do not run as root inside a docker container.

I cannot emphasize enough that you should use the free serial port library I linked earlier in this discussion and stop trying to treat serial I/O as “just another file.” Been at this over 30 years. Started doing serial communications under MS DOS with 640K and 16-port DigiBoard add-in cards. There are no shortcuts with serial communications. No matter what you assume to be true about how a port is configured by default, it will be false at least some of the time on the same device with the same software because ports get left in funky states. The course you are on is a trail of tears that will have you living on this board asking for help.

There, I’ll put my soap box away now, I’m done.

@seasoned_geek ,

Yes, I agree! Unfortunately, I am also using CAN bus with this application, and it requires to be root to run! Who the heck thought that was a good idea?

Yes, I am using your serial port library you sent. I like it! Thank you!!

Steve

@Evets For information sake are you going to share what you did here to get it working with the original open() method?

I did get the thing to open the serial port (finally!) in my test program after some fiddling

It could be helpful to others.

By the way regarding this serial port library, it seems to use open() under the hood anyways when accessing the UART device: https://github.com/gbmhunter/CppLinuxSerial/blob/master/src/SerialPort.cpp#L142

So I don’t think it’s doing anything dramatically different on this regard.

Best Regards,
Jeremias

Your assessment of CAN is incorrect.

permissions - How to allow I2C access for non-root users? - Raspberry Pi Stack Exchange.

Jeremias,

The difference with the library is the class solid values for BAUD, bits, stop, flow control established when the class is instantiated.

When you “just call open” you get the port in whatever state it was left in by the last process. This actually isn’t to you, but is part of this conversation.

I didn’t read all the way through that, but I assume there is a Yocto/BSP method of allowing CAN bus access in a docker container without running as root inside of the container.

You should never run as root inside a container.

Your assessment of CAN is incorrect.
permissions - How to allow I2C access for non-root users? - Raspberry Pi Stack Exchange .

You linked to an external discussion bout I2C? I don’t see what this has to do with CAN bus.

The difference with the library is the class solid values for BAUD, bits, stop, flow control established when the class is instantiated.
When you “just call open” you get the port in whatever state it was left in by the last process. This actually isn’t to you, but is part of this conversation.

Again incorrect. If you look at this SerialPort library you are advertising it does not do what you just said. In the Open() function of this library it literally runs the standard open() then it sets the BAUD, bits, and other configurations after running open(). It even uses termios to set the configurations, which is what every other example does. This library is not doing anything revolutionary or different from standard UART examples.

When this class gets instantiated it doesn’t set BAUD, bits, and other configs at that time it just stores these values into class variables on instantiation. It’s only when you run Open() will this library actually use these values to configure the serial port after opening it. Which is what everyone else does.

I didn’t read all the way through that, but I assume there is a Yocto/BSP method of allowing CAN bus access in a docker container without running as root inside of the container.

Since CAN is a network interface in Linux it typically requires higher permissions to work with. Typically this would mean things like iproute2 since NetworkManager can’t configure CAN. Which means using root level permissions unless you want to hack the default permission settings of iproute2 to allow non-root users. This is something we state here: https://developer.toradex.com/torizon/torizoncore/best-practices/torizon-best-practices-guide/#exceptions-when-you-must-run-as-root-inside-the-container

If you know of an alternate method that would not require this feel free to suggest such.

Best Regards,
Jeremias

1 Like

@jeremias.tx ,
Yes, I understand this. However, I just used that library, and it worked. So, it must be something in what files it brings in. I don’t understand it yet, but I’ve got lots of other things to do, so I’ll have to try to figure it out later. Sorry!!

Okay then we can just conclude this issue as “solved” in that case. Keep in mind we can’t reasonably support you with any issues regarding 3rd party libraries like this.