ast@cs.vu.nl (Andy Tanenbaum) (06/28/88)
keybd_mess.m_type = TTY_CHAR_INT; keybd_mess.ADDRESS = tty_driver_buf; interrupt(TTY, &keybd_mess); /* send a message to the tty task */ } else { /* Too many characters have been buffered. Discard excess. */ port_out(INT_CTL, ENABLE); /* re-enable 8259A controller */ } } /*===========================================================================* * console * *===========================================================================*/ PRIVATE console(tp) register struct tty_struct *tp; /* tells which terminal is to be used */ { /* Copy as much data as possible to the output queue, then start I/O. On * memory-mapped terminals, such as the IBM console, the I/O will also be * finished, and the counts updated. Keep repeating until all I/O done. */ extern char get_byte(); int count; char c; unsigned segment, offset, offset1; /* Loop over the user bytes one at a time, outputting each one. */ segment = (tp->tty_phys >> 4) & WORD_MASK; offset = tp->tty_phys & OFF_MASK; offset1 = offset; count = 0; while (tp->tty_outleft > 0 && tp->tty_inhibited == RUNNING) { c = get_byte(segment, offset); /* fetch 1 byte from user space */ out_char(tp, c); /* write 1 byte to terminal */ offset++; /* advance one character in user buffer */ tp->tty_outleft--; /* decrement count */ } flush(tp); /* clear out the pending characters */ /* Update terminal data structure. */ count = offset - offset1; /* # characters printed */ tp->tty_phys += count; /* advance physical data pointer */ tp->tty_cum += count; /* number of characters printed */ /* If all data has been copied to the terminal, send the reply. */ if (tp->tty_outleft == 0) finish(tp, tp->tty_cum); } /*===========================================================================* * out_char * *===========================================================================*/ PRIVATE out_char(tp, c) register struct tty_struct *tp; /* pointer to tty struct */ char c; /* character to be output */ { /* Output a character on the console. Check for escape sequences first. */ if (tp->tty_esc_state > 0) { parse_escape(tp, c); return; } switch(c) { case 007: /* ring the bell */ flush(tp); /* print any chars queued for output */ beep(BEEP_FREQ);/* BEEP_FREQ gives bell tone */ return; case 013: /* CTRL-K */ move_to(tp, tp->tty_column, tp->tty_row - 1); return; case 014: /* CTRL-L */ move_to(tp, tp->tty_column + 1, tp->tty_row); return; case 016: /* CTRL-N */ move_to(tp, tp->tty_column + 1, tp->tty_row); return; case '\b': /* backspace */ move_to(tp, tp->tty_column - 1, tp->tty_row); return; case '\n': /* line feed */ if (tp->tty_mode & CRMOD) out_char(tp, '\r'); if (tp->tty_row == SCR_LINES-1) scroll_screen(tp, GO_FORWARD); else tp->tty_row++; move_to(tp, tp->tty_column, tp->tty_row); return; case '\r': /* carriage return */ move_to(tp, 0, tp->tty_row); return; case '\t': /* tab */ if ( (tp->tty_mode & XTABS) == XTABS) { do { out_char(tp, ' '); } while (tp->tty_column & TAB_MASK); return; } /* Ignore tab if XTABS is off--video RAM has no hardware tab */ return; case 033: /* ESC - start of an escape sequence */ flush(tp); /* print any chars queued for output */ tp->tty_esc_state = 1; /* mark ESC as seen */ return; default: /* printable chars are stored in ramqueue */ if (tp->tty_column >= LINE_WIDTH) return; /* long line */ if (tp->tty_rwords == TTY_RAM_WORDS) flush(tp); tp->tty_ramqueue[tp->tty_rwords++]=tp->tty_attribute|(c & BYTE); tp->tty_column++; /* next column */ return; } } /*===========================================================================* * scroll_screen * *===========================================================================*/ PRIVATE scroll_screen(tp, dir) register struct tty_struct *tp; /* pointer to tty struct */ int dir; /* GO_FORWARD or GO_BACKWARD */ { int amount, offset, bytes; bytes = 2 * (SCR_LINES - 1) * LINE_WIDTH; /* 2 * 24 * 80 bytes */ /* Scrolling the screen is a real nuisance due to the various incompatible * video cards. This driver supports hardware scrolling (mono and CGA cards) * and software scrolling (EGA cards). */ if (softscroll) { /* Software scrolling for non-IBM compatible EGA cards. */ if (dir == GO_FORWARD) { scr_up(vid_base); vid_copy(NIL_PTR, vid_base, tp->tty_org+bytes, LINE_WIDTH); } else { scr_down(vid_base); vid_copy(NIL_PTR, vid_base, tp->tty_org, LINE_WIDTH); } } else { /* Normal scrolling using the 6845 registers. */ amount = (dir == GO_FORWARD ? 2 * LINE_WIDTH : -2 * LINE_WIDTH); tp->tty_org = (tp->tty_org + amount) & vid_mask; if (dir == GO_FORWARD) offset = (tp->tty_org + bytes) & vid_mask; else offset = tp->tty_org; /* Blank the new line at top or bottom. */ vid_copy(NIL_PTR, vid_base, offset, LINE_WIDTH); set_6845(VID_ORG, tp->tty_org >> 1); /* 6845 thinks in words */ } } /*===========================================================================* * flush * *===========================================================================*/ PRIVATE flush(tp) register struct tty_struct *tp; /* pointer to tty struct */ { /* Have the characters in 'ramqueue' transferred to the screen. */ if (tp->tty_rwords == 0) return; vid_copy((char *)tp->tty_ramqueue, vid_base, tp->tty_vid, tp->tty_rwords); /* Update the video parameters and cursor. */ tp->tty_vid = (tp->tty_vid + 2 * tp->tty_rwords); set_6845(CURSOR, tp->tty_vid >> 1); /* cursor counts in words */ tp->tty_rwords = 0; } /*===========================================================================* * move_to * *===========================================================================*/ PRIVATE move_to(tp, x, y) struct tty_struct *tp; /* pointer to tty struct */ int x; /* column (0 <= x <= 79) */ int y; /* row (0 <= y <= 24, 0 at top) */ { /* Move the cursor to (x, y). */ flush(tp); /* flush any pending characters */ if (x < 0 || x >= LINE_WIDTH || y < 0 || y >= SCR_LINES) return; tp->tty_column = x; /* set x co-ordinate */ tp->tty_row = y; /* set y co-ordinate */ tp->tty_vid = (tp->tty_org + 2*y*LINE_WIDTH + 2*x); set_6845(CURSOR, tp->tty_vid >> 1); /* cursor counts in words */ } /*===========================================================================* * escape * *===========================================================================*/ PRIVATE parse_escape(tp, c) register struct tty_struct *tp; /* pointer to tty struct */ char c; /* next character in escape sequence */ { /* The following ANSI escape sequences are currently supported: * ESC M to reverse index the screen * ESC [ y ; x H to move cursor to (x, y) [default (1,1)] * ESC [ 0 J to clear from cursor to end of screen * ESC [ n m to set the screen rendition * n: 0 = normal [default] * 7 = reverse */ switch (tp->tty_esc_state) { case 1: /* ESC seen */ tp->tty_esc_intro = '\0'; tp->tty_esc_parmp = tp->tty_esc_parmv; tp->tty_esc_parmv[0] = tp->tty_esc_parmv[1] = 0; switch (c) { case '[': /* Control Sequence Introducer */ tp->tty_esc_intro = c; tp->tty_esc_state = 2; break; case 'M': /* Reverse Index */ do_escape(tp, c); break; default: tp->tty_esc_state = 0; break; } break; case 2: /* ESC [ seen */ if (c >= '0' && c <= '9') { if (tp->tty_esc_parmp < tp->tty_esc_parmv + MAX_ESC_PARMS) *tp->tty_esc_parmp = *tp->tty_esc_parmp * 10 + (c - '0'); break; } else if (c == ';') { if (++tp->tty_esc_parmp < tp->tty_esc_parmv + MAX_ESC_PARMS) *tp->tty_esc_parmp = 0; break; } else { do_escape(tp, c); } break; default: /* illegal state */ tp->tty_esc_state = 0; break; } } /*===========================================================================* * do_escape * *===========================================================================*/ PRIVATE do_escape(tp, c) register struct tty_struct *tp; /* pointer to tty struct */ char c; /* next character in escape sequence */ { int n, ct, vx; /* Handle a sequence beginning with just ESC */ if (tp->tty_esc_intro == '\0') { switch (c) { case 'M': /* Reverse Index */ if (tp->tty_row == 0) scroll_screen(tp, GO_BACKWARD); else tp->tty_row--; move_to(tp, tp->tty_column, tp->tty_row); break; default: break; } } else /* Handle a sequence beginning with ESC [ and parameters */ if (tp->tty_esc_intro == '[') { switch (c) { case 'H': /* Position cursor */ move_to(tp, MAX(1, MIN(LINE_WIDTH, tp->tty_esc_parmv[1])) - 1, MAX(1, MIN(SCR_LINES, tp->tty_esc_parmv[0])) - 1 ); break; case 'J': /* Clear from cursor to end of screen */ if (tp->tty_esc_parmv[0] == 0) { n = 2 * ((SCR_LINES - (tp->tty_row + 1)) * LINE_WIDTH + LINE_WIDTH - (tp->tty_column)); vx = tp->tty_vid; while (n > 0) { ct = MIN(n, vid_retrace); vid_copy(NIL_PTR, vid_base, vx, ct/2); vx += ct; n -= ct; } } break; case 'm': /* Set graphic rendition */ switch (tp->tty_esc_parmv[0]) { case 1: /* BOLD (light green on black) */ tp->tty_attribute = 0x0A << 8; break; case 4: /* UNDERLINE (blue on red) */ tp->tty_attribute = 0x41 << 8; break; case 5: /* BLINKING (light grey on black) */ tp->tty_attribute = 0x87 << 8; break; case 7: /* REVERSE (black on light grey) */ tp->tty_attribute = 0x70 << 8; break; default: tp->tty_attribute = 0007 << 8; break; } break; default: break; } } tp->tty_esc_state = 0; } /*===========================================================================* * set_6845 * *===========================================================================*/ PRIVATE set_6845(reg, val) int reg; /* which register pair to set */ int val; /* 16-bit value to set it to */ { /* Set a register pair inside the 6845. * Registers 10-11 control the format of the cursor (how high it is, etc). * Registers 12-13 tell the 6845 where in video ram to start (in WORDS) * Registers 14-15 tell the 6845 where to put the cursor (in WORDS) * * Note that registers 12-15 work in words, i.e. 0x0000 is the top left * character, but 0x0001 (not 0x0002) is the next character. This addressing * is different from the way the 8088 addresses the video ram, where 0x0002 * is the address of the next character. */ port_out(vid_port + INDEX, reg); /* set the index register */ port_out(vid_port + DATA, (val>>8) & BYTE); /* output high byte */ port_out(vid_port + INDEX, reg + 1); /* again */ port_out(vid_port + DATA, val&BYTE); /* output low byte */ } /*===========================================================================* * beep * *===========================================================================*/ PRIVATE beep(f) int f; /* this value determines beep frequency */ { /* Making a beeping sound on the speaker (output for CRTL-G). The beep is * kept short, because interrupts must be disabled during beeping, and it * is undesirable to keep them off too long. This routine works by turning * on the bits in port B of the 8255 chip that drive the speaker. */ int x, k; lock(); /* disable interrupts */ port_out(TIMER3,0xB6); /* set up timer channel 2 mode */ port_out(TIMER2, f&BYTE); /* load low-order bits of frequency in timer */ port_out(TIMER2,(f>>8)&BYTE); /* now high-order bits of frequency in timer */ port_in(PORT_B,&x); /* acquire status of port B */ port_out(PORT_B, x|3); /* turn bits 0 and 1 on to beep */ for (k = 0; k < B_TIME; k++); /* delay loop while beeper sounding */ port_out(PORT_B, x); /* restore port B the way it was */ unlock(); /* re-enable interrupts */ } /*===========================================================================* * set_leds * *===========================================================================*/ PRIVATE set_leds() { /* Set the LEDs on the caps lock and num lock keys */ int leds, dummy, i; if (pc_at == 0) return; /* PC/XT doesn't have LEDs */ leds = (numlock<<1) | (capslock<<2); /* encode LED bits */ port_out(KEYBD, LED_CODE); /* prepare keyboard to accept LED values */ port_in(KEYBD, &dummy); /* keyboard sends ack; accept it */ for (i = 0; i < LED_DELAY; i++) ; /* delay needed */ port_out(KEYBD, leds); /* give keyboard LED values */ port_in(KEYBD, &dummy); /* keyboard sends ack; accept it */ } /*===========================================================================* * tty_init * *===========================================================================*/ PRIVATE tty_init() { /* Initialize the tty tables. */ register struct tty_struct *tp; /* Tell the EGA card, if any, to simulate a 16K CGA card. */ port_out(EGA + INDEX, 4); /* register select */ port_out(EGA + DATA, 1); /* no extended memory to be used */ for (tp = &tty_struct[0]; tp < &tty_struct[NR_CONS]; tp++) { tp->tty_inhead = tp->tty_inqueue; tp->tty_intail = tp->tty_inqueue; tp->tty_mode = CRMOD | XTABS | ECHO; tp->tty_devstart = console; tp->tty_makebreak = TWO_INTS; tp->tty_attribute = BLANK; tp->tty_erase = ERASE_CHAR; tp->tty_kill = KILL_CHAR; tp->tty_intr = INTR_CHAR; tp->tty_quit = QUIT_CHAR; tp->tty_xon = XON_CHAR; tp->tty_xoff = XOFF_CHAR; tp->tty_eof = EOT_CHAR; } if (color) { vid_base = COLOR_BASE; vid_mask = C_VID_MASK; vid_port = C_6845; vid_retrace = C_RETRACE; } else { vid_base = MONO_BASE; vid_mask = M_VID_MASK; vid_port = M_6845; vid_retrace = M_RETRACE; } tty_driver_buf[1] = MAX_OVERRUN; /* set up limit on keyboard buffering */ set_6845(CUR_SIZE, 31); /* set cursor shape */ set_6845(VID_ORG, 0); /* use page 0 of video ram */ move_to(&tty_struct[0], 0, SCR_LINES-1); /* move cursor to lower left */ /* Determine which keyboard type is attached. The bootstrap program asks * the user to type an '='. The scan codes for '=' differ depending on the * keyboard in use. */ if (scan_code == OLIVETTI_EQUAL) olivetti = TRUE; } /*===========================================================================* * putc * *===========================================================================*/ PUBLIC putc(c) char c; /* character to print */ { /* This procedure is used by the version of printf() that is linked with * the kernel itself. The one in the library sends a message to FS, which is * not what is needed for printing within the kernel. This version just queues * the character and starts the output. */ out_char(&tty_struct[0], c); } /*===========================================================================* * func_key * *===========================================================================*/ PRIVATE func_key(ch) char ch; /* scan code for a function key */ { /* This procedure traps function keys for debugging purposes. When MINIX is * fully debugged, it should be removed. */ if (ch == F1) p_dmp(); /* print process table */ if (ch == F2) map_dmp(); /* print memory map */ if (ch == F3) { /* hardware vs. software scrolling */ softscroll = 1 - softscroll; /* toggle scroll mode */ tty_struct[0].tty_org = 0; move_to(&tty_struct[0], 0, SCR_LINES-1); /* cursor to lower left */ set_6845(VID_ORG, 0); if (softscroll) printf("\033[H\033[JSoftware scrolling enabled.\n"); else printf("\033[H\033[JHardware scrolling enabled.\n"); } if(ch == F5)prlog(); if (ch==F5+1) init_rs232();/*DEBUG*/ #ifdef AM_KERNEL #ifndef NONET if (ch == F4) net_init(); /* re-initialise the ethernet card */ #endif NONET #endif AM_KERNEL if (ch == F9) sigchar(&tty_struct[0], SIGKILL); /* issue SIGKILL */ } #endif /**************************************************************************** **************************************************************************** **************************************************************************** ****************************************************************************/ #ifdef i8088 /* Now begins the code and data for the device RS232 drivers. */ /* Definitions used by the RS232 driver. */ #define RS_BUF_SIZE 256 /* output buffer per serial line */ #define PRIMARY 0x3F8 /* I/O port of primary RS232 */ #define SECONDARY 0x2F8 /* I/O port of secondary RS232 */ /* Constants relating to the 8250. */ #define RS232_INTERRUPT_ID_REG 2 /* address of interrupt id register */ #define RS232_LINE_CONTROL 3 /* address of line control register */ #define RS232_MODEM_CONTROL 4 /* address of modem control register */ #define RS232_LINE_STATUS 5 /* address of line status register */ #define RS232_RATE_DIVISOR 0 /* address of baud rate divisor reg */ #define RS232_TRANSMIT_HOLDING 0 /* address of transmitter holding reg*/ #define RS232_RECEIVER_DATA_REG 0 /* address of receiver data register */ #define RS232_INTERRUPTS 1 /* address of interrupt enable reg */ #define LINE_CONTROLS 0x0B /* odd parity,1 stop bit,8 data bits */ #define MODEM_CONTROLS 0x0B /* RTS & DTR */ #define ADDRESS_DIVISOR 0x80 /* value to address divisor */ #define RS232_INTERRUPT_CLASSES 0x03 /* receiver Data Ready & xmt empty */ #define UART_FREQ 115200L /* UART timer frequency */ /* Line control setting related constants. */ #define ODD 0 #define EVEN 1 #define NONE -1 #define PARITY_ON_OFF 0x08 /* position of parity bit in line reg*/ #define PARITY_TYPE_SHIFT 4 /* shift count for parity_type bit */ #define STOP_BITS_SHIFT 2 /* shift count for # stop_bits */ #define DATA_LEN 8 /* how much to shift sg_mode for len */ /* RS232 interrupt types. */ #define TRANSMITTER_READY 0x02 /* transmitter ready to accept data */ #define RECEIVER_READY 0x04 /* data received interrupt */ #define INT_TYPE_MASK 0x06 /* mask to mask out interrupt type */ #define INT_PENDING 0x01 /* position of interrupt-pending bit */ /* Status register values */ #define DATA_REGISTER_EMPTY 0x20 /* mask to see if data reg is empty */ #define DATA_RECEIVED 0x01 /* mask to see if data has arrived */ /* Global variables used by the RS232 driver */ PUBLIC message rs232_mess; PRIVATE int first_rs_write_int_seen = FALSE; PRIVATE struct rs_struct{ int rs_base; /* 0x3F8 for primary, 0x2F8 secondary*/ int rs_busy; /* line is idle or not */ int rs_left; /* # chars left in buffer to output */ char *rs_next; /* pointer to next char to output */ char rs_buf[RS_BUF_SIZE]; /* output buffer */ } rs_struct[NR_RS_LINES]; /*===========================================================================* * rs232 * *===========================================================================*/ PUBLIC rs232(unit) int unit; /* which unit caused the interrupt */ { int interrupt_type, t; struct rs_struct *rs; /* When an RS232 interrupt occurs, mpx88.s catches it and calls rs232(). * Because more than one interrupt condition can occur at the same * time, the conditions are presented in the interrupt-identification * register in priority order, we have to keep scanning until the * interrupt-pending bit goes down. Only one communications port is really * supported here because the other vector is used by the Ethernet. */ Event(1); /*DEBUG*/ Count(ct_rs232); /*DEBUG*/ rs = &rs_struct[unit - NR_CONS]; do { port_in(rs->rs_base + RS232_INTERRUPT_ID_REG, & interrupt_type); t = interrupt_type & INT_TYPE_MASK; switch(t) { case RECEIVER_READY: /* a character has arrived */ Event(2); /*DEBUG*/ rs_read_int(unit); break; case TRANSMITTER_READY: /* a character has been output */ Event(3); /*DEBUG*/ rs_write_int(unit); break; default: /* something unexpected has happened */ panic("RS232_task got illegal interrupt. Type: ", t); } } while(interrupt_type & INT_PENDING); /* multiple interrupts are possible */ Event(4); /*DEBUG*/ } /*===========================================================================* * rs_read_int * *===========================================================================*/ PRIVATE rs_read_int(line) int line; { int val, k, base; Event(5); /*DEBUG*/ Count(ct_rd); /*DEBUG*/ base = rs_struct[line - NR_CONS].rs_base; /* Fetch the character from the RS232 hardware. */ port_in(base + RS232_RECEIVER_DATA_REG, &val); Event(6); /*DEBUG*/ /* Store the character in memory so the task can get at it later */ if ((k = tty_driver_buf[0]) < tty_driver_buf[1]) { /* There is room to store this character, do it */ k = k + k; /* each entry contains two bytes */ tty_driver_buf[k + 2] = val; /* store the ascii code */ tty_driver_buf[k + 3] = line; /* tell wich line it came from */ tty_driver_buf[0]++; /* increment counter */ /* Build and send the interrupt message */ rs232_mess.m_type = TTY_CHAR_INT; rs232_mess.ADDRESS = tty_driver_buf; interrupt(TTY, &rs232_mess); /* send a message to the tty task */ } else { /* Too many character have been buffered. Discard excess */ port_out(INT_CTL, ENABLE); /* re-enable 8259A controller */ } Event(7); /*DEBUG*/ } /*===========================================================================* * rs_write_int * *===========================================================================*/ PRIVATE rs_write_int(line) int line; { /* An output ready interrupt has occurred, or a write has been done to an idle * line. In both cases, start the output. */ char byte; struct rs_struct *rs; Event(8); /*DEBUG*/ Count(ct_wt); /*DEBUG*/ if (!first_rs_write_int_seen) { first_rs_write_int_seen = TRUE; port_out(INT_CTL, ENABLE); /* re-enable 8259A controller */ return; } Event(9); /*DEBUG*/ rs = &rs_struct[line-NR_CONS]; /* If there is more output queued, output the next character. */ if (rs->rs_left > 0) { byte = *rs->rs_next; putc(byte); /* DEBUG print to screen!!!!! */ Event(24); /*DEBUG*/ write_byte(rs, byte); /* Write next char to UART */ Event(25); /*DEBUG*/ rs->rs_next++; rs->rs_left--; /* One char done */ port_out(INT_CTL, ENABLE); /* re-enable 8259A controller */ return; } Event(10); /*DEBUG*/ /* The current output buffer is finished. Send a message to the tty task. */ rs->rs_busy = FALSE; rs->rs_next = &rs->rs_buf[0]; rs->rs_left = 0; rs232_mess.m_type = TTY_O_DONE; /* build the message */ rs232_mess.TTY_LINE = line; /* which line is finished */ interrupt(TTY, &rs232_mess); /* send the message to the tty task */ Event(11); /*DEBUG*/ } /*===========================================================================* * write_byte * *===========================================================================*/ PRIVATE write_byte(rs, byte) struct rs_struct *rs; /* which tty */ char byte; /* byte to write */ { Event(12); /*DEBUG*/ Count(ct_wb); /*DEBUG*/ port_out(rs->rs_base + RS232_TRANSMIT_HOLDING, (int) byte); } /*===========================================================================* * serial_out * *===========================================================================*/ PRIVATE serial_out(tp) register struct tty_struct *tp; /* tells which terminal is to be used */ { /* Copy as much data as possible to the output queue, then start I/O if * necessary. */ int room, bytes, line; phys_bytes tty_phys; struct rs_struct *rs; Event(13); /*DEBUG*/ Count(ct_ser); /*DEBUG*/ if (tp->tty_inhibited != RUNNING) return; line = tp - &tty_struct[0]; /* line is index into tty_struct */ rs = &rs_struct[line - NR_CONS]; /* 0 to NR_CONS - 1 are consoles */ /* Compute how many bytes can be copied to rs_buf and copy them. */ lock(); room = &rs->rs_buf[RS_BUF_SIZE] - (rs->rs_next + rs->rs_left); bytes = MIN(room, tp->tty_outleft); /* # bytes to copy */ tty_phys = umap(proc_addr(TTY), D, (vir_bytes) rs->rs_next + rs->rs_left, 1); phys_copy(tp->tty_phys, tty_phys, (long) bytes); rs->rs_left += bytes; /* more bytes are now in rs_buf */ tp->tty_cum += bytes; /* update cumulative total */ tp->tty_phys += bytes; /* next time, take different bytes */ tp->tty_outleft -= bytes; /* fewer bytes still pending */ unlock(); Event(14); /*DEBUG*/ Count(ct_ser1);/*DEBUG*/ if (!rs->rs_busy) { /* is the line idle? */ rs->rs_busy = TRUE; /* if so, mark it as busy */ rs_write_int(line); /* and start it going */ } Event(23); /*DEBUG*/ Count(ct_ser2); /*DEBUG*/ } /*===========================================================================* * do_tty_o_done * *===========================================================================*/ PRIVATE do_tty_o_done(tp, m_ptr) register struct tty_struct *tp; /* pointer to tty_struct */ message *m_ptr; /* pointer to message sent to task */ { /* The current block of characters has been output. See if there are more. */ int count, replyee, caller, line; register struct rs_struct *rs; Event(15); /*DEBUG*/ Count(ct_o_done); /*DEBUG*/ /* Mark rs_buf as empty */ lock(); line = tp - tty_struct; rs = &rs_struct[line - NR_CONS]; rs->rs_left = 0; rs->rs_busy = FALSE; rs->rs_next = &rs->rs_buf[0]; unlock(); /* See if more characters are pending. */ if (tp->tty_outleft > 0) { /* The output is not yet completed. Transmit another chunk. */ serial_out(tp); return; } Event(16); /*DEBUG*/ /* There is nothing more to send. Return status. */ tp->tty_rwords = 0; count = tp->tty_cum; /* Tell hanging writer that chars have been sent */ replyee = (int) tp->tty_otcaller; caller = (int) tp->tty_outproc; tty_reply(REVIVE, replyee, caller, count, 0L, 0L); Event(17); /*DEBUG*/ } /*===========================================================================* * rs_out_char * *===========================================================================*/ PRIVATE rs_out_char(tp, c) register struct tty_struct *tp; /* pointer to tty struct */ char c; /* character to be output */ { /* Output a character on an RS232 line. */ int line; register struct rs_struct *rs; /* See if there is room to store a character, and if so, do it. */ Event(18); /*DEBUG*/ Count(ct_out_char); /*DEBUG*/ lock(); line = tp - tty_struct; rs = &rs_struct[line - NR_CONS]; if (rs->rs_next == &rs->rs_buf[RS_BUF_SIZE]) return; /* full, tough luck */ *rs->rs_next++ = c; /* store the character */ rs->rs_left++; unlock(); if (!rs->rs_busy) { /* if terminal line is idle, start it */ rs->rs_busy = TRUE; rs_write_int(line); } Event(19); /*DEBUG*/ } /*===========================================================================* * init_rs232 * *===========================================================================*/ PRIVATE init_rs232() { register struct tty_struct *tp; register struct rs_struct *rs; int line; Event(20); /*DEBUG*/ for (tp = &tty_struct[NR_CONS]; tp < &tty_struct[NR_CONS+NR_RS_LINES]; tp++){ tp->tty_inhead = tp->tty_inqueue; tp->tty_intail = tp->tty_inqueue; tp->tty_mode = CRMOD | XTABS /* | ECHO */; tp->tty_devstart = serial_out; tp->tty_erase = ERASE_CHAR; tp->tty_kill = KILL_CHAR; tp->tty_intr = INTR_CHAR; tp->tty_quit = QUIT_CHAR; tp->tty_xon = XON_CHAR; tp->tty_xoff = XOFF_CHAR; tp->tty_eof = EOT_CHAR; tp->tty_makebreak = ONE_INT; /* RS232 only interrupts once/char */ } rs_struct[0].rs_base = PRIMARY; rs_struct[1].rs_base = SECONDARY; for (rs = &rs_struct[0]; rs < &rs_struct[NR_RS_LINES]; rs++) { line = rs - rs_struct; rs->rs_next = & rs->rs_buf[0]; rs->rs_left = 0; rs->rs_busy = FALSE; config_rs232(line, 1200, EVEN, 1, 7); /* set line ctl and baudrate */ port_out(rs->rs_base + RS232_MODEM_CONTROL, MODEM_CONTROLS); port_out(rs->rs_base + RS232_INTERRUPTS, RS232_INTERRUPT_CLASSES); } } /*===========================================================================* * set_uart * *===========================================================================*/ PRIVATE set_uart(line, mode, speeds) int line; /* which line number (>= NR_CONS) */ int mode; /* sgtty.h sg_mode word */ int speeds; /* low byte is input speed, next is output */ { /* Set the UART parameters. */ int baudrate, parity, stop_bits, data_bits; Event(21); /*DEBUG*/ baudrate = 100 * (speeds & BYTE); if (baudrate == 100) baudrate = 110; parity = NONE; if (mode & ODDP) parity = ODD; if (mode & EVENP) parity = EVEN; stop_bits = (baudrate == 110 ? 2 : 1); data_bits = 5 + ((mode >> DATA_LEN) & 03); config_rs232(line, baudrate, parity, stop_bits, data_bits); } /*===========================================================================* * config_rs232 * *===========================================================================*/ PRIVATE config_rs232(line, baud_rate, parity, stop_bits, data_bits) int line, baud_rate, parity, stop_bits, data_bits; { /* Set various line control parameters for RS232 I/O. * Parity may be any of ODD, EVEN or NONE. * StopBits may be 1 or 2. * DataBits may be 5 to 8. * * If DataBits == 5 and StopBits == 2, UART will generate 1.5 stop bits */ int line_controls = 0, base, freq; Event(22); /*DEBUG*/ base = rs_struct[line - NR_CONS].rs_base; /* First tell line control register to address baud rate divisor */ port_out(base + RS232_LINE_CONTROL, ADDRESS_DIVISOR); /* Now set the baud rate. */ freq = (int) (UART_FREQ / baud_rate); port_out(base + RS232_RATE_DIVISOR, freq & BYTE); port_out(base + RS232_RATE_DIVISOR+1, (freq >> 8) & BYTE); /* Put parity_type bits in line_controls */ if (parity != NONE) { line_controls |= PARITY_ON_OFF; line_controls |= (parity << PARITY_TYPE_SHIFT); } /* Put #stop_bits bits in line_controls */ if (stop_bits == 1 || stop_bits == 2) line_controls |= (stop_bits - 1) << STOP_BITS_SHIFT; /* Put #data_bits bits in line_controls */ if (data_bits >=5 && data_bits <= 8) line_controls |= (data_bits - 5); port_out(base + RS232_LINE_CONTROL, line_controls); } #endif /*________________________________________________________________________*/ Event(x) { log[logpt++] = x; if (logpt >= LOGSIZE) logpt = 0; if (rs_struct[0].rs_base > 0x3F8){ printf("Event %d hit\n",x); prlog(); while(1); } } prlog() { int i, j; i = logpt; j = 0; printf("\n\nEvents: "); while(j < LOGSIZE) { printf("%d ", log[i]); i++; if (i == LOGSIZE) i=0; j++;} printf("\n\nCounts: rs232=%u serial_out=%u ",ct_rs232, ct_ser); printf("(serial1=%u serial2=%u)\n", ct_ser1, ct_ser2); printf(" rs_read_int=%u rs_write_int=%u write_byte=%u",ct_rd,ct_wt,ct_wb); printf(" do_tty_o_done=%u rs_out_char=%u\n", ct_o_done, ct_out_char); printf("\nrs_struct[0]. rs_base=%x rs_busy=%d rs_left=%d rs_next=%d\n", rs_struct[0].rs_base, rs_struct[0].rs_busy, rs_struct[0].rs_left, rs_struct[0].rs_next - &rs_struct[0].rs_buf[0]); printf("rs_buf = %o %o %o %o %o\n", rs_struct[0].rs_buf[0], rs_struct[0].rs_buf[1], rs_struct[0].rs_buf[2],rs_struct[0].rs_buf[3], rs_struct[0].rs_buf[4]); }