lin-bus / linux-lin

Linux kernel LIN bus support implemented as TTY line discipline for generic UART conrollers, documentation https://github.com/lin-bus/linux-lin/wiki, based on https://github.com/lin-bus/linux-lin/wiki/sllin-rtlws14-paper.pdf, more CAN related projects http://canbus.pages.fel.cvut.cz/
https://github.com/lin-bus/linux-lin/wiki
40 stars 26 forks source link

Kernel generic API for low lever UART driver/HW Rx FIFO trig and or iddle timeout control #13

Open ppisa opened 2 years ago

ppisa commented 2 years ago

There are many serial protocol which require response to received data to occur in predefined time interval which is often shorter than default/fixed Rx idle FIFO timeout. The LIN specific problem is then BREAK condition generation with predefined time followed by sync character. The LIN speed is low and fully preemptive kernel latencies are far bellow required reaction times but again FIFO makes problems there as well. We use more later described hacks for slLIN to switch FIFO to desired state for different UARTs.

The sending of break character directly followed by data characters is not easy through low level Linux serial API. Same difficulties apply to acting as slave and sending response frame without delay/gap.

The main problem in case of BREAK_BY_BAUD is delay between sending final bit and reporting receive interrupt to the driver. The delay corresponding to 3 or 4 characters is typically result of UART chip FIFO mode where non-empty Rx FIFO event is reported only when threshold level is reached or when line is idle for 4 characters. It is necessary to set UART into mode where Rx FIFO is disabled or Rx threshold is set to one. Unfortunately, there is no unified API for this parameter or even its availability which is supported across multiple/all UART drivers implementations.

PC style 8250/16[45679]50 allows to set FIFO trigger level to 1 for classic by

echo 1 >/sys/class/tty/ttyS0/rx_trig_bytes

which switches off FIFO if unavailable.

There is required function in 8250 driver linux/drivers/tty/serial/8250/8250_port.c

     do_serial8250_set_rxtrig()      serial8250_set_attr_rx_trig_bytes()

and corresponding rx_trig_bytes attribute available for some of these ports.

But it is problem that there is no generic interface how to control this parameter on different UART flawors.

One some arch, rx_fifo_timeout can be used instead, which is even better because FIFO prevents lost of bytes when non RT kernel is used.

We have used some patches for Ti AM3 and for MPC5200.

On Texas Instruments AM[34]xxx chips next patch can be used

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index e14982f..13bbf90 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -80,7 +80,7 @@
 #define OMAP_UART_TX_WAKEUP_EN (1 << 7)
 
 #define TX_TRIGGER     1
-#define RX_TRIGGER     48
+#define RX_TRIGGER     1
 
 #define OMAP_UART_TCR_RESTORE(x)       ((x / 4) << 4)
 #define OMAP_UART_TCR_HALT(x)          ((x / 4) << 0)
@@ -380,7 +380,7 @@ static void omap_8250_set_termios(struct uart_port *port,
        up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
        if (termios->c_iflag & INPCK)
                up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
-       if (termios->c_iflag & (IGNBRK | PARMRK))
+       if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
                up->port.read_status_mask |= UART_LSR_BI;
 
        /*

But consequence is that Rx threshold level 1 is used on all serial ports then. Usually not a problem.

Generic API from line discipline to low level UART drivers to control FIFO and timeout would be great. It should round trig level down if given value is not available or switch FIFO entirely. Same for timeout which could be counted in number of character times or microseconds.

desowin commented 2 years ago

But consequence is that Rx threshold level 1 is used on all serial ports then. Usually not a problem.

Not a problem if the character transmission time is longer than the interrupt handling time. Otherwise, it can starve the line discipline worker and not only lead to complete system freeze, but also renders hardware/software flow control useless. A simple way out of the situation is to do the exact opposite than what is needed for lin-bus.

Generic API from line discipline to low level UART drivers to control FIFO and timeout would be great. It should round trig level down if given value is not available or switch FIFO entirely. Same for timeout which could be counted in number of character times or microseconds.

I agree that the generic API would be really nice. It is not trivial to implement as there are quite some differences at the hardware level.

For example, imx UART has fixed aging timer at 8 characters time. To get what is needed by lin-bus, you can set the RXTL to 1, effectively disabling aging timer. Note that despite RXTL set to 1, the RxFIFO is still active and it will keep collecting characters as RXTL only controls when the interrupt is raised. If RXTL is set to any value higher than 1 (i.e. any value from 2 to 32), then you still get 8 characters timeout if the frame was only 1 character long.

ppisa commented 2 years ago

Yes imx UART is good for LIN and RS-485 (like uLAN) protocols that it allows to set trigger level to 1 and still use FIFO to not lose character. But timeout of 8 bytes for other cases is far above value tolerated by many protocol standards. Even the three for 16Cx50 is too long. But it allows to set trig level only to the one quarter of the FIFO depth (1/4 of 64 or 128) which breaks the LIN protocol. So for 16Cx50 is only option to disable FIFO entirely which could lead to the lost of received characters. But bitrate is low for LIN so it works reliably and there is checksum in the worst case as a reserve to not receive corrupted data.

ppisa commented 2 years ago

For information networking, there is corresponding question on linux-serial list

https://marc.info/?t=164237500300001

Comments, suggestions welcomed.

kbader94 commented 2 years ago

Hi Pavel,

I'm not so familiar with the linux mainline development process, but am I correct in assuming this is something that could be implemented with a series of patches, first to the uart_ops struct with a new API, and then to a number of device drivers to implement the uart_ops API call? I'm interested in modifying the PL011 driver https://github.com/raspberrypi/linux/blob/rpi-5.10.y/drivers/tty/serial/amba-pl011.c for the rpi. Hopefully having a widely used device as one of the test devices would help promote this project, but feel a bit out of my league here. I've written low level drivers before for the SAM3X, and I've reviewed the PL011 datasheet and can confirm that the best I could do is disable the FIFO but this should still work. I've done some work with sllin here so I think I understand what I need to do but would like some clarification. If we come up with a generic API in uart_ops, that would need to call a corresponding function in the PL011 driver to actually set the FIFO buffer or timeout, and or disable it altogether? Then from there the sllin driver can call the new API function? It doesn't sound too complicated, or am I missing something? I've read the mailing list there and your suggestions for the uart_ops struct function there, it makes sense to me but I think maybe I'm over simplifying things. If I'm correct in my understanding so far, can we open another repo under this project for the patch to the uart_ops struct in the linux kernel?

Thanks again for your guidance Pavel, Kyle Bader

ppisa commented 2 years ago

Dear Kyle,

if you can find some time it would be great if you try to implement more generic API to control FIFO trigger level/switch FIFO off if not configurable. Unfortunately I am too loaded to do some testing. May it be it would be a little better in July.

Are you tested the latest version of slLIN from this repo. Others seems to have troubles, see #10. I am not sure if the mai cause is in slLIN or in the tools used for attach of line discipline. If you can report you setup and results it would be great help. I would be happy to know if it is general problem or incompatibility with some serial port chips/IP cores.

As for the repo to develop changes, if you think that it helps I can add Linux kernel fork but I think putting changes only in patch series form into your slLIN fork and then possibly pulled into slLIN master would be enough for start. If it is accepted by mainline then it would be great if not it can be stored there and hopefully applied to different kernel versions with minimal effort.

Best wishes,

Pavel