DOSBox-X
|
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