[comp.os.minix] V1.3 posting #30, Full tty.c

ast@cs.vu.nl (Andy Tanenbaum) (07/14/88)

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'tty.c.part2'
sed 's/^X//' > 'tty.c.part2' << '+ END-OF-FILE ''tty.c.part2'
X  /* Update terminal data structure. */
X  count = offset - offset1;	/* # characters printed */
X  tp->tty_phys += count;	/* advance physical data pointer */
X  tp->tty_cum += count;		/* number of characters printed */
X
X  /* If all data has been copied to the terminal, send the reply. */
X  if (tp->tty_outleft == 0) finish(tp, tp->tty_cum);
X}
X
X
X/*===========================================================================*
X *				out_char				     *
X *===========================================================================*/
XPRIVATE out_char(tp, c)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xchar c;				/* character to be output */
X{
X/* Output a character on the console.  Check for escape sequences first. */
X
X  if (tp->tty_esc_state > 0) {
X	parse_escape(tp, c);
X	return;
X  }
X
X  switch(c) {
X	case 007:		/* ring the bell */
X		flush(tp);	/* print any chars queued for output */
X		beep(BEEP_FREQ);/* BEEP_FREQ gives bell tone */
X		return;
X
X	case 013:		/* CTRL-K */
X		move_to(tp, tp->tty_column, tp->tty_row - 1);
X		return;
X
X	case 014:		/* CTRL-L */
X		move_to(tp, tp->tty_column + 1, tp->tty_row);
X		return;
X
X	case 016:		/* CTRL-N */
X		move_to(tp, tp->tty_column + 1, tp->tty_row);
X		return;
X
X	case '\b':		/* backspace */
X		move_to(tp, tp->tty_column - 1, tp->tty_row);
X		return;
X
X	case '\n':		/* line feed */
X		if (tp->tty_mode & CRMOD) out_char(tp, '\r');
X		if (tp->tty_row == SCR_LINES-1) 
X			scroll_screen(tp, GO_FORWARD);
X		else
X			tp->tty_row++;
X		move_to(tp, tp->tty_column, tp->tty_row);
X		return;
X
X	case '\r':		/* carriage return */
X		move_to(tp, 0, tp->tty_row);
X		return;
X
X	case '\t':		/* tab */
X		if ( (tp->tty_mode & XTABS) == XTABS) {
X			do {
X				out_char(tp, ' ');
X			} while (tp->tty_column & TAB_MASK);
X			return;
X		}
X		/* Ignore tab if XTABS is off--video RAM has no hardware tab */
X		return;
X
X	case 033:		/* ESC - start of an escape sequence */
X		flush(tp);	/* print any chars queued for output */
X		tp->tty_esc_state = 1;	/* mark ESC as seen */
X		return;
X
X	default:		/* printable chars are stored in ramqueue */
X		if (tp->tty_column >= LINE_WIDTH) return;	/* long line */
X		if (tp->tty_rwords == TTY_RAM_WORDS) flush(tp);
X		tp->tty_ramqueue[tp->tty_rwords++]=tp->tty_attribute|(c & BYTE);
X		tp->tty_column++;	/* next column */
X		return;
X  }
X}
X
X/*===========================================================================*
X *				scroll_screen				     *
X *===========================================================================*/
XPRIVATE scroll_screen(tp, dir)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xint dir;			/* GO_FORWARD or GO_BACKWARD */
X{
X  int amount, offset, bytes;
X
X  bytes = 2 * (SCR_LINES - 1) * LINE_WIDTH;	/* 2 * 24 * 80 bytes */
X
X  /* Scrolling the screen is a real nuisance due to the various incompatible
X   * video cards.  This driver supports hardware scrolling (mono and CGA cards)
X   * and software scrolling (EGA cards).
X   */
X  if (softscroll) {
X	/* Software scrolling for non-IBM compatible EGA cards. */
X	if (dir == GO_FORWARD) {
X		scr_up(vid_base);
X		vid_copy(NIL_PTR, vid_base, tp->tty_org+bytes, LINE_WIDTH);
X	} else {
X		scr_down(vid_base);
X		vid_copy(NIL_PTR, vid_base, tp->tty_org, LINE_WIDTH);
X	}
X  } else {
X	/* Normal scrolling using the 6845 registers. */
X	amount = (dir == GO_FORWARD ? 2 * LINE_WIDTH : -2 * LINE_WIDTH);
X	tp->tty_org = (tp->tty_org + amount) & vid_mask;
X	if (dir == GO_FORWARD)
X		offset = (tp->tty_org + bytes) & vid_mask;
X	else
X		offset = tp->tty_org;
X
X	/* Blank the new line at top or bottom. */
X	vid_copy(NIL_PTR, vid_base, offset, LINE_WIDTH);
X	set_6845(VID_ORG, tp->tty_org >> 1);	/* 6845 thinks in words */
X  }
X}
X
X
X/*===========================================================================*
X *				flush					     *
X *===========================================================================*/
XPRIVATE flush(tp)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
X{
X/* Have the characters in 'ramqueue' transferred to the screen. */
X
X  if (tp->tty_rwords == 0) return;
X  vid_copy((char *)tp->tty_ramqueue, vid_base, tp->tty_vid, tp->tty_rwords);
X
X  /* Update the video parameters and cursor. */
X  tp->tty_vid = (tp->tty_vid + 2 * tp->tty_rwords);
X  set_6845(CURSOR, tp->tty_vid >> 1);	/* cursor counts in words */
X  tp->tty_rwords = 0;
X}
X
X
X/*===========================================================================*
X *				move_to					     *
X *===========================================================================*/
XPRIVATE move_to(tp, x, y)
Xstruct tty_struct *tp;		/* pointer to tty struct */
Xint x;				/* column (0 <= x <= 79) */
Xint y;				/* row (0 <= y <= 24, 0 at top) */
X{
X/* Move the cursor to (x, y). */
X
X  flush(tp);			/* flush any pending characters */
X  if (x < 0 || x >= LINE_WIDTH || y < 0 || y >= SCR_LINES) return;
X  tp->tty_column = x;		/* set x co-ordinate */
X  tp->tty_row = y;		/* set y co-ordinate */
X  tp->tty_vid = (tp->tty_org + 2*y*LINE_WIDTH + 2*x);
X  set_6845(CURSOR, tp->tty_vid >> 1);	/* cursor counts in words */
X}
X
X
X/*===========================================================================*
X *				escape					     *
X *===========================================================================*/
XPRIVATE parse_escape(tp, c)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xchar c;				/* next character in escape sequence */
X{
X/* The following ANSI escape sequences are currently supported:
X *   ESC M         to reverse index the screen
X *   ESC [ y ; x H to move cursor to (x, y) [default (1,1)]
X *   ESC [ 0 J     to clear from cursor to end of screen
X *   ESC [ n m     to set the screen rendition
X *			n: 0 = normal [default]
X *			   7 = reverse
X */
X
X  switch (tp->tty_esc_state) {
X	case 1: 		/* ESC seen */
X		tp->tty_esc_intro = '\0';
X		tp->tty_esc_parmp = tp->tty_esc_parmv;
X		tp->tty_esc_parmv[0] = tp->tty_esc_parmv[1] = 0;
X		switch (c) {
X		  case '[': 	/* Control Sequence Introducer */
X			tp->tty_esc_intro = c;
X			tp->tty_esc_state = 2; 
X			break;
X		  case 'M': 	/* Reverse Index */
X			do_escape(tp, c);
X			break;
X		  default: 
X			tp->tty_esc_state = 0; 
X			break;
X		}
X		break;
X
X	case 2: 		/* ESC [ seen */
X		if (c >= '0' && c <= '9') {
X			if (tp->tty_esc_parmp 
X					< tp->tty_esc_parmv + MAX_ESC_PARMS)
X				*tp->tty_esc_parmp =
X				  *tp->tty_esc_parmp * 10 + (c - '0');
X			break;
X		}
X		else if (c == ';') {
X			if (++tp->tty_esc_parmp 
X					< tp->tty_esc_parmv + MAX_ESC_PARMS)
X				*tp->tty_esc_parmp = 0;
X			break;
X		}
X		else {
X			do_escape(tp, c);
X		}
X		break;
X	default:		/* illegal state */
X		tp->tty_esc_state = 0;
X		break;
X  }
X}
X
X
X/*===========================================================================*
X *				do_escape				     *
X *===========================================================================*/
XPRIVATE do_escape(tp, c)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xchar c;				/* next character in escape sequence */
X{
X  int n, ct, vx;
X
X  /* Handle a sequence beginning with just ESC */
X  if (tp->tty_esc_intro == '\0') {
X    switch (c) {
X	case 'M':		/* Reverse Index */
X		if (tp->tty_row == 0)
X			scroll_screen(tp, GO_BACKWARD);
X		else
X			tp->tty_row--;
X		move_to(tp, tp->tty_column, tp->tty_row);
X		break;
X	default: break;
X    }
X  }
X  else
X  /* Handle a sequence beginning with ESC [ and parameters */
X  if (tp->tty_esc_intro == '[') {
X    switch (c) {
X	case 'H':		/* Position cursor */
X		move_to(tp, 
X			MAX(1, MIN(LINE_WIDTH, tp->tty_esc_parmv[1])) - 1,
X			MAX(1, MIN(SCR_LINES, tp->tty_esc_parmv[0])) - 1 );
X		break;
X	case 'J':		/* Clear from cursor to end of screen */
X		if (tp->tty_esc_parmv[0] == 0) {
X			n = 2 * ((SCR_LINES - (tp->tty_row + 1)) * LINE_WIDTH
X				+ LINE_WIDTH - (tp->tty_column));
X			vx = tp->tty_vid;
X			while (n > 0) {
X				ct = MIN(n, vid_retrace);
X				vid_copy(NIL_PTR, vid_base, vx, ct/2);
X				vx += ct;
X				n -= ct;
X			}
X		}
X		break;
X	case 'm':		/* Set graphic rendition */
X 		switch (tp->tty_esc_parmv[0]) {
X 			case 1: /*  BOLD  (light green on black)  */
X 				tp->tty_attribute = 0x0A << 8;
X 				break;
X 
X 			case 4: /*  UNDERLINE  (blue on red)  */
X 				tp->tty_attribute = 0x41 << 8;
X 				break;
X 
X 			case 5: /*  BLINKING  (light grey on black)  */
X				tp->tty_attribute = 0x87 << 8;
X 				break;
X 
X 			case 7: /*  REVERSE  (black on light grey)  */
X 				tp->tty_attribute = 0x70 << 8;
X  				break;
X
X 			default: tp->tty_attribute = 0007 << 8;
X 				break;
X 		}
X		break;
X	default:
X		break;
X    }
X  }
X  tp->tty_esc_state = 0;
X}
X
X
X/*===========================================================================*
X *				set_6845				     *
X *===========================================================================*/
XPRIVATE set_6845(reg, val)
Xint reg;			/* which register pair to set */
Xint val;			/* 16-bit value to set it to */
X{
X/* Set a register pair inside the 6845.  
X * Registers 10-11 control the format of the cursor (how high it is, etc).
X * Registers 12-13 tell the 6845 where in video ram to start (in WORDS)
X * Registers 14-15 tell the 6845 where to put the cursor (in WORDS)
X *
X * Note that registers 12-15 work in words, i.e. 0x0000 is the top left
X * character, but 0x0001 (not 0x0002) is the next character.  This addressing
X * is different from the way the 8088 addresses the video ram, where 0x0002
X * is the address of the next character.
X */
X  port_out(vid_port + INDEX, reg);	/* set the index register */
X  port_out(vid_port + DATA, (val>>8) & BYTE);	/* output high byte */
X  port_out(vid_port + INDEX, reg + 1);	/* again */
X  port_out(vid_port + DATA, val&BYTE);	/* output low byte */
X}
X
X
X/*===========================================================================*
X *				beep					     *
X *===========================================================================*/
XPRIVATE beep(f)
Xint f;				/* this value determines beep frequency */
X{
X/* Making a beeping sound on the speaker (output for CRTL-G).  The beep is
X * kept short, because interrupts must be disabled during beeping, and it
X * is undesirable to keep them off too long.  This routine works by turning
X * on the bits in port B of the 8255 chip that drive the speaker.
X */
X
X  int x, k, s;
X
X  s = lock();			/* disable interrupts */
X  port_out(TIMER3,0xB6);	/* set up timer channel 2 mode */
X  port_out(TIMER2, f&BYTE);	/* load low-order bits of frequency in timer */
X  port_out(TIMER2,(f>>8)&BYTE);	/* now high-order bits of frequency in timer */
X  port_in(PORT_B,&x);		/* acquire status of port B */
X  port_out(PORT_B, x|3);	/* turn bits 0 and 1 on to beep */
X  for (k = 0; k < B_TIME; k++);	/* delay loop while beeper sounding */
X  port_out(PORT_B, x);		/* restore port B the way it was */
X  restore(s);			/* re-enable interrupts to previous state */
X}
X
X
X/*===========================================================================*
X *				set_leds				     *
X *===========================================================================*/
XPRIVATE set_leds()
X{
X/* Set the LEDs on the caps lock and num lock keys */
X
X  int leds, dummy, i;
X
X  if (pc_at == 0) return;	/* PC/XT doesn't have LEDs */
X  leds = (numlock<<1) | (capslock<<2);	/* encode LED bits */
X  port_out(KEYBD, LED_CODE);	/* prepare keyboard to accept LED values */
X  port_in(KEYBD, &dummy);	/* keyboard sends ack; accept it */
X  for (i = 0; i < LED_DELAY; i++) ;	/* delay needed */
X  port_out(KEYBD, leds);	/* give keyboard LED values */
X  port_in(KEYBD, &dummy);	/* keyboard sends ack; accept it */
X}
X
X
X/*===========================================================================*
X *				tty_init				     *
X *===========================================================================*/
XPRIVATE tty_init()
X{
X/* Initialize the tty tables. */
X
X  register struct tty_struct *tp;
X
X  /* Tell the EGA card, if any, to simulate a 16K CGA card. */
X  port_out(EGA + INDEX, 4);	/* register select */
X  port_out(EGA + DATA, 1);	/* no extended memory to be used */
X
X  for (tp = &tty_struct[0]; tp < &tty_struct[NR_CONS]; tp++) {
X	tp->tty_inhead = tp->tty_inqueue;
X	tp->tty_intail = tp->tty_inqueue;
X	tp->tty_mode = CRMOD | XTABS | ECHO;
X	tp->tty_devstart = console;
X	tp->tty_makebreak = TWO_INTS;
X	tp->tty_attribute = BLANK;
X	tp->tty_erase = ERASE_CHAR;
X	tp->tty_kill  = KILL_CHAR;
X	tp->tty_intr  = INTR_CHAR;
X	tp->tty_quit  = QUIT_CHAR;
X	tp->tty_xon   = XON_CHAR;
X	tp->tty_xoff  = XOFF_CHAR;
X	tp->tty_eof   = EOT_CHAR;
X  }
X
X  if (color) {
X	vid_base = COLOR_BASE;
X	vid_mask = C_VID_MASK;
X	vid_port = C_6845;
X	vid_retrace = C_RETRACE;
X  } else {
X	vid_base = MONO_BASE;
X	vid_mask = M_VID_MASK;
X	vid_port = M_6845;
X	vid_retrace = M_RETRACE;
X  }
X  tty_driver_buf[1] = MAX_OVERRUN;	/* set up limit on keyboard buffering*/
X  set_6845(CUR_SIZE, 31);		/* set cursor shape */
X  set_6845(VID_ORG, 0);			/* use page 0 of video ram */
X  move_to(&tty_struct[0], 0, SCR_LINES-1); /* move cursor to lower left */
X
X  /* Determine which keyboard type is attached.  The bootstrap program asks 
X   * the user to type an '='.  The scan codes for '=' differ depending on the
X   * keyboard in use.
X   */
X  if (scan_code == OLIVETTI_EQUAL) olivetti = TRUE;
X}
X
X
X/*===========================================================================*
X *				putc					     *
X *===========================================================================*/
XPUBLIC putc(c)
Xchar c;				/* character to print */
X{
X/* This procedure is used by the version of printf() that is linked with
X * the kernel itself.  The one in the library sends a message to FS, which is
X * not what is needed for printing within the kernel.  This version just queues
X * the character and starts the output.
X */
X
X  out_char(&tty_struct[0], c);
X}
X
X
X/*===========================================================================*
X *				func_key				     *
X *===========================================================================*/
XPRIVATE func_key(ch)
Xchar ch;			/* scan code for a function key */
X{
X/* This procedure traps function keys for debugging purposes.  When MINIX is
X * fully debugged, it should be removed.
X */
X
X  if (ch == F1) p_dmp();	/* print process table */
X  if (ch == F2) map_dmp();	/* print memory map */
X  if (ch == F3) {		/* hardware vs. software scrolling */
X	softscroll = 1 - softscroll;	/* toggle scroll mode */
X	tty_struct[0].tty_org = 0;
X	move_to(&tty_struct[0], 0, SCR_LINES-1); /* cursor to lower left */
X	set_6845(VID_ORG, 0);
X	if (softscroll)
X		printf("\033[H\033[JSoftware scrolling enabled.\n");
X	else
X		printf("\033[H\033[JHardware scrolling enabled.\n");
X  }
X
X#ifdef AM_KERNEL
X#ifndef NONET
X  if (ch == F4) net_init();	/* re-initialise the ethernet card */
X#endif NONET
X#endif AM_KERNEL
X  if (ch == F9) sigchar(&tty_struct[0], SIGKILL);	/* issue SIGKILL */
X}
X#endif
X
X/****************************************************************************
X ****************************************************************************
X ****************************************************************************
X ****************************************************************************/
X
X
X#ifdef i8088
X/* Now begins the code and data for the device RS232 drivers. */
X
X/* Definitions used by the RS232 driver. */
X#define	RS_BUF_SIZE		 256	/* output buffer per serial line */
X#define PRIMARY                0x3F8	/* I/O port of primary RS232 */
X#define SECONDARY              0x2F8	/* I/O port of secondary RS232 */
X#define SPARE                     16	/* leave room in buffer for echoes */
X#define THRESHOLD                 20	/* # chars to accumulate before msg */
X
X/* Constants relating to the 8250. */
X#define	RS232_RATE_DIVISOR	   0	/* address of baud rate divisor reg */
X#define	RS232_TRANSMIT_HOLDING	   0	/* address of transmitter holding reg*/
X#define	RS232_RECEIVER_DATA_REG	   0	/* address of receiver data register */
X#define	RS232_INTERRUPTS	   1	/* address of interrupt enable reg */
X#define	RS232_INTERRUPT_ID_REG	   2	/* address of interrupt id register */
X#define	RS232_LINE_CONTROL	   3	/* address of line control register */
X#define	RS232_MODEM_CONTROL	   4	/* address of modem control register */
X#define	RS232_LINE_STATUS	   5	/* address of line status register */
X#define	RS232_MODEM_STATUS	   6	/* address of modem status register */
X#define	LINE_CONTROLS		0x0B	/* odd parity,1 stop bit,8 data bits */
X#define	MODEM_CONTROLS		0x0B	/* RTS & DTR */
X#define	ADDRESS_DIVISOR		0x80	/* value to address divisor */
X#define HOLDING_REG_EMPTY       0x20	/* transmitter holding reg empty */
X#define	RS232_INTERRUPT_CLASSES	0x03	/* receiver Data Ready & xmt empty */
X#define	UART_FREQ  	     115200L	/* UART timer frequency */
X#define DEF_BAUD                1200	/* default baud rate */
X
X/* Line control setting related constants. */
X#define ODD			   0
X#define	EVEN			   1
X#define NONE			  -1
X#define	PARITY_ON_OFF		0x08	/* position of parity bit in line reg*/
X#define	PARITY_TYPE_SHIFT	   4	/* shift count for parity_type bit */
X#define	STOP_BITS_SHIFT		   2	/* shift count for # stop_bits */
X#define DATA_LEN                   8	/* how much to shift sg_mode for len */
X
X/* RS232 interrupt types. */
X#define MODEM_STATUS            0x00	/* UART modem status change */
X#define TRANSMITTER_READY	0x02	/* transmitter ready to accept data */
X#define RECEIVER_READY		0x04	/* data received interrupt */
X#define LINE_STATUS             0x06	/* UART line status change */
X#define INT_TYPE_MASK		0x06	/* mask to mask out interrupt type */
X#define	INT_PENDING		0x01	/* position of interrupt-pending bit */
X
X/* Status register values. */
X#define DATA_REGISTER_EMPTY	0x20	/* mask to see if data reg is empty */
X#define DATA_RECEIVED		0x01	/* mask to see if data has arrived */
X
X/* Global variables used by the RS232 driver. */
XPUBLIC message rs232_rd_mess;		/* used when chars arrive from tty */
XPUBLIC message rs232_wt_mess;		/* used when output to tty done */
XPUBLIC int flush_flag;			/* indicates chars in tty_driver_buf */
XPRIVATE int first_rs_write_int_seen = FALSE;
X
XPRIVATE struct rs_struct{
X  int rs_base;			/* 0x3F8 for primary, 0x2F8 secondary*/
X  int rs_busy;			/* line is idle or not */
X  int rs_left;			/* # chars left in buffer to output */
X  char *rs_next;		/* pointer to next char to output */
X  char rs_buf[RS_BUF_SIZE];	/* output buffer */
X} rs_struct[NR_RS_LINES];
X
X
X/*===========================================================================*
X *				rs232				 	     *
X *===========================================================================*/
XPUBLIC rs232(unit)
Xint unit;				/* which unit caused the interrupt */
X{
X/* When an RS232 interrupt occurs, mpx88.s catches it and calls rs232().
X * Because more than one interrupt condition can occur at the same
X * time, the conditions are presented in the interrupt-identification
X * register in priority order, we have to keep scanning until the 
X * interrupt-pending bit goes down.  Only one communications port is really
X * supported here because the other vector is used by the Ethernet.
X */
X
X  int interrupt_type, t, old_state, val;
X  struct rs_struct *rs;
X
X  old_state = lock();
X  rs = &rs_struct[unit - NR_CONS];
X  while (TRUE) {
X	port_in(rs->rs_base + RS232_INTERRUPT_ID_REG, &interrupt_type); 
X	if ((interrupt_type & INT_PENDING) == 1) break;	/* 1 = no interrupt */
X	t = interrupt_type & INT_TYPE_MASK;
X	switch(t) {
X	    case RECEIVER_READY:	/* a character has arrived */
X		rs_read_int(unit);
X		break;
X
X	    case TRANSMITTER_READY:	/* a character has been output */
X		rs_write_int(unit);
X		break;
X
X	    case LINE_STATUS:		/* line status event, (disabled) */
X		port_in(rs_struct[unit-1].rs_base + RS232_LINE_STATUS, &val);
X		printf("RS 232 line status event %x\n", val);
X		break;
X
X	    case MODEM_STATUS:		/* modem status event, (disabled) */
X		port_in(rs_struct[unit-1].rs_base + RS232_MODEM_STATUS, &val);
X		printf("RS 232 modem status event %x\n", val);
X		break;
X	}
X  }
X  restore(old_state);
X}
X
X
X/*===========================================================================*
X *				rs_read_int			 	     *
X *===========================================================================*/
XPRIVATE	rs_read_int(line)
Xint line;
X{
X  int val, k, base;
X
X  base = rs_struct[line - NR_CONS].rs_base;
X
X  /* Fetch the character from the RS232 hardware. */
X  port_in(base + RS232_RECEIVER_DATA_REG, &val);
X
X  /* Store the character in memory so the task can get at it later */
X  if ((k = tty_driver_buf[0]) < tty_driver_buf[1]) {
X	/* There is room to store this character, do it */
X	k = k + k;			/* each entry contains two bytes */
X	tty_driver_buf[k + 2] = val;	/* store the ascii code */
X	tty_driver_buf[k + 3] = line;	/* tell wich line it came from */ 
X	tty_driver_buf[0]++;		/* increment counter */
X
X	if (tty_driver_buf[0] < THRESHOLD) {
X		/* Don't send message.  Just accumulate.  Let clock do it. */
X		port_out(INT_CTL, ENABLE);
X		flush_flag++;
X		return;
X	}
X	rs_flush();			/* send TTY task a message */
X  } else {
X	/* Too many character have been buffered. Discard excess */
X	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
X  }
X}
X
X
X/*===========================================================================*
X *				rs_flush	  		 	     *
X *===========================================================================*/
XPUBLIC rs_flush()
X{
X/* Flush the tty_driver_buf by sending a message to TTY.  This procedure can
X * be triggered locally, when a character arrives, or by the clock task.
X */
X
X  /* Build and send the interrupt message */ 
X  flush_flag = 0;
X  if (tty_driver_buf[0] == 0) return;	/* nothing to flush */
X  rs232_rd_mess.m_type = TTY_CHAR_INT;
X  rs232_rd_mess.ADDRESS = tty_driver_buf;
X  interrupt(TTY, &rs232_rd_mess);	/* send a message to the tty task */
X}
X
X
X/*===========================================================================*
X *				rs_write_int	  		 	     *
X *===========================================================================*/
XPRIVATE	rs_write_int(line)
Xint line;
X{
X/* An output ready interrupt has occurred, or a write has been done to an idle
X * line.  In both cases, start the output.
X */
X
X  char byte;
X  int val;
X  struct tty_struct *tp;
X  struct rs_struct *rs;
X
X  if (first_rs_write_int_seen == FALSE) {
X	first_rs_write_int_seen = TRUE;
X	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
X	return;
X  }
X
X  /* On the XT and clones, check for spurious write-completed interrupts. */
X  rs = &rs_struct[line - NR_CONS];
X  port_in(rs->rs_base + RS232_LINE_STATUS, &val);
X  if ( (val & HOLDING_REG_EMPTY) == 0) {
X	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
X	return;
X  }
X
X  /* If there are more characters in rs_buf, output the next one. */
X  if (rs->rs_left > 0) {
X	rs_feed(rs);			/* output the next char in rs_buf */
X	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
X	return;
X  }
X
X  /* The current rs_buf is finished.  See if the complete user buf is done. */
X  tp = &tty_struct[line];
X  rs->rs_busy = FALSE;
X  rs->rs_next = &rs->rs_buf[0];
X  if (tp->tty_outleft > 0) {
X	serial_out(tp);			/* copy the next chunk to rs_buf */
X	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
X	return;
X  }
X
X  /* The current output buffer is finished.  Send a message to the tty task. */
X  if (tp->tty_waiting == WAITING) {
X	rs232_wt_mess.m_type = TTY_O_DONE;	/* build the message */
X	rs232_wt_mess.ADDRESS = tty_driver_buf;	/* pro forma */
X	rs232_wt_mess.TTY_LINE = line;	/* which line is finished */
X	tp->tty_waiting = COMPLETED;	/* mark this line as done */
X	output_done++;			/* # of RS232 lines now completed */
X	interrupt(TTY, &rs232_wt_mess);	/* send the message to the tty task */
X  } else {
X	port_out(INT_CTL, ENABLE);	/* re-enable 8259A controller */
X  }
X}
X
X
X/*===========================================================================*
X *				rs_feed		  		 	     *
X *===========================================================================*/
XPRIVATE rs_feed(rs)
Xstruct rs_struct *rs;			/* which line */
X{
X/* If there is more output queued, output the next character. */
X
X  char byte;
X
X  if (rs->rs_left > 0) {
X	byte = *rs->rs_next;
X	port_out(rs->rs_base + RS232_TRANSMIT_HOLDING, (int) byte);
X	rs->rs_next++;
X	rs->rs_left--;			/* one char done */
X  }
X}
X
X
X/*===========================================================================*
X *				start_rs232				     * 
X *===========================================================================*/
XPRIVATE	start_rs232(tp)
Xstruct tty_struct *tp;			/* which tty */
X{
X  int old_state;
X
X  old_state = lock();
X  serial_out(tp);
X  restore(old_state);
X}	
X
X
X/*===========================================================================*
X *				serial_out				     * 
X *===========================================================================*/
XPRIVATE	serial_out(tp)
Xregister struct tty_struct *tp;	/* tells which terminal is to be used */
X{
X/* Copy as much data as possible to the output queue, then start I/O if
X * necessary.
X */
X  int bytes, line;
X  char c, *limit;
X  unsigned short segment, offset, offset1;
X  register struct rs_struct *rs;
X  extern char get_byte();
X
X  if (tp->tty_inhibited != RUNNING) return;
X
X  line = tp - &tty_struct[0];		/* line is index into tty_struct */
X  rs = &rs_struct[line - NR_CONS];	/* 0 to NR_CONS - 1 are consoles */
X	
X  /* Copy bytes from user space to rs_buf. */
X  limit = &rs->rs_buf[RS_BUF_SIZE - SPARE];
X  segment = (tp->tty_phys >> 4) & WORD_MASK;
X  offset = tp->tty_phys & OFF_MASK;
X  offset1 = offset;
X
X  /* While there is still data to output and there is still room in buf, copy*/
X  while (tp->tty_outleft > 0 && rs->rs_next + rs->rs_left < limit) {
X	c = get_byte(segment, offset);	/* fetch 1 byte */
X	offset++;
X	tp->tty_outleft--;
X	if (c < ' ') {
X		rs_expand(tp, rs, c);	/* insert the char in rs_buf */
X	} else {
X		*(rs->rs_next + rs->rs_left) = c;	/* avoid proc call */
X		rs->rs_left++;
X		tp->tty_column++;
X	}
X  }
X
X  bytes = offset - offset1;		/* does not include '\r' or tab exp */
X  tp->tty_cum += bytes;			/* update cumulative total */
X  tp->tty_phys += bytes;		/* next time, take different bytes */
X
X  if (!rs->rs_busy) {			/* is the line idle? */
X	rs->rs_busy = TRUE;		/* if so, mark it as busy */
X	rs_feed(rs);			/* and start it going */
X  }
X}
X
X
X/*===========================================================================*
X *				rs_out_char				     *
X *===========================================================================*/
XPRIVATE rs_out_char(tp, c)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xchar c;				/* character to be output */
X{
X/* Output a character on an RS232 line. */
X
X  int line, old_state;
X  register struct rs_struct *rs;
X
X  /* See if there is room to store a character, and if so, do it. */
X  old_state = lock();
X  line = tp - tty_struct;
X  rs = &rs_struct[line - NR_CONS];
X  if (rs->rs_next + rs->rs_left == &rs->rs_buf[RS_BUF_SIZE]) return;  /*full */
X  rs_expand(tp, rs, c);
X
X  if (!rs->rs_busy) {		/* if terminal line is idle, start it */
X	rs->rs_busy = TRUE;
X	rs_feed(rs);
X  }
X  restore(old_state);
X}
X
X
X/*===========================================================================*
X *				rs_expand				     *
X *===========================================================================*/
XPRIVATE rs_expand(tp, rs, c)
Xregister struct tty_struct *tp;	/* pointer to tty struct */
Xregister struct rs_struct *rs;	/* pointer to rs struct */
Xchar c;				/* character to be output */
X{
X/* Some characters output to RS-232 lines need special processing, such as
X * tab expansion and LF to CR+CF mapping.  These things are done here.
X */
X
X  int mode, count, count1;
X  char *deposit;		/* where to deposit the next character */
X
X  mode = tp->tty_mode;
X  deposit = rs->rs_next + rs->rs_left;
X
X  switch(c) {
X	case '\b':
X		tp->tty_column -= 2;	/* it is incremented later */
X		break;
X
X	case '\r':
X		tp->tty_column = -1;	/* it is incremented below */
X		break;
X
X	case '\n':
X		/* Check to see if LF has to be mapped to CR + LF. */
X		if (mode & CRMOD) {
X			*deposit++ = '\r';
X			rs->rs_left++;
X			tp->tty_column = -1;
X		}
X		break;
X
X	case '\t':
X	 	count = 8 - (tp->tty_column % 8);	/* # spaces */
X		count1 = count;
X		if ((mode & XTABS) == XTABS) {
X			/* Tabs must be expanded. */
X			while (count1--) *deposit++ = ' ';
X			rs->rs_left += count;
X			tp->tty_column += count;
X			return;
X		} else {
X			/* Tabs are sent to the terminal hardware. */
X			tp->tty_column += count - 1;
X		}
X  }
X
X  /* Output character and update counters. */
X  *deposit = c;			/* copy character to rs_buf */
X  rs->rs_left++;		/* there is one more character to print */
X  tp->tty_column++;		/* update column */
X}
X
X
X/*===========================================================================*
X *				tty_o_done				     *
X *===========================================================================*/
XPRIVATE int tty_o_done(tp)
Xregister struct tty_struct *tp;	/* pointer to tty_struct */
X{
X/* A write request on an RS232 line has completed.  Send FS a message. */
X
X  int replyee, caller, old_state;
X  struct tty_struct *tp;
X
X  /* See if any of the RS232 lines are complete.  Send at most one message. */
X  old_state = lock();
X  for (tp = &tty_struct[NR_CONS]; tp < &tty_struct[NR_CONS+NR_RS_LINES]; tp++){
X	if (tp->tty_waiting == COMPLETED) {
X		replyee = (int) tp->tty_otcaller;
X		caller = (int) tp->tty_outproc;
X		tty_reply(REVIVE, replyee, caller, tp->tty_cum, 0L, 0L);
X		tp->tty_waiting = NOT_WAITING;
X		output_done--;
X		restore(old_state);
X		return(1);
X	}
X  }
X  restore(old_state);
X  return(0);
X}
X
X/*===========================================================================*
X *				rs_sig					     * 
X *===========================================================================*/
XPRIVATE rs_sig(tp)
Xstruct tty_struct *tp;
X{
X/* Called when a DEL character is typed.  It resets the output. */
X
X  int line;
X  struct rs_struct *rs;
X
X  line = tp - tty_struct;
X  rs = &rs_struct[line - NR_CONS];
X  rs->rs_left = 0;
X  rs->rs_busy = 0;
X  rs->rs_next = &rs->rs_buf[0];
X}
X
X
X/*===========================================================================*
X *				init_rs232				     * 
X *===========================================================================*/
XPRIVATE	init_rs232()
X{
X  register struct tty_struct *tp;
X  register struct rs_struct *rs;
X  int line;
X
X  for (tp = &tty_struct[NR_CONS]; tp < &tty_struct[NR_CONS+NR_RS_LINES]; tp++){
X	tp->tty_inhead = tp->tty_inqueue;
X	tp->tty_intail = tp->tty_inqueue;
X	tp->tty_mode = CRMOD | XTABS | ECHO;
X	tp->tty_devstart = start_rs232;
X	tp->tty_erase	= ERASE_CHAR;
X	tp->tty_kill	= KILL_CHAR;
X	tp->tty_intr	= INTR_CHAR;
X	tp->tty_quit	= QUIT_CHAR;
X	tp->tty_xon	= XON_CHAR;
X	tp->tty_xoff	= XOFF_CHAR;
X	tp->tty_eof	= EOT_CHAR;
X	tp->tty_makebreak = ONE_INT;	/* RS232 only interrupts once/char */
X  }
X
X  rs_struct[0].rs_base = PRIMARY;
X  rs_struct[1].rs_base = SECONDARY;
X
X  for (rs = &rs_struct[0]; rs < &rs_struct[NR_RS_LINES]; rs++) {
X	line = rs - rs_struct + NR_CONS;
X	rs->rs_next = & rs->rs_buf[0];
X	rs->rs_left = 0;
X	rs->rs_busy = FALSE;
X	config_rs232(line, DEF_BAUD, DEF_BAUD, NONE, 1, 8);    /* set params */
X	port_out(rs->rs_base + RS232_MODEM_CONTROL, MODEM_CONTROLS);
X	port_out(rs->rs_base + RS232_INTERRUPTS, RS232_INTERRUPT_CLASSES);
X  }
X}
X
X
X/*===========================================================================*
X *				set_uart			 	     *
X *===========================================================================*/
XPRIVATE set_uart(line, mode, speeds)
Xint line;			/* which line number (>= NR_CONS) */
Xint mode;			/* sgtty.h sg_mode word */
Xint speeds;			/* low byte is input speed, next is output */
X{
X/* Set the UART parameters. */
X  int in_baud, out_baud, parity, stop_bits, data_bits;
X
X  in_baud = 100 * (speeds & BYTE);
X  if (in_baud == 100) in_baud = 110;
X  out_baud = 100 * ((speeds >> 8) & BYTE);
X  if (out_baud == 100) out_baud = 110;
X  parity = NONE;
X  if (mode & ODDP) parity = ODD;
X  if (mode & EVENP) parity = EVEN;
X  stop_bits = (in_baud == 110 ? 2 : 1);		/* not quite cricket */
X  data_bits = 5 + ((mode >> DATA_LEN) & 03);
X  config_rs232(line, in_baud, out_baud, parity, stop_bits, data_bits);
X}
X
X
X/*===========================================================================*
X *				config_rs232			 	     *
X *===========================================================================*/
XPRIVATE	config_rs232(line, in_baud, out_baud, parity, stop_bits, data_bits)
Xint line;			/* which tty */
Xint in_baud;			/* input speed: 110, 300, 1200, etc. */
Xint out_baud;			/* output speed: 110, 300, 1200, etc. */
Xint parity;			/* EVEN, ODD, or NONE */
Xint stop_bits;			/* 2 (110 baud) or 1 (other speeds) */
Xint data_bits;			/* 5, 6, 7, or 8 */
X{
X/* Set various line control parameters for RS232 I/O.
X * If DataBits == 5 and StopBits == 2, UART will generate 1.5 stop bits
X * The 8250 can't handle split speed, but we have propagated both speeds
X * anyway for the benefit of future UART chips.
X */
X
X  int line_controls = 0, base, freq;
X
X  base = rs_struct[line - NR_CONS].rs_base;
X
X  /* First tell line control register to address baud rate divisor */
X  port_out(base + RS232_LINE_CONTROL, ADDRESS_DIVISOR);
X
X  /* Now set the baud rate. */
X  if (in_baud < 50) in_baud = DEF_BAUD;		/* prevent divide overflow */
X  if (out_baud < 50) out_baud = DEF_BAUD;	/* prevent divide overflow */
X  freq = (int) (UART_FREQ / in_baud);		/* UART can't hack 2 speeds  */
X  port_out(base + RS232_RATE_DIVISOR, freq & BYTE);
X  port_out(base + RS232_RATE_DIVISOR+1, (freq >> 8) & BYTE);
X  tty_struct[line].tty_speed = ((out_baud/100) << 8) | (in_baud/100);
X
X  /* Put parity_type bits in line_controls */
X  if (parity != NONE) {
X	line_controls |= PARITY_ON_OFF;
X	line_controls |= (parity << PARITY_TYPE_SHIFT);
X  }
X
X  /* Put #stop_bits bits in line_controls */
X  if (stop_bits == 1 || stop_bits == 2)
X	line_controls |= (stop_bits - 1) << STOP_BITS_SHIFT;
X
X  /* Put #data_bits bits in line_controls */
X  if (data_bits >=5 && data_bits <= 8)
X	line_controls |= (data_bits - 5);
X
X  port_out(base + RS232_LINE_CONTROL, line_controls);
X}
X
X#endif
X
+ END-OF-FILE tty.c.part2
chmod 'u=rw,g=r,o=r' 'tty.c.part2'
set `wc -c 'tty.c.part2'`
count=$1
case $count in
33602)	:;;
*)	echo 'Bad character count in ''tty.c.part2' >&2
		echo 'Count should be 33602' >&2
esac
exit 0