On Friday 04 May 2007, Corey Minyard wrote:
>I did think of one other thing: if you clear the MSR delta flags while
>polling the serial console, you need to call the routine to check the
>modem status since the interrupt will not happen.  So here's the
>patch...
>
>Subject: Serial 8250: Handle saving the clear-on-read bits from the LSR and
> MSR
>
I applied this patch because I was curious to see what effect if any it had on 
some serial based acessories I use here, like a cm11 x10 controller with 
heyu, and a belkin ups with its supplied software.

But at first boot, its not 100% operationally safe, but please read on.

1) heyu (the x10 control program) is now stuck in a loop, repeating the last 
command issued, at about 40 second repeat intervals.  AND it is using 90%+ of 
the cpu while it executes each loop, which takes about 7 to 10 seconds each 
time.  heyu is interfacing to the world via /dev/ttyUSB0, through an FTDI 
adapter so I'm not able to make a mental connection here between this patch 
and that effect.

2) HOWEVER!  The belkin upsd daemon, which started being a cpu hog, using 40% 
of the cpu continuously since something in the 2.6.20 to 2.6.21 time frame, 
and this was regardless of whether it was talking through a /dev/ttyUSB# port 
and either a pl2303, or an FTDI adaptor, or direct through /dev/ttyS0, is at 
least for /dev/ttyS0, now operating normally and apparently 100% stable.  So 
I pulled out another FTDI adaptor, and reconfigured the daemon to 
use /dev/ttyUSB1, and that is also operating 100% normally now.

Can someone explain 1) above?  Even odder still, I'd killed and restarted heyu 
several times to no avail before I tried putting upsd on /dev/ttyUSB1, but 
after moving the upsd access from /dev/ttyS0 to /dev/ttyUSB1, then heyu, 
running through /dev/ttyUSB0, is now operating 100% normally.  ???

Does anyone have any idea what kind of bait I should put out to kill this 
nilmerg?  I'm happy, its all working again for a change, but I'll be damned 
if I ain't totally bumfuzzled.  And I wonder if it will survive a reboot...

