diff --git a/common/include/termios.h b/common/include/termios.h index 5818e31a9..9ed08c918 100644 --- a/common/include/termios.h +++ b/common/include/termios.h @@ -145,6 +145,8 @@ int tcsetattr(int _filedes, int _opt_actions, const struct termios #define cfsetospeed(termios_p, speed) ((termios_p)->c_ospeed = (speed), 0) #endif +/* Here are the local extensions to the POSIX standard for Minix. */ + /* Extensions to the termios c_iflag bit map. */ #define IXANY 0x0800 /* allow any key to continue ouptut */ #define SCANCODES 0x1000 /* send scancodes */ @@ -164,8 +166,16 @@ int tcsetattr(int _filedes, int _opt_actions, const struct termios #define VDISCARD 13 /* cc_c[VDISCARD] (^O) */ /* Extensions to baud rate settings. */ +#ifdef _MINIX #define B57600 0x0100 /* 57600 baud */ #define B115200 0x0200 /* 115200 baud */ +#define B230400 0x0400 /* 230400 baud */ +#define B460800 0x0800 /* 460800 baud */ +#define B921600 0x1000 /* 921600 baud */ +#define B1843200 0x2000 /* 1843200 baud */ +#define B3000000 0x4000 /* 3000000 baud */ +#define B3686400 0x8000 /* 3686400 baud */ +#endif /* _MINIX */ /* These are the default settings used by the kernel and by 'stty sane' */ diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 7d46a7358..d609aad75 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -1,7 +1,10 @@ # Makefile for terminal driver (TTY) PROG= tty -SRCS= tty.c console.c keyboard.c pty.c rs232.c + +.include "arch/${MACHINE_ARCH}/Makefile.inc" + +SRCS += tty.c pty.c DPADD+= ${LIBSYS} ${LIBTIMERS} LDADD+= -lsys -ltimers @@ -13,7 +16,7 @@ BINDIR?= /usr/sbin SUBDIR= keymaps # Needs kernel/const.h, etc -CPPFLAGS+= -I${NETBSDSRCDIR} +CPPFLAGS+= -I${.CURDIR} -I${NETBSDSRCDIR} .include .include diff --git a/drivers/tty/arch/arm/Makefile.inc b/drivers/tty/arch/arm/Makefile.inc new file mode 100644 index 000000000..3ad697a9e --- /dev/null +++ b/drivers/tty/arch/arm/Makefile.inc @@ -0,0 +1,7 @@ +# Makefile for arch-dependent TTY code +.include + +HERE=${.CURDIR}/arch/${MACHINE_ARCH} +.PATH: ${HERE} + +SRCS += console.c keyboard.c rs232.c diff --git a/drivers/tty/arch/arm/console.c b/drivers/tty/arch/arm/console.c new file mode 100644 index 000000000..3c820bcea --- /dev/null +++ b/drivers/tty/arch/arm/console.c @@ -0,0 +1,26 @@ +/* Console unsupport for ARM. Just stubs. */ +#include +#include +#include "tty.h" + +void +do_video(message *m) +{ +} + +void +scr_init(tty_t *tp) +{ +} + +void +cons_stop(void) +{ +} + +int +con_loadfont(message *m) +{ + return 0; +} + diff --git a/drivers/tty/arch/arm/keyboard.c b/drivers/tty/arch/arm/keyboard.c new file mode 100644 index 000000000..3c6c1e570 --- /dev/null +++ b/drivers/tty/arch/arm/keyboard.c @@ -0,0 +1,51 @@ +/* Keyboard unsupport for ARM. Just stubs. */ +#include +#include +#include "tty.h" + +void +kbd_interrupt(message *m) +{ +} + +void +do_fkey_ctl(message *m) +{ +} + +void +do_kb_inject(message *m) +{ +} + +void +do_kbd(message *m) +{ +} + +void +do_kbdaux(message *m) +{ +} + +void +kb_init_once(void) +{ +} + +int +kbd_status(message *m) +{ + return 0; +} + +int +kbd_loadmap(message *m) +{ + return 0; +} + +void +kb_init(tty_t *tp) +{ +} diff --git a/drivers/tty/arch/arm/omap_serial.h b/drivers/tty/arch/arm/omap_serial.h new file mode 100644 index 000000000..ec96eb3fd --- /dev/null +++ b/drivers/tty/arch/arm/omap_serial.h @@ -0,0 +1,106 @@ +#ifndef _OMAP_SERIAL_H +#define _OMAP_SERIAL_H + +/* UART register map */ +#define OMAP3_UART1_BASE 0x4806A000 /* UART1 physical address */ +#define OMAP3_UART2_BASE 0x4806C000 /* UART2 physical address */ +#define OMAP3_UART3_BASE 0x49020000 /* UART3 physical address */ + +/* UART registers */ +#define OMAP3_THR 0 /* Transmit holding register */ +#define OMAP3_RHR 0 /* Receive holding register */ +#define OMAP3_DLL 0 /* Divisor latches low */ +#define OMAP3_DLH 1 /* Divisor latches high */ +#define OMAP3_IER 1 /* Interrupt enable register */ +#define OMAP3_IIR 2 /* Interrupt identification register */ +#define OMAP3_EFR 2 /* Extended features register */ +#define OMAP3_FCR 2 /* FIFO control register */ +#define OMAP3_LCR 3 /* Line control register */ +#define OMAP3_MCR 4 /* Modem control register */ +#define OMAP3_LSR 5 /* Line status register */ +#define OMAP3_MSR 6 /* Modem status register */ +#define OMAP3_MDR1 0x08 /* Mode definition register 1 */ +#define OMAP3_MDR2 0x09 /* Mode definition register 2 */ +#define OMAP3_SCR 0x10 /* Supplementary control register */ +#define OMAP3_SSR 0x11 /* Supplementary status register */ +#define OMAP3_SYSC 0x15 /* System configuration register */ +#define OMAP3_SYSS 0x16 /* System status register */ + +/* Enhanced Features Register bits */ +#define UART_EFR_ECB 0x10 /* Enhanced control bit */ + +/* Interrupt Enable Register bits */ +#define UART_IER_MSI 0x08 /* Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Receiver data interrupt */ + +/* FIFO control register */ +#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT 6 +#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) +#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the fifo */ +#define UART_FCR_CLR_RCVR 0x02 /* Clear the RCVR FIFO */ +#define UART_FCR_CLR_XMIT 0x04 /* Clear the XMIT FIFO */ + +/* Interrupt Identification Register bits */ +#define UART_IIR_RDI 0x04 /* Data ready interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_NO_INT 0x01 /* No interrupt is pending */ + +/* Line Control Register bits */ +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Enable parity */ +#define UART_LCR_STOP 0x04 /* Stop bits; 0=1 bit, 1=2 bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength 8 bits */ + +#define UART_LCR_CONF_MODE_A UART_LCR_DLAB /* Configuration Mode A */ +#define UART_LCR_CONF_MODE_B 0xBF /* Configuration Mode B */ + +/* Line Status Register bits */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break condition */ +#define UART_LSR_DR 0x01 /* Data ready */ + +/* Modem Control Register bits */ +#define UART_MCR_TCRTLR 0x40 /* Access TCR/TLR */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR output low */ + +/* Mode Definition Register 1 bits */ +#define OMAP_MDR1_DISABLE 0x07 +#define OMAP_MDR1_MODE13X 0x03 +#define OMAP_MDR1_MODE16X 0x00 + +/* Modem Status Register bits */ +#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ +#define UART_MSR_CTS 0x10 /* Clear to Send */ +#define UART_MSR_DDCD 0x08 /* Delta DCD */ + +/* Supplementary control Register bits */ +#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) + +/* System Control Register bits */ +#define UART_SYSC_SOFTRESET 0x02 + +/* System Status Register bits */ +#define UART_SYSS_RESETDONE 0x01 + +/* Line status register fields */ +#define OMAP3_LSR_TX_FIFO_E (1 << 5) /* Transmit FIFO empty */ +#define OMAP3_LSR_RX_FIFO_E (1 << 0) /* Receive FIFO empty */ + +/* Supplementary status register fields */ +#define OMAP3_SSR_TX_FIFO_FULL (1 << 0) /* Transmit FIFO full */ + +#define OMAP3_UART3_THR (OMAP3_UART3_BASE + OMAP3_THR) +#define OMAP3_UART3_IIR (OMAP3_UART3_BASE + OMAP3_IIR) +#define OMAP3_UART3_LSR (OMAP3_UART3_BASE + OMAP3_LSR) +#define OMAP3_UART3_SSR (OMAP3_UART3_BASE + OMAP3_SSR) + +#endif /* _OMAP_SERIAL_H */ diff --git a/drivers/tty/arch/arm/rs232.c b/drivers/tty/arch/arm/rs232.c new file mode 100644 index 000000000..3a3b256bd --- /dev/null +++ b/drivers/tty/arch/arm/rs232.c @@ -0,0 +1,797 @@ +#include +#include +#include +#include +#include +#include +#include +#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 */ + diff --git a/drivers/tty/arch/i386/Makefile.inc b/drivers/tty/arch/i386/Makefile.inc new file mode 100644 index 000000000..3ad697a9e --- /dev/null +++ b/drivers/tty/arch/i386/Makefile.inc @@ -0,0 +1,7 @@ +# Makefile for arch-dependent TTY code +.include + +HERE=${.CURDIR}/arch/${MACHINE_ARCH} +.PATH: ${HERE} + +SRCS += console.c keyboard.c rs232.c diff --git a/drivers/tty/console.c b/drivers/tty/arch/i386/console.c similarity index 94% rename from drivers/tty/console.c rename to drivers/tty/arch/i386/console.c index 6ec7fb4eb..de50c67f4 100644 --- a/drivers/tty/console.c +++ b/drivers/tty/arch/i386/console.c @@ -122,7 +122,6 @@ struct sequence { static int cons_write(struct tty *tp, int try); static void cons_echo(tty_t *tp, int c); static void out_char(console_t *cons, int c); -static void cons_putk(int c); static void beep(void); static void do_escape(console_t *cons, int c); static void flush(console_t *cons); @@ -172,9 +171,17 @@ int try; */ do { if (count > sizeof(buf)) count = sizeof(buf); - if ((result = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant, - tp->tty_outoffset, (vir_bytes) buf, count)) != OK) - break; + if (tp->tty_outcaller == KERNEL) { + /* We're trying to print on kernel's behalf */ + memcpy(buf, (void *) tp->tty_outgrant + tp->tty_outoffset, + count); + } else { + if ((result = sys_safecopyfrom(tp->tty_outcaller, + tp->tty_outgrant, tp->tty_outoffset, + (vir_bytes) buf, count)) != OK) { + break; + } + } tp->tty_outoffset += count; tbuf = buf; @@ -1008,64 +1015,6 @@ tty_t *tp; cons_ioctl(tp, 0); } -extern struct minix_kerninfo *_minix_kerninfo; - -/*===========================================================================* - * do_new_kmess * - *===========================================================================*/ -void do_new_kmess() -{ -/* Notification for a new kernel message. */ - struct kmessages *kmess_ptr; /* kmessages structure */ - static int prev_next = 0; /* previous next seen */ - int bytes; - int r; - - assert(_minix_kerninfo); - kmess_ptr = _minix_kerninfo->kmessages; - - /* Print only the new part. Determine how many new bytes there are with - * help of the current and previous 'next' index. Note that the kernel - * buffer is circular. This works fine if less then _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; - r=prev_next; /* start at previous old */ - while (bytes > 0) { - cons_putk( kmess_ptr->km_buf[(r%_KMESS_BUF_SIZE)] ); - bytes --; - r ++; - } - cons_putk(0); /* terminate to flush output */ - } - - /* Almost done, store 'next' 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; -} - -/*===========================================================================* - * cons_putk * - *===========================================================================*/ -static void cons_putk(c) -int c; /* character to print */ -{ -/* This procedure is used to print a character on the console. - */ - if (c != 0) { - if (c == '\n') cons_putk('\r'); - out_char(&cons_table[0], (int) c); -#if 0 - ser_putc(c); -#endif - } else { - flush(&cons_table[0]); - } -} - /*===========================================================================* * toggle_scroll * *===========================================================================*/ diff --git a/drivers/tty/keyboard.c b/drivers/tty/arch/i386/keyboard.c similarity index 100% rename from drivers/tty/keyboard.c rename to drivers/tty/arch/i386/keyboard.c diff --git a/drivers/tty/rs232.c b/drivers/tty/arch/i386/rs232.c similarity index 98% rename from drivers/tty/rs232.c rename to drivers/tty/arch/i386/rs232.c index 4a5813439..7525c2784 100644 --- a/drivers/tty/rs232.c +++ b/drivers/tty/arch/i386/rs232.c @@ -248,9 +248,14 @@ static int rs_write(register tty_t *tp, int try) if (try) return 1; /* Copy from user space to the RS232 output buffer. */ - if ((r = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant, - tp->tty_outoffset, (vir_bytes) rs->ohead, count)) != OK) - printf("TTY: sys_safecopyfrom() failed: %d", r); + 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) + printf("TTY: sys_safecopyfrom() failed: %d", r); + } /* Perform output processing on the output buffer. */ out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount); @@ -754,8 +759,6 @@ static void in_int(register rs232_t *rs) rs->tty->tty_events = 1; force_timeout(); } - else - printf("in_int: icount = %d\n", rs->icount); } /*===========================================================================* diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 9d718214b..72e4724eb 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -242,9 +242,16 @@ static int pty_write(tty_t *tp, int try) break; /* Copy from user space to the PTY output buffer. */ - if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant, - tp->tty_outoffset, (vir_bytes) pp->ohead, count))!=OK) { - break; + if (tp->tty_outcaller == KERNEL) { + /* We're trying to print on kernel's behalf */ + memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outoffset, + count); + } else { + if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant, + tp->tty_outoffset, (vir_bytes) pp->ohead, + count)) != OK) { + break; + } } /* Perform output processing on the output buffer. */ diff --git a/drivers/tty/tty.c b/drivers/tty/tty.c index 206498f57..29d65a054 100644 --- a/drivers/tty/tty.c +++ b/drivers/tty/tty.c @@ -57,6 +57,7 @@ * Jul 13, 2004 support for function key observers (Jorrit N. Herder) */ +#include #include #include #include @@ -80,7 +81,7 @@ unsigned long rs_irq_set = 0; /* 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) @@ -120,6 +121,12 @@ static void dev_ioctl(tty_t *tp); 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 = { @@ -137,12 +144,16 @@ tty_t tty_table[NR_CONS+NR_RS_LINES+NR_PTYS]; 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 * *===========================================================================*/ @@ -158,7 +169,6 @@ int main(void) /* 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++) { @@ -229,6 +239,14 @@ int main(void) 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; @@ -238,30 +256,26 @@ int main(void) } 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; } @@ -286,6 +300,61 @@ int main(void) 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 * *===========================================================================*/ @@ -311,12 +380,21 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info)) { /* 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(); @@ -326,6 +404,115 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info)) 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 * *===========================================================================*/ @@ -335,7 +522,7 @@ static void sef_cb_signal_handler(int signo) 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: @@ -586,7 +773,6 @@ message *m_ptr; /* pointer to message sent to task */ size = sizeof(struct winsize); break; -#if defined(__i386__) case KIOCSMAP: /* load keymap (Minix extension) */ size = sizeof(keymap_t); break; @@ -595,7 +781,6 @@ message *m_ptr; /* pointer to message sent to task */ size = sizeof(u8_t [8192]); break; -#endif case TCDRAIN: /* Posix tcdrain function -- no parameter */ default: size = 0; } @@ -679,7 +864,6 @@ message *m_ptr; /* pointer to message sent to task */ 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); @@ -692,7 +876,6 @@ message *m_ptr; /* pointer to message sent to task */ /* 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. @@ -729,6 +912,10 @@ message *m_ptr; /* pointer to message sent to task */ 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); } @@ -1476,6 +1663,9 @@ int status; /* reply code */ 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. @@ -1555,10 +1745,11 @@ static void tty_init() 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; @@ -1566,7 +1757,7 @@ static void tty_init() 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); diff --git a/drivers/tty/tty.h b/drivers/tty/tty.h index 311befbc6..9bf4147de 100644 --- a/drivers/tty/tty.h +++ b/drivers/tty/tty.h @@ -17,6 +17,7 @@ #define TTYPX_MINOR 128 #define PTYPX_MINOR 192 +#define CONS_ARG 30 /* console= boot param length (incl. nul) */ #define LINEWRAP 1 /* console.c - wrap lines at column 80 */ #define TTY_IN_BYTES 256 /* tty input queue size */ @@ -91,6 +92,7 @@ typedef struct tty { /* Miscellaneous. */ devfun_t tty_ioctl; /* set line speed, etc. at the device level */ + devfun_t tty_open; /* tell the device that the tty is opened */ devfun_t tty_close; /* tell the device that the tty is closed */ void *tty_priv; /* pointer to per device private data */ struct termios tty_termios; /* terminal attributes */ @@ -154,11 +156,9 @@ int select_retry(struct tty *tp); void rs_init(struct tty *tp); void rs_interrupt(message *m); -#if defined(__i386__) /* console.c */ void kputc(int c); void cons_stop(void); -void do_new_kmess(void); void scr_init(struct tty *tp); void toggle_scroll(void); int con_loadfont(message *m); @@ -183,5 +183,3 @@ void pty_init(struct tty *tp); void select_retry_pty(struct tty *tp); int pty_status(message *m_ptr); -#endif /* defined(__i386__) */ -