1156952Sume/*	$OpenBSD: hayes.c,v 1.13 2006/03/17 19:17:13 moritz Exp $	*/
2156952Sume/*	$NetBSD: hayes.c,v 1.6 1997/02/11 09:24:17 mrg Exp $	*/
3156952Sume
4156952Sume/*
5156952Sume * Copyright (c) 1983, 1993
6156952Sume *	The Regents of the University of California.  All rights reserved.
7156952Sume *
8156952Sume * Redistribution and use in source and binary forms, with or without
9156952Sume * modification, are permitted provided that the following conditions
10156952Sume * are met:
11156952Sume * 1. Redistributions of source code must retain the above copyright
12156952Sume *    notice, this list of conditions and the following disclaimer.
13156952Sume * 2. Redistributions in binary form must reproduce the above copyright
14156952Sume *    notice, this list of conditions and the following disclaimer in the
15156952Sume *    documentation and/or other materials provided with the distribution.
16156952Sume * 3. Neither the name of the University nor the names of its contributors
17156952Sume *    may be used to endorse or promote products derived from this software
18156952Sume *    without specific prior written permission.
19156952Sume *
20156952Sume * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21156952Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22156952Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23156952Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24156952Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25156952Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26156952Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27156952Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28156952Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29156952Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30156952Sume * SUCH DAMAGE.
31156952Sume */
32156952Sume
33156952Sume#include <sys/cdefs.h>
34156952Sume__FBSDID("$FreeBSD$");
35156952Sume
36156952Sume#ifndef lint
37156952Sume#if 0
38156952Sumestatic char sccsid[] = "@(#)hayes.c	8.1 (Berkeley) 6/6/93";
39156952Sumestatic const char rcsid[] = "$OpenBSD: hayes.c,v 1.13 2006/03/17 19:17:13 moritz Exp $";
40156952Sume#endif
41156952Sume#endif /* not lint */
42156952Sume
43156952Sume/*
44156952Sume * Routines for calling up on a Hayes Modem
45156952Sume * (based on the old VenTel driver).
46156952Sume * The modem is expected to be strapped for "echo".
47156952Sume * Also, the switches enabling the DTR and CD lines
48156952Sume * must be set correctly.
49156952Sume * NOTICE:
50156952Sume * The easy way to hang up a modem is always simply to
51156952Sume * clear the DTR signal. However, if the +++ sequence
52156952Sume * (which switches the modem back to local mode) is sent
53156952Sume * before modem is hung up, removal of the DTR signal
54156952Sume * has no effect (except that it prevents the modem from
55156952Sume * recognizing commands).
56156952Sume * (by Helge Skrivervik, Calma Company, Sunnyvale, CA. 1984)
57156952Sume */
58156952Sume/*
59156952Sume * TODO:
60156952Sume * It is probably not a good idea to switch the modem
61156952Sume * state between 'verbose' and terse (status messages).
62156952Sume * This should be kicked out and we should use verbose
63156952Sume * mode only. This would make it consistent with normal
64156952Sume * interactive use thru the command 'tip dialer'.
65156952Sume */
66156952Sume#include "tip.h"
67156952Sume
68156952Sume#include <termios.h>
69156952Sume#include <sys/ioctl.h>
70156952Sume
71156952Sume#define	min(a,b)	((a < b) ? a : b)
72156952Sume
73156952Sumestatic	int dialtimeout = 0;
74156952Sumestatic	jmp_buf timeoutbuf;
75156952Sume
76156952Sume#define DUMBUFLEN	40
77156952Sumestatic char dumbuf[DUMBUFLEN];
78156952Sume
79156952Sume#define	DIALING		1
80156952Sume#define IDLE		2
81156952Sume#define CONNECTED	3
82156952Sume#define	FAILED		4
83156952Sumestatic	int state = IDLE;
84156952Sume
85156952Sumestatic void	sigALRM(int);
86156952Sumestatic char	gobble(char *);
87156952Sumestatic void	error_rep(char);
88156952Sumestatic void	goodbye(void);
89156952Sumestatic int	hay_sync(void);
90156952Sume
91156952Sumeint
92156952Sumehay_dialer(char *num, char *acu)
93156952Sume{
94156952Sume	char *cp;
95156952Sume	int connected = 0;
96156952Sume	char dummy;
97156952Sume	struct termios cntrl;
98156952Sume#ifdef ACULOG
99156952Sume	char line[80];
100156952Sume#endif
101156952Sume	if (hay_sync() == 0)		/* make sure we can talk to the modem */
102156952Sume		return(0);
103156952Sume	if (boolean(value(VERBOSE)))
104156952Sume		printf("\ndialing...");
105156952Sume	fflush(stdout);
106156952Sume	tcgetattr(FD, &cntrl);
107156952Sume	cntrl.c_cflag |= HUPCL;
108156952Sume	tcsetattr(FD, TCSANOW, &cntrl);
109156952Sume	tcflush(FD, TCIOFLUSH);
110156952Sume	write(FD, "ATv0\r", 5);	/* tell modem to use short status codes */
111156952Sume	gobble("\r");
112156952Sume	gobble("\r");
113156952Sume	write(FD, "ATTD", 4);	/* send dial command */
114156952Sume	for (cp = num; *cp; cp++)
115156952Sume		if (*cp == '=')
116156952Sume			*cp = ',';
117156952Sume	write(FD, num, strlen(num));
118156952Sume	state = DIALING;
119156952Sume	write(FD, "\r", 1);
120156952Sume	connected = 0;
121156952Sume	if (gobble("\r")) {
122156952Sume		if ((dummy = gobble("01234")) != '1')
123156952Sume			error_rep(dummy);
124156952Sume		else
125156952Sume			connected = 1;
126156952Sume	}
127156952Sume	if (connected)
128156952Sume		state = CONNECTED;
129156952Sume	else {
130156952Sume		state = FAILED;
131156952Sume		return (connected);	/* lets get out of here.. */
132156952Sume	}
133156952Sume	tcflush(FD, TCIOFLUSH);
134156952Sume#ifdef ACULOG
135156952Sume	if (dialtimeout) {
136156952Sume		(void)snprintf(line, sizeof line, "%ld second dial timeout",
137156952Sume			number(value(DIALTIMEOUT)));
138156952Sume		logent(value(HOST), num, "hayes", line);
139156952Sume	}
140156952Sume#endif
141156952Sume	if (dialtimeout)
142156952Sume		hay_disconnect();	/* insurance */
143156952Sume	return (connected);
144156952Sume}
145156952Sume
146156952Sumevoid
147156952Sumehay_disconnect(void)
148156952Sume{
149156952Sume	/* first hang up the modem*/
150156952Sume#ifdef DEBUG
151156952Sume	printf("\rdisconnecting modem....\n\r");
152156952Sume#endif
153156952Sume	ioctl(FD, TIOCCDTR, 0);
154156952Sume	sleep(1);
155156952Sume	ioctl(FD, TIOCSDTR, 0);
156156952Sume	goodbye();
157156952Sume}
158156952Sume
159156952Sumevoid
160156952Sumehay_abort(void)
161156952Sume{
162156952Sume	write(FD, "\r", 1);	/* send anything to abort the call */
163156952Sume	hay_disconnect();
164156952Sume}
165156952Sume
166156952Sume/*ARGSUSED*/
167156952Sumestatic void
168156952SumesigALRM(int signo)
169156952Sume{
170156952Sume	printf("\07timeout waiting for reply\n\r");
171156952Sume	dialtimeout = 1;
172156952Sume	longjmp(timeoutbuf, 1);
173156952Sume}
174156952Sume
175156952Sumestatic char
176156952Sumegobble(char *match)
177156952Sume{
178156952Sume	char c;
179156952Sume	sig_t f;
180156952Sume	size_t i;
181156952Sume	int status = 0;
182156952Sume
183156952Sume	f = signal(SIGALRM, sigALRM);
184156952Sume	dialtimeout = 0;
185156952Sume#ifdef DEBUG
186156952Sume	printf("\ngobble: waiting for %s\n", match);
187156952Sume#endif
188156952Sume	do {
189156952Sume		if (setjmp(timeoutbuf)) {
190156952Sume			signal(SIGALRM, f);
191156952Sume			return (0);
192156952Sume		}
193156952Sume		alarm(number(value(DIALTIMEOUT)));
194156952Sume		read(FD, &c, 1);
195156952Sume		alarm(0);
196156952Sume		c &= 0177;
197156952Sume#ifdef DEBUG
198156952Sume		printf("%c 0x%x ", c, c);
199156952Sume#endif
200156952Sume		for (i = 0; i < strlen(match); i++)
201156952Sume			if (c == match[i])
202156952Sume				status = c;
203156952Sume	} while (status == 0);
204156952Sume	signal(SIGALRM, SIG_DFL);
205156952Sume#ifdef DEBUG
206156952Sume	printf("\n");
207156952Sume#endif
208156952Sume	return (status);
209156952Sume}
210156952Sume
211156952Sumestatic void
212156952Sumeerror_rep(char c)
213156952Sume{
214156952Sume	printf("\n\r");
215156952Sume	switch (c) {
216156952Sume
217156952Sume	case '0':
218156952Sume		printf("OK");
219156952Sume		break;
220156952Sume
221156952Sume	case '1':
222156952Sume		printf("CONNECT");
223156952Sume		break;
224156952Sume
225156952Sume	case '2':
226156952Sume		printf("RING");
227156952Sume		break;
228156952Sume
229156952Sume	case '3':
230156952Sume		printf("NO CARRIER");
231156952Sume		break;
232156952Sume
233156952Sume	case '4':
234156952Sume		printf("ERROR in input");
235156952Sume		break;
236156952Sume
237156952Sume	case '5':
238156952Sume		printf("CONNECT 1200");
239156952Sume		break;
240156952Sume
241156952Sume	default:
242156952Sume		printf("Unknown Modem error: %c (0x%x)", c, c);
243156952Sume	}
244156952Sume	printf("\n\r");
245156952Sume	return;
246156952Sume}
247156952Sume
248156952Sume/*
249156952Sume * set modem back to normal verbose status codes.
250156952Sume */
251156952Sumestatic void
252156952Sumegoodbye(void)
253156952Sume{
254156952Sume	int len;
255156952Sume	char c;
256156952Sume
257156952Sume	tcflush(FD, TCIOFLUSH);
258156952Sume	if (hay_sync()) {
259156952Sume		sleep(1);
260156952Sume#ifndef DEBUG
261156952Sume		tcflush(FD, TCIOFLUSH);
262156952Sume#endif
263156952Sume		write(FD, "ATH0\r", 5);		/* insurance */
264156952Sume#ifndef DEBUG
265156952Sume		c = gobble("03");
266156952Sume		if (c != '0' && c != '3') {
267156952Sume			printf("cannot hang up modem\n\r");
268156952Sume			printf("please use 'tip dialer' to make sure the line is hung up\n\r");
269156952Sume		}
270156952Sume#endif
271156952Sume		sleep(1);
272156952Sume		ioctl(FD, FIONREAD, &len);
273156952Sume#ifdef DEBUG
274156952Sume		printf("goodbye1: len=%d -- ", len);
275156952Sume		rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
276156952Sume		dumbuf[rlen] = '\0';
277156952Sume		printf("read (%d): %s\r\n", rlen, dumbuf);
278156952Sume#endif
279156952Sume		write(FD, "ATv1\r", 5);
280156952Sume		sleep(1);
281156952Sume#ifdef DEBUG
282156952Sume		ioctl(FD, FIONREAD, &len);
283156952Sume		printf("goodbye2: len=%d -- ", len);
284156952Sume		rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
285156952Sume		dumbuf[rlen] = '\0';
286156952Sume		printf("read (%d): %s\r\n", rlen, dumbuf);
287156952Sume#endif
288156952Sume	}
289156952Sume	tcflush(FD, TCIOFLUSH);
290156952Sume	ioctl(FD, TIOCCDTR, 0);		/* clear DTR (insurance) */
291156952Sume	close(FD);
292156952Sume}
293156952Sume
294156952Sume#define MAXRETRY	5
295156952Sume
296156952Sumestatic int
297156952Sumehay_sync(void)
298156952Sume{
299156952Sume	int len, retry = 0;
300156952Sume
301156952Sume	while (retry++ <= MAXRETRY) {
302156952Sume		write(FD, "AT\r", 3);
303156952Sume		sleep(1);
304156952Sume		ioctl(FD, FIONREAD, &len);
305156952Sume		if (len) {
306156952Sume			len = read(FD, dumbuf, min(len, DUMBUFLEN));
307156952Sume			if (strchr(dumbuf, '0') ||
308156952Sume		   	(strchr(dumbuf, 'O') && strchr(dumbuf, 'K')))
309156952Sume				return(1);
310156952Sume#ifdef DEBUG
311156952Sume			dumbuf[len] = '\0';
312156952Sume			printf("hay_sync: (\"%s\") %d\n\r", dumbuf, retry);
313156952Sume#endif
314156952Sume		}
315156952Sume		ioctl(FD, TIOCCDTR, 0);
316156952Sume		ioctl(FD, TIOCSDTR, 0);
317156952Sume	}
318156952Sume	printf("Cannot synchronize with hayes...\n\r");
319156952Sume	return(0);
320156952Sume}
321156952Sume