📄 rs232.c
字号:
case CS5: line_controls |= U_D8; break;
}
lock();
MFP->mf_ucr = line_controls;
MFP->mf_tddr = divisor;
unlock();
#endif /* MACHINE == ATARI */
}
/*==========================================================================*
* rs_init *
*==========================================================================*/
PUBLIC void rs_init(tp)
tty_t *tp; /* which TTY */
{
/* Initialize RS232 for one line. */
register rs232_t *rs;
int line;
#if (MACHINE == IBM_PC)
port_t this_8250;
int irq;
long v;
#endif
/* Associate RS232 and TTY structures. */
line = tp - &tty_table[NR_CONS];
rs = tp->tty_priv = &rs_lines[line];
rs->tty = tp;
/* Set up input queue. */
rs->ihead = rs->itail = rs->ibuf;
#if (MACHINE == IBM_PC)
/* 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;
#endif
/* 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);
#if (MACHINE == IBM_PC)
out_byte(rs->int_enab_port, 0);
#endif
/* 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.
*/
#if (MACHINE == IBM_PC)
in_byte(rs->line_status_port);
in_byte(rs->recv_port);
#endif
rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
rs->ohead = rs->otail = rs->obuf;
#if (MACHINE == IBM_PC)
/* Enable interrupts for both interrupt controller and device. */
irq = (line & 1) ? SECONDARY_IRQ : RS232_IRQ;
#if ENABLE_NETWORKING
/* The ethernet driver may steal the IRQ of an RS232 line. */
v = ETHER_IRQ;
switch (env_parse("DPETH0", "x:d:x", 1, &v, 0L, (long) NR_IRQ_VECTORS-1)) {
case EP_ON:
case EP_SET:
if (v == irq) return; /* IRQ in use, don't configure line */
}
#endif
put_irq_handler(irq, (line & 1) ? rs232_2handler : rs232_1handler);
enable_irq(irq);
out_byte(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
| IE_RECEIVER_READY | IE_TRANSMITTER_READY);
#else /* MACHINE == ATARI */
/* Initialize the 68901 chip, then enable interrupts. */
MFP->mf_scr = 0x00;
MFP->mf_tcdcr |= T_Q004;
MFP->mf_rsr = R_ENA;
MFP->mf_tsr = T_ENA;
MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^
(MFP->mf_gpip & (IO_SCTS|IO_SDCD));
MFP->mf_ddr = (MFP->mf_ddr & ~ (IO_SCTS|IO_SDCD));
MFP->mf_iera |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR);
MFP->mf_imra |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR);
MFP->mf_ierb |= (IB_SCTS|IB_SDCD);
MFP->mf_imrb |= (IB_SCTS|IB_SDCD);
#endif /* MACHINE == ATARI */
/* 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;
/* Tell external device we are ready. */
istart(rs);
}
/*==========================================================================*
* rs_icancel *
*==========================================================================*/
PRIVATE void rs_icancel(tp)
tty_t *tp; /* which TTY */
{
/* Cancel waiting input. */
rs232_t *rs = tp->tty_priv;
lock();
rs->icount = 0;
rs->itail = rs->ihead;
istart(rs);
unlock();
}
/*==========================================================================*
* rs_ocancel *
*==========================================================================*/
PRIVATE void rs_ocancel(tp)
tty_t *tp; /* which TTY */
{
/* Cancel pending output. */
rs232_t *rs = tp->tty_priv;
lock();
rs->ostate &= ~(ODONE | OQUEUED);
rs->ocount = 0;
rs->otail = rs->ohead;
unlock();
}
/*==========================================================================*
* rs_read *
*==========================================================================*/
PRIVATE void rs_read(tp)
tty_t *tp; /* which tty */
{
/* Process characters from the circular input buffer. */
rs232_t *rs = tp->tty_priv;
int icount, count, ostate;
if (!(tp->tty_termios.c_cflag & CLOCAL)) {
/* Send a SIGHUP if hangup detected. */
lock();
ostate = rs->ostate;
rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
unlock();
if (ostate & ODEVHUP) { sigchar(tp, SIGHUP); return; }
}
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;
lock(); /* protect interrupt sensitive variables */
rs->icount -= count;
if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
unlock();
if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
}
}
/*==========================================================================*
* rs_ostart *
*==========================================================================*/
PRIVATE void rs_ostart(rs)
rs232_t *rs; /* which rs line */
{
/* Tell RS232 there is something waiting in the output buffer. */
rs->ostate |= OQUEUED;
if (txready(rs)) out_int(rs);
}
/*==========================================================================*
* rs_break *
*==========================================================================*/
PRIVATE void rs_break(tp)
tty_t *tp; /* which tty */
{
/* Generate a break condition by setting the BREAK bit for 0.4 sec. */
rs232_t *rs = tp->tty_priv;
int line_controls;
line_controls = in_byte(rs->line_ctl_port);
out_byte(rs->line_ctl_port, line_controls | LC_BREAK);
milli_delay(400); /* ouch */
out_byte(rs->line_ctl_port, line_controls);
}
/* Low level (interrupt) routines. */
#if (MACHINE == IBM_PC)
/*==========================================================================*
* rs232_1handler *
*==========================================================================*/
PRIVATE int rs232_1handler(irq)
int irq;
{
/* Interrupt hander for IRQ4.
* Only 1 line (usually COM1) should use it.
*/
register rs232_t *rs = &rs_lines[0];
while (TRUE) {
/* 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.
*/
switch (in_byte(rs->int_id_port)) {
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;
}
return(1); /* reenable serial interrupt */
}
}
/*==========================================================================*
* rs232_2handler *
*==========================================================================*/
PRIVATE int rs232_2handler(irq)
int irq;
{
/* Interrupt hander for IRQ3.
* Only 1 line (usually COM2) should use it.
*/
register rs232_t *rs = &rs_lines[1];
while (TRUE) {
switch (in_byte(rs->int_id_port)) {
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;
}
return(1); /* reenable serial interrupt */
}
}
#else /* MACHINE == ATARI */
/*==========================================================================*
* siaint *
*==========================================================================*/
PRIVATE void siaint(type)
int type; /* interrupt type */
{
/* siaint is the rs232 interrupt procedure for Atari ST's. For ST there are
* as much as 5 interrupt lines used for rs232. The trap type byte left on the
* stack by the assembler interrupt handler identifies the interrupt cause.
*/
register unsigned char code;
register rs232_t *rs = &rs_lines[0];
int s = lock();
switch (type & 0x00FF)
{
case 0x00: /* receive buffer full */
in_int(rs);
break;
case 0x01: /* receive error */
line_int(rs);
break;
case 0x02: /* transmit buffer empty */
out_int(rs);
break;
case 0x03: /* transmit error */
code = MFP->mf_tsr;
if (code & ~(T_ENA | T_UE | T_EMPTY))
{
printf("sia: transmit error: status=%x\r\n", code);
/* MFP->mf_udr = lastchar; */ /* retry */
}
break;
case 0x04: /* modem lines change */
modem_int(rs);
break;
}
restore(s);
}
#endif /* MACHINE == ATARI */
/*==========================================================================*
* in_int *
*==========================================================================*/
PRIVATE void in_int(rs)
register rs232_t *rs; /* line with input interrupt */
{
/* 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.
*/
int c;
#if (MACHINE == IBM_PC)
c = in_byte(rs->recv_port);
#else /* MACHINE == ATARI */
c = MFP->mf_udr;
#endif
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)) return; /* input buffer full, discard */
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();
}
}
/*==========================================================================*
* line_int *
*==========================================================================*/
PRIVATE void line_int(rs)
register rs232_t *rs; /* line with line status interrupt */
{
/* Check for and record errors. */
#if (MACHINE == IBM_PC)
rs->lstatus = in_byte(rs->line_status_port);
#else /* MACHINE == ATARI */
rs->lstatus = MFP->mf_rsr;
MFP->mf_rsr &= R_ENA;
rs->pad = MFP->mf_udr; /* discard char in case of LS_OVERRUN_ERR */
#endif /* MACHINE == ATARI */
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;
}
/*==========================================================================*
* modem_int *
*==========================================================================*/
PRIVATE void modem_int(rs)
register rs232_t *rs; /* line with modem interrupt */
{
/* Get possibly new device-ready status, and clear ODEVREADY if necessary.
* If the device just became ready, restart output.
*/
#if (MACHINE == ATARI)
/* Set active edge interrupt so that next change causes a new interrupt */
MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^
(MFP->mf_gpip & (IO_SCTS|IO_SDCD));
#endif
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);
}
}
/*==========================================================================*
* out_int *
*==========================================================================*/
PRIVATE void out_int(rs)
register rs232_t *rs; /* line with output interrupt */
{
/* 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. */
#if (MACHINE == IBM_PC)
out_byte(rs->xmit_port, *rs->otail);
#else /* MACHINE == ATARI */
MFP->mf_udr = *rs->otail;
#endif
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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -