/* ** Copyright (C) 1991, 1997 Free Software Foundation, Inc. ** ** This file is part of TACK. ** ** TACK is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2, or (at your option) ** any later version. ** ** TACK is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with TACK; see the file COPYING. If not, write to ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ** Boston, MA 02111-1307, USA. */ #include #include MODULE_ID("$Id: sync.c,v 1.3 2001/06/16 17:55:48 tom Exp $") /* terminal-synchronization and performance tests */ static void sync_home(struct test_list *, int *, int *); static void sync_lines(struct test_list *, int *, int *); static void sync_clear(struct test_list *, int *, int *); static void sync_summary(struct test_list *, int *, int *); struct test_list sync_test_list[] = { {MENU_NEXT, 0, 0, 0, "b) baud rate test", sync_home, 0}, {MENU_NEXT, 0, 0, 0, "l) scroll performance", sync_lines, 0}, {MENU_NEXT, 0, 0, 0, "c) clear screen performance", sync_clear, 0}, {MENU_NEXT, 0, 0, 0, "p) summary of results", sync_summary, 0}, {0, 0, 0, 0, txt_longer_test_time, longer_test_time, 0}, {0, 0, 0, 0, txt_shorter_test_time, shorter_test_time, 0}, {MENU_LAST, 0, 0, 0, 0, 0, 0} }; struct test_menu sync_menu = { 0, 'n', 0, "Performance tests", "perf", "n) run standard tests", sync_test, sync_test_list, 0, 0, 0 }; int tty_can_sync; /* TRUE if tty_sync_error() returned FALSE */ int tty_newline_rate; /* The number of newlines per second */ int tty_clear_rate; /* The number of clear-screens per second */ int tty_cps; /* The number of characters per second */ #define TTY_ACK_SIZE 64 int ACK_terminator; /* terminating ACK character */ int ACK_length; /* length of ACK string */ const char *tty_ENQ; /* enquire string */ char tty_ACK[TTY_ACK_SIZE]; /* ACK response, set by tty_sync_error() */ /***************************************************************************** * * Terminal synchronization. * * These functions handle the messy business of enq-ack handshaking * for timing purposes. * *****************************************************************************/ int tty_sync_error(void) { int ch, trouble, ack; trouble = FALSE; for (;;) { tt_putp(tty_ENQ); /* send ENQ */ ch = getnext(STRIP_PARITY); event_start(TIME_SYNC); /* start the timer */ /* The timer doesn't start until we get the first character. After that I expect to get the remaining characters of the acknowledge string in a short period of time. If that is not true then these characters are coming from the user and we need to send the ENQ sequence out again. */ for (ack = 0; ; ) { if (ack < TTY_ACK_SIZE - 2) { tty_ACK[ack] = ch; tty_ACK[ack + 1] = '\0'; } if (ch == ACK_terminator) { return trouble; } if (++ack >= ACK_length) { return trouble; } ch = getnext(STRIP_PARITY); if (event_time(TIME_SYNC) > 400000) { break; } } set_attr(0); /* just in case */ put_crlf(); if (trouble) { /* The terminal won't sync. Life is not good. */ return TRUE; } put_str(" -- sync -- "); trouble = TRUE; } } /* ** flush_input() ** ** Throw away any output. */ void flush_input(void) { if (tty_can_sync == SYNC_TESTED && ACK_terminator >= 0) { (void) tty_sync_error(); } else { spin_flush(); } } /* ** probe_enq_ok() ** ** does the terminal do enq/ack handshaking? */ static void probe_enq_ok(void) { int tc, len, ulen; put_str("Testing ENQ/ACK, standby..."); fflush(stdout); can_test("u8 u9", FLAG_TESTED); #ifdef user9 tty_ENQ = user9 ? user9 : "\005"; #else tty_ENQ = "\005"; #endif tc_putp(tty_ENQ); event_start(TIME_SYNC); /* start the timer */ read_key(tty_ACK, TTY_ACK_SIZE - 1); if (event_time(TIME_SYNC) > 400000 || tty_ACK[0] == '\0') { /* These characters came from the user. Sigh. */ tty_can_sync = SYNC_FAILED; ptext("\nThis program expects the ENQ sequence to be"); ptext(" answered with the ACK character. This will help"); ptext(" the program reestablish synchronization when"); ptextln(" the terminal is overrun with data."); ptext("\nENQ sequence from (u9): "); putln(expand(tty_ENQ)); ptext("ACK received: "); putln(expand(tty_ACK)); #ifdef user8 len = user8 ? strlen(user8) : 0; #else len = 0; #endif sprintf(temp, "Length of ACK %d. Expected length of ACK %d.", (int) strlen(tty_ACK), len); ptextln(temp); #ifdef user8 if (len) { temp[0] = user8[len - 1]; temp[1] = '\0'; ptext("Terminating character found in (u8): "); putln(expand(temp)); } #endif return; } tty_can_sync = SYNC_TESTED; if ((len = strlen(tty_ACK)) == 1) { /* single character acknowledge string */ ACK_terminator = tty_ACK[0]; ACK_length = 4096; return; } tc = tty_ACK[len - 1]; #ifdef user8 if (user8) { ulen = strlen(user8); if (tc == user8[ulen - 1]) { /* ANSI style acknowledge string */ ACK_terminator = tc; ACK_length = 4096; return; } } #endif /* fixed length acknowledge string */ ACK_length = len; ACK_terminator = -2; } /* ** verify_time() ** ** verify that the time tests are ready to run. ** If the baud rate is not set then compute it. */ void verify_time(void) { int status, ch; if (tty_can_sync == SYNC_FAILED) { return; } probe_enq_ok(); put_crlf(); if (tty_can_sync == SYNC_TESTED) { put_crlf(); if (ACK_terminator >= 0) { ptext("ACK terminating character: "); temp[0] = ACK_terminator; temp[1] = '\0'; ptextln(expand(temp)); } else { sprintf(temp, "Fixed length ACK, %d characters", ACK_length); ptextln(temp); } } if (tty_baud_rate == 0) { sync_home(&sync_test_list[0], &status, &ch); } } /***************************************************************************** * * Terminal performance tests * * Find out how fast the terminal can: * 1) accept characters * 2) scroll the screen * 3) clear the screen * *****************************************************************************/ /* ** sync_home(test_list, status, ch) ** ** Baudrate test */ void sync_home( struct test_list *t, int *state, int *ch) { int j, k; unsigned long rate; if (!cursor_home && !cursor_address && !row_address) { ptext("Terminal can not home cursor. "); generic_done_message(t, state, ch); return; } if (skip_pad_test(t, state, ch, "(home) Start baudrate search")) { return; } pad_test_startup(1); do { go_home(); for (j = 1; j < lines; j++) { for (k = 0; k < columns; k++) { if (k & 0xF) { put_this(letter); } else { put_this('.'); } } SLOW_TERMINAL_EXIT; } NEXT_LETTER; } while(still_testing()); pad_test_shutdown(t, auto_right_margin == 0); /* note: tty_frame_size is the real framesize times two. This takes care of half bits. */ rate = (tx_cps * tty_frame_size) >> 1; if (rate > tty_baud_rate) { tty_baud_rate = rate; } if (tx_cps > tty_cps) { tty_cps = tx_cps; } sprintf(temp, "%d characters per second. Baudrate %d ", tx_cps, j); ptext(temp); generic_done_message(t, state, ch); } /* ** sync_lines(test_list, status, ch) ** ** How many newlines/second? */ static void sync_lines( struct test_list *t, int *state, int *ch) { int j; if (skip_pad_test(t, state, ch, "(nel) Start scroll performance test")) { return; } pad_test_startup(0); repeats = 100; do { sprintf(temp, "%d", test_complete); put_str(temp); put_newlines(repeats); } while(still_testing()); pad_test_shutdown(t, 0); j = sliding_scale(tx_count[0], 1000000, usec_run_time); if (j > tty_newline_rate) { tty_newline_rate = j; } sprintf(temp, "%d linefeeds per second. ", j); ptext(temp); generic_done_message(t, state, ch); } /* ** sync_clear(test_list, status, ch) ** ** How many clear-screens/second? */ static void sync_clear( struct test_list *t, int *state, int *ch) { int j; if (!clear_screen) { ptext("Terminal can not clear-screen. "); generic_done_message(t, state, ch); return; } if (skip_pad_test(t, state, ch, "(clear) Start clear-screen performance test")) { return; } pad_test_startup(0); repeats = 20; do { sprintf(temp, "%d", test_complete); put_str(temp); for (j = 0; j < repeats; j++) { put_clear(); } } while(still_testing()); pad_test_shutdown(t, 0); j = sliding_scale(tx_count[0], 1000000, usec_run_time); if (j > tty_clear_rate) { tty_clear_rate = j; } sprintf(temp, "%d clear-screens per second. ", j); ptext(temp); generic_done_message(t, state, ch); } /* ** sync_summary(test_list, status, ch) ** ** Print out the test results. */ static void sync_summary( struct test_list *t, int *state, int *ch) { char size[32]; put_crlf(); ptextln("Terminal size characters/sec linefeeds/sec clears/sec"); sprintf(size, "%dx%d", columns, lines); sprintf(temp, "%-10s%-11s%11d %11d %11d", tty_basename, size, tty_cps, tty_newline_rate, tty_clear_rate); ptextln(temp); generic_done_message(t, state, ch); } /* ** sync_test(menu) ** ** Run at the beginning of the pad tests and function key tests */ void sync_test( struct test_menu *menu) { control_init(); if (tty_can_sync == SYNC_NOT_TESTED) { verify_time(); } if (menu->menu_title) { put_crlf(); ptextln(menu->menu_title); } } /* ** sync_handshake(test_list, status, ch) ** ** Test or retest the ENQ/ACK handshake */ void sync_handshake( struct test_list *t GCC_UNUSED, int *state GCC_UNUSED, int *ch GCC_UNUSED) { tty_can_sync = SYNC_NOT_TESTED; verify_time(); }