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