Error 25 (Inappropriate ioctl for device) when setting up RS-485 config
Linux - Embedded & Single-board computerThis forum is for the discussion of Linux on both embedded devices and single-board computers (such as the Raspberry Pi, BeagleBoard and PandaBoard). Discussions involving Arduino, plug computers and other micro-controller like devices are also welcome.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Error 25 (Inappropriate ioctl for device) when setting up RS-485 config
Hi,
I have code similar to the following which I am trying to use to set up my serial port (/dev/ttyAPP1) for use as 2-wire RS-485:
Code:
#include <sys/ioctl.h>
#include <linux/serial.h>
... other includes ...
int fd;
struct serial_rs485 rs485config;
fd = open("/dev/ttyAPP1", O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0)
{
// Code to handle error
}
rs485config.flags |= SER_RS485_ENABLED;
if (ioctl(fd, TIOCSRS485, &rs485config) < 0)
{
fprintf(stderr, "Failed to execute ioctl, error %d\n", errno);
}
// Now do write/read of serial device ...
The code is compiling OK, so presumably must be resolving links to the relevant ioctl reference TIOCSRS485 and to the rs485 conhfiguration structure. However, when the code executes, I'm getting:
Failed to execute ioctl, error 25
Environment is GNU/Linux 3.10.28 on armv5, ELDK v5.5. The UART in question (AUART1) is built into the Freescale CPU. I think that is the only extra info I can provide. Unfortunately, I've no real experience of Linux programming at or near the kernel level, so I have no idea why I'm getting the error 25, what it *really* means, and what I might be missing if anything (e.g. do I need a specific device driver, how do I know if my serial.h and ioctl.h headers are correct, how do I know if my Linux kernel is correctly configured, etc.).
I know for sure that /dev/ttyAPP1 is the correct filename for the port.
Sorry not to be able to add much more intelligence to this; my programming skills lie in Java, so if you do feel able to respond to me, please treat me as a newbie in this specific area and spell things out for me, if you'd be so kind (even if we have to take this one step at a time!).
Hoping someone out there can give me a helping hand.
> I know for sure that /dev/ttyAPP1 is the correct filename for the port.
You need to double check that.
The error number 25 is ENOTTY, or "Not a typewriter", e.g. not a serial terminal device.
The error number 25 is also a substitute for ENOIOCTLCMD, "No ioctl command".
Your effort to use this ioctl could be futile because it may not be implemented in that driver.
There are scores of serial port drivers in the Linux kernel, but only a few supported that RS-485 API before kernel version 3.19.
Examine the boot log to get the name of the serial port driver (perhaps mxs-auart).
Then examine the driver source code to confirm the existence (or not) of the RS-485 feature.
FWIW in 3.10 mainline, only atmel_serial, crisv10 (ETRAX), and mfc (Freescale Coldfire) have that RS-485 support.
The reason I say /dev/ttyAPP1 is the correct device needs a little explanation.
The previous code that was handling the 2-wire RS-485 traffic on this port was working of a fashion -- but unreliably (not written by me!). Our controller allocates two specific GPIO pins as responsible for enabling/disabling the line transmitter & receiver. The logical steps of the previous write() routine used were as follows:
a - Disable line receiver
b - Enable line transmitter
c - Do the writing
d - Disable line transmitter
e - Enable line receiver (so port can now receive)
However, I noticed that a good half of the packets (only about 8-byte Modbus messages) that we wrote weren't being responded to correctly. we put the device on an oscilloscope and noticed that, in this routine, the turnaround of the line was nowhere near instantaneous (but certainly should be). There would often be upto 10ms delays whilst waiting for the turnaround either before or after writing data; during these delays, the remote device would start responding to our write() before we were ready to receive. Thus the bit train we received would be corrupted quite often.
When I put these findings to another couple members of my team, they were pretty sure they could see the issue straight away: in steps a, b, d & e, we were flipping the GPIO pins by opening a file descriptor to '/sys/class/gpio/gpioXXX/value' and then writing a 0 or 1 to it (as appropriate). Functionally that is correct, but it appears that, some of the time, the kernel is busy doing other work and so there's a delay returning from those steps. They hold that the turnaround of the lines should take place much closer to the kernel, and the accepted wisdom is to do this by using the ioctl rs485 memory structure & calls. Hope this all makes sense ?
So now you can see why & how I've attempted to fix it -- except that we've gone from poorly working comms to none at all! However, the unreliable comms was definitely using /dev/ttyAPP1 so the device name is correct in that sense -- although I appreciate what you are suggesting about the device driver quite probably not recognising the ioctl call, and/or /dev/ttyAPP1 not being recognised as a TTY device. In my previous post I only stated the description of error 25 based on what perror() gave me; understand what you say about the ambiguity.
Incidentally, if I understand the electronics correctly (and I'm not experienced in that area), there is usually both a UART on-board the CPU as well as a separate line driver chip for converting signals into those suitable for 232 and 485 usage. Is that correct ? If so, I think the line driver in our case is based on the Linear Tech Corp 2870 chip. Don't know if that helps.
If I do manage to ascertain which serial driver is in use, where am I likely to find the source code for it ?
They hold that the turnaround of the lines should take place much closer to the kernel, and the accepted wisdom is to do this by using the ioctl rs485 memory structure & calls. Hope this all makes sense ?
Yes, and I agree with that wisdom.
The gotcha is that the serial port driver is probably going to require enhancement to provide this feature.
Quote:
Originally Posted by gazroobari
In my previous post I only stated the description of error 25 based on what perror() gave me; understand what you say about the ambiguity.
The initial reason for for doubting that device name was that I have never encountered it after several years of reading various forums.
If you had code that did a tcgetattr() and tcsetattr() sucessfully before trying the ioctl(), then one meaning of ENOTTY is eliminated.
After looking at the mxs-auart.c driver, your claim is obviously valid, but left the comment for completeness since errno 25 is used for several reasons.
Quote:
Originally Posted by gazroobari
there is usually both a UART on-board the CPU ...
[Nit] The CPU and UART (plus other peripherals) are integrated into an SoC.
Quote:
Originally Posted by gazroobari
... as well as a separate line driver chip for converting signals into those suitable for 232 and 485 usage. Is that correct ?
Correct. Your GPIOs (or the RTS control pin if the UART has one) connect to either this RS-485 line driver or (additional) gates for directional control.
Independent receive and transmit controls are not common (for half-duplex operation), but could be beneficial.
Since direction control of a half-duplex RS-485 port is typically by the RTS pin of the UART/USART, the use of two GPIOs is not conventional, and therefore should not be assumed to be supported by a device driver. Someone should have questioned how this generic interface was going to work for a specific (i.e. your) board.
E.G. the atmel_serial.c and mcf.c RS-485 implementations require that the RTS pin of the UART/USART control the direction. You cannot assign an arbitrary GPIO pin with these drivers.
The "accepted wisdom" did not think this through.
FWIW the 4.7 version of mxs-auart.c has definitions for RS-485 and multidrop features of the UART (e.g. either RTS or DTR is used for direction control), but does not seem to use them nor support the updated TIOCSRS485 interface.
Quote:
Originally Posted by gazroobari
If I do manage to ascertain which serial driver is in use, where am I likely to find the source code for it ?
The 3.10.xx kernel that you're using is probably a BSP version for whatever reference design that your board is based on.
You mention "ELDK v5.5", which could be from Wolfgang Denk's site.
One of your teammates should be able to provide a more accurate answer.
Spoken to a colleague and I think we are much closer to knowing what we need to do.
Turns out our CPU is iMX28 and the serial driver is mxs-auart.c (for ttyAPP0 thru' ttyAPP2). We do indeed need to add the RS-485 support to that ourselves, as you pointed out, and my colleague is fairly clear what needs coding. In fact he found a patch:
The patch in all probability is for a different version of the driver than we have but he is happy enough to do a diff, apply the changes & rebuild.
Our hardware guy is also involved. He wondered if the serial driver would be difficult to modify but we will see. He accepts that RTS is the way to go for line control rather than switching GPIOs. There's also talk of a temporary workaround to set the GPIO 'driver enable' pin to input and adding a wire link to RTS.
Anyway, with a bit of hard work hopefully we are on the way to resolving this properly.
Once again, blue_z, totally appreciate your assistance.
He accepts that RTS is the way to go for line control rather than switching GPIOs.
If this involves a board change, then IMO there's not a compelling reason to change.
A quick look at i.MX28 documentation indicates that the AUART does not have hardware support for RS-485.
Whereas the Atmel USARTs do have hardware support for RS-485 that specifically involves the RTS pin.
Since the AUART does not have HW support for RS-485, then IMO you are free to depart from convention, and use any pin(s) to control the transceiver/gates.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.