mirror of
https://github.com/retro100/dosbox-wii.git
synced 2024-11-04 17:35:10 +01:00
319 lines
8.4 KiB
C++
319 lines
8.4 KiB
C++
/*
|
|
* Copyright (C) 2002-2019 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
#include "dosbox.h"
|
|
|
|
#if C_DIRECTSERIAL
|
|
|
|
#include "serialport.h"
|
|
#include "directserial.h"
|
|
#include "misc_util.h"
|
|
#include "pic.h"
|
|
|
|
#include "libserial.h"
|
|
|
|
/* This is a serial passthrough class. Its amazingly simple to */
|
|
/* write now that the serial ports themselves were abstracted out */
|
|
|
|
CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd)
|
|
:CSerial (id, cmd) {
|
|
InstallationSuccessful = false;
|
|
comport = 0;
|
|
|
|
rx_retry = 0;
|
|
rx_retry_max = 0;
|
|
|
|
std::string tmpstring;
|
|
if(!cmd->FindStringBegin("realport:",tmpstring,false)) return;
|
|
|
|
LOG_MSG ("Serial%d: Opening %s", COMNUMBER, tmpstring.c_str());
|
|
if(!SERIAL_open(tmpstring.c_str(), &comport)) {
|
|
char errorbuffer[256];
|
|
SERIAL_getErrorString(errorbuffer, sizeof(errorbuffer));
|
|
LOG_MSG("Serial%d: Serial Port \"%s\" could not be opened.",
|
|
COMNUMBER, tmpstring.c_str());
|
|
LOG_MSG("%s",errorbuffer);
|
|
return;
|
|
}
|
|
|
|
#if SERIAL_DEBUG
|
|
dbgmsg_poll_block=false;
|
|
dbgmsg_rx_block=false;
|
|
#endif
|
|
|
|
// rxdelay: How many milliseconds to wait before causing an
|
|
// overflow when the application is unresponsive.
|
|
if(getBituSubstring("rxdelay:", &rx_retry_max, cmd)) {
|
|
if(!(rx_retry_max<=10000)) {
|
|
rx_retry_max=0;
|
|
}
|
|
}
|
|
|
|
CSerial::Init_Registers();
|
|
InstallationSuccessful = true;
|
|
rx_state = D_RX_IDLE;
|
|
setEvent(SERIAL_POLLING_EVENT, 1); // millisecond receive tick
|
|
}
|
|
|
|
CDirectSerial::~CDirectSerial () {
|
|
if(comport) SERIAL_close(comport);
|
|
// We do not use own events so we don't have to clear them.
|
|
}
|
|
|
|
// CanReceive: true:UART part has room left
|
|
// doReceive: true:there was really a byte to receive
|
|
// rx_retry is incremented in polling events
|
|
|
|
// in POLLING_EVENT: always add new polling event
|
|
// D_RX_IDLE + CanReceive + doReceive -> D_RX_WAIT , add RX_EVENT
|
|
// D_RX_IDLE + CanReceive + not doReceive -> D_RX_IDLE
|
|
// D_RX_IDLE + not CanReceive -> D_RX_BLOCKED, add RX_EVENT
|
|
|
|
// D_RX_BLOCKED + CanReceive + doReceive -> D_RX_FASTWAIT, rem RX_EVENT
|
|
// rx_retry=0 , add RX_EVENT
|
|
// D_RX_BLOCKED + CanReceive + !doReceive -> D_RX_IDLE, rem RX_EVENT
|
|
// rx_retry=0
|
|
// D_RX_BLOCKED + !CanReceive + doReceive + retry < max -> D_RX_BLOCKED, rx_retry++
|
|
// D_RX_BLOCKED + !CanReceive + doReceive + retry >=max -> rx_retry=0
|
|
|
|
// to be continued...
|
|
|
|
void CDirectSerial::handleUpperEvent(Bit16u type) {
|
|
switch(type) {
|
|
case SERIAL_POLLING_EVENT: {
|
|
setEvent(SERIAL_POLLING_EVENT, 1.0f);
|
|
// update Modem input line states
|
|
switch(rx_state) {
|
|
case D_RX_IDLE:
|
|
if(CanReceiveByte()) {
|
|
if(doReceive()) {
|
|
// a byte was received
|
|
rx_state=D_RX_WAIT;
|
|
setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
|
} // else still idle
|
|
} else {
|
|
#if SERIAL_DEBUG
|
|
if(!dbgmsg_poll_block) {
|
|
log_ser(dbg_aux,"Directserial: block on polling.");
|
|
dbgmsg_poll_block=true;
|
|
}
|
|
#endif
|
|
rx_state=D_RX_BLOCKED;
|
|
// have both delays (1ms + bytetime)
|
|
setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
|
}
|
|
break;
|
|
case D_RX_BLOCKED:
|
|
// one timeout tick
|
|
if(!CanReceiveByte()) {
|
|
rx_retry++;
|
|
if(rx_retry>=rx_retry_max) {
|
|
// it has timed out:
|
|
rx_retry=0;
|
|
removeEvent(SERIAL_RX_EVENT);
|
|
if(doReceive()) {
|
|
// read away everything
|
|
// this will set overrun errors
|
|
while(doReceive());
|
|
rx_state=D_RX_WAIT;
|
|
setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
|
} else {
|
|
// much trouble about nothing
|
|
rx_state=D_RX_IDLE;
|
|
}
|
|
} // else wait further
|
|
} else {
|
|
// good: we can receive again
|
|
#if SERIAL_DEBUG
|
|
dbgmsg_poll_block=false;
|
|
dbgmsg_rx_block=false;
|
|
#endif
|
|
removeEvent(SERIAL_RX_EVENT);
|
|
rx_retry=0;
|
|
if(doReceive()) {
|
|
rx_state=D_RX_FASTWAIT;
|
|
setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
|
|
} else {
|
|
// much trouble about nothing
|
|
rx_state=D_RX_IDLE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case D_RX_WAIT:
|
|
case D_RX_FASTWAIT:
|
|
break;
|
|
}
|
|
updateMSR();
|
|
break;
|
|
}
|
|
case SERIAL_RX_EVENT: {
|
|
switch(rx_state) {
|
|
case D_RX_IDLE:
|
|
LOG_MSG("internal error in directserial");
|
|
break;
|
|
|
|
case D_RX_BLOCKED: // try to receive
|
|
case D_RX_WAIT:
|
|
case D_RX_FASTWAIT:
|
|
if(CanReceiveByte()) {
|
|
// just works or unblocked
|
|
rx_retry=0; // not waiting anymore
|
|
if(doReceive()) {
|
|
if(rx_state==D_RX_WAIT) setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
|
else {
|
|
// maybe unblocked
|
|
rx_state=D_RX_FASTWAIT;
|
|
setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
|
|
}
|
|
} else {
|
|
// didn't receive anything
|
|
rx_state=D_RX_IDLE;
|
|
}
|
|
} else {
|
|
// blocking now or still blocked
|
|
#if SERIAL_DEBUG
|
|
if(rx_state==D_RX_BLOCKED) {
|
|
if(!dbgmsg_rx_block) {
|
|
log_ser(dbg_aux,"Directserial: rx still blocked (retry=%d)",rx_retry);
|
|
dbgmsg_rx_block=true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else log_ser(dbg_aux,"Directserial: block on continued rx (retry=%d).",rx_retry);
|
|
#endif
|
|
setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
|
|
rx_state=D_RX_BLOCKED;
|
|
}
|
|
|
|
break;
|
|
}
|
|
updateMSR();
|
|
break;
|
|
}
|
|
case SERIAL_TX_EVENT: {
|
|
// Maybe echo cirquit works a bit better this way
|
|
if(rx_state==D_RX_IDLE && CanReceiveByte()) {
|
|
if(doReceive()) {
|
|
// a byte was received
|
|
rx_state=D_RX_WAIT;
|
|
setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
|
}
|
|
}
|
|
ByteTransmitted();
|
|
updateMSR();
|
|
break;
|
|
}
|
|
case SERIAL_THR_EVENT: {
|
|
ByteTransmitting();
|
|
setEvent(SERIAL_TX_EVENT,bytetime*1.1f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CDirectSerial::doReceive() {
|
|
int value = SERIAL_getextchar(comport);
|
|
if(value) {
|
|
receiveByteEx((Bit8u)(value&0xff),(Bit8u)((value&0xff00)>>8));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// updatePortConfig is called when emulated app changes the serial port
|
|
// parameters baudrate, stopbits, number of databits, parity.
|
|
void CDirectSerial::updatePortConfig (Bit16u divider, Bit8u lcr) {
|
|
Bit8u parity = 0;
|
|
|
|
switch ((lcr & 0x38)>>3) {
|
|
case 0x1: parity='o'; break;
|
|
case 0x3: parity='e'; break;
|
|
case 0x5: parity='m'; break;
|
|
case 0x7: parity='s'; break;
|
|
default: parity='n'; break;
|
|
}
|
|
|
|
Bit8u bytelength = (lcr & 0x3)+5;
|
|
|
|
// baudrate
|
|
Bitu baudrate;
|
|
if(divider==0) baudrate=115200;
|
|
else baudrate = 115200 / divider;
|
|
|
|
// stopbits
|
|
Bit8u stopbits;
|
|
if (lcr & 0x4) {
|
|
if (bytelength == 5) stopbits = SERIAL_15STOP;
|
|
else stopbits = SERIAL_2STOP;
|
|
} else stopbits = SERIAL_1STOP;
|
|
|
|
if(!SERIAL_setCommParameters(comport, baudrate, parity, stopbits, bytelength)) {
|
|
#if SERIAL_DEBUG
|
|
log_ser(dbg_aux,"Serial port settings not supported by host." );
|
|
#endif
|
|
LOG_MSG ("Serial%d: Desired serial mode not supported (%d,%d,%c,%d)",
|
|
COMNUMBER, baudrate,bytelength,parity,stopbits);
|
|
}
|
|
CDirectSerial::setRTSDTR(getRTS(), getDTR());
|
|
}
|
|
|
|
void CDirectSerial::updateMSR () {
|
|
int new_status = SERIAL_getmodemstatus(comport);
|
|
|
|
setCTS(new_status&SERIAL_CTS? true:false);
|
|
setDSR(new_status&SERIAL_DSR? true:false);
|
|
setRI(new_status&SERIAL_RI? true:false);
|
|
setCD(new_status&SERIAL_CD? true:false);
|
|
}
|
|
|
|
void CDirectSerial::transmitByte (Bit8u val, bool first) {
|
|
if(!SERIAL_sendchar(comport, val))
|
|
LOG_MSG("Serial%d: COM port error: write failed!", COMNUMBER);
|
|
if(first) setEvent(SERIAL_THR_EVENT, bytetime/8);
|
|
else setEvent(SERIAL_TX_EVENT, bytetime);
|
|
}
|
|
|
|
|
|
// setBreak(val) switches break on or off
|
|
void CDirectSerial::setBreak (bool value) {
|
|
SERIAL_setBREAK(comport,value);
|
|
}
|
|
|
|
// updateModemControlLines(mcr) sets DTR and RTS.
|
|
void CDirectSerial::setRTSDTR(bool rts, bool dtr) {
|
|
SERIAL_setRTS(comport,rts);
|
|
SERIAL_setDTR(comport,dtr);
|
|
}
|
|
|
|
void CDirectSerial::setRTS(bool val) {
|
|
SERIAL_setRTS(comport,val);
|
|
}
|
|
|
|
void CDirectSerial::setDTR(bool val) {
|
|
SERIAL_setDTR(comport,val);
|
|
}
|
|
|
|
#endif
|