From: Thomas Veerman Date: Wed, 17 Oct 2012 14:07:53 +0000 (+0000) Subject: TTY: seperate hardware dependent parts + add new serial driver X-Git-Tag: v3.2.1~251 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/man.named-checkconf.html?a=commitdiff_plain;h=b01e9ebfdbe0c30aafc76e25690a4f2a28716753;p=minix.git TTY: seperate hardware dependent parts + add new serial driver .Split TTY in order to support both x86 and ARM. .Add support for the TI 16750 UARTs on OMAP35x. .Various other improvements: .Kernel messages are printed using generic terminal write functions. That is, they are no longer directly displayed on the console. .The console can now be displayed on any terminal. This is configured by the "console={tty00,tty01,ttyc2,ttyc3,ttyc4}" boot variable -- basically any valid /dev/tty* terminal. .Cutify kernel messages with colors. Configured by "kernelclr={1,2,3,4,5,6,7}" boot variable. --- 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__) */ -