From 9951618476f25d92907510eddd83ab51bbbd58d3 Mon Sep 17 00:00:00 2001 From: Pavel Vymetálek Date: Thu, 11 Jan 2024 13:44:10 +0100 Subject: Communication confirmed by Ack messages without hw-flow control - default confirmation by Ack messages - still compatible with old .sercp by -l switch for hw flow control - switch -w is not used when using ack messages - new switch for turbo speed - 115200Bd. Same as on the Speccy TODO - check Ack messages properly --- CHANGELOG | 1 + TODO | 6 ++ sercp.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++--------------- sercp.rc | 10 ++-- 4 files changed, 160 insertions(+), 50 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bcab710..834fa8f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +2024-01-11 v0.4.0 use new ACK messages instead hw-flow control 2020-05-01 v0.3.5 use gnu99 instead of gnu11, add O_NDELAY to serial port initialization 2020-04-08 v0.3.4 removed hw flow control for mac and bsd 2020-04-06 v0.3.3 fixed progressbar for files smaller than 256 bytes diff --git a/TODO b/TODO index f8ac68e..d560927 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,9 @@ +2024-01-11 + - check Ack messages properly + +2023-07-29 + - add support for ack of messages + 2020-04-05 + for short files < 256 bytes does not work the progressbar - underflow counter - listing of enumerated serial ports - linux, win, mac diff --git a/sercp.c b/sercp.c index a726055..fdc9fb8 100644 --- a/sercp.c +++ b/sercp.c @@ -68,12 +68,12 @@ #define FALSE 0 #define true 1 #define TRUE 1 -const char* _version = "v0.3.7"; +const char* _version = "v0.4.0"; // SERIAL FILE *tapout_fd = NULL; int is_outfile = 0; int baud_rate = 0; -int wait_ms = 800; +int wait_ms = 800 char *path; char sercp_file[FILENAME_MAX]; unsigned char buff[32768]; @@ -88,7 +88,7 @@ long pos = 0; int scp = 0; int is_scp_read = 0; int is_continue = 1; -int is_flow_control_none = 0; // 1 - set flow control to NONE +int is_flow_control_hw = 0; // 1 - set flow control to NONE // prototypes @@ -132,21 +132,28 @@ typedef struct { FILEINFO fileinfo; void usage(void) { - printf ("sercp %s (c)2018-2023 Pavel Vymetalek \n", _version); + printf ("sercp %s (c)2018-2024 Pavel Vymetalek \n", _version); printf ("serial copy for transfering file to/from ZX Spectrum 128 AY's RS232\n"); - printf ("Uses 1109bytes of fileinfo - blocks sums, filename, etc.\n"); + // printf ("Use 1109-bytes of fileinfo - blocks sums, filename, etc.\n"); + printf ("Confirmation of communication using ack messages - no HW flow control\n"); + printf ("Still compatible with old .sercp - with -l switch\n"); printf ("More info at https://vym.cz/sercp/\n"); - printf ("Usage:\nsercp [-v] [-h] -d /dev/serial [-b baud_rate] [-w time] [-r] \n"); +#ifdef _WIN32 + printf ("Usage:\nsercp.exe -d com1 [-t] [-r] \n"); +#else + printf ("Usage:\nsercp -d /dev/serial [-t] [-r] \n"); +#endif printf ("\t-v, --version\tShow version info\n"); printf ("\t-h, --help\tShow this text\n"); #ifdef _WIN32 printf ("\t-d, --device\tSerial com port\n"); #else - printf ("\t-d, --device\tSerial communication device\n"); + printf ("\t-d, --device\tSerial communication device. Default /dev/ttyUSB0\n"); #endif - printf ("\t-b, --baud\tSet the communication speed. Default 38400Bd\n"); - printf ("\t-n, --none\tSet the flow control to NONE. Default is CTS/RTS\n\t\t\tThis is useful for eLeMeNt ZX and machines or serial port without CTS/RTS wires\n"); - printf ("\t-w, --wait\tWaiting in milliseconds between transmitted blocks.\n\t\t\tDefault is -w 800 ms\n"); + printf ("\t-b, --baud\tSet the communication baud rate. Default 38400Bd\n"); + printf ("\t-t, --turbo\tSet the baud rate 115200Bd\n"); + printf ("\t-l, --hwflow\tSet the flow control to CTS/RTS. Default is NONE.\n\t\t\tThis is useful for old .sercp with HW flow control\n"); + printf ("\t-w, --wait\tWaiting in milliseconds between transmitted blocks.\n\t\t\tIn case of improperly functioning hw flow control\n"); printf ("\t-r, --read\tRead file from serial port\n"); } @@ -161,13 +168,14 @@ void TestArgs (int argc, char *argv[]) {"device", required_argument, NULL, 'd'}, {"baud", required_argument, NULL, 'b'}, {"wait", required_argument, NULL, 'w'}, - {"none", no_argument, NULL, 'n'}, + {"hwflow", no_argument, NULL, 'l'}, {"read", no_argument, NULL, 'r'}, + {"turbo", no_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {0, 0, 0, 0} }; - c = getopt_long (argc, argv, "d:b:w:nrvh", long_options, &option_index); + c = getopt_long (argc, argv, "d:b:w:lrtvh", long_options, &option_index); if (c == -1) { // end of arguments break; @@ -183,14 +191,17 @@ void TestArgs (int argc, char *argv[]) case 'b': baud_rate = atoi(optarg); break; + case 't': + baud_rate = 115200; + break; case 'w': wait_ms = atoi(optarg); break; case 'r': is_scp_read = 1; break; - case 'n': - is_flow_control_none = 1; + case 'l': + is_flow_control_hw = 1; break; case 'v': printf ("%s\n", _version); @@ -242,22 +253,6 @@ void CloseSerialPort() { //***************************************************************************** // -// uSleep win32 implementation - -void uSleep(int waitTime) { - #ifdef _WIN32 - __int64 time1 = 0, time2 = 0, freq = 0; - - QueryPerformanceCounter((LARGE_INTEGER *) &time1); - QueryPerformanceFrequency((LARGE_INTEGER *)&freq); - - do { - QueryPerformanceCounter((LARGE_INTEGER *) &time2); - } while ((time2 - time1) < waitTime); - #else - usleep(waitTime); - #endif -} void sleep_ms(int milliseconds) { #ifdef WIN32 @@ -401,6 +396,99 @@ uint32_t GetOverallLen(FILEINFO *p_fi) { return overall_len; } +void sendFileinfoAck(void) { +uint8_t fi_ack_buffer[] = {'A','c','k',0, 0x49, 0x0F}; +#ifdef _WIN32 + unsigned long ulNumBytes; + WriteFile(serial_fd, (void*)fi_ack_buffer, sizeof(fi_ack_buffer), &ulNumBytes, NULL); +#else + size_t odeslano; + odeslano = write (serial_fd, (void*)fi_ack_buffer, sizeof(fi_ack_buffer)); + if (tcdrain(serial_fd) == -1) { + perror("tcdrain err1: "); + return; + } +#endif +} + +void sendBlockAck(uint8_t block_num) { + uint8_t fi_ack_buffer[] = {'A','C','K',0, 0x49, 0x0F}; + int x; + uint8_t s_xor = 0; + uint8_t s_sum = 0; + fi_ack_buffer[3] = block_num; + // printf("\n Ack sent: "); + for (x=0; x<(sizeof(fi_ack_buffer)-2);x++) { + s_xor ^= fi_ack_buffer[x]; + s_sum += fi_ack_buffer[x]; + fi_ack_buffer[4] = s_xor; + fi_ack_buffer[5] = s_sum; + // printf("%2X ",fi_ack_buffer[x]); + } + // printf("%2X ",fi_ack_buffer[4]); + // printf("%2X \n",fi_ack_buffer[5]); +#ifdef _WIN32 + unsigned long ulNumBytes; + WriteFile(serial_fd, (void*)fi_ack_buffer, sizeof(fi_ack_buffer), &ulNumBytes, NULL); +#else + size_t odeslano; + odeslano = write (serial_fd, (void*)fi_ack_buffer, sizeof(fi_ack_buffer)); + if (tcdrain(serial_fd) == -1) { + perror("tcdrain err1: "); + return; + } +#endif +} + +uint8_t buf_ack[8]; + +uint8_t* WaitReadAck(void) { +int result; +int x; +size_t len = 0; + +#ifdef _WIN32 + unsigned long ulNumBytes; + sleep_ms(10); + ReadFile(serial_fd, buf_ack, 6, &ulNumBytes, NULL); +#else + memset(buf_ack, 0, 8); + while (1) { + result = poll (spolfd_serial, 1, 300); // 200ms timeout + if (result == 0) { + // nothing is comming + continue; + } else if (result == -1) { + FlushSerialPort(); + CloseSerialPort(); + exit(EXIT_FAILURE); + } else if (result) { + len += read (serial_fd, &buf_ack[len], 6); + if (len == 6) { + // printf("\nAck ok: "); + // for (x = 0; x < 6; x++) { + // printf("%2X ", buf_ack[x]); + // } + // printf("\n"); + break; + } + } + } +#endif + return (buf_ack); +} +// receive fileinfo ACK + +void RecvAckFileinfo(void) { + WaitReadAck(); + sleep_ms(100); // 100ms to wait speccy +} + +void RecvAckBlock(uint8_t block_number) { + WaitReadAck(); + sleep_ms(100); // 100ms to wait speccy +} + /************************************************************************/ void sercpRecv(void) { int recv_phase = 0; // 0 - fileinfo, 1 - 16kiB block, 2 - last block @@ -454,6 +542,9 @@ void sercpRecv(void) { p_buff = buff; // initialize buffer pointer length = 0; expected_len = p_fileinfo->fi_blocks[block_index].block_len; + // TODO Send FileinfoAck + sleep_ms(100); + sendFileinfoAck(); tapout_fd = fopen ((char*)p_fileinfo->fi_name, "wb"); if (tapout_fd == NULL) { #ifdef _WIN32 @@ -504,6 +595,9 @@ void sercpRecv(void) { if (CheckSumBlock(p_fileinfo, block_index, buff) == 0) { // block is ok - write to file if (tapout_fd) fwrite(buff, length, 1, tapout_fd); + // TODO + sleep_ms(100); + sendBlockAck(block_index); } length = 0; p_buff = buff; @@ -556,6 +650,7 @@ void sercpSend(void) { uint32_t len_sent; uint16_t send_size; uint32_t overall_sent; + uint8_t blck_num = 0; #ifdef _WIN32 unsigned long ulNumBytes; #endif @@ -626,8 +721,11 @@ void sercpSend(void) { } #endif printf("Fileinfo sent with filename: %s\n", p_bname); - sleep_ms(wait_ms); - + if (!is_flow_control_hw) { + RecvAckFileinfo(); + } else { + sleep_ms(wait_ms); + } rewind(tap_fd); file_len = (uint32_t) st.st_size; overall_sent = 0; @@ -660,9 +758,14 @@ void sercpSend(void) { DoProgress(overall_sent, st.st_size, PROGRESS_PERCENT); } file_len -= len; - if (file_len > 0) { - sleep_ms(wait_ms); + if (!is_flow_control_hw) { + RecvAckBlock(blck_num); + } else { + if (file_len > 0) { + sleep_ms(wait_ms); + } } + blck_num++; } printf("\nFile sent...\n"); #ifdef __APPLE__ @@ -693,12 +796,12 @@ int OpenUart() { sDCB.fAbortOnError = TRUE; sDCB.fOutxDsrFlow = FALSE; sDCB.fDtrControl = DTR_CONTROL_DISABLE; - if (is_flow_control_none) { - sDCB.fRtsControl = RTS_CONTROL_DISABLE; - sDCB.fOutxCtsFlow = FALSE; - } else { + if (is_flow_control_hw) { sDCB.fRtsControl = RTS_CONTROL_HANDSHAKE; sDCB.fOutxCtsFlow = TRUE; + } else { + sDCB.fRtsControl = RTS_CONTROL_DISABLE; + sDCB.fOutxCtsFlow = FALSE; } if (SetCommState(serial_fd, &sDCB) == 0) { return (-1); @@ -788,18 +891,18 @@ int OpenUart() { newtio.c_cflag |= CS8 | CLOCAL | CREAD | CCTS_OFLOW | CRTS_IFLOW | HUPCL; } #else - if (is_flow_control_none) { - // usefull for eLeMeNt ZX and machines or serial port without CTS/RTS wires - newtio.c_cflag |= CS8 | CLOCAL | CREAD; - } else { + if (is_flow_control_hw) { // standard CTS/RTS flow control newtio.c_cflag |= CS8 | CLOCAL | CREAD | CRTSCTS; + } else { + // usefull for eLeMeNt ZX and machines or serial port without CTS/RTS wires + newtio.c_cflag |= CS8 | CLOCAL | CREAD; } #endif newtio.c_iflag &= ~(IXON | IXOFF | IXANY); newtio.c_oflag &= ~(OPOST); newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // raw input - printf ("Serial device: %s, communication speed is: %d Bd, flow control: %s\n", SERIALDEVICE, baud_rate, (is_flow_control_none)?"None":"CTS/RTS"); + printf ("Serial device: %s, communication speed is: %d Bd, flow control: %s\n", SERIALDEVICE, baud_rate, (is_flow_control_hw)?"CTS/RTS":"None"); // http://unixwiz.net/techtips/termios-vmin-vtime.html newtio.c_cc[VMIN] = 1; // minimum number to read @@ -820,7 +923,7 @@ int main(int argc, char** argv, char** env) width = GetTerminalWidth(); inp_indx = 0; if (argc < 2) { - printf("You must specify the Serial device and file\n"); + // printf("You must specify the Serial device and file\n"); usage(); exit(1); } @@ -830,7 +933,7 @@ int main(int argc, char** argv, char** env) printf ("Can't open serial port\n"); exit (EXIT_FAILURE); } - + FlushSerialPort(); #ifndef _WIN32 spolfd_serial[0].fd = serial_fd; // watching descriptor spolfd_serial[0].events = POLLIN; // watch data on input diff --git a/sercp.rc b/sercp.rc index b5cd913..1d13589 100644 --- a/sercp.rc +++ b/sercp.rc @@ -1,8 +1,8 @@ -// RC file, codepage utf-8 !!!!! +// RC file, codepage utf-8! #include // include for version info constants 1 VERSIONINFO -FILEVERSION 0,3,7,0 -PRODUCTVERSION 0,3,7,0 +FILEVERSION 0,4,0,0 +PRODUCTVERSION 0,4,0,0 FILETYPE VFT_APP { BLOCK "StringFileInfo" @@ -10,14 +10,14 @@ FILETYPE VFT_APP BLOCK "040904E4" { VALUE "CompanyName", "vym.cz" - VALUE "FileVersion", "0.3.7" + VALUE "FileVersion", "0.4.0" VALUE "FileDescription", "sercp - serial copy for ZX Spectrum" VALUE "InternalName", "sercp" VALUE "LegalCopyright", "GNU GPL v3 or above" VALUE "LegalTrademarks", "Pavel Vymetálek" VALUE "OriginalFilename", "sercp" VALUE "ProductName", "sercp" - VALUE "ProductVersion", "0.3.7" + VALUE "ProductVersion", "0.4.0" VALUE "Build environment", "Linux: Mingw64, x86_64" } } -- cgit