1/*	$OpenBSD: hayes.c,v 1.13 2006/03/17 19:17:13 moritz Exp $	*/
2/*	$NetBSD: hayes.c,v 1.6 1997/02/11 09:24:17 mrg Exp $	*/
3
4/*
5 * Copyright (c) 1983, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)hayes.c	8.1 (Berkeley) 6/6/93";
39static const char rcsid[] = "$OpenBSD: hayes.c,v 1.13 2006/03/17 19:17:13 moritz Exp $";
40#endif
41#endif /* not lint */
42
43/*
44 * Routines for calling up on a Hayes Modem
45 * (based on the old VenTel driver).
46 * The modem is expected to be strapped for "echo".
47 * Also, the switches enabling the DTR and CD lines
48 * must be set correctly.
49 * NOTICE:
50 * The easy way to hang up a modem is always simply to
51 * clear the DTR signal. However, if the +++ sequence
52 * (which switches the modem back to local mode) is sent
53 * before modem is hung up, removal of the DTR signal
54 * has no effect (except that it prevents the modem from
55 * recognizing commands).
56 * (by Helge Skrivervik, Calma Company, Sunnyvale, CA. 1984)
57 */
58/*
59 * TODO:
60 * It is probably not a good idea to switch the modem
61 * state between 'verbose' and terse (status messages).
62 * This should be kicked out and we should use verbose
63 * mode only. This would make it consistent with normal
64 * interactive use thru the command 'tip dialer'.
65 */
66#include "tip.h"
67
68#include <termios.h>
69#include <sys/ioctl.h>
70
71#define	min(a,b)	((a < b) ? a : b)
72
73static	int dialtimeout = 0;
74static	jmp_buf timeoutbuf;
75
76#define DUMBUFLEN	40
77static char dumbuf[DUMBUFLEN];
78
79#define	DIALING		1
80#define IDLE		2
81#define CONNECTED	3
82#define	FAILED		4
83static	int state = IDLE;
84
85static void	sigALRM(int);
86static char	gobble(char *);
87static void	error_rep(char);
88static void	goodbye(void);
89static int	hay_sync(void);
90
91int
92hay_dialer(char *num, char *acu)
93{
94	char *cp;
95	int connected = 0;
96	char dummy;
97	struct termios cntrl;
98#ifdef ACULOG
99	char line[80];
100#endif
101	if (hay_sync() == 0)		/* make sure we can talk to the modem */
102		return(0);
103	if (boolean(value(VERBOSE)))
104		printf("\ndialing...");
105	fflush(stdout);
106	tcgetattr(FD, &cntrl);
107	cntrl.c_cflag |= HUPCL;
108	tcsetattr(FD, TCSANOW, &cntrl);
109	tcflush(FD, TCIOFLUSH);
110	write(FD, "ATv0\r", 5);	/* tell modem to use short status codes */
111	gobble("\r");
112	gobble("\r");
113	write(FD, "ATTD", 4);	/* send dial command */
114	for (cp = num; *cp; cp++)
115		if (*cp == '=')
116			*cp = ',';
117	write(FD, num, strlen(num));
118	state = DIALING;
119	write(FD, "\r", 1);
120	connected = 0;
121	if (gobble("\r")) {
122		if ((dummy = gobble("01234")) != '1')
123			error_rep(dummy);
124		else
125			connected = 1;
126	}
127	if (connected)
128		state = CONNECTED;
129	else {
130		state = FAILED;
131		return (connected);	/* lets get out of here.. */
132	}
133	tcflush(FD, TCIOFLUSH);
134#ifdef ACULOG
135	if (dialtimeout) {
136		(void)snprintf(line, sizeof line, "%ld second dial timeout",
137			number(value(DIALTIMEOUT)));
138		logent(value(HOST), num, "hayes", line);
139	}
140#endif
141	if (dialtimeout)
142		hay_disconnect();	/* insurance */
143	return (connected);
144}
145
146void
147hay_disconnect(void)
148{
149	/* first hang up the modem*/
150#ifdef DEBUG
151	printf("\rdisconnecting modem....\n\r");
152#endif
153	ioctl(FD, TIOCCDTR, 0);
154	sleep(1);
155	ioctl(FD, TIOCSDTR, 0);
156	goodbye();
157}
158
159void
160hay_abort(void)
161{
162	write(FD, "\r", 1);	/* send anything to abort the call */
163	hay_disconnect();
164}
165
166/*ARGSUSED*/
167static void
168sigALRM(int signo)
169{
170	printf("\07timeout waiting for reply\n\r");
171	dialtimeout = 1;
172	longjmp(timeoutbuf, 1);
173}
174
175static char
176gobble(char *match)
177{
178	char c;
179	sig_t f;
180	size_t i;
181	int status = 0;
182
183	f = signal(SIGALRM, sigALRM);
184	dialtimeout = 0;
185#ifdef DEBUG
186	printf("\ngobble: waiting for %s\n", match);
187#endif
188	do {
189		if (setjmp(timeoutbuf)) {
190			signal(SIGALRM, f);
191			return (0);
192		}
193		alarm(number(value(DIALTIMEOUT)));
194		read(FD, &c, 1);
195		alarm(0);
196		c &= 0177;
197#ifdef DEBUG
198		printf("%c 0x%x ", c, c);
199#endif
200		for (i = 0; i < strlen(match); i++)
201			if (c == match[i])
202				status = c;
203	} while (status == 0);
204	signal(SIGALRM, SIG_DFL);
205#ifdef DEBUG
206	printf("\n");
207#endif
208	return (status);
209}
210
211static void
212error_rep(char c)
213{
214	printf("\n\r");
215	switch (c) {
216
217	case '0':
218		printf("OK");
219		break;
220
221	case '1':
222		printf("CONNECT");
223		break;
224
225	case '2':
226		printf("RING");
227		break;
228
229	case '3':
230		printf("NO CARRIER");
231		break;
232
233	case '4':
234		printf("ERROR in input");
235		break;
236
237	case '5':
238		printf("CONNECT 1200");
239		break;
240
241	default:
242		printf("Unknown Modem error: %c (0x%x)", c, c);
243	}
244	printf("\n\r");
245	return;
246}
247
248/*
249 * set modem back to normal verbose status codes.
250 */
251static void
252goodbye(void)
253{
254	int len;
255	char c;
256
257	tcflush(FD, TCIOFLUSH);
258	if (hay_sync()) {
259		sleep(1);
260#ifndef DEBUG
261		tcflush(FD, TCIOFLUSH);
262#endif
263		write(FD, "ATH0\r", 5);		/* insurance */
264#ifndef DEBUG
265		c = gobble("03");
266		if (c != '0' && c != '3') {
267			printf("cannot hang up modem\n\r");
268			printf("please use 'tip dialer' to make sure the line is hung up\n\r");
269		}
270#endif
271		sleep(1);
272		ioctl(FD, FIONREAD, &len);
273#ifdef DEBUG
274		printf("goodbye1: len=%d -- ", len);
275		rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
276		dumbuf[rlen] = '\0';
277		printf("read (%d): %s\r\n", rlen, dumbuf);
278#endif
279		write(FD, "ATv1\r", 5);
280		sleep(1);
281#ifdef DEBUG
282		ioctl(FD, FIONREAD, &len);
283		printf("goodbye2: len=%d -- ", len);
284		rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
285		dumbuf[rlen] = '\0';
286		printf("read (%d): %s\r\n", rlen, dumbuf);
287#endif
288	}
289	tcflush(FD, TCIOFLUSH);
290	ioctl(FD, TIOCCDTR, 0);		/* clear DTR (insurance) */
291	close(FD);
292}
293
294#define MAXRETRY	5
295
296static int
297hay_sync(void)
298{
299	int len, retry = 0;
300
301	while (retry++ <= MAXRETRY) {
302		write(FD, "AT\r", 3);
303		sleep(1);
304		ioctl(FD, FIONREAD, &len);
305		if (len) {
306			len = read(FD, dumbuf, min(len, DUMBUFLEN));
307			if (strchr(dumbuf, '0') ||
308		   	(strchr(dumbuf, 'O') && strchr(dumbuf, 'K')))
309				return(1);
310#ifdef DEBUG
311			dumbuf[len] = '\0';
312			printf("hay_sync: (\"%s\") %d\n\r", dumbuf, retry);
313#endif
314		}
315		ioctl(FD, TIOCCDTR, 0);
316		ioctl(FD, TIOCSDTR, 0);
317	}
318	printf("Cannot synchronize with hayes...\n\r");
319	return(0);
320}
321