/* * * *************************************************************************** ** * ** COPYRIGHT (c) 1991, 1992 BY * ** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. * ** ALL RIGHTS RESERVED. * ** * ** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED * ** ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE * ** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER * ** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY * ** OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY * ** TRANSFERRED. * ** * ** THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE * ** AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT * ** CORPORATION. * ** * ** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS * ** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. * ** * ** * ***************************************************************************** * * ++ * FACILITY: * * RTPAD - Modem dialer for modems using the AT command set * * ABSTRACT: * * This module contains code to dial Digital Equpiment Corporation modems * using the AT command set. You may need to make changes to this code to * make your AT compatible modem function correctly. The code assumes that the * modem is set to verbose mode responses. If not you will have to change the * code or the modems settings. Many thanks to Rick Murphy who provided to * the base code for this version. * * * INSTRUCTIONS: * * 1) Compile it using the C compiler * 2) Use the following link command: * * $ LINK/SHARE=DTE_AT DTE_AT,SYS$INPUT:/OPTION * universal=dial_routine * ^z * * If you are linking this for ALPHA replace * * universal=dial_routine * * with * symbol_vector=(dial_routine = procedure) * * * 3) The image needs to be copied to SYS$LIBRARY. * * * ORIGINAL AUTHOR: Rick Murphy ??-???????-19?? * * * * Revision history: * * X-01 FAK001 Forrest A. Kenney 22-April-1992 * Convert original FORTRAN code to C and remove modem specific * stuff. * * * -- */ /*+ * * INCLUDE FILES * -*/ #include #include #include #include #include #include #include #include #include /*+ * * CONSTANTS * -*/ #define CALL_COMPLETE 1 #define LINE_BUSY 2 #define DIAL_ERROR 3 #define NO_ANSWER 4 #define NO_DIAL_TONE 5 #define RINGING 6 #define FALSE 0 #define RESPONSE_SIZE 40 #define TRUE 1 /*+ * * VARIOUS STRUCUTRE DEFINITIONS * -*/ struct read_iosb { short int status; short int byte_cnt; short int terminator; short int terminator_size; }; struct set_iosb { short int status; char xmit_speed; char rcv_speed; char cr_fill; char lf_fill; char parity; char unused; }; struct write_iosb { short int status; short int byte_cnt; int unused; }; struct char_buff { unsigned char class; unsigned char type; short int buff_size; int basic_char; /* Actually basic is 3 bytes long */ int extended_char; }; /*+ * * FORWARD ROUTINE DEFINITIONS * -*/ int get_speed(short int, int *); int modem_dialer(char *, short int); int read_com(short int, char *, int *, int, int); int send_com(short int, char *); int set_speed(short int, int); /* **+ ** dial_routine - This routine is called SET HOST/DTE to dial the phone ** ** Functional Description: ** ** This routine will be invoked by SET HOST/DTE/DIAL to dial the supplied ** number. It is a simple jacket to the real dialer routine..... ** ** Explicit Inputs: ** ** number - Address of a string descriptor for the phone ** number ** com_chan - Pointer to terminal (VMs channel #) ** tt_chan - Pointer to users terminal (VMS channel #) ** ** Implicit Inputs: ** ** None ** ** Local Variables: ** ** status - Return status from program ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ int dial_routine(struct dsc$descriptor_s *number, short int com_chan, short int tt_chan) { int status; number->dsc$a_pointer[number->dsc$w_length] = '\0'; status = modem_dialer(number->dsc$a_pointer, com_chan); switch(status) { case CALL_COMPLETE: printf("Connection made to remote port \07\n"); status = SS$_NORMAL; break; case LINE_BUSY: case NO_ANSWER: printf("Failed to connect to remote port\n"); status = SS$_HANGUP; break; case DIAL_ERROR: printf("Failed to initialize dialer\n"); status = SS$_HANGUP; break; case NO_DIAL_TONE: printf("Autodialer did not detect dial tone\n"); status = SS$_HANGUP; break; } return status; } /* **+ ** modem_dialer - This routine does the actual dialing ** ** Functional Description: ** ** This routine controls that actual dialing steps to dial a HAYES ** compatible modem. ** ** Explicit Inputs: ** ** number - Character string containg phone number ** com_chan - Pointer to modem VMS channel number ** ** Implicit Inputs: ** ** None ** ** Local Variables: ** ** send_buffer - Character buffer to hole sequence ** response_buffer - Character buffer to hold responses from modem ** ** dmf_32 - Flag indicationg if terminal controller is ** DMF32 ** i - Loop counter ** purge_flag - Flag indication if type-ahead buffer should be ** purged before reading data ** read_status - Return status from read_com routine ** response_length - Length of response from modem ** saved_speed - Old terminal line speed ** status - Return status from program ** ** iosb - I/O status block for sense ** modem_state - Structure with modem status bits ** ** Outputs: ** ** status - Indicator if call worked or failed ** ** NOTES: ** ** None ** **- */ int modem_dialer(char *number, short int com_chan) { char send_buffer[RESPONSE_SIZE+1]; char response_buffer[RESPONSE_SIZE+1]; int dmf_32; int i = 0; int purge_flag; int read_status; int response_length = RESPONSE_SIZE; int saved_speed; int status; struct set_iosb iosb; struct { char controller_type; char unused; char receive_modem; int more_unused; } modem_state; /* Get the terminal controller type we need to know if a DMF 32 is being used. */ status = sys$qiow(0, com_chan, IO$_SENSEMODE | IO$M_RD_MODEM, &iosb, 0, 0, &modem_state, 0, 0, 0, 0, 0); /* Should also check IOSB but if setup OK IOSB will be success for this call. */ if ((status & SS$_NORMAL) && (modem_state.controller_type == DT$_DMF32)) dmf_32 = TRUE; else dmf_32 = FALSE; /* Get the modems speed */ status = get_speed(com_chan, &saved_speed); if (status & SS$_NORMAL) { /* Start of clean by purging the type-ahead buffer */ status = read_com(com_chan, response_buffer, &response_length, 0, TRUE); /* Dial the phone */ if (status & SS$_NORMAL) { /* AT&Dt enables DTR disconnect */ status = send_com(com_chan, "AT&D2\015"); response_length = RESPONSE_SIZE; status= read_com(com_chan, response_buffer, &response_length, 1, FALSE); /* AT&C1 enables carrier sense */ status = send_com(com_chan, "AT&C1\015"); response_length = RESPONSE_SIZE; status= read_com(com_chan, response_buffer, &response_length, 1, FALSE); /* Now figure out how to dial the number */ if ((number[0] == 'P') || (number[1] == 'D')) sprintf(send_buffer, "ATD%s\015", number); else sprintf(send_buffer, "ATDT%s\015", number); status = send_com(com_chan, send_buffer); /* Now wait for call to complete */ if (status & SS$_NORMAL) { printf("Dialing %s\n", number); /* Loop up to 30 times (once every 2 seconds) trying to read a response from the modem. */ purge_flag = TRUE; do { response_length = RESPONSE_SIZE; read_status = read_com(com_chan, response_buffer, &response_length, 2, purge_flag); purge_flag = FALSE; /* This is a DMF32 then we may have answered the call but not received the connect message. The problem is that until CD is raised the DMF drops all characters sent back from the modem. It is possible for the modem to send back the connect message before CD is raised and it will be lost this avoids that problem. It does this at the expense of not being able to alter the line speed it needed. */ if ((dmf_32) && (response_length == 0)) { status = sys$qiow(0, com_chan, IO$_SENSEMODE | IO$M_RD_MODEM, &iosb, 0, 0, &modem_state, 0, 0, 0, 0, 0); /* Should also check IOSB but if setup OK IOSB will be success for this call */ if ((status & SS$_NORMAL) && (modem_state.receive_modem & TT$M_DS_CARRIER)) { response_length = sprintf(response_buffer, "CONN"); status = CALL_COMPLETE; break; /* get out of loop */ } } status = LINE_BUSY; /* Assume a call failure */ /* Check to see what response we got from modem */ if (response_length) { switch (response_buffer[0]) { case 'B' : /* Phone was busy */ status = LINE_BUSY; i = 31; break; case 'C' : /* Answered */ status = CALL_COMPLETE; i = 31; break; case 'E' : /* Modem reported error */ status = DIAL_ERROR; i = 31; break; case 'R' : /* Phone is ringing */ status = RINGING; break; } /* Check for no answer and no dial tone responses they are are too long to use simple case statement. */ if (strncmp(response_buffer, "NO A", 4) == 0) { status = NO_ANSWER; break; } else if (strncmp(response_buffer, "NO D", 4) == 0) { status = NO_DIAL_TONE; break; } } } while (i++ <= 30); /* If call completed then see if we got a change modem speed request */ if (status == CALL_COMPLETE) { if (strstr(response_buffer, "12")) { if (saved_speed != TT$C_BAUD_1200) { saved_speed = TT$C_BAUD_1200; printf("Speed falling back to 1200 baud\n"); } } else if (strstr(response_buffer, "24")) { if (saved_speed != TT$C_BAUD_2400) { saved_speed = TT$C_BAUD_2400; printf("Speed falling back to 2400 baud\n"); } } else if (strstr(response_buffer, "CONNECT")) { if (saved_speed != TT$C_BAUD_300) { saved_speed = TT$C_BAUD_300; printf("Speed falling back to 300 baud\n"); } } /* Set the line to the desired speed */ read_status = set_speed(com_chan, saved_speed); } } } } return status; } /* **+ ** read_com - This routine reads responses from the modem ** ** Functional Description: ** ** This routine reads responses from the modem. ** ** Explicit Inputs: ** ** com_chan - Pointer to terminal ** purge - Flag indicating if type ahead buffer should be ** purged ** size - Lenght of string and count of characters read ** string - Pointer to character string to be output ** time - Timeout period for read ** ** Implicit Inputs: ** ** None ** ** Local Variables: ** ** func - Read function code ** status - Return status from program ** termin - Terminator mask short form with ASCII 0 - 31 ** clear ** ** iosb - VMS I/O status block ** ** Outputs: ** ** status - return status from system calls ** ** NOTES: ** ** None ** **- */ int read_com(short int com_chan, char *string, int *size, int time, int purge) { int func = IO$_READVBLK | IO$M_NOECHO | IO$M_TIMED; int status; int termin[2]; struct read_iosb iosb; termin[0] = 0; /* No terminators for ASCII 0-31 */ termin[1] = 0; #ifdef DEBUG printf("Read_Com called with TIME= %d 'Purge= %d\n" time, purge); #endif /* If type-ahead is to be purged then add in purge flag */ if (purge) func = func | IO$M_PURGE; /* Now read the modems response */ status = sys$qiow (0, com_chan, func, &iosb, 0, 0, string, RESPONSE_SIZE, time, termin, 0, 0); if (!(status & SS$_NORMAL)) return status; status = iosb.status; if (status == SS$_TIMEOUT) status = 1; *size = iosb.byte_cnt + iosb.terminator_size; /* Size including terminator */ string[*size] = '\0'; #ifdef DEBUG if (size .gt. 0) printf("Got string %s ", string); #endif return status; } /* **+ ** send_com - This routine outputs a string ** ** Functional Description: ** ** This routine will a string to the specified terminal line. ** ** Explicit Inputs: ** ** com_chan - Pointer to terminal ** string - Pointer to character string to be output ** ** Implicit Inputs: ** ** None ** ** Local Variables: ** ** status - Return status from program ** ** iosb - VMS I/O status block ** ** Outputs: ** ** status - return status from system calls ** ** NOTES: ** ** None ** **- */ int send_com(short int com_chan, char *string) { int status; struct write_iosb iosb; status = sys$qiow(0, com_chan, IO$_WRITEVBLK | IO$M_NOFORMAT, &iosb, 0, 0, string, strlen(string), 0, 0, 0, 0); /* Make sure $QIO setup is OK */ if (!(status & SS$_NORMAL)) return status; return iosb.status; } /* **+ ** get_speed - This routine is called to read the terminals speed ** ** Functional Description: ** ** This routine is called to read the specified terminals line speed. ** ** Explicit Inputs: ** ** com_chan - Pointer to terminal ** speed - New input and output speed for terminal line ** ** Implicit Inputs: ** ** None ** ** Local Variables: ** ** status - Return status from program ** ** chars - Device characteristics data structure ** iosb - VMS I/O status block ** ** Outputs: ** ** status - Indicates if request completed correctly ** ** NOTES: ** ** None ** **- */ int get_speed(short int com_chan, int *speed) { int status; struct set_iosb iosb; struct char_buff chars; /* In order to change the device characteristics we must first read them */ status = sys$qiow (0, com_chan, IO$_SENSEMODE, &iosb, 0, 0, &chars, 12, 0, 0, 0,0); /* Make sure $QIO setup is OK */ if (!(status & SS$_NORMAL)) return status; /* See if QIO completed correctly */ if (!(iosb.status & SS$_NORMAL)) return iosb.status; *speed = iosb.xmit_speed & 0xff; return iosb.status; } /* **+ ** set_speed - This routine is called to alter speed of device ** ** Functional Description: ** ** This routine is called to change the speed of the device pointed to by ** the com_chan parameter. ** ** Explicit Inputs: ** ** com_chan - Pointer to terminal ** speed - New input and output speed for terminal line ** ** Implicit Inputs: ** ** None ** ** Local Variables: ** ** status - Return status from program ** ** chars - Device characteristics data structure ** iosb - VMS I/O status block ** ** Outputs: ** ** status - Indicates if request completed correctly ** ** NOTES: ** ** None ** **- */ int set_speed(short int com_chan, int speed) { int status; struct set_iosb iosb; struct char_buff chars; /* In order to change the device characteristics we must first read them */ status = sys$qiow (0, com_chan, IO$_SENSEMODE, &iosb, 0, 0, &chars, 12, 0, 0, 0,0); /* Make sure $QIO setup is OK */ if (!(status & SS$_NORMAL)) return status; /* See if QIO completed correctly */ if (!(iosb.status & SS$_NORMAL)) return iosb.status; /* Set the new device speed */ status = sys$qiow(0, com_chan, IO$_SETMODE, &iosb, 0, 0, &chars, 12, speed, 0,0, 0); /* Make sure $QIO setup is OK */ if (!(status & SS$_NORMAL)) return status; /* See if QIO completed correctly */ if (!(iosb.status & SS$_NORMAL)) return iosb.status; else return status; }