/* * serial_windows.cpp - Serial device driver for Win32 * * Basilisk II (C) 1997-2008 Christian Bauer * * Windows platform specific code copyright (C) Lauri Pesonen * * 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 2 of the License, or * (at your option) any later version. * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // TODO: serial i/o threads should have high priority. #include "sysdeps.h" #include #include #include "main.h" #include "util_windows.h" // somehow util_windows undefines min #define min(x,y) ((x) < (y) ? (x) : (y)) #include "macos_util.h" #include "prefs.h" #include "serial.h" #include "serial_defs.h" #include "cpu_emulation.h" // This must be always on. #define DEBUG 1 #undef OutputDebugString #define OutputDebugString serial_log_write static void serial_log_write( char *s ); #define SERIAL_LOG_FILE_NAME TEXT("serial.log") #include "debug.h" #undef D #define D(x) if(debug_serial != DB_SERIAL_NONE) (x); enum { DB_SERIAL_NONE=0, DB_SERIAL_NORMAL, DB_SERIAL_LOUD }; static int16 debug_serial = DB_SERIAL_NONE; static HANDLE serial_log_file = INVALID_HANDLE_VALUE; static void serial_log_open( LPCTSTR path ) { if(debug_serial == DB_SERIAL_NONE) return; DeleteFile( path ); serial_log_file = CreateFile( path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL ); if( serial_log_file == INVALID_HANDLE_VALUE ) { ErrorAlert( "Could not create the serial log file." ); } } static void serial_log_close( void ) { if(debug_serial == DB_SERIAL_NONE) return; if( serial_log_file != INVALID_HANDLE_VALUE ) { CloseHandle( serial_log_file ); serial_log_file = INVALID_HANDLE_VALUE; } } static void serial_log_write( char *s ) { DWORD bytes_written; // should have been checked already. if(debug_serial == DB_SERIAL_NONE) return; if( serial_log_file != INVALID_HANDLE_VALUE ) { DWORD count = strlen(s); if (0 == WriteFile(serial_log_file, s, count, &bytes_written, NULL) || (int)bytes_written != count) { serial_log_close(); ErrorAlert( "serial log file write error (out of disk space?). Log closed." ); } else { FlushFileBuffers( serial_log_file ); } } } // Driver private variables class XSERDPort : public SERDPort { public: XSERDPort(LPCTSTR dev, LPCTSTR suffix) { D(bug(TEXT("XSERDPort constructor %s\r\n"), dev)); read_pending = write_pending = false; if(dev) _tcscpy( device_name, dev ); else *device_name = 0; _tcsupr(device_name); is_parallel = (_tcsncmp(device_name, TEXT("LPT"), 3) == 0); is_file = (_tcsncmp(device_name, TEXT("FILE"), 4) == 0); if(is_file) { char entry_name[20]; _snprintf( entry_name, lengthof(entry_name), "portfile%s", str(suffix).get() ); const char *path = PrefsFindString(entry_name); if(path) { _tcscpy( output_file_name, tstr(path).get() ); } else { _tcscpy( output_file_name, TEXT("C:\\B2TEMP.OUT") ); } } is_serial = !is_parallel && !is_file; fd = INVALID_HANDLE_VALUE; input_thread_active = output_thread_active = NULL; } virtual ~XSERDPort() { D(bug("XSERDPort destructor \r\n")); if (input_thread_active) { D(bug("WARNING: brute TerminateThread(input)\r\n")); TerminateThread(input_thread_active,0); CloseHandle(input_signal); input_thread_active = NULL; } if (output_thread_active) { D(bug("WARNING: brute TerminateThread(output)\r\n")); TerminateThread(output_thread_active,0); CloseHandle(output_signal); output_thread_active = NULL; } } virtual int16 open(uint16 config); virtual int16 prime_in(uint32 pb, uint32 dce); virtual int16 prime_out(uint32 pb, uint32 dce); virtual int16 control(uint32 pb, uint32 dce, uint16 code); virtual int16 status(uint32 pb, uint32 dce, uint16 code); virtual int16 close(void); private: bool configure(uint16 config); void set_handshake(uint32 s, bool with_dtr); static unsigned int WINAPI input_func(void *arg); static unsigned int WINAPI output_func(void *arg); static int acknowledge_error(HANDLE h, bool is_read); bool set_timeouts(int bauds, int parity_bits, int stop_bits); TCHAR device_name[256]; HANDLE fd; bool io_killed; // Flag: KillIO called, I/O threads must not call deferred tasks bool quitting; // Flag: Quit threads HANDLE input_thread_active; // Handle: Input thread installed (was a bool) unsigned int input_thread_id; HANDLE input_signal; // Signal for input thread: execute command uint32 input_pb, input_dce; // Command parameters for input thread HANDLE output_thread_active; // Handle: Output thread installed (was a bool) unsigned int output_thread_id; HANDLE output_signal; // Signal for output thread: execute command uint32 output_pb, output_dce; // Command parameters for output thread DCB mode; // Terminal configuration bool is_serial; bool is_parallel; // true if LPTx bool is_file; // true if FILE TCHAR output_file_name[256]; }; /* * Initialization */ void SerialInit(void) { const char *port; debug_serial = PrefsFindInt32("debugserial"); serial_log_open( SERIAL_LOG_FILE_NAME ); // Read serial preferences and create structs for both ports port = PrefsFindString("seriala"); if(port) { D(bug("SerialInit seriala=%s\r\n",port)); } the_serd_port[0] = new XSERDPort(tstr(port).get(), TEXT("0")); port = PrefsFindString("serialb"); if(port) { D(bug("SerialInit serialb=%s\r\n",port)); } the_serd_port[1] = new XSERDPort(tstr(port).get(), TEXT("1")); } /* * Deinitialization */ void SerialExit(void) { D(bug("SerialExit\r\n")); if(the_serd_port[0]) delete (XSERDPort *)the_serd_port[0]; if(the_serd_port[1]) delete (XSERDPort *)the_serd_port[1]; D(bug("SerialExit done\r\n")); serial_log_close(); } /* * Open serial port */ int16 XSERDPort::open(uint16 config) { // Don't open NULL name devices if (!device_name || !*device_name) return openErr; D(bug(TEXT("XSERDPort::open device=%s,config=0x%X\r\n"),device_name,(int)config)); // Init variables io_killed = false; quitting = false; // Open port if(is_file) { DeleteFile( output_file_name ); fd = CreateFile( output_file_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); } else { fd = CreateFile( device_name, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 ); } if(fd == INVALID_HANDLE_VALUE) { goto open_error; D(bug(TEXT("XSERDPort::open failed to open port %s\r\n"),device_name)); } if(is_serial) { // Configure port for raw mode memset( &mode, 0, sizeof(DCB) ); mode.DCBlength = sizeof(mode); if(!GetCommState( fd, &mode )) goto open_error; mode.fBinary = TRUE; if(!configure(config)) { D(bug("XSERDPort::configure failed\r\n")); goto open_error; } } // Start input/output threads input_signal = CreateSemaphore( 0, 0, 1, NULL); if(!input_signal) goto open_error; output_signal = CreateSemaphore( 0, 0, 1, NULL); if(!output_signal) goto open_error; D(bug("Semaphores created\r\n")); input_thread_active = (HANDLE)_beginthreadex( 0, 0, input_func, (LPVOID)this, 0, &input_thread_id ); output_thread_active = (HANDLE)_beginthreadex( 0, 0, output_func, (LPVOID)this, 0, &output_thread_id ); if (!input_thread_active || !output_thread_active) goto open_error; D(bug("Threads created, Open returns success\r\n")); return noErr; open_error: D(bug("Open cleanup after failure\r\n")); if (input_thread_active) { TerminateThread(input_thread_active,0); CloseHandle(input_signal); input_thread_active = 0; } if (output_thread_active) { TerminateThread(output_thread_active,0); CloseHandle(output_signal); output_thread_active = 0; } if(fd != INVALID_HANDLE_VALUE) { CloseHandle(fd); fd = 0; } return openErr; } /* * Read data from port */ int16 XSERDPort::prime_in(uint32 pb, uint32 dce) { D(bug("XSERDPort::prime_in\r\n")); // Send input command to input_thread read_done = false; read_pending = true; input_pb = pb; input_dce = dce; ReleaseSemaphore(input_signal,1,NULL); return 1; // Command in progress } /* * Write data to port */ int16 XSERDPort::prime_out(uint32 pb, uint32 dce) { D(bug("XSERDPort::prime_out\r\n")); // Send output command to output_thread write_done = false; write_pending = true; output_pb = pb; output_dce = dce; ReleaseSemaphore(output_signal,1,NULL); return 1; // Command in progress } static DWORD get_comm_output_buf_size( HANDLE h ) { DWORD size = 0; COMMPROP cp; if(GetCommProperties(h,&cp)) { size = cp.dwCurrentTxQueue; } return size; } /* * Control calls */ int16 XSERDPort::control(uint32 pb, uint32 dce, uint16 code) { D(bug("XSERDPort::control code=%d\r\n",(int)code)); switch (code) { case kSERDClockMIDI: /* http://til.info.apple.com/techinfo.nsf/artnum/n2425 A MIDI interface operates at 31.25 Kbaud (+/- 1%) [== 31400] asynchronously, using a data format of one start bit, eight data bits, and one stop bit. This makes a total of 10 bits for each 320 microsecond period per serial byte. */ D(bug("kSERDClockMIDI setting 38400,n,8,1\n")); return noErr; /* mode.BaudRate = 38400; mode.ByteSize = 8; mode.StopBits = ONESTOPBIT; mode.Parity = NOPARITY; if(!SetCommState( fd, &mode )) { D(bug("kSERDClockMIDI SetCommState() failed\n")); return controlErr; } else { if(!set_timeouts(38400,0,2)) { D(bug("kSERDClockMIDI set_timeouts() failed\n")); return controlErr; } D(bug("kSERDClockMIDI OK\n")); return noErr; } */ case 1: // KillIO io_killed = true; if(is_serial) { // Make sure we won't hang waiting. There is something wrong // in how read_pending & write_pending are handled. DWORD endtime = GetTickCount() + 1000; while ( (read_pending || write_pending) && (GetTickCount() < endtime) ) { Sleep(20); } if(read_pending || write_pending) { D(bug("Warning (KillIO): read_pending=%d, write_pending=%d\n", read_pending, write_pending)); read_pending = write_pending = false; } // | PURGE_TXABORT | PURGE_RXABORT not needed, no overlapped i/o PurgeComm(fd,PURGE_TXCLEAR|PURGE_RXCLEAR); FlushFileBuffers(fd); } io_killed = false; D(bug("KillIO done\n")); return noErr; case kSERDConfiguration: if (configure((uint16)ReadMacInt16(pb + csParam))) return noErr; else return paramErr; case kSERDInputBuffer: if(is_serial) { // SetupComm() wants both values, so we need to know the output size. DWORD osize = get_comm_output_buf_size(fd); DWORD isize = ReadMacInt16(pb + csParam + 4) & 0xffffffc0; // 1k minimum // Was this something Amiga specific -- do I need to do this? if (isize < 1024) isize = 1024; if(isize > 0 && osize > 0) { if(SetupComm( fd, isize, osize )) { D(bug(" buffer size is now %08lx\n", isize)); return noErr; } else { D(bug(" SetupComm(%d,%d) failed, error = %08lx\n", isize, osize, GetLastError())); } } } // Always return ok. return noErr; case kSERDSerHShake: set_handshake(pb + csParam, false); return noErr; case kSERDSetBreak: if(is_serial) { if(!SetCommBreak(fd)) return controlErr; } return noErr; case kSERDClearBreak: if(is_serial) { if(!ClearCommBreak(fd)) return controlErr; } return noErr; case kSERDBaudRate: { if (is_serial) { uint16 rate = (uint16)ReadMacInt16(pb + csParam); int baud_rate; if (rate <= 50) { rate = 50; baud_rate = CBR_110; } else if (rate <= 75) { rate = 75; baud_rate = CBR_110; } else if (rate <= 110) { rate = 110; baud_rate = CBR_110; } else if (rate <= 134) { rate = 134; baud_rate = CBR_110; } else if (rate <= 150) { rate = 150; baud_rate = CBR_110; } else if (rate <= 200) { rate = 200; baud_rate = CBR_300; } else if (rate <= 300) { rate = 300; baud_rate = CBR_300; } else if (rate <= 600) { rate = 600; baud_rate = CBR_600; } else if (rate <= 1200) { rate = 1200; baud_rate = CBR_1200; } else if (rate <= 1800) { rate = 1800; baud_rate = CBR_2400; } else if (rate <= 2400) { rate = 2400; baud_rate = CBR_2400; } else if (rate <= 4800) { rate = 4800; baud_rate = CBR_4800; } else if (rate <= 9600) { rate = 9600; baud_rate = CBR_9600; } else if (rate <= 19200) { rate = 19200; baud_rate = CBR_19200; } else if (rate <= 38400) { rate = 38400; baud_rate = CBR_38400; } else if (rate <= 57600) { rate = 57600; baud_rate = CBR_57600; } else { rate = 57600; baud_rate = CBR_57600; } WriteMacInt16(pb + csParam, rate); mode.BaudRate = baud_rate; if(!SetCommState( fd, &mode )) return controlErr; // TODO: save parity/stop values and use here (not critical) if(!set_timeouts(rate,0,1)) return controlErr; } return noErr; } case kSERDHandshake: case kSERDHandshakeRS232: set_handshake(pb + csParam, true); return noErr; case kSERDMiscOptions: if (ReadMacInt8(pb + csParam) & kOptionPreserveDTR) mode.fDtrControl = DTR_CONTROL_ENABLE; // correct? else mode.fDtrControl = DTR_CONTROL_DISABLE; // correct? if(is_serial) { if(!SetCommState( fd, &mode )) return controlErr; } return noErr; case kSERDAssertDTR: { if (is_serial) { if(!EscapeCommFunction(fd,SETDTR)) return controlErr; } return noErr; } case kSERDNegateDTR: { if (is_serial) { if(!EscapeCommFunction(fd,CLRDTR)) return controlErr; } return noErr; } case kSERDSetPEChar: case kSERDSetPEAltChar: { uint16 errChar = (uint16)ReadMacInt16(pb + csParam); mode.fErrorChar = TRUE; mode.ErrorChar = (char)errChar; return noErr; } case kSERDResetChannel: if (is_serial) { // | PURGE_TXABORT | PURGE_RXABORT not needed, no overlapped i/o PurgeComm(fd,PURGE_TXCLEAR|PURGE_RXCLEAR); FlushFileBuffers(fd); } return noErr; case kSERDAssertRTS: { if (is_serial) { if(!EscapeCommFunction(fd,SETRTS)) return controlErr; } return noErr; } case kSERDNegateRTS: { if (is_serial) { if(!EscapeCommFunction(fd,CLRRTS)) return controlErr; } return noErr; } case kSERD115KBaud: if (is_serial) { mode.BaudRate = CBR_115200; if(!SetCommState( fd, &mode )) return controlErr; } return noErr; case kSERD230KBaud: case kSERDSetHighSpeed: if (is_serial) { mode.BaudRate = CBR_256000; if(!SetCommState( fd, &mode )) return controlErr; } return noErr; default: D(bug("WARNING: SerialControl(): unimplemented control code %d\r\n", code)); return controlErr; } } /* * Status calls */ int16 XSERDPort::status(uint32 pb, uint32 dce, uint16 code) { // D(bug("XSERDPort::status code=%d\r\n",(int)code)); DWORD error_state; COMSTAT comstat; switch (code) { case kSERDInputCount: { uint32 num = 0; if (is_serial) { if(!ClearCommError(fd,&error_state,&comstat)) return statusErr; num = comstat.cbInQue; } WriteMacInt32(pb + csParam, num); return noErr; } case kSERDStatus: { uint32 p = pb + csParam; WriteMacInt8(p + staCumErrs, cum_errors); cum_errors = 0; DWORD status; if(is_serial) { if(!GetCommModemStatus(fd,&status)) return statusErr; } else { status = MS_CTS_ON | MS_DSR_ON | MS_RLSD_ON; D(bug("kSERDStatus: faking status for LPT port or FILE\r\n")); } WriteMacInt8(p + staXOffSent, 0); WriteMacInt8(p + staXOffHold, 0); WriteMacInt8(p + staRdPend, read_pending); WriteMacInt8(p + staWrPend, write_pending); WriteMacInt8(p + staCtsHold, status & MS_CTS_ON ? 0 : 1); WriteMacInt8(p + staDsrHold, status & MS_DSR_ON ? 0 : 1); WriteMacInt8(p + staModemStatus, (status & MS_DSR_ON ? dsrEvent : 0) | (status & MS_RING_ON ? riEvent : 0) | (status & MS_RLSD_ON ? dcdEvent : 0) // is this carrier detect? | (status & MS_CTS_ON ? ctsEvent : 0)); return noErr; } default: D(bug("WARNING: SerialStatus(): unimplemented status code %d\r\n", code)); return statusErr; } } /* * Close serial port */ int16 XSERDPort::close() { D(bug("XSERDPort::close\r\n")); // Kill threads if (input_thread_active) { quitting = true; ReleaseSemaphore(input_signal,1,NULL); input_thread_active = 0; CloseHandle(input_signal); } if (output_thread_active) { quitting = true; ReleaseSemaphore(output_signal,1,NULL); output_thread_active = 0; // bugfix: was: CloseHandle(&output_signal); CloseHandle(output_signal); } // Close port if(fd != INVALID_HANDLE_VALUE) { CloseHandle(fd); fd = 0; } return noErr; } bool XSERDPort::set_timeouts( int bauds, int parity_bits, int stop_bits ) { COMMTIMEOUTS timeouts; uint32 bytes_per_sec; uint32 msecs_per_ch; bool result = false; // Should already been checked if (!is_serial) return true; bytes_per_sec = bauds / (mode.ByteSize + parity_bits + stop_bits); // 75% bytes_per_sec // bytes_per_sec = (bytes_per_sec+bytes_per_sec+bytes_per_sec) >> 2; // 50% bytes_per_sec bytes_per_sec = bytes_per_sec >> 1; msecs_per_ch = 1000 / bytes_per_sec; if(msecs_per_ch == 0) msecs_per_ch = 1; if(GetCommTimeouts(fd,&timeouts)) { D(bug("old timeout values: %ld %ld %ld %ld %ld\r\n", timeouts.ReadIntervalTimeout, timeouts.ReadTotalTimeoutMultiplier, timeouts.ReadTotalTimeoutConstant, timeouts.WriteTotalTimeoutMultiplier, timeouts.WriteTotalTimeoutConstant )); timeouts.WriteTotalTimeoutMultiplier = msecs_per_ch; timeouts.WriteTotalTimeoutConstant = 10; /* timeouts.ReadIntervalTimeout = msecs_per_ch; timeouts.ReadTotalTimeoutMultiplier = msecs_per_ch; timeouts.ReadTotalTimeoutConstant = 10; */ timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = 0; if(!SetCommTimeouts(fd,&timeouts)) { D(bug("SetCommTimeouts() failed in configure()\r\n")); } else { D(bug("new timeout values: %ld %ld %ld %ld %ld\r\n", timeouts.ReadIntervalTimeout, timeouts.ReadTotalTimeoutMultiplier, timeouts.ReadTotalTimeoutConstant, timeouts.WriteTotalTimeoutMultiplier, timeouts.WriteTotalTimeoutConstant )); result = true; } } else { D(bug("GetCommTimeouts() failed in set_timeouts()\r\n")); } return(result); } /* * Configure serial port with MacOS config word */ bool XSERDPort::configure(uint16 config) { D(bug("XSERDPort::configure, config=%d\r\n",(int)config)); if (!is_serial) return true; // needed to calculate optimal timeouts uint32 bauds = 57600; uint32 stop_bits = 1; uint32 parity_bits = 0; // Not all of these can be set here anyway. /* mode.fOutxCtsFlow = TRUE; mode.fOutxDsrFlow = FALSE; mode.fDtrControl = DTR_CONTROL_ENABLE; // DTR_CONTROL_HANDSHAKE? mode.fDsrSensitivity = FALSE; // ??? mode.fOutX = FALSE; mode.fInX = FALSE; mode.fTXContinueOnXoff = FALSE; mode.fErrorChar = FALSE; mode.ErrorChar = 0; mode.fNull = FALSE; mode.fRtsControl = 2; // ??? mode.fAbortOnError = FALSE; mode.XonLim = 0x800; mode.XoffLim = 0x200; mode.XonChar = 0x11; mode.XoffChar = 0x13; mode.EofChar = 0; mode.EvtChar = '\0'; */ // Set baud rate switch (config & 0x03ff) { // no baud1800, CBR_14400, CBR_56000, CBR_115200, CBR_128000, CBR_256000 case baud150: mode.BaudRate = CBR_110; bauds = 110; break; case baud300: mode.BaudRate = CBR_300; bauds = 300; break; case baud600: mode.BaudRate = CBR_600; bauds = 600; break; case baud1200: mode.BaudRate = CBR_1200; bauds = 1200; break; case baud1800: return false; case baud2400: mode.BaudRate = CBR_2400; bauds = 2400; break; case baud4800: mode.BaudRate = CBR_4800; bauds = 4800; break; case baud9600: mode.BaudRate = CBR_9600; bauds = 9600; break; case baud19200: mode.BaudRate = CBR_19200; bauds = 19200; break; case baud38400: mode.BaudRate = CBR_38400; bauds = 38400; break; case baud57600: mode.BaudRate = CBR_57600; bauds = 57600; break; default: return false; } // Set number of stop bits switch (config & 0xc000) { case stop10: mode.StopBits = ONESTOPBIT; stop_bits = 1; break; case stop15: mode.StopBits = ONE5STOPBITS; stop_bits = 2; break; case stop20: mode.StopBits = TWOSTOPBITS; stop_bits = 2; break; default: return false; } // Set parity mode switch (config & 0x3000) { case noParity: mode.Parity = NOPARITY; mode.fParity = FALSE; parity_bits = 0; break; case oddParity: mode.Parity = ODDPARITY; mode.fParity = TRUE; parity_bits = 1; break; case evenParity: mode.Parity = EVENPARITY; mode.fParity = TRUE; parity_bits = 1; break; // No MARKPARITY, SPACEPARITY default: return false; } // Set number of data bits switch (config & 0x0c00) { // No data4 case data5: mode.ByteSize = 5; break; case data6: mode.ByteSize = 6; break; case data7: mode.ByteSize = 7; break; case data8: mode.ByteSize = 8; break; default: return false; } D(bug("Interpreted configuration: %d,%d,%d,%d\r\n", bauds, mode.ByteSize, stop_bits, parity_bits )); if(!SetCommState( fd, &mode )) { D(bug("SetCommState failed in configure()\r\n")); return false; } if(!set_timeouts(bauds,parity_bits,stop_bits)) return false; return true; } /* * Set serial handshaking */ void XSERDPort::set_handshake(uint32 s, bool with_dtr) { D(bug(" set_handshake %02x %02x %02x %02x %02x %02x %02x %02x\r\n", ReadMacInt8(s + 0), ReadMacInt8(s + 1), ReadMacInt8(s + 2), ReadMacInt8(s + 3), ReadMacInt8(s + 4), ReadMacInt8(s + 5), ReadMacInt8(s + 6), ReadMacInt8(s + 7))); if (!is_serial) return; if (with_dtr) { mode.fDtrControl = DTR_CONTROL_ENABLE; if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR)) mode.fOutxCtsFlow = TRUE; else mode.fOutxCtsFlow = FALSE; } else { mode.fDtrControl = DTR_CONTROL_DISABLE; if (ReadMacInt8(s + shkFCTS)) mode.fOutxCtsFlow = TRUE; else mode.fOutxCtsFlow = FALSE; } // MIDI: set_handshake 00 00 f4 f5 21 00 00 00 // shkFXOn = 0 // shkFCTS = 0 // shkXOn = f4 // shkXOff = f5 // shkErrs = 21 // shkEvts = 0 // shkFInX = 0 // shkFDTR = 0 if (ReadMacInt8(s + shkXOn) && ReadMacInt8(s + shkXOn)) { mode.fOutX = 1; mode.fInX = 1; mode.XonChar = ReadMacInt8(s + shkXOn); mode.XoffChar = ReadMacInt8(s + shkXOff); } else { mode.fOutX = 0; mode.fInX = 0; } if (ReadMacInt8(s + shkErrs)) { mode.ErrorChar = ReadMacInt8(s + shkErrs); mode.fErrorChar = 1; } else { mode.fErrorChar = 0; } (void)SetCommState( fd, &mode ); // D(bug(" %sware flow control\r\n", mode.c_cflag & CRTSCTS ? "hard" : "soft")); // tcsetattr(fd, TCSANOW, &mode); } /* if mode.fAbortOnError is TRUE, ClearCommError() *MUST* be called after any read or write errors. Otherwise no i/o will occur again These error codes should be translated but the Mac Device Manager error code mnemonics are too cryptic to me. */ int XSERDPort::acknowledge_error(HANDLE h, bool is_read) { DWORD error_state; COMSTAT comstat; int err; // default error code if cannot map correctly err = is_read ? readErr : writErr; if(ClearCommError(h,&error_state,&comstat)) { D(bug("A %s error 0x%X occured.\r\n", is_read ? "read" : "write", error_state)); D(bug("There was %d bytes in input buffer and %d bytes in output buffer.\r\n",(int)comstat.cbInQue,(int)comstat.cbOutQue)); if(error_state & CE_MODE) { D(bug("The requested mode is not supported.\r\n")); } else { if(error_state & CE_BREAK) { D(bug("The hardware detected a break condition.\r\n")); } if(error_state & CE_FRAME) { D(bug("The hardware detected a framing error.\r\n")); } if(error_state & CE_IOE) { D(bug("An I/O error occurred during communications with the device.\r\n")); } if(error_state & CE_RXOVER) { D(bug("An input buffer overflow has occurred.\r\n")); } if(error_state & CE_RXPARITY) { D(bug("The hardware detected a parity error.\r\n")); err = badDCksum; } if(error_state & CE_TXFULL) { D(bug("The application tried to transmit a character, but the output buffer was full.\r\n")); } // Win95 specific errors if(error_state & CE_OVERRUN) { D(bug("A character-buffer overrun has occurred. The next character is lost.\r\n")); if(!is_read) err = wrUnderrun; } // Win95 parallel devices really. if(error_state & CE_DNS) { D(bug("A parallel device is not selected (Windows 95).\r\n")); } if(error_state & CE_OOP) { D(bug("A parallel device signaled that it is out of paper (Windows 95 only).\r\n")); err = unitEmptyErr; } if(error_state & CE_PTO) { D(bug("A time-out occurred on a parallel device (Windows 95).\r\n")); } } } else { D(bug("Failed to resume after %s operation.\r\n",is_read ? "read" : "write")); } return(err); } #if DEBUG static void dump_dirst_bytes( BYTE *buf, int32 actual ) { if(debug_serial != DB_SERIAL_LOUD) return; BYTE b[256]; int32 i, bytes = min(actual,sizeof(b)-3); for (i=0; idevice_name)); for (;;) { // Wait for commands WaitForSingleObject(s->input_signal,INFINITE); if (s->quitting) break; // Execute command void *buf = Mac2HostAddr(ReadMacInt32(s->input_pb + ioBuffer)); uint32 length = ReadMacInt32(s->input_pb + ioReqCount); D(bug("input_func waiting for %ld bytes of data...\r\n", length)); if(length & 0xFFFF0000) { length &= 0x0000FFFF; D(bug("byte count fixed to be %ld...\r\n", length)); } int32 actual; if(s->is_file) { actual = -1; error_code = readErr; } else if(!ReadFile(s->fd, buf, length, (LPDWORD)&actual, 0)) { actual = -1; if(s->is_serial) error_code = acknowledge_error(s->fd,true); else error_code = readErr; } D(bug(" %ld bytes received\r\n", actual)); if(actual > 0) { dump_dirst_bytes( (BYTE*)buf, actual ); } // KillIO called? Then simply return if (s->io_killed) { WriteMacInt16(s->input_pb + ioResult, abortErr); WriteMacInt32(s->input_pb + ioActCount, 0); s->read_pending = s->read_done = false; } else { // Set error code if (actual >= 0) { WriteMacInt32(s->input_pb + ioActCount, actual); WriteMacInt32(s->input_dt + serdtResult, noErr); } else { WriteMacInt32(s->input_pb + ioActCount, 0); WriteMacInt32(s->input_dt + serdtResult, error_code); } // Trigger serial interrupt D(bug(" triggering serial interrupt\r\n")); WriteMacInt32(s->input_dt + serdtDCE, s->input_dce); s->read_done = true; SetInterruptFlag(INTFLAG_SERIAL); TriggerInterrupt(); } } D(bug("XSERDPort::input_func terminating gracefully\r\n")); _endthreadex( 0 ); return(0); } /* * Data output thread */ unsigned int XSERDPort::output_func(void *arg) { XSERDPort *s = (XSERDPort *)arg; int error_code; #if 0 SetThreadPriority( GetCurrentThread(), threads[THREAD_SERIAL_OUT].priority_running ); SetThreadAffinityMask( GetCurrentThread(), threads[THREAD_SERIAL_OUT].affinity_mask ); set_desktop(); #endif D(bug(TEXT("XSERDPort::output_func started for device %s\r\n"),s->device_name)); for (;;) { // Wait for commands WaitForSingleObject(s->output_signal,INFINITE); if (s->quitting) break; // Execute command void *buf = Mac2HostAddr(ReadMacInt32(s->output_pb + ioBuffer)); uint32 length = ReadMacInt32(s->output_pb + ioReqCount); D(bug("output_func transmitting %ld bytes of data...\r\n", length)); if(length & 0xFFFF0000) { length &= 0x0000FFFF; D(bug("byte count fixed to be %ld...\r\n", length)); } int32 actual; if(!WriteFile(s->fd, buf, length, (LPDWORD)&actual, 0)) { actual = -1; if(s->is_serial) error_code = acknowledge_error(s->fd,false); else error_code = writErr; } D(bug(" %ld bytes transmitted\r\n", actual)); if(actual > 0) { dump_dirst_bytes( (BYTE*)buf, actual ); } // KillIO called? Then simply return if (s->io_killed) { WriteMacInt16(s->output_pb + ioResult, abortErr); WriteMacInt32(s->output_pb + ioActCount, 0); s->write_pending = s->write_done = false; } else { // Set error code if (actual >= 0) { WriteMacInt32(s->output_pb + ioActCount, actual); WriteMacInt32(s->output_dt + serdtResult, noErr); } else { WriteMacInt32(s->output_pb + ioActCount, 0); WriteMacInt32(s->output_dt + serdtResult, error_code); } // Trigger serial interrupt D(bug(" triggering serial interrupt\r\n")); WriteMacInt32(s->output_dt + serdtDCE, s->output_dce); s->write_done = true; SetInterruptFlag(INTFLAG_SERIAL); TriggerInterrupt(); } } D(bug("XSERDPort::output_func terminating gracefully\r\n")); _endthreadex( 0 ); return(0); }