aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--TODO12
-rw-r--r--sercp.c310
-rw-r--r--sercp.rc10
4 files changed, 271 insertions, 62 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..91596cc 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,13 @@
+2024-02-05
+ + add -o switch to use old "protocol" wihout ack messages
+ - fix buffer overflow when receiving fileinfo @115200Bd and other computer send it @38400Bd
+
+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
@@ -25,11 +35,11 @@ VÝHODY
* funguje :-)
* nejrychlejší způsob jak dostat do ZX malý soubor
* nepotřebuje PC - přenos mezi dvěma Spectry
++ nastavuje datum a čas souboru - esxdosu má api utime()
NEVÝHODY
* nemá LFN (esxdos) - ale sercp je na to připraven :-)
-* nenastavuje datum a čas souboru - esxdosu chybí api
* nejde obnovit přenos
* zatím není na ZX psáno portabilně - na jiné diskové systémy
diff --git a/sercp.c b/sercp.c
index f85871b..d9c4f9b 100644
--- a/sercp.c
+++ b/sercp.c
@@ -18,6 +18,10 @@
/*
* IFDEFS inspired here:
* https://iq.opengenus.org/detect-operating-system-in-c/
+ *
+ * vmin, vtime insipired here:
+ * // http://unixwiz.net/techtips/termios-vmin-vtime.html
+ *
*/
#ifdef _WIN32
@@ -64,12 +68,12 @@
#define FALSE 0
#define true 1
#define TRUE 1
-const char* _version = "v0.3.5";
+const char* _version = "v0.8.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];
@@ -84,6 +88,8 @@ long pos = 0;
int scp = 0;
int is_scp_read = 0;
int is_continue = 1;
+int is_flow_control_hw = 0; // 1 - set flow control to NONE
+int is_old_protocol = 0; // 1 - set old protocol without AKC messages
// prototypes
@@ -101,7 +107,7 @@ char SERIALDEVICE[128] = {
static HANDLE serial_fd;
#else
int serial_fd;
- struct sigaction saterm; /* definition of signal action */
+ // struct sigaction saterm; /* definition of signal action */
struct pollfd spolfd_serial[1]; // pole descriptoru pro poll
#endif
@@ -127,20 +133,29 @@ typedef struct {
FILEINFO fileinfo;
void usage(void) {
- printf ("sercp %s (c)2018-2020 Pavel Vymetalek <pavel@vym.cz>\n", _version);
+ printf ("sercp %s (c)2018-2024 Pavel Vymetalek <pavel@vym.cz>\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] <filename>\n");
+#ifdef _WIN32
+ printf ("Usage:\nsercp.exe -d com1 [-t] [-r] <filename>\n");
+#else
+ printf ("Usage:\nsercp -d /dev/serial [-t] [-r] <filename>\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-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-o, --oldprot\tUse the old protocol without AKC messages.\n\t\t\tThis is useful for old .sercp with missing 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");
}
@@ -155,14 +170,17 @@ void TestArgs (int argc, char *argv[])
{"device", required_argument, NULL, 'd'},
{"baud", required_argument, NULL, 'b'},
{"wait", required_argument, NULL, 'w'},
+ {"hwflow", no_argument, NULL, 'l'},
+ {"oldprot", no_argument, NULL, 'o'},
{"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:rvh", long_options, &option_index);
+ c = getopt_long (argc, argv, "d:b:w:lortvh", long_options, &option_index);
if (c == -1) {
- // konec parametru
+ // end of arguments
break;
}
switch (c) {
@@ -172,17 +190,25 @@ void TestArgs (int argc, char *argv[])
#else
sprintf(SERIALDEVICE, "%s", optarg);
#endif
-// printf ("Serial port: %s\n", SERIALDEVICE);
break;
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 'l':
+ is_flow_control_hw = 1;
+ break;
+ case 'o':
+ is_old_protocol = 1;
+ break;
case 'v':
printf ("%s\n", _version);
exit(1);
@@ -204,15 +230,27 @@ void TestArgs (int argc, char *argv[])
//*****************************************************************************
+// TODO read bytes from serial port for 200ms
void FlushSerialPort() {
-if (serial_fd) {
- sleep_ms(20);
+ ssize_t len = 0;
+ if (serial_fd) {
+ sleep_ms(20);
#ifdef _WIN32
+ unsigned long ulNumBytes = 0;
PurgeComm(serial_fd, PURGE_RXABORT| PURGE_TXABORT | PURGE_RXCLEAR | PURGE_TXCLEAR);
+ do {
+ sleep_ms(100);
+ ReadFile(serial_fd, p_buff, 100, &ulNumBytes, NULL);
+ len = (size_t) ulNumBytes;
+ } while (len > 1);
#else
tcflush(serial_fd, TCIOFLUSH);
- #endif
+ do {
+ sleep_ms(100);
+ len = read (serial_fd, p_buff, 100);
+ } while (len > 1);
+#endif
}
}
@@ -233,22 +271,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
@@ -290,7 +312,7 @@ void DoProgress(size_t pos, size_t max, unsigned char csum_ok) {
char progress_char;
p = pos;
m = max;
- percent = 100 / m * p;
+ percent = (100 / m * p) + 0.5;
ipercent = percent / 100 * imax;
if (is_binary) progress_char = '#';
else progress_char = '=';
@@ -392,6 +414,132 @@ uint32_t GetOverallLen(FILEINFO *p_fi) {
return overall_len;
}
+void sendAckFileinfo(void) {
+uint8_t fi_ack_buffer[] = {'A','c','k',0, 0x49, 0x0F};
+ if (is_old_protocol) return;
+#ifdef _WIN32
+ unsigned long ulNumBytes;
+ WriteFile(serial_fd, (void*)fi_ack_buffer, sizeof(fi_ack_buffer), &ulNumBytes, NULL);
+#else
+ write (serial_fd, (void*)fi_ack_buffer, sizeof(fi_ack_buffer));
+ if (tcdrain(serial_fd) == -1) {
+ perror("tcdrain err1: ");
+ return;
+ }
+#endif
+}
+
+void sendAckBlock(uint8_t block_num) {
+ uint8_t fi_ack_buffer[] = {'A','C','K',0, 0x49, 0xCF};
+ int x;
+ uint8_t s_xor = 0;
+ uint8_t s_sum = 0;
+ if (is_old_protocol) return;
+
+ 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
+ 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) {
+ size_t len = 0;
+ #ifdef _WIN32
+ unsigned long ulNumBytes =0;
+ while (1) {
+ sleep_ms(10);
+ ReadFile(serial_fd, &buf_ack[len], sizeof(buf_ack)-len, &ulNumBytes, NULL);
+ len += (size_t) ulNumBytes;
+ if (len == 6) break;
+ }
+#else
+ // int x;
+ int result;
+ 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], sizeof(buf_ack)-len);
+ if (len == 6) {
+ break;
+ }
+ }
+ }
+#endif
+ return (len);
+}
+// receive fileinfo ACK
+int CheckAckSum() {
+ int x;
+ uint8_t xorsum = 0;
+ uint8_t sumsum = 0;
+ for (x = 0; x < 4; x++) {
+ sumsum += buf_ack[x];
+ xorsum ^= buf_ack[x];
+ }
+ if ((xorsum == buf_ack[4]) && (sumsum == buf_ack[5])) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+void RecvAckFileinfo(void) {
+ if (is_old_protocol) return;
+ if (WaitReadAck() == 6) {
+ if (CheckAckSum()) {
+ printf("\nErr Ack fileinfo\n");
+ for (int x=0; x<6; x++) {
+ printf("%2X ",buf_ack[x]);
+ }
+ printf ("\n");
+ exit(-1);
+ };
+ };
+ sleep_ms(100); // 100ms to wait speccy
+}
+
+void RecvAckBlock(uint8_t block_number) {
+ if (is_old_protocol) return;
+ if (WaitReadAck() == 6) {
+ if (CheckAckSum() || (buf_ack[3] != block_number)) {
+ printf("\nErr ACK block\n");
+ for (int x=0; x<6; x++) {
+ printf("%2X ",buf_ack[x]);
+ }
+ printf ("\n");
+ exit(-1);
+ }
+ }
+ sleep_ms(100); // 100ms to wait speccy
+}
+
/************************************************************************/
void sercpRecv(void) {
int recv_phase = 0; // 0 - fileinfo, 1 - 16kiB block, 2 - last block
@@ -427,8 +575,8 @@ void sercpRecv(void) {
// receive fileinfo
#ifdef _WIN32
sleep_ms(10);
- ReadFile(serial_fd, p_buff, sizeof(fileinfo), &ulNumBytes, NULL);
- len = (uint16_t) ulNumBytes;
+ ReadFile(serial_fd, p_buff, sizeof(fileinfo)-length, &ulNumBytes, NULL);
+ len = (size_t) ulNumBytes;
#else
sleep_ms(10);
len = read (serial_fd, p_buff, sizeof(fileinfo)-length);
@@ -445,6 +593,8 @@ void sercpRecv(void) {
p_buff = buff; // initialize buffer pointer
length = 0;
expected_len = p_fileinfo->fi_blocks[block_index].block_len;
+ sleep_ms(100);
+ sendAckFileinfo();
tapout_fd = fopen ((char*)p_fileinfo->fi_name, "wb");
if (tapout_fd == NULL) {
#ifdef _WIN32
@@ -480,7 +630,7 @@ void sercpRecv(void) {
// receive of data block - max. length 16kiB
#ifdef _WIN32
ReadFile(serial_fd, p_buff, expected_len, &ulNumBytes, NULL);
- len = (uint16_t)ulNumBytes;
+ len = (size_t)ulNumBytes;
#else
len = read (serial_fd, p_buff, expected_len);
#endif
@@ -495,6 +645,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);
+ sendAckBlock(block_index);
}
length = 0;
p_buff = buff;
@@ -547,6 +700,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
@@ -617,8 +771,11 @@ void sercpSend(void) {
}
#endif
printf("Fileinfo sent with filename: %s\n", p_bname);
- sleep_ms(wait_ms);
-
+ if (!is_flow_control_hw && !is_old_protocol) {
+ RecvAckFileinfo();
+ } else {
+ sleep_ms(wait_ms);
+ }
rewind(tap_fd);
file_len = (uint32_t) st.st_size;
overall_sent = 0;
@@ -651,13 +808,20 @@ 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 && !is_old_protocol) {
+ RecvAckBlock(blck_num);
+ } else {
+ if (file_len > 0) {
+ sleep_ms(wait_ms);
+ }
}
+ blck_num++;
}
printf("\nFile sent...\n");
- // FIXME na __APPLE__ tady pomaha pockat
- // sleep_ms(5000);
+#ifdef __APPLE__
+ // FIXME on __APPLE__ this waiting helps
+ sleep_ms(wait_ms);
+#endif
fclose(tap_fd);
}
@@ -682,8 +846,13 @@ int OpenUart() {
sDCB.fAbortOnError = TRUE;
sDCB.fOutxDsrFlow = FALSE;
sDCB.fDtrControl = DTR_CONTROL_DISABLE;
- sDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;
- sDCB.fOutxCtsFlow = TRUE;
+ 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);
}
@@ -700,16 +869,34 @@ int OpenUart() {
if (SetCommTimeouts(serial_fd, &sCommTimeouts) == 0) {
return (-1);
}
- FlushSerialPort();
return (0);
#else
struct termios oldtio, newtio;
/* open the device to be non-blocking (read will return immediatly) */
- serial_fd = open(SERIALDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
+ serial_fd = open(SERIALDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK); // | O_NDELAY
if (serial_fd < 0) {
perror(SERIALDEVICE);
return(-1);
}
+#ifdef __APPLE__
+ // Note that open() follows POSIX semantics: multiple open() calls to the same file will succeed
+ // unless the TIOCEXCL ioctl is issued. This will prevent additional opens except by root-owned
+ // processes.
+ // See tty(4) <x-man-page//4/tty> and ioctl(2) <x-man-page//2/ioctl> for details.
+ if (ioctl(serial_fd, TIOCEXCL) == -1) {
+ printf("Error setting TIOCEXCL on %s - %s(%d).\n", SERIALDEVICE, strerror(errno), errno);
+ return (-1);
+ }
+
+ // Now that the device is open, clear the O_NONBLOCK flag so subsequent I/O will block.
+ // See fcntl(2) <x-man-page//2/fcntl> for details.
+ if (fcntl(serial_fd, F_SETFL, 0) == -1) {
+ printf("Error clearing O_NONBLOCK %s - %s(%d).\n", SERIALDEVICE, strerror(errno), errno);
+ return (-1);
+ }
+
+
+#endif
tcgetattr(serial_fd, &oldtio); /* save current port settings */
bzero(&newtio, sizeof (newtio));
cfmakeraw(&newtio);
@@ -743,29 +930,40 @@ int OpenUart() {
newtio.c_cflag = B38400;
break;
}
- cfsetspeed(&newtio, baud_rate);
+ cfsetospeed(&newtio, baud_rate);
+ cfsetispeed(&newtio, baud_rate); // set ispeed same as ospeed (see POSIX)
#if defined (__APPLE__) || defined (BSD)
-// newtio.c_cflag |= CS8 | CLOCAL | CREAD | CCTS_OFLOW | CRTS_IFLOW;
- newtio.c_cflag |= CS8 | CLOCAL | CREAD;
+ if (is_flow_control_none) {
+ printf("Sorry, flow control is not tested on this platform\n");
+ newtio.c_cflag |= CS8 | CLOCAL | CREAD | HUPCL;
+ } else {
+ newtio.c_cflag |= CS8 | CLOCAL | CREAD | CCTS_OFLOW | CRTS_IFLOW | HUPCL;
+ }
#else
- newtio.c_cflag |= CS8 | CLOCAL | CREAD | CRTSCTS;
+ 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);
- printf ("Serial device: %s, communication speed is: %d Bd\n", SERIALDEVICE, baud_rate);
+ 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_hw)?"CTS/RTS":"None");
+
// http://unixwiz.net/techtips/termios-vmin-vtime.html
- newtio.c_cc[VMIN] = 0;
- newtio.c_cc[VTIME] = 0;
+ newtio.c_cc[VMIN] = 1; // minimum number to read
+ newtio.c_cc[VTIME] = 1; // time to wait
tcsetattr(serial_fd, TCSANOW, &newtio);
sleep_ms(20);
- tcflush(serial_fd, TCIOFLUSH);
return 0;
#endif
}
-
/************************************************************************/
/************************************************************************/
int main(int argc, char** argv, char** env)
@@ -773,7 +971,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);
}
@@ -783,7 +981,7 @@ int main(int argc, char** argv, char** env)
printf ("Can't open serial port\n");
exit (EXIT_FAILURE);
}
-
+ FlushSerialPort(); // make the input fifo empty
#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 038e212..0e723fe 100644
--- a/sercp.rc
+++ b/sercp.rc
@@ -1,8 +1,8 @@
-// RC file, codepage utf-8 !!!!!
+// RC file, codepage utf-8!
#include <windows.h> // include for version info constants
1 VERSIONINFO
-FILEVERSION 0,3,5,0
-PRODUCTVERSION 0,3,5,0
+FILEVERSION 0,8,0,0
+PRODUCTVERSION 0,8,0,0
FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
@@ -10,14 +10,14 @@ FILETYPE VFT_APP
BLOCK "040904E4"
{
VALUE "CompanyName", "vym.cz"
- VALUE "FileVersion", "0.3.5"
+ VALUE "FileVersion", "0.8.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.5"
+ VALUE "ProductVersion", "0.8.0"
VALUE "Build environment", "Linux: Mingw64, x86_64"
}
}