2005-06-20 06:11:28 +00:00
|
|
|
/*
|
|
|
|
* serial_windows.cpp - Serial device driver for Win32
|
|
|
|
*
|
2008-01-01 09:40:36 +00:00
|
|
|
* Basilisk II (C) 1997-2008 Christian Bauer
|
2005-06-20 06:11:28 +00:00
|
|
|
*
|
|
|
|
* 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 <ctype.h>
|
|
|
|
#include <process.h>
|
|
|
|
|
|
|
|
#include "main.h"
|
2015-08-06 09:34:24 +00:00
|
|
|
#include "util_windows.h"
|
2017-01-18 12:51:34 +00:00
|
|
|
// somehow util_windows undefines min
|
|
|
|
#define min(x,y) ((x) < (y) ? (x) : (y))
|
2005-06-20 06:11:28 +00:00
|
|
|
#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 );
|
2015-08-06 09:34:24 +00:00
|
|
|
#define SERIAL_LOG_FILE_NAME TEXT("serial.log")
|
2005-06-20 06:11:28 +00:00
|
|
|
#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;
|
|
|
|
|
2015-08-06 09:34:24 +00:00
|
|
|
static void serial_log_open( LPCTSTR path )
|
2005-06-20 06:11:28 +00:00
|
|
|
{
|
|
|
|
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:
|
2015-08-06 09:34:24 +00:00
|
|
|
XSERDPort(LPCTSTR dev, LPCTSTR suffix)
|
2005-06-20 06:11:28 +00:00
|
|
|
{
|
2015-08-06 09:34:24 +00:00
|
|
|
D(bug(TEXT("XSERDPort constructor %s\r\n"), dev));
|
2005-06-20 06:11:28 +00:00
|
|
|
|
|
|
|
read_pending = write_pending = false;
|
|
|
|
|
|
|
|
if(dev)
|
2015-08-06 09:34:24 +00:00
|
|
|
_tcscpy( device_name, dev );
|
2005-06-20 06:11:28 +00:00
|
|
|
else
|
|
|
|
*device_name = 0;
|
2015-08-06 09:34:24 +00:00
|
|
|
_tcsupr(device_name);
|
|
|
|
is_parallel = (_tcsncmp(device_name, TEXT("LPT"), 3) == 0);
|
|
|
|
is_file = (_tcsncmp(device_name, TEXT("FILE"), 4) == 0);
|
2005-06-20 06:11:28 +00:00
|
|
|
if(is_file) {
|
|
|
|
char entry_name[20];
|
2015-08-06 09:34:24 +00:00
|
|
|
_snprintf( entry_name, lengthof(entry_name), "portfile%s", str(suffix).get() );
|
2005-06-20 06:11:28 +00:00
|
|
|
const char *path = PrefsFindString(entry_name);
|
|
|
|
if(path) {
|
2015-08-06 09:34:24 +00:00
|
|
|
_tcscpy( output_file_name, tstr(path).get() );
|
2005-06-20 06:11:28 +00:00
|
|
|
} else {
|
2015-08-06 09:34:24 +00:00
|
|
|
_tcscpy( output_file_name, TEXT("C:\\B2TEMP.OUT") );
|
2005-06-20 06:11:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2015-08-06 09:31:42 +00:00
|
|
|
static unsigned int WINAPI input_func(void *arg);
|
|
|
|
static unsigned int WINAPI output_func(void *arg);
|
2005-06-20 06:11:28 +00:00
|
|
|
static int acknowledge_error(HANDLE h, bool is_read);
|
|
|
|
bool set_timeouts(int bauds, int parity_bits, int stop_bits);
|
|
|
|
|
2015-08-06 09:34:24 +00:00
|
|
|
TCHAR device_name[256];
|
2005-06-20 06:11:28 +00:00
|
|
|
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
|
2015-08-06 09:34:24 +00:00
|
|
|
TCHAR output_file_name[256];
|
2005-06-20 06:11:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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));
|
|
|
|
}
|
2015-08-06 09:34:24 +00:00
|
|
|
the_serd_port[0] = new XSERDPort(tstr(port).get(), TEXT("0"));
|
2005-06-20 06:11:28 +00:00
|
|
|
|
|
|
|
port = PrefsFindString("serialb");
|
|
|
|
if(port) {
|
|
|
|
D(bug("SerialInit serialb=%s\r\n",port));
|
|
|
|
}
|
2015-08-06 09:34:24 +00:00
|
|
|
the_serd_port[1] = new XSERDPort(tstr(port).get(), TEXT("1"));
|
2005-06-20 06:11:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2015-08-06 09:34:24 +00:00
|
|
|
D(bug(TEXT("XSERDPort::open device=%s,config=0x%X\r\n"),device_name,(int)config));
|
2005-06-20 06:11:28 +00:00
|
|
|
|
|
|
|
// 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;
|
2015-08-06 09:34:24 +00:00
|
|
|
D(bug(TEXT("XSERDPort::open failed to open port %s\r\n"),device_name));
|
2005-06-20 06:11:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2017-01-18 12:51:34 +00:00
|
|
|
input_thread_active = 0;
|
2005-06-20 06:11:28 +00:00
|
|
|
}
|
|
|
|
if (output_thread_active) {
|
|
|
|
TerminateThread(output_thread_active,0);
|
|
|
|
CloseHandle(output_signal);
|
2017-01-18 12:51:34 +00:00
|
|
|
output_thread_active = 0;
|
2005-06-20 06:11:28 +00:00
|
|
|
}
|
|
|
|
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);
|
2017-01-18 12:51:34 +00:00
|
|
|
input_thread_active = 0;
|
2005-06-20 06:11:28 +00:00
|
|
|
CloseHandle(input_signal);
|
|
|
|
}
|
|
|
|
if (output_thread_active) {
|
|
|
|
quitting = true;
|
|
|
|
ReleaseSemaphore(output_signal,1,NULL);
|
2017-01-18 12:51:34 +00:00
|
|
|
output_thread_active = 0;
|
2005-06-20 06:11:28 +00:00
|
|
|
// 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; i<bytes; i++) {
|
|
|
|
b[i] = isprint(buf[i]) ? buf[i] : '.';
|
|
|
|
}
|
|
|
|
b[i] = 0;
|
|
|
|
strcat((char*)b,"\r\n");
|
|
|
|
D(bug((char*)b));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define dump_dirst_bytes(b,a) {}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Data input thread
|
|
|
|
*/
|
|
|
|
|
|
|
|
unsigned int XSERDPort::input_func(void *arg)
|
|
|
|
{
|
|
|
|
XSERDPort *s = (XSERDPort *)arg;
|
|
|
|
int error_code;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
SetThreadPriority( GetCurrentThread(), threads[THREAD_SERIAL_IN].priority_running );
|
|
|
|
SetThreadAffinityMask( GetCurrentThread(), threads[THREAD_SERIAL_IN].affinity_mask );
|
|
|
|
set_desktop();
|
|
|
|
#endif
|
|
|
|
|
2015-08-06 09:34:24 +00:00
|
|
|
D(bug(TEXT("XSERDPort::input_func started for device %s\r\n"),s->device_name));
|
2005-06-20 06:11:28 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2015-08-06 09:34:24 +00:00
|
|
|
D(bug(TEXT("XSERDPort::output_func started for device %s\r\n"),s->device_name));
|
2005-06-20 06:11:28 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|