mirror of
https://github.com/retro100/dosbox-wii.git
synced 2024-11-17 15:49:15 +01:00
419 lines
11 KiB
C++
419 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2002-2006 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.2 2006/02/09 11:47:54 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 <os2.h>
|
|
|
|
/* 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
|