///////////////////////////////////////////////////////////////////////// // $Id$ ///////////////////////////////////////////////////////////////////////// // // Copyright (C) 2004-2021 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 } } bool serial_raw::ready_transmit() { BX_DEBUG(("ready_transmit returning %d", present)); return present; } 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