/* * sercp - serial copy for transfering file to/from ZX Spectrum 128 AY's RS232 * Copyright (c) 2018-2019 Pavel Vymetalek * * This program 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 3 of the License, or * any later version (GPL-3.0-or-later). * * This program 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 this program. If not, see . */ /* * 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 #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 #include #include #include #include #endif #define false 0 #define FALSE 0 #define true 1 #define TRUE 1 const char* _version = "v0.4.0"; // SERIAL FILE *tapout_fd = NULL; int is_outfile = 0; int baud_rate = 0; int wait_ms = 800; char *path; char sercp_file[FILENAME_MAX]; unsigned char buff[32768]; unsigned char *p_buff; 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; 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 void sleep_ms(int milliseconds); char SERIALDEVICE[128] = { #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; // legth of block uint8_t block_xor; // xor of all bytes in block uint8_t block_sum; // 8-bit sum of all bytes in block } __attribute__((packed)) fi_sum;// used as fi_blocks in fileinfo typedef struct { uint16_t length; // length of data in fileinfo uint8_t h_xor; // xor of all bytes of fileinfo uint8_t h_sum; // sum of all bytes of fileinfo uint8_t fi_numblocks; // number of transfered blocks uint8_t fi_name[64]; // filename (8.3 format for now) uint8_t fi_reserved[12];// reserved uint16_t fi_time[1]; // fat file time uint16_t fi_date[1]; // fat file date fi_sum fi_blocks[256]; // blocks - see fi_sums above } __attribute__((packed)) FILEINFO; FILEINFO fileinfo; void usage(void) { 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 ("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"); #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. Default /dev/ttyUSB0\n"); #endif 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"); } // 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'}, {"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:lortvh", long_options, &option_index); if (c == -1) { // end of arguments break; } switch (c) { case 'd': #ifdef _WIN32 sprintf(SERIALDEVICE, "\\\\.\\%s", optarg); #else sprintf(SERIALDEVICE, "%s", optarg); #endif 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); 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 } } } //***************************************************************************** void FlushSerialPort() { if (serial_fd) { sleep_ms(20); #ifdef _WIN32 PurgeComm(serial_fd, PURGE_RXABORT| PURGE_TXABORT | PURGE_RXCLEAR | PURGE_TXCLEAR); #else tcflush(serial_fd, TCIOFLUSH); #endif } } //***************************************************************************** void CloseSerialPort() { if (serial_fd) { #ifdef _WIN32 PurgeComm(serial_fd, PURGE_RXABORT| PURGE_TXABORT | PURGE_RXCLEAR | PURGE_TXCLEAR); CloseHandle(serial_fd); #else tcflush(serial_fd, TCIOFLUSH); close (serial_fd); #endif } } //***************************************************************************** // void sleep_ms(int milliseconds) { #ifdef WIN32 Sleep(milliseconds); #else usleep(milliseconds * 1000); #endif } /************************************************************************/ int GetTerminalWidth(void) { #ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO csbi; int columns; // int rows; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; // rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; return (columns); #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) + 0.5; 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; // sum is counting from offset 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; // sum is counting from offset 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; // success } return -1; // sum error } /************************************************************************/ 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 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], 6, &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], 6); 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"); 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"); exit(-1); } } sleep_ms(100); // 100ms to wait speccy } /************************************************************************/ void sercpRecv(void) { int recv_phase = 0; // 0 - fileinfo, 1 - 16kiB block, 2 - last block uint8_t block_index = 0; size_t len = 0; uint16_t length = 0; uint16_t expected_len = 0; uint32_t overall_length = 0; // overall length of file uint32_t recv_length = 0; // receive length counter struct utimbuf f_datetime; struct tm timdat; #ifndef _WIN32 int result; #endif FILEINFO *p_fileinfo = &fileinfo; unsigned char *p_buff = buff; tapout_fd = NULL; #ifdef _WIN32 unsigned long ulNumBytes; #endif memset (p_fileinfo, 0, sizeof(fileinfo)); while (is_continue) { #ifndef _WIN32 result = poll (spolfd_serial, 1, 200); // 200ms timeout if (result == 0) { // nothing is comming continue; } #endif switch (recv_phase) { case 0: // receive fileinfo #ifdef _WIN32 sleep_ms(10); 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); #endif p_buff += len; length += len; if (length == sizeof(fileinfo)) { memcpy((unsigned char*) p_fileinfo, buff, sizeof(fileinfo)); if (CheckFileInfo(p_fileinfo) == 0) { overall_length = GetOverallLen(p_fileinfo); printf("File: \"%s\" number of blocks:%d, length of file: %u\n", p_fileinfo->fi_name, p_fileinfo->fi_numblocks, overall_length); recv_phase++; // will follow receive data blocks block_index = 0; // begins from first block 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 printf ("can't open output file"); exit (EXIT_FAILURE); #else err (1, "can't open output file"); #endif } } else { printf("Fileinfo corrupted. End...\n"); FlushSerialPort(); CloseSerialPort(); exit(EXIT_FAILURE); 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("Received unknown data. End...\n"); FlushSerialPort(); exit (EXIT_FAILURE); break; } break; case 1: // receive of data block - max. length 16kiB #ifdef _WIN32 ReadFile(serial_fd, p_buff, expected_len, &ulNumBytes, NULL); len = (size_t)ulNumBytes; #else len = read (serial_fd, p_buff, expected_len); #endif sleep_ms(10); 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 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; block_index++; expected_len = p_fileinfo->fi_blocks[block_index].block_len; } if (expected_len == 0 || block_index == 255) { printf ("\nTransfer successful\n"); is_continue = 0; recv_phase++; break; } break; } } if (tapout_fd) { fflush (tapout_fd); fclose (tapout_fd); // set date and time which is received memset(&timdat, 0, sizeof(struct tm)); if (*p_fileinfo->fi_date != 0) { timdat.tm_year = (((*p_fileinfo->fi_date & 0xFE00) >> 9) + 1980)-1900; timdat.tm_mon = ((*p_fileinfo->fi_date & 0x1E0) >> 5) - 1; timdat.tm_mday = *p_fileinfo->fi_date & 0x1F; timdat.tm_hour = ((*p_fileinfo->fi_time ) >> 11); timdat.tm_min = ((*p_fileinfo->fi_time & 0x7e0) >> 5); timdat.tm_sec = (*p_fileinfo->fi_time & 0x1F)*2; timdat.tm_isdst = -1; f_datetime.modtime = mktime(&timdat); f_datetime.actime = f_datetime.modtime; utime ((char*)p_fileinfo->fi_name, &f_datetime); } } } /************************************************************************/ void sercpSend(void) { FILE *tap_fd; char *p_bname, *p_basec; char *p_shortfilename; size_t fn_len; unsigned int 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 send_size; uint32_t overall_sent; uint8_t blck_num = 0; #ifdef _WIN32 unsigned long ulNumBytes; #endif struct tm *timdat; no = stat(sercp_file, &st); if (no == -1) { printf ("can't stat input file\n"); exit (EXIT_FAILURE); } if (st.st_size == 0) { printf ("Zero length of file. End\n"); exit (EXIT_FAILURE); } tap_fd = fopen(sercp_file, "rb"); if (tap_fd == NULL) { printf ("can't open input file\n"); exit (EXIT_FAILURE); } // FIXME do this more sophisticated and crossplatform, for example by __SIZE_WIDTH__ #if defined (__linux__) || defined(_WIN32) || defined (BSD) printf ("File %s, length: %ld\n", sercp_file, st.st_size); #else printf ("File %s, length: %lld\n", sercp_file, st.st_size); #endif timdat = localtime(&st.st_mtime); uint16_t FatDate = ((timdat->tm_year - 80) << 9) | ((timdat->tm_mon + 1) << 5) | timdat->tm_mday; uint16_t FatTime = ((timdat->tm_hour << 11) | (timdat->tm_min << 5) | (timdat->tm_sec >> 1)); memset (p_fileinfo, 0, sizeof(fileinfo)); // erase fileinfo p_basec = strdup(sercp_file); p_bname = basename (p_basec); if (strlen(p_bname) > 12) { printf ("Short filename: "); p_shortfilename = strdup(p_bname); fn_len = strlen(p_shortfilename); memcpy(p_shortfilename+4, p_shortfilename+fn_len-8, 8); *(p_shortfilename+12) = 0; printf ("%s\n", p_shortfilename); p_bname = p_shortfilename; } memcpy(p_fileinfo->fi_name, p_bname, strlen(p_bname)); memcpy(p_fileinfo->fi_date, &FatDate, sizeof(FatDate)); memcpy(p_fileinfo->fi_time, &FatTime, sizeof(FatTime)); file_len = (uint32_t) st.st_size; while (file_len) { len = fread(buff, 1, 16384, tap_fd); // read 16kiB from file CountSumBlock(p_fileinfo, num_blocks, buff, 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)); if (tcdrain(serial_fd) == -1) { perror("tcdrain err1: "); return; } #endif printf("Fileinfo sent with filename: %s\n", p_bname); 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; while (file_len) { len = fread(buff, 1, 16384, tap_fd); // read 16kiB from file p_buff = buff; if (len > 256) { send_size = 256; } else { send_size = len; } len_sent = len; while (len_sent) { #ifdef _WIN32 WriteFile(serial_fd, (void*)p_buff, send_size, &ulNumBytes, NULL); odeslano = (size_t)ulNumBytes; #else odeslano = write (serial_fd, (void*)p_buff, send_size); if (tcdrain(serial_fd) == -1) { perror("tcdrain err2: "); return; } #endif p_buff += odeslano; overall_sent += odeslano; len_sent -= odeslano; if (len_sent < 256) { send_size = len_sent; } DoProgress(overall_sent, st.st_size, PROGRESS_PERCENT); } file_len -= len; 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"); #ifdef __APPLE__ // FIXME on __APPLE__ this waiting helps sleep_ms(wait_ms); #endif fclose(tap_fd); } int OpenUart() { #ifdef _WIN32 DCB sDCB; COMMTIMEOUTS sCommTimeouts; serial_fd = CreateFile(SERIALDEVICE, 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.fOutxDsrFlow = FALSE; sDCB.fDtrControl = DTR_CONTROL_DISABLE; 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); } 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); } 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 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) and ioctl(2) 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) 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); switch (baud_rate){ case 115200: newtio.c_cflag = B115200; break; case 57600: newtio.c_cflag = B57600; break; 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; default: baud_rate = 38400; newtio.c_cflag = B38400; break; } cfsetospeed(&newtio, baud_rate); cfsetispeed(&newtio, baud_rate); // set ispeed same as ospeed (see POSIX) #if defined (__APPLE__) || defined (BSD) 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 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_hw)?"CTS/RTS":"None"); // http://unixwiz.net/techtips/termios-vmin-vtime.html 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) { width = GetTerminalWidth(); 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); } FlushSerialPort(); #ifndef _WIN32 spolfd_serial[0].fd = serial_fd; // watching descriptor spolfd_serial[0].events = POLLIN; // watch data on input #endif if (is_scp_read) { sercpRecv(); } else { sercpSend(); } CloseSerialPort(); return 0; }