DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/serialport/softmodem.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_MODEM
00023 
00024 #include <string.h>
00025 #include <stdlib.h>
00026 #include <ctype.h>
00027 #include <fstream>
00028 #include <sstream>
00029 
00030 #include "support.h"
00031 #include "serialport.h"
00032 #include "softmodem.h"
00033 #include "misc_util.h"
00034 
00035 //#include "mixer.h"
00036 
00037 class PhonebookEntry {
00038 public:
00039         PhonebookEntry(const std::string &_phone, const std::string &_address) :
00040                 phone(_phone),
00041                 address(_address) {
00042         }
00043 
00044         bool IsMatchingPhone(const std::string &input) const {
00045                 return (input == phone);
00046         }
00047 
00048         const std::string &GetAddress() const {
00049                 return address;
00050         }
00051 
00052 private:
00053         std::string phone;
00054         std::string address;
00055 };
00056 
00057 static std::vector<PhonebookEntry *> phones;
00058 static const char phoneValidChars[] = "01234567890*=,;#+>";
00059 
00060 static bool MODEM_IsPhoneValid(const std::string &input) {
00061         size_t found = input.find_first_not_of(phoneValidChars);
00062         if (found != std::string::npos) {
00063                 LOG_MSG("SERIAL: Phonebook %s contains invalid character %c.",
00064                         input.c_str(), input[found]);
00065                 return false;
00066         }
00067 
00068         return true;
00069 }
00070 
00071 bool MODEM_ReadPhonebook(const std::string &filename) {
00072         std::ifstream loadfile(filename);
00073         if (!loadfile)
00074                 return false;
00075 
00076         LOG_MSG("SERIAL: Loading phonebook from %s", filename.c_str());
00077 
00078         std::string linein;
00079         while (std::getline(loadfile, linein)) {
00080                 std::istringstream iss(linein);
00081                 std::string phone, address;
00082 
00083                 if (!(iss >> phone >> address)) {
00084                         LOG_MSG("SERIAL: Skipped a bad line in %s", filename.c_str());
00085                         continue;
00086                 }
00087 
00088                 // Check phone number for characters ignored by Hayes modems.
00089                 if (!MODEM_IsPhoneValid(phone))
00090                         continue;
00091 
00092                 LOG_MSG("SERIAL: Mapped phone %s to address %s", phone.c_str(), address.c_str());
00093                 PhonebookEntry *pbEntry = new PhonebookEntry(phone, address);
00094                 phones.push_back(pbEntry);
00095         }
00096 
00097         return true;
00098 }
00099 
00100 static const char *MODEM_GetAddressFromPhone(const char *input) {
00101         for (const auto entry : phones) {
00102                 if (entry->IsMatchingPhone(input))
00103                         return entry->GetAddress().c_str();
00104         }
00105 
00106         return nullptr;
00107 }
00108 
00109 CSerialModem::CSerialModem(Bitu id, CommandLine* cmd):CSerial(id, cmd) {
00110         InstallationSuccessful=false;
00111         connected=false;
00112 
00113         rqueue=new CFifo(MODEM_BUFFER_QUEUE_SIZE);
00114         tqueue=new CFifo(MODEM_BUFFER_QUEUE_SIZE);
00115         
00116         // Default to direct null modem connection.  Telnet mode interprets IAC codes
00117         telnetmode = false;
00118 
00119         // Initialize the sockets and setup the listening port
00120         listenport = 23;
00121         waitingclientsocket=0;
00122         clientsocket = 0;
00123         serversocket = 0;
00124         getBituSubstring("listenport:", &listenport, cmd);
00125         
00126         // TODO: Fix dialtones if requested
00127         //mhd.chan=MIXER_AddChannel((MIXER_MixHandler)this->MODEM_CallBack,8000,"MODEM");
00128         //MIXER_Enable(mhd.chan,false);
00129         //MIXER_SetMode(mhd.chan,MIXER_16MONO);
00130                 
00131         CSerial::Init_Registers();
00132         Reset(); // reset calls EnterIdleState
00133                 
00134         setEvent(SERIAL_POLLING_EVENT,1);
00135         InstallationSuccessful=true;
00136 }
00137 
00138 CSerialModem::~CSerialModem() {
00139         if(serversocket) delete serversocket;
00140         if(clientsocket) delete clientsocket;
00141         if(waitingclientsocket) delete waitingclientsocket;
00142 
00143         delete rqueue;
00144         delete tqueue;
00145 
00146         // remove events
00147         for(Bit8u i = SERIAL_BASE_EVENT_COUNT+1;        i <= SERIAL_MODEM_EVENT_COUNT; i++)
00148                 removeEvent(i);
00149 }
00150 
00151 void CSerialModem::handleUpperEvent(Bit16u type) {
00152         switch (type) {
00153         case SERIAL_RX_EVENT: {
00154                 // check for bytes to be sent to port
00155                 if(CSerial::CanReceiveByte())
00156                         if(rqueue->inuse() && (CSerial::getRTS()||(flowcontrol!=3))) {
00157                                 Bit8u rbyte = rqueue->getb();
00158                                 //LOG_MSG("Modem: sending byte %2x back to UART3",rbyte);
00159                                 CSerial::receiveByte(rbyte);
00160                         }
00161                 if(CSerial::CanReceiveByte()) setEvent(SERIAL_RX_EVENT, bytetime*0.98f);
00162                 break;
00163         }
00164         case MODEM_TX_EVENT: {
00165                 if (tqueue->left()) {
00166                         tqueue->addb(waiting_tx_character);
00167                         if (tqueue->left() < 2) {
00168                                 CSerial::setCTS(false);
00169                         }
00170                 } else {
00171                         static Bits lcount=0;
00172                         if (lcount<1000) {
00173                                 lcount++;
00174                                 LOG_MSG("MODEM: TX Buffer overflow!");
00175                         }
00176                 }
00177                 ByteTransmitted();
00178                 break;
00179         }
00180         case SERIAL_POLLING_EVENT: {
00181                 if (rqueue->inuse()) {
00182                         removeEvent(SERIAL_RX_EVENT);
00183                         setEvent(SERIAL_RX_EVENT, (float)0.01);
00184                 }
00185                 Timer2();
00186                 setEvent(SERIAL_POLLING_EVENT,1);
00187                 break;
00188         }
00189 
00190         case MODEM_RING_EVENT: {
00191                 break;
00192         }
00193         }
00194 }
00195 
00196 void CSerialModem::SendLine(const char *line) {
00197         rqueue->addb(0xd);
00198         rqueue->addb(0xa);
00199         rqueue->adds((Bit8u *)line,(Bitu)strlen(line));
00200         rqueue->addb(0xd);
00201         rqueue->addb(0xa);
00202 }
00203 
00204 // only for numbers < 1000...
00205 void CSerialModem::SendNumber(Bitu val) {
00206         rqueue->addb(0xd);
00207         rqueue->addb(0xa);
00208         
00209         rqueue->addb((Bit8u)(val / 100+'0'));
00210         val = val%100;
00211         rqueue->addb((Bit8u)(val / 10+'0'));
00212         val = val%10;
00213         rqueue->addb((Bit8u)(val + '0'));
00214 
00215         rqueue->addb(0xd);
00216         rqueue->addb(0xa);
00217 }
00218 
00219 void CSerialModem::SendRes(ResTypes response) {
00220         char const * string;Bitu code;
00221         switch (response)
00222         {
00223                 case ResOK:         code = 0; string = "OK"; break;
00224                 case ResCONNECT:    code = 1; string = "CONNECT 57600"; break;
00225                 case ResRING:       code = 2; string = "RING"; break;
00226                 case ResNOCARRIER:  code = 3; string = "NO CARRIER"; break;
00227                 case ResERROR:      code = 4; string = "ERROR"; break;
00228                 case ResNODIALTONE: code = 6; string = "NO DIALTONE"; break;
00229                 case ResBUSY:       code = 7; string = "BUSY"; break;
00230                 case ResNOANSWER:   code = 8; string = "NO ANSWER"; break;
00231                 case ResNONE:       return;
00232                 default:            return;
00233         }
00234         
00235         if(doresponse != 1) {
00236                 if(doresponse==2 && (response==ResRING || 
00237                         response == ResCONNECT || response==ResNOCARRIER)) return;
00238                 if(numericresponse) SendNumber(code);
00239                 else SendLine(string);
00240 
00241                 //if(CSerial::CanReceiveByte()) // very fast response
00242                 //      if(rqueue->inuse() && CSerial::getRTS())
00243                 //      { Bit8u rbyte =rqueue->getb();
00244                 //              CSerial::receiveByte(rbyte);
00245                 //      LOG_MSG("Modem: sending byte %2x back to UART2",rbyte);
00246                 //      }
00247 
00248                 LOG_MSG("Modem response: %s", string);
00249         }
00250 }
00251 
00252 bool CSerialModem::Dial(const char *host) {
00253         char buf[128] = "";
00254         safe_strcpy(buf, host);
00255 
00256         const char *destination = buf;
00257 
00258         // Scan host for port
00259         Bit16u port;
00260         char *hasport=strrchr(buf, ':');
00261         if (hasport) {
00262                 *hasport++ = 0;
00263                 port = (Bit16u)atoi(hasport);
00264         }
00265         else port=MODEM_DEFAULT_PORT;
00266         
00267         // Resolve host we're gonna dial
00268         LOG_MSG("Connecting to host %s port %u", destination, port);
00269         clientsocket = new TCPClientSocket(destination, port);
00270         if(!clientsocket->isopen) {
00271                 delete clientsocket;
00272                 clientsocket=0;
00273                 LOG_MSG("Failed to connect.");
00274                 SendRes(ResNOCARRIER);
00275                 EnterIdleState();
00276                 return false;
00277         } else {
00278                 EnterConnectedState();
00279                 return true;
00280         }
00281 }
00282 
00283 void CSerialModem::AcceptIncomingCall(void) {
00284         if(waitingclientsocket) {
00285                 clientsocket=waitingclientsocket;
00286                 waitingclientsocket=0;
00287                 EnterConnectedState();
00288         } else {
00289                 EnterIdleState();
00290         }
00291 }
00292 
00293 Bitu CSerialModem::ScanNumber(char * & scan) {
00294         Bitu ret = 0;
00295         while (char c = *scan) {
00296                 if (c >= '0' && c <= '9') {
00297                         ret*=10;
00298                         ret+=(Bitu)(c-'0');
00299                         scan++;
00300                 } else break;
00301         }
00302         return ret;
00303 }
00304 
00305 char CSerialModem::GetChar(char * & scan) {
00306         char ch = *scan;
00307         scan++;
00308         return ch;
00309 }
00310 
00311 void CSerialModem::Reset(){
00312         EnterIdleState();
00313         cmdpos = 0;
00314         cmdbuf[0] = 0;
00315         oldDTRstate = getDTR();
00316         flowcontrol = 0;
00317         plusinc = 0;
00318         dtrmode = 2;
00319         if(clientsocket) {
00320                 delete clientsocket;
00321                 clientsocket = 0;
00322         }
00323         memset(&reg,0,sizeof(reg));
00324         reg[MREG_AUTOANSWER_COUNT] = 0; // no autoanswer
00325         reg[MREG_RING_COUNT]       = 1;
00326         reg[MREG_ESCAPE_CHAR]      = '+';
00327         reg[MREG_CR_CHAR]          = '\r';
00328         reg[MREG_LF_CHAR]          = '\n';
00329         reg[MREG_BACKSPACE_CHAR]   = '\b';
00330         reg[MREG_GUARD_TIME]       = 50;
00331         reg[MREG_DTR_DELAY]        = 5;
00332 
00333         cmdpause = 0;   
00334         echo = true;
00335         doresponse = 0;
00336         numericresponse = false;
00337 
00338         /* Default to direct null modem connection.  Telnet mode interprets IAC codes */
00339         telnetmode = false;
00340 }
00341 
00342 void CSerialModem::EnterIdleState(void){
00343         connected = false;
00344         ringing = false;
00345         dtrofftimer = -1;
00346 
00347         if(clientsocket) {
00348                 delete clientsocket;
00349                 clientsocket=0;
00350         }
00351 
00352         if(waitingclientsocket) {       // clear current incoming socket
00353                 delete waitingclientsocket;
00354                 waitingclientsocket = 0;
00355         }
00356         // get rid of everything
00357         if(serversocket) {
00358                 while ((waitingclientsocket=serversocket->Accept()))
00359                         delete waitingclientsocket;
00360         } else if (listenport) {
00361 
00362                 serversocket=new TCPServerSocket((Bit16u)listenport);
00363                 if(!serversocket->isopen) {
00364                         LOG_MSG("Serial%d: Modem could not open TCP port %u.",
00365                                 static_cast<uint32_t>(COMNUMBER),
00366                                 static_cast<uint32_t>(listenport));
00367                         delete serversocket;
00368                         serversocket = 0;
00369                 } else
00370                     LOG_MSG("Serial%u: Modem listening on port %u...",
00371                             static_cast<uint32_t>(COMNUMBER),
00372                                 static_cast<uint32_t>(listenport));
00373         }
00374         waitingclientsocket = 0;
00375 
00376         commandmode = true;
00377         CSerial::setCD(false);
00378         CSerial::setRI(false);
00379         CSerial::setDSR(true);
00380         CSerial::setCTS(true);
00381         tqueue->clear();
00382 }
00383 
00384 void CSerialModem::EnterConnectedState(void) {
00385         if(serversocket) {
00386                 // we don't accept further calls
00387                 delete serversocket;
00388                 serversocket = 0;
00389         }
00390         SendRes(ResCONNECT);
00391         commandmode = false;
00392         memset(&telClient, 0, sizeof(telClient));
00393         connected = true;
00394         ringing = false;
00395         dtrofftimer = -1;
00396         CSerial::setCD(true);
00397         CSerial::setRI(false);
00398 }
00399 
00400 void CSerialModem::DoCommand() {
00401         cmdbuf[cmdpos] = 0;
00402         cmdpos = 0;                     //Reset for next command
00403         upcase(cmdbuf);
00404         LOG_MSG("Command sent to modem: ->%s<-\n", cmdbuf);
00405         /* Check for empty line, stops dialing and autoanswer */
00406         if (!cmdbuf[0]) {
00407                 reg[MREG_AUTOANSWER_COUNT] = 0; // autoanswer off
00408                 return;
00409         }
00410         //else {
00411                 //MIXER_Enable(mhd.chan,false);
00412         //      dialing = false;
00413         //      SendRes(ResNOCARRIER);
00414         //      goto ret_none;
00415         //}
00416         /* AT command set interpretation */
00417 
00418         if ((cmdbuf[0] != 'A') || (cmdbuf[1] != 'T')) {
00419                 SendRes(ResERROR);
00420                 return;
00421         }
00422         if (strstr(cmdbuf,"NET0")) {
00423                 telnetmode = false;
00424                 SendRes(ResOK);
00425                 return;
00426         }
00427         else if (strstr(cmdbuf,"NET1")) {
00428                 telnetmode = true;
00429                 SendRes(ResOK);
00430                 return;
00431         }
00432 
00433         char * scanbuf = &cmdbuf[2];
00434         while (1) {
00435                 // LOG_MSG("loopstart ->%s<-",scanbuf);
00436                 char chr = GetChar(scanbuf);
00437                 switch (chr) {
00438                 case 'D': { // Dial
00439                         char *foundstr = &scanbuf[0];
00440                         if (*foundstr=='T' || *foundstr=='P') foundstr++;
00441                         
00442                         // Small protection against empty line and long string
00443                         if ((!foundstr[0]) || (strlen(foundstr) > 100)) {
00444                                 SendRes(ResERROR);
00445                                 return;
00446                         }
00447                         // scan for and remove spaces; weird bug: with leading spaces in the string,
00448                         // SDLNet_ResolveHost will return no error but not work anyway (win)
00449                         while(foundstr[0] == ' ') foundstr++;
00450                         char* helper = foundstr;
00451                         helper+=strlen(foundstr);
00452                         while(helper[0] == ' ') {
00453                                 helper[0] = 0;
00454                                 helper--;
00455                         }
00456                         const char *mappedaddr = MODEM_GetAddressFromPhone(foundstr);
00457                         if (mappedaddr) {
00458                                 Dial(mappedaddr);
00459                                 return;
00460                         }
00461                         //Large enough scope, so the buffers are still valid when reaching Dial.
00462                         char buffer[128];
00463                         char obuffer[128];
00464                         if (strlen(foundstr) >= 12) {
00465                                 // Check if supplied parameter only consists of digits
00466                                 bool isNum = true;
00467                                 size_t fl = strlen(foundstr);
00468                                 for (size_t i = 0; i < fl; i++)
00469                                         if (foundstr[i] < '0' || foundstr[i] > '9') isNum = false;
00470                                 if (isNum) {
00471                                         // Parameter is a number with at least 12 digits => this cannot
00472                                         // be a valid IP/name
00473                                         // Transform by adding dots
00474                                         size_t j = 0;
00475                                         size_t foundlen = strlen(foundstr);
00476                                         for (size_t i = 0; i < foundlen; i++) {
00477                                                 buffer[j++] = foundstr[i];
00478                                                 // Add a dot after the third, sixth and ninth number
00479                                                 if (i == 2 || i == 5 || i == 8)
00480                                                         buffer[j++] = '.';
00481                                                 // If the string is longer than 12 digits,
00482                                                 // interpret the rest as port
00483                                                 if (i == 11 && strlen(foundstr) > 12)
00484                                                         buffer[j++] = ':';
00485                                         }
00486                                         buffer[j] = 0;
00487                                         foundstr = buffer;
00488                                         
00489                                         // Remove Zeros from beginning of octets
00490                                         size_t k = 0;
00491                                         size_t foundlen2 = strlen(foundstr);
00492                                         for (size_t i = 0; i < foundlen2; i++) {
00493                                                 if (i == 0 && foundstr[0] == '0') continue;
00494                                                 if (i == 1 && foundstr[0] == '0' && foundstr[1] == '0') continue;
00495                                                 if (foundstr[i] == '0' && foundstr[i-1] == '.') continue;
00496                                                 if (foundstr[i] == '0' && foundstr[i-1] == '0' && foundstr[i-2] == '.') continue;
00497                                                 obuffer[k++] = foundstr[i];
00498                                                 }
00499                                         obuffer[k] = 0;
00500                                         foundstr = obuffer;
00501                                 }
00502                         }
00503                         Dial(foundstr);
00504                         return;
00505                 }
00506                 case 'I': // Some strings about firmware
00507                         switch (ScanNumber(scanbuf)) {
00508                         case 3: SendLine("DOSBox-X Emulated Modem Firmware V1.00"); break;
00509                         case 4: SendLine("Modem compiled for DOSBox-X version " VERSION); break;
00510                         }
00511                         break;
00512                 case 'E': // Echo on/off
00513                         switch (ScanNumber(scanbuf)) {
00514                         case 0: echo = false; break;
00515                         case 1: echo = true; break;
00516                         }
00517                         break;
00518                 case 'V':
00519                         switch (ScanNumber(scanbuf)) {
00520                         case 0: numericresponse = true; break;
00521                         case 1: numericresponse = false; break;
00522                         }
00523                         break;
00524                 case 'H': // Hang up
00525                         switch (ScanNumber(scanbuf)) {
00526                         case 0:
00527                                 if (connected) {
00528                                         SendRes(ResNOCARRIER);
00529                                         EnterIdleState();
00530                                         return;
00531                                 }
00532                                 // else return ok
00533                         }
00534                         break;
00535                 case 'O': // Return to data mode
00536                         switch (ScanNumber(scanbuf)) {
00537                         case 0:
00538                                 if (clientsocket) {
00539                                         commandmode = false;
00540                                         return;
00541                                 } else {
00542                                         SendRes(ResERROR);
00543                                         return;
00544                                 }
00545                         }
00546                         break;
00547                 case 'T': // Tone Dial
00548                 case 'P': // Pulse Dial
00549                         break;
00550                 case 'M': // Monitor
00551                 case 'L': // Volume
00552                         ScanNumber(scanbuf);
00553                         break;
00554                 case 'A': // Answer call
00555                         if (waitingclientsocket) {
00556                                 AcceptIncomingCall();
00557                         } else {
00558                                 SendRes(ResERROR);
00559                                 return;
00560                         }
00561                         return;
00562                 case 'Z': { // Reset and load profiles
00563                         // scan the number away, if any
00564                         ScanNumber(scanbuf);
00565                         if (clientsocket) SendRes(ResNOCARRIER);
00566                         Reset();
00567                         break;
00568                 }
00569                 case ' ': // skip space
00570                         break;
00571                 case 'Q': {
00572                         // Response options
00573                         // 0 = all on, 1 = all off,
00574                         // 2 = no ring and no connect/carrier in answermode
00575                         Bitu val = ScanNumber(scanbuf); 
00576                         if(!(val>2)) {
00577                                 doresponse=val;
00578                                 break;
00579                         } else {
00580                                 SendRes(ResERROR);
00581                                 return;
00582                         }
00583                 }
00584                 case 'S': { // Registers        
00585                         Bitu index=ScanNumber(scanbuf);
00586                         if(index>=SREGS) {
00587                                 SendRes(ResERROR);
00588                                 return; //goto ret_none;
00589                         }
00590                         
00591                         while(scanbuf[0]==' ') scanbuf++;       // skip spaces
00592                         
00593                         if(scanbuf[0]=='=') {   // set register
00594                                 scanbuf++;
00595                                 while(scanbuf[0]==' ') scanbuf++;       // skip spaces
00596                                 Bitu val = ScanNumber(scanbuf);
00597                                 reg[index]=(Bit8u)val;
00598                                 break;
00599                         }
00600                         else if(scanbuf[0]=='?') {      // get register
00601                                 SendNumber(reg[index]);
00602                                 scanbuf++;
00603                                 break;
00604                         }
00605                         //else LOG_MSG("print reg %d with %d",index,reg[index]);
00606                 }
00607                 break;
00608                 case '&': { // & escaped commands
00609                         char cmdchar = GetChar(scanbuf);
00610                         switch(cmdchar) {
00611                                 case 'K': {
00612                                         Bitu val = ScanNumber(scanbuf);
00613                                         if(val<5) flowcontrol=val;
00614                                         else {
00615                                                 SendRes(ResERROR);
00616                                                 return;
00617                                         }
00618                                         break;
00619                                 }
00620                                 case 'D': {
00621                                         Bitu val = ScanNumber(scanbuf);
00622                                         if (val<4) dtrmode=val;
00623                                         else {
00624                                                 SendRes(ResERROR);
00625                                                 return;
00626                                         }
00627                                         break;
00628                                 }
00629                                 case '\0':
00630                                         // end of string
00631                                         SendRes(ResERROR);
00632                                         return;
00633                                 default:
00634                                         LOG_MSG("Modem: Unhandled command: &%c%u",
00635                                                 cmdchar,
00636                                                 static_cast<uint32_t>(ScanNumber(scanbuf)));
00637                                         break;
00638                         }
00639                         break;
00640                 }
00641                 case '\\': { // \ escaped commands
00642                         char cmdchar = GetChar(scanbuf);
00643                         switch(cmdchar) {
00644                                 case 'N':
00645                                         // error correction stuff - not emulated
00646                                         if (ScanNumber(scanbuf) > 5) {
00647                                                 SendRes(ResERROR);
00648                                                 return;
00649                                         }
00650                                         break;
00651                                 case '\0':
00652                                         // end of string
00653                                         SendRes(ResERROR);
00654                                         return;
00655                                 default:
00656                                         LOG_MSG("Modem: Unhandled command: \\%c%u",
00657                                                 cmdchar,
00658                                                 static_cast<uint32_t>(ScanNumber(scanbuf)));
00659                                         break;
00660                         }
00661                         break;
00662                 }
00663                 case '\0':
00664                         SendRes(ResOK);
00665                         return;
00666                 default:
00667                         LOG_MSG("Modem: Unhandled command: %c%u",
00668                                 chr,
00669                                 static_cast<uint32_t>(ScanNumber(scanbuf)));
00670                         break;
00671                 }
00672         }
00673 }
00674 
00675 void CSerialModem::TelnetEmulation(Bit8u * data, Bitu size) {
00676         Bitu i;
00677         Bit8u c;
00678         for(i=0;i<size;i++) {
00679                 c = data[i];
00680                 if(telClient.inIAC) {
00681                         if(telClient.recCommand) {
00682                                 if((c != 0) && (c != 1) && (c != 3)) {
00683                                         LOG_MSG("MODEM: Unrecognized option %u", c);
00684                                         if(telClient.command>250) {
00685                                                 /* Reject anything we don't recognize */
00686                                                 tqueue->addb(0xff);
00687                                                 tqueue->addb(252);
00688                                                 tqueue->addb(c); /* We won't do crap! */
00689                                         }
00690                         }
00691                         switch(telClient.command) {
00692                                 case 251: /* Will */
00693                                         if(c == 0) telClient.binary[TEL_SERVER] = true;
00694                                         if(c == 1) telClient.echo[TEL_SERVER] = true;
00695                                         if(c == 3) telClient.supressGA[TEL_SERVER] = true;
00696                                         break;
00697                                 case 252: /* Won't */
00698                                         if(c == 0) telClient.binary[TEL_SERVER] = false;
00699                                         if(c == 1) telClient.echo[TEL_SERVER] = false;
00700                                         if(c == 3) telClient.supressGA[TEL_SERVER] = false;
00701                                         break;
00702                                 case 253: /* Do */
00703                                         if(c == 0) {
00704                                                 telClient.binary[TEL_CLIENT] = true;
00705                                                         tqueue->addb(0xff);
00706                                                         tqueue->addb(251);
00707                                                         tqueue->addb(0); /* Will do binary transfer */
00708                                         }
00709                                         if(c == 1) {
00710                                                 telClient.echo[TEL_CLIENT] = false;
00711                                                         tqueue->addb(0xff);
00712                                                         tqueue->addb(252);
00713                                                         tqueue->addb(1); /* Won't echo (too lazy) */
00714                                         }
00715                                         if(c == 3) {
00716                                                 telClient.supressGA[TEL_CLIENT] = true;
00717                                                         tqueue->addb(0xff);
00718                                                         tqueue->addb(251);
00719                                                         tqueue->addb(3); /* Will Suppress GA */
00720                                         }
00721                                         break;
00722                                 case 254: /* Don't */
00723                                         if(c == 0) {
00724                                                 telClient.binary[TEL_CLIENT] = false;
00725                                                         tqueue->addb(0xff);
00726                                                         tqueue->addb(252);
00727                                                         tqueue->addb(0); /* Won't do binary transfer */
00728                                         }
00729                                         if(c == 1) {
00730                                                 telClient.echo[TEL_CLIENT] = false;
00731                                                         tqueue->addb(0xff);
00732                                                         tqueue->addb(252);
00733                                                         tqueue->addb(1); /* Won't echo (fine by me) */
00734                                         }
00735                                         if(c == 3) {
00736                                                 telClient.supressGA[TEL_CLIENT] = true;
00737                                                         tqueue->addb(0xff);
00738                                                         tqueue->addb(251);
00739                                                         tqueue->addb(3); /* Will Suppress GA (too lazy) */
00740                                         }
00741                                         break;
00742                                 default:
00743                                         LOG_MSG("MODEM: Telnet client sent IAC %d", telClient.command);
00744                                         break;
00745                         }
00746                         telClient.inIAC = false;
00747                         telClient.recCommand = false;
00748                         continue;
00749                 } else {
00750                         if(c==249) {
00751                                 /* Go Ahead received */
00752                                 telClient.inIAC = false;
00753                                 continue;
00754                         }
00755                         telClient.command = c;
00756                         telClient.recCommand = true;
00757                         
00758                         if((telClient.binary[TEL_SERVER]) && (c == 0xff)) {
00759                                 /* Binary data with value of 255 */
00760                                 telClient.inIAC = false;
00761                                 telClient.recCommand = false;
00762                                         rqueue->addb(0xff);
00763                                 continue;
00764                         }
00765                 }
00766         } else {
00767                 if(c == 0xff) {
00768                         telClient.inIAC = true;
00769                         continue;
00770                 }
00771                         rqueue->addb(c);
00772                 }
00773         }
00774 }
00775 
00776 void CSerialModem::Timer2(void) {
00777 
00778         bool sendbyte = true;
00779         Bitu usesize;
00780         Bit8u txval;
00781         Bitu txbuffersize =0;
00782 
00783         (void)sendbyte;// UNUSED
00784 
00785         // Check for eventual break command
00786         if (!commandmode) {
00787                 cmdpause++;
00788                 if (cmdpause > (20 * reg[MREG_GUARD_TIME])) {
00789                         if (plusinc == 0) {
00790                                 plusinc = 1;
00791                         }
00792                         else if (plusinc == 4) {
00793                                 LOG_MSG("Modem: Entering command mode(escape sequence)");
00794                                 commandmode = true;
00795                                 SendRes(ResOK);
00796                                 plusinc = 0;
00797                         }
00798                 }
00799         }
00800         
00801         // Handle incoming data from serial port, read as much as available
00802         CSerial::setCTS(true);  // buffer will get 'emptier', new data can be received 
00803         while (tqueue->inuse()) {
00804                 txval = tqueue->getb();
00805                 if (commandmode) {
00806                         if (echo) {
00807                                 rqueue->addb(txval);
00808                                 //LOG_MSG("Echo back to queue: %x",txval);
00809                         }
00810                         if (txval == '\n')
00811                             continue; // Real modem doesn't seem to skip this?
00812                         
00813                         if (txval == '\b') {
00814                             if (cmdpos > 0)
00815                                 cmdpos--;
00816                         } else if (txval == '\r') {
00817                             DoCommand();
00818                         } else if (txval != '+') {
00819                             if (cmdpos < 99) {
00820                                         cmdbuf[cmdpos] = (char)txval;
00821                                         cmdpos++;
00822                                 }
00823                         }
00824                 }
00825                 else {// + character
00826                         if (plusinc >= 1 && plusinc <= 3 && txval == reg[MREG_ESCAPE_CHAR]) // +
00827                             plusinc++;
00828                         else {
00829                             plusinc = 0;
00830                         }
00831                         cmdpause = 0;
00832                         tmpbuf[txbuffersize] = txval;
00833                         txbuffersize++;
00834                 }
00835         } // while loop
00836         
00837         if (clientsocket && txbuffersize) {
00838                 // down here it saves a lot of network traffic
00839                 if(!clientsocket->SendArray(tmpbuf,txbuffersize)) {
00840                         SendRes(ResNOCARRIER);
00841                         EnterIdleState();
00842                 }
00843         }
00844         // Handle incoming to the serial port
00845         if(!commandmode && clientsocket && rqueue->left()) {
00846                 usesize = rqueue->left();
00847                 if (usesize>16) usesize=16;
00848                 if(!clientsocket->ReceiveArray(tmpbuf, &usesize)) {
00849                         SendRes(ResNOCARRIER);
00850                         EnterIdleState();
00851                 } else if(usesize) {
00852                         // Filter telnet commands 
00853                         if(telnetmode) TelnetEmulation(tmpbuf, usesize);
00854                         else rqueue->adds(tmpbuf,usesize);
00855                         cmdpause = 0;
00856                 } 
00857         }
00858         // Check for incoming calls
00859         if (!connected && !waitingclientsocket && serversocket) {
00860                 waitingclientsocket=serversocket->Accept();
00861                 if(waitingclientsocket) {       
00862                         if(!CSerial::getDTR() && dtrmode != 0) {
00863                                 // accept no calls with DTR off; TODO: AT &Dn
00864                                 EnterIdleState();
00865                         } else {
00866                                 ringing=true;
00867                                 SendRes(ResRING);
00868                                 CSerial::setRI(!CSerial::getRI());
00869                                 //MIXER_Enable(mhd.chan,true);
00870                                 ringtimer = 3000;
00871                                 reg[MREG_RING_COUNT] = 0; //Reset ring counter reg
00872                         }
00873                 }
00874         }
00875         if (ringing) {
00876                 if (ringtimer <= 0) {
00877                         reg[MREG_RING_COUNT]++;
00878                         if ((reg[MREG_AUTOANSWER_COUNT] > 0) &&
00879                                 (reg[MREG_RING_COUNT] >= reg[MREG_AUTOANSWER_COUNT])) {
00880                                 AcceptIncomingCall();
00881                                 return;
00882                         }
00883                         SendRes(ResRING);
00884                         CSerial::setRI(!CSerial::getRI());
00885 
00886                         //MIXER_Enable(mhd.chan,true);
00887                         ringtimer = 3000;
00888                 }
00889                 --ringtimer;
00890         }
00891 
00892         if (connected && !getDTR()) {
00893                 if (dtrofftimer == 0) {
00894                         switch (dtrmode) {
00895                                 case 0:
00896                                         // Do nothing.
00897                                         //LOG_MSG("Modem: Dropped DTR.");
00898                                         break;
00899                                 case 1:
00900                                         // Go back to command mode.
00901                                         LOG_MSG("Modem: Entering command mode due to dropped DTR.");
00902                                         commandmode = true;
00903                                         SendRes(ResOK);
00904                                         break;
00905                                 case 2:
00906                                         // Hang up.
00907                                         LOG_MSG("Modem: Hanging up due to dropped DTR.");
00908                                         SendRes(ResNOCARRIER);
00909                                         EnterIdleState();
00910                                         break;
00911                                 case 3:
00912                                         // Reset.
00913                                         LOG_MSG("Modem: Resetting due to dropped DTR.");
00914                                         SendRes(ResNOCARRIER);
00915                                         Reset();
00916                                         break;
00917                         }
00918                 }
00919 
00920                 // Set the timer to -1 once it's expired to turn it off.
00921                 if (dtrofftimer >= 0) {
00922                         dtrofftimer--;
00923                 }
00924         }
00925 }
00926 
00927 
00928 //TODO
00929 void CSerialModem::RXBufferEmpty() {
00930         // see if rqueue has some more bytes
00931         if(rqueue->inuse() && (CSerial::getRTS()||(flowcontrol!=3))){
00932                 Bit8u rbyte = rqueue->getb();
00933                 //LOG_MSG("Modem: sending byte %2x back to UART1",rbyte);
00934                 CSerial::receiveByte(rbyte);
00935         }
00936 }
00937 
00938 void CSerialModem::transmitByte(Bit8u val, bool first) {
00939         waiting_tx_character=val;
00940         setEvent(MODEM_TX_EVENT, bytetime); // TX event
00941         if(first) ByteTransmitting();
00942         //LOG_MSG("MODEM: Byte %x to be transmitted",val);
00943 }
00944 
00945 void CSerialModem::updatePortConfig(Bit16u, Bit8u lcr) {
00946     (void) lcr; // deliberately unused by needed to meet the API
00947 }
00948 
00949 void CSerialModem::updateMSR() {
00950         // think it is not needed
00951 }
00952 
00953 void CSerialModem::setBreak(bool) {
00954         // TODO: handle this
00955 }
00956 
00957 void CSerialModem::setRTSDTR(bool rts, bool dtr) {
00958     (void) rts; // deliberately unused but needed to meet the API
00959     setDTR(dtr);
00960 }
00961 void CSerialModem::setRTS(bool val) {
00962     (void) val; // deliberately unused but needed to meet the API
00963 }
00964 void CSerialModem::setDTR(bool val) {
00965         if (val != oldDTRstate) {
00966                 if (connected && !val) {
00967                         // Start the timer upon losing DTR.
00968                         dtrofftimer = reg[MREG_DTR_DELAY];
00969                 } else {
00970                         dtrofftimer = -1;
00971                 }
00972         }
00973 
00974         oldDTRstate = val;
00975 }
00976 /*
00977 void CSerialModem::updateModemControlLines() {
00978         //bool txrdy=tqueue->left();
00979         //if(CSerial::getRTS() && txrdy) CSerial::setCTS(true);
00980         //else CSerial::setCTS(tqueue->left());
00981         
00982         // If DTR goes low, hang up.
00983         if(connected)
00984                 if(oldDTRstate)
00985                         if(!getDTR()) {
00986                                 SendRes(ResNOCARRIER);
00987                                 EnterIdleState();
00988                                 LOG_MSG("Modem: Hang up due to dropped DTR.");
00989                         }
00990 
00991         oldDTRstate = getDTR();
00992 }
00993 */
00994 
00995 #endif
00996