⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rs232.c

📁 MINIX2.0操作系统源码 MINIX2.0操作系统源码
💻 C
📖 第 1 页 / 共 2 页
字号:
	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 + -