From b8138ed47749fd8d51559fbbd3fa2a88a1d2f17f Mon Sep 17 00:00:00 2001 From: Ralf Grillenberger Date: Sun, 4 Oct 2009 20:57:40 +0000 Subject: [PATCH] - make the modem command parsing code more readable - add dummy \K modem command - improve timing when receiving data so it can go faster Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@3477 --- src/hardware/serialport/softmodem.cpp | 535 +++++++++++++------------- src/hardware/serialport/softmodem.h | 5 +- 2 files changed, 279 insertions(+), 261 deletions(-) diff --git a/src/hardware/serialport/softmodem.cpp b/src/hardware/serialport/softmodem.cpp index 7651dac2..0dd33c25 100644 --- a/src/hardware/serialport/softmodem.cpp +++ b/src/hardware/serialport/softmodem.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: softmodem.cpp,v 1.11 2009-05-27 09:15:42 qbix79 Exp $ */ +/* $Id: softmodem.cpp,v 1.12 2009-10-04 20:57:40 h-a-l-9000 Exp $ */ #include "dosbox.h" @@ -77,41 +77,47 @@ CSerialModem::~CSerialModem() { } void CSerialModem::handleUpperEvent(Bit16u type) { - switch(type) - { - case SERIAL_RX_EVENT: - { - break; - } - case MODEM_TX_EVENT: - { - if(tqueue->left()) { - tqueue->addb(waiting_tx_character); - if(tqueue->left() < 2) { - CSerial::setCTS(false); - } - } else { - static Bits lcount=0; - if (lcount<1000) { - lcount++; - LOG_MSG("MODEM: TX Buffer overflow!"); - } + switch (type) { + case SERIAL_RX_EVENT: { + // check for bytes to be sent to port + if(CSerial::CanReceiveByte()) + if(rqueue->inuse() && (CSerial::getRTS()||(flowcontrol!=3))) { + Bit8u rbyte = rqueue->getb(); + //LOG_MSG("Modem: sending byte %2x back to UART3",rbyte); + CSerial::receiveByte(rbyte); + } + if(CSerial::CanReceiveByte()) setEvent(SERIAL_RX_EVENT, bytetime*0.98f); + break; + } + case MODEM_TX_EVENT: { + if (tqueue->left()) { + tqueue->addb(waiting_tx_character); + if (tqueue->left() < 2) { + CSerial::setCTS(false); + } + } else { + static Bits lcount=0; + if (lcount<1000) { + lcount++; + LOG_MSG("MODEM: TX Buffer overflow!"); } - ByteTransmitted(); - - break; } - case SERIAL_POLLING_EVENT: - { - Timer2(); - setEvent(SERIAL_POLLING_EVENT,1); - break; + ByteTransmitted(); + break; + } + case SERIAL_POLLING_EVENT: { + if (rqueue->inuse()) { + removeEvent(SERIAL_RX_EVENT); + setEvent(SERIAL_RX_EVENT, (float)0.01); } + Timer2(); + setEvent(SERIAL_POLLING_EVENT,1); + break; + } - case MODEM_RING_EVENT: - { - break; - } + case MODEM_RING_EVENT: { + break; + } } } @@ -216,6 +222,12 @@ Bitu CSerialModem::ScanNumber(char * & scan) { return ret; } +char CSerialModem::GetChar(char * & scan) { + char ch = *scan; + scan++; + return ch; +} + void CSerialModem::Reset(){ EnterIdleState(); cmdpos = 0; @@ -300,227 +312,238 @@ void CSerialModem::DoCommand() { cmdpos = 0; //Reset for next command upcase(cmdbuf); LOG_MSG("Command sent to modem: ->%s<-\n", cmdbuf); - /* Check for empty line, stops dialing and autoanswer */ - if (!cmdbuf[0]) { - reg[0]=0; // autoanswer off - return; - // } - //else { - //MIXER_Enable(mhd.chan,false); - // dialing = false; - // SendRes(ResNOCARRIER); - // goto ret_none; - // } - } - /* AT command set interpretation */ + /* Check for empty line, stops dialing and autoanswer */ + if (!cmdbuf[0]) { + reg[0]=0; // autoanswer off + return; + } + //else { + //MIXER_Enable(mhd.chan,false); + // dialing = false; + // SendRes(ResNOCARRIER); + // goto ret_none; + //} + /* AT command set interpretation */ - if ((cmdbuf[0] != 'A') || (cmdbuf[1] != 'T')) { - SendRes(ResERROR); - return; - } - - if (strstr(cmdbuf,"NET0")) { - telnetmode = false; - SendRes(ResOK); - return; - } - else if (strstr(cmdbuf,"NET1")) { - telnetmode = true; - SendRes(ResOK); - return; - } - - char * scanbuf; - scanbuf=&cmdbuf[2]; - char chr; - Bitu num; - while (chr=*scanbuf++) { - switch (chr) { - case 'D': // Dial - { - char * foundstr=&scanbuf[0]; - if (*foundstr=='T' || *foundstr=='P') foundstr++; - // Small protection against empty line and long string - if ((!foundstr[0]) || (strlen(foundstr)>100)) { - SendRes(ResERROR); - return; - } - char* helper; - // scan for and remove spaces; weird bug: with leading spaces in the string, - // SDLNet_ResolveHost will return no error but not work anyway (win) - while(foundstr[0]==' ') foundstr++; - helper=foundstr; - helper+=strlen(foundstr); - while(helper[0]==' ') { - helper[0]=0; - helper--; - } - if (strlen(foundstr) >= 12) { - // Check if supplied parameter only consists of digits - bool isNum = true; - for (Bitu i=0; i '9') - isNum = false; - if (isNum) { - // Parameter is a number with at least 12 digits => this cannot - // be a valid IP/name - // Transform by adding dots - char buffer[128]; - Bitu j = 0; - for (Bitu i=0; i12) - buffer[j++] = ':'; - } - buffer[j] = 0; - foundstr = buffer; - } - } - Dial(foundstr); - return; - } - case 'I': //Some strings about firmware - switch (num=ScanNumber(scanbuf)) { - case 3:SendLine("DosBox Emulated Modem Firmware V1.00");break; - case 4:SendLine("Modem compiled for DosBox version " VERSION);break; - };break; - case 'E': //Echo on/off - switch (num=ScanNumber(scanbuf)) { - case 0:echo = false;break; - case 1:echo = true;break; - };break; - case 'V': - switch (num=ScanNumber(scanbuf)) { - case 0:numericresponse = true;break; - case 1:numericresponse = false;break; - };break; - case 'H': //Hang up - switch (num=ScanNumber(scanbuf)) { - case 0: - if (connected) { - SendRes(ResNOCARRIER); - EnterIdleState(); - return; - } - //Else return ok - };break; - case 'O': //Return to data mode - switch (num=ScanNumber(scanbuf)) - { - case 0: - if (clientsocket) { - commandmode = false; - return; - } else { - SendRes(ResERROR); - return; - } - };break; - case 'T': //Tone Dial - case 'P': //Pulse Dial - break; - case 'M': //Monitor - case 'L': //Volume - ScanNumber(scanbuf); - break; - case 'A': //Answer call - if (waitingclientsocket) { - AcceptIncomingCall(); - } else { - SendRes(ResERROR); - return; - } - return; - case 'Z': //Reset and load profiles - { - // scan the number away, if any - ScanNumber(scanbuf); - if (clientsocket/*socket*/) SendRes(ResNOCARRIER); - Reset(); - break; - } - case ' ': //Space just skip - break; - case 'Q': // Response options - { // 0 = all on, 1 = all off, - // 2 = no ring and no connect/carrier in answermode - Bitu val = ScanNumber(scanbuf); - if(!(val>2)) { - doresponse=val; - break; - } else { - SendRes(ResERROR); - return; - } - } - case 'S': //Registers - { - Bitu index=ScanNumber(scanbuf); - if(index>=SREGS) { - SendRes(ResERROR); - return; //goto ret_none; - } - - while(scanbuf[0]==' ') scanbuf++; // skip spaces - - if(scanbuf[0]=='=') { // set register - scanbuf++; - while(scanbuf[0]==' ') scanbuf++; // skip spaces - Bitu val = ScanNumber(scanbuf); - reg[index]=val; - break; - } - else if(scanbuf[0]=='?') { // get register - SendNumber(reg[index]); - scanbuf++; - break; - } - //else LOG_MSG("print reg %d with %d",index,reg[index]); - } - break; - case '&': - { - if(scanbuf[0]!=0) { - char ch = scanbuf[0]; - scanbuf++; - switch(ch) { - case 'K': - { - Bitu val = ScanNumber(scanbuf); - if(val<5) flowcontrol=val; - else { - SendRes(ResERROR); - return; - } - break; - } - default: - { - scanbuf++; - LOG_MSG("Modem: Unhandled command: &%c%d",ch,ScanNumber(scanbuf)); - break; - } - } - } else { - SendRes(ResERROR); - return; - } - } - break; - - default: - LOG_MSG("Modem: Unhandled command: %c%d",chr,ScanNumber(scanbuf)); - } - } - + if ((cmdbuf[0] != 'A') || (cmdbuf[1] != 'T')) { + SendRes(ResERROR); + return; + } + if (strstr(cmdbuf,"NET0")) { + telnetmode = false; SendRes(ResOK); return; } + else if (strstr(cmdbuf,"NET1")) { + telnetmode = true; + SendRes(ResOK); + return; + } + + char * scanbuf = &cmdbuf[2]; + while (1) { + // LOG_MSG("loopstart ->%s<-",scanbuf); + char chr = GetChar(scanbuf); + switch (chr) { + case 'D': { // Dial + char * foundstr=&scanbuf[0]; + if (*foundstr=='T' || *foundstr=='P') foundstr++; + // Small protection against empty line and long string + if ((!foundstr[0]) || (strlen(foundstr)>100)) { + SendRes(ResERROR); + return; + } + char* helper; + // scan for and remove spaces; weird bug: with leading spaces in the string, + // SDLNet_ResolveHost will return no error but not work anyway (win) + while(foundstr[0]==' ') foundstr++; + helper=foundstr; + helper+=strlen(foundstr); + while(helper[0]==' ') { + helper[0]=0; + helper--; + } + if (strlen(foundstr) >= 12) { + // Check if supplied parameter only consists of digits + bool isNum = true; + for (Bitu i=0; i '9') isNum = false; + if (isNum) { + // Parameter is a number with at least 12 digits => this cannot + // be a valid IP/name + // Transform by adding dots + char buffer[128]; + Bitu j = 0; + for (Bitu i=0; i12) + buffer[j++] = ':'; + } + buffer[j] = 0; + foundstr = buffer; + } + } + Dial(foundstr); + return; + } + case 'I': // Some strings about firmware + switch (ScanNumber(scanbuf)) { + case 3: SendLine("DosBox Emulated Modem Firmware V1.00"); break; + case 4: SendLine("Modem compiled for DosBox version " VERSION); break; + } + break; + case 'E': // Echo on/off + switch (ScanNumber(scanbuf)) { + case 0: echo = false; break; + case 1: echo = true; break; + } + break; + case 'V': + switch (ScanNumber(scanbuf)) { + case 0: numericresponse = true; break; + case 1: numericresponse = false; break; + } + break; + case 'H': // Hang up + switch (ScanNumber(scanbuf)) { + case 0: + if (connected) { + SendRes(ResNOCARRIER); + EnterIdleState(); + return; + } + // else return ok + } + break; + case 'O': // Return to data mode + switch (ScanNumber(scanbuf)) { + case 0: + if (clientsocket) { + commandmode = false; + return; + } else { + SendRes(ResERROR); + return; + } + } + break; + case 'T': // Tone Dial + case 'P': // Pulse Dial + break; + case 'M': // Monitor + case 'L': // Volume + ScanNumber(scanbuf); + break; + case 'A': // Answer call + if (waitingclientsocket) { + AcceptIncomingCall(); + } else { + SendRes(ResERROR); + return; + } + return; + case 'Z': { // Reset and load profiles + // scan the number away, if any + ScanNumber(scanbuf); + if (clientsocket) SendRes(ResNOCARRIER); + Reset(); + break; + } + case ' ': // skip space + break; + case 'Q': { + // Response options + // 0 = all on, 1 = all off, + // 2 = no ring and no connect/carrier in answermode + Bitu val = ScanNumber(scanbuf); + if(!(val>2)) { + doresponse=val; + break; + } else { + SendRes(ResERROR); + return; + } + } + case 'S': { // Registers + Bitu index=ScanNumber(scanbuf); + if(index>=SREGS) { + SendRes(ResERROR); + return; //goto ret_none; + } + + while(scanbuf[0]==' ') scanbuf++; // skip spaces + + if(scanbuf[0]=='=') { // set register + scanbuf++; + while(scanbuf[0]==' ') scanbuf++; // skip spaces + Bitu val = ScanNumber(scanbuf); + reg[index]=val; + break; + } + else if(scanbuf[0]=='?') { // get register + SendNumber(reg[index]); + scanbuf++; + break; + } + //else LOG_MSG("print reg %d with %d",index,reg[index]); + } + break; + case '&': { // & escaped commands + char cmdchar = GetChar(scanbuf); + switch(cmdchar) { + case 'K': { + Bitu val = ScanNumber(scanbuf); + if(val<5) flowcontrol=val; + else { + SendRes(ResERROR); + return; + } + break; + } + case '\0': + // end of string + SendRes(ResERROR); + return; + default: + LOG_MSG("Modem: Unhandled command: &%c%d",cmdchar,ScanNumber(scanbuf)); + break; + } + break; + } + case '\\': { // \ escaped commands + char cmdchar = GetChar(scanbuf); + switch(cmdchar) { + case 'N': + // error correction stuff - not emulated + if (ScanNumber(scanbuf) > 5) { + SendRes(ResERROR); + return; + } + break; + case '\0': + // end of string + SendRes(ResERROR); + return; + default: + LOG_MSG("Modem: Unhandled command: \\%c%d",cmdchar, ScanNumber(scanbuf)); + break; + } + break; + } + case '\0': + SendRes(ResOK); + return; + default: + LOG_MSG("Modem: Unhandled command: %c%d",chr,ScanNumber(scanbuf)); + break; + } + } +} void CSerialModem::TelnetEmulation(Bit8u * data, Bitu size) { Bitu i; @@ -631,13 +654,6 @@ void CSerialModem::Timer2(void) { Bit8u txval; Bitu txbuffersize =0; - // check for bytes to be sent to port - if(CSerial::CanReceiveByte()) - if(rqueue->inuse() && (CSerial::getRTS()||(flowcontrol!=3))) { - Bit8u rbyte = rqueue->getb(); - //LOG_MSG("Modem: sending byte %2x back to UART3",rbyte); - CSerial::receiveByte(rbyte); - } // Check for eventual break command if (!commandmode) cmdpause++; // Handle incoming data from serial port, read as much as available @@ -684,8 +700,10 @@ void CSerialModem::Timer2(void) { if (clientsocket && sendbyte && txbuffersize) { // down here it saves a lot of network traffic - clientsocket->SendArray(tmpbuf,txbuffersize); - //TODO error testing + if(!clientsocket->SendArray(tmpbuf,txbuffersize)) { + SendRes(ResNOCARRIER); + EnterIdleState(); + } } // Handle incoming to the serial port if(!commandmode && clientsocket && rqueue->left()) { @@ -695,7 +713,6 @@ void CSerialModem::Timer2(void) { SendRes(ResNOCARRIER); EnterIdleState(); } else if(usesize) { - // LOG_MSG("rcv:%d", result); // Filter telnet commands if(telnetmode) TelnetEmulation(tmpbuf, usesize); else rqueue->adds(tmpbuf,usesize); diff --git a/src/hardware/serialport/softmodem.h b/src/hardware/serialport/softmodem.h index 7cd1e792..42406c37 100644 --- a/src/hardware/serialport/softmodem.h +++ b/src/hardware/serialport/softmodem.h @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: softmodem.h,v 1.11 2009-09-25 23:40:47 h-a-l-9000 Exp $ */ +/* $Id: softmodem.h,v 1.12 2009-10-04 20:57:40 h-a-l-9000 Exp $ */ #ifndef DOSBOX_SERIALMODEM_H #define DOSBOX_SERIALMODEM_H @@ -102,7 +102,7 @@ public: used+=_len; while (_len--) { if (where>=size) where-=size; - //LOG_MSG("+%x",*_str); + //LOG_MSG("+'%x'",*_str); data[where++]=*_str++; } } @@ -172,6 +172,7 @@ public: bool Dial(char * host); void AcceptIncomingCall(void); Bitu ScanNumber(char * & scan); + char GetChar(char * & scan); void DoCommand();