/* * Copyright (C) 2001-2003 R. David Quattlebaum * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ /* $Id: modem.c,v 1.23 2003/02/12 19:42:36 drq Exp $ */ /* * modem.c - all routines dealing I/O to the modem */ #ifdef WIN32 #include #include #else #include #include #include #include #include #include #include #include #endif #include #include "scud.h" #include "util.h" /* * option variables */ extern int verbose; extern int read_two_bytes; /* * modem_read_byte - read one byte from modem * * returns: 1 - one byte read * 0 - no byte available */ int last_timeout = 0; int modem_read_byte(int fd, char *ch, int timeout) { int n; #ifdef WIN32 COMMTIMEOUTS timeouts = {0}; /* * only reset the timeout if it changes from the last time */ if (timeout != last_timeout) { timeouts.ReadTotalTimeoutConstant = timeout*1000; // to seconds n = SetCommTimeouts((HANDLE)fd, &timeouts); last_timeout = timeout; } ReadFile((HANDLE)fd, ch, 1, &n, 0); #else fd_set readfd = {{0}}; struct timeval to = {0}; FD_SET(fd, &readfd); to.tv_sec = timeout; n = select(fd+1, &readfd, NULL, NULL, &to); if (n) n = read(fd, ch, 1); #endif if (!n) return(0); if (verbose >= 2) log_printf("; modem_read_byte: 0x%02x (%d) '%c'\n", *ch, *ch, isprint(*ch)?*ch:'.'); if (read_two_bytes) { char ch2; char buffer[4]; #ifdef WIN32 ReadFile((HANDLE)fd, &ch2, 1, &n, 0); #else n = read(fd, &ch2, 1); #endif if (!n) return(0); if (verbose >= 2) log_printf("; modem_read_byte: 0x%02x (%d) '%c'\n", ch2, ch2, isprint(ch2)?ch2:'.'); sprintf(buffer, "0x%c%c", *ch, ch2); sscanf(buffer, "%X", (unsigned int *)ch); } return(n); } /* * modem_read_string - read one string from modem * remove trailing \n * * returns: length of string (without \r \n or \0) * -1 if we timed out */ int modem_read_string(int fd, char *buffer, int buflen, int timeout) { int i = 0; int bytes; char ch; memset(buffer, 0, buflen); do { bytes = modem_read_byte(fd, &ch, timeout); if (bytes != 1) return(-1); if (ch != '\r' && ch != '\n') buffer[i++] = ch; } while (ch != '\n'); buffer[i] = 0; return(i); } /* * modem_read_response - wait for and read a response from the * modem for timeout seconds */ int modem_read_response(int fd, char *buf, int maxlen, int timeout) { int tlen; memset(buf, 0, maxlen); tlen = modem_read_string(fd, buf, maxlen, 2); if (!strncmp(buf, "OK", 2)) return(1); return(0); } /* * modem_send_command - send modem command to modem * * returns: 0 - success, !0 - error from write */ int modem_send_command(int fd, char *cmd) { char buffer[128]; int len = strlen(cmd); int bytes; /* send command if len is zero */ if (len > 0) { #ifdef WIN32 WriteFile((HANDLE)fd, cmd, len, &bytes, NULL); #else bytes = write(fd, cmd, len); #endif if (bytes != len) { log_printf("ERROR: Unable to write to modem, rc %d\n", bytes); return(bytes); } } /* now write carraige return */ #ifdef WIN32 WriteFile((HANDLE)fd, "\r", 1, &bytes, 0); #else write(fd, "\r", 1); #endif /* read the echoed command (or just the \r\n pair) */ len = modem_read_string(fd, buffer, sizeof(buffer), 2); return(0); } /* * modem_send_raw - send raw bytes to modem * * returns: 0 - number of bytes written */ int modem_send_raw(int fd, char *data, int len) { int bytes = len; /* send data if len is zero */ if (len > 0) { #ifdef WIN32 WriteFile((HANDLE)fd, data, len, &bytes, NULL); #else bytes = write(fd, data, len); #endif if (bytes != len) { log_printf("ERROR: Unable to write to modem, rc %d\n", bytes); return(bytes); } } return(bytes); } /* * reply_analyze - analyze the two strings * * returns: >= 0 - index of reply that matched reply * -1 - reply matches no valid_answers */ int reply_analyze(char *reply, char *valid_answers) { int rc = -1; int current; int answer_len; char *answer; char *next_answer; answer = valid_answers; for (current=0; ((answer != NULL) && (strlen(answer) > 0)); current++) { next_answer = strstr(answer, "|"); if (next_answer == NULL) answer_len = strlen(answer); else answer_len = (int)(next_answer - answer); if (!strncmp(reply, answer, answer_len)) { rc = current; break; } if (next_answer == NULL) answer = NULL; else answer = next_answer+1; } return(rc); } /* * modem_send_command_reply - send modem command to modem and wait * for given reply * * example: modem_send_command_reply(fd, "ATZ", "OK"); * * returns: n - index of item that matched reply (0-based) * -1 - reply text not found */ int modem_send_command_reply(int fd, char *cmd, char *reply) { char buffer[256]; int len = strlen(cmd); int rc; /* * send command to modem */ rc = modem_send_command(fd, cmd); /* * now wait for modem echo or response */ len = modem_read_string(fd, buffer, sizeof(buffer), 2); if (reply_analyze(buffer, cmd) == -1) { if ((rc = reply_analyze(buffer, reply)) >= 0) { if (verbose >= 1) log_printf("; send_modem_command_reply: %s: %s\n", cmd, buffer); return(rc); } } /* * now wait for modem to respond to command */ len = modem_read_string(fd, buffer, sizeof(buffer), 2); if (verbose >= 1) log_printf("; send_modem_command_reply: %s: %s\n", cmd, buffer); rc = reply_analyze(buffer, reply); return(rc); } int modem_open(char *device) { int fd; #ifdef WIN32 BOOL rc; DCB modemcb = {0}; fd = (int)CreateFile(device, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); if (fd == BAD_FD) { log_printf("Unable to open modem, errno %d\n", GetLastError()); return(-1); } modemcb.DCBlength = sizeof(modemcb); rc = GetCommState((HANDLE)fd, &modemcb); // Change the DCB structure settings. modemcb.BaudRate = 9600; // Current baud modemcb.fBinary = TRUE; // Binary mode; no EOF check modemcb.fParity = FALSE; // Enable parity checking modemcb.fOutxCtsFlow = FALSE; // No CTS output flow control modemcb.fOutxDsrFlow = FALSE; // No DSR output flow control modemcb.fDtrControl = DTR_CONTROL_ENABLE; modemcb.fDsrSensitivity = FALSE; // DSR sensitivity modemcb.fTXContinueOnXoff = TRUE; // XOFF continues Tx modemcb.fOutX = FALSE; // No XON/XOFF out flow control modemcb.fInX = FALSE; // No XON/XOFF in flow control modemcb.fErrorChar = FALSE; // Disable error replacement modemcb.fNull = FALSE; // Disable null stripping modemcb.fRtsControl = RTS_CONTROL_ENABLE; modemcb.fAbortOnError = FALSE; // Do not abort reads/writes on modemcb.ByteSize = 8; // Number of bits/byte, 4-8 modemcb.Parity = NOPARITY; // 0-4=no,odd,even,mark,space modemcb.StopBits = ONESTOPBIT; // 0,1,2 = 1, 1.5, 2 rc = SetCommState((HANDLE)fd, &modemcb); #else struct termios opts; /* open the serial port */ if ((fd = open(device, O_RDWR|O_NOCTTY|O_NONBLOCK)) == -1) { log_printf("ERROR: Unable to open modem, errno %d\n", errno); return(-1); } /* set the correct options */ tcgetattr(fd, &opts); opts.c_iflag = 0; opts.c_oflag = 0; opts.c_cflag &= ~ (PARENB | CSIZE | CSTOPB); opts.c_cflag |= (CS8 | HUPCL | CREAD | CLOCAL); opts.c_lflag = 0; opts.c_cc[VMIN] = 1; opts.c_cc[VTIME] = 0; tcsetattr(fd, TCSANOW, &opts); #endif return(fd); }