Making a serial port notify on change of handshaking lines - linux

Is it possible to open a serial device (such as /dev/ttyS0) and be informed via select/poll/etc... or a signal when the handshaking lines (such as CTS/RTS or DSR/DTR) change? I know at the hardware level there's an interrupt from the UART to tell the kernel it has changed, but can I be informed of that up in userland?
Edit: I am aware of TIOCMIWAIT, but that ioctl call blocks until the status lines change. I would like instead to keep processing generally and have a poll or similar be informed on change, as well as other events.

There's no way to wait for the DTR/RTS lines to change in userland. The only way to do this on Linux is to constantly poll the device, checking to see if the status of the RTS/DTR lines have changed. I generally steal my serial port code from gtkerm, and it polls.
You can try using TIOCMIWAIT, but if I remember correctly that's going to be very tied to the driver for the serial port that Linux is using, and so may not work from driver to driver.

Related

Linux FTDI USB-to-Serial overlflow errors FTDI_RS_OE on 1Mbaud

I am trying to use a genuine FTDI USB-to-serial in Linux at 1Mbaud (instead of the usual 115200). This all works fine, but sometimes I seem to lose data. When checking the counters with the TIOCGICOUNT ioctl call, I can see that I get overrun errors (not buf_overrun).
When looking in the source code of the FTDI linux driver (https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ftdi_sio.c), this is done when the chip sends a USB packet containing a FTDI_RS_OE error flag.
(For reference, when I use the exact same userspace application, but with a totally different serial device (the imx6 mxc), I do not get these errors. It's really an FTDI-driver specific thing)
I find very little regarding this, and strangely enough, the windows driver does not seems to suffer from this problem. If anybody has gotten these FTDI chips working at high speeds in linux, feel free to help me out!
Kind regards,
Arnout
I think I figured it out. Bottom line was that this was a genuine RX overrun due to my code not reading the uart buffers fast enough. When I make sure to "read" the serial port fast enough, I can attain the constant Mbaud data rate. The reason I got totally misguided (and that the weird and unexpected FTDI_RS_OE is sent) I will explain below.
A few notes on the protocol I was using. I was sending some "request" packet on the serial line, and expecting a large reply. (and doing this in loop). "My bug" was that I expected the remote device to reply very quickly, and if not stop processing. This timeout was too short, but the actual reply did still come in. "Some" buffer then overflowed, causing the RX overruns.
But, this was not so clear. A few subtleties:
The rx overrun counter was only incremented upon the NEXT uart read syscall (e.g. minutes later if my code went to some idle state) (NOT immediately after the actual issue happened, which is very confusing)
I was under the assumption that, just like the imx6 driver, the linux kernel would always simply service the USB device if incoming data was available. And that the data would be sent into a 640kB buffer (defined https://elixir.bootlin.com/linux/v4.9.192/source/drivers/tty/tty_buffer.c). In the imx6 driver it can then clearly be seen what happens if that buffer overflows
But that turns out not to be the case. Instead, my best guess at what happens here (I haven't profiled/debugged the kernel to verify this) is that serial "throtteling" happens. When this 640kB is "getting" full, linux will issue a "throttle" callback to the FTDI driver. That then simply uses the generic usb_serial_generic_throttle, which sets a flag, and discards incoming urb data in https://elixir.bootlin.com/linux/latest/ident/usb_serial_generic_read_bulk_callback. This would explain why no overruns are "detected" when the incident actually occurs, but suddenly is detected when (e.g. after 1 minute of inactivity) I restart a read operation. The FTDI chip's internal buffer must be overflowing due to this mechanism, casuing this FTDI_RS_OE flag to be set, which is then only actually correctly parsed when throtteling is disabled again.
So conclusion: The main issue was at my side, but the FTDI driver does not correctly implement its overrun counters (they only show up 'late' or even never depending on the usecase) due to most likely the linux throtteling feature.

How does UART communication work between two devices?

In one of my project, I have a nano-computer (embedded Linux) which is connected to a microcontroller with an UART connection.
Both do some process on their own, but sometimes the nano-computer needs to send data on the UART and vice versa.
I suppose that if A wants to communicate with B, B needs to be listening, right ? How do I know when to listen, and when to talk ?
Do I need to have a special thread running in parallel in both of my devices only responsible for UART communication, while they do other stuff ?
If I missed a message, is there a buffer which is filled that I can read when I am ready ?
Thanks for your advices. :)
'A' and 'B' are listening all time. You have to enable the UART receive interupt.
Maybe this link will explain the basics: UART basics
Connected and initialized correctly the hardware has a tx and rx on both sides tx to rx. So both sides are listening all the time from a hardware perspective. The operating system likely has a driver and a buffer that is accumulating that input all the time. BUT if you have no software that is asking for the data coming in then you wont see it. You do need some software monitoring the uart if you will (through drivers and operating system usually) so that you can see what the other side sends at any given time. You do this on both ends of the connection if that is what is required.
There are two approaches that are used.
In the past, it was common to use hardware flow control. This uses an additional wire in each direction. The sender waits until the wire indicates the receiver is ready. When the receiver is not ready to receive data, it signals the other side. Hardware would buffer at least one byte and, if the buffer was full, signal the other side not to send over this wire.
This is less common today. UARTs are so slow relative to modern hardware and large buffers are so cheap and easy to provide that there is no longer an issue. The sender just fills the receiver's hardware buffer and the receiver empties the hardware buffer periodically. Software would have to ignore the buffer for a long time for it to overflow.
An intermediate solution is to use flow control in the data flow. Generally, two characters are reserved, one to stop the flow and one to resume it. The receiver sends a flow control character to the sender if its buffer is getting close to full and another one if its buffer is getting close to empty. This is really only useful if the data flow doesn't need to handle binary data. This is extremely rare and was traditionally used primarily for connections that had a human on one end. You could also pause the flow if the information was coming faster than you could read it.
Generally, the protocols used are tolerant of overflow and include some form of high-level acknowledgement and/or retransmission as appropriate. One device might wait for the other side to send some kind of response to its command and, if it doesn't get one, retry the command. The protocol is designed not to do anything terrible if a command is received twice since it might be the reply that's lost.

how did the kernel find the right process to send hardware interrupt

At first, this two things might exist:
A system has a table to response hardware interrupt
A process has a table to response interrupt send/set by the kernel
If I hit a key on the keyboard, the keyboard will send a interrupt to the CPU/kernel, and the kernel will process this interrupt. But, maybe the current running process is not the foreground one in front of our eyes, it could be a daemon process or something else. So, how the kernel knows which process should read/response our key stroke?
Thanks!
Hardware interrupts are only handled by the kernel. The device-specific event is processed and if there is event/data to share with user space then the driver makes that available. In your example of a keyboard, the device driver services the interrupt, extracting any data and clearing the condition. The input event representing the data which is pulled from the hardware is then sent to the input subsystem. A user space process must have the exposed input device handle open and blocked on a read. The input subsystem within the kernel is managing this. It's very common to see the same in other drivers: expose a device handle (e.g. /dev/misc/mydevice) which responds to open/close/read/write/ioctl. When a process performs a "read" and there is no data, the kernel code blocks the calling process causing it to wait until there is data to satisfy the read condition. I recommend reading up on kernel device drivers. "Linux Device Drivers" is a great start.

Disabling specific USB devices

I need to write a program in linux (debian, to be exact) that disables a USB device if it doesn't pass specific filters. For example, the program might be set to disallow webcams, usb sticks and keyboards, enable mice and printer (through usb). The filters may change in runtime. For example, the program might receive a message to enable usb sticks and it should respond without rebooting the system. The program is written in python but embedding c code (or others) is also acceptable.
What I have tried
I have tried many methods but some of them aren't about programming.
First, I tried to mess with udev. I can monitor the activities when a device is plugged and write filters. There used to be an option "ignore_device" that ignored the filtered devices. For example, to ignore all the devices that are a member of usb subsystem, I would write this as a udev rule:
ACTION=="add", SUBSYSTEM=="usb", OPTIONS+="ignore_device"
But this option is released in this release of udev. What I get so far is that udev can currently be used mainly for monitoring. Sure, I can write additional rules for the rule above that runs a script, but I have to do the disabling elsewhere.
Second, I tried ioctl to send a DISCONNECT signal to device handle. I'm testing this with a USB mouse. This is the python code for that: (I have also tried this in C, nothing changed)
import fcntl
import sys
USBDEVFS_RESET = ord('U') << (4*2) | 20
USBDEVFS_DISCONNECT = ord('U') << (4*2) | 22
raw_name = "/dev/bus/usb/{:03d}/{:03d}"
filename = raw_name.format(1,2)
fd = open(filename, "wb")
fcntl.ioctl(fd, USBDEVFS_DISCONNECT, 0)
Here, if I would send USBDEVFS_RESET, it works, the mouse input is ignored for a second or two. But disconnect signal raises an error:
IOError: [Errno 25] Inappropriate ioctl for device
What I get from here is, I cannot send disconnect signal to a mouse. Maybe a usb stick or printer or some other devices would work, I haven't tried. I want to develop the program as generic as possible so as to prevent writing additional device-specific code, so this approach seems useless for me. And another point here is that when I manually disconnect/connect my mouse, I see events in udev monitor. But when I send reset signal, no event is sent.
The udev monitor says that the mouse was mounted to this path: /sys/bus/usb/devices/1-3 (which is a symlink for /sys/devices/pci0000:00/0000:00:14.0/usb1/1-3). Some documents told me that this folder contains the device's settings and setting /power/level to "off" or "suspended" would turn the device power off. But I cannot manipulate any files in /power. Come to think about it, it might not be a good idea after all.
The Question
So, the question is, what is the best way to achieve such task? I have an idea but I'm not sure whether it will work and even if it does, it might be overkill. My idea is writing a "wrapper driver" that identifies itself to linux kernel as driver for all usb devices. The "wrapper driver" reads the device information and if the device is good to go, it acts as a wrapper for real drivers in the kernel, calling their functions. If not, the "wrapper driver" just ignores the device.
I'm not sure it can even be done, I'm not experienced in kernel or driver programming.
Another way is, -somehow- getting the handle of the device programatically and telling it to power off (or making it busy forever, whatever works). I have also done some little research but couldn't find a proper-easy way. They say all devices are considered as "files" but I cannot reach those files at all.
Note:
The question is mostly about linux internals but it also involves kernel programming. I read a lot about usb manipulation/monitoring programs, I read manual pages of udevadm. But these approaches do not help me at all. I think I need to alter either kernel or device internals programatically.
I have also tried manipulating authorized file that resides in /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4 (for a keyboard). It's default value is 1. Changing it to 0 successfully disabled the bus (NOT device, but the physical usb port. The same device can still be used when plugged in another port). But making it 0 also stopped udev events from this usb port. So, I can disable the port if the user plugs in a forbidden device but I cannot decide when to enable it since I cannot listen to remove events in udev. Would it make sense if I delve deeper to lower levels of code (possibly kernel) and listen to usb events in some other way?
I think the simplest way to solve your problem is balcklisting all usb device drivers excepting mouse, keyboard etc.
The cleanest way is whitlisting mouse etc. with udev using usb device id's
Writing a wrapper driver is an absolute overkill never chose that way.
One possible solution could be tying usb access to user permissions. here is a related link you may find handy
http://robots.mobilerobots.com/wiki/Linux_udev_USB_Device_Permissions_Configuration

request_irq succeeds but interrupt is never detected

I am running embedded linux 3.2.6 on an ARM processor. I am using a modified version of atmel's serial driver to control the 4 USART ports on my device. When I use the driver compiled with the kernel, all works fine. But I want to run the driver as a kernel module instead. I make all of the necessary changes and disable the internal driver and everything seems fine. The 4 tty devices are registered successfully and I can see that the all of my probe and initialization functions work correctly.
So here's the problem:
When I try to write to any of the devices, my "start transmit" function gets called but then waits for an interrupt from the usart which never occurs. So the write just hangs, and using a logic analyzer I can see that RTS gets asserted but no bytes show up on the tx line. I know that my call to request_irq succeeds and yet i never see any of the irq entries in /proc/interrupts. In the driver, I have also tried using request_irq to register a separate interrupt handler for a gpio line, and this works fine.
I know that this is a problem that is probably hard to diagnose, but I am looking for ANY possible suggestions that could lead me in the right direction to finding a solution. Let me know if you need any clarifications. Thank you
The symptoms reads like a peripheral clock that has not been enabled (or turned off): the device can be initialized w/o errors and an I/O operation can be setup, but the device doesn't do anything; it plays dead. Since no I/O ever starts, you're never going to get an interrupt indicating completion!
The other thing to check are the conditional compilation directives for HW configuration structures in your arch/arm/mach-xxx/zzz_devices.c file.
Make sure that the serial port structures have something like:
#if defined(CONFIG_SERIAL_ATMEL) || defined(CONFIG_SERIAL_ATMEL_MODULE)
and not just
#if defined(CONFIG_SERIAL_ATMEL)
Addendum
I could be wrong but the clock shouldn't have any effect on the CTS pin causing an interrupt, right?
Not right.
These digital circuits are synchronous state machines: without a clock, a change-of-state by an input cannot be processed.
Also, SoCs and modern uControllers use the peripheral clocks as on/off switches for those integrated peripherals. There is often way more functionality, i.e. peripherals, on the silicon chip than can actually be used, mostly due to insufficient quantity of pins to the board. So disabling the clocks to unused devices is employed to reduce power consumption.
You are far too focused on interrupts.
You do not have a solvable interrupt problem; those are secondary failures.
The lack of output when attempting to transmit is far more significant and revealing.
The root cause is probably a flawed configuration of the USART devices, since transmitting bits is an automatic operation for a configured & operational USART.
If the difference between not-working versus working is loadable module versus static linking, then the root cause is going to be something fundamental (and trivial) like my two suggestions.
Also your lack of acknowledgement regarding the #if defined(), e.g. you didn't respond with "Oh yeah, we already knew that", raises a gigantic red flag that says "Fix me first!"
Addendum 2
I'm tempted to delete this answer after discovering that the Atmel serial driver cannot be configured/built as a loadable module using make menuconfig (which is the premise for half of the answer). (Of course the Kconfig file could be hacked to make the config variable tristate instead of boolean to overcome the module restriction.) I've left a comment for the OP. But I also wanted to preserve the comment to Mr. Stratton pointing out how symbols in the .config file are (not) used.
So I did finally fix my problem. Thank you for the responses, none of them directly solved my problem but they did prompt further examination of my code. After some trial and error I finally got it working. I had originally moved the platform_device structures for each usart from /mach-at91/xxx_devices.c to my loadable module. Well for some reason the structures weren't getting the correct data to map to the hardware, I suppose because it wasn't correctly linking the symbols from the kernel (never got an error message though) and so some of the registration functions weren't even getting called. I ended up moving the structures and platform_device_register calls back into the devices file. I also decided to keep the driver for the console built-in using the original atmel_serial.c driver. I had to change the platform_device name for the console in both the devices file and in the built-in atmel_serial.c file in order for it to not conflict with my usart ports driver. I found that changing the platform_device and platform_driver name for the usarts from anything but "atmel_usart" resulted in usart transmission failing. I really don't understand why, but i'm just leaving it as atmel_usart so it works.
Thanks again to everybody who responded to my problem.

Resources