Re: [uClinux-dev] NET+Lx serial driver bug

From: Pontus Fuchs (pontus.fuchs@tactel.se)
Date: Wed Apr 04 2001 - 11:40:05 EDT


Hi!

> I've got really problems in using the serial device on netsilicons Net+40
> evaluation board. It is not possible to initialize the serial driver for non
> canonnical reading. After setting up the device there is no chance to read
> or write.
> Has anybody solved this problem, or is a fix available ?

Attached are 2 files.

"serial_fix.tar.gz" is by netsilicon or redhat (I don't know) and
fixes a few bugs. Apply it by unpacking in the top of the kernel
sourcetree.

"netarm-serial.diff1" is by me and fixes and fixes even more bugs.
Apply it with patch to arch/armnommu/drivers/char/serial-netarm.c.

This patch does:
* Add debug-macro.
* initialize rx_head, rx_tail and rx_cnt.
* Disable port at shutdown.
* Return number of bytes written in nas_write, not 0.
* Don't oops when sending break.
* Setting baudrate via nas_set_termios now works.
* Don't crash when recieving other rx_interrupt than data
  available (break for example).

Hope this helps you. If not, go chase more bugs (there definilty are).

/Pontus Fuchs

--- serial-netarm.c.orig	Thu Dec 28 15:59:54 2000
+++ serial-netarm.c	Tue Jan  9 17:24:33 2001
@@ -32,8 +32,11 @@
 
 #include	<linux/autoconf.h>
 
+/* For debugging */
 #if 0
-#define	NAS_DEBUG_VERBOSE	(1)
+#define NAS_DEBUG(args...) printk(args)
+#else
+#define NAS_DEBUG(args...)
 #endif
 
 #if !defined(MODULE) && !defined(CONFIG_ARCH_NETARM)
@@ -404,9 +407,9 @@
 	}
   }
   
-  port->tx_head = 0;
-  port->tx_tail = 0;
-  port->tx_cnt = 0;
+  port->rx_head = 0;
+  port->rx_tail = 0;
+  port->rx_cnt = 0;
 
   if ( NULL != port->tx_buf )
   {
@@ -421,9 +424,7 @@
   port->tx_tail = 0;
   port->tx_cnt = 0;
 
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_startup_port: setting port at 0x%08x to %d baud\n", (unsigned int)port, port->baud);
-#endif
+  NAS_DEBUG("nas_startup_port: setting port at 0x%08x to %d baud\n", (unsigned int)port, port->baud);
 
   regs = port->registers;
   regs->rx_match = 0;
@@ -437,9 +438,9 @@
   regs->ctrl_b = NETARM_SER_CTLB_RCGT_EN | NETARM_SER_CTLB_UART_MODE;
 #ifdef	POLLED_SERIAL
   regs->ctrl_a |= NETARM_SER_CTLA_ENABLE ;
-  
+
 #else
-  regs->ctrl_a |= NETARM_SER_CTLA_ENABLE | NETARM_SER_CTLA_IE_RX_RDY | 
+  regs->ctrl_a |= NETARM_SER_CTLA_ENABLE | NETARM_SER_CTLA_IE_RX_RDY |
     NETARM_SER_CTLA_IE_RX_FULL ;
 #endif
   
@@ -465,14 +466,10 @@
   /* I assume we should free the tx buffer, but  */
   /* is the tty driver above still using the rx buf ??? */
 
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_shutdown_port: disabling port at 0x%08x\n", (unsigned int)port);
-#endif
+  NAS_DEBUG("nas_shutdown_port: disabling port at 0x%08x\n", (unsigned int)port);
 
-#if 0
   /* turn off the port */
-  regs->ctrl_a |= ~NETARM_SER_CTLA_ENABLE ;
-#endif
+  regs->ctrl_a &= ~NETARM_SER_CTLA_ENABLE ;
 
   return 0;
 }
@@ -495,7 +492,7 @@
   major = MAJOR(tty->device);
   line = MINOR(tty->device) - tty->driver.minor_start;
 
-  printk("nas_open: major: %d, minor:%d\n", major, line);
+  NAS_DEBUG("nas_open: major: %d, minor:%d\n", major, line);
 #if 0
   netarm_dump_int_stats();
   printk("port 1 status a 0x%08x\n",(nas_ports[1].registers)->status_a);
@@ -551,10 +548,7 @@
 {
   struct nas_port *port = (struct nas_port *) tty->driver_data;
 
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_close: called\n");
-#endif
-  printk("nas_close: called\n");
+  NAS_DEBUG(__FUNCTION__ "()\n");
 
   if (port) {
     if (--port->count < 0) {
@@ -564,10 +558,8 @@
     }
     if (port->count) {
       MOD_DEC_USE_COUNT;
-#ifdef NAS_DEBUG_VERBOSE
-      printk("nas_close: not really closing, since count is %d (not 0)\n",
+      NAS_DEBUG(__FUNCTION__ "() Not really closing, since count is %d (not 0)\n",
 	     port->count);
-#endif
       return;
     }
   }
@@ -603,9 +595,9 @@
   struct nas_port *port = (struct nas_port *) tty->driver_data;
   int free_space;
   unsigned char *fifo;
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_write: called with %d chars\n", count);
-#endif
+  int actual = 0;
+
+  NAS_DEBUG(__FUNCTION__ "() Going to write %d chars\n", count);
 
   free_space = ( NAS_BUF_SIZE - 1 ) - port->tx_tail ;
 
@@ -620,9 +612,11 @@
     *fifo = *buf;
     buf++ ;
     count--;
+    actual++;
   }
 
-  return count; // not supported
+  NAS_DEBUG(__FUNCTION__ "() Wrote %d chars\n", actual);
+  return actual;
 }
 
 //
@@ -631,9 +625,7 @@
 static void 
 nas_flush_chars(struct tty_struct *tty)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_flush_chars: called\n");
-#endif
+  NAS_DEBUG(__FUNCTION__ "()\n");
 }
 
 //
@@ -645,9 +637,8 @@
 {
   struct nas_port *port = (struct nas_port *) tty->driver_data;
   int free_space;
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_write_room: called\n");
-#endif
+
+  NAS_DEBUG(__FUNCTION__ "()\n");
 
   free_space = ( NAS_BUF_SIZE - 1 ) - port->tx_tail ;
 
@@ -662,9 +653,8 @@
 {
   struct nas_port *port = (struct nas_port *) tty->driver_data;
   int chars_in;
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_chars_in_buffer: called\n");
-#endif
+
+  NAS_DEBUG(__FUNCTION__ "()\n");
 
   chars_in = port->tx_tail - port->tx_head ;
 
@@ -677,9 +667,7 @@
 static void 
 nas_flush_buffer(struct tty_struct *tty)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_flush_buffer: called\n");
-#endif
+  NAS_DEBUG(__FUNCTION__ "()\n");
 }
 
 //
@@ -687,14 +675,14 @@
 //
 
 static void
-nas_send_break(struct tty_struct *tty, int duration)
+nas_send_break(struct nas_port *port, int duration)
 {
-  struct nas_port *port = (struct nas_port *) tty->driver_data;
   volatile netarm_serial_channel_t *regs = port->registers;
 
   /* set the SEND BREAK bit */
-  regs->ctrl_a &= ~NETARM_SER_CTLA_BRK ;
- 
+  regs->ctrl_a |= NETARM_SER_CTLA_BRK ;
+//  regs->ctrl_a &= ~NETARM_SER_CTLA_BRK ;
+
   /* let the system run... */
   current->state = TASK_INTERRUPTIBLE ;
   current->timeout = jiffies+duration ;
@@ -715,13 +703,11 @@
   int retval;
   struct nas_port *port = (struct nas_port *) tty->driver_data;
 
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_ioctl: called, cmd = 0x%x, arg = 0x%x\n", cmd, arg);
-#endif
   switch (cmd)
   {
   case TCSBRK:
     /* send BRK to line */
+    NAS_DEBUG(__FUNCTION__ "() Send break\n");
     retval = tty_check_change(tty);
     if (retval)
       return retval;
@@ -730,8 +716,10 @@
       nas_send_break(port,HZ/4);
     return 0;
     break;
+
   case TCSBRKP:
     /* send BRK to line */
+    NAS_DEBUG(__FUNCTION__ "() Send break\n");
     retval = tty_check_change(tty);
     if (retval)
       return retval;
@@ -739,16 +727,19 @@
     nas_send_break(port,arg ? arg*(HZ/10) : HZ/4);
     return 0;
     break;
+
   default:
-    if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+    NAS_DEBUG(__FUNCTION__ "() Unsupported ioctl cmd = 0x%x, arg = 0x%x\n", (int) cmd, (int) arg);
+    return -ENOIOCTLCMD; // not supported
+/*    if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
 	(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
 	(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+        NAS_DEBUG(__FUNCTION__ "() cmd = 0x%x, arg = 0x%x\n", (int) cmd, (int) arg);
 	if (tty->flags & (1 << TTY_IO_ERROR))
 	    return -EIO;
     }
+*/
   }
-
-  return -ENOIOCTLCMD; // not supported
 }
 
 //
@@ -759,9 +750,7 @@
 static void 
 nas_throttle(struct tty_struct * tty)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_throttle: called\n");
-#endif
+  NAS_DEBUG(__FUNCTION__ "()\n");
 }
 
 //
@@ -772,9 +761,7 @@
 static void 
 nas_unthrottle(struct tty_struct * tty)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_unthrottle: called\n");
-#endif
+  NAS_DEBUG(__FUNCTION__ "()\n");
 }
 
 //
@@ -785,9 +772,17 @@
 static void
 nas_set_termios(struct tty_struct *tty, struct termios *old_termios)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_set_termios: called\n");
-#endif
+  struct nas_port *port = (struct nas_port *) tty->driver_data;
+  volatile netarm_serial_channel_t *regs = port->registers;
+  int baud;
+
+  NAS_DEBUG(__FUNCTION__ "()\n");
+
+  /* At least set baudrate */
+  baud = nas_tty_get_baud_rate(port->tty);
+  port->baud = baud;
+  regs->bitrate = NETARM_SER_BR_X16(baud);
+  regs->rx_char_timer = NETARM_SER_RXGAP(baud);
 }
 
 //
@@ -800,9 +795,7 @@
 static void 
 nas_stop(struct tty_struct *tty)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_stop: called\n");
-#endif
+  NAS_DEBUG(__FUNCTION__ "()\n");
 }
 
 //
@@ -815,9 +808,7 @@
 static void 
 nas_start(struct tty_struct *tty)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_start: called\n");
-#endif
+  NAS_DEBUG(__FUNCTION__ "()\n");
 }
 
 //
@@ -827,9 +818,7 @@
 static void 
 nas_hangup(struct tty_struct *tty)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_hangup: called\n");
-#endif
+  NAS_DEBUG(__FUNCTION__ "()\n");
 }
 
 //
@@ -901,25 +890,23 @@
 }
 
 #if defined(CONFIG_ARCH_NETARM)
+
+
 //
 // Handler for transmit interrupts
 //
 static void
 nas_tx_interrupt_1(int irq, void *dev_id, struct pt_regs *regs)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_tx_interrupt_0: int!\n");
-#endif
-
+  NAS_DEBUG(__FUNCTION__ "() int!\n");
   nas_tx_interrupt(&nas_ports[0]) ;
 }
 
 static void
 nas_tx_interrupt_2(int irq, void *dev_id, struct pt_regs *regs)
 {
-#ifdef	NAS_DEBUG_VERBOSE
+  NAS_DEBUG(__FUNCTION__ "() int!\n");
   printk("nas_tx_interrupt_1: int!\n");
-#endif
 
   nas_tx_interrupt(&nas_ports[0]) ;
 }
@@ -927,14 +914,15 @@
 static void
 nas_tx_interrupt(struct nas_port *port)
 {
+#if 0
   struct tty_struct *tty = port->tty;
   char ch;
   int char_count = 0;
   unsigned int status;
   volatile netarm_serial_channel_t *regs = port->registers ;
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_tx_interrupt: int!\n");
 #endif
+
+  NAS_DEBUG(__FUNCTION__ "() int!\n");
 }
 
 //
@@ -943,20 +931,14 @@
 static void
 nas_rx_interrupt_1(int irq, void *dev_id, struct pt_regs *regs)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_rx_interrupt: int!\n");
-#endif
-
+  NAS_DEBUG(__FUNCTION__ "() int!\n");
   nas_rx_interrupt(&nas_ports[0]) ;
 }
 
 static void
 nas_rx_interrupt_2(int irq, void *dev_id, struct pt_regs *regs)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_rx_interrupt: int!\n");
-#endif
-
+  NAS_DEBUG(__FUNCTION__ "() int!\n");
   nas_rx_interrupt(&nas_ports[1]) ;
 }
 
@@ -965,32 +947,29 @@
 {
   struct tty_struct *tty = port->tty;
   unsigned int ch_uint;
-  char ch;
-  int ch_count;
   int buf_count;
   unsigned int status;
   volatile unsigned int *fifo;
   volatile unsigned char *fifo_char;
   volatile netarm_serial_channel_t *regs = port->registers ;
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_rx_interrupt: int!\n");
-#endif
-  /*printk("nas_rx_interrupt: int!\n");*/
 
-  ch_count = 0 ;
+//  NAS_DEBUG("nas_rx_interrupt: int!\n");
 
+  buf_count = 0 ;
   fifo = (volatile unsigned int*)&(regs->fifo) ;
 
+  /* Service any pending interupts */
   do
   {
     status = regs->status_a  ;
+    fifo_char = (volatile unsigned char*) &ch_uint ;
+    /* FIXME : do we need to mask the status ??? as in serial.c */
+
     if (status & NETARM_SER_STATA_RX_RDY )
     {
       /* need to read the whole buffer (1 to 4 bytes) */
       ch_uint = *fifo ;
 
-      fifo_char = &ch_uint ;
-      
       /* check the status word to see how many bytes are pending */
       buf_count = NETARM_SER_STATA_RXFDB(status) ; 
       
@@ -1009,28 +988,19 @@
       	buf_count = 1 ;
 	break;
       default:
-      	printk("AIEE!! GARBAGE!\n");
+      	NAS_DEBUG(__FUNCTION__ "() AIEE!! GARBAGE!\n");
       }
       /*printk("nas_rx_interrupt: read : %c\n",ch);*/
-#if 1
+#if 0
     regs->status_a = NETARM_SER_STATA_CLR_ALL ;
 #endif
     }
-    else
-    {
-      ch = 0;
-    }
-    
-    /* don't want to overrun */
-    if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-    {
-      break;
-    }
-    
+
     /* handle port error conditions */
     if (status & NETARM_SER_STATA_RX_BRK )
     {
       *tty->flip.flag_buf_ptr = TTY_BREAK ;
+      NAS_DEBUG(__FUNCTION__ "() got break\n");
       /* FIXME : do we need to handle ASYNC_SAK ???  as in serial.c */
     }
     else if ( status & NETARM_SER_STATA_RX_PARERR )
@@ -1053,24 +1023,26 @@
       *tty->flip.flag_buf_ptr = 0 ;
     }
     
-#if 0
+    /* Now clear all interrupts */
     regs->status_a = NETARM_SER_STATA_CLR_ALL ;
-#endif
-     
+
+    /* don't want to overrun */
+    if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+    {
+      break;
+    }
+
     while ( buf_count-- > 0 )
     {
-    tty->flip.count++;
+      tty->flip.count++;
 
-    /* ohyea... handle data also */
-    *tty->flip.char_buf_ptr++ = *fifo_char ;
-    fifo_char++ ;
+      /* ohyea... handle data also */
+      *tty->flip.char_buf_ptr++ = *fifo_char ;
+      fifo_char++ ;
     }
 
-    /* read status anew */
-    status = regs->status_a ;
-    /* FIXME : do we need to mask the status ??? as in serial.c */
   } while ( status & NETARM_SER_STATA_RX_RDY ) ;
-  
+
   regs->ctrl_a |= NETARM_SER_CTLA_ENABLE | NETARM_SER_CTLA_IE_RX_RDY |
     NETARM_SER_CTLA_IE_RX_FULL ;
 
@@ -1131,9 +1103,8 @@
 static void 
 nas_do_serial_bh(void)
 {
-#ifdef	NAS_DEBUG_VERBOSE
-  printk("nas_do_serial_bh: called\n");
-#endif
+  NAS_DEBUG(__FUNCTION__ "()\n");
+
 #ifdef	POLLED_SERIAL
   run_task_queue(&poll_serial_tqueue);
 #endif


This message resent by the uclinux-dev@uclinux.org list server http://www.uClinux.org/


This archive was generated by hypermail 2.1.4 : Thu Sep 19 2002 - 13:19:38 EDT