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