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