diff --git a/src/hardware/serialport/Makefile.am b/src/hardware/serialport/Makefile.am index dd3633d7..4ae53b23 100644 --- a/src/hardware/serialport/Makefile.am +++ b/src/hardware/serialport/Makefile.am @@ -4,4 +4,5 @@ noinst_LIBRARIES = libserial.a libserial_a_SOURCES = directserial_win32.cpp directserial_win32.h \ serialdummy.cpp serialdummy.h serialport.cpp \ - softmodem.cpp softmodem.h + softmodem.cpp softmodem.h \ + directserial_os2.h directserial_os2.cpp diff --git a/src/hardware/serialport/directserial_os2.cpp b/src/hardware/serialport/directserial_os2.cpp new file mode 100644 index 00000000..5c986154 --- /dev/null +++ b/src/hardware/serialport/directserial_os2.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2002-2005 The DOSBox Team + * + * 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. + */ + +/* $Id: directserial_os2.cpp,v 1.1 2005-11-28 16:18:35 qbix79 Exp $ */ + +#include "dosbox.h" + +#if C_DIRECTSERIAL + + +#if defined(OS2) +#include "serialport.h" +#include "directserial_os2.h" + +// OS/2 related headers +#define INCL_DOSFILEMGR +#define INCL_DOSERRORS +#define INCL_DOSDEVICES +#define INCL_DOSDEVIOCTL +#define INCL_DOSPROCESS +#include + +/* This is a serial passthrough class. Its amazingly simple to */ +/* write now that the serial ports themselves were abstracted out */ + +CDirectSerial::CDirectSerial (IO_ReadHandler * rh, IO_WriteHandler * wh, + TIMER_TickHandler th, Bit16u baseAddr, Bit8u initIrq, + Bit32u initBps, Bit8u bytesize, const char *parity, + Bit8u stopbits,const char *realPort) + :CSerial (rh, wh, th,baseAddr,initIrq, initBps, + bytesize, parity,stopbits) { + InstallationSuccessful = false; + InstallTimerHandler(th); + lastChance = 0; + LOG_MSG ("OS/2 Serial port at %x: Opening %s", base, realPort); + LOG_MSG("Opening OS2 serial port"); + + ULONG ulAction = 0; + APIRET rc = DosOpen((unsigned char*)realPort, &hCom, &ulAction, 0L, FILE_NORMAL, FILE_OPEN, + OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | OPEN_FLAGS_SEQUENTIAL, 0L); + if (rc != NO_ERROR) + { + LOG_MSG ("Serial port \"%s\" could not be opened.", realPort); + if (rc == 2) { + LOG_MSG ("The specified port does not exist."); + } else if (rc == 99) { + LOG_MSG ("The specified port is already in use."); + } else { + LOG_MSG ("OS/2 error %d occurred.", rc); + } + + hCom = 0; + return; + } + + DCBINFO dcb; + ULONG ulParmLen = sizeof(DCBINFO); + rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, 0, 0, 0, &dcb, ulParmLen, &ulParmLen); + if ( rc != NO_ERROR) + { + DosClose(hCom); + hCom = 0; + return; + } + dcb.usWriteTimeout = 0; + dcb.usReadTimeout = 0; //65535; + dcb.fbTimeout |= 6; + rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, ulParmLen, &ulParmLen, 0, 0, 0); + if ( rc != NO_ERROR) + { + DosClose(hCom); + hCom = 0; + return; + } + + CSerial::Init_Registers (initBps, bytesize, parity, stopbits); + InstallationSuccessful = true; + //LOG_MSG("InstSuccess"); +} + +CDirectSerial::~CDirectSerial () { + if (hCom != 0) + DosClose (hCom); +} + +Bitu lastChance; + +void CDirectSerial::RXBufferEmpty () { + ULONG dwRead; + Bit8u chRead; + USHORT errors = 0; + ULONG ulParmLen = sizeof(errors); + + if (lastChance > 0) { + receiveByte (ChanceChar); + lastChance = 0; + } else { + // update RX + if (DosRead (hCom, &chRead, 1, &dwRead) != NO_ERROR) { + if (dwRead != 0) { + //LOG_MSG("UART 0x%x: RX 0x%x", base,chRead); + receiveByte (chRead); + } + } + } + // check for errors + Bit8u errreg = 0; + APIRET rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMERROR, 0, 0, 0, &errors, ulParmLen, &ulParmLen); + if (rc != NO_ERROR && errors) + { + if (errors & 8) { + LOG_MSG ("Serial port at 0x%x: line error: framing error.", base); + errreg |= LSR_FRAMING_ERROR_MASK; + } + if (errors & 4) { + LOG_MSG ("Serial port at 0x%x: line error: parity error.", base); + errreg |= LSR_PARITY_ERROR_MASK; + } + } + errors = 0; + rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMEVENT, 0, 0, 0, &errors, ulParmLen, &ulParmLen); + if (rc != NO_ERROR && errors) + { + if (errors & 6) { + LOG_MSG ("Serial port at 0x%x: line error: break received.", base); + errreg |= LSR_RX_BREAK_MASK; + } + } + if (errreg != 0) + { + receiveError (errreg); + } + +} + +/*****************************************************************************/ +/* updatePortConfig is called when emulated app changes the serial port **/ +/* parameters baudrate, stopbits, number of databits, parity. **/ +/*****************************************************************************/ +void CDirectSerial::updatePortConfig (Bit8u dll, Bit8u dlm, Bit8u lcr) { + Bit8u parity = 0; + Bit8u bytelength = 0; + Bit16u baudrate = 0, baud = 0; + + // baud + baudrate = dlm; + baudrate = baudrate << 8; + baudrate |= dll; + if (baudrate <= 0x1) + baud = 115200; + else if (baudrate <= 0x2) + baud = 57600; + else if (baudrate <= 0x3) + baud = 38400; + else if (baudrate <= 0x6) + baud = 19200; + else if (baudrate <= 0xc) + baud = 9600; + else if (baudrate <= 0x18) + baud = 4800; + else if (baudrate <= 0x30) + baud = 2400; + else if (baudrate <= 0x60) + baud = 1200; + else if (baudrate <= 0xc0) + baud = 600; + else if (baudrate <= 0x180) + baud = 300; + else if (baudrate <= 0x417) + baud = 110; + + // I read that windows can handle nonstandard baudrates: + else + baud = 115200 / baudrate; + +#ifdef SERIALPORT_DEBUGMSG + LOG_MSG ("Serial port at %x: new baud rate: %d", base, dcb.BaudRate); +#endif + + struct { + ULONG baud; + BYTE fraction; + } setbaud; + setbaud.baud = baud; + setbaud.fraction = 0; + ULONG ulParmLen = sizeof(setbaud); + APIRET rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE, &setbaud, ulParmLen, &ulParmLen, 0, 0, 0); + if (rc != NO_ERROR) + { + } + + + struct { + UCHAR data; + UCHAR parity; + UCHAR stop; + } paramline; + + // byte length + bytelength = lcr & 0x3; + bytelength += 5; + paramline.data = bytelength; + + // parity + parity = lcr & 0x38; + parity = parity >> 3; + switch (parity) { + case 0x1: + paramline.parity = 1; + break; + case 0x3: + paramline.parity = 2; + break; + case 0x5: + paramline.parity = 3; + break; + case 0x7: + paramline.parity = 4; + break; + default: + paramline.parity = 0; + break; + } + + // stopbits + if (lcr & 0x4) { + if (bytelength == 5) + paramline.stop = 1; + else + paramline.stop = 2; + } else { + paramline.stop = 0; + } + + + ulParmLen = sizeof(paramline); + rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETLINECTRL, ¶mline, ulParmLen, &ulParmLen, 0, 0, 0); + if ( rc != NO_ERROR) + { + LOG_MSG ("Serial port at 0x%x: API did not like the new values.", base); + } + +} + +void CDirectSerial::updateMSR () { + Bit8u newmsr = 0; + UCHAR dptr = 0; + ULONG ulParmLen = sizeof(dptr); + + APIRET rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, &dptr, ulParmLen, &ulParmLen, 0, 0, 0); + if (rc != NO_ERROR) { +#ifdef SERIALPORT_DEBUGMSG +// LOG_MSG ("Serial port at %x: GetCommModemStatus failed!", base); +#endif + //return; + } + if (dptr & 16) + newmsr |= MSR_CTS_MASK; + if (dptr & 32) + newmsr |= MSR_DSR_MASK; + if (dptr & 64) + newmsr |= MSR_RI_MASK; + if (dptr & 128) + newmsr |= MSR_CD_MASK; + changeMSR (newmsr); +} + +void CDirectSerial::transmitByte (Bit8u val) { + // mean bug: with break = 1, WriteFile will never return. + ULONG bytesWritten = 0; + APIRET rc = DosWrite (hCom, &val, 1, &bytesWritten); + if (rc == NO_ERROR && bytesWritten > 0) { + ByteTransmitted (); + //LOG_MSG("UART 0x%x: TX 0x%x", base,val); + } else { + LOG_MSG ("UART 0x%x: NO BYTE WRITTEN!", base); + } +} + +/*****************************************************************************/ +/* setBreak(val) switches break on or off **/ +/*****************************************************************************/ + +void CDirectSerial::setBreak (bool value) { + //#ifdef SERIALPORT_DEBUGMSG + //LOG_MSG("UART 0x%x: Break toggeled: %d", base, value); + //#endif + USHORT error; + ULONG ulParmLen = sizeof(error); + if (value) + DosDevIOCtl (hCom, IOCTL_ASYNC, ASYNC_SETBREAKON, 0,0,0, &error, ulParmLen, &ulParmLen); + else + DosDevIOCtl (hCom, IOCTL_ASYNC, ASYNC_SETBREAKOFF, 0,0,0, &error, ulParmLen, &ulParmLen); +} + +/*****************************************************************************/ +/* updateModemControlLines(mcr) sets DTR and RTS. **/ +/*****************************************************************************/ +void CDirectSerial::updateModemControlLines ( /*Bit8u mcr */ ) { + bool change = false; + DCBINFO dcb; + ULONG ulParmLen = sizeof(dcb); + + DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, 0, 0, 0, &dcb, ulParmLen, &ulParmLen); + + /*** DTR ***/ + if (CSerial::getDTR ()) { // DTR on + if (dcb.fbCtlHndShake && 3 == 0) { // DTR disabled + dcb.fbCtlHndShake |= 1; + change = true; + } + } else { + if (dcb.fbCtlHndShake && 3 == 1) { // DTR enabled + dcb.fbCtlHndShake &= ~3; + change = true; + } + } + /*** RTS ***/ + if (CSerial::getRTS ()) { // RTS on + if (dcb.fbFlowReplace && 192 == 0) { //RTS disabled + dcb.fbFlowReplace |= 64; + change = true; + } + } else { + if (dcb.fbFlowReplace && 192 == 1) { // RTS enabled + dcb.fbFlowReplace &= ~192; + change = true; + } + } + if (change) + DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, ulParmLen, &ulParmLen, 0, 0, 0); +} + +void CDirectSerial::Timer2(void) { + ULONG dwRead = 0; + USHORT errors = 0; + Bit8u chRead = 0; + ULONG ulParmLen = sizeof(errors); + + + if (lastChance == 0) { // lastChance = 0 + if (CanReceiveByte ()) { + if (DosRead (hCom, &chRead, 1, &dwRead)) { + if (dwRead) + receiveByte (chRead); + } + } else { + if (DosRead (hCom, &chRead, 1, &dwRead)) { + if (dwRead) { + ChanceChar = chRead; + lastChance++; + } + } + } + } else if (lastChance > 10) { + receiveByte (0); // this causes RX Overrun now + lastChance = 0; + // empty serial buffer + dwRead = 1; + while (dwRead > 0) { // throw away bytes in buffer + DosRead (hCom, &chRead, 1, &dwRead); + } + } else { // lastChance>0 // already one waiting + if (CanReceiveByte ()) { // chance used + receiveByte (ChanceChar); + lastChance = 0; + } else + lastChance++; + } + + // check for errors + Bit8u errreg = 0; + APIRET rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMERROR, 0, 0, 0, &errors, ulParmLen, &ulParmLen); + if (rc != NO_ERROR && errors) + { + if (errors & 8) { + LOG_MSG ("Serial port at 0x%x: line error: framing error.", base); + errreg |= LSR_FRAMING_ERROR_MASK; + } + if (errors & 4) { + LOG_MSG ("Serial port at 0x%x: line error: parity error.", base); + errreg |= LSR_PARITY_ERROR_MASK; + } + } + errors = 0; + rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMEVENT, 0, 0, 0, &errors, ulParmLen, &ulParmLen); + if (rc != NO_ERROR && errors) + { + if (errors & 6) { + LOG_MSG ("Serial port at 0x%x: line error: break received.", base); + errreg |= LSR_RX_BREAK_MASK; + } + } + if (errreg != 0) + { + receiveError (errreg); + } + // update Modem input line states + updateMSR (); +} + +#endif +#endif diff --git a/src/hardware/serialport/directserial_os2.h b/src/hardware/serialport/directserial_os2.h new file mode 100644 index 00000000..431c1519 --- /dev/null +++ b/src/hardware/serialport/directserial_os2.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2002-2005 The DOSBox Team + * + * 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. + */ + +/* $Id: directserial_os2.h,v 1.1 2005-11-28 16:18:35 qbix79 Exp $ */ + +// include guard +#ifndef DOSBOX_DIRECTSERIAL_OS2_H +#define DOSBOX_DIRECTSERIAL_OS2_H + +#include "dosbox.h" + +#if C_DIRECTSERIAL +#if defined(OS2) +#define DIRECTSERIAL_AVAILIBLE +#include "serialport.h" +#define INCL_DOSFILEMGR +#define INCL_DOSERRORS +#define INCL_DOSDEVICES +#define INCL_DOSDEVIOCTL +#define INCL_DOSPROCESS +#include + +class CDirectSerial : public CSerial { +public: + HFILE hCom; + BOOL fSuccess; + + CDirectSerial( + IO_ReadHandler* rh, + IO_WriteHandler* wh, + TIMER_TickHandler th, + Bit16u baseAddr, + Bit8u initIrq, + Bit32u initBps, + Bit8u bytesize, + const char *parity, + Bit8u stopbits, + const char * realPort + ); + + + ~CDirectSerial(); + + + Bitu lastChance; // If there is no space for new + // received data, it gets a little chance + Bit8u ChanceChar; + + bool CanRecv(void); + bool CanSend(void); + + bool InstallationSuccessful; // check after constructing. If + // something was wrong, delete it right away. + + void RXBufferEmpty(); + + + void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr); + void updateMSR(); + void transmitByte(Bit8u val); + void setBreak(bool value); + void updateModemControlLines(/*Bit8u mcr*/); + void Timer2(void); + + +}; +#endif // IFDEF + +#endif // C_DIRECTSERIAL +#endif // include guard + diff --git a/src/hardware/serialport/serialport.cpp b/src/hardware/serialport/serialport.cpp index d57f6d4f..ade40bd0 100644 --- a/src/hardware/serialport/serialport.cpp +++ b/src/hardware/serialport/serialport.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: serialport.cpp,v 1.2 2005-11-04 08:53:07 qbix79 Exp $ */ +/* $Id: serialport.cpp,v 1.3 2005-11-28 16:18:35 qbix79 Exp $ */ #include #include @@ -31,6 +31,7 @@ #include "serialport.h" #include "directserial_win32.h" +#include "directserial_os2.h" #include "serialdummy.h" #include "softmodem.h"