- add 16C550A FIFO support to the serial port
- timing improvements for directserial - the platform specific part of directserial is now combined for less redundancy Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@3472
This commit is contained in:
parent
599de7a67f
commit
8115555a4a
18 changed files with 1834 additions and 1913 deletions
|
@ -16,16 +16,11 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* $Id: serialport.h,v 1.17 2009-05-27 09:15:41 qbix79 Exp $ */
|
||||
/* $Id: serialport.h,v 1.18 2009-09-25 23:40:48 h-a-l-9000 Exp $ */
|
||||
|
||||
#ifndef DOSBOX_SERIALPORT_H
|
||||
#define DOSBOX_SERIALPORT_H
|
||||
|
||||
#define SERIAL_DEBUG 0
|
||||
|
||||
// Uncomment this for a lot of debug messages:
|
||||
//#define LOG_UART
|
||||
|
||||
#ifndef DOSBOX_DOSBOX_H
|
||||
#include "dosbox.h"
|
||||
#endif
|
||||
|
@ -42,12 +37,91 @@
|
|||
#include "programs.h"
|
||||
#endif
|
||||
|
||||
// set this to 1 for serial debugging in release mode
|
||||
#define SERIAL_DBG_FORCED 0
|
||||
|
||||
#if (C_DEBUG || SERIAL_DBG_FORCED)
|
||||
#define SERIAL_DEBUG 1
|
||||
#endif
|
||||
|
||||
#if SERIAL_DEBUG
|
||||
#include "hardware.h"
|
||||
#endif
|
||||
|
||||
// Serial port interface
|
||||
|
||||
class MyFifo {
|
||||
public:
|
||||
MyFifo(Bitu maxsize_) {
|
||||
maxsize=size=maxsize_;
|
||||
pos=used=0;
|
||||
data=new Bit8u[size];
|
||||
}
|
||||
~MyFifo() {
|
||||
delete[] data;
|
||||
}
|
||||
INLINE Bitu getFree(void) {
|
||||
return size-used;
|
||||
}
|
||||
bool isEmpty() {
|
||||
return used==0;
|
||||
}
|
||||
bool isFull() {
|
||||
return (size-used)==0;
|
||||
}
|
||||
|
||||
INLINE Bitu getUsage(void) {
|
||||
return used;
|
||||
}
|
||||
void setSize(Bitu newsize)
|
||||
{
|
||||
size=newsize;
|
||||
pos=used=0;
|
||||
}
|
||||
void clear(void) {
|
||||
pos=used=0;
|
||||
data[0]=0;
|
||||
}
|
||||
|
||||
bool addb(Bit8u _val) {
|
||||
Bitu where=pos+used;
|
||||
if (where>=size) where-=size;
|
||||
if(used>=size) {
|
||||
// overwrite last byte
|
||||
if(where==0) where=size-1;
|
||||
else where--;
|
||||
data[where]=_val;
|
||||
return false;
|
||||
}
|
||||
data[where]=_val;
|
||||
used++;
|
||||
return true;
|
||||
}
|
||||
Bit8u getb() {
|
||||
if (!used) return data[pos];
|
||||
Bitu where=pos;
|
||||
if (++pos>=size) pos-=size;
|
||||
used--;
|
||||
return data[where];
|
||||
}
|
||||
Bit8u getTop() {
|
||||
Bitu where=pos+used;
|
||||
if (where>=size) where-=size;
|
||||
if(used>=size) {
|
||||
if(where==0) where=size-1;
|
||||
else where--;
|
||||
}
|
||||
return data[where];
|
||||
}
|
||||
|
||||
Bit8u probeByte() {
|
||||
return data[pos];
|
||||
}
|
||||
private:
|
||||
Bit8u * data;
|
||||
Bitu maxsize,size,pos,used;
|
||||
};
|
||||
|
||||
class CSerial {
|
||||
public:
|
||||
|
||||
|
@ -58,7 +132,7 @@ public:
|
|||
bool dbg_register;
|
||||
bool dbg_interrupt;
|
||||
bool dbg_aux;
|
||||
|
||||
void log_ser(bool active, char const* format,...);
|
||||
#endif
|
||||
|
||||
static bool getBituSubstring(const char* name,Bitu* data, CommandLine* cmd);
|
||||
|
@ -92,8 +166,9 @@ public:
|
|||
#define SERIAL_RX_EVENT 4
|
||||
#define SERIAL_POLLING_EVENT 5
|
||||
#define SERIAL_THR_EVENT 6
|
||||
#define SERIAL_RX_TIMEOUT_EVENT 7
|
||||
|
||||
#define SERIAL_BASE_EVENT_COUNT 6
|
||||
#define SERIAL_BASE_EVENT_COUNT 7
|
||||
|
||||
#define COMNUMBER idnumber+1
|
||||
|
||||
|
@ -144,6 +219,7 @@ public:
|
|||
|
||||
// If a byte comes from loopback or prepherial, put it in here.
|
||||
void receiveByte(Bit8u data);
|
||||
void receiveByteEx(Bit8u data, Bit8u error);
|
||||
|
||||
// If an error was received, put it here (in LSR register format)
|
||||
void receiveError(Bit8u errorword);
|
||||
|
@ -177,7 +253,7 @@ private:
|
|||
|
||||
DOS_Device* mydosdevice;
|
||||
|
||||
// I used this spec: http://www.exar.com/products/st16c450v420.pdf
|
||||
// I used this spec: st16c450v420.pdf
|
||||
|
||||
void ComputeInterrupts();
|
||||
|
||||
|
@ -191,23 +267,20 @@ private:
|
|||
#define RX_PRIORITY 1 // a byte has been received
|
||||
#define TX_PRIORITY 2 // tx buffer has become empty
|
||||
#define MSR_PRIORITY 8 // CRS, DSR, RI, DCD change
|
||||
#define TIMEOUT_PRIORITY 0x10
|
||||
#define NONE_PRIORITY 0
|
||||
|
||||
Bit8u waiting_interrupts; // these are on, but maybe not enabled
|
||||
|
||||
// 16C450 (no FIFO)
|
||||
// 16C550
|
||||
// read/write name
|
||||
|
||||
Bit16u baud_divider;
|
||||
Bit8u RHR; // r Receive Holding Register, also LSB of Divisor Latch (r/w)
|
||||
#define RHR_OFFSET 0
|
||||
// Data: whole byte
|
||||
|
||||
Bit8u THR; // w Transmit Holding Register
|
||||
#define THR_OFFSET 0
|
||||
// Data: whole byte
|
||||
|
||||
Bit8u IER; // r/w Interrupt Enable Register, also MSB of Divisor Latch
|
||||
#define RHR_OFFSET 0 // r Receive Holding Register, also LSB of Divisor Latch (r/w)
|
||||
// Data: whole byte
|
||||
#define THR_OFFSET 0 // w Transmit Holding Register
|
||||
// Data: whole byte
|
||||
Bit8u IER; // r/w Interrupt Enable Register, also MSB of Divisor Latch
|
||||
#define IER_OFFSET 1
|
||||
|
||||
bool irq_active;
|
||||
|
@ -221,6 +294,7 @@ private:
|
|||
#define ISR_OFFSET 2
|
||||
|
||||
#define ISR_CLEAR_VAL 0x1
|
||||
#define ISR_FIFOTIMEOUT_VAL 0xc
|
||||
#define ISR_ERROR_VAL 0x6
|
||||
#define ISR_RX_VAL 0x4
|
||||
#define ISR_TX_VAL 0x2
|
||||
|
@ -292,6 +366,7 @@ private:
|
|||
Bitu framingErrors;
|
||||
Bitu parityErrors;
|
||||
Bitu overrunErrors;
|
||||
Bitu txOverrunErrors;
|
||||
Bitu overrunIF0;
|
||||
Bitu breakErrors;
|
||||
|
||||
|
@ -329,16 +404,28 @@ private:
|
|||
void transmitLoopbackByte(Bit8u val, bool value);
|
||||
|
||||
// 16C550 (FIFO)
|
||||
// TODO
|
||||
public: // todo remove
|
||||
MyFifo* rxfifo;
|
||||
private:
|
||||
MyFifo* txfifo;
|
||||
MyFifo* errorfifo;
|
||||
Bitu errors_in_fifo;
|
||||
Bitu rx_interrupt_threshold;
|
||||
Bitu fifosize;
|
||||
Bit8u FCR;
|
||||
bool sync_guardtime;
|
||||
#define FIFO_STATUS_ACTIVE 0xc0 // FIFO is active AND works ;)
|
||||
#define FIFO_ERROR 0x80
|
||||
#define FCR_ACTIVATE 0x01
|
||||
#define FCR_CLEAR_RX 0x02
|
||||
#define FCR_CLEAR_TX 0x04
|
||||
#define FCR_OFFSET 2
|
||||
bool fifo_warn;
|
||||
//Bit8u FCR; // FIFO Control Register
|
||||
|
||||
#define FIFO_FLOWCONTROL 0x20
|
||||
};
|
||||
|
||||
extern CSerial* serialports[];
|
||||
const Bit8u serial_defaultirq[4] = { 4, 3, 4, 3 };
|
||||
const Bit16u serial_baseaddr[4] = {0x3f8,0x2f8,0x3e8,0x2e8};
|
||||
const Bit8u serial_defaultirq[] = { 4, 3, 4, 3 };
|
||||
const Bit16u serial_baseaddr[] = {0x3f8,0x2f8,0x3e8,0x2e8};
|
||||
const char* const serial_comname[]={"COM1","COM2","COM3","COM4"};
|
||||
|
||||
// the COM devices
|
||||
|
@ -358,4 +445,3 @@ private:
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -2,9 +2,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
|
|||
|
||||
noinst_LIBRARIES = libserial.a
|
||||
|
||||
libserial_a_SOURCES = directserial_win32.cpp directserial_win32.h \
|
||||
serialdummy.cpp serialdummy.h serialport.cpp \
|
||||
softmodem.cpp softmodem.h misc_util.cpp misc_util.h \
|
||||
directserial_os2.h directserial_os2.cpp \
|
||||
directserial_posix.h directserial_posix.cpp \
|
||||
nullmodem.cpp nullmodem.h
|
||||
libserial_a_SOURCES = directserial.cpp directserial.h \
|
||||
libserial.cpp libserial.h \
|
||||
serialdummy.cpp serialdummy.h serialport.cpp \
|
||||
softmodem.cpp softmodem.h misc_util.cpp misc_util.h \
|
||||
nullmodem.cpp nullmodem.h
|
||||
|
|
319
src/hardware/serialport/directserial.cpp
Normal file
319
src/hardware/serialport/directserial.cpp
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2007 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.cpp,v 1.1 2009-09-25 23:40:47 h-a-l-9000 Exp $ */
|
||||
|
||||
#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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2009 The DOSBox Team
|
||||
* Copyright (C) 2002-2007 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
|
||||
|
@ -16,7 +16,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* $Id: directserial_win32.h,v 1.6 2009-05-27 09:15:41 qbix79 Exp $ */
|
||||
/* $Id: directserial.h,v 1.1 2009-09-25 23:40:47 h-a-l-9000 Exp $ */
|
||||
|
||||
// include guard
|
||||
#ifndef DOSBOX_DIRECTSERIAL_WIN32_H
|
||||
|
@ -25,33 +25,17 @@
|
|||
#include "dosbox.h"
|
||||
|
||||
#if C_DIRECTSERIAL
|
||||
#ifdef WIN32
|
||||
|
||||
|
||||
|
||||
#define DIRECTSERIAL_AVAILIBLE
|
||||
#include "serialport.h"
|
||||
#include <winsock2.h> //To prevent compilation problems with windows.h including winsock.h
|
||||
#include <windows.h>
|
||||
|
||||
#include "libserial.h"
|
||||
|
||||
class CDirectSerial : public CSerial {
|
||||
public:
|
||||
HANDLE hCom;
|
||||
DCB dcb;
|
||||
BOOL fSuccess;
|
||||
|
||||
CDirectSerial(Bitu id, CommandLine* cmd/*const char* configstring*/);
|
||||
CDirectSerial(Bitu id, CommandLine* cmd);
|
||||
~CDirectSerial();
|
||||
bool receiveblock; // It's not a block of data it rather blocks
|
||||
|
||||
Bitu rx_retry; // counter of retries
|
||||
|
||||
Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing
|
||||
// a overrun error.
|
||||
|
||||
|
||||
void CheckErrors();
|
||||
|
||||
void updatePortConfig(Bit16u divider, Bit8u lcr);
|
||||
void updateMSR();
|
||||
void transmitByte(Bit8u val, bool first);
|
||||
|
@ -61,9 +45,27 @@ public:
|
|||
void setRTS(bool val);
|
||||
void setDTR(bool val);
|
||||
void handleUpperEvent(Bit16u type);
|
||||
|
||||
|
||||
private:
|
||||
COMPORT comport;
|
||||
|
||||
Bitu rx_state;
|
||||
#define D_RX_IDLE 0
|
||||
#define D_RX_WAIT 1
|
||||
#define D_RX_BLOCKED 2
|
||||
#define D_RX_FASTWAIT 3
|
||||
|
||||
Bitu rx_retry; // counter of retries (every millisecond)
|
||||
Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing
|
||||
// an overrun error.
|
||||
bool doReceive();
|
||||
|
||||
#if SERIAL_DEBUG
|
||||
bool dbgmsg_poll_block;
|
||||
bool dbgmsg_rx_block;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // WIN32
|
||||
#endif // C_DIRECTSERIAL
|
||||
#endif // include guard
|
|
@ -1,515 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2009 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.5 2009-05-27 09:15:41 qbix79 Exp $ */
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_DIRECTSERIAL
|
||||
|
||||
|
||||
#if defined(OS2)
|
||||
#include "serialport.h"
|
||||
#include "directserial_os2.h"
|
||||
#include "misc_util.h"
|
||||
#include "pic.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 (Bitu id, CommandLine *cmd)
|
||||
: CSerial(id, cmd) {
|
||||
InstallationSuccessful = false;
|
||||
|
||||
|
||||
rx_retry = 0;
|
||||
rx_retry_max = 0;
|
||||
|
||||
std::string tmpstring;
|
||||
|
||||
if (!cmd->FindStringBegin("realport:", tmpstring, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#if SERIAL_DEBUG
|
||||
if (dbg_modemcontrol)
|
||||
{
|
||||
fprintf(debugfp, "%12.3f Port type directserial realport %s\r\n", PIC_FullIndex(), tmpstring.c_str());
|
||||
}
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
const char* tmpchar=tmpstring.c_str();
|
||||
|
||||
LOG_MSG ("Serial%d: Opening %s", COMNUMBER, tmpstring.c_str());
|
||||
|
||||
ULONG ulAction = 0;
|
||||
APIRET rc = DosOpen((unsigned char*)tmpstring.c_str(), &hCom, &ulAction, 0L, FILE_NORMAL, FILE_OPEN,
|
||||
OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | OPEN_FLAGS_SEQUENTIAL, 0L);
|
||||
if (rc != NO_ERROR)
|
||||
{
|
||||
LOG_MSG ("Serial%d: Serial port \"%s\" could not be opened.", COMNUMBER, tmpstring.c_str());
|
||||
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)
|
||||
{
|
||||
LOG_MSG("GetCommState failed with error %d.\n", rc);
|
||||
DosClose(hCom);
|
||||
hCom = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dcb.usWriteTimeout = 0;
|
||||
dcb.usReadTimeout = 0; //65535;
|
||||
dcb.fbCtlHndShake = dcb.fbFlowReplace = 0;
|
||||
dcb.fbTimeout = 6;
|
||||
rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, ulParmLen, &ulParmLen, 0, 0, 0);
|
||||
if ( rc != NO_ERROR)
|
||||
{
|
||||
LOG_MSG("SetDCBInfo failed with error %d.\n", rc);
|
||||
DosClose(hCom);
|
||||
hCom = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
struct {
|
||||
ULONG baud;
|
||||
BYTE fraction;
|
||||
} setbaud;
|
||||
setbaud.baud = 9600;
|
||||
setbaud.fraction = 0;
|
||||
ulParmLen = sizeof(setbaud);
|
||||
rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE, &setbaud, ulParmLen, &ulParmLen, 0, 0, 0);
|
||||
if (rc != NO_ERROR)
|
||||
{
|
||||
LOG_MSG("ExtSetBaudrate failed with error %d.\n", rc);
|
||||
DosClose (hCom);
|
||||
hCom = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
struct {
|
||||
UCHAR data;
|
||||
UCHAR parity;
|
||||
UCHAR stop;
|
||||
} paramline;
|
||||
|
||||
// byte length
|
||||
paramline.data = 8;
|
||||
paramline.parity = 0;
|
||||
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 ("SetLineCtrl failed with error %d.\n", rc);
|
||||
}
|
||||
|
||||
CSerial::Init_Registers();
|
||||
InstallationSuccessful = true;
|
||||
receiveblock = false;
|
||||
|
||||
// Clears comm errors
|
||||
USHORT errors = 0;
|
||||
ulParmLen = sizeof(errors);
|
||||
DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMERROR, 0, 0, 0, &errors, ulParmLen, &ulParmLen);
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
}
|
||||
|
||||
CDirectSerial::~CDirectSerial () {
|
||||
if (hCom != 0)
|
||||
DosClose (hCom);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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;
|
||||
Bit8u bytelength = 0;
|
||||
struct {
|
||||
ULONG baud;
|
||||
BYTE fraction;
|
||||
} setbaud;
|
||||
|
||||
// baud
|
||||
if (divider <= 0x1)
|
||||
setbaud.baud = 115200;
|
||||
else if (divider <= 0x2)
|
||||
setbaud.baud = 57600;
|
||||
else if (divider <= 0x3)
|
||||
setbaud.baud = 38400;
|
||||
else if (divider <= 0x6)
|
||||
setbaud.baud = 19200;
|
||||
else if (divider <= 0xc)
|
||||
setbaud.baud = 9600;
|
||||
else if (divider <= 0x18)
|
||||
setbaud.baud = 4800;
|
||||
else if (divider <= 0x30)
|
||||
setbaud.baud = 2400;
|
||||
else if (divider <= 0x60)
|
||||
setbaud.baud = 1200;
|
||||
else if (divider <= 0xc0)
|
||||
setbaud.baud = 600;
|
||||
else if (divider <= 0x180)
|
||||
setbaud.baud = 300;
|
||||
else if (divider <= 0x417)
|
||||
setbaud.baud = 110;
|
||||
|
||||
// I read that windows can handle nonstandard baudrates:
|
||||
else
|
||||
setbaud.baud = 115200 / divider;
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
LOG_MSG("Serial%d: Desired serial mode not supported (Baud: %d, %d, Error: %d)",
|
||||
COMNUMBER, setbaud.baud, divider, rc);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SERIAL_DEBUG
|
||||
LOG_MSG("_____________________________________________________");
|
||||
LOG_MSG("Serial%d, new baud rate: %d", COMNUMBER, setbaud.baud);
|
||||
LOG_MSG("Serial%d: new bytelen: %d", COMNUMBER, paramline.data);
|
||||
LOG_MSG("Serial%d: new parity: %d", COMNUMBER, paramline.parity);
|
||||
LOG_MSG("Serial%d: new stopbits: %d", COMNUMBER, paramline.stop);
|
||||
#endif
|
||||
|
||||
ulParmLen = sizeof(paramline);
|
||||
rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETLINECTRL, ¶mline, ulParmLen, &ulParmLen, 0, 0, 0);
|
||||
if ( rc != NO_ERROR)
|
||||
{
|
||||
#ifdef SERIAL_DEBUG
|
||||
if (dbg_modemcontrol)
|
||||
{
|
||||
fprintf(debugfp, "%12.3f serial mode not supported: rate=%d, LCR=%x.\r\n", PIC_FullIndex(), setbaud.baud, lcr);
|
||||
}
|
||||
#endif
|
||||
LOG_MSG("Serial%d: Desired serial mode not supported (%d,%d,%d,%d)",
|
||||
COMNUMBER, setbaud.baud, paramline.data, paramline.parity, lcr);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CDirectSerial::updateMSR () {
|
||||
Bit8u newmsr = 0;
|
||||
UCHAR dptr = 0;
|
||||
ULONG ulParmLen = sizeof(dptr);
|
||||
|
||||
APIRET rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, 0, 0, 0, &dptr, ulParmLen, &ulParmLen);
|
||||
if (rc != NO_ERROR) {
|
||||
LOG_MSG ("Serial port at %x: GetModemInput failed with %d !", idnumber, dptr);
|
||||
}
|
||||
setCTS( (dptr & 16) != 0);
|
||||
setDSR( (dptr & 32) != 0);
|
||||
setRI( (dptr & 64) != 0);
|
||||
setCD( (dptr & 128) != 0);
|
||||
}
|
||||
|
||||
void CDirectSerial::transmitByte (Bit8u val, bool first) {
|
||||
ULONG bytesWritten = 0;
|
||||
APIRET rc = DosWrite (hCom, &val, 1, &bytesWritten);
|
||||
if (rc == NO_ERROR && bytesWritten > 0) {
|
||||
//LOG_MSG("UART 0x%x: TX 0x%x", base,val);
|
||||
} else {
|
||||
LOG_MSG ("Serial%d: NO BYTE WRITTEN!", idnumber);
|
||||
}
|
||||
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) {
|
||||
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::setRTSDTR(bool rts, bool dtr)
|
||||
{
|
||||
bool change = false;
|
||||
DCBINFO dcb;
|
||||
ULONG ulParmLen = sizeof(dcb);
|
||||
|
||||
DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, 0, 0, 0, &dcb, ulParmLen, &ulParmLen);
|
||||
|
||||
/*** DTR ***/
|
||||
if (dtr) { // 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 (rts) { // 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::setRTS(bool val)
|
||||
{
|
||||
bool change = false;
|
||||
DCBINFO dcb;
|
||||
ULONG ulParmLen = sizeof(dcb);
|
||||
|
||||
DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, 0, 0, 0, &dcb, ulParmLen, &ulParmLen);
|
||||
|
||||
/*** RTS ***/
|
||||
if (val) { // 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::setDTR(bool val)
|
||||
{
|
||||
bool change = false;
|
||||
DCBINFO dcb;
|
||||
ULONG ulParmLen = sizeof(dcb);
|
||||
|
||||
DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, 0, 0, 0, &dcb, ulParmLen, &ulParmLen);
|
||||
|
||||
/*** DTR ***/
|
||||
if (val) { // 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;
|
||||
}
|
||||
}
|
||||
if (change)
|
||||
DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, ulParmLen, &ulParmLen, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
void CDirectSerial::handleUpperEvent(Bit16u type)
|
||||
{
|
||||
switch(type) {
|
||||
case SERIAL_POLLING_EVENT: {
|
||||
ULONG dwRead = 0;
|
||||
ULONG errors = 0;
|
||||
Bit8u chRead = 0;
|
||||
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
if(!receiveblock) {
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
rx_retry=0;
|
||||
if (DosRead (hCom, &chRead, 1, &dwRead) == NO_ERROR) {
|
||||
if (dwRead) {
|
||||
receiveByte (chRead);
|
||||
setEvent(40, bytetime-0.03f); // receive timing
|
||||
receiveblock=true;
|
||||
}
|
||||
}
|
||||
} else rx_retry++;
|
||||
}
|
||||
// check for errors
|
||||
CheckErrors();
|
||||
// update Modem input line states
|
||||
updateMSR ();
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
// receive time is up
|
||||
ULONG dwRead = 0;
|
||||
Bit8u chRead = 0;
|
||||
receiveblock=false;
|
||||
// check if there is something to receive
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
rx_retry=0;
|
||||
if (DosRead (hCom, &chRead, 1, &dwRead) == NO_ERROR) {
|
||||
if (dwRead) {
|
||||
receiveByte (chRead);
|
||||
setEvent(40, bytetime-0.03f); // receive timing
|
||||
receiveblock=true;
|
||||
}
|
||||
}
|
||||
} else rx_retry++;
|
||||
break;
|
||||
}
|
||||
case SERIAL_TX_EVENT: {
|
||||
ULONG dwRead = 0;
|
||||
Bit8u chRead = 0;
|
||||
if(!receiveblock) {
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
rx_retry=0;
|
||||
if (DosRead (hCom, &chRead, 1, &dwRead) == NO_ERROR) {
|
||||
if (dwRead) {
|
||||
receiveByte (chRead);
|
||||
setEvent(40, bytetime-0.03f); // receive timing
|
||||
receiveblock=true;
|
||||
}
|
||||
}
|
||||
} else rx_retry++;
|
||||
}
|
||||
ByteTransmitted();
|
||||
break;
|
||||
}
|
||||
case SERIAL_THR_EVENT: {
|
||||
ByteTransmitting();
|
||||
setEvent(SERIAL_TX_EVENT,bytetime+0.03f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CDirectSerial::CheckErrors() {
|
||||
|
||||
USHORT errors = 0, event = 0;
|
||||
ULONG ulParmLen = sizeof(errors);
|
||||
DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMEVENT, 0, 0, 0, &event, ulParmLen, &ulParmLen);
|
||||
if (event & (64 + 128) ) { // Break (Bit 6) or Frame or Parity (Bit 7) error
|
||||
Bit8u errreg = 0;
|
||||
if (event & 64) errreg |= LSR_RX_BREAK_MASK;
|
||||
if (event & 128) {
|
||||
DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMERROR, 0, 0, 0, &errors, ulParmLen, &ulParmLen);
|
||||
if (errors & 8) errreg |= LSR_FRAMING_ERROR_MASK;
|
||||
if (errors & 4) errreg |= LSR_PARITY_ERROR_MASK;
|
||||
}
|
||||
receiveError (errreg);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2009 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.5 2009-05-27 09:15:41 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 <os2.h>
|
||||
|
||||
class CDirectSerial : public CSerial {
|
||||
public:
|
||||
HFILE hCom;
|
||||
BOOL fSuccess;
|
||||
|
||||
CDirectSerial(Bitu id, CommandLine* cmd);
|
||||
~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);
|
||||
|
||||
|
||||
//void RXBufferEmpty();
|
||||
bool receiveblock;
|
||||
Bitu rx_retry;
|
||||
Bitu rx_retry_max;
|
||||
|
||||
void CheckErrors();
|
||||
|
||||
void updatePortConfig(Bit16u divider, Bit8u lcr);
|
||||
void updateMSR();
|
||||
void transmitByte(Bit8u val, bool first);
|
||||
void setBreak(bool value);
|
||||
|
||||
void setRTSDTR(bool rts, bool dtr);
|
||||
void setRTS(bool val);
|
||||
void setDTR(bool val);
|
||||
void handleUpperEvent(Bit16u type);
|
||||
|
||||
|
||||
//void updateModemControlLines(/*Bit8u mcr*/);
|
||||
//void Timer2(void);
|
||||
|
||||
|
||||
};
|
||||
#endif // IFDEF
|
||||
|
||||
#endif // C_DIRECTSERIAL
|
||||
#endif // include guard
|
||||
|
|
@ -1,361 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2009 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_posix.cpp,v 1.4 2009-05-27 09:15:41 qbix79 Exp $ */
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_DIRECTSERIAL
|
||||
|
||||
// Posix version
|
||||
#if defined (LINUX) || defined (MACOSX) || defined (BSD)
|
||||
|
||||
#include "serialport.h"
|
||||
#include "directserial_posix.h"
|
||||
#include "pic.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.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;
|
||||
|
||||
rx_retry = 0;
|
||||
rx_retry_max = 0;
|
||||
|
||||
std::string prefix="/dev/";
|
||||
std::string tmpstring;
|
||||
if(!cmd->FindStringBegin("realport:",tmpstring,false)) return;
|
||||
|
||||
#if SERIAL_DEBUG
|
||||
if(dbg_modemcontrol)
|
||||
fprintf(debugfp,"%12.3f Port type directserial realport %s\r\n",
|
||||
PIC_FullIndex(),tmpstring.c_str());
|
||||
#endif
|
||||
|
||||
prefix.append(tmpstring);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
const char* tmpchar=prefix.c_str();
|
||||
|
||||
LOG_MSG ("Serial%d: Opening %s", COMNUMBER, tmpchar);
|
||||
|
||||
fileHandle = open (tmpchar, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||
|
||||
if (fileHandle < 0) {
|
||||
LOG_MSG ("Serial%d: Serial Port \"%s\" could not be opened.",
|
||||
COMNUMBER, tmpchar);
|
||||
if (errno == 2) {
|
||||
LOG_MSG ("The specified port does not exist.");
|
||||
} else if (errno == EBUSY) {
|
||||
LOG_MSG ("The specified port is already in use.");
|
||||
} else {
|
||||
LOG_MSG ("Errno %d occurred.", errno);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int result = tcgetattr(fileHandle, &termInfo);
|
||||
|
||||
|
||||
if (result==-1) {
|
||||
// Handle the error.
|
||||
LOG_MSG ("tcgetattr failed with error %d.\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// save it here to restore in destructor
|
||||
tcgetattr(fileHandle,&backup);
|
||||
|
||||
// initialize the port
|
||||
termInfo.c_cflag = CS8 | CREAD | CLOCAL; // noparity, 1 stopbit
|
||||
termInfo.c_iflag = PARMRK | INPCK;
|
||||
termInfo.c_oflag = 0;
|
||||
termInfo.c_lflag = 0;
|
||||
|
||||
cfsetospeed (&termInfo, B9600);
|
||||
cfsetispeed (&termInfo, B9600);
|
||||
|
||||
termInfo.c_cc[VMIN] = 0;
|
||||
termInfo.c_cc[VTIME] = 0;
|
||||
|
||||
tcflush (fileHandle, TCIFLUSH);
|
||||
tcsetattr (fileHandle, TCSANOW, &termInfo);
|
||||
|
||||
//initialise base class
|
||||
CSerial::Init_Registers();
|
||||
InstallationSuccessful = true;
|
||||
receiveblock=false;
|
||||
|
||||
setEvent(SERIAL_POLLING_EVENT, 1); // millisecond tick
|
||||
}
|
||||
|
||||
CDirectSerial::~CDirectSerial () {
|
||||
if (fileHandle >= 0)
|
||||
{
|
||||
tcsetattr(fileHandle, TCSANOW, &backup);
|
||||
close(fileHandle);
|
||||
}
|
||||
// We do not use own events so we don't have to clear them.
|
||||
}
|
||||
|
||||
void CDirectSerial::handleUpperEvent(Bit16u type) {
|
||||
|
||||
switch(type) {
|
||||
case SERIAL_POLLING_EVENT: {
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
if(!receiveblock) {
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
ReadCharacter();
|
||||
} else rx_retry++;
|
||||
}
|
||||
// check for errors
|
||||
CheckErrors();
|
||||
// update Modem input line states
|
||||
updateMSR ();
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
// receive time is up
|
||||
receiveblock=false;
|
||||
// check if there is something to receive
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
ReadCharacter();
|
||||
} else rx_retry++;
|
||||
break;
|
||||
}
|
||||
case SERIAL_TX_EVENT: {
|
||||
if(!receiveblock) {
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
ReadCharacter();
|
||||
} else rx_retry++;
|
||||
}
|
||||
ByteTransmitted();
|
||||
break;
|
||||
}
|
||||
case SERIAL_THR_EVENT: {
|
||||
ByteTransmitting();
|
||||
setEvent(SERIAL_TX_EVENT,bytetime+0.03f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDirectSerial::ReadCharacter()
|
||||
{
|
||||
Bit8u chRead = 0;
|
||||
int dwRead = 0;
|
||||
rx_retry=0;
|
||||
|
||||
dwRead=read(fileHandle,&chRead,1);
|
||||
if (dwRead==1) {
|
||||
if(chRead==0xff) // error escape
|
||||
{
|
||||
dwRead=read(fileHandle,&chRead,1);
|
||||
if(chRead==0x00) // an error
|
||||
{
|
||||
dwRead=read(fileHandle,&chRead,1);
|
||||
if(chRead==0x0)receiveError(LSR_RX_BREAK_MASK);
|
||||
else receiveError(LSR_PARITY_ERROR_MASK);
|
||||
}
|
||||
}
|
||||
receiveByte (chRead);
|
||||
setEvent(40, bytetime-0.03f); // receive timing
|
||||
receiveblock=true;
|
||||
}
|
||||
}
|
||||
|
||||
void CDirectSerial::CheckErrors() {
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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;
|
||||
Bit8u bytelength = 0;
|
||||
int baudrate=0;
|
||||
|
||||
// baud
|
||||
termInfo.c_cflag = CREAD | CLOCAL;
|
||||
|
||||
if (divider == 0x1) baudrate = B115200;
|
||||
else if (divider == 0x2) baudrate = B57600;
|
||||
else if (divider == 0x3) baudrate = B38400;
|
||||
else if (divider == 0x6) baudrate = B19200;
|
||||
else if (divider == 0xc) baudrate = B9600;
|
||||
else if (divider == 0x18) baudrate = B4800;
|
||||
else if (divider == 0x30) baudrate = B2400;
|
||||
else if (divider == 0x60) baudrate = B1200;
|
||||
else if (divider == 0xc0) baudrate = B600;
|
||||
else if (divider == 0x180) baudrate = B300;
|
||||
else if (divider == 0x417) baudrate = B110;
|
||||
|
||||
// Don't think termios supports nonstandard baudrates
|
||||
else baudrate = B9600;
|
||||
|
||||
// byte length
|
||||
bytelength = lcr & 0x3;
|
||||
bytelength += 5;
|
||||
|
||||
switch (bytelength) {
|
||||
case 5:
|
||||
termInfo.c_cflag |= CS5;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
termInfo.c_cflag |= CS6;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
termInfo.c_cflag |= CS7;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
default:
|
||||
termInfo.c_cflag |= CS8;
|
||||
break;
|
||||
}
|
||||
|
||||
// parity
|
||||
parity = lcr & 0x38;
|
||||
parity >>= 3;
|
||||
switch (parity) {
|
||||
case 0x1:
|
||||
termInfo.c_cflag |= PARODD;
|
||||
termInfo.c_cflag |= PARENB;
|
||||
break;
|
||||
case 0x3:
|
||||
termInfo.c_cflag |= PARENB;
|
||||
break;
|
||||
case 0x5:
|
||||
|
||||
// "works on many systems"
|
||||
#define CMSPAR 010000000000
|
||||
|
||||
termInfo.c_cflag |= PARODD;
|
||||
termInfo.c_cflag |= PARENB;
|
||||
termInfo.c_cflag |= CMSPAR;
|
||||
//LOG_MSG("Serial%d: Mark parity not supported.", COMNUMBER);
|
||||
break;
|
||||
case 0x7:
|
||||
termInfo.c_cflag |= PARENB;
|
||||
termInfo.c_cflag |= CMSPAR;
|
||||
//LOG_MSG("Serial%d: Space parity not supported.", COMNUMBER);
|
||||
break;
|
||||
default: // no parity
|
||||
break;
|
||||
}
|
||||
|
||||
// stopbits
|
||||
if (lcr & 0x4) termInfo.c_cflag |= CSTOPB;
|
||||
|
||||
cfsetospeed (&termInfo, baudrate);
|
||||
cfsetispeed (&termInfo, baudrate);
|
||||
|
||||
int retval = tcsetattr(fileHandle, TCSANOW, &termInfo);
|
||||
|
||||
if(retval==-1)
|
||||
LOG_MSG ("Serial%d: Desired serial mode not supported", COMNUMBER);
|
||||
|
||||
}
|
||||
|
||||
void CDirectSerial::updateMSR () {
|
||||
long flags = 0;
|
||||
ioctl (fileHandle, TIOCMGET, &flags);
|
||||
|
||||
if (flags & TIOCM_CTS) setCTS(true);
|
||||
else setCTS(false);
|
||||
|
||||
if (flags & TIOCM_DSR) setDSR(true);
|
||||
else setDSR(false);
|
||||
|
||||
if (flags & TIOCM_RI) setRI(true);
|
||||
else setRI(false);
|
||||
|
||||
if (flags & TIOCM_CD) setCD(true);
|
||||
else setCD(false);
|
||||
}
|
||||
|
||||
void CDirectSerial::transmitByte (Bit8u val, bool first) {
|
||||
if((LCR&LCR_BREAK_MASK) == 0) {
|
||||
|
||||
int bytesWritten = write(fileHandle, &val, 1);
|
||||
if (bytesWritten != 1)
|
||||
LOG_MSG ("Serial%d: COM port error: write failed!", idnumber);
|
||||
}
|
||||
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) {
|
||||
if (value) ioctl (fileHandle, TIOCSBRK);
|
||||
else ioctl (fileHandle, TIOCCBRK);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* updateModemControlLines(mcr) sets DTR and RTS. **/
|
||||
/*****************************************************************************/
|
||||
void CDirectSerial::setRTSDTR(bool rts, bool dtr) {
|
||||
|
||||
long setflags = 0;
|
||||
long clearflags = 0;
|
||||
|
||||
if(rts) setflags |= TIOCM_RTS;
|
||||
else clearflags |= TIOCM_RTS;
|
||||
|
||||
if(dtr) setflags |= TIOCM_DTR;
|
||||
else clearflags |= TIOCM_DTR;
|
||||
|
||||
if(setflags) ioctl (fileHandle, TIOCMBIS, &setflags);
|
||||
if(clearflags) ioctl (fileHandle, TIOCMBIC, &clearflags);
|
||||
}
|
||||
void CDirectSerial::setRTS(bool val) {
|
||||
long flag = TIOCM_RTS;
|
||||
if(val) ioctl(fileHandle, TIOCMBIS, &flag);
|
||||
else ioctl(fileHandle, TIOCMBIC, &flag);
|
||||
}
|
||||
void CDirectSerial::setDTR(bool val) {
|
||||
long flag = TIOCM_DTR;
|
||||
if(val) ioctl(fileHandle, TIOCMBIS, &flag);
|
||||
else ioctl(fileHandle, TIOCMBIC, &flag);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2009 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_posix.h,v 1.4 2009-05-27 09:15:41 qbix79 Exp $ */
|
||||
|
||||
// include guard
|
||||
#ifndef DOSBOX_DIRECTSERIAL_POSIX_H
|
||||
#define DOSBOX_DIRECTSERIAL_POSIX_H
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_DIRECTSERIAL
|
||||
#if defined (LINUX) || defined (MACOSX) || defined (BSD)
|
||||
|
||||
|
||||
|
||||
#define DIRECTSERIAL_AVAILIBLE
|
||||
#include "serialport.h"
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
class CDirectSerial : public CSerial {
|
||||
public:
|
||||
termios termInfo;
|
||||
termios backup;
|
||||
int fileHandle;
|
||||
|
||||
CDirectSerial(Bitu id, CommandLine* cmd);
|
||||
~CDirectSerial();
|
||||
bool receiveblock; // It's not a block of data it rather blocks
|
||||
|
||||
Bitu rx_retry; // counter of retries
|
||||
|
||||
Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing
|
||||
// a overrun error.
|
||||
|
||||
void ReadCharacter();
|
||||
void CheckErrors();
|
||||
|
||||
void updatePortConfig(Bit16u divider, Bit8u lcr);
|
||||
void updateMSR();
|
||||
void transmitByte(Bit8u val, bool first);
|
||||
void setBreak(bool value);
|
||||
|
||||
void setRTSDTR(bool rts, bool dtr);
|
||||
void setRTS(bool val);
|
||||
void setDTR(bool val);
|
||||
void handleUpperEvent(Bit16u type);
|
||||
|
||||
};
|
||||
|
||||
#endif // WIN32
|
||||
#endif // C_DIRECTSERIAL
|
||||
#endif // include guard
|
|
@ -1,394 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2009 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_win32.cpp,v 1.8 2009-05-27 09:15:41 qbix79 Exp $ */
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
#if C_DIRECTSERIAL
|
||||
|
||||
/* Windows version */
|
||||
#if defined (WIN32)
|
||||
|
||||
#include "serialport.h"
|
||||
#include "directserial_win32.h"
|
||||
#include "misc_util.h"
|
||||
#include "pic.h"
|
||||
|
||||
// Win32 related headers
|
||||
#include <windows.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;
|
||||
hCom = INVALID_HANDLE_VALUE; // else destructor may close an invalid handle
|
||||
rx_retry = 0;
|
||||
rx_retry_max = 0;
|
||||
|
||||
// open the port in NT object space (recommended by Microsoft)
|
||||
// allows the user to open COM10+ and custom port names.
|
||||
std::string prefix="\\\\.\\";
|
||||
std::string tmpstring;
|
||||
if(!cmd->FindStringBegin("realport:",tmpstring,false)) return;
|
||||
|
||||
#if SERIAL_DEBUG
|
||||
if(dbg_modemcontrol)
|
||||
fprintf(debugfp,"%12.3f Port type directserial realport %s\r\n",
|
||||
PIC_FullIndex(),tmpstring.c_str());
|
||||
#endif
|
||||
|
||||
prefix.append(tmpstring);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
const char* tmpchar=prefix.c_str();
|
||||
|
||||
LOG_MSG ("Serial%d: Opening %s", COMNUMBER, tmpstring.c_str());
|
||||
hCom = CreateFile (tmpchar,
|
||||
GENERIC_READ | GENERIC_WRITE, 0,
|
||||
// must be opened with exclusive-access
|
||||
NULL, // no security attributes
|
||||
OPEN_EXISTING, // must use OPEN_EXISTING
|
||||
0, // non overlapped I/O
|
||||
NULL // hTemplate must be NULL for comm devices
|
||||
);
|
||||
|
||||
if (hCom == INVALID_HANDLE_VALUE) {
|
||||
int error = GetLastError ();
|
||||
LOG_MSG ("Serial%d: Serial Port \"%s\" could not be opened.",
|
||||
COMNUMBER, tmpstring.c_str());
|
||||
if (error == 2) {
|
||||
LOG_MSG ("The specified port does not exist.");
|
||||
} else if (error == 5) {
|
||||
LOG_MSG ("The specified port is already in use.");
|
||||
} else {
|
||||
LOG_MSG ("Windows error %d occurred.", error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dcb.DCBlength=sizeof(dcb);
|
||||
fSuccess = GetCommState (hCom, &dcb);
|
||||
|
||||
if (!fSuccess) {
|
||||
// Handle the error.
|
||||
LOG_MSG ("GetCommState failed with error %d.\n", (int)GetLastError ());
|
||||
hCom = INVALID_HANDLE_VALUE;
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize the port
|
||||
dcb.BaudRate=CBR_9600;
|
||||
dcb.fBinary=true;
|
||||
dcb.fParity=true;
|
||||
dcb.fOutxCtsFlow=false;
|
||||
dcb.fOutxDsrFlow=false;
|
||||
dcb.fDtrControl=DTR_CONTROL_DISABLE;
|
||||
dcb.fDsrSensitivity=false;
|
||||
|
||||
dcb.fOutX=false;
|
||||
dcb.fInX=false;
|
||||
dcb.fErrorChar=0;
|
||||
dcb.fNull=false;
|
||||
dcb.fRtsControl=RTS_CONTROL_DISABLE;
|
||||
dcb.fAbortOnError=false;
|
||||
|
||||
dcb.ByteSize=8;
|
||||
dcb.Parity=NOPARITY;
|
||||
dcb.StopBits=ONESTOPBIT;
|
||||
|
||||
fSuccess = SetCommState (hCom, &dcb);
|
||||
|
||||
if (!fSuccess) {
|
||||
// Handle the error.
|
||||
LOG_MSG ("SetCommState failed with error %d.\n", (int)GetLastError ());
|
||||
hCom = INVALID_HANDLE_VALUE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure timeouts to effectively use polling
|
||||
COMMTIMEOUTS ct;
|
||||
ct.ReadIntervalTimeout = MAXDWORD;
|
||||
ct.ReadTotalTimeoutConstant = 0;
|
||||
ct.ReadTotalTimeoutMultiplier = 0;
|
||||
ct.WriteTotalTimeoutConstant = 0;
|
||||
ct.WriteTotalTimeoutMultiplier = 0;
|
||||
SetCommTimeouts (hCom, &ct);
|
||||
|
||||
CSerial::Init_Registers();
|
||||
InstallationSuccessful = true;
|
||||
receiveblock=false;
|
||||
|
||||
ClearCommBreak (hCom);
|
||||
setEvent(SERIAL_POLLING_EVENT, 1); // millisecond tick
|
||||
}
|
||||
|
||||
CDirectSerial::~CDirectSerial () {
|
||||
if (hCom != INVALID_HANDLE_VALUE) CloseHandle (hCom);
|
||||
// We do not use own events so we don't have to clear them.
|
||||
}
|
||||
|
||||
void CDirectSerial::handleUpperEvent(Bit16u type) {
|
||||
|
||||
switch(type) {
|
||||
case SERIAL_POLLING_EVENT: {
|
||||
DWORD dwRead = 0;
|
||||
Bit8u chRead = 0;
|
||||
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
if(!receiveblock) {
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
rx_retry=0;
|
||||
if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) {
|
||||
if (dwRead) {
|
||||
receiveByte (chRead);
|
||||
setEvent(40, bytetime-0.03f); // receive timing
|
||||
receiveblock=true;
|
||||
}
|
||||
}
|
||||
} else rx_retry++;
|
||||
}
|
||||
// check for errors
|
||||
CheckErrors();
|
||||
// update Modem input line states
|
||||
updateMSR ();
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
// receive time is up
|
||||
DWORD dwRead = 0;
|
||||
Bit8u chRead = 0;
|
||||
receiveblock=false;
|
||||
// check if there is something to receive
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
rx_retry=0;
|
||||
if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) {
|
||||
if (dwRead) {
|
||||
receiveByte (chRead);
|
||||
setEvent(40, bytetime-0.03f); // receive timing
|
||||
receiveblock=true;
|
||||
}
|
||||
}
|
||||
} else rx_retry++;
|
||||
break;
|
||||
}
|
||||
case SERIAL_TX_EVENT: {
|
||||
DWORD dwRead = 0;
|
||||
Bit8u chRead = 0;
|
||||
if(!receiveblock) {
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ))
|
||||
{
|
||||
rx_retry=0;
|
||||
if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) {
|
||||
if (dwRead) {
|
||||
receiveByte (chRead);
|
||||
setEvent(40, bytetime-0.03f); // receive timing
|
||||
receiveblock=true;
|
||||
}
|
||||
}
|
||||
} else rx_retry++;
|
||||
}
|
||||
ByteTransmitted();
|
||||
break;
|
||||
}
|
||||
case SERIAL_THR_EVENT: {
|
||||
ByteTransmitting();
|
||||
setEvent(SERIAL_TX_EVENT,bytetime+0.03f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDirectSerial::CheckErrors() {
|
||||
|
||||
DWORD errors=0;
|
||||
// check for errors
|
||||
if (ClearCommError (hCom, &errors, NULL))
|
||||
if (errors & (CE_BREAK | CE_FRAME | CE_RXPARITY)) {
|
||||
Bit8u errreg = 0;
|
||||
if (errors & CE_BREAK) errreg |= LSR_RX_BREAK_MASK;
|
||||
if (errors & CE_FRAME) errreg |= LSR_FRAMING_ERROR_MASK;
|
||||
if (errors & CE_RXPARITY) errreg |= LSR_PARITY_ERROR_MASK;
|
||||
receiveError (errreg);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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;
|
||||
Bit8u bytelength = 0;
|
||||
|
||||
// baud
|
||||
if (divider == 0x1)
|
||||
dcb.BaudRate = CBR_115200;
|
||||
else if (divider == 0x2)
|
||||
dcb.BaudRate = CBR_57600;
|
||||
else if (divider == 0x3)
|
||||
dcb.BaudRate = CBR_38400;
|
||||
else if (divider == 0x6)
|
||||
dcb.BaudRate = CBR_19200;
|
||||
else if (divider == 0xc)
|
||||
dcb.BaudRate = CBR_9600;
|
||||
else if (divider == 0x18)
|
||||
dcb.BaudRate = CBR_4800;
|
||||
else if (divider == 0x30)
|
||||
dcb.BaudRate = CBR_2400;
|
||||
else if (divider == 0x60)
|
||||
dcb.BaudRate = CBR_1200;
|
||||
else if (divider == 0xc0)
|
||||
dcb.BaudRate = CBR_600;
|
||||
else if (divider == 0x180)
|
||||
dcb.BaudRate = CBR_300;
|
||||
else if (divider == 0x417)
|
||||
dcb.BaudRate = CBR_110;
|
||||
|
||||
// I read that windows can handle nonstandard baudrates:
|
||||
else
|
||||
dcb.BaudRate = 115200 / divider;
|
||||
|
||||
// byte length
|
||||
bytelength = lcr & 0x3;
|
||||
bytelength += 5;
|
||||
dcb.ByteSize = bytelength;
|
||||
|
||||
// parity
|
||||
parity = lcr & 0x38;
|
||||
parity = parity >> 3;
|
||||
switch (parity) {
|
||||
case 0x1:
|
||||
dcb.Parity = ODDPARITY;
|
||||
break;
|
||||
case 0x3:
|
||||
dcb.Parity = EVENPARITY;
|
||||
break;
|
||||
case 0x5:
|
||||
dcb.Parity = MARKPARITY;
|
||||
break;
|
||||
case 0x7:
|
||||
dcb.Parity = SPACEPARITY;
|
||||
break;
|
||||
default:
|
||||
dcb.Parity = NOPARITY;
|
||||
break;
|
||||
}
|
||||
|
||||
// stopbits
|
||||
if (lcr & 0x4) {
|
||||
if (bytelength == 5)
|
||||
dcb.StopBits = ONE5STOPBITS;
|
||||
else
|
||||
dcb.StopBits = TWOSTOPBITS;
|
||||
} else {
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
}
|
||||
|
||||
#ifdef SERIALPORT_DEBUGMSG
|
||||
LOG_MSG ("__________________________");
|
||||
LOG_MSG ("Serial%d: new baud rate: %d", COMNUMBER, dcb.BaudRate);
|
||||
LOG_MSG ("Serial%d: new bytelen: %d", COMNUMBER, dcb.ByteSize);
|
||||
LOG_MSG ("Serial%d: new parity: %d", COMNUMBER, dcb.Parity);
|
||||
LOG_MSG ("Serial%d: new stopbits: %d", COMNUMBER, dcb.StopBits);
|
||||
#endif
|
||||
|
||||
if (!SetCommState (hCom, &dcb)) {
|
||||
|
||||
#if SERIAL_DEBUG
|
||||
if(dbg_modemcontrol)
|
||||
fprintf(debugfp,"%12.3f serial mode not supported: rate=%d,LCR=%x.\r\n",
|
||||
PIC_FullIndex(),dcb.BaudRate,lcr);
|
||||
#endif
|
||||
|
||||
LOG_MSG ("Serial%d: Desired serial mode not supported (%d,%d,%d,%d",
|
||||
(Bit32u)dcb.BaudRate,(Bit32u)dcb.ByteSize,
|
||||
(Bit32u)dcb.Parity,(Bit32u)dcb.StopBits, COMNUMBER);
|
||||
}
|
||||
}
|
||||
|
||||
void CDirectSerial::updateMSR () {
|
||||
DWORD dptr = 0;
|
||||
|
||||
if (!GetCommModemStatus (hCom, &dptr)) {
|
||||
#ifdef SERIALPORT_DEBUGMSG
|
||||
// LOG_MSG ("Serial port at %x: GetCommModemStatus failed!", base);
|
||||
#endif
|
||||
//return;
|
||||
}
|
||||
setCTS((dptr & MS_CTS_ON)!=0);
|
||||
setDSR((dptr & MS_DSR_ON)!=0);
|
||||
setRI ((dptr & MS_RING_ON)!=0);
|
||||
setCD((dptr & MS_RLSD_ON)!=0);
|
||||
}
|
||||
|
||||
void CDirectSerial::transmitByte (Bit8u val, bool first) {
|
||||
// mean bug: with break = 1, WriteFile will never return.
|
||||
if((LCR&LCR_BREAK_MASK) == 0) {
|
||||
DWORD bytesWritten = 0;
|
||||
WriteFile (hCom, &val, 1, &bytesWritten, NULL);
|
||||
if (bytesWritten != 1)
|
||||
LOG_MSG ("Serial%d: COM port error: write failed!", idnumber);
|
||||
}
|
||||
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) {
|
||||
if (value) SetCommBreak (hCom);
|
||||
else ClearCommBreak (hCom);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* updateModemControlLines(mcr) sets DTR and RTS. **/
|
||||
/*****************************************************************************/
|
||||
void CDirectSerial::setRTSDTR(bool rts, bool dtr) {
|
||||
if(rts) dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
else dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
if(dtr) dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
else dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
SetCommState (hCom, &dcb);
|
||||
|
||||
}
|
||||
void CDirectSerial::setRTS(bool val) {
|
||||
if(val) dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
else dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
SetCommState (hCom, &dcb);
|
||||
}
|
||||
void CDirectSerial::setDTR(bool val) {
|
||||
if(val) dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
else dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
SetCommState (hCom, &dcb);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
706
src/hardware/serialport/libserial.cpp
Normal file
706
src/hardware/serialport/libserial.cpp
Normal file
|
@ -0,0 +1,706 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2007 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: libserial.cpp,v 1.1 2009-09-25 23:40:47 h-a-l-9000 Exp $ */
|
||||
|
||||
#include "libserial.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct _COMPORT {
|
||||
HANDLE porthandle;
|
||||
bool breakstatus;
|
||||
DCB orig_dcb;
|
||||
};
|
||||
|
||||
bool SERIAL_open(const char* portname, COMPORT* port) {
|
||||
// allocate COMPORT structure
|
||||
COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
|
||||
if(cp == NULL) return false;
|
||||
|
||||
cp->breakstatus=false;
|
||||
|
||||
// open the port in NT object space (recommended by Microsoft)
|
||||
// allows the user to open COM10+ and custom port names.
|
||||
int len = strlen(portname);
|
||||
if(len > 240) {
|
||||
SetLastError(ERROR_BUFFER_OVERFLOW);
|
||||
return false;
|
||||
}
|
||||
char extended_portname[256] = "\\\\.\\";
|
||||
memcpy(extended_portname+4,portname,len+1);
|
||||
|
||||
cp->porthandle = CreateFile (extended_portname,
|
||||
GENERIC_READ | GENERIC_WRITE, 0,
|
||||
// must be opened with exclusive-access
|
||||
NULL, // no security attributes
|
||||
OPEN_EXISTING, // must use OPEN_EXISTING
|
||||
0, // non overlapped I/O
|
||||
NULL // hTemplate must be NULL for comm devices
|
||||
);
|
||||
|
||||
if (cp->porthandle == INVALID_HANDLE_VALUE) goto cleanup_error;
|
||||
|
||||
cp->orig_dcb.DCBlength=sizeof(DCB);
|
||||
|
||||
if(!GetCommState(cp->porthandle, &cp->orig_dcb)) {
|
||||
goto cleanup_error;
|
||||
}
|
||||
|
||||
// configure the port for polling
|
||||
DCB newdcb;
|
||||
memcpy(&newdcb,&cp->orig_dcb,sizeof(DCB));
|
||||
|
||||
newdcb.fBinary=true;
|
||||
newdcb.fParity=true;
|
||||
newdcb.fOutxCtsFlow=false;
|
||||
newdcb.fOutxDsrFlow=false;
|
||||
newdcb.fDtrControl=DTR_CONTROL_DISABLE;
|
||||
newdcb.fDsrSensitivity=false;
|
||||
|
||||
newdcb.fOutX=false;
|
||||
newdcb.fInX=false;
|
||||
newdcb.fErrorChar=0;
|
||||
newdcb.fNull=false;
|
||||
newdcb.fRtsControl=RTS_CONTROL_DISABLE;
|
||||
newdcb.fAbortOnError=false;
|
||||
|
||||
if(!SetCommState(cp->porthandle, &newdcb)) {
|
||||
goto cleanup_error;
|
||||
}
|
||||
|
||||
// Configure timeouts to effectively use polling
|
||||
COMMTIMEOUTS ct;
|
||||
ct.ReadIntervalTimeout = MAXDWORD;
|
||||
ct.ReadTotalTimeoutConstant = 0;
|
||||
ct.ReadTotalTimeoutMultiplier = 0;
|
||||
ct.WriteTotalTimeoutConstant = 0;
|
||||
ct.WriteTotalTimeoutMultiplier = 0;
|
||||
if(!SetCommTimeouts(cp->porthandle, &ct)) {
|
||||
goto cleanup_error;
|
||||
}
|
||||
if(!ClearCommBreak(cp->porthandle)) {
|
||||
// Bluetooth Bluesoleil seems to not implement it
|
||||
//goto cleanup_error;
|
||||
}
|
||||
DWORD errors;
|
||||
if(!ClearCommError(cp->porthandle, &errors, NULL)) {
|
||||
goto cleanup_error;
|
||||
}
|
||||
*port = cp;
|
||||
return true;
|
||||
|
||||
cleanup_error:
|
||||
if (cp->porthandle != INVALID_HANDLE_VALUE) CloseHandle(cp->porthandle);
|
||||
free(cp);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SERIAL_close(COMPORT port) {
|
||||
// restore original DCB, close handle, free the COMPORT struct
|
||||
if (port->porthandle != INVALID_HANDLE_VALUE) {
|
||||
SetCommState(port->porthandle, &port->orig_dcb);
|
||||
CloseHandle(port->porthandle);
|
||||
}
|
||||
free(port);
|
||||
}
|
||||
|
||||
void SERIAL_getErrorString(char* buffer, int length) {
|
||||
int error = GetLastError();
|
||||
if(length < 50) return;
|
||||
memset(buffer,0,length);
|
||||
// get the error message text from the operating system
|
||||
LPVOID sysmessagebuffer;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL,
|
||||
error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &sysmessagebuffer,
|
||||
0,NULL);
|
||||
|
||||
const char* err5text = "The specified port is already in use.\n";
|
||||
const char* err2text = "The specified port does not exist.\n";
|
||||
|
||||
int sysmsg_offset = 0;
|
||||
|
||||
if(error == 5) {
|
||||
sysmsg_offset = strlen(err5text);
|
||||
memcpy(buffer,err5text,sysmsg_offset);
|
||||
|
||||
} else if(error == 2) {
|
||||
sysmsg_offset = strlen(err2text);
|
||||
memcpy(buffer,err2text,sysmsg_offset);
|
||||
}
|
||||
|
||||
if((length - sysmsg_offset - strlen((const char*)sysmessagebuffer)) >= 0)
|
||||
memcpy(buffer + sysmsg_offset, sysmessagebuffer,
|
||||
strlen((const char*)sysmessagebuffer));
|
||||
|
||||
LocalFree(sysmessagebuffer);
|
||||
}
|
||||
|
||||
|
||||
void SERIAL_setDTR(COMPORT port, bool value) {
|
||||
EscapeCommFunction(port->porthandle, value ? SETDTR:CLRDTR);
|
||||
}
|
||||
|
||||
void SERIAL_setRTS(COMPORT port, bool value) {
|
||||
EscapeCommFunction(port->porthandle, value ? SETRTS:CLRRTS);
|
||||
}
|
||||
|
||||
void SERIAL_setBREAK(COMPORT port, bool value) {
|
||||
EscapeCommFunction(port->porthandle, value ? SETBREAK:CLRBREAK);
|
||||
port->breakstatus = value;
|
||||
}
|
||||
|
||||
int SERIAL_getmodemstatus(COMPORT port) {
|
||||
DWORD retval = 0;
|
||||
GetCommModemStatus (port->porthandle, &retval);
|
||||
return (int)retval;
|
||||
}
|
||||
|
||||
bool SERIAL_sendchar(COMPORT port, char data) {
|
||||
DWORD bytesWritten;
|
||||
|
||||
// mean bug: with break = 1, WriteFile will never return.
|
||||
if(port->breakstatus) return true; // true or false?!
|
||||
|
||||
WriteFile (port->porthandle, &data, 1, &bytesWritten, NULL);
|
||||
if(bytesWritten==1) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
// 0-7 char data, higher=flags
|
||||
int SERIAL_getextchar(COMPORT port) {
|
||||
DWORD errors = 0; // errors from API
|
||||
DWORD dwRead = 0; // Number of chars read
|
||||
char chRead;
|
||||
|
||||
int retval = 0;
|
||||
// receive a byte; TODO communicate faliure
|
||||
if (ReadFile (port->porthandle, &chRead, 1, &dwRead, NULL)) {
|
||||
if (dwRead) {
|
||||
// check for errors
|
||||
ClearCommError(port->porthandle, &errors, NULL);
|
||||
// mask bits are identical
|
||||
errors &= CE_BREAK|CE_FRAME|CE_RXPARITY|CE_OVERRUN;
|
||||
retval |= (errors<<8);
|
||||
retval |= (chRead & 0xff);
|
||||
retval |= 0x10000;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool SERIAL_setCommParameters(COMPORT port,
|
||||
int baudrate, char parity, int stopbits, int length) {
|
||||
|
||||
DCB dcb;
|
||||
dcb.DCBlength=sizeof(dcb);
|
||||
GetCommState(port->porthandle,&dcb);
|
||||
|
||||
// parity
|
||||
switch (parity) {
|
||||
case 'n': dcb.Parity = NOPARITY; break;
|
||||
case 'o': dcb.Parity = ODDPARITY; break;
|
||||
case 'e': dcb.Parity = EVENPARITY; break;
|
||||
case 'm': dcb.Parity = MARKPARITY; break;
|
||||
case 's': dcb.Parity = SPACEPARITY; break;
|
||||
default:
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// stopbits
|
||||
switch(stopbits) {
|
||||
case SERIAL_1STOP: dcb.StopBits = ONESTOPBIT; break;
|
||||
case SERIAL_2STOP: dcb.StopBits = TWOSTOPBITS; break;
|
||||
case SERIAL_15STOP: dcb.StopBits = ONE5STOPBITS; break;
|
||||
default:
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// byte length
|
||||
if(length > 8 || length < 5) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
dcb.ByteSize = length;
|
||||
dcb.BaudRate = baudrate;
|
||||
|
||||
if (!SetCommState (port->porthandle, &dcb)) return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (LINUX) || defined (MACOSX)
|
||||
|
||||
#include <memory.h> // strlen
|
||||
#include <malloc.h>
|
||||
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h> // sprinf
|
||||
|
||||
struct _COMPORT {
|
||||
int porthandle;
|
||||
bool breakstatus;
|
||||
termios backup;
|
||||
};
|
||||
|
||||
bool SERIAL_open(const char* portname, COMPORT* port) {
|
||||
int result;
|
||||
// allocate COMPORT structure
|
||||
COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
|
||||
if(cp == NULL) return false;
|
||||
|
||||
cp->breakstatus=false;
|
||||
|
||||
int len = strlen(portname);
|
||||
if(len > 240) {
|
||||
///////////////////////////////////SetLastError(ERROR_BUFFER_OVERFLOW);
|
||||
return false;
|
||||
}
|
||||
char extended_portname[256] = "/dev/";
|
||||
memcpy(extended_portname+5,portname,len);
|
||||
|
||||
cp->porthandle = open (extended_portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||
if (cp->porthandle < 0) goto cleanup_error;
|
||||
|
||||
result = tcgetattr(cp->porthandle,&cp->backup);
|
||||
if (result==-1) goto cleanup_error;
|
||||
|
||||
// get port settings
|
||||
termios termInfo;
|
||||
memcpy(&termInfo,&cp->backup,sizeof(termios));
|
||||
|
||||
// initialize the port
|
||||
termInfo.c_cflag = CS8 | CREAD | CLOCAL; // noparity, 1 stopbit
|
||||
termInfo.c_iflag = PARMRK | INPCK;
|
||||
termInfo.c_oflag = 0;
|
||||
termInfo.c_lflag = 0;
|
||||
termInfo.c_cc[VMIN] = 0;
|
||||
termInfo.c_cc[VTIME] = 0;
|
||||
|
||||
tcflush (cp->porthandle, TCIFLUSH);
|
||||
tcsetattr (cp->porthandle, TCSANOW, &termInfo);
|
||||
|
||||
*port = cp;
|
||||
return true;
|
||||
|
||||
cleanup_error:
|
||||
if (cp->porthandle != 0) close(cp->porthandle);
|
||||
free(cp);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SERIAL_close(COMPORT port) {
|
||||
// restore original termios, close handle, free the COMPORT struct
|
||||
if (port->porthandle >= 0) {
|
||||
tcsetattr(port->porthandle, TCSANOW, &port->backup);
|
||||
close(port->porthandle);
|
||||
}
|
||||
free(port);
|
||||
}
|
||||
|
||||
void SERIAL_getErrorString(char* buffer, int length) {
|
||||
int error = errno;
|
||||
if(length < 50) return;
|
||||
memset(buffer,0,length);
|
||||
// get the error message text from the operating system
|
||||
// TODO (or not)
|
||||
|
||||
const char* err5text = "The specified port is already in use.\n";
|
||||
const char* err2text = "The specified port does not exist.\n";
|
||||
|
||||
int sysmsg_offset = 0;
|
||||
|
||||
if(error == EBUSY) {
|
||||
sysmsg_offset = strlen(err5text);
|
||||
memcpy(buffer,err5text,sysmsg_offset);
|
||||
|
||||
} else if(error == 2) {
|
||||
sysmsg_offset = strlen(err2text);
|
||||
memcpy(buffer,err2text,sysmsg_offset);
|
||||
}
|
||||
|
||||
sprintf(buffer + sysmsg_offset, "System error %d.",error);
|
||||
|
||||
}
|
||||
|
||||
int SERIAL_getmodemstatus(COMPORT port) {
|
||||
long flags = 0;
|
||||
ioctl (port->porthandle, TIOCMGET, &flags);
|
||||
int retval = 0;
|
||||
if (flags & TIOCM_CTS) retval |= SERIAL_CTS;
|
||||
if (flags & TIOCM_DSR) retval |= SERIAL_DSR;
|
||||
if (flags & TIOCM_RI) retval |= SERIAL_RI;
|
||||
if (flags & TIOCM_CD) retval |= SERIAL_CD;
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool SERIAL_sendchar(COMPORT port, char data) {
|
||||
if(port->breakstatus) return true; // true or false?!; Does POSIX need this check?
|
||||
int bytesWritten = write(port->porthandle, &data, 1);
|
||||
if(bytesWritten==1) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
int SERIAL_getextchar(COMPORT port) {
|
||||
unsigned char chRead = 0;
|
||||
int dwRead = 0;
|
||||
unsigned char error = 0;
|
||||
int retval = 0;
|
||||
|
||||
dwRead=read(port->porthandle,&chRead,1);
|
||||
if (dwRead==1) {
|
||||
if(chRead==0xff) // error escape
|
||||
{
|
||||
dwRead=read(port->porthandle,&chRead,1);
|
||||
if(chRead==0x00) // an error
|
||||
{
|
||||
dwRead=read(port->porthandle,&chRead,1);
|
||||
if(chRead==0x0) error=SERIAL_BREAK_ERR;
|
||||
else error=SERIAL_FRAMING_ERR;
|
||||
}
|
||||
}
|
||||
retval |= (error<<8);
|
||||
retval |= chRead;
|
||||
retval |= 0x10000;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool SERIAL_setCommParameters(COMPORT port,
|
||||
int baudrate, char parity, int stopbits, int length) {
|
||||
|
||||
termios termInfo;
|
||||
int result = tcgetattr(port->porthandle, &termInfo);
|
||||
if (result==-1) return false;
|
||||
termInfo.c_cflag = CREAD | CLOCAL;
|
||||
|
||||
// parity
|
||||
// "works on many systems"
|
||||
#define CMSPAR 010000000000
|
||||
switch (parity) {
|
||||
case 'n': break;
|
||||
case 'o': termInfo.c_cflag |= (PARODD | PARENB); break;
|
||||
case 'e': termInfo.c_cflag |= PARENB; break;
|
||||
case 'm': termInfo.c_cflag |= (PARENB | CMSPAR | PARODD); break;
|
||||
case 's': termInfo.c_cflag |= (PARENB | CMSPAR); break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// stopbits
|
||||
switch(stopbits) {
|
||||
case SERIAL_1STOP: break;
|
||||
case SERIAL_2STOP:
|
||||
case SERIAL_15STOP: termInfo.c_cflag |= CSTOPB; break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// byte length
|
||||
if(length > 8 || length < 5) return false;
|
||||
switch (length) {
|
||||
case 5: termInfo.c_cflag |= CS5; break;
|
||||
case 6: termInfo.c_cflag |= CS6; break;
|
||||
case 7: termInfo.c_cflag |= CS7; break;
|
||||
case 8: termInfo.c_cflag |= CS8; break;
|
||||
}
|
||||
// baudrate
|
||||
int posix_baudrate=0;
|
||||
switch(baudrate) {
|
||||
case 115200: posix_baudrate = B115200; break;
|
||||
case 57600: posix_baudrate = B57600; break;
|
||||
case 38400: posix_baudrate = B38400; break;
|
||||
case 19200: posix_baudrate = B19200; break;
|
||||
case 9600: posix_baudrate = B9600; break;
|
||||
case 4800: posix_baudrate = B4800; break;
|
||||
case 2400: posix_baudrate = B2400; break;
|
||||
case 1200: posix_baudrate = B1200; break;
|
||||
case 600: posix_baudrate = B600; break;
|
||||
case 300: posix_baudrate = B300; break;
|
||||
case 110: posix_baudrate = B110; break;
|
||||
default: return false;
|
||||
}
|
||||
cfsetospeed (&termInfo, posix_baudrate);
|
||||
cfsetispeed (&termInfo, posix_baudrate);
|
||||
|
||||
int retval = tcsetattr(port->porthandle, TCSANOW, &termInfo);
|
||||
if(retval==-1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SERIAL_setBREAK(COMPORT port, bool value) {
|
||||
ioctl(port->porthandle, value?TIOCSBRK:TIOCCBRK);
|
||||
}
|
||||
|
||||
void SERIAL_setDTR(COMPORT port, bool value) {
|
||||
long flag = TIOCM_DTR;
|
||||
ioctl(port->porthandle, value?TIOCMBIS:TIOCMBIC, &flag);
|
||||
}
|
||||
|
||||
void SERIAL_setRTS(COMPORT port, bool value) {
|
||||
long flag = TIOCM_RTS;
|
||||
ioctl(port->porthandle, value?TIOCMBIS:TIOCMBIC, &flag);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OS2
|
||||
// OS/2 related headers
|
||||
#define INCL_DOSFILEMGR
|
||||
#define INCL_DOSERRORS
|
||||
#define INCL_DOSDEVICES
|
||||
#define INCL_DOSDEVIOCTL
|
||||
#define INCL_DOSPROCESS
|
||||
#include <os2.h>
|
||||
|
||||
struct _COMPORT {
|
||||
HFILE porthandle;
|
||||
bool breakstatus;
|
||||
DCBINFO backup;
|
||||
};
|
||||
// TODO: THIS IS INCOMPLETE and UNTESTED.
|
||||
|
||||
bool SERIAL_open(const char* portname, COMPORT* port) {
|
||||
// allocate COMPORT structure
|
||||
COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
|
||||
if(cp == NULL) return false;
|
||||
cp->porthandle=0;
|
||||
cp->breakstatus=false;
|
||||
|
||||
ULONG ulAction = 0;
|
||||
APIRET rc = DosOpen(portname, &cp->porthandle,
|
||||
&ulAction, 0L, FILE_NORMAL, FILE_OPEN,
|
||||
OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | OPEN_FLAGS_SEQUENTIAL, 0L);
|
||||
if (rc != NO_ERROR) {
|
||||
goto cleanup_error;
|
||||
}
|
||||
|
||||
ULONG ulParmLen = sizeof(DCBINFO);
|
||||
rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO,
|
||||
0, 0, 0, &cp->orig_dcb, ulParmLen, &ulParmLen);
|
||||
if ( rc != NO_ERROR) {
|
||||
goto cleanup_error;
|
||||
}
|
||||
// configure the port for polling
|
||||
DCBINFO newdcb;
|
||||
memcpy(&newdcb,&cp->orig_dcb,sizeof(DCBINFO));
|
||||
|
||||
newdcb.usWriteTimeout = 0;
|
||||
newdcb.usReadTimeout = 0; //65535;
|
||||
newdcb.fbCtlHndShake = dcb.fbFlowReplace = 0;
|
||||
newdcb.fbTimeout = 6;
|
||||
|
||||
rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO,
|
||||
&newdcb, ulParmLen, &ulParmLen, 0, 0, 0);
|
||||
if ( rc != NO_ERROR) {
|
||||
goto cleanup_error;
|
||||
}
|
||||
|
||||
USHORT errors = 0;
|
||||
ulParmLen = sizeof(errors);
|
||||
rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMERROR,
|
||||
0, 0, 0, &errors, ulParmLen, &ulParmLen);
|
||||
if ( rc != NO_ERROR) {
|
||||
goto cleanup_error;
|
||||
}
|
||||
|
||||
*port = cp;
|
||||
return true;
|
||||
|
||||
cleanup_error:
|
||||
// TODO error string - rc value
|
||||
if (cp->porthandle != 0) CloseHandle(cp->porthandle);
|
||||
free(cp);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SERIAL_getErrorString(char* buffer, int length) {
|
||||
sprintf(buffer, "TODO: error handling is not fun");
|
||||
}
|
||||
void SERIAL_close(COMPORT port) {
|
||||
ULONG ulParmLen = sizeof(DCBINFO);
|
||||
// restore original DCB, close handle, free the COMPORT struct
|
||||
if (port->porthandle != 0) {
|
||||
DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETDCBINFO,
|
||||
&port->orig_dcb, ulParmLen, &ulParmLen, 0, 0, 0);
|
||||
SetCmmState(port->porthandle, &port->orig_dcb);
|
||||
DosClose (port->porthandle);
|
||||
}
|
||||
free(port);
|
||||
}
|
||||
bool SERIAL_sendchar(COMPORT port, char data) {
|
||||
ULONG bytesWritten = 0;
|
||||
if(port->breakstatus) return true; // does OS/2 need this?
|
||||
|
||||
APIRET rc = DosWrite(port->porthandle, &data, 1, &bytesWritten);
|
||||
if (rc == NO_ERROR && bytesWritten > 0) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
void SERIAL_setBREAK(COMPORT port, bool value) {
|
||||
USHORT error;
|
||||
ULONG ulParmLen = sizeof(error);
|
||||
DosDevIOCtl(port->porthandle, IOCTL_ASYNC,
|
||||
value? ASYNC_SETBREAKON:ASYNC_SETBREAKOFF,
|
||||
0,0,0, &error, ulParmLen, &ulParmLen);
|
||||
}
|
||||
|
||||
int SERIAL_getextchar(COMPORT port) {
|
||||
ULONG dwRead = 0; // Number of chars read
|
||||
char chRead;
|
||||
|
||||
int retval = 0;
|
||||
// receive a byte; TODO communicate faliure
|
||||
if (DosRead(port->porthandle, &chRead, 1, &dwRead) == NO_ERROR) {
|
||||
if (dwRead) {
|
||||
// check for errors; will OS/2 clear the error on reading its data?
|
||||
// if yes then this is in wrong order
|
||||
USHORT errors = 0, event = 0;
|
||||
ULONG ulParmLen = sizeof(errors);
|
||||
DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMEVENT,
|
||||
0, 0, 0, &event, ulParmLen, &ulParmLen);
|
||||
if (event & (64 + 128) ) { // Break (Bit 6) or Frame or Parity (Bit 7) error
|
||||
Bit8u errreg = 0;
|
||||
if (event & 64) retval |= SERIAL_BREAK_ERR;
|
||||
if (event & 128) {
|
||||
DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMERROR,
|
||||
0, 0, 0, &errors, ulParmLen, &ulParmLen);
|
||||
if (errors & 8) retval |= SERIAL_FRAMING_ERR;
|
||||
if (errors & 4) retval |= SERIAL_PARITY_ERR;
|
||||
}
|
||||
}
|
||||
retval |= (chRead & 0xff);
|
||||
retval |= 0x10000;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
int SERIAL_getmodemstatus(COMPORT port) {
|
||||
UCHAR dptr = 0;
|
||||
ULONG ulParmLen = sizeof(dptr);
|
||||
DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETMODEMINPUT,
|
||||
0, 0, 0, &dptr, ulParmLen, &ulParmLen);
|
||||
// bits are the same as return value
|
||||
return (int)dptr;
|
||||
}
|
||||
void SERIAL_setDTR(COMPORT port, bool value) {
|
||||
UCHAR masks[2];
|
||||
ULONG ulParmLen = sizeof(masks);
|
||||
if(value) {
|
||||
masks[0]=0x01;
|
||||
masks[1]=0xFF;
|
||||
} else {
|
||||
masks[0]=0x00;
|
||||
masks[1]=0xFE;
|
||||
}
|
||||
DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETMODEMCTRL,
|
||||
0,0,0, &masks, ulParmLen, &ulParmLen);
|
||||
}
|
||||
|
||||
void SERIAL_setRTS(COMPORT port, bool value) {
|
||||
UCHAR masks[2];
|
||||
ULONG ulParmLen = sizeof(masks);
|
||||
if(value) {
|
||||
masks[0]=0x02;
|
||||
masks[1]=0xFF;
|
||||
} else {
|
||||
masks[0]=0x00;
|
||||
masks[1]=0xFD;
|
||||
}
|
||||
DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETMODEMCTRL,
|
||||
0,0,0, &masks, ulParmLen, &ulParmLen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool SERIAL_setCommParameters(COMPORT port,
|
||||
int baudrate, char parity, int stopbits, int length) {
|
||||
// baud
|
||||
struct {
|
||||
ULONG baud;
|
||||
BYTE fraction;
|
||||
} setbaud;
|
||||
|
||||
setbaud.baud = baudrate;
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct {
|
||||
UCHAR data;
|
||||
UCHAR parity;
|
||||
UCHAR stop;
|
||||
} paramline;
|
||||
|
||||
// byte length
|
||||
if(length > 8 || length < 5) {
|
||||
// TODO SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
paramline.data = length;
|
||||
|
||||
// parity
|
||||
switch (parity) {
|
||||
case 'n': paramline.parity = 0; break;
|
||||
case 'o': paramline.parity = 1; break;
|
||||
case 'e': paramline.parity = 2; break;
|
||||
case 'm': paramline.parity = 3; break;
|
||||
case 's': paramline.parity = 4; break;
|
||||
default:
|
||||
// TODO SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
// stopbits
|
||||
switch(stopbits) {
|
||||
case SERIAL_1STOP: paramline.stop = 0; break;
|
||||
case SERIAL_2STOP: paramline.stop = 2; break;
|
||||
case SERIAL_15STOP: paramline.stop = 1; break;
|
||||
default:
|
||||
// TODO SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
// set it
|
||||
ulParmLen = sizeof(paramline);
|
||||
rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETLINECTRL,
|
||||
¶mline, ulParmLen, &ulParmLen, 0, 0, 0);
|
||||
if ( rc != NO_ERROR)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
56
src/hardware/serialport/libserial.h
Normal file
56
src/hardware/serialport/libserial.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2007 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: libserial.h,v 1.1 2009-09-25 23:40:47 h-a-l-9000 Exp $ */
|
||||
|
||||
typedef struct _COMPORT *COMPORT;
|
||||
|
||||
bool SERIAL_open(const char* portname, COMPORT* port);
|
||||
void SERIAL_close(COMPORT port);
|
||||
void SERIAL_getErrorString(char* buffer, int length);
|
||||
|
||||
#define SERIAL_1STOP 1
|
||||
#define SERIAL_2STOP 2
|
||||
#define SERIAL_15STOP 0
|
||||
|
||||
// parity: n, o, e, m, s
|
||||
|
||||
bool SERIAL_setCommParameters(COMPORT port,
|
||||
int baudrate, char parity, int stopbits, int length);
|
||||
|
||||
void SERIAL_setDTR(COMPORT port, bool value);
|
||||
void SERIAL_setRTS(COMPORT port, bool value);
|
||||
void SERIAL_setBREAK(COMPORT port, bool value);
|
||||
|
||||
#define SERIAL_CTS 0x10
|
||||
#define SERIAL_DSR 0x20
|
||||
#define SERIAL_RI 0x40
|
||||
#define SERIAL_CD 0x80
|
||||
|
||||
int SERIAL_getmodemstatus(COMPORT port);
|
||||
bool SERIAL_setmodemcontrol(COMPORT port, int flags);
|
||||
|
||||
bool SERIAL_sendchar(COMPORT port, char data);
|
||||
|
||||
// 0-7 char data, higher=flags
|
||||
#define SERIAL_BREAK_ERR 0x10
|
||||
#define SERIAL_FRAMING_ERR 0x08
|
||||
#define SERIAL_PARITY_ERR 0x04
|
||||
#define SERIAL_OVERRUN_ERR 0x02
|
||||
|
||||
int SERIAL_getextchar(COMPORT port);
|
|
@ -1,3 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2009 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 $ */
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if C_MODEM
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2009 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: misc_util.h,v 1.5 2009-09-25 23:40:47 h-a-l-9000 Exp $ */
|
||||
|
||||
#ifndef SDLNETWRAPPER_H
|
||||
#define SDLNETWRAPPER_H
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* $Id: nullmodem.cpp,v 1.7 2009-05-27 09:15:41 qbix79 Exp $ */
|
||||
/* $Id: nullmodem.cpp,v 1.8 2009-09-25 23:40:47 h-a-l-9000 Exp $ */
|
||||
|
||||
#include "dosbox.h"
|
||||
|
||||
|
@ -36,7 +36,8 @@ CNullModem::CNullModem(Bitu id, CommandLine* cmd):CSerial (id, cmd) {
|
|||
clientport = 0;
|
||||
|
||||
rx_retry = 0;
|
||||
rx_retry_max = 100;
|
||||
rx_retry_max = 20;
|
||||
rx_state=N_RX_DISC;
|
||||
|
||||
tx_gather = 12;
|
||||
|
||||
|
@ -51,7 +52,7 @@ CNullModem::CNullModem(Bitu id, CommandLine* cmd):CSerial (id, cmd) {
|
|||
// usedtr: The nullmodem will
|
||||
// 1) when it is client connect to the server not immediately but
|
||||
// as soon as a modem-aware application is started (DTR is switched on).
|
||||
// 2) only transfer data when DTR is on.
|
||||
// 2) only receive data when DTR is on.
|
||||
if(getBituSubstring("usedtr:", &bool_temp, cmd)) {
|
||||
if(bool_temp==1) {
|
||||
dtrrespect=true;
|
||||
|
@ -105,6 +106,9 @@ CNullModem::CNullModem(Bitu id, CommandLine* cmd):CSerial (id, cmd) {
|
|||
clientsocket = new TCPClientSocket(sock);
|
||||
if(!clientsocket->isopen) {
|
||||
LOG_MSG("Serial%d: Connection failed.",COMNUMBER);
|
||||
#if SERIAL_DEBUG
|
||||
log_ser(dbg_aux,"Nullmodem: Connection failed.");
|
||||
#endif
|
||||
delete clientsocket;
|
||||
clientsocket=0;
|
||||
return;
|
||||
|
@ -115,6 +119,9 @@ CNullModem::CNullModem(Bitu id, CommandLine* cmd):CSerial (id, cmd) {
|
|||
if(!transparent) setRTSDTR(getRTS(), getDTR());
|
||||
|
||||
LOG_MSG("Serial%d: Connected to %s",COMNUMBER,peernamebuf);
|
||||
#if SERIAL_DEBUG
|
||||
log_ser(dbg_aux,"Nullmodem: Connected to %s",peernamebuf);
|
||||
#endif
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
|
||||
CSerial::Init_Registers ();
|
||||
|
@ -204,7 +211,7 @@ Bits CNullModem::readChar() {
|
|||
if(rxchar==0xff) return rxchar; // 0xff 0xff -> 0xff was meant
|
||||
rxchar&0x1? setCTS(true) : setCTS(false);
|
||||
rxchar&0x2? setDSR(true) : setDSR(false);
|
||||
if(rxchar&0x4) receiveError(0x10);
|
||||
if(rxchar&0x4) receiveByteEx(0x0,0x10);
|
||||
return -1; // no "payload" received
|
||||
} else return rxchar;
|
||||
}
|
||||
|
@ -223,14 +230,16 @@ void CNullModem::ClientConnect(){
|
|||
clientsocket->GetRemoteAddressString(peernamebuf);
|
||||
// transmit the line status
|
||||
if(!transparent) setRTSDTR(getRTS(), getDTR());
|
||||
|
||||
rx_state=N_RX_IDLE;
|
||||
LOG_MSG("Serial%d: Connected to %s",idnumber+1,peernamebuf);
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
}
|
||||
|
||||
void CNullModem::Disconnect() {
|
||||
removeEvent(SERIAL_POLLING_EVENT);
|
||||
removeEvent(SERIAL_RX_EVENT);
|
||||
// it was disconnected; free the socket and restart the server socket
|
||||
LOG_MSG("Serial%d: Disconnected.",idnumber+1);
|
||||
LOG_MSG("Serial%d: Disconnected.",COMNUMBER);
|
||||
delete clientsocket;
|
||||
clientsocket=0;
|
||||
setDSR(false);
|
||||
|
@ -249,48 +258,115 @@ void CNullModem::handleUpperEvent(Bit16u type) {
|
|||
case SERIAL_POLLING_EVENT: {
|
||||
// periodically check if new data arrived, disconnect
|
||||
// if required. Add it back.
|
||||
if(!receiveblock && clientsocket) {
|
||||
if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max )
|
||||
&&(!dtrrespect | (dtrrespect&& getDTR()) )) {
|
||||
rx_retry=0;
|
||||
Bits rxchar = readChar();
|
||||
if(rxchar>=0) {
|
||||
receiveblock=true;
|
||||
setEvent(SERIAL_RX_EVENT, bytetime-0.01f);
|
||||
receiveByte((Bit8u)rxchar);
|
||||
setEvent(SERIAL_POLLING_EVENT, 1.0f);
|
||||
// update Modem input line states
|
||||
updateMSR();
|
||||
switch(rx_state) {
|
||||
case N_RX_IDLE:
|
||||
if(CanReceiveByte()) {
|
||||
if(doReceive()) {
|
||||
// a byte was received
|
||||
rx_state=N_RX_WAIT;
|
||||
setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
||||
} // else still idle
|
||||
} else {
|
||||
#if SERIAL_DEBUG
|
||||
log_ser(dbg_aux,"Nullmodem: block on polling.");
|
||||
#endif
|
||||
rx_state=N_RX_BLOCKED;
|
||||
// have both delays (1ms + bytetime)
|
||||
setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
||||
}
|
||||
else if(rxchar==-2) Disconnect();
|
||||
else setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
} else {
|
||||
rx_retry++;
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N_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
|
||||
while(doReceive());
|
||||
rx_state=N_RX_WAIT;
|
||||
setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
||||
} else {
|
||||
// much trouble about nothing
|
||||
rx_state=N_RX_IDLE;
|
||||
#if SERIAL_DEBUG
|
||||
log_ser(dbg_aux,"Nullmodem: unblock due to no more data",rx_retry);
|
||||
#endif
|
||||
}
|
||||
} // else wait further
|
||||
} else {
|
||||
// good: we can receive again
|
||||
removeEvent(SERIAL_RX_EVENT);
|
||||
rx_retry=0;
|
||||
if(doReceive()) {
|
||||
rx_state=N_RX_FASTWAIT;
|
||||
setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
|
||||
} else {
|
||||
// much trouble about nothing
|
||||
rx_state=N_RX_IDLE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case N_RX_WAIT:
|
||||
case N_RX_FASTWAIT:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SERIAL_RX_EVENT: {
|
||||
// receive time is up, try to receive another byte.
|
||||
receiveblock=false;
|
||||
|
||||
if((!(LSR&LSR_RX_DATA_READY_MASK) || rx_retry>=rx_retry_max)
|
||||
&&(!dtrrespect | (dtrrespect&& getDTR()) )
|
||||
) {
|
||||
rx_retry=0;
|
||||
Bits rxchar = readChar();
|
||||
if(rxchar>=0) {
|
||||
receiveblock=true;
|
||||
setEvent(SERIAL_RX_EVENT, bytetime-0.01f);
|
||||
receiveByte((Bit8u)rxchar);
|
||||
}
|
||||
else if(rxchar==-2) Disconnect();
|
||||
else setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
} else {
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
rx_retry++;
|
||||
switch(rx_state) {
|
||||
case N_RX_IDLE:
|
||||
LOG_MSG("internal error in nullmodem");
|
||||
break;
|
||||
|
||||
case N_RX_BLOCKED: // try to receive
|
||||
case N_RX_WAIT:
|
||||
case N_RX_FASTWAIT:
|
||||
if(CanReceiveByte()) {
|
||||
// just works or unblocked
|
||||
if(doReceive()) {
|
||||
rx_retry=0; // not waiting anymore
|
||||
if(rx_state==N_RX_WAIT) setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
||||
else {
|
||||
// maybe unblocked
|
||||
rx_state=N_RX_FASTWAIT;
|
||||
setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
|
||||
}
|
||||
} else {
|
||||
// didn't receive anything
|
||||
rx_retry=0;
|
||||
rx_state=N_RX_IDLE;
|
||||
}
|
||||
} else {
|
||||
// blocking now or still blocked
|
||||
#if SERIAL_DEBUG
|
||||
if(rx_state==N_RX_BLOCKED)
|
||||
log_ser(dbg_aux,"Nullmodem: rx still blocked (retry=%d)",rx_retry);
|
||||
else log_ser(dbg_aux,"Nullmodem: block on continued rx (retry=%d).",rx_retry);
|
||||
#endif
|
||||
setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
|
||||
rx_state=N_RX_BLOCKED;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SERIAL_TX_EVENT: {
|
||||
// Maybe echo cirquit works a bit better this way
|
||||
if(rx_state==N_RX_IDLE && CanReceiveByte() && clientsocket) {
|
||||
if(doReceive()) {
|
||||
// a byte was received
|
||||
rx_state=N_RX_WAIT;
|
||||
setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
|
||||
}
|
||||
}
|
||||
ByteTransmitted();
|
||||
break;
|
||||
}
|
||||
|
@ -301,15 +377,18 @@ void CNullModem::handleUpperEvent(Bit16u type) {
|
|||
break;
|
||||
}
|
||||
case SERIAL_SERVER_POLLING_EVENT: {
|
||||
// As long as nothing is connected to out server poll the
|
||||
// As long as nothing is connected to our server poll the
|
||||
// connection.
|
||||
clientsocket=serversocket->Accept();
|
||||
if(clientsocket) {
|
||||
Bit8u peeripbuf[16];
|
||||
clientsocket->GetRemoteAddressString(peeripbuf);
|
||||
LOG_MSG("Serial%d: A client (%s) has connected.",idnumber+1,peeripbuf);
|
||||
// new socket found...
|
||||
LOG_MSG("Serial%d: A client (%s) has connected.",COMNUMBER,peeripbuf);
|
||||
#if SERIAL_DEBUG
|
||||
log_ser(dbg_aux,"Nullmodem: A client (%s) has connected.", peeripbuf);
|
||||
#endif// new socket found...
|
||||
clientsocket->SetSendBufferSize(256);
|
||||
rx_state=N_RX_IDLE;
|
||||
setEvent(SERIAL_POLLING_EVENT, 1);
|
||||
|
||||
// we don't accept further connections
|
||||
|
@ -350,15 +429,23 @@ void CNullModem::updateMSR () {
|
|||
|
||||
}
|
||||
|
||||
bool CNullModem::doReceive () {
|
||||
Bits rxchar = readChar();
|
||||
if(rxchar>=0) {
|
||||
receiveByteEx((Bit8u)rxchar,0);
|
||||
return true;
|
||||
}
|
||||
else if(rxchar==-2) {
|
||||
Disconnect();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CNullModem::transmitByte (Bit8u val, bool first) {
|
||||
// transmit it later in THR_Event
|
||||
if(first) setEvent(SERIAL_THR_EVENT, bytetime/8);
|
||||
else setEvent(SERIAL_TX_EVENT, bytetime);
|
||||
|
||||
// transmit it later in THR_Event
|
||||
if(first) {
|
||||
setEvent(SERIAL_THR_EVENT, bytetime/8);
|
||||
} else {
|
||||
//if(clientsocket) clientsocket->Putchar(val);
|
||||
setEvent(SERIAL_TX_EVENT, bytetime);
|
||||
}
|
||||
// disable 0xff escaping when transparent mode is enabled
|
||||
if (!transparent && (val==0xff)) WriteChar(0xff);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* $Id: nullmodem.h,v 1.3 2009-05-27 09:15:41 qbix79 Exp $ */
|
||||
/* $Id: nullmodem.h,v 1.4 2009-09-25 23:40:47 h-a-l-9000 Exp $ */
|
||||
|
||||
// include guard
|
||||
#ifndef DOSBOX_NULLMODEM_WIN32_H
|
||||
|
@ -57,6 +57,14 @@ public:
|
|||
void setDTR(bool val);
|
||||
void handleUpperEvent(Bit16u type);
|
||||
|
||||
Bitu rx_state;
|
||||
#define N_RX_IDLE 0
|
||||
#define N_RX_WAIT 1
|
||||
#define N_RX_BLOCKED 2
|
||||
#define N_RX_FASTWAIT 3
|
||||
#define N_RX_DISC 4
|
||||
|
||||
bool doReceive();
|
||||
void ClientConnect();
|
||||
void Disconnect();
|
||||
Bits readChar();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* $Id: softmodem.h,v 1.10 2009-05-27 09:15:42 qbix79 Exp $ */
|
||||
/* $Id: softmodem.h,v 1.11 2009-09-25 23:40:47 h-a-l-9000 Exp $ */
|
||||
|
||||
#ifndef DOSBOX_SERIALMODEM_H
|
||||
#define DOSBOX_SERIALMODEM_H
|
||||
|
@ -92,7 +92,7 @@ public:
|
|||
static Bits lcount=0;
|
||||
if (lcount<1000) {
|
||||
lcount++;
|
||||
LOG_MSG("MODEM: FIFO Overflow! (adds len %d)",_len);
|
||||
LOG_MSG("MODEM: FIFO Overflow! (adds len %u)",_len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -309,10 +309,10 @@
|
|||
RelativePath="..\src\cpu\core_dynrec\operators.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\risc_x86.h">
|
||||
RelativePath="..\src\cpu\core_dynrec\risc_x64.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\cpu\core_dynrec\risc_x64.h">
|
||||
RelativePath="..\src\cpu\core_dynrec\risc_x86.h">
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
|
@ -567,28 +567,16 @@
|
|||
Name="serialport"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="..\src\hardware\serialport\directserial_os2.cpp">
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
ExcludedFromBuild="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"/>
|
||||
</FileConfiguration>
|
||||
RelativePath="..\src\hardware\serialport\directserial.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\hardware\serialport\directserial_os2.h">
|
||||
RelativePath="..\src\hardware\serialport\directserial.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\hardware\serialport\directserial_posix.cpp">
|
||||
RelativePath="..\src\hardware\serialport\libserial.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\hardware\serialport\directserial_posix.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\hardware\serialport\directserial_win32.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\hardware\serialport\directserial_win32.h">
|
||||
RelativePath="..\src\hardware\serialport\libserial.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\hardware\serialport\misc_util.cpp">
|
||||
|
@ -622,6 +610,9 @@
|
|||
<Filter
|
||||
Name="gui"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="..\src\libs\gui_tk\gui_tk.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\gui\midi.cpp">
|
||||
</File>
|
||||
|
@ -649,9 +640,6 @@
|
|||
<File
|
||||
RelativePath="..\src\gui\sdlmain.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\libs\gui_tk\gui_tk.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="ints"
|
||||
|
|
Loading…
Add table
Reference in a new issue