--- /dev/null
+#include <minix/config.h>
+#include <minix/drivers.h>
+#include <minix/vm.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <signal.h>
+#include <termios.h>
+#include "omap_serial.h"
+#include "tty.h"
+
+#if NR_RS_LINES > 0
+
+#define UART_FREQ 48000000L /* timer frequency */
+#if 0
+#define DFLT_BAUD TSPEED_DEF /* default baud rate */
+#else
+#define DFLT_BAUD B115200 /* default baud rate */
+#endif
+
+
+#define RS_IBUFSIZE 1024 /* RS232 input buffer size */
+#define RS_OBUFSIZE 1024 /* RS232 output buffer size */
+
+/* Input buffer watermarks.
+ * The external device is asked to stop sending when the buffer
+ * exactly reaches high water, or when TTY requests it. Sending restarts
+ * when the input buffer empties below the low watermark.
+ */
+#define RS_ILOWWATER (1 * RS_IBUFSIZE / 4)
+#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
+
+/* Output buffer low watermark.
+ * TTY is notified when the output buffer empties below the low watermark, so
+ * it may continue filling the buffer if doing a large write.
+ */
+#define RS_OLOWWATER (1 * RS_OBUFSIZE / 4)
+
+/* Macros to handle flow control.
+ * Interrupts must be off when they are used.
+ * Time is critical - already the function call for outb() is annoying.
+ * If outb() can be done in-line, tests to avoid it can be dropped.
+ * istart() tells external device we are ready by raising RTS.
+ * istop() tells external device we are not ready by dropping RTS.
+ * DTR is kept high all the time (it probably should be raised by open and
+ * dropped by close of the device).
+ * OUT2 is also kept high all the time.
+ */
+#define istart(rs) \
+ (serial_out((rs), OMAP3_MCR, UART_MCR_OUT2|UART_MCR_RTS|UART_MCR_DTR),\
+ (rs)->idevready = TRUE)
+#define istop(rs) \
+ (serial_out((rs), OMAP3_MCR, UART_MCR_OUT2|UART_MCR_DTR), \
+ (rs)->idevready = FALSE)
+
+/* Macro to tell if device is ready. The rs->cts field is set to UART_MSR_CTS
+ * if CLOCAL is in effect for a line without a CTS wire.
+ */
+#define devready(rs) ((serial_in(rs, OMAP3_MSR) | rs->cts) & UART_MSR_CTS)
+
+/* Macro to tell if transmitter is ready. */
+#define txready(rs) (serial_in(rs, OMAP3_LSR) & UART_LSR_THRE)
+
+/* RS232 device structure, one per device. */
+typedef struct rs232 {
+ tty_t *tty; /* associated TTY structure */
+
+ int icount; /* number of bytes in the input buffer */
+ char *ihead; /* next free spot in input buffer */
+ char *itail; /* first byte to give to TTY */
+ char idevready; /* nonzero if we are ready to receive (RTS) */
+ char cts; /* normally 0, but MS_CTS if CLOCAL is set */
+
+ unsigned char ostate; /* combination of flags: */
+#define ODONE 1 /* output completed (< output enable bits) */
+#define ORAW 2 /* raw mode for xoff disable (< enab. bits) */
+#define OWAKEUP 4 /* tty_wakeup() pending (asm code only) */
+#define ODEVREADY UART_MSR_CTS /* external device hardware ready (CTS) */
+#define OQUEUED 0x20 /* output buffer not empty */
+#define OSWREADY 0x40 /* external device software ready (no xoff) */
+#define ODEVHUP UART_MSR_DCD /* external device has dropped carrier */
+#define OSOFTBITS (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY)
+ /* user-defined bits */
+#if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS
+ /* a weak sanity check */
+#error /* bits are not unique */
+#endif
+ unsigned char oxoff; /* char to stop output */
+ char inhibited; /* output inhibited? (follows tty_inhibited) */
+ char drain; /* if set drain output and reconfigure line */
+ int ocount; /* number of bytes in the output buffer */
+ char *ohead; /* next free spot in output buffer */
+ char *otail; /* next char to output */
+
+ phys_bytes phys_base; /* UART physical base address (I/O map) */
+ unsigned int reg_offset; /* UART register offset */
+ unsigned int ier; /* copy of ier register */
+ unsigned int scr; /* copy of scr register */
+ unsigned int fcr; /* copy of fcr register */
+ unsigned int dll; /* copy of dll register */
+ unsigned int dlh; /* copy of dlh register */
+ unsigned int uartclk; /* UART clock rate */
+
+ unsigned char lstatus; /* last line status */
+ unsigned framing_errors; /* error counts (no reporting yet) */
+ unsigned overrun_errors;
+ unsigned parity_errors;
+ unsigned break_interrupts;
+
+ int irq; /* irq for this line */
+ int irq_hook_id; /* interrupt hook */
+
+ char ibuf[RS_IBUFSIZE]; /* input buffer */
+ char obuf[RS_OBUFSIZE]; /* output buffer */
+} rs232_t;
+
+static rs232_t rs_lines[NR_RS_LINES];
+
+typedef struct uart_port {
+ phys_bytes base_addr;
+ int irq;
+} uart_port_t;
+
+/* OMAP3 UART base addresses. */
+static uart_port_t omap3[] = {
+ { OMAP3_UART1_BASE, 72}, /* UART1 */
+ { OMAP3_UART2_BASE, 73}, /* UART2 */
+ { OMAP3_UART3_BASE, 74}, /* UART3 */
+ { 0, 0 }
+};
+
+static int rs_write(tty_t *tp, int try);
+static void rs_echo(tty_t *tp, int c);
+static int rs_ioctl(tty_t *tp, int try);
+static void rs_config(rs232_t *rs);
+static int rs_read(tty_t *tp, int try);
+static int rs_icancel(tty_t *tp, int try);
+static int rs_ocancel(tty_t *tp, int try);
+static void rs_ostart(rs232_t *rs);
+static int rs_break(tty_t *tp, int try);
+static int rs_close(tty_t *tp, int try);
+static int rs_open(tty_t *tp, int try);
+static void rs232_handler(rs232_t *rs);
+static void rs_reset(rs232_t *rs);
+static unsigned int check_modem_status(rs232_t *rs);
+static int termios_baud_rate(struct termios *term);
+
+static inline unsigned int readw(vir_bytes addr);
+static inline unsigned int serial_in(rs232_t *rs, int offset);
+static inline void serial_out(rs232_t *rs, int offset, int val);
+static inline void writew(vir_bytes addr, int val);
+static void write_chars(rs232_t *rs);
+static void read_chars(rs232_t *rs, unsigned int status);
+
+static inline unsigned int
+readw(vir_bytes addr)
+{
+ return *((volatile unsigned int *) addr);
+}
+
+static inline void
+writew(vir_bytes addr, int val)
+{
+ *((volatile unsigned int *) addr) = val;
+}
+
+static inline unsigned int
+serial_in(rs232_t *rs, int offset)
+{
+ offset <<= rs->reg_offset;
+ return readw(rs->phys_base + offset);
+}
+
+static inline void
+serial_out(rs232_t *rs, int offset, int val)
+{
+ offset <<= rs->reg_offset;
+ writew(rs->phys_base + offset, val);
+}
+
+static void
+rs_reset(rs232_t *rs)
+{
+ u32_t syss;
+
+ serial_out(rs, OMAP3_SYSC, UART_SYSC_SOFTRESET);
+
+ /* Poll until done */
+ do {
+ syss = serial_in(rs, OMAP3_SYSS);
+ } while (!(syss & UART_SYSS_RESETDONE));
+}
+
+static int
+rs_write(register tty_t *tp, int try)
+{
+/* (*devwrite)() routine for RS232. */
+
+ rs232_t *rs = tp->tty_priv;
+ int r, count, ocount;
+
+ if (rs->inhibited != tp->tty_inhibited) {
+ /* Inhibition state has changed. */
+ rs->ostate |= OSWREADY;
+ if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
+ rs->inhibited = tp->tty_inhibited;
+ }
+
+ if (rs->drain) {
+ /* Wait for the line to drain then reconfigure and continue
+ * output. */
+ if (rs->ocount > 0) return 0;
+ rs->drain = FALSE;
+ rs_config(rs);
+ }
+ /* While there is something to do. */
+ for (;;) {
+ ocount = buflen(rs->obuf) - rs->ocount;
+ count = bufend(rs->obuf) - rs->ohead;
+ if (count > ocount) count = ocount;
+ if (count > tp->tty_outleft) count = tp->tty_outleft;
+ if (count == 0 || tp->tty_inhibited) {
+ if (try) return 0;
+ break;
+ }
+
+ if (try) return 1;
+
+ /* Copy from user space to the RS232 output buffer. */
+ if (tp->tty_outcaller == KERNEL) {
+ /* We're trying to print on kernel's behalf */
+ memcpy(rs->ohead,
+ (void *) tp->tty_outgrant + tp->tty_outoffset,
+ count);
+ } else {
+ if ((r = sys_safecopyfrom(tp->tty_outcaller,
+ tp->tty_outgrant, tp->tty_outoffset,
+ (vir_bytes) rs->ohead, count)) != OK) {
+ return 0;
+ }
+ }
+
+ /* Perform output processing on the output buffer. */
+ out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count,
+ &ocount);
+ if (count == 0) {
+ break;
+ }
+
+ /* Assume echoing messed up by output. */
+ tp->tty_reprint = TRUE;
+
+ /* Bookkeeping. */
+ rs->ocount += ocount;
+ rs_ostart(rs);
+ if ((rs->ohead += ocount) >= bufend(rs->obuf))
+ rs->ohead -= buflen(rs->obuf);
+ tp->tty_outoffset += count;
+ tp->tty_outcum += count;
+ if ((tp->tty_outleft -= count) == 0) {
+ /* Output is finished, reply to the writer. */
+ if(tp->tty_outrepcode == TTY_REVIVE) {
+ notify(tp->tty_outcaller);
+ tp->tty_outrevived = 1;
+ } else {
+ tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
+ tp->tty_outproc, tp->tty_outcum);
+ tp->tty_outcum = 0;
+ }
+ }
+
+ }
+ if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
+ /* Oops, the line has hung up. */
+ if(tp->tty_outrepcode == TTY_REVIVE) {
+ notify(tp->tty_outcaller);
+ tp->tty_outrevived = 1;
+ } else {
+ tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
+ tp->tty_outproc, EIO);
+ tp->tty_outleft = tp->tty_outcum = 0;
+ }
+ }
+
+ return 1;
+}
+
+static void
+rs_echo(tty_t *tp, int character)
+{
+/* Echo one character. (Like rs_write, but only one character, optionally.) */
+
+ rs232_t *rs = tp->tty_priv;
+ int count, ocount;
+
+ ocount = buflen(rs->obuf) - rs->ocount;
+ if (ocount == 0) return; /* output buffer full */
+ count = 1;
+ *rs->ohead = character; /* add one character */
+
+ out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
+ if (count == 0) return;
+
+ rs->ocount += ocount;
+ rs_ostart(rs);
+ if ((rs->ohead += ocount) >= bufend(rs->obuf))
+ rs->ohead -= buflen(rs->obuf);
+}
+
+static int
+rs_ioctl(tty_t *tp, int UNUSED(dummy))
+{
+/* Reconfigure the line as soon as the output has drained. */
+ rs232_t *rs = tp->tty_priv;
+
+ rs->drain = TRUE;
+ return 0; /* dummy */
+}
+
+static unsigned int
+omap_get_divisor(rs232_t *rs, unsigned int baud)
+{
+/* Calculate divisor value. The 16750 has two oversampling modes to reach
+ * high baud rates with little error rate (see table 17-1 in OMAP TRM).
+ * Baud rates 460800, 921600, 1843200, and 3686400 use 13x oversampling,
+ * the other rates 16x. The baud rate is calculated as follows:
+ * baud rate = (functional clock / oversampling) / divisor.
+ */
+
+ unsigned int oversampling;
+ assert(baud != 0);
+
+ switch(baud) {
+ case B460800: /* Fall through */
+ case B921600: /* Fall through */
+ case B1843200: /* Fall through */
+ case B3686400: oversampling = 13; break;
+ default: oversampling = 16;
+ }
+
+ return (rs->uartclk / oversampling) / baud;
+}
+
+static int
+termios_baud_rate(struct termios *term)
+{
+ int baud;
+ switch(term->c_ospeed) {
+ case B0: term->c_ospeed = DFLT_BAUD; baud = termios_baud_rate(term);
+ case B300: baud = 300; break;
+ case B600: baud = 600; break;
+ case B1200: baud = 1200; break;
+ case B2400: baud = 2400; break;
+ case B4800: baud = 4800; break;
+ case B9600: baud = 9600; break;
+ case B38400: baud = 38400; break;
+ case B57600: baud = 57600; break;
+ case B115200: baud = 115200; break;
+ default: term->c_ospeed = DFLT_BAUD; baud = termios_baud_rate(term);
+ }
+
+ return baud;
+}
+static void rs_config(rs232_t *rs)
+{
+/* Set various line control parameters for RS232 I/O. */
+ tty_t *tp = rs->tty;
+ unsigned int divisor, efr, lcr, mcr, baud;
+
+ /* Fifo and DMA settings */
+ /* See OMAP35x TRM 17.5.1.1.2 */
+ lcr = serial_in(rs, OMAP3_LCR); /* 1a */
+ serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B); /* 1b */
+ efr = serial_in(rs, OMAP3_EFR); /* 2a */
+ serial_out(rs, OMAP3_EFR, efr | UART_EFR_ECB); /* 2b */
+ serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_A); /* 3 */
+ mcr = serial_in(rs, OMAP3_MCR); /* 4a */
+ serial_out(rs, OMAP3_MCR, mcr | UART_MCR_TCRTLR); /* 4b */
+ /* Set up FIFO for 1 byte */
+ rs->fcr = 0;
+ rs->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
+ rs->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT);
+ serial_out(rs, OMAP3_FCR, rs->fcr); /* 5 */
+ serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B); /* 6 */
+ /* DMA triggers, not supported by this driver */ /* 7 */
+ rs->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK;
+ serial_out(rs, OMAP3_SCR, rs->scr); /* 8 */
+ serial_out(rs, OMAP3_EFR, efr); /* 9 */
+ serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_A); /* 10 */
+ serial_out(rs, OMAP3_MCR, mcr); /* 11 */
+ serial_out(rs, OMAP3_LCR, lcr); /* 12 */
+
+ /* RS232 needs to know the xoff character, and if CTS works. */
+ rs->oxoff = tp->tty_termios.c_cc[VSTOP];
+ rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? UART_MSR_CTS : 0;
+ baud = termios_baud_rate(&tp->tty_termios);
+
+ /* Look up the 16750 rate divisor from the output speed. */
+ divisor = omap_get_divisor(rs, baud);
+ rs->dll = divisor & 0xFF;
+ rs->dlh = divisor >> 8;
+
+ /* Compute line control flag bits. */
+ lcr = 0;
+ if (tp->tty_termios.c_cflag & PARENB) {
+ lcr |= UART_LCR_PARITY;
+ if (!(tp->tty_termios.c_cflag & PARODD)) lcr |= UART_LCR_EPAR;
+ }
+ if (tp->tty_termios.c_cflag & CSTOPB) lcr |= UART_LCR_STOP;
+ switch(tp->tty_termios.c_cflag & CSIZE) {
+ case CS5:
+ lcr |= UART_LCR_WLEN5;
+ break;
+ case CS6:
+ lcr |= UART_LCR_WLEN6;
+ break;
+ case CS7:
+ lcr |= UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ lcr |= UART_LCR_WLEN8;
+ break;
+ }
+
+ /* Lock out interrupts while setting the speed. The receiver register
+ * is going to be hidden by the div_low register, but the input
+ * interrupt handler relies on reading it to clear the interrupt and
+ * avoid looping forever.
+ */
+
+ if (sys_irqdisable(&rs->irq_hook_id) != OK)
+ panic("unable to disable interrupts");
+
+ /* Select the baud rate divisor registers and change the rate. */
+ /* See OMAP35x TRM 17.5.1.1.3 */
+ serial_out(rs, OMAP3_MDR1, OMAP_MDR1_DISABLE); /* 1 */
+ serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B); /* 2 */
+ efr = serial_in(rs, OMAP3_EFR); /* 3a */
+ serial_out(rs, OMAP3_EFR, efr | UART_EFR_ECB); /* 3b */
+ serial_out(rs, OMAP3_LCR, 0); /* 4 */
+ serial_out(rs, OMAP3_IER, 0); /* 5 */
+ serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B); /* 6 */
+ serial_out(rs, OMAP3_DLL, rs->dll); /* 7 */
+ serial_out(rs, OMAP3_DLH, rs->dlh); /* 7 */
+ serial_out(rs, OMAP3_LCR, 0); /* 8 */
+ serial_out(rs, OMAP3_IER, rs->ier); /* 9 */
+ serial_out(rs, OMAP3_LCR, UART_LCR_CONF_MODE_B); /* 10 */
+ serial_out(rs, OMAP3_EFR, efr); /* 11 */
+ serial_out(rs, OMAP3_LCR, lcr); /* 12 */
+ if (baud > 230400 && baud != 3000000)
+ serial_out(rs, OMAP3_MDR1, OMAP_MDR1_MODE13X); /* 13 */
+ else
+ serial_out(rs, OMAP3_MDR1, OMAP_MDR1_MODE16X);
+
+ rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads MSR */
+ if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
+ rs->ostate &= ~ORAW;
+ (void) serial_in(rs, OMAP3_IIR);
+ if (sys_irqenable(&rs->irq_hook_id) != OK)
+ panic("unable to enable interrupts");
+}
+
+void
+rs_init(tty_t *tp)
+{
+/* Initialize RS232 for one line. */
+ register rs232_t *rs;
+ int line;
+ uart_port_t this_omap3;
+ char l[10];
+ struct minix_mem_range mr;
+
+ /* Associate RS232 and TTY structures. */
+ line = tp - &tty_table[NR_CONS];
+
+ /* See if kernel debugging is enabled; if so, don't initialize this
+ * serial line, making tty not look at the irq and returning ENXIO
+ * for all requests on it from userland. (The kernel will use it.)
+ */
+ if(env_get_param(SERVARNAME, l, sizeof(l)-1) == OK && atoi(l) == line){
+ printf("TTY: rs232 line %d not initialized (used by kernel)\n",
+ line);
+ return;
+ }
+
+ rs = tp->tty_priv = &rs_lines[line];
+ rs->tty = tp;
+
+ /* Set up input queue. */
+ rs->ihead = rs->itail = rs->ibuf;
+
+ this_omap3 = omap3[line];
+ if (this_omap3.base_addr == 0) return;
+
+ /* Configure memory access */
+ mr.mr_base = rs->phys_base;
+ mr.mr_limit = rs->phys_base + 0x100;
+ if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) {
+ panic("Unable to request access to UART memory");
+ }
+ rs->phys_base = (vir_bytes) vm_map_phys(SELF,
+ (void *) this_omap3.base_addr, 0x100);
+
+ if (rs->phys_base == (vir_bytes) MAP_FAILED) {
+ panic("Unable to request access to UART memory");
+ }
+ rs->reg_offset = 2;
+
+ rs->uartclk = UART_FREQ;
+ rs->ohead = rs->otail = rs->obuf;
+
+ /* Override system default baud rate. We do this because u-boot
+ * configures the UART for a baud rate of 115200 b/s and the kernel
+ * directly sends data over serial out upon boot up. If we then
+ * suddenly change the settings, the output will be garbled during
+ * booting.
+ */
+ tp->tty_termios.c_ospeed = DFLT_BAUD;
+
+ /* Configure IRQ */
+ rs->irq = this_omap3.irq;
+ rs->irq_hook_id = 1 << line; /* call back with irq line number */
+ if (sys_irqsetpolicy(rs->irq, 0, &rs->irq_hook_id) != OK) {
+ printf("RS232: Couldn't obtain hook for irq %d\n", rs->irq);
+ } else {
+ if (sys_irqenable(&rs->irq_hook_id) != OK) {
+ printf("RS232: Couldn't enable irq %d (hooked)\n",
+ rs->irq);
+ }
+ }
+ rs_irq_set |= (1 << (rs->irq_hook_id + 1));
+
+ /* Enable interrupts */
+ rs_reset(rs);
+ rs->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_MSI;
+ rs_config(rs);
+
+ /* Fill in TTY function hooks. */
+ tp->tty_devread = rs_read;
+ tp->tty_devwrite = rs_write;
+ tp->tty_echo = rs_echo;
+ tp->tty_icancel = rs_icancel;
+ tp->tty_ocancel = rs_ocancel;
+ tp->tty_ioctl = rs_ioctl;
+ tp->tty_break = rs_break;
+ tp->tty_open = rs_open;
+ tp->tty_close = rs_close;
+
+ /* Tell external device we are ready. */
+ istart(rs);
+}
+
+void
+rs_interrupt(message *m)
+{
+ unsigned long irq_set;
+ int line;
+ rs232_t *rs;
+
+ irq_set = m->NOTIFY_ARG;
+ for (line = 0, rs = rs_lines; line < NR_RS_LINES; line++, rs++) {
+ if (irq_set & (1 << (rs->irq_hook_id+1))) {
+ rs232_handler(rs);
+ if (sys_irqenable(&rs->irq_hook_id) != OK)
+ panic("unable to enable interrupts");
+ }
+ }
+}
+
+static int
+rs_icancel(tty_t *tp, int UNUSED(dummy))
+{
+/* Cancel waiting input. */
+ rs232_t *rs = tp->tty_priv;
+
+ rs->icount = 0;
+ rs->itail = rs->ihead;
+ istart(rs);
+ return 0; /* dummy */
+}
+
+static int
+rs_ocancel(tty_t *tp, int UNUSED(dummy))
+{
+/* Cancel pending output. */
+ rs232_t *rs = tp->tty_priv;
+
+ rs->ostate &= ~(ODONE | OQUEUED);
+ rs->ocount = 0;
+ rs->otail = rs->ohead;
+
+ return 0; /* dummy */
+}
+
+static int
+rs_read(tty_t *tp, int try)
+{
+/* Process characters from the circular input buffer. */
+
+ rs232_t *rs = tp->tty_priv;
+ int icount, count, ostate;
+
+ if (!(tp->tty_termios.c_cflag & CLOCAL)) {
+ if (try) return 1;
+
+ /* Send a SIGHUP if hangup detected. */
+ ostate = rs->ostate;
+ rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
+ if (ostate & ODEVHUP) {
+ sigchar(tp, SIGHUP, 1);
+ tp->tty_termios.c_ospeed = B0;/* Disable further I/O.*/
+ return 0;
+ }
+ }
+
+ if (try) {
+ return(rs->icount > 0);
+ }
+
+ while ((count = rs->icount) > 0) {
+ icount = bufend(rs->ibuf) - rs->itail;
+ if (count > icount) count = icount;
+
+ /* Perform input processing on (part of) the input buffer. */
+ if ((count = in_process(tp, rs->itail, count, -1)) == 0) break;
+ rs->icount -= count;
+ if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
+ if ((rs->itail += count) == bufend(rs->ibuf))
+ rs->itail = rs->ibuf;
+ }
+
+ return 0;
+}
+
+static void
+rs_ostart(rs232_t *rs)
+{
+/* Tell RS232 there is something waiting in the output buffer. */
+
+ rs->ostate |= OQUEUED;
+ if (txready(rs)) write_chars(rs);
+}
+
+static int
+rs_break(tty_t *tp, int UNUSED(dummy))
+{
+/* Generate a break condition by setting the BREAK bit for 0.4 sec. */
+ rs232_t *rs = tp->tty_priv;
+ unsigned int lsr;
+
+ lsr = serial_in(rs, OMAP3_LSR);
+ serial_out(rs, OMAP3_LSR, lsr | UART_LSR_BI);
+ /* XXX */
+ /* milli_delay(400); */ /* ouch */
+ serial_out(rs, OMAP3_LSR, lsr);
+ return 0; /* dummy */
+}
+
+static int
+rs_open(tty_t *tp, int UNUSED(dummy))
+{
+ /* Set the speed to 115200 by default */
+ tp->tty_termios.c_ospeed = DFLT_BAUD;
+ return 0;
+}
+
+static int
+rs_close(tty_t *tp, int UNUSED(dummy))
+{
+/* The line is closed; optionally hang up. */
+ rs232_t *rs = tp->tty_priv;
+
+ if (tp->tty_termios.c_cflag & HUPCL) {
+ serial_out(rs, OMAP3_MCR, UART_MCR_OUT2|UART_MCR_RTS);
+ if (rs->ier & UART_IER_THRI) {
+ rs->ier &= ~UART_IER_THRI;
+ serial_out(rs, OMAP3_IER, rs->ier);
+ }
+ }
+ return 0; /* dummy */
+}
+
+/* Low level (interrupt) routines. */
+
+static void
+rs232_handler(struct rs232 *rs)
+{
+/* Handle interrupt of a UART port */
+ unsigned int iir, lsr;
+
+ iir = serial_in(rs, OMAP3_IIR);
+ if (iir & UART_IIR_NO_INT) /* No interrupt */
+ return;
+
+ lsr = serial_in(rs, OMAP3_LSR);
+ if (iir & UART_IIR_RDI) { /* Data ready interrupt */
+ if (lsr & UART_LSR_DR) {
+ read_chars(rs, lsr);
+ }
+ }
+ check_modem_status(rs);
+ if (iir & UART_IIR_THRI) {
+ if (lsr & UART_LSR_THRE) {
+ /* Ready to send and space available */
+ write_chars(rs);
+ }
+ }
+}
+
+static void
+read_chars(rs232_t *rs, unsigned int status)
+{
+ unsigned char c;
+ unsigned int lsr;
+
+ lsr = status;
+
+ if (lsr & UART_LSR_DR) {
+ c = serial_in(rs, OMAP3_RHR);
+ if (!(rs->ostate & ORAW)) {
+ if (c == rs->oxoff) {
+ rs->ostate &= ~OSWREADY;
+ } else if (!(rs->ostate & OSWREADY)) {
+ rs->ostate = OSWREADY;
+ }
+ }
+
+ if (rs->icount == buflen(rs->ibuf)) {
+ printf("%s:%d buffer full, discarding byte\n",
+ __FUNCTION__, __LINE__);
+ return;
+ }
+
+ if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
+ *rs->ihead = c;
+ if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
+ if (rs->icount == 1) {
+ rs->tty->tty_events = 1;
+ }
+ }
+}
+
+static void
+write_chars(rs232_t *rs)
+{
+/* If there is output to do and everything is ready, do it (local device is
+ * known ready).
+ * Notify TTY when the buffer goes empty.
+ */
+
+ if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
+ /* Bit test allows ORAW and requires the others. */
+ serial_out(rs, OMAP3_THR, *rs->otail);
+ if (++rs->otail == bufend(rs->obuf))
+ rs->otail = rs->obuf;
+ if (--rs->ocount == 0) {
+ /* Turn on ODONE flag, turn off OQUEUED */
+ rs->ostate ^= (ODONE | OQUEUED);
+ rs->tty->tty_events = 1;
+ if (rs->ier & UART_IER_THRI) {
+ rs->ier &= ~UART_IER_THRI;
+ serial_out(rs, OMAP3_IER, rs->ier);
+ }
+ } else {
+ if (rs->icount == RS_OLOWWATER)
+ rs->tty->tty_events = 1;
+ if (!(rs->ier & UART_IER_THRI)) {
+ rs->ier |= UART_IER_THRI;
+ serial_out(rs, OMAP3_IER, rs->ier);
+ }
+ }
+ }
+}
+
+static unsigned int
+check_modem_status(rs232_t *rs)
+{
+/* Check modem status */
+
+ unsigned int msr;
+
+ msr = serial_in(rs, OMAP3_MSR); /* Resets modem interrupt */
+ if ((msr & (UART_MSR_DCD|UART_MSR_DDCD)) == UART_MSR_DDCD) {
+ rs->ostate |= ODEVHUP;
+ rs->tty->tty_events = 1;
+ }
+
+ if (!devready(rs))
+ rs->ostate &= ~ODEVREADY;
+ else
+ rs->ostate |= ODEVREADY;
+
+ return msr;
+}
+
+#endif /* NR_RS_LINES > 0 */
+
* Jul 13, 2004 support for function key observers (Jorrit N. Herder)
*/
+#include <assert.h>
#include <minix/drivers.h>
#include <minix/driver.h>
#include <termios.h>
/* Macros for magic tty types. */
#define isconsole(tp) ((tp) < tty_addr(NR_CONS))
-#define ispty(tp) ((tp) >= tty_addr(NR_CONS+NR_RS_LINES))
+#define ispty(tp) ((tp) != NULL && (tp)->tty_minor >= TTYPX_MINOR)
/* Macros for magic tty structure pointers. */
#define FIRST_TTY tty_addr(0)
static void setattr(tty_t *tp);
static void tty_icancel(tty_t *tp);
static void tty_init(void);
+static void do_new_kmess(void);
+static tty_t * line2tty(int line);
+static void set_console_line(char term[CONS_ARG]);
+static void set_kernel_color(char color[CONS_ARG]);
+static void set_color(tty_t *tp, int color);
+static void reset_color(tty_t *tp);
/* Default attributes. */
static struct termios termios_defaults = {
int ccurrent; /* currently active console */
struct machine machine; /* kernel environment variables */
u32_t system_hz;
+u32_t consoleline = CONS_MINOR;
+u32_t kernel_msg_color = 0;
/* SEF functions and variables. */
static void sef_local_startup(void);
static int sef_cb_init_fresh(int type, sef_init_info_t *info);
static void sef_cb_signal_handler(int signo);
+extern struct minix_kerninfo *_minix_kerninfo;
+
/*===========================================================================*
* tty_task *
*===========================================================================*/
/* SEF local startup. */
sef_local_startup();
-
while (TRUE) {
/* Check for and handle any events on any of the ttys. */
for (tp = FIRST_TTY; tp < END_TTY; tp++) {
continue;
}
line = tty_mess.TTY_LINE;
+ if (line == CONS_MINOR || line == LOG_MINOR) {
+ /* /dev/log output goes to /dev/console */
+ if (consoleline != CONS_MINOR) {
+ /* Console output must redirected */
+ line = consoleline;
+ tty_mess.TTY_LINE = line;
+ }
+ }
if (line == KBD_MINOR) {
do_kbd(&tty_mess);
continue;
} else if (line == VIDEO_MINOR) {
do_video(&tty_mess);
continue;
- } else if ((line - CONS_MINOR) < NR_CONS) {
- tp = tty_addr(line - CONS_MINOR);
- } else if (line == LOG_MINOR) {
- tp = tty_addr(0);
- } else if ((line - RS232_MINOR) < NR_RS_LINES) {
- tp = tty_addr(line - RS232_MINOR + NR_CONS);
- } else if ((line - TTYPX_MINOR) < NR_PTYS) {
- tp = tty_addr(line - TTYPX_MINOR + NR_CONS + NR_RS_LINES);
- } else if ((line - PTYPX_MINOR) < NR_PTYS) {
- tp = tty_addr(line - PTYPX_MINOR + NR_CONS + NR_RS_LINES);
- if (tty_mess.m_type != DEV_IOCTL_S) {
+ } else {
+ tp = line2tty(line);
+
+ /* Terminals and pseudo terminals belong together. We can only
+ * make a distinction between the two based on position in the
+ * tty_table and not on minor number (i.e., use ispty macro).
+ * Hence this special case.
+ */
+ if (line - PTYPX_MINOR < NR_PTYS &&
+ tty_mess.m_type != DEV_IOCTL_S){
do_pty(tp, &tty_mess);
continue;
}
- } else {
- tp = NULL;
}
/* If the device doesn't exist or is not configured return ENXIO. */
if (tp == NULL || ! tty_active(tp)) {
- if (tty_mess.m_source != LOG_PROC_NR)
- {
+ if (tty_mess.m_source != LOG_PROC_NR) {
tty_reply(TASK_REPLY, tty_mess.m_source,
- tty_mess.USER_ENDPT, ENXIO);
+ tty_mess.USER_ENDPT, ENXIO);
}
continue;
}
return 0;
}
+static void
+set_color(tty_t *tp, int color)
+{
+ message msg;
+ char buf[8];
+
+ buf[0] = '\033';
+ snprintf(&buf[1], sizeof(buf) - 1, "[1;%dm", color);
+ msg.m_source = KERNEL;
+ msg.IO_GRANT = buf;
+ msg.COUNT = sizeof(buf);
+ do_write(tp, &msg);
+}
+
+static void
+reset_color(tty_t *tp)
+{
+ message msg;
+ char buf[8];
+
+#define SGR_COLOR_RESET 39
+ buf[0] = '\033';
+ snprintf(&buf[1], sizeof(buf) - 1, "[0;%dm", SGR_COLOR_RESET);
+ msg.m_source = KERNEL;
+ msg.IO_GRANT = buf;
+ msg.COUNT = sizeof(buf);
+ do_write(tp, &msg);
+}
+
+static tty_t *
+line2tty(int line)
+{
+/* Convert a terminal line to tty_table pointer */
+
+ tty_t* tp;
+
+ if (line == KBD_MINOR || line == KBDAUX_MINOR || line == VIDEO_MINOR) {
+ return(NULL);
+ } else if ((line - CONS_MINOR) < NR_CONS) {
+ tp = tty_addr(line - CONS_MINOR);
+ } else if (line == LOG_MINOR) {
+ tp = tty_addr(consoleline);
+ } else if ((line - RS232_MINOR) < NR_RS_LINES) {
+ tp = tty_addr(line - RS232_MINOR + NR_CONS);
+ } else if ((line - TTYPX_MINOR) < NR_PTYS) {
+ tp = tty_addr(line - TTYPX_MINOR + NR_CONS + NR_RS_LINES);
+ } else if ((line - PTYPX_MINOR) < NR_PTYS) {
+ tp = tty_addr(line - PTYPX_MINOR + NR_CONS + NR_RS_LINES);
+ } else {
+ tp = NULL;
+ }
+
+ return(tp);
+}
+
/*===========================================================================*
* sef_local_startup *
*===========================================================================*/
{
/* Initialize the tty driver. */
int r;
+ char val[CONS_ARG];
/* Get kernel environment (protected_mode, pc_at and ega are needed). */
if (OK != (r=sys_getmachine(&machine))) {
panic("Couldn't obtain kernel environment: %d", r);
}
+ if (env_get_param("console", val, sizeof(val)) == OK) {
+ set_console_line(val);
+ }
+
+ if ((r = env_get_param("kernelclr", val, sizeof(val))) == OK) {
+ set_kernel_color(val);
+ }
+
/* Initialize the TTY driver. */
tty_init();
return(OK);
}
+static void
+set_console_line(char term[CONS_ARG])
+{
+/* Parse 'term' and redirect console output there. */
+ int i;
+
+ /* Console */
+ if (!strncmp(term, "console", CONS_ARG - 1)) {
+ consoleline = CONS_MINOR+0;
+ }
+
+ /* The other console terminals */
+ for (i = 1; i < NR_CONS; i++) {
+ char cons[6];
+ strlcpy(cons, "ttyc0", sizeof(cons));
+ cons[4] += i;
+ if (!strncmp(term, cons,
+ CONS_ARG < sizeof(cons) ? CONS_ARG-1 : sizeof(cons) - 1))
+ consoleline = CONS_MINOR + i;
+ }
+
+ /* Serial lines */
+ for (i = 0; i < NR_RS_LINES; i++) {
+ char sercons[6];
+ strlcpy(sercons, "tty00", sizeof(sercons));
+ sercons[4] += i;
+ if (!strncmp(term, sercons,
+ CONS_ARG < sizeof(sercons) ? CONS_ARG-1:sizeof(sercons)-1))
+ consoleline = RS232_MINOR + i;
+ }
+}
+
+static void
+set_kernel_color(char color[CONS_ARG])
+{
+ int def_color;
+
+#define SGR_COLOR_START 30
+#define SGR_COLOR_END 37
+
+ def_color = atoi(color);
+ if ((SGR_COLOR_START + def_color) >= SGR_COLOR_START &&
+ (SGR_COLOR_START + def_color) <= SGR_COLOR_END) {
+ kernel_msg_color = def_color + SGR_COLOR_START;
+ }
+}
+
+static void
+do_new_kmess(void)
+{
+/* Kernel wants to print a new message */
+ struct kmessages *kmess_ptr; /* kmessages structure */
+ char kernel_buf_copy[2*_KMESS_BUF_SIZE];
+ static int prev_next = 0;
+ int bytes, copy, restore = 0;
+ tty_t *tp, rtp;
+ message print_kmsg;
+
+ assert(_minix_kerninfo);
+ kmess_ptr = _minix_kerninfo->kmessages;
+
+ /* The kernel buffer is circular; print only the new part. Determine
+ * how many new bytes there are with the help of current and
+ * previous 'next' index. This works fine if less than _KMESS_BUF_SIZE
+ * bytes is new data; else we miss % _KMESS_BUF_SIZE here.
+ * Check for size being positive; the buffer might as well be emptied!
+ */
+ if (kmess_ptr->km_size > 0) {
+ bytes = ((kmess_ptr->km_next + _KMESS_BUF_SIZE) - prev_next) %
+ _KMESS_BUF_SIZE;
+ /* Copy from current position to end of buffer */
+ copy = (prev_next + bytes) % _KMESS_BUF_SIZE;
+ memcpy(kernel_buf_copy, &kmess_ptr->km_buf[prev_next], copy);
+
+ /* Copy remainder from start of buffer */
+ if (copy < bytes) {
+ memcpy(&kernel_buf_copy[copy], &kmess_ptr->km_buf[0],
+ bytes - copy);
+ }
+
+ tp = line2tty(consoleline);
+ if (tp == NULL || !tty_active(tp))
+ panic("Don't know where to send kernel messages");
+ if (tp->tty_outleft > 0) {
+ /* Terminal is already printing */
+ rtp = *tp; /* Make backup */
+ tp->tty_outleft = 0; /* So do_write is happy */
+ restore = 1;
+ }
+
+ if (kernel_msg_color != 0)
+ set_color(tp, kernel_msg_color);
+ print_kmsg.m_source = KERNEL;
+ print_kmsg.IO_GRANT = kernel_buf_copy;
+ print_kmsg.COUNT = bytes;
+ do_write(tp, &print_kmsg);
+ if (kernel_msg_color != 0)
+ reset_color(tp);
+ if (restore) {
+ *tp = rtp;
+ }
+ }
+
+ /* Store 'next' pointer so that we can determine what part of the
+ * kernel messages buffer to print next time a notification arrives.
+ */
+ prev_next = kmess_ptr->km_next;
+}
+
/*===========================================================================*
* sef_cb_signal_handler *
*===========================================================================*/
switch(signo) {
/* There is a pending message from the kernel. */
case SIGKMESS:
- do_new_kmess();
+ do_new_kmess();
break;
/* Switch to primary console on termination. */
case SIGTERM:
size = sizeof(struct winsize);
break;
-#if defined(__i386__)
case KIOCSMAP: /* load keymap (Minix extension) */
size = sizeof(keymap_t);
break;
size = sizeof(u8_t [8192]);
break;
-#endif
case TCDRAIN: /* Posix tcdrain function -- no parameter */
default: size = 0;
}
sigchar(tp, SIGWINCH, 0);
break;
-#if defined(__i386__)
case KIOCSMAP:
/* Load a new keymap (only /dev/console). */
if (isconsole(tp)) r = kbd_loadmap(m_ptr);
/* Load a font into an EGA or VGA card (hs@hck.hr) */
if (isconsole(tp)) r = con_loadfont(m_ptr);
break;
-#endif
/* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is
* not defined.
r = 1;
}
tp->tty_openct++;
+ if (tp->tty_openct == 1) {
+ /* Tell the device that the tty is opened */
+ (*tp->tty_open)(tp, 0);
+ }
}
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->USER_ENDPT, r);
}
tty_mess.REP_ENDPT = proc_nr;
tty_mess.REP_STATUS = status;
+ /* Don't reply to KERNEL (kernel messages) */
+ if (replyee == KERNEL) return;
+
/* TTY is not supposed to send a TTY_REVIVE message. The
* REVIVE message is gone, TTY_REVIVE is only used as an internal
* placeholder for something that is not supposed to be a message.
system_hz = sys_hz();
/* Initialize the terminal lines. */
+ memset(tty_table, '\0' , sizeof(tty_table));
+
for (tp = FIRST_TTY,s=0; tp < END_TTY; tp++,s++) {
tp->tty_index = s;
-
init_timer(&tp->tty_tmr);
tp->tty_intail = tp->tty_inhead = tp->tty_inbuf;
tp->tty_ingrant = tp->tty_outgrant = tp->tty_iogrant = GRANT_INVALID;
tp->tty_termios = termios_defaults;
tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close =
- tty_devnop;
+ tp->tty_open = tty_devnop;
if (tp < tty_addr(NR_CONS)) {
scr_init(tp);