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/*
36 * Routines for calling up on a Hayes Modem
37 * (based on the old VenTel driver).
38 * The modem is expected to be strapped for "echo".
39 * Also, the switches enabling the DTR and CD lines
40 * must be set correctly.
41 * NOTICE:
42 * The easy way to hang up a modem is always simply to
43 * clear the DTR signal. However, if the +++ sequence
44 * (which switches the modem back to local mode) is sent
45 * before modem is hung up, removal of the DTR signal
46 * has no effect (except that it prevents the modem from
47 * recognizing commands).
48 * (by Helge Skrivervik, Calma Company, Sunnyvale, CA. 1984)
49 */
50/*
51 * TODO:
52 * It is probably not a good idea to switch the modem
53 * state between 'verbose' and terse (status messages).
54 * This should be kicked out and we should use verbose
55 * mode only. This would make it consistent with normal
56 * interactive use thru the command 'tip dialer'.
57 */
58#include "tip.h"
59
60#include <termios.h>
61#include <sys/ioctl.h>
62
63#define	min(a,b)	((a < b) ? a : b)
64
65static	int dialtimeout = 0;
66static	jmp_buf timeoutbuf;
67
68#define DUMBUFLEN	40
69static char dumbuf[DUMBUFLEN];
70
71#define	DIALING		1
72#define IDLE		2
73#define CONNECTED	3
74#define	FAILED		4
75static	int state = IDLE;
76
77static void	sigALRM(int);
78static char	gobble(char *);
79static void	error_rep(char);
80static void	goodbye(void);
81static int	hay_sync(void);
82
83int
84hay_dialer(char *num, char *acu)
85{
86	char *cp;
87	int connected = 0;
88	char dummy;
89	struct termios cntrl;
90#ifdef ACULOG
91	char line[80];
92#endif
93	if (hay_sync() == 0)		/* make sure we can talk to the modem */
94		return(0);
95	if (boolean(value(VERBOSE)))
96		printf("\ndialing...");
97	fflush(stdout);
98	tcgetattr(FD, &cntrl);
99	cntrl.c_cflag |= HUPCL;
100	tcsetattr(FD, TCSANOW, &cntrl);
101	tcflush(FD, TCIOFLUSH);
102	write(FD, "ATv0\r", 5);	/* tell modem to use short status codes */
103	gobble("\r");
104	gobble("\r");
105	write(FD, "ATTD", 4);	/* send dial command */
106	for (cp = num; *cp; cp++)
107		if (*cp == '=')
108			*cp = ',';
109	write(FD, num, strlen(num));
110	state = DIALING;
111	write(FD, "\r", 1);
112	connected = 0;
113	if (gobble("\r")) {
114		if ((dummy = gobble("01234")) != '1')
115			error_rep(dummy);
116		else
117			connected = 1;
118	}
119	if (connected)
120		state = CONNECTED;
121	else {
122		state = FAILED;
123		return (connected);	/* lets get out of here.. */
124	}
125	tcflush(FD, TCIOFLUSH);
126#ifdef ACULOG
127	if (dialtimeout) {
128		(void)snprintf(line, sizeof line, "%ld second dial timeout",
129			number(value(DIALTIMEOUT)));
130		logent(value(HOST), num, "hayes", line);
131	}
132#endif
133	if (dialtimeout)
134		hay_disconnect();	/* insurance */
135	return (connected);
136}
137
138void
139hay_disconnect(void)
140{
141	/* first hang up the modem*/
142#ifdef DEBUG
143	printf("\rdisconnecting modem....\n\r");
144#endif
145	ioctl(FD, TIOCCDTR, 0);
146	sleep(1);
147	ioctl(FD, TIOCSDTR, 0);
148	goodbye();
149}
150
151void
152hay_abort(void)
153{
154	write(FD, "\r", 1);	/* send anything to abort the call */
155	hay_disconnect();
156}
157
158/*ARGSUSED*/
159static void
160sigALRM(int signo)
161{
162	printf("\07timeout waiting for reply\n\r");
163	dialtimeout = 1;
164	longjmp(timeoutbuf, 1);
165}
166
167static char
168gobble(char *match)
169{
170	char c;
171	sig_t f;
172	size_t i;
173	int status = 0;
174
175	f = signal(SIGALRM, sigALRM);
176	dialtimeout = 0;
177#ifdef DEBUG
178	printf("\ngobble: waiting for %s\n", match);
179#endif
180	do {
181		if (setjmp(timeoutbuf)) {
182			signal(SIGALRM, f);
183			return (0);
184		}
185		alarm(number(value(DIALTIMEOUT)));
186		read(FD, &c, 1);
187		alarm(0);
188		c &= 0177;
189#ifdef DEBUG
190		printf("%c 0x%x ", c, c);
191#endif
192		for (i = 0; i < strlen(match); i++)
193			if (c == match[i])
194				status = c;
195	} while (status == 0);
196	signal(SIGALRM, SIG_DFL);
197#ifdef DEBUG
198	printf("\n");
199#endif
200	return (status);
201}
202
203static void
204error_rep(char c)
205{
206	printf("\n\r");
207	switch (c) {
208
209	case '0':
210		printf("OK");
211		break;
212
213	case '1':
214		printf("CONNECT");
215		break;
216
217	case '2':
218		printf("RING");
219		break;
220
221	case '3':
222		printf("NO CARRIER");
223		break;
224
225	case '4':
226		printf("ERROR in input");
227		break;
228
229	case '5':
230		printf("CONNECT 1200");
231		break;
232
233	default:
234		printf("Unknown Modem error: %c (0x%x)", c, c);
235	}
236	printf("\n\r");
237	return;
238}
239
240/*
241 * set modem back to normal verbose status codes.
242 */
243static void
244goodbye(void)
245{
246	int len;
247	char c;
248
249	tcflush(FD, TCIOFLUSH);
250	if (hay_sync()) {
251		sleep(1);
252#ifndef DEBUG
253		tcflush(FD, TCIOFLUSH);
254#endif
255		write(FD, "ATH0\r", 5);		/* insurance */
256#ifndef DEBUG
257		c = gobble("03");
258		if (c != '0' && c != '3') {
259			printf("cannot hang up modem\n\r");
260			printf("please use 'tip dialer' to make sure the line is hung up\n\r");
261		}
262#endif
263		sleep(1);
264		ioctl(FD, FIONREAD, &len);
265#ifdef DEBUG
266		printf("goodbye1: len=%d -- ", len);
267		rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
268		dumbuf[rlen] = '\0';
269		printf("read (%d): %s\r\n", rlen, dumbuf);
270#endif
271		write(FD, "ATv1\r", 5);
272		sleep(1);
273#ifdef DEBUG
274		ioctl(FD, FIONREAD, &len);
275		printf("goodbye2: len=%d -- ", len);
276		rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
277		dumbuf[rlen] = '\0';
278		printf("read (%d): %s\r\n", rlen, dumbuf);
279#endif
280	}
281	tcflush(FD, TCIOFLUSH);
282	ioctl(FD, TIOCCDTR, 0);		/* clear DTR (insurance) */
283	close(FD);
284}
285
286#define MAXRETRY	5
287
288static int
289hay_sync(void)
290{
291	int len, retry = 0;
292
293	while (retry++ <= MAXRETRY) {
294		write(FD, "AT\r", 3);
295		sleep(1);
296		ioctl(FD, FIONREAD, &len);
297		if (len) {
298			len = read(FD, dumbuf, min(len, DUMBUFLEN));
299			if (strchr(dumbuf, '0') ||
300		   	(strchr(dumbuf, 'O') && strchr(dumbuf, 'K')))
301				return(1);
302#ifdef DEBUG
303			dumbuf[len] = '\0';
304			printf("hay_sync: (\"%s\") %d\n\r", dumbuf, retry);
305#endif
306		}
307		ioctl(FD, TIOCCDTR, 0);
308		ioctl(FD, TIOCSDTR, 0);
309	}
310	printf("Cannot synchronize with hayes...\n\r");
311	return(0);
312}
313