>Reading the LSR clears the break, parity, frame error, and overrun
>bits in the 8250 chip, but these are not being saved in all places
>that read the LSR.  Same goes for the MSR delta bits.  Save the LSR
>bits off whenever the lsr is read so they can be handled later in the
>receive routine.  Save the MSR bits to be handled in the modem status
>routine.
>
>Also, clear the stored bits and clear the interrupt registers before
>enabling interrupts, to avoid handling old values of the stored bits
>in the interrupt routines.
>
>Signed-off-by: Corey Minyard <[EMAIL PROTECTED]>
>Cc: Russell King <[EMAIL PROTECTED]>
>
> drivers/serial/8250.c      |   85
> +++++++++++++++++++++++++++++---------------- include/linux/serial_reg.h | 
>   1
> 2 files changed, 57 insertions(+), 29 deletions(-)
>
>Index: linux-2.6.21/drivers/serial/8250.c
>===================================================================
>--- linux-2.6.21.orig/drivers/serial/8250.c
>+++ linux-2.6.21/drivers/serial/8250.c
>@@ -129,7 +129,16 @@ struct uart_8250_port {
>       unsigned char           mcr;
>       unsigned char           mcr_mask;       /* mask of user bits */
>       unsigned char           mcr_force;      /* mask of forced bits */
>-      unsigned char           lsr_break_flag;
>+
>+      /*
>+       * Some bits in registers are cleared on a read, so they must
>+       * be saved whenever the register is read but the bits will not
>+       * be immediately processed.
>+       */
>+#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
>+      unsigned char           lsr_saved_flags;
>+#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
>+      unsigned char           msr_saved_flags;
>
>       /*
>        * We provide a per-port pm hook.
>@@ -1159,6 +1168,7 @@ static void serial8250_start_tx(struct u
>               if (up->bugs & UART_BUG_TXEN) {
>                       unsigned char lsr, iir;
>                       lsr = serial_in(up, UART_LSR);
>+                      up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
>                       iir = serial_in(up, UART_IIR);
>                       if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT)
>                               transmit_chars(up);
>@@ -1208,18 +1218,10 @@ receive_chars(struct uart_8250_port *up,
>               flag = TTY_NORMAL;
>               up->port.icount.rx++;
>
>-#ifdef CONFIG_SERIAL_8250_CONSOLE
>-              /*
>-               * Recover the break flag from console xmit
>-               */
>-              if (up->port.line == up->port.cons->index) {
>-                      lsr |= up->lsr_break_flag;
>-                      up->lsr_break_flag = 0;
>-              }
>-#endif
>+              lsr |= up->lsr_saved_flags;
>+              up->lsr_saved_flags = 0;
>
>-              if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
>-                                  UART_LSR_FE | UART_LSR_OE))) {
>+              if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
>                       /*
>                        * For statistics only
>                        */
>@@ -1310,6 +1312,8 @@ static unsigned int check_modem_status(s
> {
>       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_TERI)
>@@ -1496,7 +1500,8 @@ static void serial8250_timeout(unsigned
> static void serial8250_backup_timeout(unsigned long data)
> {
>       struct uart_8250_port *up = (struct uart_8250_port *)data;
>-      unsigned int iir, ier = 0;
>+      unsigned int iir, ier = 0, lsr;
>+      unsigned long flags;
>
>       /*
>        * Must disable interrupts or else we risk racing with the interrupt
>@@ -1515,9 +1520,13 @@ static void serial8250_backup_timeout(un
>        * the "Diva" UART used on the management processor on many HP
>        * ia64 and parisc boxes.
>        */
>+      spin_lock_irqsave(&up->port.lock, flags);
>+      lsr = serial_in(up, UART_LSR);
>+      up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
>+      spin_unlock_irqrestore(&up->port.lock, flags);
>       if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) &&
>           (!uart_circ_empty(&up->port.info->xmit) || up->port.x_char) &&
>-          (serial_in(up, UART_LSR) & UART_LSR_THRE)) {
>+          (lsr & UART_LSR_THRE)) {
>               iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
>               iir |= UART_IIR_THRI;
>       }
>@@ -1536,13 +1545,14 @@ static unsigned int serial8250_tx_empty(
> {
>       struct uart_8250_port *up = (struct uart_8250_port *)port;
>       unsigned long flags;
>-      unsigned int ret;
>+      unsigned int lsr;
>
>       spin_lock_irqsave(&up->port.lock, flags);
>-      ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
>+      lsr = serial_in(up, UART_LSR);
>+      up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
>       spin_unlock_irqrestore(&up->port.lock, flags);
>
>-      return ret;
>+      return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
> }
>
> static unsigned int serial8250_get_mctrl(struct uart_port *port)
>@@ -1613,8 +1623,7 @@ static inline void wait_for_xmitr(struct
>       do {
>               status = serial_in(up, UART_LSR);
>
>-              if (status & UART_LSR_BI)
>-                      up->lsr_break_flag = UART_LSR_BI;
>+              up->lsr_saved_flags |= status & LSR_SAVE_FLAGS;
>
>               if (--tmout == 0)
>                       break;
>@@ -1623,8 +1632,12 @@ static inline void wait_for_xmitr(struct
>
>       /* Wait up to 1s for flow control if necessary */
>       if (up->port.flags & UPF_CONS_FLOW) {
>-              tmout = 1000000;
>-              while (!(serial_in(up, UART_MSR) & UART_MSR_CTS) && --tmout) {
>+              unsigned int tmout;
>+              for (tmout = 1000000; tmout; tmout--) {
>+                      unsigned int msr = serial_in(up, UART_MSR);
>+                      up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
>+                      if (msr & UART_MSR_CTS)
>+                              break;
>                       udelay(1);
>                       touch_nmi_watchdog();
>               }
>@@ -1794,6 +1807,18 @@ static int serial8250_startup(struct uar
>       spin_unlock_irqrestore(&up->port.lock, flags);
>
>       /*
>+       * Clear the interrupt registers again for luck, and clear the
>+       * saved flags to avoid getting false values from polling
>+       * routines or the previous session.
>+       */
>+      (void) serial_inp(up, UART_LSR);
>+      (void) serial_inp(up, UART_RX);
>+      (void) serial_inp(up, UART_IIR);
>+      (void) serial_inp(up, UART_MSR);
>+      up->lsr_saved_flags = 0;
>+      up->msr_saved_flags = 0;
>+
>+      /*
>        * Finally, enable interrupts.  Note: Modem status interrupts
>        * are set via set_termios(), which will be occurring imminently
>        * anyway, so we don't enable them here.
>@@ -1811,14 +1836,6 @@ static int serial8250_startup(struct uar
>               (void) inb_p(icp);
>       }
>
>-      /*
>-       * And clear the interrupt registers again for luck.
>-       */
>-      (void) serial_inp(up, UART_LSR);
>-      (void) serial_inp(up, UART_RX);
>-      (void) serial_inp(up, UART_IIR);
>-      (void) serial_inp(up, UART_MSR);
>-
>       return 0;
> }
>
>@@ -2387,6 +2404,16 @@ serial8250_console_write(struct console
>       wait_for_xmitr(up, BOTH_EMPTY);
>       serial_out(up, UART_IER, ier);
>
>+      /*
>+       *      The receive handling will happen properly because the
>+       *      receive ready bit will still be set; it is not cleared
>+       *      on read.  However, modem control will not, we must
>+       *      call it if we have saved something in the saved flags
>+       *      while processing with interrupts off.
>+       */
>+      if (up->msr_saved_flags)
>+              check_modem_status(up);
>+
>       if (locked)
>               spin_unlock(&up->port.lock);
>       local_irq_restore(flags);
>Index: linux-2.6.21/include/linux/serial_reg.h
>===================================================================
>--- linux-2.6.21.orig/include/linux/serial_reg.h
>+++ linux-2.6.21/include/linux/serial_reg.h
>@@ -116,6 +116,7 @@
> #define UART_LSR_PE           0x04 /* Parity error indicator */
> #define UART_LSR_OE           0x02 /* Overrun error indicator */
> #define UART_LSR_DR           0x01 /* Receiver data ready */
>+#define UART_LSR_BRK_ERROR_BITS       0x1E /* BI, FE, PE, OE bits */
>
> #define UART_MSR      6       /* In:  Modem Status Register */
> #define UART_MSR_DCD          0x80 /* Data Carrier Detect */
>-
>To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>the body of a message to [EMAIL PROTECTED]
>More majordomo info at  http://vger.kernel.org/majordomo-info.html
>Please read the FAQ at  http://www.tux.org/lkml/



-- 
Cheers, Gene
"There are four boxes to be used in defense of liberty:
 soap, ballot, jury, and ammo. Please use in that order."
-Ed Howdershelt (Author)
#define NULL 0           /* silly thing is, we don't even use this */
             -- Larry Wall in perl.c from the perl source code
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to