[LinuxPPS] [SOLVED] linuxPPS stops working when NTP starts

George Spelvin linux at horizon.com
Tue Nov 11 21:36:36 CET 2008


For a few months, I've been having the problem that /dev/pps0 stops
working while NTP has /dev/ttyS0 open.  A few other people have reported
this problem as well.  I finally figured out the problem.

It boils down to the definition of UART_ENABLE_MS in
include/linux/serial_core.h:568.
/*
 *	UART_ENABLE_MS - determine if port should enable modem status irqs
 */
#define UART_ENABLE_MS(port,cflag)      ((port)->flags & UPF_HARDPPS_CD || \
					 (cflag) & CRTSCTS || \
					 !((cflag) & CLOCAL))

If this is false, the following code in serial8250_set_termios
(drivers/serial/8250.c:2188) clears UART_IER_MSI
	/*
	 * CTS flow control flag and modem status interrupts
	 */
	up->ier &= ~UART_IER_MSI;
	if (!(up->bugs & UART_BUG_NOMSR) &&
			UART_ENABLE_MS(&up->port, termios->c_cflag))
		up->ier |= UART_IER_MSI;

Then, in check_modem_status, if UART_IER_MSI is clear, the delta
status bits aren't even examined:
	unsigned int status = serial_in(up, UART_MSR);

	status |= up->msr_saved_flags;
	up->msr_saved_flags = 0;
	if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
	    up->port.info != NULL) {
		/* ... */
		if (status & UART_MSR_DDCD)
			uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);

As verification, turning on crtscts makes pps start working again.

A simple fix would be to add the existence of ld->ops->dcd_change to
the UART_ENABLE_MS condition, but I'd have to ensure that
serial8250_set_termios is called on ldisc change.



Still to be figured out: Why linuxpps 5.x has trouble seeing short
status pulses that linuxpps 4.x had no trouble with.

I'm wondering if moving check_modem_status() up would help.
I'm about to recompile and test

static inline void
serial8250_handle_port(struct uart_8250_port *up)
{
	unsigned int status;
	unsigned long flags;

	spin_lock_irqsave(&up->port.lock, flags);

	check_modem_status(up);			/* NEW */

	status = serial_inp(up, UART_LSR);

	DEBUG_INTR("status = %x...", status);

	if (status & (UART_LSR_DR | UART_LSR_BI)) {
		receive_chars(up, &status);
		check_modem_status(up);		/* MOVED */
	}
	if (status & UART_LSR_THRE)
		transmit_chars(up);

	spin_unlock_irqrestore(&up->port.lock, flags);
}



More information about the LinuxPPS mailing list