// ********************** START OF MODEM.CPP ********************** // // // This file contains all of the code for the Modem class. It should // be compile and linked with any program wanting to use the class. #include #include #include #include #include #include #include "rs232.h" #include "modem.h" // The modem capability database is used to define all of the // attributes used by a particular brand of modem. These are // stored in the application, which is practical when just a few // are defined. A bigger database might have to be moved into a // conventional file. ModemCapabilities ModemDataBase[] = { { "Hayes Compatible", "AT &F &C1 &D2", "NO CARRIER\0ERROR\0NO DIALTONE\0BUSY\0NO ANSWER\0\0", "", "", 2400L, 0, 0 }, { "Practical Peripherals V.34", "AT &F0 &C1 &D2 S95=44", "NO CARRIER\0ERROR\0NO DIALTONE\0BUSY\0NO ANSWER\0\0", "CLASS 5\0V.42BIS\0\0", "LAP-M\0ALT\0\0", 57600L, 1, 1 }, { "Intel SatisFAXtion 400e", "AT &F", "NO CARRIER\0ERROR\0NO DIALTONE\0BUSY\0NO ANSWER\0\0", "COMP\0\0", "LAPM\0MNP\0REL\0\0", 57600L, 1, 1 }, { "" } }; // The modem constructur sets up the capability database for the // modem of the particular name, but doesn't do much else. If the // brand name mode is not found via an exact match, the generic Hayes // compatible definition is used. Modem::Modem( RS232 &rs232_port, char *modem_name ) { int i; port = &rs232_port; modem_data = &ModemDataBase[ 0 ]; for ( i = 0 ; *ModemDataBase[ i ].name != '\0' ; i++ ) { if ( strcmp( modem_name, ModemDataBase[ i ].name ) == 0 ) { modem_data = &ModemDataBase[ i ]; break; } } tone_dial = 1; carrier_timeout = 60000L; } // The usual translation routine is used to print out the error // names in more descriptive form. char *Modem::ErrorName( ModemError status ) { switch ( status ) { case MODEM_SUCCESS : return "Success"; case MODEM_NO_RESPONSE : return "No Response"; case MODEM_NO_CONNECTION : return "No Connection"; case MODEM_DISCONNECT_FAILED : return "Disconnect failed"; case MODEM_USER_ABORT : return "User abort"; default : return "Unknown Error"; } } // The initialization routine just has to send out the initialization // string, then wait for a response. It inserts an extra one second // delay in this routine, because some modems need a little extra time // to handle initialization. ModemError Modem::Initialize( void ) { long delay_time; port->Set( modem_data->initial_baud_rate ); port->RtsCtsHandshaking( modem_data->handshaking ); port->Write( '\r' ); delay_time = ReadTime() + 1000; while ( ReadTime() < delay_time ) port->IdleFunction(); port->Write( modem_data->initialization_string ); port->Write( '\r' ); return wait_for_response(); } // This protected routine is used to read lines of data back from the // modem, generally after a response to a command. It reads the // characters in, echos them using the echo routine, and tries to // assemble a complete line. A '\n' character is used to terminate the // line, or a timeout. void Modem::read_line( char *buffer, int buf_size ) { int c; for ( ; ; ) { c = port->Read( 500 ); if ( c < 0 ) break; echo( (char) c ); *buffer++ = (char) c; if ( --buf_size <= 1 ) break; if ( c == '\n' ) break; } *buffer = '\0'; } // This protected routine is used to wait for an OK message // after a modem command is sent. If it doesn't get it within // 2 seconds, and error is returned. Most commands going to the // modem can expect an OK response. The two notable exceptions // are the dialing and answer commands. ModemError Modem::wait_for_response( void ) { long timeout; char buffer[ 81 ]; ModemError status; timeout = ReadTime() + 2000; while ( ReadTime() < timeout ) { read_line( buffer, 81 ); if ( strncmp( buffer, "OK", 2 ) == 0 ) return MODEM_SUCCESS; if ( ( status = UserAbort() ) != MODEM_SUCCESS ) return status; } return MODEM_NO_RESPONSE; } // During dialing, the Dial routine has to scan the input stream for // lots of different strings that can indicate various things about // protocols, data compression, and connections. This command is used // to scan for a list of strings stored in the format used by the // modem capability database. char *scan_strings( char *buffer, char *strings ) { char *p; while ( *strings ) { if ( ( p = strstr( buffer, strings ) ) != 0 ) return p; strings += strlen( strings ) + 1; } return p; } // This routine is called by both the Answer and Dial routines. // It has to scan the incoming lines of data not just for the // "CONNECT" message, but also the protocol and compression strings // as well. Additionally, if the baud rate is not locked, it has // to detect the new baud rate on connection. ModemError Modem::wait_for_connection( void ) { long timeout; char *connect; char buffer[ 81 ]; ModemError status; compressing = 0; protocol = 0; timeout = ReadTime() + carrier_timeout; while ( ReadTime() < timeout ) { read_line( buffer, 81 ); if ( scan_strings( buffer, modem_data->fail_strings ) ) return MODEM_NO_CONNECTION; if ( scan_strings( buffer, modem_data->compression_strings ) ) compressing = 1; if ( scan_strings( buffer, modem_data->protocol_strings ) ) protocol = 1; if ( ( connect = strstr( buffer, "CONNECT" ) ) != 0 ) { if ( !modem_data->locked_baud_rate ) { local_baud_rate = atol( connect + 8 ); if ( local_baud_rate !=0 ) port->Set( local_baud_rate ); } else local_baud_rate = modem_data->initial_baud_rate; return MODEM_SUCCESS; } if ( ( status = UserAbort() ) != MODEM_SUCCESS ) return status; } return MODEM_NO_CONNECTION; } // This routine dials and then has another routine do the hard work // of waiting for a response. ModemError Modem::Dial( char *dial_string ) { port->Write( "ATD" ); if ( tone_dial ) port->Write( 'T' ); else port->Write( 'P' ); port->Write( dial_string ); port->Write( '\r' ); return wait_for_connection(); } // This routine sends the answer command, then lets the other routine // wait for success or failure. ModemError Modem::Answer( void ) { port->Write( "ATA\r" ); return wait_for_connection(); } // Although all of the modems in the database are supposed to be set // up so that dropping DTR causes a disconnect, some may slip through // the net. If dropping DTR doesn't cause a disconnect, the // escape sequence is sent, followed by a Hangup message. ModemError Modem::Disconnect( void ) { long delay_time; port->Dtr( 0 ); delay_time = ReadTime() + 1250; while ( ReadTime() < delay_time ) port->IdleFunction(); port->Dtr( 1 ); port->Write( "AT\r" ); if ( wait_for_response() == MODEM_SUCCESS ) { port->Set( modem_data->initial_baud_rate ); return MODEM_SUCCESS; } port->Write( "+++" ); delay_time = ReadTime() + 1250; wait_for_response(); port->Write( "ATH0\r" ); if ( wait_for_response() == MODEM_SUCCESS ) { port->Set( modem_data->initial_baud_rate ); return MODEM_SUCCESS; } return MODEM_DISCONNECT_FAILED; } // This routine gives the user an opportunity to abort during long // sequences, such as dialing. ModemError Modem::UserAbort( void ) { if ( !kbhit() ) return MODEM_SUCCESS; getch(); return MODEM_USER_ABORT; } // ReadRegister() not only asks for the register value, it then // scans it in and converts it from ASCII to binary so it can be // used by the program. int Modem::ReadRegister( int reg ) { char buffer[ 81 ]; long timeout; int value; ModemError status; sprintf( buffer, "ATS%d?\r", reg ); port->Write( buffer ); timeout = ReadTime() + 3000; value = (int) MODEM_NO_RESPONSE; while ( timeout > ReadTime() ) { read_line( buffer, 80 ); if ( strncmp( buffer, "OK", 2 ) == 0 ) break; if ( ( status = UserAbort() ) != MODEM_SUCCESS ) return status; if ( isdigit( *buffer ) ) value = atoi( buffer ); } return value; } // This is the generic routine to send a command of your choice. It // assumes the command will get an OK message in return. ModemError Modem::SendCommand( char *command ) { port->Write( command ); port->Write( '\r' ); return wait_for_response(); } // This routine is generally only useful during debugging. It // dumps the state of the Modem structure out to the screen. void Modem::DumpState( void ) { char *p; cout << "\nModem Status:\n\n" << "Name: " << modem_data->name << '\n'; cout << "Init string: " << modem_data->initialization_string << '\n'; cout << "Fail strings: "; p = modem_data->fail_strings; while ( *p ) { cout << p; p += strlen( p ) + 1; if ( *p ) cout << ", "; } cout << '\n'; cout << "Compression strings: "; p = modem_data->compression_strings; while ( *p ) { cout << p; p += strlen( p ) + 1; if ( *p ) cout << ", "; } cout << '\n'; cout << "Protocol strings: "; p = modem_data->protocol_strings; while ( *p ) { cout << p; p += strlen( p ) + 1; if ( *p ) cout << ", "; } cout << '\n'; cout << "Initial baud rate: " << modem_data->initial_baud_rate << '\n'; cout << "Baud rate locked: " << (( modem_data->locked_baud_rate ) ? 'Y' : 'N') << '\n'; cout << "Hardware handshaking: " << (( modem_data->handshaking ) ? 'Y' : 'N') << '\n'; cout << "Dialing method: " << (( tone_dial ) ? "Tone" : "Pulse" ) << '\n'; cout << "Carrier timeout: " << carrier_timeout << '\n'; cout << "Connected: " << (( port->Cd() ) ? 'Y' : 'N' ) << '\n'; if ( !port->Cd() ) return; cout << "Local baud rate: " << local_baud_rate << '\n'; cout << "Compressing: " << (( compressing ) ? 'Y' : 'N' ) << '\n'; cout << "Protocol: " << (( protocol ) ? 'Y' : 'N' ) << '\n'; } // ********************** END OF MODEM.CPP **********************