tty serial 8250 rs485 support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello.

I have modified some kernel sources to add support for the rs485 API in the 8250 serial driver. I copy the patch at the end of this message.

This modification affect only to the modules 8250_core.cc and 8250_port.cc. Basically this programs a timer after the last byte written to the UART to delaying the RTS change for two bytes, one is the previous byte that starts going out and the second for this last byte. Also it adds an additional  tick for avoid the error due to the rounding in ticks number. In the sources the changes are commented with the mark FPG (my initials).

The patch is applied to the version 4.14.53, but it is also valid for the 4.14.63 because the affected modules have not changed.

I am new sending kernel patches, so please say me if this mail is enough to report the change or I have to send the patch to another place.

Regards.

Francisco Pastor
fpastor.etraid@xxxxxxxxxxxxx

-------------- patch -------------------

diff -Naur '--exclude=Makefile' linux-4.14.53/drivers/tty/serial/8250/8250_core.c drivers/4.14.53/CUBE-CYME/8250/8250_core.c
--- linux-4.14.53/drivers/tty/serial/8250/8250_core.c	2018-07-03 11:25:05.000000000 +0200
+++ drivers/4.14.53/CUBE-CYME/8250/8250_core.c	2018-07-10 14:25:25.747564455 +0200
@@ -954,6 +954,30 @@
 }
 
 /**
+ * FPG: Function to set the 485 configuration
+ */
+static int serial8250_rs485_config(struct uart_port *port,
+			      struct serial_rs485 *rs485)
+{
+	int ret = 0;
+	struct uart_8250_port *up = up_to_u8250p(port);
+
+	if (rs485->flags & SER_RS485_ENABLED)
+	{
+		memset(rs485->padding, 0, sizeof(rs485->padding));
+		port->rs485 = *rs485;
+		ret = serial8250_em485_init(up);
+	}
+	else
+	{
+		serial8250_em485_destroy(up);
+		memset(&port->rs485, 0, sizeof(port->rs485));
+	}
+
+	return ret;
+}
+
+/**
  *	serial8250_register_8250_port - register a serial port
  *	@up: serial port template
  *
@@ -1062,6 +1086,8 @@
 
 			ret = 0;
 		}
+		// FPG: Enable the 485 operation programming the config function
+		uart->port.rs485_config = serial8250_rs485_config;
 	}
 	mutex_unlock(&serial_mutex);
 
diff -Naur '--exclude=Makefile' linux-4.14.53/drivers/tty/serial/8250/8250_port.c drivers/4.14.53/CUBE-CYME/8250/8250_port.c
--- linux-4.14.53/drivers/tty/serial/8250/8250_port.c	2018-07-03 11:25:05.000000000 +0200
+++ drivers/4.14.53/CUBE-CYME/8250/8250_port.c	2018-07-11 09:58:28.442176995 +0200
@@ -620,23 +620,52 @@
  *
  *	Return 0 - success, -errno - otherwise
  */
+// FPG: Ajust the after_delay after last bytes is transmited
 int serial8250_em485_init(struct uart_8250_port *p)
 {
-	if (p->em485)
-		return 0;
+	struct uart_port *up = &p->port;
+	struct ktermios termios;
+	unsigned int baud;
+	unsigned int nbits = 2;		// Start and Stop
 
-	p->em485 = kmalloc(sizeof(struct uart_8250_em485), GFP_ATOMIC);
 	if (!p->em485)
-		return -ENOMEM;
-
-	hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC,
-		     HRTIMER_MODE_REL);
-	hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC,
-		     HRTIMER_MODE_REL);
-	p->em485->stop_tx_timer.function = &serial8250_em485_handle_stop_tx;
-	p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx;
-	p->em485->port = p;
-	p->em485->active_timer = NULL;
+	{
+		p->em485 = kmalloc(sizeof(struct uart_8250_em485), GFP_ATOMIC);
+		if (!p->em485)
+			return -ENOMEM;
+
+		hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC,
+				HRTIMER_MODE_REL);
+		hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC,
+				HRTIMER_MODE_REL);
+		p->em485->stop_tx_timer.function = &serial8250_em485_handle_stop_tx;
+		p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx;
+		p->em485->port = p;
+		p->em485->active_timer = NULL;
+	}
+
+	// Add to the after_delay the time of two bytes plus a jiffie
+	termios.c_cflag = up->cons->cflag;
+	if (up->state->port.tty && termios.c_cflag == 0);
+		termios.c_cflag = up->state->port.tty->termios.c_cflag;
+
+	switch ( termios.c_cflag & CSIZE )
+	{
+		case CS5: nbits += 5; break;
+		case CS6: nbits += 6; break;
+		case CS7: nbits += 7; break;
+		case CS8: nbits += 8; break;
+	}
+	if ( termios.c_cflag & CSTOPB ) nbits++;
+	baud = uart_get_baud_rate(up, &termios, NULL, up->uartclk / 16 / 0xffff, up->uartclk);
+	if ( baud )
+	{
+		up->rs485.delay_rts_after_send += 1000 * nbits * 2 / baud + 1000 / HZ;
+		dev_dbg(up->dev, "em485_init: baud=%d bits=%d HZ=%d delay=%d", baud, nbits, HZ, up->rs485.delay_rts_after_send);
+	}
+	else
+		dev_err(up->dev, "em485_init: ERROR: Can not get the baudrate");
+		
 	serial8250_em485_rts_after_send(p);
 
 	return 0;
@@ -1517,15 +1546,17 @@
 	struct uart_8250_em485 *em485 = p->em485;
 
 	if (em485) {
-		unsigned char lsr = serial_in(p, UART_LSR);
 		/*
 		 * To provide required timeing and allow FIFO transfer,
 		 * __stop_tx_rs485() must be called only when both FIFO and
 		 * shift register are empty. It is for device driver to enable
 		 * interrupt on TEMT.
 		 */
-		if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
-			return;
+		// FPG: Commented since we always need the after_delay to change RTS
+		//unsigned char lsr = serial_in(p, UART_LSR);
+		//
+		//if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
+		//	return;
 
 		em485->active_timer = NULL;
 		hrtimer_cancel(&em485->start_tx_timer);
@@ -1805,6 +1836,9 @@
 		if ((up->capabilities & UART_CAP_MINI) &&
 		    !(serial_in(up, UART_LSR) & UART_LSR_THRE))
 			break;
+		// FPG: If 485 enabled we always leave one byte out of the FIFO
+		if ( up->em485 && (up->port.rs485.flags & SER_RS485_ENABLED) && uart_circ_chars_pending(xmit) <= 1 )
+			break;
 	} while (--count > 0);
 
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -2417,6 +2451,10 @@
 	struct uart_8250_port *up = up_to_u8250p(port);
 	unsigned long flags;
 
+	// FPG: Clean the 485 configuration before close the port
+	serial8250_em485_destroy(up);
+	memset(&port->rs485, 0, sizeof(port->rs485));
+
 	serial8250_rpm_get(up);
 	/*
 	 * Disable interrupts from this port





[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux