> > If that freaks out the CPM, put one > > byte of zero into the buffer and give it a count of 1. Then you just have > > to hold the lock across the update of the BD ptr, just like everywhere else. > > That will work, but wont the zero show up on the console? hmm, since > the zero count will be very unlikely case anyhow, I can live with that.
OK, this patch does the above. I left my test function(err_copy_from_user) to simulate bad copy_from_user() copies every now and then. Just remove it and change the call in rs_8xx_write when you have tested it. Works great for me, not even jove(emacs like editor) will break it. Jocke Index: arch/ppc/8xx_io/uart.c =================================================================== RCS file: /home/cvsadmin/cvsroot/kernel/linuxppc/arch/ppc/8xx_io/uart.c,v retrieving revision 1.7 diff -u -r1.7 uart.c --- arch/ppc/8xx_io/uart.c 13 Jun 2003 09:04:43 -0000 1.7 +++ arch/ppc/8xx_io/uart.c 27 Sep 2003 13:29:53 -0000 @@ -81,7 +81,11 @@ #define TX_WAKEUP ASYNC_SHARE_IRQ static char *serial_name = "CPM UART driver"; -static char *serial_version = "0.03"; +static char *serial_version = "0.04"; + +/* TX buffer length used by my_console_write. + Assume minumun length until it gets set by this driver */ +static int console_tx_buf_len = 1; static DECLARE_TASK_QUEUE(tq_serial); @@ -196,6 +200,7 @@ /* The number of buffer descriptors and their sizes. */ +#define EARLY_BUF_SIZE 4 #define RX_NUM_FIFO 4 #define RX_BUF_SIZE 32 #define TX_NUM_FIFO 4 @@ -1068,6 +1073,7 @@ ser_info_t *info = (ser_info_t *)tty->driver_data; volatile cbd_t *bdp; unsigned char *cp; + unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_put_char")) return; @@ -1075,7 +1081,16 @@ if (!tty) return; + local_irq_save(flags); bdp = info->tx_cur; + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + info->tx_cur = info->tx_bd_base; + else + info->tx_cur = (cbd_t *)bdp + 1; + local_irq_restore(flags); + while (bdp->cbd_sc & BD_SC_READY); cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); @@ -1083,14 +1098,20 @@ bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; - /* Get next BD. - */ - if (bdp->cbd_sc & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; +} +int err_copy_from_user(void * to, void* from, int len) +{ + static int err; + int res; + + err = (err+1)%13; - info->tx_cur = (cbd_t *)bdp; + res = copy_from_user(to, from, len); + if(!err && len > 1) + return len-1; /* partial copy */ + if(!err) + return len; /* nothing copied */ + return res; } static int rs_8xx_write(struct tty_struct * tty, int from_user, @@ -1100,6 +1121,7 @@ ser_info_t *info = (ser_info_t *)tty->driver_data; volatile cbd_t *bdp; unsigned char *cp; + unsigned long flags; #ifdef CONFIG_KGDB_CONSOLE /* Try to let stub handle output. Returns true if it did. */ @@ -1113,44 +1135,47 @@ if (!tty) return 0; - bdp = info->tx_cur; - while (1) { c = MIN(count, TX_BUF_SIZE); if (c <= 0) break; + local_irq_save(flags); + bdp = info->tx_cur; if (bdp->cbd_sc & BD_SC_READY) { info->flags |= TX_WAKEUP; + local_irq_restore(flags); break; } + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + info->tx_cur = info->tx_bd_base; + else + info->tx_cur = (cbd_t *)bdp + 1; + local_irq_restore(flags); cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); - if (from_user) { - if (copy_from_user((void *)cp, buf, c)) { - if (!ret) - ret = -EFAULT; - break; - } - } else { + if (from_user) + c -= err_copy_from_user((void *)cp, buf, c); + else memcpy((void *)cp, buf, c); + + if (c) { + bdp->cbd_datlen = c; + bdp->cbd_sc |= BD_SC_READY; + } else { + bdp->cbd_datlen = 1;/* Need to TX at least 1 char to keep CPM sane */ + *cp = 0; + bdp->cbd_sc |= BD_SC_READY; + if (!ret) + ret = -EFAULT; + break; } - - bdp->cbd_datlen = c; - bdp->cbd_sc |= BD_SC_READY; - buf += c; count -= c; ret += c; - - /* Get next BD. - */ - if (bdp->cbd_sc & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; - info->tx_cur = (cbd_t *)bdp; } return ret; } @@ -1210,28 +1235,28 @@ { volatile cbd_t *bdp; unsigned char *cp; + unsigned long flags; ser_info_t *info = (ser_info_t *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_send_char")) return; + local_irq_save(flags); bdp = info->tx_cur; + /* Get next BD. + */ + if (bdp->cbd_sc & BD_SC_WRAP) + info->tx_cur = info->tx_bd_base; + else + info->tx_cur = (cbd_t *)bdp + 1; + local_irq_restore(flags); while (bdp->cbd_sc & BD_SC_READY); cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); *cp = ch; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; - - /* Get next BD. - */ - if (bdp->cbd_sc & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; - - info->tx_cur = (cbd_t *)bdp; } /* @@ -1802,7 +1827,7 @@ static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout) { ser_info_t *info = (ser_info_t *)tty->driver_data; - unsigned long orig_jiffies, char_time; + unsigned long orig_jiffies, char_time, tst_res; /*int lsr;*/ volatile cbd_t *bdp; @@ -1837,6 +1862,7 @@ * are empty. */ do { + unsigned long flags; #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...", lsr, jiffies); #endif @@ -1854,12 +1880,15 @@ * is the buffer is available. There are still characters * in the CPM FIFO. */ + local_irq_save(flags); bdp = info->tx_cur; if (bdp == info->tx_bd_base) bdp += (TX_NUM_FIFO-1); else bdp--; - } while (bdp->cbd_sc & BD_SC_READY); + tst_res = !!(bdp->cbd_sc & BD_SC_READY); + local_irq_restore(flags); + } while (tst_res); current->state = TASK_RUNNING; #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); @@ -2261,10 +2290,11 @@ { struct serial_state *ser; ser_info_t *info; - unsigned i; + unsigned i, c, cr_missing, max_tx_size; volatile cbd_t *bdp, *bdbase; volatile smc_uart_t *up; volatile u_char *cp; + unsigned long flags; ser = rs_table + idx; @@ -2273,40 +2303,39 @@ * we simply use the single buffer allocated. */ if ((info = (ser_info_t *)ser->info) != NULL) { - bdp = info->tx_cur; bdbase = info->tx_bd_base; - } - else { - /* Pointer to UART in parameter ram. - */ + } else { + /* Pointer to UART in parameter ram. */ up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; - - /* Get the address of the host memory buffer. - */ - bdp = bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + /* Get the address of the host memory buffer.*/ info = &consinfo; + info->tx_bd_base = (cbd_t *)bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + info->tx_cur = (cbd_t *)bdbase; } + max_tx_size = console_tx_buf_len; + cr_missing = 0; + while (1){ + c = MIN(max_tx_size, count); + if (c <= 0) + break; - /* - * We need to gracefully shut down the transmitter, disable - * interrupts, then send our bytes out. - */ - - /* - * Now, do each character. This is not as bad as it looks - * since this is a holding FIFO and not a transmitting FIFO. - * We could add the complexity of filling the entire transmit - * buffer, but we would just wait longer between accesses...... - */ - for (i = 0; i < count; i++, s++) { + local_irq_save(flags); + bdp = info->tx_cur; + bdbase = info->tx_bd_base; + if (bdp->cbd_sc & BD_SC_WRAP) + info->tx_cur = (cbd_t *)bdbase; + else + info->tx_cur = (cbd_t *)(bdp+1); + local_irq_restore(flags); + /* Wait for transmitter fifo to empty. * Ready indicates output is ready, and xmt is doing * that, not that it is ready for us to send. */ while (bdp->cbd_sc & BD_SC_READY); - /* Send the character out. + /* Send the characters out. * If the buffer address is in the CPM DPRAM, don't * convert it. */ @@ -2314,55 +2343,32 @@ cp = (u_char *)(bdp->cbd_bufaddr); else cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); - *cp = *s; - - bdp->cbd_datlen = 1; - bdp->cbd_sc |= BD_SC_READY; - - if (bdp->cbd_sc & BD_SC_WRAP) - bdp = bdbase; - else - bdp++; - - /* if a LF, also do CR... */ - if (*s == 10) { - while (bdp->cbd_sc & BD_SC_READY); - - /* This 'if' below will never be true, but a few - * people argued with me that it was a "bug by - * inspection" that is was easier to add the code - * than continue the discussion. The only time - * the buffer address is in DPRAM is during early - * use by kgdb/xmon, which format their own packets - * and we never get here. -- Dan - */ - if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR) - cp = (u_char *)(bdp->cbd_bufaddr); - else - cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); - *cp = 13; - bdp->cbd_datlen = 1; - bdp->cbd_sc |= BD_SC_READY; - if (bdp->cbd_sc & BD_SC_WRAP) { - bdp = bdbase; - } - else { - bdp++; - } + i=1; /* Keeps track of consumed TX buffer space */ + if (cr_missing) { + /* Previus loop didn't have room for the CR, insert it first in this */ + *cp++ = '\r'; + i++; + } + cr_missing = 0; + for (; i <= c; i++) { + if ((*cp++ = *s++) != '\n') + continue; /* Copy bytes until a NewLine is found */ + /* NewLine found, see if there is space in the TX buffer to add a CR */ + if (i < max_tx_size) { + *cp++ = '\r'; /* yes, there is space to add a CR */ + i++; + } else + cr_missing = 1; /* No space in the TX buffer, + rember it so it can be inserted in the next loop */ } - } - - /* - * Finally, Wait for transmitter & holding register to empty - * and restore the IER - */ - while (bdp->cbd_sc & BD_SC_READY); + count -= (c-cr_missing); + bdp->cbd_datlen = i-1; + bdp->cbd_sc |= BD_SC_READY; - if (info) - info->tx_cur = (cbd_t *)bdp; + } + /* while (bdp->cbd_sc & BD_SC_READY); is this really needed? */ } - static void serial_console_write(struct console *c, const char *s, unsigned count) { @@ -3003,7 +3009,7 @@ } } - + console_tx_buf_len = TX_BUF_SIZE; return 0; } @@ -3059,7 +3065,7 @@ * memory yet because vm allocator isn't initialized * during this early console init. */ - dp_addr = m8xx_cpm_dpalloc(8); + dp_addr = m8xx_cpm_dpalloc(2*EARLY_BUF_SIZE); mem_addr = (uint)(&cpmp->cp_dpmem[dp_addr]); /* Allocate space for two buffer descriptors in the DP ram. @@ -3073,10 +3079,10 @@ bdp->cbd_bufaddr = iopa(mem_addr); (bdp+1)->cbd_bufaddr = iopa(mem_addr+4); - consinfo.rx_va_base = mem_addr; - consinfo.rx_bd_base = bdp; - consinfo.tx_va_base = mem_addr + 4; - consinfo.tx_bd_base = bdp+1; + consinfo.rx_va_base = (unsigned char *) mem_addr; + consinfo.rx_bd_base = (cbd_t *) bdp; + consinfo.tx_va_base = (unsigned char *) (mem_addr + EARLY_BUF_SIZE); + consinfo.tx_bd_base = (cbd_t *) (bdp+1); /* For the receive, set empty and wrap. * For transmit, set wrap. @@ -3177,7 +3183,7 @@ /* Set up the baud rate generator. */ m8xx_cpm_setbrg((ser - rs_table), bd->bi_baudrate); - + console_tx_buf_len = EARLY_BUF_SIZE; return 0; } ** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/