DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/serialport/directserial.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 
00022 #if C_DIRECTSERIAL
00023 
00024 #include "serialport.h"
00025 #include "directserial.h"
00026 #include "misc_util.h"
00027 #include "pic.h"
00028 
00029 #include "libserial.h"
00030 
00031 /* This is a serial passthrough class.  Its amazingly simple to */
00032 /* write now that the serial ports themselves were abstracted out */
00033 
00034 CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd)
00035                                         :CSerial (id, cmd) {
00036         InstallationSuccessful = false;
00037         comport = 0;
00038 
00039         rx_retry = 0;
00040     rx_retry_max = 0;
00041     rx_state = 0;
00042 
00043         std::string tmpstring;
00044         if(!cmd->FindStringBegin("realport:",tmpstring,false)) return;
00045 
00046         LOG_MSG ("Serial%d: Opening %s", (int)(COMNUMBER), tmpstring.c_str());
00047         if(!SERIAL_open(tmpstring.c_str(), &comport)) {
00048                 char errorbuffer[256];
00049                 SERIAL_getErrorString(errorbuffer, sizeof(errorbuffer));
00050                 LOG_MSG("Serial%d: Serial Port \"%s\" could not be opened.",
00051                         (int)(COMNUMBER), tmpstring.c_str());
00052                 LOG_MSG("%s",errorbuffer);
00053                 return;
00054         }
00055 
00056         // rxdelay: How many milliseconds to wait before causing an
00057         // overflow when the application is unresponsive.
00058         if(getBituSubstring("rxdelay:", &rx_retry_max, cmd)) {
00059                 if(!(rx_retry_max<=10000)) {
00060                         rx_retry_max=0;
00061                 }
00062         }
00063 
00064         CSerial::Init_Registers();
00065         InstallationSuccessful = true;
00066         rx_state = D_RX_IDLE;
00067         setEvent(SERIAL_POLLING_EVENT, 1); // millisecond receive tick
00068 }
00069 
00070 CDirectSerial::~CDirectSerial () {
00071         if(comport) SERIAL_close(comport);
00072         // We do not use own events so we don't have to clear them.
00073 }
00074 
00075 // CanReceive: true:UART part has room left
00076 // doReceive:  true:there was really a byte to receive
00077 // rx_retry is incremented in polling events
00078 
00079 // in POLLING_EVENT: always add new polling event
00080 // D_RX_IDLE + CanReceive + doReceive           -> D_RX_WAIT   , add RX_EVENT
00081 // D_RX_IDLE + CanReceive + not doReceive       -> D_RX_IDLE
00082 // D_RX_IDLE + not CanReceive                           -> D_RX_BLOCKED, add RX_EVENT
00083 
00084 // D_RX_BLOCKED + CanReceive + doReceive        -> D_RX_FASTWAIT, rem RX_EVENT
00085 //                                                                                         rx_retry=0   , add RX_EVENT
00086 // D_RX_BLOCKED + CanReceive + !doReceive       -> D_RX_IDLE,     rem RX_EVENT
00087 //                                                                                         rx_retry=0
00088 // D_RX_BLOCKED + !CanReceive + doReceive + retry < max -> D_RX_BLOCKED, rx_retry++ 
00089 // D_RX_BLOCKED + !CanReceive + doReceive + retry >=max -> rx_retry=0   
00090 
00091 // to be continued...
00092 
00093 void CDirectSerial::handleUpperEvent(Bit16u type) {
00094 /*
00095 #if SERIAL_DEBUG
00096                 const char* s;
00097                 const char* s2;
00098                 switch(type) {
00099                 case SERIAL_POLLING_EVENT: s = "POLLING_EVENT"; break;
00100                 case SERIAL_RX_EVENT: s = "RX_EVENT"; break;
00101                 case SERIAL_TX_EVENT: s = "TX_EVENT"; break;
00102                 case SERIAL_THR_EVENT: s = "THR_EVENT"; break;
00103                 }
00104                 switch(rx_state) {
00105                 case D_RX_IDLE: s2 = "RX_IDLE"; break;
00106                 case D_RX_WAIT: s2 = "RX_WAIT"; break;
00107                 case D_RX_BLOCKED: s2 = "RX_BLOCKED"; break;
00108                 case D_RX_FASTWAIT: s2 = "RX_FASTWAIT"; break;
00109                 }
00110                 log_ser(dbg_aux,"Directserial: Event enter %s, %s",s,s2);
00111 #endif
00112                 */
00113 
00114         switch(type) {
00115                 case SERIAL_POLLING_EVENT: {
00116                         setEvent(SERIAL_POLLING_EVENT, 1.0f);
00117                         // update Modem input line states
00118                         switch(rx_state) {
00119                                 case D_RX_IDLE:
00120                                         if(CanReceiveByte()) {
00121                                                 if(doReceive()) {
00122                                                         // a byte was received
00123                                                         rx_state=D_RX_WAIT;
00124                                                         setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
00125                                                 } // else still idle
00126                                         } else {
00127 #if SERIAL_DEBUG
00128                                                 if(!dbgmsg_poll_block) {
00129                                                         log_ser(dbg_aux,"Directserial: block on polling.");
00130                                                         dbgmsg_poll_block=true;
00131                                                 }
00132 #endif
00133                                                 rx_state=D_RX_BLOCKED;
00134                                                 // have both delays (1ms + bytetime)
00135                                                 setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
00136                                         }
00137                                         break;
00138                                 case D_RX_BLOCKED:
00139                     // one timeout tick
00140                                         if(!CanReceiveByte()) {
00141                                                 rx_retry++;
00142                                                 if(rx_retry>=rx_retry_max) {
00143                                                         // it has timed out:
00144                                                         rx_retry=0;
00145                                                         removeEvent(SERIAL_RX_EVENT);
00146                                                         if(doReceive()) {
00147                                                                 // read away everything
00148                                                                 // this will set overrun errors
00149                                                                 while(doReceive());
00150                                                                 rx_state=D_RX_WAIT;
00151                                                                 setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
00152                                                         } else {
00153                                                                 // much trouble about nothing
00154                                 rx_state=D_RX_IDLE;
00155                                                         }
00156                                                 } // else wait further
00157                                         } else {
00158                                                 // good: we can receive again
00159 #if SERIAL_DEBUG
00160                                                 dbgmsg_poll_block=false;
00161                                                 dbgmsg_rx_block=false;
00162 #endif
00163                                                 removeEvent(SERIAL_RX_EVENT);
00164                                                 rx_retry=0;
00165                                                 if(doReceive()) {
00166                                                         rx_state=D_RX_FASTWAIT;
00167                                                         setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
00168                                                 } else {
00169                                                         // much trouble about nothing
00170                                                         rx_state=D_RX_IDLE;
00171                                                 }
00172                                         }
00173                                         break;
00174 
00175                                 case D_RX_WAIT:
00176                                 case D_RX_FASTWAIT:
00177                                         break;
00178                         }
00179                         updateMSR();
00180                         break;
00181                 }
00182                 case SERIAL_RX_EVENT: {
00183                         switch(rx_state) {
00184                                 case D_RX_IDLE:
00185                                         LOG_MSG("internal error in directserial");
00186                                         break;
00187 
00188                                 case D_RX_BLOCKED: // try to receive
00189                                 case D_RX_WAIT:
00190                                 case D_RX_FASTWAIT:
00191                                         if(CanReceiveByte()) {
00192                                                 // just works or unblocked
00193                                                 rx_retry=0; // not waiting anymore
00194                                                 if(doReceive()) {
00195                                                         if(rx_state==D_RX_WAIT) setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
00196                                                         else {
00197                                                                 // maybe unblocked
00198                                                                 rx_state=D_RX_FASTWAIT;
00199                                                                 setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
00200                                                         }
00201                                                 } else {
00202                                                         // didn't receive anything
00203                                                         rx_state=D_RX_IDLE;
00204                                                 }
00205                                         } else {
00206                                                 // blocking now or still blocked
00207 #if SERIAL_DEBUG
00208                                                 if(rx_state==D_RX_BLOCKED) {
00209                                                         if(!dbgmsg_rx_block) {
00210                                 log_ser(dbg_aux,"Directserial: rx still blocked (retry=%d)",rx_retry);
00211                                                                 dbgmsg_rx_block=true;
00212                                                         }
00213                                                 }
00214 
00215 
00216 
00217 
00218 
00219 
00220                                                 else log_ser(dbg_aux,"Directserial: block on continued rx (retry=%d).",rx_retry);
00221 #endif
00222                                                 setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
00223                                                 rx_state=D_RX_BLOCKED;
00224                                         }
00225 
00226                                         break;
00227                         }
00228                         updateMSR();
00229                         break;
00230                 }
00231                 case SERIAL_TX_EVENT: {
00232                         // Maybe echo cirquit works a bit better this way
00233                         if(rx_state==D_RX_IDLE && CanReceiveByte()) {
00234                                 if(doReceive()) {
00235                                         // a byte was received
00236                                         rx_state=D_RX_WAIT;
00237                                         setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
00238                                 }
00239                         }
00240                         ByteTransmitted();
00241                         updateMSR();
00242                         break;
00243                 }
00244                 case SERIAL_THR_EVENT: {
00245                         ByteTransmitting();
00246                         setEvent(SERIAL_TX_EVENT,bytetime*1.1f);
00247                         break;                             
00248                 }
00249         }
00250         /*
00251         #if SERIAL_DEBUG
00252                 switch(type) {
00253                 case SERIAL_POLLING_EVENT: s = "POLLING_EVENT"; break;
00254                 case SERIAL_RX_EVENT: s = "RX_EVENT"; break;
00255                 case SERIAL_TX_EVENT: s = "TX_EVENT"; break;
00256                 case SERIAL_THR_EVENT: s = "THR_EVENT"; break;
00257                 }
00258                 switch(rx_state) {
00259                         case D_RX_IDLE: s2 = "RX_IDLE"; break;
00260                         case D_RX_WAIT: s2 = "RX_WAIT"; break;
00261                         case D_RX_BLOCKED: s2 = "RX_BLOCKED"; break;
00262                         case D_RX_FASTWAIT: s2 = "RX_FASTWAIT"; break;
00263                 }
00264                 log_ser(dbg_aux,"Directserial: Event exit %s, %s",s,s2);
00265 #endif*/
00266 }
00267 
00268 bool CDirectSerial::doReceive() {
00269         int value = SERIAL_getextchar(comport);
00270         if(value) {
00271                 receiveByteEx((Bit8u)(value&0xff),(Bit8u)((value&0xff00)>>8));
00272                 return true;
00273         }
00274         return false;
00275 }
00276 
00277 // updatePortConfig is called when emulated app changes the serial port
00278 // parameters baudrate, stopbits, number of databits, parity.
00279 void CDirectSerial::updatePortConfig (Bit16u divider, Bit8u lcr) {
00280         Bit8u parity = 0;
00281 
00282         switch ((lcr & 0x38)>>3) {
00283         case 0x1: parity='o'; break;
00284         case 0x3: parity='e'; break;
00285         case 0x5: parity='m'; break;
00286         case 0x7: parity='s'; break;
00287         default: parity='n'; break;
00288         }
00289 
00290         Bit8u bytelength = (lcr & 0x3)+5;
00291 
00292         // baudrate
00293         Bitu baudrate;
00294         if(divider==0) baudrate=115200u;
00295         else baudrate = 115200u / divider;
00296 
00297         // stopbits
00298         Bit8u stopbits;
00299         if (lcr & 0x4) {
00300                 if (bytelength == 5) stopbits = SERIAL_15STOP;
00301                 else stopbits = SERIAL_2STOP;
00302         } else stopbits = SERIAL_1STOP;
00303 
00304         if(!SERIAL_setCommParameters(comport, (int)baudrate, (char)parity, (char)stopbits, (char)bytelength)) {
00305 #if SERIAL_DEBUG
00306                 log_ser(dbg_aux,"Serial port settings not supported by host." );
00307 #endif
00308                 LOG_MSG ("Serial%d: Desired serial mode not supported (%d,%d,%c,%d)",
00309                         (int)(COMNUMBER),(int)baudrate,(int)bytelength,parity,(int)stopbits);
00310         } 
00311         CDirectSerial::setRTSDTR(getRTS(), getDTR());
00312 }
00313 
00314 void CDirectSerial::updateMSR () {
00315         int new_status = SERIAL_getmodemstatus(comport);
00316 
00317         setCTS((new_status&SERIAL_CTS)? true:false);
00318         setDSR((new_status&SERIAL_DSR)? true:false);
00319         setRI((new_status&SERIAL_RI)? true:false);
00320         setCD((new_status&SERIAL_CD)? true:false);
00321 }
00322 
00323 void CDirectSerial::transmitByte (Bit8u val, bool first) {
00324         if(!SERIAL_sendchar(comport, (char)val))
00325                 LOG_MSG("Serial%d: COM port error: write failed!", (int)COMNUMBER);
00326         if(first) setEvent(SERIAL_THR_EVENT, bytetime/8);
00327         else setEvent(SERIAL_TX_EVENT, bytetime);
00328 }
00329 
00330 
00331 // setBreak(val) switches break on or off
00332 void CDirectSerial::setBreak (bool value) {
00333         SERIAL_setBREAK(comport,value);
00334 }
00335 
00336 // updateModemControlLines(mcr) sets DTR and RTS. 
00337 void CDirectSerial::setRTSDTR(bool rts, bool dtr) {
00338         SERIAL_setRTS(comport,rts);
00339         SERIAL_setDTR(comport,dtr);
00340 }
00341 
00342 void CDirectSerial::setRTS(bool val) {
00343         SERIAL_setRTS(comport,val);
00344 }
00345 
00346 void CDirectSerial::setDTR(bool val) {
00347         SERIAL_setDTR(comport,val);
00348 }
00349 
00350 #endif