#ifdef __WIN32 // #include // #include // #include // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #define false 0 #define FALSE 0 #define true 1 #define TRUE 1 const char* _version = "v0.2"; // SERIAL FILE *tapout_fd = NULL; int is_outfile = 0; int baud_rate = 0; int wait_us = 200; int wait_ms = 800; char *path; char sercp_file[FILENAME_MAX]; unsigned char buff[32768]; unsigned char *p_buff; unsigned int is_continue = 1; unsigned int is_binary = 0; static int inp_indx = 0; size_t out_indx; float width; // width of terminal long pos = 0; int scp = 0; int is_scp_read = 0; char MODEMDEVICE[64] = { #ifdef __WIN32 "\\\\.\\COM1" #else "/dev/ttyUSB0" #endif }; #ifdef __WIN32 static HANDLE serial_fd; #else int serial_fd; struct sigaction saterm; /* definition of signal action */ struct pollfd spolfd_serial[1]; // pole descriptoru pro poll #endif typedef struct { uint16_t block_len; uint8_t block_xor; uint8_t block_sum; } __attribute__((packed)) fi_sum; typedef struct { uint16_t length; // delka dat ve fileinfo uint8_t h_xor; // xor bajtu fileinfa od fi_numblocks uint8_t h_sum; // sum bajtu fileinfa od fi_numblocks uint8_t fi_numblocks; // pocet bloku uint8_t fi_name[64]; // jmeno souboru - prozatim musi byt ve fromatu 8.3 - kvuli esxdosu uint8_t fi_date[8]; // nepouzito uint8_t fi_time[8]; // nepouzito fi_sum fi_blocks[256]; // delky a soucty bloku } __attribute__((packed)) FILEINFO; FILEINFO fileinfo; void usage(void) { printf ("sercp %s (c)2018-2019 Pavel Vymetálek \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 ("Usage:\nsercp [-v] [-h] -d /dev/serial [-b baud_rate] [-w time] [-r] \n"); 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"); #endif printf ("\t-b, --baud\tSet the communication speed. Default 38400Bd\n"); printf ("\t-w, --wait\tWaiting in milliseconds between transmitted blocks. Default is -w 800 milliseconds\n"); printf ("\t-r, --read\tRead file from serial port\n"); } // fileinfo 1109bytes /************************************************************************/ void TestArgs (int argc, char *argv[]) { int c; while (1) { int option_index = 0; static struct option long_options[] = { {"device", required_argument, NULL, 'd'}, {"baud", required_argument, NULL, 'b'}, {"wait", required_argument, NULL, 'w'}, {"read", no_argument, NULL, 'r'}, {"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); if (c == -1) { // konec parametru break; } switch (c) { case 'd': #ifdef __WIN32 sprintf(MODEMDEVICE, "\\\\.\\%s", optarg); #else // strncpy(MODEMDEVICE, optarg, strlen(optarg)); sprintf(MODEMDEVICE, "%s", optarg); #endif printf ("Serial port: %s\n", MODEMDEVICE); break; case 'b': baud_rate = atoi(optarg); break; case 'w': wait_us = atoi(optarg); wait_ms = atoi(optarg); break; case 'r': is_scp_read = 1; break; case 'v': printf ("%s\n", _version); exit(1); break; case 'h': usage(); exit(1); break; default: break; } } if (optind < argc) { while (optind < argc){ strncpy (&sercp_file[0], argv[optind++], 63); // input file name or path - without option switch } } } //***************************************************************************** // // 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 } // cross-platform sleep function void sleep_ms(int milliseconds) { #ifdef WIN32 Sleep(milliseconds); // #elif _POSIX_C_SOURCE >= 199309L // struct timespec ts; // ts.tv_sec = milliseconds / 1000; // ts.tv_nsec = (milliseconds % 1000) * 1000000; // nanosleep(&ts, NULL); #else usleep(milliseconds * 1000); #endif } /************************************************************************/ void signal_handler_sigterm (int status) { // CTRL+C pressed is_continue = 0; // do not continue } /************************************************************************/ int GetTerminalWidth(void) { #ifdef __WIN32 return (80); #else struct winsize termsize; ioctl (STDOUT_FILENO, TIOCGWINSZ, &termsize); return (termsize.ws_col); #endif } #define PROGRESS_PERCENT 0 #define PROGRESS_COMPLETTE 1 #define PROGRESS_ERROR_CSUM 2 /************************************************************************/ void DoProgress(size_t pos, size_t max, unsigned char csum_ok) { width = GetTerminalWidth(); float percent, p, m; int imax = width - 40; int ipercent; int px; char progress_char; p = pos; m = max; percent = 100 / m * p; ipercent = percent / 100 * imax; if (is_binary) progress_char = '#'; else progress_char = '='; printf ("Proceed bytes: %6d/%6d [", (int) pos, (int)max); for (px = 0; px < imax; px++) { if (px < ipercent) printf ("%c", progress_char); else printf (" "); } if (csum_ok == 0) { printf ("] %3d %%\r", (int)percent); } else if (csum_ok == 1) { printf ("] OK \r"); } else { printf ("] ERR \r"); } fflush (stdout); } /************************************************************************/ int CheckFileInfo(FILEINFO* p_fi) { unsigned char *p_fiinfo = (unsigned char*)p_fi; uint16_t fi_len; uint8_t fi_xor = 0; uint8_t fi_sum = 0; fi_len = p_fi->length; p_fiinfo += 4; // suma se bude pocitat od offsetu 4 for (uint16_t indx = 0; indx < (fi_len-4); indx++) { fi_xor ^= *p_fiinfo; fi_sum += *p_fiinfo; p_fiinfo++; } if (fi_xor == p_fi->h_xor && fi_sum == p_fi->h_sum) { return 0; } return -1; } /************************************************************************/ void CountFileInfoChecksum(FILEINFO* p_fi) { unsigned char *p_fiinfo = (unsigned char*)p_fi; uint16_t fi_len; uint8_t fi_xor = 0; uint8_t fi_sum = 0; fi_len = p_fi->length; p_fiinfo += 4; // suma se bude pocitat od offsetu 4 for (uint16_t indx = 0; indx < (fi_len-4); indx++) { fi_xor ^= *p_fiinfo; fi_sum += *p_fiinfo; p_fiinfo++; } p_fi->h_xor = fi_xor; p_fi->h_sum = fi_sum; } /************************************************************************/ int CheckSumBlock(FILEINFO* p_fi, uint8_t block_indx, uint8_t *p_buffer) { uint16_t block_len; uint8_t b_xor = 0; uint8_t b_sum = 0; uint8_t block_xor; uint8_t block_sum; block_len = p_fi->fi_blocks[block_indx].block_len; block_sum = p_fi->fi_blocks[block_indx].block_sum; block_xor = p_fi->fi_blocks[block_indx].block_xor; for (uint16_t indx = 0; indx < block_len; indx++) { b_xor ^= *p_buffer; b_sum += *p_buffer; p_buffer++; } if (b_xor == block_xor && b_sum == block_sum) { return 0; // vrat se, je to v poradku } return -1; // vrat se s chybou bloku } /************************************************************************/ void CountSumBlock(FILEINFO* p_fi, uint8_t block_indx, uint8_t *p_buffer, uint16_t block_len) { uint8_t block_xor = 0; uint8_t block_sum = 0; for (uint16_t indx = 0; indx < block_len; indx++) { block_xor ^= *p_buffer; block_sum += *p_buffer; p_buffer++; } p_fi->fi_blocks[block_indx].block_len = block_len; p_fi->fi_blocks[block_indx].block_sum = block_sum; p_fi->fi_blocks[block_indx].block_xor = block_xor; } /************************************************************************/ uint32_t GetOverallLen(FILEINFO *p_fi) { uint32_t overall_len = 0; uint8_t block_num = p_fi->fi_numblocks; for (uint8_t indx = 0; indx < block_num; indx++) { overall_len += p_fi->fi_blocks[indx].block_len; } return overall_len; } /************************************************************************/ void sercpRecv(void) { int recv_phase = 0; // 0 - fileinfo, 1 - blok 16kiB, 2 - posledni blok uint8_t block_index = 0; int result; uint16_t len; uint16_t length = 0; uint16_t expected_len = 0; // uint8_t expected_xor; // uint8_t expected_sum; uint32_t overall_length = 0; // celkova delka souboru uint32_t recv_length = 0; // zatim prijatych dat ze souboru int err; unsigned long ulNumBytes; FILEINFO *p_fileinfo = &fileinfo; unsigned char *p_buff = buff; tapout_fd = NULL; memset (p_fileinfo, 0, sizeof(fileinfo)); while (is_continue) { #ifndef __WIN32 result = poll (spolfd_serial, 1, 200); // 200ms timeout if (result == 0) { // nic neprislo continue; } #endif switch (recv_phase) { case 0: // prijem fileinfo // if (spolfd_serial[0].revents & POLLIN) { #ifdef __WIN32 sleep_ms(10); ReadFile(serial_fd, p_buff, sizeof(fileinfo), &ulNumBytes, NULL); len = (uint16_t) ulNumBytes; #else sleep_ms(10); len = read (serial_fd, p_buff, sizeof(fileinfo)); #endif p_buff += len; length += len; if (length == sizeof(fileinfo)) { memcpy((unsigned char*) p_fileinfo, buff, sizeof(fileinfo)); if (CheckFileInfo(p_fileinfo) == 0) { printf("Prijato fileinfo\n"); overall_length = GetOverallLen(p_fileinfo); printf("Nazev souboru: \"%s\" pocet bloku:%d, delka souboru: %u\n", p_fileinfo->fi_name, p_fileinfo->fi_numblocks, overall_length); recv_phase++; // priste se uz prijimaji bloky block_index = 0; // zacina se prvnim blokem p_buff = buff; // buffer na zacatek length = 0; expected_len = p_fileinfo->fi_blocks[block_index].block_len; tapout_fd = fopen ((char*)p_fileinfo->fi_name, "wb"); if (tapout_fd == NULL) { err = errno; #ifdef __WIN32 printf ("can't open output file"); exit (EXIT_FAILURE); #else error (1, err, "can't open output file"); #endif } } else { printf("Fileinfo neni v cajku. Koncim...\n"); break; } break; } else if (length > sizeof(fileinfo)){ #ifdef __WIN32 ReadFile(serial_fd, p_buff, sizeof(fileinfo), &ulNumBytes, NULL); len = (uint16_t)ulNumBytes; #else len = read (serial_fd, p_buff, sizeof(fileinfo)); #endif printf("Nejaky bordel. Koncim...\n"); break; } // } break; case 1: // prijem datoveho bloku - max. delka 16kiB // if (spolfd_serial[0].revents & POLLIN) { #ifdef __WIN32 ReadFile(serial_fd, p_buff, expected_len, &ulNumBytes, NULL); len = (uint16_t)ulNumBytes; sleep_ms(10); #else len = read (serial_fd, p_buff, expected_len); sleep_ms(10); #endif // printf ("read len: %d\n", len); p_buff += len; expected_len -= len; length += len; recv_length += len; DoProgress(recv_length, overall_length, PROGRESS_PERCENT); if (length == p_fileinfo->fi_blocks[block_index].block_len) { // prijaty prvni block // printf("Prijaty blok c.%d delky: %d\n", block_index, length); if (CheckSumBlock(p_fileinfo, block_index, buff) == 0) { // blok je v cajku - zapsat do souboru if (tapout_fd) fwrite(buff, length, 1, tapout_fd); } length = 0; p_buff = buff; block_index++; expected_len = p_fileinfo->fi_blocks[block_index].block_len; } if (expected_len == 0 || block_index == 255) { printf ("\nKonec prenosu\n"); recv_phase++; is_continue = 0; break; } // } /*else if (length > 16384) { #ifdef __WIN32 ReadFile(serial_fd, p_buff, 16384, &ulNumBytes, NULL); len = (uint16_t)ulNumBytes; #else len = read (serial_fd, p_buff, 16384); #endif printf("Nejaky bordel. Koncim...\n"); break; }*/ } } if (tapout_fd) { fflush (tapout_fd); fclose (tapout_fd); } printf ("\n\nulozeno...\n"); } /************************************************************************/ void sercpSend(void) { FILE *tap_fd; char *bname, *basec; // pinter na kopii nazvu souboru - basename char *shortfilename; size_t fn_len; unsigned int err, no, len; struct stat st; FILEINFO *p_fileinfo = &fileinfo; unsigned char *p_buff = buff; uint32_t file_len; uint8_t num_blocks = 0; ssize_t odeslano; uint32_t len_sent; uint16_t sent_size; uint32_t overall_sent; unsigned long ulNumBytes; no = stat(sercp_file, &st); if (no != 0) { err = errno; // error(1, err, "can't stat input file"); printf ("can't stat input file\n"); exit (EXIT_FAILURE); } tap_fd = fopen(sercp_file, "rb"); if (tap_fd == NULL) { err = errno; // error(1, err, "can't open input file"); printf ("can't open input file\n"); exit (EXIT_FAILURE); } if (st.st_size == 0) { printf ("Zero length of file. End\n"); return; } printf ("File %s, length: %ld\n", sercp_file, st.st_size); memset (p_fileinfo, 0, sizeof(fileinfo)); // smazat fileinfo basec = strdup (sercp_file); bname = basename (basec); if (strlen(bname) > 12) { printf ("Short filename: "); shortfilename = strdup(bname); fn_len = strlen(shortfilename); memcpy(shortfilename+4, shortfilename+fn_len-8, 8); *(shortfilename+12) = 0; printf ("%s\n", shortfilename); bname = shortfilename; } memcpy(p_fileinfo->fi_name, bname, strlen(bname)); file_len = (uint32_t) st.st_size; while (file_len) { len = fread(buff, 1, 16384, tap_fd); // precti 16kiB dat CountSumBlock(p_fileinfo, num_blocks, buff, len); // printf ("Blok c. %d, delka: %d\n", num_blocks, len); file_len -= len; num_blocks++; } p_fileinfo->fi_numblocks = num_blocks; p_fileinfo->length = num_blocks*4 + 85; CountFileInfoChecksum(p_fileinfo); #ifdef __WIN32 WriteFile(serial_fd, (void*)p_fileinfo, sizeof(fileinfo), &ulNumBytes, NULL); #else odeslano = write (serial_fd, (void*)p_fileinfo, sizeof(fileinfo)); tcdrain(serial_fd); #endif printf("Fileinfo sent with filename: %s\n", bname); // TODO Cekat pauzu mezi bloky // uSleep (wait_ms * 1000); sleep_ms(wait_ms); rewind(tap_fd); // prenaseny soubor na zacatek file_len = (uint32_t) st.st_size; overall_sent = 0; while (file_len && is_continue) { len = fread(buff, 1, 16384, tap_fd); // precti 16kiB dat p_buff = buff; sent_size = 256; len_sent = len; while (len_sent && is_continue) { #ifdef __WIN32 WriteFile(serial_fd, (void*)p_buff, sent_size, &ulNumBytes, NULL); odeslano = (size_t)ulNumBytes; // sleep_ms(wait_ms); #else odeslano = write (serial_fd, (void*)p_buff, sent_size); tcdrain(serial_fd); #endif p_buff += odeslano; overall_sent += odeslano; len_sent -= odeslano; if (len_sent < 256) { sent_size = len_sent; } DoProgress(overall_sent, st.st_size, PROGRESS_PERCENT); } // uSleep (wait_ms * 1000); sleep_ms(wait_ms); file_len -= len; } printf("\nFile sent...\n"); fclose(tap_fd); } int OpenUart() { #ifdef __WIN32 DCB sDCB; COMMTIMEOUTS sCommTimeouts; serial_fd = CreateFile(MODEMDEVICE, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (serial_fd == INVALID_HANDLE_VALUE) { return (-1); } if (GetCommState(serial_fd, &sDCB) == 0) { return (-1); } if (baud_rate == 0) baud_rate = 38400; sDCB.BaudRate = baud_rate; sDCB.ByteSize = 8; sDCB.Parity = NOPARITY; sDCB.fBinary = TRUE; sDCB.StopBits = ONESTOPBIT; sDCB.fAbortOnError = TRUE; sDCB.fOutxCtsFlow = FALSE; sDCB.fOutxDsrFlow = FALSE; // sDCB.fDtrControl = DTR_CONTROL_ENABLE; if (SetCommState(serial_fd, &sDCB) == 0) { return (-1); } if (GetCommTimeouts(serial_fd, &sCommTimeouts) == 0) { return (-1); } sCommTimeouts.ReadIntervalTimeout = MAXDWORD; //80 sCommTimeouts.ReadTotalTimeoutConstant = 00; sCommTimeouts.ReadTotalTimeoutMultiplier = 00; sCommTimeouts.WriteTotalTimeoutConstant = 80; sCommTimeouts.WriteTotalTimeoutMultiplier = 80; if (SetCommTimeouts(serial_fd, &sCommTimeouts) == 0) { return (-1); } return (0); #else struct termios oldtio, newtio; /* open the device to be non-blocking (read will return immediatly) */ serial_fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK); if (serial_fd < 0) { perror(MODEMDEVICE); return(-1); } tcgetattr(serial_fd, &oldtio); /* save current port settings */ switch (baud_rate){ case 115200: newtio.c_cflag = B115200; break; case 57600: newtio.c_cflag = B57600; break; default: baud_rate = 38400; case 38400: newtio.c_cflag = B38400; break; case 19200: newtio.c_cflag = B19200; break; case 9600: newtio.c_cflag = B9600; break; case 4800: newtio.c_cflag = B4800; break; case 2400: newtio.c_cflag = B2400; break; case 1200: newtio.c_cflag = B1200; break; } newtio.c_cflag |= CS8 | CLOCAL | CREAD | CRTSCTS; // CSTOPB - dva stop bity NEE printf ("Serial device: %s, communication speed is: %d Bd\n", MODEMDEVICE, baud_rate); newtio.c_iflag = 0; newtio.c_oflag = 0; newtio.c_lflag = 0; newtio.c_cc[VMIN] = 0; newtio.c_cc[VTIME] = 10; tcsetattr(serial_fd, TCSANOW, &newtio); tcflush(serial_fd, TCIOFLUSH); return 0; #endif } void CloseUart() { if (serial_fd) { #ifdef __WIN32 CloseHandle(serial_fd); #else close (serial_fd); #endif } } /************************************************************************/ /************************************************************************/ int main(int argc, char** argv, char** env) { #ifndef __WIN32 // osetreni breaku ^C v unixech saterm.sa_handler = signal_handler_sigterm; saterm.sa_flags = 0; sigaction (SIGINT, &saterm, NULL); #endif width = GetTerminalWidth(); // nastaveni serioveho portu inp_indx = 0; if (argc < 2) { printf("You must specify the Serial device and file\n"); usage(); exit(1); } TestArgs (argc, argv); if (OpenUart() == -1) { printf ("Can't open serial port\n"); exit (EXIT_FAILURE); } #ifndef __WIN32 spolfd_serial[0].fd = serial_fd; // nastaveni hlidaneho descriptoru spolfd_serial[0].events = POLLIN; // hlidaji se data na vstupu #endif // serial copy activated if (is_scp_read) { sercpRecv(); } else { sercpSend(); } CloseUart(); return 0; }