minix/drivers/tty/arch/i386/rs232.c

840 lines
26 KiB
C
Raw Normal View History

2005-04-21 16:53:53 +02:00
#include <minix/config.h>
2005-09-11 19:09:11 +02:00
/*---------------------------------------------------------------------------*
* rs232.c - serial driver for 8250 and 16450 UARTs *
* Added support for Atari ST M68901 and YM-2149 --kub *
*---------------------------------------------------------------------------*/
2005-04-21 16:53:53 +02:00
#include <minix/drivers.h>
#include <sys/termios.h>
2005-04-21 16:53:53 +02:00
#include <signal.h>
#include "tty.h"
#if NR_RS_LINES > 0
/* 8250 constants. */
#define UART_FREQ 115200L /* timer frequency */
/* Interrupt enable bits. */
#define IE_RECEIVER_READY 1
#define IE_TRANSMITTER_READY 2
#define IE_LINE_STATUS_CHANGE 4
#define IE_MODEM_STATUS_CHANGE 8
/* Interrupt status bits. */
#define IS_MODEM_STATUS_CHANGE 0
#define IS_NOTPENDING 1
2005-04-21 16:53:53 +02:00
#define IS_TRANSMITTER_READY 2
#define IS_RECEIVER_READY 4
#define IS_LINE_STATUS_CHANGE 6
#define IS_IDBITS 6
2005-04-21 16:53:53 +02:00
/* Line control bits. */
#define LC_CS5 0x00 /* LSB0 and LSB1 encoding for CS5 */
#define LC_CS6 0x01 /* LSB0 and LSB1 encoding for CS6 */
#define LC_CS7 0x02 /* LSB0 and LSB1 encoding for CS7 */
#define LC_CS8 0x03 /* LSB0 and LSB1 encoding for CS8 */
2005-04-21 16:53:53 +02:00
#define LC_2STOP_BITS 0x04
#define LC_PARITY 0x08
#define LC_PAREVEN 0x10
#define LC_BREAK 0x40
#define LC_ADDRESS_DIVISOR 0x80
/* Line status bits. */
#define LS_OVERRUN_ERR 2
#define LS_PARITY_ERR 4
#define LS_FRAMING_ERR 8
#define LS_BREAK_INTERRUPT 0x10
#define LS_TRANSMITTER_READY 0x20
/* Modem control bits. */
#define MC_DTR 1
#define MC_RTS 2
#define MC_OUT2 8 /* required for PC & AT interrupts */
/* Modem status bits. */
#define MS_CTS 0x10
#define MS_RLSD 0x80 /* Received Line Signal Detect */
#define MS_DRLSD 0x08 /* RLSD Delta */
#define DATA_BITS_SHIFT 8 /* amount data bits shifted in mode */
#define DEF_BAUD 1200 /* default baud rate */
2005-04-21 16:53:53 +02:00
#define RS_IBUFSIZE 1024 /* RS232 input buffer size */
#define RS_OBUFSIZE 1024 /* RS232 output buffer size */
2005-04-21 16:53:53 +02:00
/* 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)
2005-04-21 16:53:53 +02:00
/* 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)
2005-04-21 16:53:53 +02:00
/* 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) \
(sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
2005-04-21 16:53:53 +02:00
(rs)->idevready = TRUE)
#define istop(rs) \
(sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \
2005-04-21 16:53:53 +02:00
(rs)->idevready = FALSE)
/* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if
* CLOCAL is in effect for a line without a CTS wire.
*/
#define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)
2005-04-21 16:53:53 +02:00
/* Macro to tell if transmitter is ready. */
#define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)
2005-04-21 16:53:53 +02:00
/* Macro to tell if carrier has dropped.
* The RS232 Carrier Detect (CD) line is usually connected to the 8250
* Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem
* Status Register. The MS_DRLSD bit tells if MS_RLSD has just changed state.
* So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just
* dropped.
*/
#define devhup(rs) \
((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
2005-04-21 16:53:53 +02:00
/* Types. */
typedef unsigned char bool_t; /* boolean */
/* 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 */
bool_t 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 MS_CTS /* external device hardware ready (CTS) */
#define OQUEUED 0x20 /* output buffer not empty */
#define OSWREADY 0x40 /* external device software ready (no xoff) */
#define ODEVHUP MS_RLSD /* 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 */
bool_t inhibited; /* output inhibited? (follows tty_inhibited) */
bool_t 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 */
#if defined(__i386__)
2005-04-21 16:53:53 +02:00
port_t xmit_port; /* i/o ports */
port_t recv_port;
port_t div_low_port;
port_t div_hi_port;
port_t int_enab_port;
port_t int_id_port;
port_t line_ctl_port;
port_t modem_ctl_port;
port_t line_status_port;
port_t modem_status_port;
#endif
unsigned char lstatus; /* last line status */
unsigned char pad; /* ensure alignment for 16-bit ints */
unsigned framing_errors; /* error counts (no reporting yet) */
unsigned overrun_errors;
unsigned parity_errors;
unsigned break_interrupts;
2005-07-29 17:22:58 +02:00
int irq; /* irq for this line */
int irq_hook_id; /* interrupt hook */
2005-04-21 16:53:53 +02:00
char ibuf[RS_IBUFSIZE]; /* input buffer */
char obuf[RS_OBUFSIZE]; /* output buffer */
} rs232_t;
2012-03-25 20:25:53 +02:00
static rs232_t rs_lines[NR_RS_LINES];
2005-04-21 16:53:53 +02:00
#if defined(__i386__)
2005-04-21 16:53:53 +02:00
/* 8250 base addresses. */
2012-03-25 20:25:53 +02:00
static port_t addr_8250[] = {
2005-04-21 16:53:53 +02:00
0x3F8, /* COM1 */
0x2F8, /* COM2 */
0x3E8, /* COM3 */
0x2E8, /* COM4 */
};
#endif
2012-03-25 20:25:53 +02:00
static void in_int(rs232_t *rs);
static void line_int(rs232_t *rs);
static void modem_int(rs232_t *rs);
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_on(tty_t *tp, int try);
static int rs_break_off(tty_t *tp, int try);
2012-03-25 20:25:53 +02:00
static int rs_close(tty_t *tp, int try);
static void out_int(rs232_t *rs);
static void rs232_handler(rs232_t *rs);
2005-04-21 16:53:53 +02:00
2012-03-25 20:25:53 +02:00
static int my_inb(port_t port)
{
2006-03-25 00:02:56 +01:00
int r;
2012-03-05 00:11:41 +01:00
u32_t v = 0;
r = sys_inb(port, &v);
2005-09-11 19:09:11 +02:00
if (r != OK)
printf("RS232 warning: failed inb 0x%x\n", port);
2006-03-25 00:02:56 +01:00
return (int) v;
}
2005-04-21 16:53:53 +02:00
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_write *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int rs_write(register tty_t *tp, int try)
2005-04-21 16:53:53 +02:00
{
/* (*devwrite)() routine for RS232. */
rs232_t *rs = tp->tty_priv;
2012-08-13 18:32:59 +02:00
int r, count, ocount;
2005-04-21 16:53:53 +02:00
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;
2005-04-21 16:53:53 +02:00
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) {
2005-09-11 19:09:11 +02:00
if (try) return 0;
break;
}
2005-09-11 19:09:11 +02:00
if (try) return 1;
2005-04-21 16:53:53 +02:00
/* 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, (char *) tp->tty_outgrant + tp->tty_outcum,
count);
} else {
if ((r = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
tp->tty_outcum, (vir_bytes) rs->ohead, count)) != OK)
printf("TTY: sys_safecopyfrom() failed: %d", r);
}
2005-04-21 16:53:53 +02:00
/* 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_outcum += count;
if ((tp->tty_outleft -= count) == 0) {
/* Output is finished, reply to the writer. */
if (tp->tty_outcaller != KERNEL)
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
tp->tty_outcum);
tp->tty_outcum = 0;
tp->tty_outcaller = NONE;
2005-04-21 16:53:53 +02:00
}
}
if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
/* Oops, the line has hung up. */
if (tp->tty_outcaller != KERNEL)
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
tp->tty_outleft = tp->tty_outcum = 0;
tp->tty_outcaller = NONE;
2005-04-21 16:53:53 +02:00
}
return 1;
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_echo *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void rs_echo(tp, c)
2005-04-21 16:53:53 +02:00
tty_t *tp; /* which TTY */
int c; /* character to echo */
{
/* 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 = c; /* 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);
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_ioctl *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int rs_ioctl(tty_t *tp, int UNUSED(dummy))
2010-01-27 11:19:13 +01:00
/* tp; which TTY */
2005-04-21 16:53:53 +02:00
{
/* Reconfigure the line as soon as the output has drained. */
rs232_t *rs = tp->tty_priv;
rs->drain = TRUE;
2005-07-29 17:22:58 +02:00
return 0; /* dummy */
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_config *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void rs_config(rs232_t *rs)
2010-01-27 11:19:13 +01:00
/* rs which line */
2005-04-21 16:53:53 +02:00
{
/* Set various line control parameters for RS232 I/O.
* If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
* The 8250 can't handle split speed, so we use the input speed.
*/
tty_t *tp = rs->tty;
int divisor;
int line_controls;
static struct speed2divisor {
speed_t speed;
int divisor;
} s2d[] = {
#if defined(__i386__)
2005-04-21 16:53:53 +02:00
{ B50, UART_FREQ / 50 },
#endif
{ B75, UART_FREQ / 75 },
{ B110, UART_FREQ / 110 },
{ B134, UART_FREQ * 10 / 1345 },
{ B150, UART_FREQ / 150 },
{ B200, UART_FREQ / 200 },
{ B300, UART_FREQ / 300 },
{ B600, UART_FREQ / 600 },
{ B1200, UART_FREQ / 1200 },
#if defined(__i386__)
2005-04-21 16:53:53 +02:00
{ B1800, UART_FREQ / 1800 },
#endif
{ B2400, UART_FREQ / 2400 },
{ B4800, UART_FREQ / 4800 },
{ B9600, UART_FREQ / 9600 },
{ B19200, UART_FREQ / 19200 },
#if defined(__i386__)
2005-04-21 16:53:53 +02:00
{ B38400, UART_FREQ / 38400 },
{ B57600, UART_FREQ / 57600 },
{ B115200, UART_FREQ / 115200L },
#endif
};
struct speed2divisor *s2dp;
/* 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) ? MS_CTS : 0;
/* Look up the 8250 rate divisor from the output speed. */
divisor = 0;
for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
}
if (divisor == 0) return; /* B0? */
/* Compute line control flag bits. */
line_controls = 0;
if (tp->tty_termios.c_cflag & PARENB) {
line_controls |= LC_PARITY;
if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
}
2005-04-21 16:53:53 +02:00
if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
/* which word size is configured? set the bits explicitly. */
if((tp->tty_termios.c_cflag & CSIZE) == CS5)
line_controls |= LC_CS5;
else if((tp->tty_termios.c_cflag & CSIZE) == CS6)
line_controls |= LC_CS6;
else if((tp->tty_termios.c_cflag & CSIZE) == CS7)
line_controls |= LC_CS7;
else if((tp->tty_termios.c_cflag & CSIZE) == CS8)
line_controls |= LC_CS8;
else printf("rs232: warning: no known word size set\n");
2005-04-21 16:53:53 +02:00
/* Select the baud rate divisor registers and change the rate. */
sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
sys_outb(rs->div_low_port, divisor);
sys_outb(rs->div_hi_port, divisor >> 8);
2005-04-21 16:53:53 +02:00
/* Change the line controls and reselect the usual registers. */
sys_outb(rs->line_ctl_port, line_controls);
2005-04-21 16:53:53 +02:00
rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
rs->ostate &= ~ORAW;
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_init *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void rs_init(tty_t *tp)
2010-01-27 11:19:13 +01:00
/* tp which TTY */
2005-04-21 16:53:53 +02:00
{
2012-03-05 00:11:41 +01:00
u32_t dummy;
2005-04-21 16:53:53 +02:00
/* Initialize RS232 for one line. */
register rs232_t *rs;
int line;
port_t this_8250;
2012-08-13 18:32:59 +02:00
int s, irq;
char l[10];
2005-04-21 16:53:53 +02:00
/* 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: not initializing rs232 line %d (in use by kernel)\n",
line);
return;
}
2005-04-21 16:53:53 +02:00
rs = tp->tty_priv = &rs_lines[line];
rs->tty = tp;
/* Set up input queue. */
rs->ihead = rs->itail = rs->ibuf;
/* Precalculate port numbers for speed. Magic numbers in the code (once). */
this_8250 = addr_8250[line];
rs->xmit_port = this_8250 + 0;
rs->recv_port = this_8250 + 0;
rs->div_low_port = this_8250 + 0;
rs->div_hi_port = this_8250 + 1;
rs->int_enab_port = this_8250 + 1;
rs->int_id_port = this_8250 + 2;
rs->line_ctl_port = this_8250 + 3;
rs->modem_ctl_port = this_8250 + 4;
rs->line_status_port = this_8250 + 5;
rs->modem_status_port = this_8250 + 6;
/* Set up the hardware to a base state, in particular
* o turn off DTR (MC_DTR) to try to stop the external device.
* o be careful about the divisor latch. Some BIOS's leave it enabled
* here and that caused trouble (no interrupts) in version 1.5 by
* hiding the interrupt enable port in the next step, and worse trouble
* (continual interrupts) in an old version by hiding the receiver
* port in the first interrupt. Call rs_ioctl() early to avoid this.
* o disable interrupts at the chip level, to force an edge transition
* on the 8259 line when interrupts are next enabled and active.
* RS232 interrupts are guaranteed to be disabled now by the 8259
* mask, but there used to be trouble if the mask was set without
* handling a previous interrupt.
*/
istop(rs); /* sets modem_ctl_port */
rs_config(rs);
sys_outb(rs->int_enab_port, 0);
2005-04-21 16:53:53 +02:00
/* Clear any harmful leftover interrupts. An output interrupt is harmless
* and will occur when interrupts are enabled anyway. Set up the output
* queue using the status from clearing the modem status interrupt.
*/
2012-08-13 18:32:59 +02:00
if ((s = sys_inb(rs->line_status_port, &dummy)) != OK)
printf("TTY: sys_inb() failed: %d", s);
if ((s = sys_inb(rs->recv_port, &dummy)) != OK)
printf("TTY: sys_inb() failed: %d", s);
2005-04-21 16:53:53 +02:00
rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
rs->ohead = rs->otail = rs->obuf;
/* Enable interrupts for both interrupt controller and device. */
irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;
2005-07-29 17:22:58 +02:00
rs->irq = irq;
2005-07-29 17:33:31 +02:00
rs->irq_hook_id = rs->irq; /* call back with irq line number */
2005-09-11 19:09:11 +02:00
if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
printf("RS232: Couldn't obtain hook for irq %d\n", irq);
} else {
2005-09-11 19:09:11 +02:00
if (sys_irqenable(&rs->irq_hook_id) != OK) {
printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
}
}
2005-07-29 17:22:58 +02:00
rs_irq_set |= (1 << irq);
sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
2005-04-21 16:53:53 +02:00
| IE_RECEIVER_READY | IE_TRANSMITTER_READY);
/* 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_on = rs_break_on;
tp->tty_break_off = rs_break_off;
2005-04-21 16:53:53 +02:00
tp->tty_close = rs_close;
/* Tell external device we are ready. */
istart(rs);
2005-07-29 17:22:58 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_interrupt *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void rs_interrupt(message *m)
2005-07-29 17:22:58 +02:00
{
unsigned long irq_set;
int i;
rs232_t *rs;
irq_set= m->m_notify.interrupts;
2005-07-29 17:22:58 +02:00
for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
{
if (irq_set & (1 << rs->irq))
rs232_handler(rs);
}
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_icancel *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int rs_icancel(tty_t *tp, int UNUSED(dummy))
2005-04-21 16:53:53 +02:00
{
/* Cancel waiting input. */
rs232_t *rs = tp->tty_priv;
rs->icount = 0;
rs->itail = rs->ihead;
istart(rs);
2005-07-29 17:22:58 +02:00
return 0; /* dummy */
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_ocancel *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int rs_ocancel(tty_t *tp, int UNUSED(dummy))
2005-04-21 16:53:53 +02:00
{
/* Cancel pending output. */
rs232_t *rs = tp->tty_priv;
rs->ostate &= ~(ODONE | OQUEUED);
rs->ocount = 0;
rs->otail = rs->ohead;
2005-07-29 17:22:58 +02:00
return 0; /* dummy */
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_read *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int rs_read(tty_t *tp, int try)
2005-04-21 16:53:53 +02:00
{
/* Process characters from the circular input buffer. */
rs232_t *rs = tp->tty_priv;
int icount, count, ostate;
if (!(tp->tty_termios.c_cflag & CLOCAL)) {
2005-09-11 19:09:11 +02:00
if (try) return 1;
2005-04-21 16:53:53 +02:00
/* Send a SIGHUP if hangup detected. */
ostate = rs->ostate;
rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
if (ostate & ODEVHUP) {
sigchar(tp, SIGHUP, 1);
2005-04-21 16:53:53 +02:00
tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */
return 0;
2005-04-21 16:53:53 +02:00
}
}
2005-09-11 19:09:11 +02:00
if (try) {
if (rs->icount > 0)
return 1;
return 0;
}
2005-04-21 16:53:53 +02:00
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. */
Input infrastructure, INPUT server, PCKBD driver This commit separates the low-level keyboard driver from TTY, putting it in a separate driver (PCKBD). The commit also separates management of raw input devices from TTY, and puts it in a separate server (INPUT). All keyboard and mouse input from hardware is sent by drivers to the INPUT server, which either sends it to a process that has opened a raw input device, or otherwise forwards it to TTY for standard processing. Design by Dirk Vogt. Prototype by Uli Kastlunger. Additional changes made to the prototype: - the event communication is now based on USB HID codes; all input drivers have to use USB codes to describe events; - all TTY keymaps have been converted to USB format, with the effect that a single keymap covers all keys; there is no (static) escaped keymap anymore; - further keymap tweaks now allow remapping of literally all keys; - input device renumbering and protocol rewrite; - INPUT server rewrite, with added support for cancel and select; - PCKBD reimplementation, including PC/AT-to-USB translation; - support for manipulating keyboard LEDs has been added; - keyboard and mouse multiplexer devices have been added to INPUT, primarily so that an X server need only open two devices; - a new "libinputdriver" library abstracts away protocol details from input drivers, and should be used by all future input drivers; - both INPUT and PCKBD can be restarted; - TTY is now scheduled by KERNEL, so that it won't be punished for running a lot; without this, simply running "yes" on the console kills the system; - the KIOCBELL IOCTL has been moved to /dev/console; - support for the SCANCODES termios setting has been removed; - obsolete keymap compression has been removed; - the obsolete Olivetti M24 keymap has been removed. Change-Id: I3a672fb8c4fd566734e4b46d3994b4b7fc96d578
2013-09-28 14:46:21 +02:00
if ((count = in_process(tp, rs->itail, count)) == 0) break;
2005-04-21 16:53:53 +02:00
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;
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_ostart *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void rs_ostart(rs232_t *rs)
2005-04-21 16:53:53 +02:00
{
/* Tell RS232 there is something waiting in the output buffer. */
rs->ostate |= OQUEUED;
if (txready(rs)) out_int(rs);
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_break_on *
2005-09-11 19:09:11 +02:00
*===========================================================================*/
static int rs_break_on(tty_t *tp, int UNUSED(dummy))
2005-04-21 16:53:53 +02:00
{
/* Raise break condition. */
2005-04-21 16:53:53 +02:00
rs232_t *rs = tp->tty_priv;
2012-03-05 00:11:41 +01:00
u32_t line_controls;
2012-08-13 18:32:59 +02:00
int s;
2005-04-21 16:53:53 +02:00
2012-08-13 18:32:59 +02:00
if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
printf("TTY: sys_inb() failed: %d", s);
sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
return 0; /* dummy */
}
/*===========================================================================*
* rs_break_off *
*===========================================================================*/
static int rs_break_off(tty_t *tp, int UNUSED(dummy))
{
/* Clear break condition. */
rs232_t *rs = tp->tty_priv;
u32_t line_controls;
int s;
if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
printf("TTY: sys_inb() failed: %d", s);
sys_outb(rs->line_ctl_port, line_controls & ~LC_BREAK);
2005-07-29 17:22:58 +02:00
return 0; /* dummy */
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs_close *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int rs_close(tty_t *tp, int UNUSED(dummy))
2005-04-21 16:53:53 +02:00
{
/* The line is closed; optionally hang up. */
rs232_t *rs = tp->tty_priv;
if (tp->tty_termios.c_cflag & HUPCL) {
sys_outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS);
2005-04-21 16:53:53 +02:00
}
2005-07-29 17:22:58 +02:00
return 0; /* dummy */
2005-04-21 16:53:53 +02:00
}
/* Low level (interrupt) routines. */
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* rs232_handler *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void rs232_handler(struct rs232 *rs)
2005-04-21 16:53:53 +02:00
{
/* Interrupt hander for RS232. */
2012-08-13 18:32:59 +02:00
int s;
int trying = 1000;
2005-04-21 16:53:53 +02:00
while (trying--) {
2012-03-05 00:11:41 +01:00
u32_t v;
2005-04-21 16:53:53 +02:00
/* Loop to pick up ALL pending interrupts for device.
* This usually just wastes time unless the hardware has a buffer
* (and then we have to worry about being stuck in the loop too long).
* Unfortunately, some serial cards lock up without this.
*/
2012-08-13 18:32:59 +02:00
if ((s = sys_inb(rs->int_id_port, &v)) != OK)
panic("TTY: sys_inb() failed: %d", s);
/* do we have interrupt info? */
if(v & IS_NOTPENDING) return;
/* what kind of interrupt? */
switch (v & IS_IDBITS) {
2005-04-21 16:53:53 +02:00
case IS_RECEIVER_READY:
in_int(rs);
continue;
case IS_TRANSMITTER_READY:
out_int(rs);
continue;
case IS_MODEM_STATUS_CHANGE:
modem_int(rs);
continue;
case IS_LINE_STATUS_CHANGE:
line_int(rs);
continue;
}
2005-07-29 17:22:58 +02:00
return;
2005-04-21 16:53:53 +02:00
}
printf("tty rs232: enough!\n");
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* in_int *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void in_int(register rs232_t *rs)
2010-01-27 11:19:13 +01:00
/* rs line with input interrupt */
2005-04-21 16:53:53 +02:00
{
/* Read the data which just arrived.
* If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
* it and restart output (any char does this, not just xon).
* Put data in the buffer if room, otherwise discard it.
* Set a flag for the clock interrupt handler to eventually notify TTY.
*/
2012-08-13 18:32:59 +02:00
int s;
2012-03-05 00:11:41 +01:00
u32_t c;
2005-04-21 16:53:53 +02:00
#if 0 /* Enable this if you want serial input in the kernel */
return;
#endif
2012-08-13 18:32:59 +02:00
if ((s = sys_inb(rs->recv_port, &c)) != OK)
printf("TTY: sys_inb() failed: %d", s);
2005-04-21 16:53:53 +02:00
if (!(rs->ostate & ORAW)) {
if (c == rs->oxoff) {
rs->ostate &= ~OSWREADY;
} else
if (!(rs->ostate & OSWREADY)) {
rs->ostate |= OSWREADY;
if (txready(rs)) out_int(rs);
}
}
if (rs->icount == buflen(rs->ibuf))
{
printf("in_int: discarding byte\n");
return; /* input buffer full, discard */
}
2005-04-21 16:53:53 +02:00
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;
force_timeout();
}
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* line_int *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void line_int(register rs232_t *rs)
2010-01-27 11:19:13 +01:00
/* rs line with line status interrupt */
2005-04-21 16:53:53 +02:00
{
/* Check for and record errors. */
2012-08-13 18:32:59 +02:00
int r;
2012-03-05 00:11:41 +01:00
u32_t s;
2012-08-13 18:32:59 +02:00
if ((r = sys_inb(rs->line_status_port, &s)) != OK)
printf("TTY: sys_inb() failed: %d", r);
2006-03-25 00:02:56 +01:00
rs->lstatus = s;
2005-04-21 16:53:53 +02:00
if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* modem_int *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void modem_int(register rs232_t *rs)
2010-01-27 11:19:13 +01:00
/* rs line with modem interrupt */
2005-04-21 16:53:53 +02:00
{
/* Get possibly new device-ready status, and clear ODEVREADY if necessary.
* If the device just became ready, restart output.
*/
if (devhup(rs)) {
rs->ostate |= ODEVHUP;
rs->tty->tty_events = 1;
force_timeout();
}
if (!devready(rs))
rs->ostate &= ~ODEVREADY;
else if (!(rs->ostate & ODEVREADY)) {
rs->ostate |= ODEVREADY;
if (txready(rs)) out_int(rs);
}
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
2005-04-21 16:53:53 +02:00
* out_int *
2005-09-11 19:09:11 +02:00
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void out_int(register rs232_t *rs)
2010-01-27 11:19:13 +01:00
/* rs; line with output interrupt */
2005-04-21 16:53:53 +02:00
{
/* 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. */
sys_outb(rs->xmit_port, *rs->otail);
2005-04-21 16:53:53 +02:00
if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
if (--rs->ocount == 0) {
rs->ostate ^= (ODONE | OQUEUED); /* ODONE on, OQUEUED off */
rs->tty->tty_events = 1;
force_timeout();
} else
if (rs->ocount == RS_OLOWWATER) { /* running low? */
rs->tty->tty_events = 1;
force_timeout();
}
}
}
#endif /* NR_RS_LINES > 0 */