/* Copyright (C) Magna Carta Software, Inc. 1990, 1991. All Rights Reserved. C COMMUNICATIONS TOOLKIT MODEM.C -- MODEM INTERFACE ROUTINES */ #define CCT_DEVELOPMENT #if (defined(CCTW) || defined(_WINDOWS)) #include #endif #include #include #include #include #include #if defined (__cplusplus) #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif char hayes_default_init[] = ""; char hayes_1200_init[MODEM_BUF_SIZE]= "X1"; char hayes_1200ef_init[] = "L1X4"; char hayes_2400_init[] = "&C1&D2X4S11=50"; char hayes_9600_init[] = "&C1&D2X4S11=50"; char telebit_init[] = "X3"; /* THE ARRAY OF MODEM RESPONSES TO DIAL COMMANDS. COLUMN 0 -- VERBAL CODES. COLUMN 1 -- NUMERIC CODES WHEN COMMAND ECHO IS ENABLED. COLUMN 1 -- NUMERIC CODES WHEN COMMAND ECHO IS DISABLED. */ char *a_rc[][3] = { { "PROTOCOL:ERROR-CONTROL/AFT\r\n", "\r73\r", "73\r"}, { "PROTOCOL:ERROR-CONTROL/LAP-B/HDX\r\n", "\r72\r", "72\r"}, { "PROTOCOL:ERROR-CONTROL/LAP-B\r\n", "\r71\r", "71\r"}, { "PROTOCOL:NONE\r\n", "\r70\r", "70\r"}, { "CARRIER 9600", "\r50\r", "50\r"}, { "CARRIER 2400", "\r47\r", "47\r"}, { "CARRIER 1200", "\r46\r", "46\r"}, { "CARRIER 300", "\r40\r", "40\r"}, { "CONNECT 19200", "\r14\r", "14\r"}, { "CONNECT 9600", "\r12\r", "12\r"}, { "CONNECT 4800", "\r11\r", "11\r"}, { "CONNECT 2400", "\r10\r", "10\r"}, { "CONNECT 600", "\r9\r", "9\r" }, /* Intel Connection CoProcessor */ { "NO ANSWER", "\r8\r", "8\r" }, { "BUSY", "\r7\r", "7\r" }, { "NO DIAL TONE", "\r6\r", "6\r" }, { "NO DIALTONE", "\r6\r", "6\r" }, { "CONNECT 1200", "\r5\r", "5\r" }, { "ERROR", "\r4\r", "4\r" }, { "NO CARRIER", "\r3\r", "3\r" }, { "RING", "\r2\r", "2\r" }, { "CONNECT", "\r1\r", "1\r" }, { "OK", "\r0\r", "0\r" }, { NULL, NULL, NULL }, }; /* MODEM_ASSIGN_ -- Assign values to the MODEM structure if necessary. May be called directly but, if not, called automatically by: isamodem() modem_dial() modem_init() modem_reset() modem_set() Flag set if MODEM is dynamically allocated so the structure can be freed on exit. Parameters: COMM_PORT *p -- ptr. to the port; MODEM *m -- ptr. to a modem structure to be initialized; Normally passed as NULL to dynamically allocate MODEM. Return Value: 0 -- OK; EOF -- modem apparently already assigned to this port; NO_RAM -- insufficient memory to allocate MODEM; */ short modem_assign_(COMM_PORT *p, MODEM *m) { /* ASSUME THAT MODEM ALREADY ALLOCATED STATICALLY */ if (p->modem != NULL && !isdynamic(m)) return (EOF); if (p->modem != NULL && isdynamic(m)) { memfree(m); p->modem = NULL; } if (m == NULL) { if ((m = (MODEM *) memcalloc(1, sizeof(MODEM))) == NULL) return (NO_RAM); m->flags |= CCT_DYNAMIC; /* tell CCT it is dynamically allocated */ } p->modem = m; if (*m->dial_cmd == '\0') strcpy(m->dial_cmd, "D"); if (*m->echo == '\0') strcpy(m->echo, "E1"); if (*m->speaker == '\0') strcpy(m->speaker, "M1"); if (*m->quiet == '\0') strcpy(m->quiet, "Q0"); if (*m->verbose == '\0') strcpy(m->verbose, "V1"); if (*m->reset == '\0') strcpy(m->reset, "Z"); if (*m->escape == '\0') strcpy(m->escape, "S2=043"); if (*m->csi == '\0') strcpy(m->csi, "AT"); if (*m->eocstr == '\0') strcpy(m->eocstr, "\r"); if (m->a_rc == NULL) m->a_rc = (char *) a_rc; if (m->repeat == 0) m->repeat = 3; if (m->tx_ibdelay == 0) m->tx_ibdelay = 50; if (m->rx_ibdelay == 0) m->rx_ibdelay = 500; if (m->guardtime == 0) m->guardtime = 1050; if (m->rx_lbdelay == 0) m->rx_lbdelay = 2500; if (m->tx_delay == 0) m->tx_delay = 500; if (m->tx_delaych == 0) m->tx_delaych = '~'; if (*m->pause_len == '\0') strcpy(m->pause_len, "S8=2"); if (*m->dcd_wait == '\0') strcpy(m->dcd_wait, "S7=60"); if (*m->tone_wait == '\0') strcpy(m->tone_wait, "S6=2"); if (*m->rings == '\0') strcpy(m->rings, "S0=0"); if (*m->dial_mode == '\0') strcpy(m->dial_mode, "T"); if (*m->okalpha == '\0') strcpy(m->okalpha, ",*#()-R;"); /* FLAG THAT INIT. IS COMPLETED */ m->flags |= CCT_INIT_OK; return (0); } /* MODEM_CMD -- Send a command to the modem, retry if necessary and convert the response to a digit ("icmd" means return an integral value). Return value: 0-n -- success (OK); EOF -- fail (!OK, usually ERROR); */ short modem_cmd(COMM_PORT *p, char *cmd) { short ret; char *r; short count = ((MODEM *) p->modem)->repeat; do { if ((r = modem_scmd(p, cmd, MODEM_REPLY_LEN)) != NULL) if ((ret = modem_parse_reply(r)) != EOF) return (ret); } while (--count > 0); return (EOF); } /* MODEM_DIAL -- Dial the phone and wait for result code. Return value: EOF -- length of number to dial is 0; NO_RAM -- insufficient memory to allocate MODEM; USER_CANCELLED -- abort key pressed during dial; 0 - n -- numeric modem response code; */ short modem_dial(COMM_PORT *p, const char *number) { short ret; char dialstr[MODEM_BUF_SIZE]; MODEM *m; if (p->modem == NULL) ret = modem_assign_(p, NULL); if (ret == NO_RAM) return (NO_RAM); m = (MODEM *) p->modem; strcpy(dialstr, m->dial_cmd); strcat(dialstr, m->dial_mode); strcat(dialstr, number); set_dtr(p, HIGH); modem_set(p, dialstr); mspause(500); /* wait one half second, then flush any echo */ c_rxflush(p, 0); ret = modem_wait_dial_response(p); if (ret >= 0 && !modem_is_fixedspeed(m) && m->carrier) set_speed(p, (long) m->carrier); if (ret == USER_CANCELLED) c_putc(p, CR); /* allow for DCD not TRUE */ if (ret < 0) set_dtr(p, LOW); /* cancel call */ return (ret); } /* MODEM_GET -- Return the response from the modem after issuing a command to it. Return value: A pointer to the verbal modem response; NULL -- error; */ char *modem_get(COMM_PORT *p, short count) { char sxlat[XLAT_SIZE]; MODEM *m = (MODEM *) p->modem; char *r = m->buf; save_xlat(p, sxlat); /* save our XLAT state */ count = (count >= MODEM_BUF_SIZE) ? MODEM_BUF_SIZE : count; p->rx_xlat = ON; if (p->rx_lbdelay < m->rx_lbdelay) p->rx_lbdelay = m->rx_lbdelay; if (p->rx_ibdelay < m->rx_ibdelay) p->rx_ibdelay = m->rx_ibdelay; if (c_gets(p, r, count, EOF) <= 0) r = NULL; rest_xlat(p, sxlat); /* restore our XLAT status */ return (r); } /* MODEM_GO_CMD -- Go from online state to command state. Return Value: Always 0 in current implementation; */ short modem_go_cmd(COMM_PORT *p, short escchar) { short i; short ret = 0; char cmd[MODEM_REPLY_LEN+1]; char sxlat[XLAT_SIZE]; MODEM *m = (MODEM *) p->modem; if (get_dcd(p)) { save_xlat(p, sxlat); /* save our XLAT state */ p->tx_xlat = ON; p->tx_delay = m->guardtime; p->tx_delaych = m->tx_delaych; cmd[0] = '~'; for (i=1; i< 4; i++) cmd[i] = (char) escchar; cmd[i++] = '~'; cmd[i] = '\0'; c_puts(p, cmd); rest_xlat(p, sxlat); /* restore our XLAT status */ } return (ret); } /* MODEM_HANGUP -- Hangup the modem. Return Value: 0-n -- success; EOF -- error; */ short modem_hangup(COMM_PORT *p) { short ret, save_dtr; if (p->modem == NULL) ret = modem_assign_(p, NULL); if (ret == NO_RAM) return (NO_RAM); save_dtr = get_dtr(p); /* save current setting of DTR */ set_dtr(p, LOW); mspause(((MODEM *) p->modem)->guardtime); if ((ret = get_dcd(p)) == HIGH) { modem_go_cmd(p, atoi(&((MODEM *) p->modem)->escape[3])); ret = modem_cmd(p, "H0"); } set_dtr(p, save_dtr); /* restore DTR setting */ return (ret); } /* MODEM_INIT -- Initialize the modem. This routine is designed to be as generic as possible to be compatible with the widest range of modems in use. "type" is the type of modem installed. Return value: 0 -- success; NO_RAM -- insufficient memory to allocate MODEM; */ short modem_init(COMM_PORT *p, short type) { short ret = 0; MODEM *m; if (p->modem == NULL) ret = modem_assign_(p, NULL); if (ret == NO_RAM) return (NO_RAM); m = (MODEM *) p->modem; strcpy(m->init, ""); /* INITIALIZE MODEM PARAMETERS THAT EXTEND THE SMARTMODEM 300 STD. */ /* EACH IS INITIALIZED TO ITS FACTORY DEFAULTS EXCEPT FOR 'X' */ switch (type) { case HAYES_300: m->max_speed = 300; break; case HAYES_1200: case FALSE: m->max_speed = 1200; strcat(m->init, hayes_1200_init); break; case HAYES_1200EF: m->max_speed = 1200; strcat(m->okalpha, "!@W"); /* new dial modifiers */ strcat(m->init, hayes_1200ef_init); break; case HAYES_2400: default: m->max_speed = 2400; strcat(m->okalpha, "!/@W"); /* new dial modifiers */ ret = modem_cmd(p, hayes_2400_init); break; case HAYES_9600: case UDS_9600: case USR_9600_DUAL_STD: m->max_speed = 9600; strcat(m->okalpha, "!/@WS"); /* new dial modifiers */ ret = modem_cmd(p, hayes_9600_init); break; case TELEBIT_RA12C: case TELEBIT_RA12E: case TELEBIT_T18PC: case TELEBIT_T18SA: m->max_speed = 9600; strcat(m->okalpha, "!/\\NRW"); /* new dial modifiers */ strcat(m->init, telebit_init); break; } /* CONSTRUCT INITIALIZATION STRING */ strcat(m->init, m->dial_mode); strcat(m->init, m->echo); strcat(m->init, m->verbose); strcat(m->init, m->rings); strcat(m->init, m->escape); strcat(m->init, m->dcd_wait); strcat(m->init, m->tone_wait); strcat(m->init, m->speaker); if (ret >= 0) { ret = modem_cmd(p, m->init); modem_cmd(p, ""); } if (!ret) m->flags |= CCT_INIT_OK; /* show that init. is completed */ return (ret); } /* MODEM_PARSE_REPLY -- Get the modem response to a command (especially a dial command). This may be a string, or numeric. Convert it to a digit. First scan column 0, rows from last to first and then increment the column. Parameters: char *s -- actual response from modem; Return value: EOF -- Returned value not valid; 0-n -- Modem response code; */ short modem_parse_reply(char *s) { short i; short j = 0; do { for (i = 0; a_rc[i][j] != NULL; i++) if (strstr(s, a_rc[i][j]) != NULL) return (atoi(a_rc[i][2])); } while (++j < 3); return (EOF); } /* MODEM_RESET -- Reset the modem to the factory defaults. This function should be called before dialling operations and after isamodem(); Return value: 0 -- success; EOF -- error, data transfer in progress; NO_RAM -- Insufficient memory to allocate for MODEM; USER_CANCELLED -- abort key pressed; */ short modem_reset(COMM_PORT *p) { MODEM *m; short tr, ret, j; short DONE = FALSE; char sxlat[XLAT_SIZE]; if (p->modem == NULL) ret = modem_assign_(p, NULL); /* create a modem */ if (ret == NO_RAM) return (NO_RAM); m = (MODEM *) p->modem; if (c_rxflush(p, MODEM_BUF_SIZE) == FALSE) return (EOF); save_xlat(p, sxlat); p->rx_lbdelay = p->rx_ibdelay = m->rx_lbdelay; tr = m->repeat; if (!m->repeat) m->repeat = 2; /* FIRST TRY LOWERING AND RAISING DTR */ set_dtr(p, LOW); mspause(2000); /* wait. Early Hayes require > 2 secs. */ set_dtr(p, HIGH); /* NEXT TRY ISSUING THE RESET COMMAND AT EACH BAUD RATE */ for (j=0; j < m->repeat && !DONE; j++) { if (is_key(p->abort_key)) { DONE = TRUE; ret = USER_CANCELLED; break; } if ((ret = modem_cmd(p, m->reset)) == 0) DONE = TRUE; } m->repeat = tr; rest_xlat(p, sxlat); return (ret); } /* MODEM_SCMD -- The workhorse of program communication with the modem. Issue a command and get the response as a string ("scmd" means return a string value). Return value: NULL -- Receive buffer has more characters in it on entry than the modem buffer can hold. Assume that we are not in command mode and exit. */ char *modem_scmd(COMM_PORT *p, char *cmd, WORD reply_len) { c_rxflush(p, 0); modem_set(p, cmd); return (modem_get(p, reply_len)); } /* MODEM_SET -- Send a command to the modem. Timing parameters are set conservatively to accomodate slow modems. Return Code: 0 -- success; NO_RAM -- Insufficient memory to allocate for MODEM; */ short modem_set(COMM_PORT *p, char *cmd) { MODEM *m; short save_dtr, ret; char sxlat[XLAT_SIZE], buf[MODEM_BUF_SIZE]; if (p->modem == NULL) ret = modem_assign_(p, NULL); /* assign a modem */ if (ret == NO_RAM) return (NO_RAM); m = (MODEM *) p->modem; /* MAKE SURE THAT DTR IS ASSERTED */ if ((save_dtr = get_dtr(p)) == LOW) set_dtr(p, HIGH); save_xlat(p, sxlat); /* save our XLAT state */ p->tx_xlat = ON; p->tx_case_convert = UPPER; p->tx_ascii_only = TRUE; p->tx_ibdelay = m->tx_ibdelay; p->tx_delay = m->tx_delay; p->tx_delaych = m->tx_delaych; strcpy(buf, m->csi); strcat(buf, m->quiet); strcat(buf, cmd); strcat(buf, m->eocstr); c_puts(p, buf); if (save_dtr == LOW) set_dtr(p, LOW); rest_xlat(p, sxlat); /* restore our XLAT status */ return (0); } /* MODEM_TYPE -- An attempt to identify the type of modem installed. Return value: EOF -- MODEM not assigned to COMM_PORT or not present; NO_RAM -- Insufficient memory for MODEM; FALSE -- Not recognized; Otherwise: Hayes modem type; */ short modem_type(COMM_PORT *p) { char *s; short id; short ret = 0; if (p->modem == NULL) ret = modem_assign_(p, NULL); if (ret == NO_RAM) return (NO_RAM); ret = modem_cmd(p, "V1"); if (ret != EOF) { if ((s = modem_scmd(p, "I0", MODEM_REPLY_LEN)) != NULL) { id = 0; while (*s) { if (isdigit(*s)) id = id*10 + (*s & 0XF); ++s; } switch (id) { case TELEBIT_RA12C: ret = TELEBIT_RA12C; break; case TELEBIT_RA12E: ret = TELEBIT_RA12E; break; case TELEBIT_RM12C: ret = TELEBIT_RM12C; break; case TELEBIT_T18PC: ret = TELEBIT_T18PC; break; case TELEBIT_T18SA: ret = TELEBIT_T18SA; break; case TELEBIT_T18RMM: ret = TELEBIT_T18RMM; break; case UDS_9600: ret = UDS_9600; break; case USR_9600_DUAL_STD: ret = USR_9600_DUAL_STD; break; default: ret = FALSE; } if (ret == FALSE) switch (id/10) { case 12: ret = HAYES_1200; break; case 13: ret = HAYES_1200EF; break; case 14: ret = USR_9600_DUAL_STD; break; case 24: ret = HAYES_2400; break; case 90: ret = UDS_9600; break; case 96: ret = HAYES_9600; break; default: ret = FALSE; } } } ((MODEM *)p->modem)->mtype = ret; return (ret); } /* MODEM_WAIT_DIAL_RESPONSE -- After a dial command to the modem, wait for the response. Return value: EOF -- receive buffer full (not in command mode?); -2 -- invalid response for modem; USER_CANCELLED -- abort key pressed from keyboard; 0-n -- valid modem response; */ short modem_wait_dial_response(COMM_PORT *p) { short mret, i, save_kbd, f_i; short ret = FALSE; char response[MODEM_BUF_SIZE]; char sa[MODEM_BUF_SIZE], *psa = sa; char sxlat[XLAT_SIZE]; MODEM *m = (MODEM *) p->modem; save_xlat(p, sxlat); /* save our XLAT state */ save_kbd = kbd_clear; kbd_clear = TRUE; p->rx_xlat = ON; p->rx_lbdelay = min(atoi(&m->dcd_wait[3]), 64) * 1000; p->rx_ibdelay = m->rx_ibdelay; memset(response, 0X0, sizeof(response)); do { do { mret = c_gets(p, response, MODEM_REPLY_LEN, EOF); } while (mret == 0 || mret == EOF); if (mret > 0) ret = modem_parse_reply(response); else if (mret == USER_CANCELLED) { ret = USER_CANCELLED; break; } } while (ret < 0 || ret == 52 || ret == 2); if (ret >= 0) { memset(sa, 0X0, sizeof(sa)); /* GET THE CONNECT SPEED */ for (f_i=i=0; i < MODEM_BUF_SIZE && response[i] != '\0'; i++) { if (isdigit(response[i])) { *psa++ = response[i]; f_i = TRUE; } else if (f_i) break; } m->carrier = atoi(sa); if (strstr(response, "REL") != NULL) m->ec |= 1; if (strstr(response, "V42") != NULL) m->ec |= 2; if (strstr(response, "00T") != NULL) m->mod |= 1; if (strstr(response, "COMPRESS") != NULL) m->comp |= 1; } else ret = USER_CANCELLED; kbd_clear = save_kbd; rest_xlat(p, sxlat); /* restore our XLAT status */ return (ret); }