Mercurial > ecos
diff packages/io/serial/current/tests/ser_test_protocol.inl @ 4:1d7f19c9e4d1 ecos-sw-1999-05-11
Merge from eCos master repository on 1999-05-11-21:11:10-BST
| author | jlarmour |
|---|---|
| date | Tue, 11 May 1999 14:07:25 +0000 |
| parents | 443894e2e912 |
| children | d376b777e2ce |
line wrap: on
line diff
--- a/packages/io/serial/current/tests/ser_test_protocol.inl +++ b/packages/io/serial/current/tests/ser_test_protocol.inl @@ -56,8 +56,16 @@ #include <cyg/hal/hal_intr.h> // for reclaiming interrup vector +#ifdef CYG_HAL_SPARCLITE_SLEB +#include <pkgconf/hal_sparclite_sleb.h> // CYG_KERNEL_DIAG_GDB_SERIAL_DIRECT +#endif + //---------------------------------------------------------------------------- // Definition of which device to run tests on on various platforms. + +#define NA_MSG "No test device specified" + + #if defined(CYGPKG_HAL_POWERPC_COGENT) \ && defined(CYGPKG_IO_SERIAL_POWERPC_COGENT) \ && defined(CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_B) @@ -92,24 +100,29 @@ #endif #if defined(CYGPKG_HAL_MN10300_STDEVAL1) \ && defined(CYGPKG_IO_SERIAL_MN10300) \ - && defined(CYGPKG_IO_SERIAL_MN10300_SERIAL1) -// Note: Serial1 is *not* the same port as GDB is using. It seems that -// CygMon is interfering with the tests if run on the same port. -// This configuration allows the serial driver to be tested using the -// filter in stand alone mode (option -n). -# define TEST_SER_DEV CYGDAT_IO_SERIAL_MN10300_SERIAL1_NAME -# if defined(CYGPKG_IO_SERIAL_TTY_TTY1) + && defined(CYGPKG_IO_SERIAL_MN10300_SERIAL2) +# define TEST_SER_DEV CYGDAT_IO_SERIAL_MN10300_SERIAL2_NAME +# if defined(CYGPKG_IO_SERIAL_TTY_TTY2) # define TEST_TTY_DEV CYGDAT_IO_SERIAL_TTY_TTY1_DEV # endif #endif #if defined(CYGPKG_HAL_SPARCLITE_SLEB) \ && defined(CYGPKG_IO_SERIAL_SPARCLITE_SLEB) \ && defined(CYGPKG_IO_SERIAL_SPARCLITE_SLEB_CON1) -# define TEST_SER_DEV CYGDAT_IO_SERIAL_SPARCLITE_SLEB_CON1_NAME -# define SER_OVERRIDE_INT_1 CYGNUM_HAL_INTERRUPT_9 -# define SER_OVERRIDE_INT_2 CYGNUM_HAL_INTERRUPT_10 -# if defined(CYGPKG_IO_SERIAL_TTY_TTY0) -# define TEST_TTY_DEV CYGDAT_IO_SERIAL_TTY_TTY0_DEV +# undef NA_MSG +# define NA_MSG "CYG_KERNEL_DIAG_GDB_SERIAL_DIRECT is unset" +# if !defined(CYG_HAL_STARTUP_RAM) \ + || defined(CYG_KERNEL_DIAG_GDB_SERIAL_DIRECT) +# define TEST_SER_DEV CYGDAT_IO_SERIAL_SPARCLITE_SLEB_CON1_NAME +// The interrupt vectors are normally in CygMon's control. Steal them back, +// but beware that this results in GDB acknowledge characters showing up in +// the serial driver if CygMon is used for hal_diag output. +// Set CYG_KERNEL_DIAG_GDB_SERIAL_DIRECT in hal_sparclite_sleb as a workaround. +# define SER_OVERRIDE_INT_1 CYGNUM_HAL_INTERRUPT_9 +# define SER_OVERRIDE_INT_2 CYGNUM_HAL_INTERRUPT_10 +# if defined(CYGPKG_IO_SERIAL_TTY_TTY0) +# define TEST_TTY_DEV CYGDAT_IO_SERIAL_TTY_TTY0_DEV +# endif # endif #endif @@ -165,12 +178,21 @@ typedef struct ser_cfg { // A few predefined configurations. These must all be valid for any // given target until change_config is behaving correctly. cyg_ser_cfg_t test_configs[] = { -#if !defined(CYGPKG_HAL_TX39_JMR3904) && \ - !defined(CYGPKG_HAL_ARM_PID) && \ - !defined(CYGPKG_HAL_ARM_AEB) && \ - !defined(CYGPKG_HAL_MN10300_STDEVAL1) && \ - !defined(CYGPKG_HAL_SPARCLITE_SLEB) - { CYGNUM_SERIAL_BAUD_115200, CYGNUM_SERIAL_WORD_LENGTH_8, + +#if !defined(CYGPKG_HAL_TX39_JMR3904) && !defined(CYGPKG_HAL_ARM_PID) + { CYGNUM_SERIAL_BAUD_9600, CYGNUM_SERIAL_WORD_LENGTH_8, + CYGNUM_SERIAL_STOP_1, CYGNUM_SERIAL_PARITY_NONE }, +#endif + + { CYGNUM_SERIAL_BAUD_14400, CYGNUM_SERIAL_WORD_LENGTH_8, + CYGNUM_SERIAL_STOP_1, CYGNUM_SERIAL_PARITY_NONE }, + + { CYGNUM_SERIAL_BAUD_19200, CYGNUM_SERIAL_WORD_LENGTH_8, + CYGNUM_SERIAL_STOP_1, CYGNUM_SERIAL_PARITY_NONE }, + +#if !defined(CYGPKG_HAL_SPARCLITE_SLEB) && \ + !defined(CYGPKG_HAL_ARM_AEB) + { CYGNUM_SERIAL_BAUD_38400, CYGNUM_SERIAL_WORD_LENGTH_8, CYGNUM_SERIAL_STOP_1, CYGNUM_SERIAL_PARITY_NONE }, #endif @@ -181,14 +203,12 @@ cyg_ser_cfg_t test_configs[] = { CYGNUM_SERIAL_STOP_1, CYGNUM_SERIAL_PARITY_NONE }, #endif - { CYGNUM_SERIAL_BAUD_38400, CYGNUM_SERIAL_WORD_LENGTH_8, - CYGNUM_SERIAL_STOP_1, CYGNUM_SERIAL_PARITY_NONE }, - - { CYGNUM_SERIAL_BAUD_19200, CYGNUM_SERIAL_WORD_LENGTH_8, - CYGNUM_SERIAL_STOP_1, CYGNUM_SERIAL_PARITY_NONE }, - -#if !defined(CYGPKG_HAL_TX39_JMR3904) && !defined(CYGPKG_HAL_ARM_PID) - { CYGNUM_SERIAL_BAUD_9600, CYGNUM_SERIAL_WORD_LENGTH_8, +#if !defined(CYGPKG_HAL_TX39_JMR3904) && \ + !defined(CYGPKG_HAL_ARM_PID) && \ + !defined(CYGPKG_HAL_ARM_AEB) && \ + !defined(CYGPKG_HAL_MN10300_STDEVAL1) && \ + !defined(CYGPKG_HAL_SPARCLITE_SLEB) + { CYGNUM_SERIAL_BAUD_115200, CYGNUM_SERIAL_WORD_LENGTH_8, CYGNUM_SERIAL_STOP_1, CYGNUM_SERIAL_PARITY_NONE }, #endif @@ -252,7 +272,8 @@ do { //---------------------------------------------------------------------------- // Macros for read/write to serial with error cheking. -cyg_uint32 r_stamp; +static volatile cyg_uint32 r_stamp; +static volatile int aborted; // This routine will be called if the read "times out" static void @@ -261,6 +282,7 @@ do_abort(void *handle) cyg_io_handle_t io_handle = (cyg_io_handle_t)handle; cyg_int32 len = 1; // Need something here cyg_io_get_config(io_handle, CYG_IO_GET_CONFIG_SERIAL_ABORT, 0, &len); + aborted = 1; } #include "timeout.inl" @@ -286,6 +308,7 @@ do_abort(void *handle) int __res; \ cyg_uint32 __len = 1; \ __res = cyg_io_write((__h), (__d), (__l)); \ + if (ENOERR != __res) diag_printf("[%d]\n", __res); \ CYG_TEST_CHECK(ENOERR == __res, "cyg_io_write failed"); \ __res = cyg_io_get_config((__h), \ CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN, \ @@ -344,27 +367,45 @@ hang(void) //----------------------------------------------------------------------------- // Configuration changing function. -// FIXME: This is still slightly bogus in that it doesn't check for target -// capabilities. Correct way is; -// o try setting new cfg -// o restore -// o if OK: -// o send cfg to target -// o if reply OK: -// o change to new cfg +// +// First change to the new config and back again to determine if the driver +// can handle the config. +// If not, return error. +// +// Then query the host for its capability to use the config: +// Format out: +// "@CONFIG:<baud rate code>:<#data bits>:<#stop bits>:<parity on/off>!" +// Format in: +// OK/ER +// +// On ER, return error. // -// Host&protocol currently only supports: -// - no/even parity +// On OK, change to the new configuration. Resynchronize with the host: +// Write 'T'-chars to the host. +// The host will echo anything it reads until it sees a 'O' character. +// Continue until four 'T'-chars has been read back. +// Then send a single 'O'-character. This will signal success to the host. +// +// If the synchronization has not succeeded within 100 ticks +// (configurable in the protocol), both host and target will revert to +// the previous configuration and attempt to synchronize again. If +// this fails, this call will hang and the host will consider the test +// a failure. +// +// To Do: +// Host&protocol currently only supports: +// - no/even parity int change_config(cyg_io_handle_t handle, cyg_ser_cfg_t* cfg) { + cyg_serial_info_t old_cfg, new_cfg; const char cmd[] = "@CONFIG:"; char reply[2]; int msglen; - int res; + int res, len; cyg_uint8 *p1; - - // Prepare and send the command. + + // Prepare the command. p1 = &cmd_buffer[0]; p1 = strcpy(p1, &cmd[0]); p1 = itoa(p1, cfg->baud_rate); @@ -375,60 +416,179 @@ change_config(cyg_io_handle_t handle, cy *p1++ = ':'; p1 = itoa(p1, cfg->parity); *p1++ = '!'; - *p1++ = 0; + *p1 = 0; // note: we may append to this later + // Tell user what we're up to. CYG_TEST_INFO(&cmd_buffer[1]); + // Change to new config and then back to determine if the driver likes it. + len = sizeof(old_cfg); + res = cyg_io_get_config(handle, CYG_IO_GET_CONFIG_SERIAL_INFO, + &old_cfg, &len); + res = cyg_io_get_config(handle, CYG_IO_GET_CONFIG_SERIAL_INFO, + &new_cfg, &len); + if (res != ENOERR) { + diag_printf("Can't get serial config - DEVIO error: %d\n", res); + hang(); + } + + new_cfg.baud = cfg->baud_rate; + new_cfg.word_length = cfg->data_bits; + new_cfg.stop = cfg->stop_bits; + new_cfg.parity = cfg->parity; + + res = cyg_io_set_config(handle, CYG_IO_SET_CONFIG_SERIAL_INFO, + &new_cfg, &len); + + // Driver didn't like it. It will not have changed anything, so it's + // safe to return now. + if (ENOERR != res) { + // Let user know that the config was skipped due to the target. + const char txt_tskipped[] = "- skipped by target!"; + p1 = strcpy(p1, txt_tskipped); + *p1 = 0; + CYG_TEST_INFO(&cmd_buffer[1]); + return res; + } + + // Succeeded. Change back to the original config so we can communicate + // with the host. + res = cyg_io_set_config(handle, CYG_IO_SET_CONFIG_SERIAL_INFO, + &old_cfg, &len); + if (ENOERR != res) { + diag_printf("change_config: set_config failed/1 (%d)\n", res); + hang(); + } + + // Send command to host and read host's reply. msglen = strlen(&cmd_buffer[0]); Tcyg_io_write(handle, &cmd_buffer[0], &msglen); - msglen = 2; Tcyg_io_read(handle, &reply[0], &msglen); - if (reply[0] == 'O') { - // Change config - cyg_serial_info_t serial_info; - int len = sizeof(serial_info); - res = cyg_io_get_config(handle, CYG_IO_GET_CONFIG_SERIAL_INFO, &serial_info, &len); - if (res != ENOERR) { - diag_printf("Can't get serial config - DEVIO error: %d\n", res); - hang(); - } + // Did host accept configuration? + if ('O' != reply[0] || 'K' != reply[1]) { + // Let user know that the config was skipped due to the host. + const char txt_hskipped[] = "- skipped by host!"; + p1 = strcpy(p1, txt_hskipped); + *p1 = 0; + CYG_TEST_INFO(&cmd_buffer[1]); + diag_printf("Host didn't accept config (%02x, %02x).\n", + reply[0], reply[1]); + + res = ENOSUPP; + return res; + } + + // Now test the new configuration: Loop until we read what we + // write. This may hang (as seen from the host), but only when we + // get totally lost, in which case there's not much else to do + // really. In this case the host will consider the test a FAIL. + len = sizeof(new_cfg); + res = cyg_io_set_config(handle, CYG_IO_SET_CONFIG_SERIAL_INFO, + &new_cfg, &len); + if (ENOERR != res) { + diag_printf("change_config: set_config failed/2 (%d)\n", res); + hang(); + } + + // Note: Only sends a single char - a string wuld be safer, but if + // there's not a 1-1 relationship between outgoing and incoming + // chars, the reader needs to be smarter. Compensate by + // requirering the char to be read 4 times in succession. + for (;;) { + int change_succeeded = 0; + int using_old_config = 0; + int matches = 0; + char in_buf[1]; + int len; + char out_buf[1]; + + out_buf[0] = 'a'; - serial_info.baud = cfg->baud_rate; - serial_info.word_length = cfg->data_bits; - serial_info.stop = cfg->stop_bits; - serial_info.parity = cfg->parity; + aborted = 0; // global abort flag + // FIXME: Timeout time needs to be configurable, and needs to + // be sent to the host before getting here. That would allow + // changing the timeout by just rebuilding the test - without + // changing the host software. + r_stamp = timeout(100, do_abort, handle); + while (!aborted) { + len = 1; + res = cyg_io_write(handle, out_buf, &len); + if (ENOERR != res && -EINTR != res) { + // We may have to reset the driver here if the fail + // was due to a framing or parity error. + break; + } + + len = 1; + res = cyg_io_read(handle, in_buf, &len); + if (ENOERR != res && -EINTR != res) { + // We may have to reset the driver here if the fail + // was due to a framing or parity error. + break; + } + + // Check for match. + if (out_buf[0] == in_buf[0]) { + matches++; + // We want 4 in succession before we accept it. + if (4 == matches) { + change_succeeded = 1; + break; + } + } else + matches = 0; + } + untimeout(r_stamp); + + // Did we succeed? + if (change_succeeded) { + // Yup! Send 'O' to host so it knows we're OK. + const char ok_str[1] = "O"; + len = 1; + res = cyg_io_write(handle, ok_str, &len); + if (res != ENOERR) { + diag_printf("write failed - DEVIO error: %d\n", res); + hang(); + } - res = cyg_io_set_config(handle, CYG_IO_SET_CONFIG_SERIAL_INFO, &serial_info, &len); - if (res != ENOERR) { - diag_printf("Can't set serial config - DEVIO error: %d\n", res); - hang(); + // Now wait for the 'O' to be echoed so we know both lines + // have been emptied. + r_stamp = timeout(100, do_abort, handle); + do { + len = 1; + res = cyg_io_read(handle, in_buf, &len); + CYG_ASSERT(ENOERR == res, "Failed when waiting for 'O'"); + } while ('O' != in_buf[0]); + untimeout(r_stamp); + + // If we had to revert to the old configuration, return error. + if (using_old_config) + return -EIO; + else + return ENOERR; } - res = ENOERR; - } else { - res = ENOSUPP; -#ifdef __DEVELOPER__ - diag_printf("Host didn't accept config (%02x, %02x).\n", - reply[0], reply[1]); -#endif - } + // We didn't. Due to an IO error? + if (ENOERR != res && -EINTR != res) { + // We may have to reset the driver if the fail was due to + // a framing or parity error. + } -#if 0 - // FIXME: This is needed to prevent sending data at the new baud rate - // before the host is ready.... Need to fiddle this a bit more. - // Loop counts for ARM and PII in host side source. Replace with - // timed loops. - { - int i; - for (i = 0; i < 100000; i++); - } -#else - cyg_thread_delay(80); // this requires kernel -#endif - - return res; + // Revert to the old configuration and try again. + len = sizeof(old_cfg); + res = cyg_io_set_config(handle, CYG_IO_SET_CONFIG_SERIAL_INFO, + &old_cfg, &len); + if (res != ENOERR) { + diag_printf("change_config: set_config failed/3 (%d)\n", res); + hang(); + } + out_buf[0] = 't'; // change the char so a developer can + // see this has happened by the chars + // being written. + using_old_config = 1; + } } @@ -791,7 +951,7 @@ test_open_ser( cyg_io_handle_t* handle ) CYG_TEST_FAIL_FINISH("Can't lookup " TEST_SER_DEV); } #else - CYG_TEST_NA("No test device specified"); + CYG_TEST_NA(NA_MSG); #endif } @@ -818,7 +978,7 @@ test_open_tty( cyg_io_handle_t* handle ) CYG_TEST_FAIL_FINISH("Can't lookup " TEST_TTY_DEV); } #else - CYG_TEST_NA("No test device specified"); + CYG_TEST_NA(NA_MSG); #endif }
