1/*	$OpenBSD: ventel.c,v 1.12 2006/03/17 19:17:13 moritz Exp $	*/
2/*	$NetBSD: ventel.c,v 1.6 1997/02/11 09:24:21 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[] = "@(#)ventel.c	8.1 (Berkeley) 6/6/93";
41static const char rcsid[] = "$OpenBSD: ventel.c,v 1.12 2006/03/17 19:17:13 moritz Exp $";
42#endif
43#endif /* not lint */
44
45/*
46 * Routines for calling up on a Ventel Modem
47 * The Ventel is expected to be strapped for local echo (just like uucp)
48 */
49#include "tip.h"
50#include <termios.h>
51#include <sys/ioctl.h>
52
53#define	MAXRETRY	5
54
55static	int dialtimeout = 0;
56static	jmp_buf timeoutbuf;
57
58static void	echo(char *);
59static void	sigALRM(int);
60static int	gobble(char, char *);
61static int	vensync(int);
62
63/*
64 * some sleep calls have been replaced by this macro
65 * because some ventel modems require two <cr>s in less than
66 * a second in order to 'wake up'... yes, it is dirty...
67 */
68#define delay(num,denom) busyloop(CPUSPEED*num/denom)
69#define CPUSPEED 1000000	/* VAX 780 is 1MIPS */
70#define DELAY(n) do { long N = (n); while (--N > 0); } while (0)
71#define busyloop(n) do { DELAY(n); } while (0)
72
73int
74ven_dialer(char *num, char *acu)
75{
76	char *cp;
77	int connected = 0;
78	char *msg, line[80];
79	struct termios	cntrl;
80
81	/*
82	 * Get in synch with a couple of carriage returns
83	 */
84	if (!vensync(FD)) {
85		printf("can't synchronize with ventel\n");
86#ifdef ACULOG
87		logent(value(HOST), num, "ventel", "can't synch up");
88#endif
89		return (0);
90	}
91	if (boolean(value(VERBOSE)))
92		printf("\ndialing...");
93	fflush(stdout);
94	tcgetattr(FD, &cntrl);
95	cntrl.c_cflag |= HUPCL;
96	tcsetattr(FD, TCSANOW, &cntrl);
97	echo("#k$\r$\n$D$I$A$L$:$ ");
98	for (cp = num; *cp; cp++) {
99		delay(1, 10);
100		write(FD, cp, 1);
101	}
102	delay(1, 10);
103	write(FD, "\r", 1);
104	gobble('\n', line);
105	if (gobble('\n', line))
106		connected = gobble('!', line);
107	tcflush(FD, TCIOFLUSH);
108#ifdef ACULOG
109	if (dialtimeout) {
110		(void)snprintf(line, sizeof line, "%ld second dial timeout",
111			number(value(DIALTIMEOUT)));
112		logent(value(HOST), num, "ventel", line);
113	}
114#endif
115	if (dialtimeout)
116		ven_disconnect();	/* insurance */
117	if (connected || dialtimeout || !boolean(value(VERBOSE)))
118		return (connected);
119	/* call failed, parse response for user */
120	cp = strchr(line, '\r');
121	if (cp)
122		*cp = '\0';
123	for (cp = line; (cp = strchr(cp, ' ')) != NULL; cp++)
124		if (cp[1] == ' ')
125			break;
126	if (cp) {
127		while (*cp == ' ')
128			cp++;
129		msg = cp;
130		while (*cp) {
131			if (isupper(*cp))
132				*cp = tolower(*cp);
133			cp++;
134		}
135		printf("%s...", msg);
136	}
137	return (connected);
138}
139
140void
141ven_disconnect(void)
142{
143	close(FD);
144}
145
146void
147ven_abort(void)
148{
149	write(FD, "\03", 1);
150	close(FD);
151}
152
153static void
154echo(char *s)
155{
156	char c;
157
158	while ((c = *s++) != '\0')
159		switch (c) {
160		case '$':
161			read(FD, &c, 1);
162			s++;
163			break;
164
165		case '#':
166			c = *s++;
167			write(FD, &c, 1);
168			break;
169
170		default:
171			write(FD, &c, 1);
172			read(FD, &c, 1);
173		}
174}
175
176/*ARGSUSED*/
177static void
178sigALRM(int signo)
179{
180	printf("\07timeout waiting for reply\n");
181	dialtimeout = 1;
182	longjmp(timeoutbuf, 1);
183}
184
185static int
186gobble(char match, char response[])
187{
188	char *cp = response;
189	sig_t f;
190	char c;
191
192	f = signal(SIGALRM, sigALRM);
193	dialtimeout = 0;
194	do {
195		if (setjmp(timeoutbuf)) {
196			signal(SIGALRM, f);
197			*cp = '\0';
198			return (0);
199		}
200		alarm(number(value(DIALTIMEOUT)));
201		read(FD, cp, 1);
202		alarm(0);
203		c = (*cp++ &= 0177);
204#ifdef notdef
205		if (boolean(value(VERBOSE)))
206			putchar(c);
207#endif
208	} while (c != '\n' && c != match);
209	signal(SIGALRM, SIG_DFL);
210	*cp = '\0';
211	return (c == match);
212}
213
214#define min(a,b)	((a)>(b)?(b):(a))
215/*
216 * This convoluted piece of code attempts to get
217 * the ventel in sync.  If you don't have FIONREAD
218 * there are gory ways to simulate this.
219 */
220static int
221vensync(int fd)
222{
223	int already = 0, nread;
224	char buf[60];
225
226	/*
227	 * Toggle DTR to force anyone off that might have left
228	 * the modem connected, and insure a consistent state
229	 * to start from.
230	 *
231	 * If you don't have the ioctl calls to diddle directly
232	 * with DTR, you can always try setting the baud rate to 0.
233	 */
234	ioctl(FD, TIOCCDTR, 0);
235	sleep(1);
236	ioctl(FD, TIOCSDTR, 0);
237	while (already < MAXRETRY) {
238		/*
239		 * After reseting the modem, send it two \r's to
240		 * autobaud on. Make sure to delay between them
241		 * so the modem can frame the incoming characters.
242		 */
243		write(fd, "\r", 1);
244		delay(1,10);
245		write(fd, "\r", 1);
246		sleep(2);
247		if (ioctl(fd, FIONREAD, (caddr_t)&nread) < 0) {
248			perror("tip: ioctl");
249			continue;
250		}
251		while (nread > 0) {
252			read(fd, buf, min(nread, 60));
253			if ((buf[nread - 1] & 0177) == '$')
254				return (1);
255			nread -= min(nread, 60);
256		}
257		sleep(1);
258		already++;
259	}
260	return (0);
261}
262