dosbox-wii/src/hardware/serialport/directserial_os2.cpp
2009-05-02 22:18:08 +00:00

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, &paramline, 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