/* Hatari - rs232.c This file is distributed under the GNU General Public License, version 2 or at your option any later version. Read the file gpl.txt for details. RS-232 Communications This is similar to the printing functions, we open a direct file (e.g. /dev/ttyS0) and send bytes over it. Using such method mimicks the ST exactly, and even allows us to connect to an actual ST! To wait for incoming data, we create a thread which copies the bytes into an input buffer. This method fits in with the internet code which also reads data into a buffer. */ const char RS232_fileid[] = "Hatari rs232.c : " __DATE__ " " __TIME__; #include #if HAVE_TERMIOS_H # include # include #endif #include #include #include #include "main.h" #include "configuration.h" #include "ioMem.h" #include "m68000.h" #include "mfp.h" #include "rs232.h" #define RS232_DEBUG 0 #if RS232_DEBUG #define Dprintf(a) printf a #else #define Dprintf(a) #endif static FILE *hComIn = NULL; /* Handle to file for reading */ static FILE *hComOut = NULL; /* Handle to file for writing */ static unsigned char InputBuffer_RS232[MAX_RS232INPUT_BUFFER]; static int InputBuffer_Head=0, InputBuffer_Tail=0; static volatile bool bQuitThread = false; #if HAVE_TERMIOS_H #if !HAVE_CFMAKERAW static inline void cfmakeraw(struct termios *termios_p) { termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); termios_p->c_oflag &= ~OPOST; termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); termios_p->c_cflag &= ~(CSIZE|PARENB); termios_p->c_cflag |= CS8; } #endif #if defined(__AMIGAOS4__) // dummy functions. REMOVE THEM LATER int tcgetattr(int file_descriptor,struct termios *tios_p) { return -1; } int tcsetattr(int file_descriptor,int action,struct termios *tios_p) { return -1; } int cfsetospeed(struct termios *tios, speed_t ospeed) { tios->c_ospeed = ospeed; return 0; } int cfsetispeed(struct termios *tios,speed_t ispeed) { tios->c_ispeed = ispeed; return 0; } #endif /* __AMIGAOS4__ */ /*-----------------------------------------------------------------------*/ /** * Set serial line parameters to "raw" mode. */ static bool RS232_SetRawMode(FILE *fhndl) { struct termios termmode; int fd; memset (&termmode, 0, sizeof(termmode)); /* Init with zeroes */ fd = fileno(fhndl); /* Get file descriptor */ if (isatty(fd)) { if (tcgetattr(fd, &termmode) != 0) return false; /* Set "raw" mode: */ termmode.c_cc[VMIN] = 1; termmode.c_cc[VTIME] = 0; cfmakeraw(&termmode); if (tcsetattr(fd, TCSADRAIN, &termmode) != 0) return false; } return true; } /*-----------------------------------------------------------------------*/ /** * Set hardware configuration of RS-232: * - Bits per character * - Parity * - Start/stop bits */ static bool RS232_SetBitsConfig(FILE *fhndl, int nCharSize, int nStopBits, bool bUseParity, bool bEvenParity) { struct termios termmode; int fd; memset (&termmode, 0, sizeof(termmode)); /* Init with zeroes */ fd = fileno(fhndl); if (isatty(fd)) { if (tcgetattr(fd, &termmode) != 0) { Dprintf(("RS232_SetBitsConfig: tcgetattr failed.\n")); return false; } /* Set the character size: */ termmode.c_cflag &= ~CSIZE; switch (nCharSize) { case 8: termmode.c_cflag |= CS8; break; case 7: termmode.c_cflag |= CS7; break; case 6: termmode.c_cflag |= CS6; break; case 5: termmode.c_cflag |= CS5; break; } /* Set stop bits: */ if (nStopBits >= 2) termmode.c_oflag |= CSTOPB; else termmode.c_oflag &= ~CSTOPB; /* Parity bit: */ if (bUseParity) termmode.c_cflag |= PARENB; else termmode.c_cflag &= ~PARENB; if (bEvenParity) termmode.c_cflag &= ~PARODD; else termmode.c_cflag |= PARODD; /* Now store the configuration: */ if (tcsetattr(fd, TCSADRAIN, &termmode) != 0) { Dprintf(("RS232_SetBitsConfig: tcsetattr failed.\n")); return false; } } return true; } #endif /* HAVE_TERMIOS_H */ /*-----------------------------------------------------------------------*/ /** * Open file on COM port. */ static bool RS232_OpenCOMPort(void) { bool ok = true; if (!hComOut && ConfigureParams.RS232.szOutFileName[0]) { /* Create our COM file for output */ hComOut = fopen(ConfigureParams.RS232.szOutFileName, "wb"); if (hComOut) { setvbuf(hComOut, NULL, _IONBF, 0); #if HAVE_TERMIOS_H /* First set the output parameters to "raw" mode */ if (!RS232_SetRawMode(hComOut)) { Log_Printf(LOG_WARN, "Can't set raw mode for %s\n", ConfigureParams.RS232.szOutFileName); } #endif Dprintf(("Successfully opened RS232 output file.\n")); } else { Log_Printf(LOG_WARN, "RS232: Failed to open output file %s\n", ConfigureParams.RS232.szOutFileName); ok = false; } } if (!hComIn && ConfigureParams.RS232.szInFileName[0]) { /* Create our COM file for input */ hComIn = fopen(ConfigureParams.RS232.szInFileName, "rb"); if (hComIn) { setvbuf(hComIn, NULL, _IONBF, 0); #if HAVE_TERMIOS_H /* Now set the input parameters to "raw" mode */ if (!RS232_SetRawMode(hComIn)) { Log_Printf(LOG_WARN, "Can't set raw mode for %s\n", ConfigureParams.RS232.szInFileName); } #endif Dprintf(("Successfully opened RS232 input file.\n")); } else { Log_Printf(LOG_WARN, "RS232: Failed to open input file %s\n", ConfigureParams.RS232.szInFileName); ok = false; } } return ok; } /*-----------------------------------------------------------------------*/ /** * Close file on COM port */ static void RS232_CloseCOMPort(void) { if (hComIn) { /* Close */ fclose(hComIn); hComIn = NULL; } if (hComOut) { fclose(hComOut); hComOut = NULL; } Dprintf(("Closed RS232 files.\n")); } /* thread stuff */ static SDL_sem* pSemFreeBuf; /* Semaphore to sync free space in InputBuffer_RS232 */ static SDL_Thread *RS232Thread = NULL; /* Thread handle for reading incoming data */ /*-----------------------------------------------------------------------*/ /** * Add incoming bytes from other machine into our input buffer */ static void RS232_AddBytesToInputBuffer(unsigned char *pBytes, int nBytes) { int i; /* Copy bytes into input buffer */ for (i=0; i> 5) & 3); nStopBits = (ucr >> 3) & 3; Dprintf(("RS232_HandleUCR(%i) : character size=%i , stop bits=%i\n", ucr, nCharSize, nStopBits)); if (hComOut != NULL) { if (!RS232_SetBitsConfig(hComOut, nCharSize, nStopBits, ucr&4, ucr&2)) Log_Printf(LOG_WARN, "RS232_HandleUCR: failed to set bits configuration for %s\n", ConfigureParams.RS232.szOutFileName); } if (hComIn != NULL) { if (!RS232_SetBitsConfig(hComIn, nCharSize, nStopBits, ucr&4, ucr&2)) Log_Printf(LOG_WARN, "RS232_HandleUCR: failed to set bits configuration for %s\n", ConfigureParams.RS232.szInFileName); } #endif /* HAVE_TERMIOS_H */ } /*-----------------------------------------------------------------------*/ /** * Set baud rate configuration of RS-232. */ bool RS232_SetBaudRate(int nBaud) { #if HAVE_TERMIOS_H int i; int fd; speed_t baudtype; struct termios termmode; static const int baudtable[][2] = { { 50, B50 }, { 75, B75 }, { 110, B110 }, { 134, B134 }, { 150, B150 }, { 200, B200 }, { 300, B300 }, { 600, B600 }, { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 }, { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 }, #ifdef B230400 /* B230400 is not defined on all systems */ { 230400, B230400 }, #endif { -1, -1 } }; Dprintf(("RS232_SetBaudRate(%i)\n", nBaud)); /* Convert baud number to baud termios constant: */ baudtype = -1; for (i = 0; baudtable[i][0] != -1; i++) { if (baudtable[i][0] == nBaud) { baudtype = baudtable[i][1]; break; } } if (baudtype == (speed_t)-1) { Dprintf(("RS232_SetBaudRate: Unsupported baud rate %i.\n", nBaud)); return false; } /* Set ouput speed: */ if (hComOut != NULL) { memset (&termmode, 0, sizeof(termmode)); /* Init with zeroes */ fd = fileno(hComOut); if (isatty(fd)) { if (tcgetattr(fd, &termmode) != 0) return false; cfsetospeed(&termmode, baudtype); if (tcsetattr(fd, TCSADRAIN, &termmode) != 0) return false; } } /* Set input speed: */ if (hComIn != NULL) { memset (&termmode, 0, sizeof(termmode)); /* Init with zeroes */ fd = fileno(hComIn); if (isatty(fd)) { if (tcgetattr(fd, &termmode) != 0) return false; cfsetispeed(&termmode, baudtype); if (tcsetattr(fd, TCSADRAIN, &termmode) != 0) return false; } } #endif /* HAVE_TERMIOS_H */ return true; } /*-----------------------------------------------------------------------*/ /** * Set baud rate configuration of RS-232 according to the Timer-D hardware * registers. */ void RS232_SetBaudRateFromTimerD(void) { int nTimerD_CR, nTimerD_DR, nBaudRate; nTimerD_CR = IoMem[0xfffa1d] & 0x07; nTimerD_DR = IoMem[0xfffa25]; if (!nTimerD_CR) return; if ( nTimerD_DR == 0 ) nTimerD_DR = 256; /* In MFP, a data register=0 is in fact 256 */ /* Calculate baud rate: (MFP/Timer-D is supplied with 2.4576 MHz) */ nBaudRate = 2457600 / nTimerD_DR / 2; /*if (IoMem[0xfffa29] & 0x80)*/ /* We only support the by-16 prescaler */ nBaudRate /= 16; switch (nTimerD_CR) { case 1: nBaudRate /= 4; break; case 2: nBaudRate /= 10; break; case 3: nBaudRate /= 16; break; case 4: nBaudRate /= 50; break; case 5: nBaudRate /= 64; break; case 6: nBaudRate /= 100; break; case 7: nBaudRate /= 200; break; } /* Adjust some ugly baud rates from TOS to more reasonable values: */ switch (nBaudRate) { case 80: nBaudRate = 75; break; case 109: nBaudRate = 110; break; case 120: nBaudRate = 110; break; case 1745: nBaudRate = 1800; break; case 1920: nBaudRate = 1800; break; } RS232_SetBaudRate(nBaudRate); } /*-----------------------------------------------------------------------*/ /** * Set flow control configuration of RS-232. */ void RS232_SetFlowControl(Sint16 ctrl) { Dprintf(("RS232_SetFlowControl(%i)\n", ctrl)); /* Not yet written */ } /*----------------------------------------------------------------------- */ /** * Pass bytes from emulator to RS-232 */ bool RS232_TransferBytesTo(Uint8 *pBytes, int nBytes) { /* Make sure there's a RS-232 connection if it's enabled */ if (ConfigureParams.RS232.bEnableRS232) RS232_OpenCOMPort(); /* Have we connected to the RS232? */ if (hComOut) { /* Send bytes directly to the COM file */ if (fwrite(pBytes, 1, nBytes, hComOut)) { Dprintf(("RS232: Sent %i bytes ($%x ...)\n", nBytes, *pBytes)); MFP_InputOnChannel ( MFP_INT_TRN_BUF_EMPTY , 0 ); return true; /* OK */ } } return false; /* Failed */ } /*-----------------------------------------------------------------------*/ /** * Read characters from our internal input buffer (bytes from other machine) */ bool RS232_ReadBytes(Uint8 *pBytes, int nBytes) { int i; /* Connected? */ if (hComIn && InputBuffer_Head != InputBuffer_Tail) { /* Read bytes out of input buffer */ for (i=0; i