88d9ad57bf
(untested - code should be enabled after successful testing).
494 lines
13 KiB
C++
494 lines
13 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2004-2014 The Bochs Project
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library 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
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
//
|
|
|
|
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
|
|
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
|
|
// is used to know when we are exporting symbols and when we are importing.
|
|
#define BX_PLUGGABLE
|
|
|
|
#include "iodev.h"
|
|
|
|
#if USE_RAW_SERIAL
|
|
|
|
#include "serial_raw.h"
|
|
|
|
#define LOG_THIS
|
|
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
DWORD WINAPI RawSerialThread(VOID *this_ptr);
|
|
#endif
|
|
|
|
serial_raw::serial_raw(const char *devname)
|
|
{
|
|
#ifdef WIN32
|
|
char portstr[MAX_PATH];
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
DWORD threadID;
|
|
#endif
|
|
#endif
|
|
|
|
put("serial_raw", "SERRAW");
|
|
#ifdef WIN32
|
|
memset(&dcb, 0, sizeof(DCB));
|
|
dcb.DCBlength = sizeof(DCB);
|
|
dcb.fBinary = 1;
|
|
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
|
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
|
dcb.Parity = NOPARITY;
|
|
dcb.ByteSize = 8;
|
|
dcb.StopBits = ONESTOPBIT;
|
|
dcb.BaudRate = CBR_115200;
|
|
DCBchanged = FALSE;
|
|
if (lstrlen(devname) > 0) {
|
|
wsprintf(portstr, "\\\\.\\%s", devname);
|
|
hCOM = CreateFile(portstr, GENERIC_READ|GENERIC_WRITE, 0, NULL,
|
|
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
|
if (hCOM != INVALID_HANDLE_VALUE) {
|
|
present = 1;
|
|
GetCommModemStatus(hCOM, &MSR_value);
|
|
SetupComm(hCOM, 8192, 2048);
|
|
PurgeComm(hCOM, PURGE_TXABORT | PURGE_RXABORT |
|
|
PURGE_TXCLEAR | PURGE_RXCLEAR);
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
SetCommMask(hCOM, EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR);
|
|
memset(&rx_ovl, 0, sizeof(OVERLAPPED));
|
|
rx_ovl.hEvent = CreateEvent(NULL,TRUE,FALSE,"receive");
|
|
InitializeCriticalSection(&serialCS);
|
|
hRawSerialThread = CreateThread(NULL, 0, RawSerialThread, this, 0, &threadID);
|
|
#endif
|
|
} else {
|
|
present = 0;
|
|
BX_ERROR(("Raw device '%s' not present", devname));
|
|
}
|
|
} else {
|
|
present = 0;
|
|
}
|
|
#else
|
|
present = 0;
|
|
#endif
|
|
set_modem_control(0x00);
|
|
set_break(0);
|
|
rxdata_count = 0;
|
|
}
|
|
|
|
serial_raw::~serial_raw(void)
|
|
{
|
|
if (present) {
|
|
#ifdef WIN32
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
thread_quit = TRUE;
|
|
SetCommMask(hCOM, 0);
|
|
while (thread_active) Sleep(10);
|
|
DeleteCriticalSection(&serialCS);
|
|
CloseHandle(thread_ovl.hEvent);
|
|
CloseHandle(rx_ovl.hEvent);
|
|
CloseHandle(hRawSerialThread);
|
|
#endif
|
|
CloseHandle(hCOM);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void serial_raw::set_baudrate(int rate)
|
|
{
|
|
BX_DEBUG(("set_baudrate %d", rate));
|
|
#ifdef WIN32
|
|
switch (rate) {
|
|
case 110: dcb.BaudRate = CBR_110; break;
|
|
case 300: dcb.BaudRate = CBR_300; break;
|
|
case 600: dcb.BaudRate = CBR_600; break;
|
|
case 1200: dcb.BaudRate = CBR_1200; break;
|
|
case 2400: dcb.BaudRate = CBR_2400; break;
|
|
case 4800: dcb.BaudRate = CBR_4800; break;
|
|
case 9600: dcb.BaudRate = CBR_9600; break;
|
|
case 19200: dcb.BaudRate = CBR_19200; break;
|
|
case 38400: dcb.BaudRate = CBR_38400; break;
|
|
case 57600: dcb.BaudRate = CBR_57600; break;
|
|
case 115200: dcb.BaudRate = CBR_115200; break;
|
|
default: BX_ERROR(("set_baudrate(): unsupported value %d", rate));
|
|
}
|
|
DCBchanged = TRUE;
|
|
#endif
|
|
}
|
|
|
|
void serial_raw::set_data_bits(int val)
|
|
{
|
|
BX_DEBUG(("set data bits (%d)", val));
|
|
#ifdef WIN32
|
|
dcb.ByteSize = val;
|
|
DCBchanged = TRUE;
|
|
#endif
|
|
}
|
|
|
|
void serial_raw::set_stop_bits(int val)
|
|
{
|
|
BX_DEBUG(("set stop bits (%d)", val));
|
|
#ifdef WIN32
|
|
if (val == 1) {
|
|
dcb.StopBits = ONESTOPBIT;
|
|
} if (dcb.ByteSize == 5) {
|
|
dcb.StopBits = ONE5STOPBITS;
|
|
} else {
|
|
dcb.StopBits = TWOSTOPBITS;
|
|
}
|
|
DCBchanged = TRUE;
|
|
#endif
|
|
}
|
|
|
|
void serial_raw::set_parity_mode(int mode)
|
|
{
|
|
BX_DEBUG(("set parity mode %d", mode));
|
|
#ifdef WIN32
|
|
switch (mode) {
|
|
case P_NONE:
|
|
dcb.fParity = FALSE;
|
|
dcb.Parity = NOPARITY;
|
|
break;
|
|
case P_ODD:
|
|
dcb.fParity = TRUE;
|
|
dcb.Parity = ODDPARITY;
|
|
break;
|
|
case P_EVEN:
|
|
dcb.fParity = TRUE;
|
|
dcb.Parity = EVENPARITY;
|
|
break;
|
|
case P_HIGH:
|
|
dcb.fParity = TRUE;
|
|
dcb.Parity = MARKPARITY;
|
|
break;
|
|
case P_LOW:
|
|
dcb.fParity = TRUE;
|
|
dcb.Parity = SPACEPARITY;
|
|
break;
|
|
}
|
|
DCBchanged = TRUE;
|
|
#endif
|
|
}
|
|
|
|
void serial_raw::set_break(int mode)
|
|
{
|
|
BX_DEBUG(("set break %s", mode?"on":"off"));
|
|
#ifdef WIN32
|
|
if (mode) {
|
|
SetCommBreak(hCOM);
|
|
} else {
|
|
ClearCommBreak(hCOM);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void serial_raw::set_modem_control(int ctrl)
|
|
{
|
|
BX_DEBUG(("set modem control 0x%02x", ctrl));
|
|
#ifdef WIN32
|
|
EscapeCommFunction(hCOM, (ctrl & 0x01)?SETDTR:CLRDTR);
|
|
EscapeCommFunction(hCOM, (ctrl & 0x02)?SETRTS:CLRRTS);
|
|
#endif
|
|
}
|
|
|
|
int serial_raw::get_modem_status()
|
|
{
|
|
int status = 0;
|
|
|
|
#ifdef WIN32
|
|
status = MSR_value;
|
|
#endif
|
|
BX_DEBUG(("get modem status returns 0x%02x", status));
|
|
return status;
|
|
}
|
|
|
|
void serial_raw::setup_port()
|
|
{
|
|
#ifdef WIN32
|
|
DWORD DErr;
|
|
COMMTIMEOUTS ctmo;
|
|
|
|
ClearCommError(hCOM, &DErr, NULL);
|
|
PurgeComm(hCOM, PURGE_TXABORT | PURGE_RXABORT |
|
|
PURGE_TXCLEAR | PURGE_RXCLEAR);
|
|
memset(&ctmo, 0, sizeof(ctmo));
|
|
SetCommTimeouts(hCOM, &ctmo);
|
|
SetCommState(hCOM, &dcb);
|
|
rxdata_count = 0;
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
thread_rxdata_count = 0;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void serial_raw::transmit(Bit8u byte)
|
|
{
|
|
#ifdef WIN32
|
|
DWORD DErr, Len2;
|
|
OVERLAPPED tx_ovl;
|
|
#endif
|
|
|
|
BX_DEBUG(("transmit %d", byte));
|
|
if (present) {
|
|
#ifdef WIN32
|
|
if (DCBchanged) {
|
|
setup_port();
|
|
} else {
|
|
ClearCommError(hCOM, &DErr, NULL);
|
|
}
|
|
memset(&tx_ovl, 0, sizeof(OVERLAPPED));
|
|
tx_ovl.hEvent = CreateEvent(NULL,TRUE,TRUE,"transmit");
|
|
if (!WriteFile(hCOM, &byte, 1, &Len2, &tx_ovl)) {
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
if (WaitForSingleObject(tx_ovl.hEvent, 100) == WAIT_OBJECT_0) {
|
|
GetOverlappedResult(hCOM, &tx_ovl, &Len2, FALSE);
|
|
}
|
|
}
|
|
}
|
|
if (Len2 != 1) BX_ERROR(("transmit failed: len = %d", Len2));
|
|
ClearCommError(hCOM, &DErr, NULL);
|
|
CloseHandle(tx_ovl.hEvent);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bx_bool serial_raw::ready_transmit()
|
|
{
|
|
BX_DEBUG(("ready_transmit returning %d", present));
|
|
return present;
|
|
}
|
|
|
|
bx_bool serial_raw::ready_receive()
|
|
{
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
if ((rxdata_count == 0) && (thread_rxdata_count > 0)) {
|
|
SetEvent(thread_ovl.hEvent);
|
|
SetEvent(rx_ovl.hEvent);
|
|
}
|
|
#endif
|
|
BX_DEBUG(("ready_receive returning %d", (rxdata_count > 0)));
|
|
return (rxdata_count > 0);
|
|
}
|
|
|
|
int serial_raw::receive()
|
|
{
|
|
#ifdef WIN32
|
|
int data;
|
|
#endif
|
|
|
|
if (present) {
|
|
#ifdef WIN32
|
|
if (DCBchanged) {
|
|
setup_port();
|
|
}
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
EnterCriticalSection(&serialCS);
|
|
#endif
|
|
data = rxdata_buffer[0];
|
|
if (rxdata_count > 0) {
|
|
memmove(&rxdata_buffer[0], &rxdata_buffer[1], sizeof(Bit16s)*(RX_BUFSIZE-1));
|
|
rxdata_count--;
|
|
}
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
LeaveCriticalSection(&serialCS);
|
|
#endif
|
|
if (data < 0) {
|
|
switch (data) {
|
|
case RAW_EVENT_CTS_ON:
|
|
MSR_value |= 0x10;
|
|
break;
|
|
case RAW_EVENT_CTS_OFF:
|
|
MSR_value &= ~0x10;
|
|
break;
|
|
case RAW_EVENT_DSR_ON:
|
|
MSR_value |= 0x20;
|
|
break;
|
|
case RAW_EVENT_DSR_OFF:
|
|
MSR_value &= ~0x20;
|
|
break;
|
|
case RAW_EVENT_RING_ON:
|
|
MSR_value |= 0x40;
|
|
break;
|
|
case RAW_EVENT_RING_OFF:
|
|
MSR_value &= ~0x40;
|
|
break;
|
|
case RAW_EVENT_RLSD_ON:
|
|
MSR_value |= 0x80;
|
|
break;
|
|
case RAW_EVENT_RLSD_OFF:
|
|
MSR_value &= ~0x80;
|
|
break;
|
|
}
|
|
}
|
|
return data;
|
|
#else
|
|
BX_DEBUG(("receive returning 'A'"));
|
|
return (int)'A';
|
|
#endif
|
|
} else {
|
|
BX_DEBUG(("receive returning 'A'"));
|
|
return (int)'A';
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32_RECEIVE_RAW
|
|
|
|
DWORD WINAPI RawSerialThread(VOID *this_ptr)
|
|
{
|
|
serial_raw *class_ptr = (serial_raw *) this_ptr;
|
|
class_ptr->serial_thread();
|
|
return 0;
|
|
}
|
|
|
|
void serial_raw::serial_thread()
|
|
{
|
|
DWORD DErr, Len2;
|
|
DWORD EvtMask, MSR, Temp;
|
|
char s1[2];
|
|
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
|
|
thread_active = TRUE;
|
|
thread_quit = FALSE;
|
|
memset(&thread_ovl, 0, sizeof(OVERLAPPED));
|
|
thread_ovl.hEvent = CreateEvent(NULL,TRUE,TRUE,"thread");
|
|
thread_rxdata_count = 0;
|
|
while (!thread_quit) {
|
|
if ((rxdata_count == 0) && (thread_rxdata_count > 0)) {
|
|
if (thread_rxdata_count > RX_BUFSIZE) {
|
|
EnterCriticalSection(&serialCS);
|
|
memcpy(&rxdata_buffer[0], &thread_rxdata_buffer[0], sizeof(Bit16s)*RX_BUFSIZE);
|
|
rxdata_count = RX_BUFSIZE;
|
|
LeaveCriticalSection(&serialCS);
|
|
memmove(&thread_rxdata_buffer[0], &thread_rxdata_buffer[RX_BUFSIZE], sizeof(Bit16s)*(thread_rxdata_count-RX_BUFSIZE));
|
|
thread_rxdata_count -= RX_BUFSIZE;
|
|
} else {
|
|
EnterCriticalSection(&serialCS);
|
|
memcpy(&rxdata_buffer[0], &thread_rxdata_buffer[0], sizeof(Bit16s)*thread_rxdata_count);
|
|
rxdata_count = thread_rxdata_count;
|
|
LeaveCriticalSection(&serialCS);
|
|
thread_rxdata_count = 0;
|
|
}
|
|
}
|
|
ClearCommError(hCOM, &DErr, NULL);
|
|
EvtMask = 0;
|
|
if (!WaitCommEvent(hCOM, &EvtMask, &thread_ovl)) {
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
if (WaitForSingleObject(thread_ovl.hEvent, INFINITE) == WAIT_OBJECT_0) {
|
|
GetOverlappedResult(hCOM, &thread_ovl, &Temp, FALSE);
|
|
}
|
|
}
|
|
}
|
|
if (EvtMask & EV_RXCHAR) {
|
|
if (thread_rxdata_count < THREAD_RX_BUFSIZE) {
|
|
do {
|
|
ClearCommError(hCOM, &DErr, NULL);
|
|
if (!ReadFile(hCOM, s1, 1, &Len2, &rx_ovl)) {
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
if (WaitForSingleObject(rx_ovl.hEvent, INFINITE) != WAIT_OBJECT_0) {
|
|
Len2 = 0;
|
|
} else {
|
|
GetOverlappedResult(hCOM, &rx_ovl, &Len2, FALSE);
|
|
}
|
|
} else {
|
|
Len2 = 0;
|
|
}
|
|
}
|
|
if (Len2 > 0) {
|
|
enq_event(s1[0]);
|
|
}
|
|
if ((rxdata_count == 0) && (thread_rxdata_count > 0)) {
|
|
if (thread_rxdata_count > RX_BUFSIZE) {
|
|
EnterCriticalSection(&serialCS);
|
|
memcpy(&rxdata_buffer[0], &thread_rxdata_buffer[0], sizeof(Bit16s)*RX_BUFSIZE);
|
|
rxdata_count = RX_BUFSIZE;
|
|
LeaveCriticalSection(&serialCS);
|
|
memmove(&thread_rxdata_buffer[0], &thread_rxdata_buffer[RX_BUFSIZE], sizeof(Bit16s)*(thread_rxdata_count-RX_BUFSIZE));
|
|
thread_rxdata_count -= RX_BUFSIZE;
|
|
} else {
|
|
EnterCriticalSection(&serialCS);
|
|
memcpy(&rxdata_buffer[0], &thread_rxdata_buffer[0], sizeof(Bit16s)*thread_rxdata_count);
|
|
rxdata_count = thread_rxdata_count;
|
|
LeaveCriticalSection(&serialCS);
|
|
thread_rxdata_count = 0;
|
|
}
|
|
}
|
|
} while ((Len2 != 0) && (thread_rxdata_count < THREAD_RX_BUFSIZE));
|
|
ClearCommError(hCOM, &DErr, NULL);
|
|
}
|
|
}
|
|
if (EvtMask & EV_BREAK) {
|
|
enq_event(RAW_EVENT_BREAK);
|
|
}
|
|
if (EvtMask & EV_ERR) {
|
|
ClearCommError(hCOM, &DErr, NULL);
|
|
if (DErr & CE_FRAME) {
|
|
enq_event(RAW_EVENT_FRAME);
|
|
}
|
|
if (DErr & CE_OVERRUN) {
|
|
enq_event(RAW_EVENT_OVERRUN);
|
|
}
|
|
if (DErr & CE_RXPARITY) {
|
|
enq_event(RAW_EVENT_PARITY);
|
|
}
|
|
}
|
|
if (EvtMask & (EV_CTS | EV_DSR | EV_RING | EV_RLSD)) {
|
|
GetCommModemStatus(hCOM, &MSR);
|
|
}
|
|
if (EvtMask & EV_CTS) {
|
|
if (MSR & MS_CTS_ON) {
|
|
enq_event(RAW_EVENT_CTS_ON);
|
|
} else {
|
|
enq_event(RAW_EVENT_CTS_OFF);
|
|
}
|
|
}
|
|
if (EvtMask & EV_DSR) {
|
|
if (MSR & MS_DSR_ON) {
|
|
enq_event(RAW_EVENT_DSR_ON);
|
|
} else {
|
|
enq_event(RAW_EVENT_DSR_OFF);
|
|
}
|
|
}
|
|
if (EvtMask & EV_RING) {
|
|
if (MSR & MS_RING_ON) {
|
|
enq_event(RAW_EVENT_RING_ON);
|
|
} else {
|
|
enq_event(RAW_EVENT_RING_OFF);
|
|
}
|
|
}
|
|
if (EvtMask & EV_RLSD) {
|
|
if (MSR & MS_RLSD_ON) {
|
|
enq_event(RAW_EVENT_RLSD_ON);
|
|
} else {
|
|
enq_event(RAW_EVENT_RLSD_OFF);
|
|
}
|
|
}
|
|
}
|
|
CloseHandle(thread_ovl.hEvent);
|
|
thread_active = FALSE;
|
|
}
|
|
|
|
void serial_raw::enq_event(Bit16s event)
|
|
{
|
|
if (thread_rxdata_count < THREAD_RX_BUFSIZE) {
|
|
thread_rxdata_buffer[thread_rxdata_count++] = event;
|
|
} else {
|
|
fprintf(stderr, "receive buffer overflow\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endif
|