minix/drivers/tty/arch/earm/rs232.c
David van Moolenbroek 415782f70f TTY: do not reply to KERNEL
Diagnostics messages are printed using locally generated requests,
using KERNEL as the calling endpoint.  No reply should be sent for
such cases.  This check was accidentally lost with the previous
removal of tty_reply().

Change-Id: I4b76215a4d90e927b0071675d89d861aa399abb3
2014-07-28 17:05:13 +02:00

848 lines
23 KiB
C

#include <minix/config.h>
#include <minix/drivers.h>
#include <minix/vm.h>
#include <minix/type.h>
#include <minix/board.h>
#include <sys/mman.h>
#include <assert.h>
#include <signal.h>
#include <termios.h>
#include "omap_serial.h"
#include "tty.h"
#if NR_RS_LINES > 0
#define UART_FREQ 48000000L /* timer frequency */
#if 0
#define DFLT_BAUD TSPEED_DEF /* default baud rate */
#else
#define DFLT_BAUD B115200 /* default baud rate */
#endif
#define RS_IBUFSIZE 40960 /* RS232 input buffer size */
#define RS_OBUFSIZE 40960 /* 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 */
int rx_overrun_events;
int irq; /* irq for this line */
int irq_hook_id; /* interrupt hook */
int irq_hook_kernel_id; /* id as returned from sys_irqsetpolicy */
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 dm37xx_ports[] = {
{ OMAP3_UART1_BASE, 72}, /* UART1 */
{ OMAP3_UART2_BASE, 73}, /* UART2 */
{ OMAP3_UART3_BASE, 74}, /* UART3 */
{ 0, 0 }
};
static uart_port_t am335x_ports[] = {
{ 0x44E09000 , 72 }, /* UART0 */
{ 0, 0 },
{ 0, 0 },
{ 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_on(tty_t *tp, int try);
static int rs_break_off(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,
(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) {
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_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;
}
}
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;
}
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 */
#if 0
case B1843200: /* Fall through */
case B3686400:
#endif
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 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;
case B0:
default:
/* Reset the speed to the default speed, then call ourselves
* to convert the default speed to a baudrate. This call will
* always return a value without inducing another recursive
* call. */
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 */
rs->fcr = 0;
/* Set FIFO interrupt trigger levels high */
rs->fcr |= (0x3 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT);
rs->fcr |= (0x3 << OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT);
rs->fcr |= UART_FCR_ENABLE_FIFO;
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_kernel_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_kernel_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;
struct machine machine;
/* 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;
sys_getmachine(&machine);
if (BOARD_IS_BBXM(machine.board_id)){
this_omap3 = dm37xx_ports[line];
} else if (BOARD_IS_BB(machine.board_id)){
this_omap3 = am335x_ports[line];
} else {
return;
}
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;
/* callback with irq line number + 1 because using line number 0
fails eslewhere */
rs->irq_hook_kernel_id = rs->irq_hook_id = line + 1;
/* sys_irqsetpolicy modifies irq_hook_kernel_id. this modified id
* needs to be used in sys_irqenable and similar calls.
*/
if (sys_irqsetpolicy(rs->irq, 0, &rs->irq_hook_kernel_id) != OK) {
printf("RS232: Couldn't obtain hook for irq %d\n", rs->irq);
} else {
if (sys_irqenable(&rs->irq_hook_kernel_id) != OK) {
printf("RS232: Couldn't enable irq %d (hooked)\n",
rs->irq);
}
}
/* When we get called back we get called back using the original
* hook_id bit set. e.g. if we register with hook_id 5 the callback
* calls us with the 5 th bit set */
rs_irq_set |= (1 << (rs->irq_hook_id ));
/* 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_on = rs_break_on;
tp->tty_break_off = rs_break_off;
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->m_notify.interrupts;
for (line = 0, rs = rs_lines; line < NR_RS_LINES; line++, rs++) {
if (irq_set & (1 << rs->irq_hook_id)) {
rs232_handler(rs);
if (sys_irqenable(&rs->irq_hook_kernel_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)) == 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_on(tty_t *tp, int UNUSED(dummy))
{
/* Raise break condition */
rs232_t *rs = tp->tty_priv;
unsigned int lsr;
lsr = serial_in(rs, OMAP3_LSR);
serial_out(rs, OMAP3_LSR, lsr | UART_LSR_BI);
return 0; /* dummy */
}
static int
rs_break_off(tty_t *tp, int UNUSED(dummy))
{
/* Clear break condition */
rs232_t *rs = tp->tty_priv;
unsigned int lsr;
lsr = serial_in(rs, OMAP3_LSR);
serial_out(rs, OMAP3_LSR, lsr & ~UART_LSR_BI);
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;
if(serial_in(rs,OMAP3_LSR) & OMAP3_LSR_RXOE) {
rs->rx_overrun_events++;
}
/* check the line status to know if there are more chars */
while (serial_in(rs, OMAP3_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)) {
/* no buffer space? keep reading */
continue;
}
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 */